Merge feature/export-1: Datenexport (zip JSON+German CSVs) on /einstellungen + CI flake-cap [god-QA pending result-gate]
# Conflicts: # GerbilManager.Tests/GerbilManager.Tests.csproj # GerbilManagerWebAPI/Program.cs # gerbil-manager-web/src/App.tsx # gerbil-manager-web/src/components/AppShell.tsx # gerbil-manager-web/src/pages/EinstellungenPage.tsx
This commit is contained in:
20
gerbil-manager-web/e2e/einstellungen.spec.ts
Normal file
20
gerbil-manager-web/e2e/einstellungen.spec.ts
Normal file
@@ -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')
|
||||
})
|
||||
@@ -98,11 +98,37 @@ export async function installMockApi(page: Page): Promise<MockDb> {
|
||||
'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<MockDb> {
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user