// @ts-check const { test, expect } = require('@playwright/test') const BENUTZER = process.env.E2E_USER || 'e2e' const PASSWORT = process.env.E2E_PASSWORD || 'e2e-testpasswort' const MARKE = Date.now().toString().slice(-6) async function anmelden(page) { await page.goto('/login') if (page.url().includes('/einrichtung')) { throw new Error( 'Instanz ist noch nicht eingerichtet. Zuerst einen Benutzer anlegen: ' + 'docker compose exec -u www-data php php scripts/e2e-benutzer.php' ) } await page.fill('#benutzername', BENUTZER) await page.fill('#passwort', PASSWORT) await page.click('button[type="submit"]') await expect(page).toHaveURL(/\/$/) } // Formatwahl und Download laufen im Browser ab – der Dialog braucht das Skript, // der Download den Antwortkopf. Beides lässt sich in der PHP-Suite nicht prüfen. test.describe('Import und Export', () => { test.beforeEach(async ({ page }) => { await anmelden(page) }) test('Die Kachel fragt erst nach dem Format und öffnet dann den Dialog', async ({ page }) => { await page.goto('/verwaltung') const kachel = page.locator('.kachel', { hasText: 'Import/Export' }) await expect(kachel.getByRole('link', { name: 'Import', exact: true })).toBeVisible() await expect(kachel.getByRole('link', { name: 'Export', exact: true })).toBeVisible() // Vor dem Klick ist der Auswahldialog zu. const dialog = page.locator('dialog#formatwahl') await expect(dialog).toBeHidden() await kachel.getByRole('link', { name: 'Export', exact: true }).click() await expect(dialog).toBeVisible() await expect(dialog.locator('h2')).toContainText('Export') await dialog.getByRole('link', { name: 'CSV' }).click() await expect(page).toHaveURL(/\/verwaltung\/export\?format=csv$/) await expect(page.locator('h1')).toContainText('Export (CSV)') // Derselbe Weg für den Import – der Dialog merkt sich die Richtung nicht. await page.goto('/verwaltung') await kachel.getByRole('link', { name: 'Import', exact: true }).click() await expect(dialog.locator('h2')).toContainText('Import') await dialog.getByRole('link', { name: 'QIF' }).click() await expect(page).toHaveURL(/\/verwaltung\/import\?format=qif$/) }) test('Ohne Skript führt derselbe Knopf auf die Auswahl als Seite', async ({ page }) => { await page.goto('/verwaltung/export') await expect(page.locator('h2').first()).toContainText('Format wählen') await expect(page.locator('a[href="/verwaltung/export?format=qif"]')).toBeVisible() await expect(page.locator('a[href="/verwaltung/export?format=csv"]')).toBeVisible() }) test('Der gefilterte Export lädt eine Datei herunter', async ({ page }) => { await page.goto('/verwaltung/export?format=csv') await page.fill('input[name="von"]', '2026-01-01') await page.fill('input[name="bis"]', '2026-12-31') const [download] = await Promise.all([ page.waitForEvent('download'), page.getByRole('button', { name: 'CSV herunterladen' }).click(), ]) expect(download.suggestedFilename()).toMatch(/^buchungen-2026-01-01-bis-2026-12-31\.csv$/) // Die Kopfzeile muss stehen, sonst wäre es keine Buchungstabelle. const strom = await download.createReadStream() const teile = [] for await (const stueck of strom) { teile.push(stueck) } expect(Buffer.concat(teile).toString('utf8')).toContain('Buchungstext') }) test('Eine CSV lässt sich hochladen und im Trockenlauf prüfen', async ({ page }) => { const dateiname = `e2e-buchungen-${MARKE}.csv` const konto = `E2E CSV Konto ${MARKE}` // Das Konto muss es geben – Konten legt der CSV-Import nicht an. await page.goto('/konten/neu') await page.fill('#name', konto) await page.click('.formular button[type="submit"]') await expect(page.locator('.meldung-erfolg')).toBeVisible() const inhalt = [ 'Art;Datum;Konto;Empfänger;Buchungstext;Betrag', `Buchung;15.06.2026;${konto};E2E Laden;Testeinkauf ${MARKE};-12,34`, '', ].join('\r\n') await page.goto('/verwaltung/import?format=csv') await page.setInputFiles('#datei', { name: dateiname, mimeType: 'text/csv', buffer: Buffer.from(inhalt, 'utf8'), }) await page.click('.formular button[type="submit"]') await expect(page.locator('.meldung-erfolg')).toContainText('hochgeladen') await page.locator('tr', { hasText: dateiname }) .getByRole('button', { name: 'Trockenlauf' }).click() await expect(page.locator('.kachel h2').first()).toContainText('Trockenlauf') await expect(page.locator('tr', { hasText: 'Neu' }).first()).toContainText('1') // Aufräumen – die Datei bliebe sonst bis zum Housekeeping liegen. await page.goto('/verwaltung/import?format=csv') page.on('dialog', async (dialog) => await dialog.accept()) await page.locator('tr', { hasText: dateiname }) .getByRole('button', { name: 'Löschen' }).click() await expect(page.locator('body')).not.toContainText(dateiname) }) })