Files
planer/e2e/aufgaben.spec.ts
T
Florian WildeandClaude Opus 5 144457b9f0 fix(aufgaben): Ideen aus Aufgabenliste und Inbox heraushalten
Der Fehler fiel erst in den aufgenommenen Bildern auf: Ideen standen
mitten in der Aufgabenliste, obwohl der ganze Sinn der getrennten
Ideenseite ist, dass sie das nicht tun. Ohne Termin hätten sie dort
entweder unsichtbar unten gestanden oder mit erfundenem Termin oben.

Der Filter kennt jetzt ohneIdeen; ein ausdrücklich gewählter Typ gewinnt,
wer nach Ideen filtert, sieht sie also weiterhin.

Die Startseite filterte Ideen bisher von Hand nach dem Laden — das
erledigt jetzt dieselbe Abfrage wie überall sonst.

Ein E2E-Test hält den Fall fest.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-10 16:33:33 +02:00

183 lines
7.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { PrismaPg } from '@prisma/adapter-pg'
import { expect, test, type Page } from '@playwright/test'
import { PrismaClient } from '../src/generated/prisma/client.js'
/**
* Der Weg, der in M2 wirklich zählt: erfassen, einsortieren, abhaken.
*
* Alle Testdaten tragen ein gemeinsames Präfix und werden am Ende entfernt,
* damit die Entwicklungsdatenbank hinterher aussieht wie vorher.
*/
const EMAIL = process.env.ADMIN_EMAIL ?? ''
const PASSWORT = process.env.ADMIN_PASSWORT ?? ''
const PRAEFIX = `E2E-${Date.now()}`
const verbindung = process.env.DATABASE_URL ?? ''
const db = new PrismaClient({ adapter: new PrismaPg({ connectionString: verbindung }) })
test.describe('Aufgaben', () => {
test.skip(
!EMAIL || !PASSWORT || !verbindung,
'Ohne ADMIN_EMAIL, ADMIN_PASSWORT und DATABASE_URL gibt es kein Testkonto.',
)
test.afterAll(async () => {
await db.aufgabe.deleteMany({ where: { titel: { startsWith: PRAEFIX } } })
await db.projekt.deleteMany({ where: { name: { startsWith: PRAEFIX } } })
await db.auditEintrag.deleteMany({ where: { aktion: { startsWith: 'aufgabe.' } } })
await db.$disconnect()
})
async function anmelden(seite: Page): Promise<void> {
await seite.goto('/anmelden')
await seite.getByLabel('E-Mail-Adresse').fill(EMAIL)
await seite.getByLabel('Passwort').fill(PASSWORT)
await seite.getByRole('button', { name: 'Mit Passwort anmelden' }).click()
await expect(seite.getByRole('heading', { name: /Guten Tag/ })).toBeVisible()
}
test('erfasst schnell, sortiert ein und hakt ab', async ({ page }) => {
const titel = `${PRAEFIX} Angebot schreiben`
await anmelden(page)
// --- Erfassen: ein Feld genügt --------------------------------------
await page.goto('/inbox')
await page.getByLabel('Neue Aufgabe').fill(titel)
await page.getByRole('button', { name: 'Erfassen' }).click()
const zeile = page.locator('li').filter({ hasText: titel })
await expect(zeile).toBeVisible()
// Ohne Bereich liegt sie in der Inbox – genau das ist der Unterschied.
await expect(zeile.getByText('Inbox')).toBeVisible()
// --- Einsortieren ----------------------------------------------------
const bereich = await db.bereich.findFirst({
where: { aktiv: true },
orderBy: { reihenfolge: 'asc' },
select: { id: true, name: true },
})
expect(bereich).not.toBeNull()
await zeile.getByLabel('Einsortieren nach').selectOption(bereich!.id)
await expect(page.getByText(`liegt jetzt in ${bereich!.name}`)).toBeVisible()
// Aus der Inbox ist sie damit verschwunden.
await page.goto('/inbox')
await expect(page.locator('li').filter({ hasText: titel })).toHaveCount(0)
// --- Abhaken ---------------------------------------------------------
await page.goto('/aufgaben')
const inListe = page.locator('li').filter({ hasText: titel })
await expect(inListe).toBeVisible()
await inListe.getByRole('button', { name: 'Als erledigt abhaken' }).click()
// Erledigtes fällt aus der Standardansicht heraus.
await page.goto('/aufgaben')
await expect(page.locator('li').filter({ hasText: titel })).toHaveCount(0)
// Mit Filter "Alle" ist sie wieder da.
await page.goto('/aufgaben?status=ERLEDIGT')
await expect(page.locator('li').filter({ hasText: titel })).toBeVisible()
const gespeichert = await db.aufgabe.findFirst({ where: { titel } })
expect(gespeichert?.status).toBe('ERLEDIGT')
expect(gespeichert?.erledigtAm).not.toBeNull()
})
test('macht aus einer Idee eine Aufgabe', async ({ page }) => {
const titel = `${PRAEFIX} Blog aufsetzen`
await anmelden(page)
await page.goto('/ideen')
await page.getByLabel('Neue Idee').fill(titel)
await page.getByRole('button', { name: 'Erfassen' }).click()
const zeile = page.locator('li').filter({ hasText: titel })
await expect(zeile).toBeVisible()
await zeile.getByRole('button', { name: 'Zur Aufgabe machen' }).click()
await expect(page.getByText('ist jetzt eine Aufgabe')).toBeVisible()
const danach = await db.aufgabe.findFirst({ where: { titel } })
expect(danach?.typ).toBe('AUFGABE')
// Aus der Ideenliste ist sie damit verschwunden.
await page.goto('/ideen')
await expect(page.locator('li').filter({ hasText: titel })).toHaveCount(0)
})
test('hält Ideen aus der Aufgabenliste heraus', async ({ page }) => {
const idee = `${PRAEFIX} Nur eine Idee`
const aufgabe = `${PRAEFIX} Eine echte Aufgabe`
await db.aufgabe.createMany({
data: [
{ titel: idee, typ: 'IDEE' },
{ titel: aufgabe, typ: 'AUFGABE' },
],
})
await anmelden(page)
// In der Aufgabenliste hat die Idee nichts zu suchen.
await page.goto('/aufgaben')
await expect(page.locator('li').filter({ hasText: aufgabe })).toBeVisible()
await expect(page.locator('li').filter({ hasText: idee })).toHaveCount(0)
// Auch nicht in der Inbox, obwohl beide keinen Bereich haben.
await page.goto('/inbox')
await expect(page.locator('li').filter({ hasText: aufgabe })).toBeVisible()
await expect(page.locator('li').filter({ hasText: idee })).toHaveCount(0)
// Wer ausdrücklich danach filtert, sieht sie sehr wohl.
await page.goto('/aufgaben?typ=IDEE')
await expect(page.locator('li').filter({ hasText: idee })).toBeVisible()
await expect(page.locator('li').filter({ hasText: aufgabe })).toHaveCount(0)
})
test('filtert nach Bereich und über die Adresszeile', async ({ page }) => {
await anmelden(page)
const bereich = await db.bereich.findFirst({
where: { aktiv: true },
select: { id: true, name: true },
})
const drin = `${PRAEFIX} Gehört dazu`
const draussen = `${PRAEFIX} Gehört nicht dazu`
await db.aufgabe.createMany({
data: [
{ titel: drin, bereichId: bereich!.id },
{ titel: draussen, bereichId: null },
],
})
await page.goto(`/aufgaben?bereich=${bereich!.id}`)
await expect(page.locator('li').filter({ hasText: drin })).toBeVisible()
await expect(page.locator('li').filter({ hasText: draussen })).toHaveCount(0)
// Nur Inbox zeigt genau die andere.
await page.goto('/aufgaben?bereich=inbox')
await expect(page.locator('li').filter({ hasText: draussen })).toBeVisible()
await expect(page.locator('li').filter({ hasText: drin })).toHaveCount(0)
})
test('zeigt den Fortschritt eines Projekts', async ({ page }) => {
const projektName = `${PRAEFIX} Website neu`
const projekt = await db.projekt.create({ data: { name: projektName } })
await db.aufgabe.createMany({
data: [
{ titel: `${PRAEFIX} Entwurf`, projektId: projekt.id, status: 'ERLEDIGT' },
{ titel: `${PRAEFIX} Umsetzung`, projektId: projekt.id, status: 'OFFEN' },
// Verworfenes zählt bewusst gar nicht mit.
{ titel: `${PRAEFIX} Verworfen`, projektId: projekt.id, status: 'VERWORFEN' },
],
})
await anmelden(page)
await page.goto(`/projekte/${projekt.id}`)
await expect(page.getByRole('heading', { name: projektName })).toBeVisible()
await expect(page.getByText('1 von 2 erledigt (50 %)')).toBeVisible()
})
})