OpenNIT

Quelltext des aktuellen Standes

Fassung 1.1.1

TicketMentionTest.php

5 KB
<?php

declare(strict_types=1);

use OpenNIT\Tickets\TicketManager;

/**
 * Deckt ab, dass eine @-Erwähnung bis zur Benachrichtigung durchkommt.
 *
 * Die Kennung der erwähnten Person reist als `data-uid` am Erwähnungs-Token
 * durch die Bereinigung des eingehenden HTML. Überlebt sie diesen Schritt
 * nicht, findet die Auswertung nichts mehr vor und die Erwähnung bleibt
 * folgenlos – ohne jede Fehlermeldung. Festgehalten wird deshalb beides: dass
 * das Attribut erhalten bleibt und dass es nichts anderes transportieren kann.
 */
return static function (): void {

    $quelle = static fn(): string => (string)file_get_contents(__DIR__ . '/../src/Tickets/TicketManager.php');

    test('Die Kennung einer Erwaehnung ueberlebt die Bereinigung', function () {
        $html = '<p>Hallo <span class="ts-mention" data-uid="42" contenteditable="false">@Max Mustermann</span>, schau mal.</p>';
        $rein = TicketManager::sanitizeIncomingHtml($html);

        assertTrue(preg_match('/data-uid=["\']42["\']/', $rein) === 1,
            'Die Kennung der erwähnten Person fehlt nach der Bereinigung: ' . $rein);
        // Ohne die Klasse verliert das Token seine Hervorhebung in der Anzeige.
        assertTrue(str_contains($rein, 'ts-mention'), 'Die Klasse des Tokens fehlt');
        assertTrue(str_contains($rein, '@Max Mustermann'), 'Der angezeigte Name fehlt');
    });

    test('Mehrere Erwaehnungen bleiben einzeln erhalten', function () {
        $html = '<p><span class="ts-mention" data-uid="7">@A</span> und '
              . '<span class="ts-mention" data-uid="9">@B</span></p>';
        $rein = TicketManager::sanitizeIncomingHtml($html);

        preg_match_all('/data-uid=["\'](\d+)["\']/', $rein, $m);
        assertSame(['7', '9'], $m[1], 'Nicht alle Kennungen sind erhalten: ' . $rein);
    });

    test('Die Kennung nimmt nur Ziffern an', function () {
        foreach (['42 onmouseover=x', '', 'abc', '4.2', ' 42'] as $wert) {
            $rein = TicketManager::sanitizeIncomingHtml(
                '<p><span data-uid="' . $wert . '">@X</span></p>'
            );
            assertFalse(str_contains($rein, 'data-uid'),
                'Der Wert "' . $wert . '" hätte entfernt werden müssen: ' . $rein);
        }
    });

    test('Nur das Erwaehnungs-Token traegt die Kennung', function () {
        // An anderen Elementen hat das Attribut keinen Zweck.
        $rein = TicketManager::sanitizeIncomingHtml('<div data-uid="7">x</div><p data-uid="8">y</p>');
        assertFalse(str_contains($rein, 'data-uid'), 'Fremde Elemente tragen die Kennung: ' . $rein);

        // Andere data-Attribute bleiben weiterhin draußen.
        $rein = TicketManager::sanitizeIncomingHtml('<p><span data-foo="1" data-uid="9">@Y</span></p>');
        assertFalse(str_contains($rein, 'data-foo'), 'Ein fremdes data-Attribut kommt durch: ' . $rein);
        assertTrue(str_contains($rein, 'data-uid="9"'), 'Die Kennung fehlt: ' . $rein);
    });

    test('Die Bereinigung bleibt im Uebrigen streng', function () {
        $rein = TicketManager::sanitizeIncomingHtml(
            '<p><span data-uid="5" onclick="alert(1)">@Z</span><script>alert(1)</script></p>'
        );
        assertFalse(str_contains($rein, 'onclick'), 'Ein Event-Handler kommt durch: ' . $rein);
        assertFalse(str_contains($rein, 'script'), 'Ein Script kommt durch: ' . $rein);
        assertTrue(str_contains($rein, 'data-uid="5"'), 'Die Kennung fehlt: ' . $rein);
    });

    test('Eine Erwaehnung wirkt in der internen Notiz wie in der Antwort', function () use ($quelle) {
        $text = $quelle();
        $von  = strpos($text, 'public function addStaffReply(');
        assertTrue($von !== false, 'addStaffReply() nicht gefunden');
        $bis  = strpos($text, "\n    }", $von);
        $rumpf = substr($text, $von, $bis - $von);

        assertTrue(str_contains($rumpf, '$this->notifyMentions('), 'Die Erwähnungen werden nicht ausgewertet');

        // Der Aufruf gehört hinter den Block, der nur für sichtbare Antworten
        // gilt – sonst bliebe die interne Notiz stumm.
        $internBlock = strpos($rumpf, 'if (!$isInternal) {');
        $mention     = strpos($rumpf, '$this->notifyMentions(');
        assertTrue($internBlock !== false, 'Die Fallunterscheidung fehlt');
        assertTrue($mention > $internBlock, 'Die Auswertung steht vor der Fallunterscheidung');

        $zwischen = substr($rumpf, $internBlock, $mention - $internBlock);
        // Der Block schließt vor dem Aufruf: gleich viele geschweifte Klammern
        // auf wie zu bedeutet, dass die Auswertung außerhalb steht.
        assertSame(substr_count($zwischen, '{'), substr_count($zwischen, '}'),
            'Die Auswertung liegt innerhalb des Blocks für sichtbare Antworten');
    });

    test('Die Auswertung sucht genau nach dieser Kennung', function () use ($quelle) {
        // Das Muster der Auswertung und das vom Editor erzeugte Attribut
        // müssen zueinander passen.
        assertTrue(str_contains($quelle(), 'data-uid=["\\\'](\\d+)["\\\']'),
            'Das Suchmuster der Auswertung hat sich geändert');

        $js = (string)file_get_contents(__DIR__ . '/../public/assets/js/ticket-mention.js');
        assertTrue(str_contains($js, "setAttribute('data-uid'"), 'Der Editor setzt die Kennung nicht mehr');
    });
};