diff --git a/gerbil-manager-web/e2e/einstellungen.spec.ts b/gerbil-manager-web/e2e/einstellungen.spec.ts new file mode 100644 index 0000000..ef9bbae --- /dev/null +++ b/gerbil-manager-web/e2e/einstellungen.spec.ts @@ -0,0 +1,20 @@ +/** EXPORT-1: Einstellungen — Datenexport-Karte + Download-Smoke. */ +import { de, expect, gotoSection, test } from './fixtures' + +const t = de.pages.datenexport + +test('Einstellungen zeigt die Datenexport-Karte und der Download startet', async ({ page }) => { + await gotoSection(page, de.nav.settings) + await expect(page.getByRole('heading', { name: de.pages.einstellungen.title })).toBeVisible() + + // Datenexport-Karte mit deutschem Erklärtext + await expect(page.getByRole('heading', { name: t.title })).toBeVisible() + await expect(page.getByText(t.intro)).toBeVisible() + await expect(page.getByText(t.photoNote)).toBeVisible() + + // Klick startet den Zip-Download + const downloadPromise = page.waitForEvent('download') + await page.getByRole('link', { name: t.button }).click() + const download = await downloadPromise + expect(download.suggestedFilename()).toContain('rennmaus-export') +}) diff --git a/gerbil-manager-web/e2e/mock-api.ts b/gerbil-manager-web/e2e/mock-api.ts index 240aaa9..2aea058 100644 --- a/gerbil-manager-web/e2e/mock-api.ts +++ b/gerbil-manager-web/e2e/mock-api.ts @@ -98,11 +98,37 @@ export async function installMockApi(page: Page): Promise { 'weight-records': collection(db.weightRecords as unknown as Row[], 'wr'), } - await page.route(`${API_ORIGIN}/**`, async (route) => { + const handler = async (route: Route) => { const request = route.request() const url = new URL(request.url()) const method = request.method() - const path = url.pathname + // OPS-1: in Produktion ist die API-Basis der relative Pfad /api (nginx-Proxy); + // den Präfix normalisieren, damit der Mock unter beiden Basen funktioniert. + const path = url.pathname.replace(/^\/api(?=\/)/, '') + + // EXPORT-1: Zip-Download (Inhalt egal — der Smoke prüft nur, dass der + // Download startet; ein leeres Zip = End-of-central-directory-Record). + if (path === '/export' && method === 'GET') { + return route.fulfill({ + status: 200, + contentType: 'application/zip', + headers: { 'Content-Disposition': 'attachment; filename="rennmaus-export-e2e.zip"' }, + body: Buffer.from([0x50, 0x4b, 0x05, 0x06, ...new Array(18).fill(0)]), + }) + } + + // FEAT-13: Zuchtprofil (Einstellungen-Seite lädt es vor dem Rendern) + if (path === '/settings/breeder-profile') { + if (method === 'GET') { + return json(route, 200, { + zuchtName: 'Zucht der kleinen Chaoten', + name: 'Frau Erika Muster', + address: 'Musterweg 1, 12345 Musterstadt', + phone: '', email: '', homepage: '', city: 'Musterstadt', + }) + } + if (method === 'PUT') return json(route, 204) + } // Sonderrouten zuerst (FEAT-1b/FEAT-4-Verträge) let m = path.match(/^\/gerbils\/([^/]+)\/photos$/) @@ -157,7 +183,15 @@ export async function installMockApi(page: Page): Promise { return json(route, 204) } return json(route, 405) - }) + } + + // Beide API-Basen abfangen: absolute Dev-URL und /api-relativ (OPS-1-Proxy). + // URL-Prädikat statt Glob: '**/api/**' würde auch Vites Modul-Requests + // (/src/api/client.ts …) treffen und die App selbst kaputt-intercepten. + await page.route( + (url) => url.origin === API_ORIGIN || url.pathname.startsWith('/api/'), + handler, + ) return db }