HR reported it from testing: change someone's address and their history shows "Geänderte Felder: Adresse, Ort" — the new address is on the Stammdaten tab, the old one is nowhere. It was recorded, but only in the audit log, which is a different page sorted by time and actor rather than by person. So you had to already know what you were looking for to find out whether an address had ever changed, let alone what it used to be. The field-by-field diff was being built anyway and written to the audit log. employee_history now carries the same list, and the person's history renders it as an expandable Feld / Vorher / Nachher table — the same table the audit log uses, lifted into a shared component so the two views don't drift into reading differently. It expands with <details>, so the values are in the page: findable with Ctrl+F, present when printed, no script involved. The duplication with audit_log is deliberate. A person's history should be readable on its own, including after the log is eventually thinned by a retention rule. Rows written before today stay without values. They could only be reconstructed from the audit log, and the link is not reliable — no key, only a timestamp and a person. Honestly empty beats plausibly wrong. The migration was generated from the live function definition rather than retyped, and the diff is four lines: two column lists, two value lists. It carries a self-check that raises if either insert failed to pick up the new column, and it was rehearsed inside a rolled-back transaction against real data first — the probe confirmed the old street name lands in the history row. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
79 lines
3.3 KiB
TypeScript
79 lines
3.3 KiB
TypeScript
import { render, screen, within } from "@testing-library/react";
|
|
import { describe, expect, it } from "vitest";
|
|
import { HistorieTab } from "@/components/employees/tabs/HistorieTab";
|
|
import type { Database } from "@/lib/supabase/types";
|
|
|
|
type HistoryRow = Database["public"]["Tables"]["employee_history"]["Row"];
|
|
|
|
// Der Anlass: eine Adressänderung war in der Historie der Person nur als
|
|
// „Geänderte Felder: Adresse" zu sehen. Die alte Anschrift stand allein im
|
|
// Protokoll — einer nach Zeitpunkt sortierten Seite, auf der man erst wissen
|
|
// muss, wonach man sucht. Damit war nicht nachvollziehbar, ob und wie sich
|
|
// eine Adresse je geändert hat.
|
|
|
|
function eintrag(teil: Partial<HistoryRow>): HistoryRow {
|
|
return {
|
|
id: "h1",
|
|
employee_id: "e1",
|
|
event_date: "2026-03-02",
|
|
event_type: "Stammdatenänderung",
|
|
description: "Geänderte Felder: Adresse, Ort, wirksam ab 2026-03-02",
|
|
changes: null,
|
|
created_at: "2026-03-02T08:00:00.000Z",
|
|
...teil,
|
|
};
|
|
}
|
|
|
|
const ADRESSWECHSEL = eintrag({
|
|
changes: [
|
|
{ feld: "Adresse", vorher: "Feldweg 66", nachher: "Bahnhofstrasse 5" },
|
|
{ feld: "Ort", vorher: "Brno", nachher: "Linz" },
|
|
],
|
|
});
|
|
|
|
describe("HistorieTab", () => {
|
|
it("zeigt die alte Anschrift, nicht nur die neue", () => {
|
|
render(<HistorieTab history={[ADRESSWECHSEL]} />);
|
|
expect(screen.getByText("Feldweg 66")).toBeInTheDocument();
|
|
expect(screen.getByText("Bahnhofstrasse 5")).toBeInTheDocument();
|
|
});
|
|
|
|
it("stellt Vorher und Nachher je Feld gegenüber", () => {
|
|
render(<HistorieTab history={[ADRESSWECHSEL]} />);
|
|
const zeile = screen.getByText("Adresse").closest("tr")!;
|
|
const zellen = within(zeile).getAllByRole("cell");
|
|
expect(zellen[1]).toHaveTextContent("Feldweg 66");
|
|
expect(zellen[2]).toHaveTextContent("Bahnhofstrasse 5");
|
|
});
|
|
|
|
it("nennt die Zahl der Felder am Aufklapper", () => {
|
|
render(<HistorieTab history={[ADRESSWECHSEL]} />);
|
|
expect(screen.getByText(/2 Felder im Detail/)).toBeInTheDocument();
|
|
});
|
|
|
|
it("benennt ein einzelnes Feld im Singular", () => {
|
|
render(<HistorieTab history={[eintrag({ changes: [{ feld: "Ort", vorher: "Brno", nachher: "Linz" }] })]} />);
|
|
expect(screen.getByText(/1 Feld im Detail/)).toBeInTheDocument();
|
|
});
|
|
|
|
it("macht aus einem leeren Vorher-Wert eine Aussage statt einer Lücke", () => {
|
|
render(<HistorieTab history={[eintrag({ changes: [{ feld: "Telefon", vorher: null, nachher: "0664 1234567" }] })]} />);
|
|
const zeile = screen.getByText("Telefon").closest("tr")!;
|
|
expect(within(zeile).getAllByRole("cell")[1]).toHaveTextContent("leer");
|
|
});
|
|
|
|
it("hängt keinen Aufklapper an Ereignisse ohne Feldwerte", () => {
|
|
// Eintritt, Austritt und die Zeilen von vor dieser Erweiterung haben
|
|
// keine Werte. Ein leerer Aufklapper würde einen Fehler vermuten lassen.
|
|
render(<HistorieTab history={[eintrag({ event_type: "Eintritt", description: "Eintritt als Elektrotechniker:in" })]} />);
|
|
expect(screen.getByText("Eintritt als Elektrotechniker:in")).toBeInTheDocument();
|
|
expect(screen.queryByText(/im Detail/)).not.toBeInTheDocument();
|
|
expect(document.querySelector("details")).toBeNull();
|
|
});
|
|
|
|
it("bleibt bei leerer Historie bei einem Satz", () => {
|
|
render(<HistorieTab history={[]} />);
|
|
expect(screen.getByText("Keine Historieneinträge vorhanden.")).toBeInTheDocument();
|
|
});
|
|
});
|