// @vitest-environment node import { afterAll, describe, expect, it } from 'vitest' import { db } from '@/lib/db' import { parseTemplateFields } from '@/lib/templates/schema' import { dossierRows, dossierSpan, isInputField } from '@/lib/templates/fields' /** * Das Aktenblatt der Personalabteilung. * * Der erste Test ist der wichtigere: `parseTemplateFields` gibt bei einer * ungültigen Vorlage eine **leere Liste** zurück, statt zu werfen. Ein Tippfehler * im Seed würde das Blatt also lautlos leeren, statt aufzufallen. */ const url = process.env.DIRECT_DATABASE_URL ?? process.env.DATABASE_URL const suite = url ? describe : describe.skip suite('Vorlage des Aktenblatts', () => { afterAll(async () => { await db.$disconnect() }) async function felder() { const vorlage = await db.documentTemplate.findFirst({ where: { key: 'personalakte', isActive: true }, orderBy: { version: 'desc' }, select: { fields: true, layout: true, uses: true, sheetTitle: true, sheetSubtitle: true }, }) expect(vorlage).not.toBeNull() return { vorlage: vorlage!, fields: parseTemplateFields(vorlage!.fields) } } it('übersteht die verbindliche Prüfung vollständig', async () => { const { vorlage, fields } = await felder() // Kein stilles Leerlaufen: die Zahl muss stimmen, nicht nur "grösser null". expect(fields).toHaveLength( (vorlage.fields as unknown[]).length, ) expect(vorlage.layout).toBe('DOSSIER') expect(vorlage.uses).toEqual(['PERSON_DOCUMENT']) expect(vorlage.sheetSubtitle).toBeTruthy() // Die Felder, die das Blatt ausmachen. const schluessel = fields.map((f) => f.key) for (const key of [ 'lichtbild', 'name', 'vorname', 'alter', 'geschlecht', 'groesse', 'abteilung', 'dienstgrad', 'sicherheitsfreigabe', 'wissensfreigabe', 'konfession', 'adresse', 'anmerkungen', 'personalarbeiter', 'standortleiter', ]) { expect(schluessel).toContain(key) } }) it('ergibt genau das Raster des Formulars', async () => { const { fields } = await felder() const { photo, photoRows, rows } = dossierRows(fields) expect(photo?.key).toBe('lichtbild') expect(photoRows).toBe(4) // Neben dem Lichtbild vier Zeilen zu je vier Spalten, danach fünf. const struktur = rows.map((row, index) => row.map((field) => `${field.key}:${dossierSpan(field, index < photoRows)}`), ) expect(struktur).toEqual([ ['name:2', 'vorname:2'], ['alter:2', 'geschlecht:2'], ['groesse:4'], ['abteilung:2', 'dienstgrad:2'], ['sicherheitsfreigabe:3', 'wissensfreigabe:2'], ['konfession:2', 'adresse:3'], ['anmerkungen:5'], ['personalarbeiter:3', 'standortleiter:2'], ]) }) it('lässt die Datumsfelder der Unterschriften aus dem Raster heraus', async () => { const { fields } = await felder() const { rows } = dossierRows(fields) const imRaster = rows.flat().map((f) => f.key) // Sie stehen unter der jeweiligen Unterschrift, nicht in einer eigenen // Zelle - bearbeitbar bleiben sie trotzdem. expect(imRaster).not.toContain('personalarbeiter_datum') expect(imRaster).not.toContain('standortleiter_datum') expect(fields.map((f) => f.key)).toContain('personalarbeiter_datum') }) it('hält die Klassifikation aus dem Raster heraus, sie gehört in den Balken', async () => { const { fields } = await felder() const { rows } = dossierRows(fields) expect(rows.flat().map((f) => f.key)).not.toContain('foundation_klassifikation') const feld = fields.find((f) => f.key === 'foundation_klassifikation') expect(feld && isInputField(feld) && feld.slot).toBe('classbar') }) it('kennt für jede Klassifikationsstufe eine Farbe', async () => { const { fields } = await felder() const feld = fields.find((f) => f.key === 'foundation_klassifikation') const stufen = await db.foundationClearance.findMany({ orderBy: { level: 'asc' } }) // Sonst bliebe der Balken bei einer gültigen Auswahl grau. const optionen = feld && isInputField(feld) ? (feld.options ?? []) : [] for (const option of optionen) { expect(stufen.map((s) => String(s.level))).toContain(option) } for (const stufe of stufen) { expect(stufe.colorHex).toMatch(/^#[0-9a-f]{6}$/i) } }) })