Files
Claude Sonnet 5andClaude Sonnet 5 deed1b1af8
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) Canceled after 0s
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (pull_request) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (push) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Canceled after 0s
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (push) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Canceled after 0s
CI / Web (dms-browser) (push) Canceled after 0s
CI / Web (pdf-viewer) (push) Canceled after 0s
CI / Web (shl) (push) Canceled after 0s
Supply-Chain-Scan (npm) / npm-audit (push) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (pull_request) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (pull_request) Canceled after 0s
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (pull_request) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (pull_request) Canceled after 0s
CI / Web (dms-browser) (pull_request) Canceled after 0s
CI / Web (pdf-viewer) (pull_request) Canceled after 0s
CI / Web (shl) (pull_request) Canceled after 0s
Core-Schnittstellen-Vertragstests / contract-tests (pull_request) Canceled after 0s
Supply-Chain-Scan (Go) / govulncheck (pull_request) Canceled after 0s
Mail-Pflichttest-Gate / pflichttest-gate (pull_request) Canceled after 0s
Supply-Chain-Scan (npm) / npm-audit (pull_request) Canceled after 0s
Core-Pflichttest-Gate / pflichttest-gate (pull_request) Canceled after 0s
RBAC-Rollenhierarchie-Pflichtgate / rbac-policy-invariant (pull_request) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (pull_request) Canceled after 0s
UX-02: e-mail-detailansicht-thread-darstellung
Detailansicht fuer web/mail-postfach (UX-01-App erweitert, nicht neu
gebaut): Header (Von/Betreff/Ordner/Datum/Nachricht-ID), Nachrichtentext,
Anhangsliste mit Download (Dateiname nicht verfuegbar -- System
persistiert keinen Originalnamen, daher Anhang N + aus dem
Content-Type geratene Endung). Thread-Gruppierung ueber normalisierten
Betreff (keine In-Reply-To/References-Header im System), Navigation
zwischen Thread-Nachrichten als reiner Komponentenzustand derselben
Seite (kein Routenwechsel -- Ordner-/Suchkontext bleibt dadurch
strukturell erhalten, Akzeptanzkriterium 3).

UX-03 (eigener offener Branch, eigenes Frontend web/mail-search) wird
NICHT wiederverwendet -- getrennte Apps ohne Komponentenfreigabe, siehe
UX-02-PRUEFPROTOKOLL.md fuer die Abgrenzung und die Begruendung der
Backend-Erweiterung.

Tests: lib/threading, lib/threadNavigation, lib/attachmentNaming,
components/MessageDetail (Komponententest je Akzeptanzkriterium),
__tests__/a11y (jest-axe, Pruefung 3), e2e/ux02 gegen die echte
mailpostfach-fixture (Pruefungen 1+2, Mandantentrennung). Bestehender
e2e/ux01-Satz bleibt unveraendert gruen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-16 07:18:57 +00:00

43 lines
1.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { groupThread, normalizeSubject } from "./threading";
import type { MessageListItemDTO } from "./api";
describe("normalizeSubject", () => {
it("entfernt einzelne Antwort-/Weiterleitungs-Präfixe", () => {
expect(normalizeSubject("Re: Projektstart")).toBe("projektstart");
expect(normalizeSubject("AW: Projektstart")).toBe("projektstart");
expect(normalizeSubject("Fwd: Projektstart")).toBe("projektstart");
expect(normalizeSubject("WG: Projektstart")).toBe("projektstart");
});
it("entfernt mehrfach verschachtelte Präfixe", () => {
expect(normalizeSubject("Re: Re: Fwd: Projektstart")).toBe("projektstart");
});
it("lässt einen Betreff ohne Präfix unverändert (bis auf Groß-/Kleinschreibung)", () => {
expect(normalizeSubject("Projektstart Alpha")).toBe("projektstart alpha");
});
});
function msg(messageId: string, subject: string, sentAt: number): MessageListItemDTO {
return { messageId, subject, sender: "a@b.test", mailbox: "posteingang", sentAt };
}
describe("groupThread", () => {
it("gruppiert zusammengehörige Nachrichten anhand des normalisierten Betreffs, sortiert nach Datum", () => {
const messages = [
msg("3", "Re: Re: Start", 300),
msg("1", "Start", 100),
msg("other", "Anderes Thema", 150),
msg("2", "Re: Start", 200),
];
const thread = groupThread(messages, "Re: Start");
expect(thread.map((m) => m.messageId)).toEqual(["1", "2", "3"]);
});
it("liefert nur die eine Nachricht, wenn kein weiterer Thread-Partner existiert", () => {
const messages = [msg("1", "Einzelnachricht", 100), msg("2", "Anderes Thema", 150)];
expect(groupThread(messages, "Einzelnachricht").map((m) => m.messageId)).toEqual(["1"]);
});
});