Merge branch 'worktree-agent-ae59040b377a324cb'
# Conflicts: # GerbilManagerWebAPI/ApplicationContext.cs # GerbilManagerWebAPI/Program.cs # gerbil-manager-web/e2e/mock-data.ts # gerbil-manager-web/src/pages/GerbilDetailPage.tsx # gerbil-manager-web/src/strings/de.ts
This commit is contained in:
49
gerbil-manager-web/e2e/ausstellungen.spec.ts
Normal file
49
gerbil-manager-web/e2e/ausstellungen.spec.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
/** EXHIBITION: Ausstellungs-/Show-Ergebnisse je Tier — Tab in der Rennmausakte. */
|
||||
import { de, expect, skipUnlessMock, test } from './fixtures'
|
||||
|
||||
const t = de.pages.tierTabs.exhibitions
|
||||
const ta = de.pages.tierTabs.actions
|
||||
const tabs = de.pages.gerbils.detail.tabs
|
||||
|
||||
test('Rennmausakte: Ausstellungsergebnis anlegen, sehen und löschen', async ({ page, mockDb }) => {
|
||||
skipUnlessMock()
|
||||
await page.goto('/rennmaeuse/kruemel')
|
||||
|
||||
// Auf den "Ausstellungen"-Tab wechseln.
|
||||
await page.getByRole('tab', { name: tabs.exhibitions }).click()
|
||||
|
||||
// Leerzustand sichtbar.
|
||||
await expect(page.getByText(t.empty)).toBeVisible()
|
||||
|
||||
// Neues Ergebnis erfassen.
|
||||
await page.getByRole('button', { name: new RegExp(t.newButton) }).click()
|
||||
await page.getByLabel(new RegExp(t.fields.eventName)).fill('Nationale Rennmausschau 2026')
|
||||
await page.getByLabel(t.fields.placement).fill('1. Platz')
|
||||
await page.getByLabel(t.fields.award).fill('Best in Show')
|
||||
await page.getByRole('button', { name: ta.save, exact: true }).click()
|
||||
|
||||
// Ergebnis erscheint in der Liste.
|
||||
await expect(page.getByText('Nationale Rennmausschau 2026')).toBeVisible()
|
||||
await expect(page.getByText('1. Platz')).toBeVisible()
|
||||
|
||||
// Wurde im Mock mit Tier-Bezug gespeichert.
|
||||
expect(mockDb).not.toBeNull()
|
||||
const row = mockDb!.exhibitions.find((r) => r.eventName === 'Nationale Rennmausschau 2026')
|
||||
expect(row).toMatchObject({ gerbilId: 'kruemel', placement: '1. Platz', award: 'Best in Show' })
|
||||
|
||||
// Löschen (window.confirm automatisch annehmen).
|
||||
page.once('dialog', (d) => void d.accept())
|
||||
await page.getByRole('button', { name: ta.delete }).click()
|
||||
await expect(page.getByText(t.empty)).toBeVisible()
|
||||
})
|
||||
|
||||
test('Rennmausakte: leere Veranstaltung wird abgelehnt (Validierung)', async ({ page }) => {
|
||||
skipUnlessMock()
|
||||
await page.goto('/rennmaeuse/kruemel')
|
||||
await page.getByRole('tab', { name: tabs.exhibitions }).click()
|
||||
await page.getByRole('button', { name: new RegExp(t.newButton) }).click()
|
||||
|
||||
// Ohne Veranstaltung speichern → Validierungsfehler, kein Eintrag.
|
||||
await page.getByRole('button', { name: ta.save, exact: true }).click()
|
||||
await expect(page.getByText(t.validation.eventNameRequired)).toBeVisible()
|
||||
})
|
||||
@@ -742,6 +742,59 @@ export async function installMockApi(page: Page): Promise<MockDb> {
|
||||
return json(route, 405)
|
||||
}
|
||||
|
||||
// EXHIBITION: Ausstellungs-/Show-Ergebnisse je Tier (POST/GET ?gerbilId=, PUT, DELETE).
|
||||
// Plain-Liste (kein Gridify), gefiltert per ?gerbilId, neueste zuerst.
|
||||
if (path === '/exhibitions') {
|
||||
if (method === 'GET') {
|
||||
const gerbilId = url.searchParams.get('gerbilId')
|
||||
const rows = (db.exhibitions as Row[]).filter(
|
||||
(r) => !gerbilId || r.gerbilId === gerbilId,
|
||||
)
|
||||
const sorted = [...rows].sort((a, b) =>
|
||||
String(b.date ?? '').localeCompare(String(a.date ?? '')),
|
||||
)
|
||||
return json(route, 200, sorted)
|
||||
}
|
||||
if (method === 'POST') {
|
||||
const body = request.postDataJSON() as Row
|
||||
if (!String(body.eventName ?? '').trim())
|
||||
return json(route, 400, 'EventName darf nicht leer sein.')
|
||||
const created = {
|
||||
id: newId('exhibition'),
|
||||
gerbilId: body.gerbilId ?? null,
|
||||
entityName: body.entityName ?? null,
|
||||
eventName: String(body.eventName).trim(),
|
||||
date: body.date ?? null,
|
||||
placement: body.placement ?? null,
|
||||
award: body.award ?? null,
|
||||
note: body.note ?? null,
|
||||
createdAt: new Date().toISOString(),
|
||||
}
|
||||
;(db.exhibitions as Row[]).push(created)
|
||||
return json(route, 201, created)
|
||||
}
|
||||
return json(route, 405)
|
||||
}
|
||||
const em = path.match(/^\/exhibitions\/([^/]+)$/)
|
||||
if (em) {
|
||||
const eid = decodeURIComponent(em[1])
|
||||
const idx = (db.exhibitions as Row[]).findIndex((r) => r.id === eid)
|
||||
if (idx < 0) return json(route, 404, { title: 'Not Found' })
|
||||
if (method === 'GET') return json(route, 200, db.exhibitions[idx])
|
||||
if (method === 'PUT') {
|
||||
const body = request.postDataJSON() as Row
|
||||
if ('eventName' in body && !String(body.eventName ?? '').trim())
|
||||
return json(route, 400, 'EventName darf nicht leer sein.')
|
||||
Object.assign(db.exhibitions[idx], body)
|
||||
return json(route, 200, db.exhibitions[idx])
|
||||
}
|
||||
if (method === 'DELETE') {
|
||||
;(db.exhibitions as Row[]).splice(idx, 1)
|
||||
return json(route, 204)
|
||||
}
|
||||
return json(route, 405)
|
||||
}
|
||||
|
||||
// Generische Kollektionen: /<resource> und /<resource>/<id>
|
||||
m = path.match(/^\/([a-z-]+)(?:\/([^/]+))?$/)
|
||||
const col = m ? collections[m[1]] : undefined
|
||||
|
||||
@@ -93,6 +93,8 @@ export interface MockDb {
|
||||
waitingList: Record<string, unknown>[]
|
||||
// RÜCKNAHMEN: zurückgenommene Tiere (CRUD /returns)
|
||||
returns: Record<string, unknown>[]
|
||||
// EXHIBITION: Ausstellungs-/Show-Ergebnisse je Tier (POST /exhibitions)
|
||||
exhibitions: Record<string, unknown>[]
|
||||
}
|
||||
|
||||
function gerbil(
|
||||
@@ -620,5 +622,6 @@ export function seedDb(): MockDb {
|
||||
},
|
||||
],
|
||||
returns: [],
|
||||
exhibitions: [],
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user