feat(bausteine): Abschnitte, die mehrere Vorlagen teilen
Prüfung / pruefung (pull_request) Canceled after 0s
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (pull_request) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s

"Datensicherung pruefen" steht in vier der sieben mitgelieferten Vorlagen,
wortgleich. Wer den Ablauf aendert, aendert ihn viermal -- oder dreimal und
vergisst den vierten.

Entschieden wie im Issue vorgeschlagen: ein Entwurf **verweist** auf eine
veroeffentlichte Baustein-Fassung, und beim Veroeffentlichen der Vorlage setzt
der Dienst deren Schritte als tiefe Kopie ein. Danach sieht ein Lauf weiterhin
konkrete Schritte, und Schrittergebnis, Druckansicht und Archiv muessen nichts
von Bausteinen wissen. Die Alternative -- Laeufe verweisen zur Laufzeit --
haette die Unveraenderlichkeit an einer zweiten Stelle durchsetzen muessen.

Eine neue Fassung aendert damit keine veroeffentlichte Vorlagenversion und
keinen laufenden Lauf. Sie wirkt in der naechsten veroeffentlichten Version;
die Oberflaeche sagt das an zwei Stellen, weil man sonst Durchschlag erwartet.

Vier Garantien liegen in der Datenbank, nicht nur im Dienst: hoechstens ein
Entwurf je Baustein, eine veroeffentlichte Fassung ist unveraenderlich, der Weg
zurueck zum Entwurf ist versperrt, und ein Abschnitt kann nur auf eine
veroeffentlichte Fassung verweisen.

Der Titel kommt aus dem Baustein, nicht aus dem Abschnitt der Vorlage: sonst
muesste er in jeder Vorlage einzeln gepflegt werden, und zwei Vorlagen zeigten
denselben Baustein unter verschiedenen Namen.

Ein Export traegt die aufgeloesten Schritte, keinen Verweis -- eine Kennung aus
dieser Installation bedeutet in einer anderen nichts.

Das Durchklicken des gebauten Stands fand einen Fehler, den die Suite nicht
gesehen haette: die Platzhalter-Schritte eines Baustein-Abschnitts haben keinen
Titel, wurden aber mitgeschickt, und SchrittSchema wies den ganzen Entwurf ab.
Behoben, mit Test und Gegenprobe. Der ganze Weg danach am gebauten Stand
nachgemessen: Baustein anlegen, veroeffentlichen, einsetzen, Vorlage
veroeffentlichen -- die Schritte stehen drin, mit Herkunft und
Protokolleintrag.

Closes #50
This commit is contained in:
Florian Wilde
2026-10-04 19:06:31 +02:00
parent b688e2ebc8
commit 0b5138d8bb
24 changed files with 2862 additions and 46 deletions
+69
View File
@@ -16,6 +16,8 @@ import {
verwendetePlatzhalter,
type EditorAufbau,
type EditorVariable,
bausteineAnreichern,
neuerBausteinAbschnitt,
} from "@/lib/editor-aufbau";
import { EINGEBAUTE_PLATZHALTER } from "@/lib/platzhalter";
import { StrukturSchema } from "@/lib/vorlagen-format";
@@ -254,3 +256,70 @@ describe("Beispielwerte der Vorschau", () => {
}
});
});
describe("Bausteine im Editor-Aufbau", () => {
const BAUSTEIN = {
bausteinVersionId: "bv-1",
name: "Datensicherung prüfen",
titel: "Datensicherung",
version: 3,
anzahlSchritte: 2,
};
it("schickt die Platzhalter-Schritte eines Baustein-Abschnitts nicht mit", () => {
/*
* Der Fehler, den erst das Durchklicken des gebauten Stands gezeigt hat
* (#030): die Schritte im Zustand sind nur da, damit die Anzahl in der Karte
* stimmt — sie haben keinen Titel. Mitgeschickt wies `SchrittSchema` den
* ganzen Entwurf ab, und das Speichern scheiterte.
*/
const abschnitt = neuerBausteinAbschnitt(BAUSTEIN);
expect(abschnitt.schritte).toHaveLength(2);
expect(abschnitt.schritte.every((schritt) => schritt.titel === "")).toBe(true);
const struktur = alsStruktur({ variablen: [], abschnitte: [abschnitt] });
expect(struktur.abschnitte[0]?.schritte).toEqual([]);
expect(struktur.abschnitte[0]?.bausteinVersionId).toBe("bv-1");
// Und das Ergebnis muss durch das Schema gehen – genau das tat es vorher nicht.
expect(() => StrukturSchema.parse(struktur)).not.toThrow();
});
it("nimmt den Verweis mit und gibt ihn zurück", () => {
const struktur = StrukturSchema.parse({
variablen: [],
abschnitte: [
{ titel: "Eigen", schritte: [{ titel: "Eins" }] },
{ titel: "x", bausteinVersionId: "bv-1", schritte: [] },
],
});
const aufbau = alsEditorAufbau(struktur);
expect(aufbau.abschnitte[0]?.baustein).toBeUndefined();
expect(aufbau.abschnitte[1]?.baustein?.versionId).toBe("bv-1");
expect(alsStruktur(aufbau).abschnitte[1]?.bausteinVersionId).toBe("bv-1");
});
it("trägt Name und Fassung nach und benennt einen Verweis, der nicht mehr gilt", () => {
const struktur = StrukturSchema.parse({
variablen: [],
abschnitte: [
{ titel: "a", bausteinVersionId: "bv-1", schritte: [] },
{ titel: "b", bausteinVersionId: "bv-weg", schritte: [] },
],
});
const aufbau = bausteineAnreichern(alsEditorAufbau(struktur), [BAUSTEIN]);
expect(aufbau.abschnitte[0]?.baustein).toEqual({
versionId: "bv-1",
name: "Datensicherung prüfen",
version: 3,
});
// Ein Verweis, dessen Baustein nicht mehr einsetzbar ist, behält seine
// Kennung – verschwinden darf er nicht, sonst wäre er beim nächsten
// Speichern still weg.
expect(aufbau.abschnitte[1]?.baustein).toEqual({
versionId: "bv-weg",
name: "(nicht mehr verfügbar)",
version: 0,
});
expect(alsStruktur(aufbau).abschnitte[1]?.bausteinVersionId).toBe("bv-weg");
});
});