0, 'einnahmen' => 0, 'ausgaben' => 0, 'klassen' => 0, 'konten' => 0, 'kontengruppen' => 0, 'reihen' => 3, 'erinnerungen' => 2, 'vertraege' => 7, 'fristen' => 1, ]; public function run(): void { $this->menueOhneTermineUndVertraege(); $this->verwaltungLeuchtetFuerBeide(); $this->verwaltungsseiteFuehrtZuDenTerminen(); $this->verwaltungsseiteFuehrtZuDenVertraegen(); $this->alleKnoepfeGleichUndFussbuendig(); } /** * Alle Kacheln der Verwaltung tragen dieselben kleinen Knöpfe wie die von * Termine, und die Leisten stehen unabhängig von der Textlänge auf gleicher * Höhe am unteren Kachelrand. */ private function alleKnoepfeGleichUndFussbuendig(): void { $kacheln = $this->kacheln(); // Das abschließende \b hält die umgebende .knopfleiste heraus. preg_match_all('#class="knopf\b([^"]*)"#', $kacheln, $treffer); $this->assertEquals( substr_count($kacheln, '
'), substr_count($kacheln, '
'), 'Jede Kachel führt eine Knopfleiste' ); $this->assertGreaterThan(0, count($treffer[1]), 'Die Kacheln führen Knöpfe'); foreach ($treffer[1] as $zusatz) { $this->assert( str_contains($zusatz, ' klein'), 'Knopf "knopf' . $zusatz . '" ist klein wie die unter Termine' ); } $this->assert( (bool)preg_match('#
#', $kacheln), 'Das Kachelraster richtet seine Knopfleisten am Fuß aus' ); $stil = $this->stilblatt(); $this->assert( (bool)preg_match('#\.kachelraster\.fussbuendig > \.kachel\s*\{[^}]*flex-direction:\s*column#', $stil), 'Die Kacheln des Rasters sind Flexspalten' ); $this->assert( (bool)preg_match( '#\.kachelraster\.fussbuendig > \.kachel > \.knopfleiste\s*\{[^}]*margin-top:\s*auto[^}]*padding-top:#', $stil ), 'Die Knopfleiste sinkt nach unten und behält ihren Abstand zum Text' ); } private function menueOhneTermineUndVertraege(): void { $menue = $this->seitenleiste($this->rendere('layout/kopf.php', ['aktuellerPfad' => '/'])); $this->assert(!str_contains($menue, 'href="/termine"'), 'Kein Menüpunkt Termine mehr'); $this->assert(!str_contains($menue, '>Termine'), 'Auch die Beschriftung Termine ist verschwunden'); $this->assert(!str_contains($menue, 'href="/vertraege"'), 'Kein Menüpunkt Verträge mehr'); $this->assert(!str_contains($menue, '>Verträge'), 'Auch die Beschriftung Verträge ist verschwunden'); $this->assert(str_contains($menue, 'href="/verwaltung"'), 'Verwaltung steht weiter im Menü'); $this->assert(str_contains($menue, 'href="/planung"'), 'Die übrigen Menüpunkte bleiben unberührt'); $this->assert(str_contains($menue, 'href="/wertpapiere"'), 'Wertpapiere stehen weiter im Menü'); } private function verwaltungLeuchtetFuerBeide(): void { // Ohne eigenen Menüpunkt bliebe die Seitenleiste auf diesen Seiten // sonst ganz ohne Markierung. $pfade = [ '/termine', '/termine/kalender', '/termine/erinnerungen', '/vertraege', '/vertraege/adressen', '/vertraege/sparziele', '/vertraege/kredite', ]; foreach ($pfade as $pfad) { $menue = $this->seitenleiste($this->rendere('layout/kopf.php', ['aktuellerPfad' => $pfad])); $this->assert( (bool)preg_match('#href="/verwaltung"\s+class="aktiv"#', $menue), 'Verwaltung ist markiert auf ' . $pfad ); } $cockpit = $this->seitenleiste($this->rendere('layout/kopf.php', ['aktuellerPfad' => '/'])); $this->assert( (bool)preg_match('#href="/"\s+class="aktiv"#', $cockpit), 'Cockpit ist auf der Startseite markiert' ); $this->assert( !preg_match('#href="/verwaltung"\s+class="aktiv"#', $cockpit), 'Verwaltung leuchtet nicht überall mit' ); } private function verwaltungsseiteFuehrtZuDenTerminen(): void { $kacheln = $this->kacheln(); $this->assert(str_contains($kacheln, 'href="/termine"'), 'Regelmäßige Buchungen sind verlinkt'); $this->assert(str_contains($kacheln, 'href="/termine/kalender"'), 'Finanzkalender ist verlinkt'); $this->assert(str_contains($kacheln, 'href="/termine/erinnerungen"'), 'Erinnerungen sind verlinkt'); $this->assert(str_contains($kacheln, '

Termine

'), 'Die Kachel heißt Termine'); $this->assert(str_contains($kacheln, '>3<'), 'Die Kennzahl zeigt die Zahl der Reihen'); $this->assert(str_contains($kacheln, 'offene Erinnerungen: 2'), 'Offene Erinnerungen werden genannt'); foreach (['/termine', '/termine/kalender', '/termine/erinnerungen'] as $ziel) { $this->assert( (bool)preg_match('#href="' . preg_quote($ziel, '#') . '" class="knopf klein#', $kacheln), 'Der Knopf zu ' . $ziel . ' ist klein' ); } // Umbruch statt Überlauf am rechten Kachelrand. $this->assert( (bool)preg_match('#\.knopfleiste\s*\{[^}]*flex-wrap:\s*wrap#', $this->stilblatt()), 'Die Knopfleiste bricht um' ); $this->assert( strrpos($kacheln, '

Termine

') > (int)strrpos($kacheln, '

Online-Banking

'), 'Termine stehen hinter dem Online-Banking' ); } private function verwaltungsseiteFuehrtZuDenVertraegen(): void { $kacheln = $this->kacheln(); $this->assert(str_contains($kacheln, 'href="/vertraege"'), 'Verträge sind verlinkt'); $this->assert(str_contains($kacheln, 'href="/vertraege/adressen"'), 'Adressen sind verlinkt'); $this->assert(str_contains($kacheln, 'href="/vertraege/sparziele"'), 'Sparziele sind verlinkt'); $this->assert(str_contains($kacheln, 'href="/vertraege/kredite"'), 'Kredite sind verlinkt'); $this->assert(str_contains($kacheln, '

Verträge

'), 'Die Kachel heißt Verträge'); $this->assert(str_contains($kacheln, '>7<'), 'Die Kennzahl zeigt die Zahl der aktiven Verträge'); $this->assert(str_contains($kacheln, 'nächsten 90 Tagen: 1'), 'Ablaufende Fristen werden genannt'); // Vier Wege in einer Kachel: nur als kleine Knöpfe, und die Leiste bricht um. foreach (['/vertraege', '/vertraege/adressen', '/vertraege/sparziele', '/vertraege/kredite'] as $ziel) { $this->assert( (bool)preg_match('#href="' . preg_quote($ziel, '#') . '" class="knopf klein#', $kacheln), 'Der Knopf zu ' . $ziel . ' ist klein' ); } // Letzter Punkt: keine weitere Kachel hinter der von Verträge. $letzte = (int)strrpos($kacheln, '

Verträge

'); foreach (['Kategorien', 'Klassen', 'Konten', 'Import/Export', 'Online-Banking', 'Termine'] as $vorher) { $this->assert( $letzte > (int)strrpos($kacheln, '

' . $vorher . '

'), 'Verträge stehen hinter der Kachel ' . $vorher ); } } private function stilblatt(): string { return (string)file_get_contents(dirname(__DIR__) . '/public/static/app.css'); } /** Nur der Inhalt der Verwaltungsseite – die Seitenleiste soll nicht mitzählen. */ private function kacheln(): string { $seite = $this->rendere('verwaltung/index.php', [ 'aktuellerPfad' => '/verwaltung', 'zahlen' => self::ZAHLEN, ]); return substr($seite, (int)strpos($seite, ' $daten */ private function rendere(string $view, array $daten): string { $daten += ['titel' => 'Test', 'flash' => [], 'benutzer' => null, 'csrfToken' => 'x']; extract($daten, EXTR_SKIP); ob_start(); include dirname(__DIR__) . '/src/Views/' . $view; return (string)ob_get_clean(); } /** Nur die Seitenleiste – Verlinkungen im Inhalt sollen nicht mitzählen. */ private function seitenleiste(string $html): string { $anfang = (int)strpos($html, ''); return substr($html, $anfang, $ende - $anfang); } }