Files
nexarch/web/dms-browser/components/MetadataForm.test.tsx
patrickandClaude Sonnet 5 dd8c620e3c UX-04: Übersetzungen Deutsch und Englisch für dms-browser/pdf-viewer
Rueckwirkende Umstellung aller vor UX-03 gebauten dms-browser- und
pdf-viewer-Seiten/Komponenten auf die @nexarch/shl-i18n-Infrastruktur
(useI18n/t(), registerMessages, formatDate) statt hartkodierter
deutscher Texte. Schliesst die in UX-03 explizit offen gelassene
Retrofit-Luecke.

Neue Sprachdateien lib/messages.ts (dms-browser, pdf-viewer) mit
vollstaendigen DE/EN-Woerterbuechern. Neuer automatisierter
Abdeckungstest (i18n-coverage.test.ts, gleiche Heuristik wie in
@nexarch/shl) belegt: keine fest verdrahteten JSX-Texte mehr in
app/ bzw. components/ beider Apps.

web/shl/i18n/i18n.tsx splittet registry/registerMessages weiter in
i18n/messages.ts (kein "use client"), um die bekannte RSC-Client/
Server-Grenzverletzung zu vermeiden.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-18 02:04:40 +02:00

64 lines
3.2 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
import { MetadataForm } from "./MetadataForm";
import { I18nProvider } from "@nexarch/shl";
import { FieldDTO } from "../lib/metadataApi";
afterEach(() => {
cleanup();
vi.restoreAllMocks();
});
const fields: FieldDTO[] = [
{ id: "f1", key: "betrag", label: "Betrag", type: "number", required: true, default_value: null },
{ id: "f2", key: "faelligkeit", label: "Fälligkeit", type: "date", required: false, default_value: null },
];
// Akzeptanzkriterium 1: Formular passt sich der Felddefinition automatisch an.
it("rendert ein Eingabefeld je Felddefinition mit korrektem Typ", () => {
render(<I18nProvider initialLocale="de"><MetadataForm fields={fields} initialValues={{}} onSave={vi.fn()} /></I18nProvider>);
const betrag = screen.getByLabelText("Betrag *") as HTMLInputElement;
const faelligkeit = screen.getByLabelText("Fälligkeit") as HTMLInputElement;
expect(betrag.type).toBe("number");
expect(faelligkeit.type).toBe("date");
});
// Pflichtpruefung 2: Formular per Tastatur vollstaendig ausfuellbar — jedes
// Feld ist ueber sein Label erreichbar und per Tastatur-Eingabe (change,
// kein Mausklick noetig) befuellbar.
it("ist ueber Labels erreichbar und ohne Maus befuellbar", () => {
render(<I18nProvider initialLocale="de"><MetadataForm fields={fields} initialValues={{}} onSave={vi.fn()} /></I18nProvider>);
const betrag = screen.getByLabelText("Betrag *");
fireEvent.change(betrag, { target: { value: "150" } });
expect((betrag as HTMLInputElement).value).toBe("150");
});
it("speichert per Speichern-Button", async () => {
const onSave = vi.fn().mockResolvedValue(undefined);
render(<I18nProvider initialLocale="de"><MetadataForm fields={fields} initialValues={{}} onSave={onSave} /></I18nProvider>);
fireEvent.change(screen.getByLabelText("Betrag *"), { target: { value: "99" } });
fireEvent.click(screen.getByRole("button", { name: "Speichern" }));
await waitFor(() => expect(onSave).toHaveBeenCalledWith({ f1: "99" }));
});
// Pflichtpruefung 1: ungespeicherte Aenderungen loesen eine Warnung aus.
it("zeigt eine Bestaetigung, bevor ungespeicherte Aenderungen verworfen werden", () => {
render(<I18nProvider initialLocale="de"><MetadataForm fields={fields} initialValues={{ f1: "10" }} onSave={vi.fn()} /></I18nProvider>);
const betrag = screen.getByLabelText("Betrag *");
fireEvent.change(betrag, { target: { value: "20" } });
fireEvent.click(screen.getByRole("button", { name: "Verwerfen" }));
expect(screen.getByRole("alertdialog")).toBeTruthy();
expect((betrag as HTMLInputElement).value).toBe("20"); // noch nicht verworfen
fireEvent.click(screen.getByRole("button", { name: "Ja, verwerfen" }));
expect((betrag as HTMLInputElement).value).toBe("10");
expect(screen.queryByRole("alertdialog")).toBeNull();
});
it("Verwerfen-Button ist deaktiviert, solange nichts geaendert wurde", () => {
render(<I18nProvider initialLocale="de"><MetadataForm fields={fields} initialValues={{ f1: "10" }} onSave={vi.fn()} /></I18nProvider>);
const discardButton = screen.getByRole("button", { name: "Verwerfen" }) as HTMLButtonElement;
expect(discardButton.disabled).toBe(true);
});