test(team): Render-Harness, 151 neue Tests und README
Vorher gab es im Repo kein Muster für Komponententests — @testing-library/react wurde ausschliesslich für renderHook verwendet. test/teamTestUtils.tsx stellt den fehlenden Kontext bereit (Router, MUI-Theme, frischer QueryClient je Test). test/setup.ts registriert jetzt afterEach(cleanup). vitest.config.ts setzt `globals` nicht, deshalb erkennt Testing Library kein globales afterEach und richtet sein automatisches Cleanup nie ein; ohne diese Zeile stapeln sich gerenderte Komponenten und getByRole scheitert ab dem zweiten Test einer Datei. Abgedeckt: Kennzahlenberechnung, Freigabe-, Zurückweisungs-, Anpassungs- und Rückfrageprozess, lokale Persistenz samt Schemaversion und Demo-Reset, Badge-Familie, Vorgangs- und Agentenkarte, Aufgaben-Aktivierung, Einstellungsvalidierung und die Position des Navigationseintrags. Die Tests haben fünf echte Mängel aufgedeckt, alle in diesem Commit behoben: - AgentAvatar hatte keinen zugänglichen Namen. MUI reicht `alt` nur an den img-Slot weiter, und ohne `src` rendert Avatar gar kein <img> — der Wert landete nirgends im DOM. Jetzt role="img" + aria-label direkt am Element. - WorkItemCard verschluckte objectLabel, wenn keine objectId gesetzt war. Genau dort steht bei zuordnungsfreien Vorgängen die entscheidende Einordnung. - aria-current rendert nicht mehr "false", sondern entfällt. - AgentChannelBadge war auf `string` statt AgentChannelType typisiert. 414 Tests grün (vorher 263), Build grün, ESLint über den Property-On-Code sauber, check:tokens unverändert bei 2249. README ersetzt das unveränderte Vite-Template durch Startanleitung und die Architekturentscheidungen samt bekannter Altlasten. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
78e2dab655
commit
a7e05fb7f0
6 files changed
+539
-64
No files matched your search
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* Property On — lokale Persistenz und «Demo zurücksetzen» (§16.2, §20.1).
|
||||
*
|
||||
* Die Zusicherung lautet: geänderte Einstellungen, Aufgabenschalter, erledigte
|
||||
* Vorgänge, Verbindungsstatus und pausierte Mitarbeiter überleben einen Reload —
|
||||
* und «Demo zurücksetzen» stellt exakt den Auslieferungszustand wieder her.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach } from 'vitest'
|
||||
import {
|
||||
TEAM_STORAGE_KEYS,
|
||||
TEAM_SCHEMA_VERSION,
|
||||
loadPersisted,
|
||||
loadVersioned,
|
||||
persist,
|
||||
persistVersioned,
|
||||
clearTeamPersistence,
|
||||
} from '../teamPersistence'
|
||||
import { MockupTeamAgentProvider } from '../MockupTeamAgentProvider'
|
||||
import { MockupAgentWorkItemProvider } from '../MockupAgentWorkItemProvider'
|
||||
import { teamDemoService } from '../../services/teamDemoService'
|
||||
import { AgentStatus } from '../../domain/teamAgent'
|
||||
import { mockTeamAgents } from '../../mock-data/teamAgents'
|
||||
|
||||
beforeEach(async () => {
|
||||
clearTeamPersistence()
|
||||
await MockupTeamAgentProvider.reset()
|
||||
await MockupAgentWorkItemProvider.reset()
|
||||
})
|
||||
|
||||
describe('Speicherschicht', () => {
|
||||
it('schreibt und liest einen Bestand', () => {
|
||||
persist(TEAM_STORAGE_KEYS.AGENTS, { hello: 'welt' })
|
||||
expect(loadPersisted<{ hello: string }>(TEAM_STORAGE_KEYS.AGENTS)).toEqual({ hello: 'welt' })
|
||||
})
|
||||
|
||||
it('gibt null zurück, wenn nichts gespeichert ist', () => {
|
||||
expect(loadPersisted(TEAM_STORAGE_KEYS.PROTOCOL)).toBeNull()
|
||||
})
|
||||
|
||||
it('verschluckt unlesbaren Inhalt, statt die Anwendung zu blockieren', () => {
|
||||
window.localStorage.setItem(TEAM_STORAGE_KEYS.AGENTS, '{ kein gültiges JSON')
|
||||
expect(loadPersisted(TEAM_STORAGE_KEYS.AGENTS)).toBeNull()
|
||||
})
|
||||
|
||||
it('verwirft Bestände einer fremden Schemaversion', () => {
|
||||
window.localStorage.setItem(
|
||||
TEAM_STORAGE_KEYS.AGENTS,
|
||||
JSON.stringify({ version: TEAM_SCHEMA_VERSION + 99, data: [{ id: 'veraltet' }] }),
|
||||
)
|
||||
expect(loadVersioned(TEAM_STORAGE_KEYS.AGENTS)).toBeNull()
|
||||
})
|
||||
|
||||
it('liest einen Bestand der aktuellen Schemaversion', () => {
|
||||
persistVersioned(TEAM_STORAGE_KEYS.CONNECTIONS, [{ id: 'verbindung-1' }])
|
||||
expect(loadVersioned<Array<{ id: string }>>(TEAM_STORAGE_KEYS.CONNECTIONS)).toEqual([
|
||||
{ id: 'verbindung-1' },
|
||||
])
|
||||
})
|
||||
|
||||
it('löscht sämtliche Property-On-Schlüssel', () => {
|
||||
for (const key of Object.values(TEAM_STORAGE_KEYS)) persist(key, { a: 1 })
|
||||
clearTeamPersistence()
|
||||
for (const key of Object.values(TEAM_STORAGE_KEYS)) {
|
||||
expect(loadPersisted(key)).toBeNull()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('Persistenz der Mitarbeiter', () => {
|
||||
it('schreibt einen pausierten Mitarbeiter in den Local Storage', async () => {
|
||||
const target = mockTeamAgents[0]
|
||||
await MockupTeamAgentProvider.update(target.id, { status: AgentStatus.PAUSED })
|
||||
|
||||
const stored = loadVersioned<Array<{ id: string; status: string }>>(TEAM_STORAGE_KEYS.AGENTS)
|
||||
expect(stored).not.toBeNull()
|
||||
expect(stored?.find(a => a.id === target.id)?.status).toBe(AgentStatus.PAUSED)
|
||||
})
|
||||
|
||||
it('lässt die übrigen Mitarbeiter unangetastet', async () => {
|
||||
const target = mockTeamAgents[0]
|
||||
await MockupTeamAgentProvider.update(target.id, { status: AgentStatus.PAUSED })
|
||||
|
||||
const all = await MockupTeamAgentProvider.getAll()
|
||||
const others = all.filter(a => a.id !== target.id)
|
||||
expect(others.every(a => a.status === AgentStatus.ACTIVE)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Demo zurücksetzen', () => {
|
||||
it('verwirft lokale Änderungen und stellt die Mockdaten wieder her', async () => {
|
||||
const target = mockTeamAgents[0]
|
||||
await MockupTeamAgentProvider.update(target.id, { status: AgentStatus.PAUSED })
|
||||
expect((await MockupTeamAgentProvider.getById(target.id))?.status).toBe(AgentStatus.PAUSED)
|
||||
|
||||
await teamDemoService.reset()
|
||||
|
||||
expect((await MockupTeamAgentProvider.getById(target.id))?.status).toBe(AgentStatus.ACTIVE)
|
||||
for (const key of Object.values(TEAM_STORAGE_KEYS)) {
|
||||
expect(loadPersisted(key)).toBeNull()
|
||||
}
|
||||
})
|
||||
|
||||
it('stellt auch die Vorgänge wieder her', async () => {
|
||||
const { 0: first } = await MockupAgentWorkItemProvider.getAll({ requiresDecision: true })
|
||||
await MockupAgentWorkItemProvider.update(first.id, { requiresDecision: false })
|
||||
|
||||
const afterEdit = await MockupAgentWorkItemProvider.getAll({ requiresDecision: true })
|
||||
expect(afterEdit.map(i => i.id)).not.toContain(first.id)
|
||||
|
||||
await teamDemoService.reset()
|
||||
|
||||
const afterReset = await MockupAgentWorkItemProvider.getAll({ requiresDecision: true })
|
||||
expect(afterReset.map(i => i.id)).toContain(first.id)
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user