OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

VaultDialogTest.php

7 KB
<?php

declare(strict_types=1);

/**
 * Deckt die Bedienung der Tresor-Dialoge und die Ordnerzuordnung ab.
 *
 * Drei Zusagen tragen sie: Eine aufgeklappte Auswahlliste bleibt in einem
 * Dialog bedienbar (sie hängt am `<body>`, gehört aber zum Dialog), der
 * Verschieben-Dialog spricht die Schnittstelle des Servers, und ein Eintrag
 * behält seinen Ordner – gesetzt wird nur, was zum Tresor des Eintrags gehört.
 */
return static function (): void {

    $wurzel = dirname(__DIR__);
    $lies   = static fn(string $pfad): string => (string)file_get_contents($wurzel . '/' . $pfad);

    // ── Dialoge und aufgeklappte Auswahllisten ──────────────────────────

    test('Der Dialog zählt seine aufgeklappten Ebenen zu sich', function () use ($lies) {
        $js = $lies('public/assets/js/dialog.js');

        assertTrue(str_contains($js, "var EBENE = '[data-dialog-layer]'"), 'Keine Kennzeichnung für Dialog-Ebenen');
        assertTrue(str_contains($js, 'function gehoertDazu'), 'Ohne Zugehörigkeitsprüfung bleibt der Fokuswächter blind');
        assertTrue(str_contains($js, 'if (gehoertDazu(dialog, e.target)) { return; }'),
            'Der Fokuswächter holt den Fokus aus einer Dialog-Ebene zurück');
        assertTrue(str_contains($js, "el.closest(AUSWAHL) || el.closest(EBENE)"),
            'Eine Dialog-Ebene gilt als Herkunft des Fokus');
    });

    test('Die durchsuchbare Auswahl weist sich als Dialog-Ebene aus', function () use ($lies) {
        $js = $lies('public/assets/js/app.js');

        assertTrue(str_contains($js, "dropdown.setAttribute('data-dialog-layer', '')"),
            'Ohne Kennzeichnung ist das Suchfeld in Dialogen nicht erreichbar');
        assertTrue(str_contains($js, "if (e.key === 'Escape') { e.stopPropagation();"),
            'Escape in der Liste schließt sonst den ganzen Dialog');
    });

    test('Die Anzeige folgt nachträglich neu aufgebauten Optionen', function () use ($lies) {
        $js = $lies('public/assets/js/app.js');

        assertTrue(str_contains($js, 'new MutationObserver(function () { self.sync(); })'),
            'Ohne Beobachtung bleibt die Anzeige auf dem alten Eintrag stehen');
        assertTrue(str_contains($js, ".observe(this.native, { childList: true, subtree: true })"),
            'Die Beobachtung muss am Select selbst hängen');
    });

    test('Jeder Tresor-Dialog hat eine Schließen-Schaltfläche', function () use ($lies) {
        foreach (['templates/vault/index.php', 'templates/vault/team_show.php'] as $pfad) {
            $html = $lies($pfad);
            preg_match_all('/<div class="modal-overlay[^"]*" id="([A-Za-z]+)"/', $html, $treffer);
            foreach ($treffer[1] as $id) {
                // Der Abschnitt bis zum nächsten Dialog enthält den Kopfbereich.
                $start = (int)strpos($html, 'id="' . $id . '"');
                $naechster = strpos($html, 'class="modal-overlay', $start + 1);
                $block = substr($html, $start, ($naechster === false ? strlen($html) : $naechster) - $start);
                assertTrue(str_contains($block, 'modal-close'),
                    'Dialog ohne Schließen-Schaltfläche: ' . $id . ' in ' . $pfad);
            }
        }
    });

    // ── Verschieben-Dialog des Team-Tresors ─────────────────────────────

    test('Der Verschieben-Dialog spricht die Schnittstelle des Servers', function () use ($lies) {
        $html = $lies('templates/vault/team_show.php');

        assertTrue(str_contains($html, "form.append('target_team_id'"), 'Zielbereich wird unter falschem Namen gesendet');
        assertTrue(str_contains($html, "form.append('target_folder_id'"), 'Zielordner wird unter falschem Namen gesendet');
        assertFalse(str_contains($html, "form.append('team_id'"), 'Alter Feldname ist noch im Spiel');
        assertTrue(str_contains($html, 'if (data.success)'), 'Die Antwort trägt success, nicht ok');
        assertFalse(str_contains($html, 'if (data.ok)'), 'Die Antwort wird falsch ausgewertet');
    });

    test('Der Server liest genau diese Felder', function () use ($lies) {
        $php = $lies('src/Controllers/VaultController.php');

        assertTrue(str_contains($php, "\$request->post('target_team_id'"), 'Zielbereich wird nicht gelesen');
        assertTrue(str_contains($php, "\$request->post('target_folder_id'"), 'Zielordner wird nicht gelesen');
    });

    // ── Ordner gehören zu genau einem Tresor ────────────────────────────

    test('Die Ordnerwahl wird gegen den Tresor geprüft', function () use ($lies) {
        $php = $lies('src/Controllers/VaultController.php');

        assertTrue(str_contains($php, 'private function folderInVault('), 'Keine Prüfung der Ordnerzugehörigkeit');
        assertSame(4, substr_count($php, '$this->folderInVault('),
            'Alle vier Schreibwege (anlegen/bearbeiten, persönlich/Team) müssen prüfen');
        assertTrue(str_contains($php, "WHERE id = ? AND team_id = ?'"), 'Team-Ordner werden nicht am Team geprüft');
        assertTrue(str_contains($php, "WHERE id = ? AND user_id = ?'"), 'Eigene Ordner werden nicht am Nutzer geprüft');
    });

    test('Der Anlege-Dialog belegt den Ordner unabhängig vom Datentyp vor', function () use ($lies) {
        $html = $lies('templates/vault/index.php');

        assertTrue(str_contains($html, "'id' => (int)\$f['id']"), 'Ordner-Kennungen müssen als Zahl im Skript landen');
        assertTrue(str_contains($html, "if (ziel !== null && String(f.id) === ziel)"),
            'Die Vorauswahl muss den Datentyp der Kennung überbrücken');
    });

    test('Bearbeiten im Dialog behält Tresor und Ordner des Eintrags', function () use ($lies) {
        $html = $lies('templates/vault/index.php');
        $php  = $lies('src/Vault/VaultManager.php');

        assertTrue(str_contains($html, 'vaultApplyCreateTarget(eTeam,'),
            'Der Dialog übernimmt den Tresor des Eintrags nicht');
        assertTrue(str_contains($html, "e.folder_id"), 'Der Ordner des Eintrags wird nicht übernommen');
        assertTrue(str_contains($php, "'team_id'        => \$row['team_id']"),
            'Ohne Tresor im Datensatz kann der Dialog ihn nicht setzen');
    });

    // ── Löschen heißt Papierkorb ────────────────────────────────────────

    test('Löschen eines Eintrags wird als Papierkorb protokolliert', function () use ($lies) {
        $php  = $lies('src/Controllers/VaultController.php');
        $html = $lies('templates/vault/index.php');

        assertFalse(str_contains($php, 'unwiderruflich gelöscht'),
            'Der Eintrag liegt 30 Tage im Papierkorb – das Protokoll darf nichts anderes behaupten');
        assertTrue(str_contains($php, 'in den Papierkorb verschoben'), 'Keine Angabe zum Papierkorb');
        assertFalse(str_contains($html, 'unwiderruflich löschen?'), 'Die Rückfrage verspricht zu viel');
    });
};