Compare commits
2 Commits
feature/ui
...
01781f23d6
| Author | SHA1 | Date | |
|---|---|---|---|
| 01781f23d6 | |||
| e1e088f041 |
90
gerbil-manager-web/e2e/gehege-regression.spec.ts
Normal file
90
gerbil-manager-web/e2e/gehege-regression.spec.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
/**
|
||||
* QA-REGRESSION-1: GEHEGE-RENAME Lock-in.
|
||||
*
|
||||
* Sichert das Verhalten von drei GEHEGE-RENAME-Änderungen ab, die leicht still
|
||||
* brechen können (Redirects, Nav-Einträge, Landing-URL):
|
||||
* (a) /becken/* → /gehege/* Redirect (bookmarks / externe Links)
|
||||
* (b) Nav zeigt 'Gehege' + 🏜️, kein 'Becken', kein 'Start'-Eintrag
|
||||
* (c) '/' (Index) landet auf /rennmaeuse (nicht mehr auf einer Startseite)
|
||||
*
|
||||
* Bereits abgedeckt (NICHT dupliziert):
|
||||
* - gotoSection(de.nav.enclosures) navigiert zu Gehege-Heading: navigation.spec.ts:19-20
|
||||
* - CRUD + 409 auf /gehege: becken-kontakte.spec.ts
|
||||
* - Wurf-Link + Herkunft: tiere.spec.ts:101-128
|
||||
*/
|
||||
import { de, expect, test } from './fixtures'
|
||||
|
||||
// ── (a) /becken/* → /gehege/* Redirect ──────────────────────────────────────
|
||||
|
||||
test('GEHEGE-RENAME: /becken leitet auf /gehege weiter (bookmark-safe)', async ({ page }) => {
|
||||
await page.goto('/becken')
|
||||
// React Router BeckenRedirect navigiert → /gehege; Gehege-Heading muss sichtbar sein
|
||||
await expect(page.getByRole('heading', { name: de.pages.becken.title, exact: true })).toBeVisible()
|
||||
await expect(page).toHaveURL(/\/gehege$/)
|
||||
})
|
||||
|
||||
test('GEHEGE-RENAME: /becken/neu leitet auf /gehege/neu weiter (deep redirect)', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/becken/neu')
|
||||
// Neues-Gehege-Formular muss sichtbar sein, URL muss /gehege/neu sein
|
||||
await expect(page.getByRole('heading', { name: de.pages.becken.form?.createTitle ?? de.pages.becken.newButton })).toBeVisible({ timeout: 6_000 })
|
||||
await expect(page).toHaveURL(/\/gehege\/neu$/)
|
||||
})
|
||||
|
||||
// ── (b) Nav: 'Gehege' ✓, 'Becken' ✗, 'Start' ✗ ──────────────────────────────
|
||||
|
||||
test('GEHEGE-RENAME: Nav hat "Gehege"-Eintrag mit Terrarium-Icon, kein "Becken"', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/rennmaeuse')
|
||||
const nav = page.getByRole('navigation', { name: de.nav.mainNavigation })
|
||||
|
||||
// Gehege-Link soll vorhanden sein (via „Mehr"-Blatt auf Phone oder direkt auf Desktop)
|
||||
// Wir öffnen „Mehr" um sekundäre Links sichtbar zu machen
|
||||
const moreBtn = nav.getByRole('button', { name: de.nav.more })
|
||||
if (await moreBtn.isVisible()) {
|
||||
await moreBtn.click()
|
||||
}
|
||||
await expect(nav.getByRole('link', { name: de.nav.enclosures })).toBeVisible()
|
||||
// "Becken" als separater Nav-Eintrag darf nicht mehr existieren
|
||||
await expect(nav.getByRole('link', { name: 'Becken', exact: true })).toBeHidden()
|
||||
})
|
||||
|
||||
test('GEHEGE-RENAME: Nav hat keinen "Start"-Eintrag mehr (de.nav.home entfernt)', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/rennmaeuse')
|
||||
// Warten bis die Tierliste geladen ist, dann Nav prüfen
|
||||
await expect(page.getByRole('heading', { name: de.pages.gerbils.title, exact: true })).toBeVisible()
|
||||
const nav = page.getByRole('navigation', { name: de.nav.mainNavigation })
|
||||
// 'Start' (de.nav.home wurde entfernt) darf kein Nav-Link mehr sein
|
||||
await expect(nav.getByRole('link', { name: 'Start', exact: true })).toBeHidden()
|
||||
})
|
||||
|
||||
// ── (c) '/' landet auf /rennmaeuse ──────────────────────────────────────────
|
||||
|
||||
test('GEHEGE-RENAME: "/" leitet auf /rennmaeuse weiter — keine Startseite mehr', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/')
|
||||
// URL muss /rennmaeuse sein (Navigate replace)
|
||||
await expect(page).toHaveURL(/\/rennmaeuse$/)
|
||||
// Tierliste-Heading sichtbar — nicht die alte Startseite
|
||||
await expect(
|
||||
page.getByRole('heading', { name: de.pages.gerbils.title, exact: true }),
|
||||
).toBeVisible()
|
||||
// Alten Willkommen-Text darf es nicht mehr geben (de.pages.home wurde entfernt)
|
||||
await expect(page.getByText('Willkommen')).toBeHidden()
|
||||
})
|
||||
|
||||
test('GEHEGE-RENAME: 404-Seite "Zur Startseite" landet weiterhin auf Tierliste', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto('/diese-seite-gibt-es-nicht')
|
||||
await expect(page.getByText(de.pages.notFound.message)).toBeVisible()
|
||||
await page.getByRole('link', { name: de.pages.notFound.backHome }).click()
|
||||
// Nach Klick auf "Zur Startseite" → '/' → redirect → /rennmaeuse
|
||||
await expect(page.getByRole('heading', { name: de.pages.gerbils.title, exact: true })).toBeVisible()
|
||||
await expect(page).toHaveURL(/\/rennmaeuse$/)
|
||||
})
|
||||
@@ -149,8 +149,6 @@ export function seedDb(): MockDb {
|
||||
enclosureId: 'enc-leer',
|
||||
notes: 'neugieriger Entdecker',
|
||||
},
|
||||
// UI-POLISH-1: Import-Stub ohne Namen — testet den '(ohne Namen)'-Platzhalter in Liste + Detail.
|
||||
gerbil('nameless-stub', '', 'male', '2023-01-01', null, null),
|
||||
]
|
||||
|
||||
const litters: Litter[] = [
|
||||
|
||||
@@ -127,19 +127,6 @@ test('Detailseite: Herkunft zeigt Kontaktlink wenn originContactId gesetzt (WURF
|
||||
await expect(originLink).toHaveAttribute('href', '/kontakte/con-meier')
|
||||
})
|
||||
|
||||
test('Namenlose Tiere zeigen Platzhalter in der Liste (UI-POLISH-1)', async ({ page }) => {
|
||||
skipUnlessMock()
|
||||
await page.goto('/rennmaeuse')
|
||||
// 'nameless-stub' hat name='' → Platzhalter '(ohne Namen)' soll als Link sichtbar sein
|
||||
await expect(page.getByRole('link', { name: de.pages.gerbils.nameless })).toBeVisible()
|
||||
})
|
||||
|
||||
test('Namenlose Tiere zeigen Platzhalter im Detail-Header (UI-POLISH-1)', async ({ page }) => {
|
||||
skipUnlessMock()
|
||||
await page.goto('/rennmaeuse/nameless-stub')
|
||||
await expect(page.getByRole('heading', { name: de.pages.gerbils.nameless })).toBeVisible()
|
||||
})
|
||||
|
||||
test('Tier bearbeiten — Notizen ändern', async ({ page }) => {
|
||||
skipUnlessMock()
|
||||
await page.goto('/rennmaeuse/kruemel/bearbeiten')
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { useState } from 'react'
|
||||
import { de } from '../strings/de'
|
||||
|
||||
const nameless = de.pages.gerbils.nameless
|
||||
import { listGerbils } from '../api/gerbils'
|
||||
import { andFilter, condition } from '../api/gridify'
|
||||
import type { Gender, Gerbil, Paged } from '../api/types'
|
||||
@@ -64,7 +62,7 @@ export default function AnimalPicker({ gender, onPick }: AnimalPickerProps) {
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
{g.name || nameless}
|
||||
{g.name}
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
|
||||
@@ -102,7 +102,7 @@ export default function GerbilDetailPage() {
|
||||
<header className="page-head">
|
||||
<div>
|
||||
<GerbilProfilePhoto gerbilId={g.id} />
|
||||
<h2>{g.name || de.pages.gerbils.nameless}</h2>
|
||||
<h2>{g.name}</h2>
|
||||
<span className={`badge badge--${g.status.toLowerCase()}`}>{statusLabel(g.status)}</span>
|
||||
{g.isResident === false && (
|
||||
<span className="badge badge--external" title={de.pages.gerbils.externalTitle}>
|
||||
@@ -153,7 +153,7 @@ export default function GerbilDetailPage() {
|
||||
{g.status === 'Deceased' && (
|
||||
<>
|
||||
<Row label={t.fields.dateOfDeath} value={formatDate(g.dateOfDeath)} />
|
||||
<Row label={t.fields.causeOfDeath} value={g.causeOfDeath || null} />
|
||||
<Row label={t.fields.causeOfDeath} value={g.causeOfDeath} />
|
||||
</>
|
||||
)}
|
||||
{g.status === 'GivenAway' && (
|
||||
@@ -190,7 +190,7 @@ export default function GerbilDetailPage() {
|
||||
}
|
||||
/>
|
||||
<Row label={t.fields.receiver} value={lookup(contactName, g.receiverContactId)} />
|
||||
<Row label={t.fields.notes} value={g.notes || null} />
|
||||
<Row label={t.fields.notes} value={g.notes} />
|
||||
</dl>
|
||||
|
||||
<h3>{t.detail.genetics}</h3>
|
||||
|
||||
@@ -256,11 +256,11 @@ export default function GerbilsPage() {
|
||||
className="gerbil-row__check"
|
||||
checked={selected.has(g.id)}
|
||||
onChange={() => toggleSelect(g.id)}
|
||||
aria-label={g.name || t.nameless}
|
||||
aria-label={g.name}
|
||||
/>
|
||||
<Link to={`/rennmaeuse/${g.id}`} className="gerbil-card">
|
||||
<span className="gerbil-card__name">
|
||||
{g.name || t.nameless}
|
||||
{g.name}
|
||||
{g.isResident === false && (
|
||||
<span className="badge badge--external" title={t.externalTitle}>
|
||||
{t.externalBadge}
|
||||
|
||||
@@ -38,7 +38,6 @@ export const de = {
|
||||
pages: {
|
||||
gerbils: {
|
||||
title: 'Rennmäuse',
|
||||
nameless: '(ohne Namen)',
|
||||
// Liste
|
||||
newButton: 'Neue Rennmaus',
|
||||
empty: 'Keine Rennmäuse gefunden.',
|
||||
|
||||
Reference in New Issue
Block a user