Files
betreuungs-cockpit/e2e/vorlagen.spec.ts
T
Florian Wilde f8d7752c27 Eigene Vorlagen fuer Anschreiben
Wer zum dritten Mal denselben Brief tippt, tippt ihn beim vierten Mal
falsch. Neu unter Einstellungen -> Schreibvorlagen: eigene Texte
hinterlegen, beim freien Schreiben auswaehlen, Platzhalter fuellen sich
aus den Daten, die ohnehin im System stehen.

Aufbau:
- Die Ersetzung als reine Funktionen in schreiben/platzhalter.ts. Drei
  Faelle, keiner davon still: Wert einsetzen, sichtbare Luecke
  [Aktenzeichen des Gerichts], unbekannter Platzhalter markiert. Ein
  Tippfehler in der Vorlage darf das Schreiben nicht verhindern - aber
  er muss ins Auge springen.
- Die Bestellungsabsatz-Warnung ist zweistufig: Der erste Speicherversuch
  ohne ihn kommt mit einer Rueckfrage zurueck, erst die Bestaetigung
  speichert. Ohne den Nachweis der Vertretungsmacht kommt keine Auskunft,
  und das faellt erst nach vier Wochen auf. Ein ausformulierter Absatz
  zaehlt so viel wie der Platzhalter, ein halber Nachweis ("Beschluss der
  Eigentuemerversammlung") nicht.
- Vier mitgelieferte Vorlagen bleiben Code und damit unveraenderlich;
  "Als Kopie uebernehmen" legt eine eigene Fassung an. Fuer Rueckkaufswert
  und Kuendigung fuehrt die Oberflaeche weiter zuerst zu den
  fallbezogenen Erzeugern - die rechnen Kuendigungstermine aus, was eine
  Vorlage nicht kann.
- Vorlagen haengen an der betreuten Person, nicht am Benutzer: Sie
  beziehen sich auf ihren Fall, liegen im Papierkorb wie alles andere und
  sind im Vollexport ihrer Akte enthalten.

Der Editor haelt Betreff und Text als eigenen State. Beim Wechsel der
Vorlage ueberlebte der alte Entwurf die Navigation, die Vorlage kam nie
im Textfeld an - ein key auf dem Editor setzt ihn neu auf. Gefunden hat
das erst der End-to-End-Test.

Geprueft: dreizehn Einheitstests fuer die Ersetzung, sechs
End-to-End-Tests fuer Warnung, Kopie, Aufloesung und Papierkorb.

Refs #7
2026-08-23 13:41:51 +02:00

158 lines
5.8 KiB
TypeScript

import { expect, test } from '@playwright/test'
import { PrismaClient } from '@prisma/client'
import { anmelden } from './hilfen'
/**
* Eigene Schreibvorlagen — gegen die echte Anwendung.
*
* Der Weg über die Oberfläche: Vorlage anlegen (mit der zweistufigen
* Bestellungsabsatz-Warnung), aus einer mitgelieferten kopieren, beim
* Schreiben auswählen und die Platzhalter-Auflösung sehen.
*/
const VORLAGE = 'Prüfhausen Testvorlage'
async function mitDb<T>(arbeit: (db: PrismaClient) => Promise<T>): Promise<T> {
const db = new PrismaClient()
try {
return await arbeit(db)
} finally {
await db.$disconnect()
}
}
async function aufraeumen(): Promise<void> {
await mitDb(async (db) => {
await db.schreibvorlage.deleteMany({ where: { name: { contains: 'Prüfhausen' } } })
await db.schreibvorlage.deleteMany({ where: { name: { contains: '(eigene Fassung)' } } })
})
}
test.describe('Schreibvorlagen', () => {
test.beforeAll(async () => {
await aufraeumen()
})
test.afterAll(async () => {
await aufraeumen()
})
test('warnt ohne Bestellungsabsatz und speichert erst nach Bestätigung', async ({ page }) => {
await anmelden(page)
await page.goto('/einstellungen/vorlagen')
await page.getByRole('button', { name: 'Neue Vorlage' }).click()
await page.locator('input[name="name"]').fill(VORLAGE)
await page
.locator('textarea[name="text"]')
.fill('Sehr geehrte Damen und Herren,\n\nbitte um Auskunft zu {{betreute_name}}.\n\nMfG')
// Erster Versuch: Warnung, nicht gespeichert.
await page.getByRole('button', { name: 'Speichern' }).click()
await expect(page.getByText(/fehlt der Bestellungsabsatz/)).toBeVisible()
// Bestätigen und erneut speichern.
await page.getByText('Bewusst ohne Bestellungsabsatz speichern').click()
await page.getByRole('button', { name: 'Speichern' }).click()
await expect(page.getByText(`„${VORLAGE}“ ist gespeichert.`)).toBeVisible()
})
test('speichert mit Platzhalter ohne Warnung', async ({ page }) => {
await anmelden(page)
await page.goto('/einstellungen/vorlagen')
await page.getByRole('button', { name: 'Neue Vorlage' }).click()
await page.locator('input[name="name"]').fill(`${VORLAGE} mit Absatz`)
await page
.locator('textarea[name="text"]')
.fill('Sehr geehrte Damen und Herren,\n\n{{bestellungsabsatz}}\n\nbitte um Auskunft.\n\nMfG')
await page.getByRole('button', { name: 'Speichern' }).click()
await expect(page.getByText(`„${VORLAGE} mit Absatz“ ist gespeichert.`)).toBeVisible()
})
test('übernimmt eine mitgelieferte Vorlage als Kopie', async ({ page }) => {
await anmelden(page)
await page.goto('/einstellungen/vorlagen')
await page
.locator('li', { hasText: 'Auskunftsersuchen' })
.getByRole('button', { name: 'Als Kopie übernehmen' })
.click()
// Der Editor öffnet mit den kopierten Inhalten.
await expect(page.locator('input[name="name"]')).toHaveValue(/eigene Fassung/)
const text = await page.locator('textarea[name="text"]').inputValue()
expect(text).toContain('{{bestellungsabsatz}}')
await page.getByRole('button', { name: 'Speichern' }).click()
await expect(page.getByText(/ist gespeichert/)).toBeVisible()
})
test('löst die Platzhalter beim Erzeugen auf — mit sichtbaren Lücken', async ({ page }) => {
await anmelden(page)
await page.goto('/schreiben/neu?art=FREI')
// Die gespeicherte Vorlage steht zur Wahl.
const wahl = page.locator('select#vorlageWahl')
await expect(wahl).toBeVisible()
await wahl.selectOption({ label: `${VORLAGE} mit Absatz` })
await page.waitForURL(/vorlageId=/)
const text = await page.locator('textarea#text').inputValue()
// Der Bestellungsabsatz ist aufgelöst (kein Platzhalter mehr) …
expect(text).not.toContain('{{bestellungsabsatz}}')
expect(text).toMatch(/Beschluss|bestellt/)
// … und der Rest des Textes steht drin.
expect(text).toContain('bitte um Auskunft.')
})
test('markiert unbekannte Platzhalter im Text und warnt', async ({ page }) => {
await mitDb(async (db) => {
const betreute = await db.betreute.findMany({
where: { geloeschtAm: null },
select: { id: true },
})
for (const person of betreute) {
await db.schreibvorlage.create({
data: {
betreuteId: person.id,
name: `${VORLAGE} kaputt`,
text: 'Sehr geehrte Damen und Herren,\n\n{{bestellungsabsatz}}\n\nHallo {{gibt_es_nicht}}.',
},
})
}
})
await anmelden(page)
await page.goto('/schreiben/neu?art=FREI')
await page.locator('select#vorlageWahl').selectOption({ label: `${VORLAGE} kaputt` })
await page.waitForURL(/vorlageId=/)
// Markiert im Text, benannt in der Warnung — kein Abbruch.
const text = await page.locator('textarea#text').inputValue()
expect(text).toContain('[Unbekannter Platzhalter: {{gibt_es_nicht}}]')
await expect(page.getByText(/unbekannte Platzhalter/)).toBeVisible()
})
test('gelöschte Vorlage liegt im Papierkorb und kommt zurück', async ({ page }) => {
await anmelden(page)
await page.goto('/einstellungen/vorlagen')
// Zweistufig - Loeschen fragt erst nach.
const eintrag = page.locator('li', { hasText: `${VORLAGE} mit Absatz` })
await eintrag.getByRole('button', { name: 'Löschen' }).click()
await eintrag.getByRole('button', { name: 'In den Papierkorb' }).click()
await expect(page.getByText(`${VORLAGE} mit Absatz`)).toHaveCount(0)
await page.goto('/einstellungen/papierkorb')
const zeile = page.locator('li', { hasText: `${VORLAGE} mit Absatz` })
await expect(zeile).toBeVisible()
await zeile.getByRole('button', { name: 'Wiederherstellen' }).click()
await page.goto('/einstellungen/vorlagen')
await expect(page.getByText(`${VORLAGE} mit Absatz`)).toBeVisible()
})
})