Files
breakpilot-compliance/admin-compliance/e2e/specs/iace-module.spec.ts
T
Benjamin Admin e7f2f98da3 feat: IACE CE-Compliance Module — Normen, Risikobewertung, Production Lines
Major features:
- 215 norms library with section references + Beuth URLs (A/B1/B2/C norms)
- 173 hazard patterns with detail fields (scenario, trigger, harm, zone)
- Deterministic pattern matching: Component × Lifecycle × Pattern cross-product
- SIL/PL auto-calculation from S×E×P risk graph
- Risk assessment table with editable S/E/P dropdowns
- Production Line Dashboard with animated station flow (Running Dots)
- IACE process flow + norms coverage on start page
- Non-blocking cookie banner, ProcessFlow SSR fix
- 104 Playwright E2E tests passing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-07 10:53:26 +02:00

329 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, expect, Page } from '@playwright/test'
/**
* IACE (CE-Compliance) Module — Comprehensive E2E Tests
*
* Tests all 4 seeded projects across every tab:
* Overview, Components, Hazards, Mitigations, Verification, Evidence, Tech-File, Monitoring.
*
* Run with:
* npx playwright test e2e/specs/iace-module.spec.ts --config e2e/playwright-live.config.ts --reporter=list
*/
const BASE = 'https://macmini:3007'
const PROJECTS = [
{
id: 'bb7d5b88-469d-401f-a0e3-ae5b867e4a1c',
name: 'Kniehebelpresse HP-500',
expectedComps: 14,
expectedHazards: 8,
expectedMeasures: 20,
},
{
id: 'a4c4031e-75a5-461e-a575-159f1eabd6b3',
name: 'EIGENBAU-Zelle (Cobot)',
expectedComps: 7,
expectedHazards: 8,
expectedMeasures: 26,
},
{
id: 'c43af8df-14e0-43ff-b26f-ab425f803e53',
name: 'Gleichstrom-/Asynchronmotor',
expectedComps: 6,
expectedHazards: 6,
expectedMeasures: 16,
},
{
id: '3e0808b2-2eed-4e82-b35d-6dd6857bc379',
name: 'Schwingarm-Rundtaktanlage',
expectedComps: 7,
expectedHazards: 10,
expectedMeasures: 38,
},
] as const
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/** Dismiss the cookie consent banner if present (blocks all clicks). */
async function dismissCookieBanner(page: Page) {
try {
const acceptBtn = page.locator('button', { hasText: 'Nur notwendige Cookies' })
if (await acceptBtn.isVisible({ timeout: 3000 })) {
await acceptBtn.click({ force: true })
await page.waitForTimeout(500)
}
} catch {
// Banner not present or already dismissed
}
}
/** Navigate, wait for async data, and dismiss cookie overlay. */
async function goTo(page: Page, path: string) {
await page.goto(`${BASE}${path}`, { waitUntil: 'networkidle', timeout: 30000 })
await page.waitForTimeout(2000)
await dismissCookieBanner(page)
}
/** Assert that no Next.js / React error overlay is present. */
async function assertNoAppError(page: Page) {
const body = await page.textContent('body')
expect(body).not.toContain('Application error')
expect(body).not.toContain('Unhandled Runtime Error')
}
// ---------------------------------------------------------------------------
// 1. IACE Start Page (/sdk/iace)
// ---------------------------------------------------------------------------
test.describe('IACE Start Page', () => {
test.setTimeout(60_000)
test('page loads without error', async ({ page }) => {
await goTo(page, '/sdk/iace')
await assertNoAppError(page)
// The page title (h1) should contain CE-Compliance
const body = await page.innerText('body')
expect(body).toContain('CE-Compliance (IACE)')
})
test('page description text visible', async ({ page }) => {
await goTo(page, '/sdk/iace')
const body = await page.innerText('body')
expect(body).toContain('Industrial AI Compliance Engine')
})
test('create project button visible', async ({ page }) => {
await goTo(page, '/sdk/iace')
await expect(
page.locator('button', { hasText: 'Neues Projekt erstellen' })
).toBeVisible({ timeout: 10000 })
})
test('sidebar navigation has IACE link', async ({ page }) => {
await goTo(page, '/sdk/iace')
// The SDK sidebar should have "CE-Compliance (IACE)" as a link
const body = await page.innerText('body')
expect(body).toContain('CE-Compliance (IACE)')
})
})
// ---------------------------------------------------------------------------
// 29. Per-project tests
// ---------------------------------------------------------------------------
for (const project of PROJECTS) {
test.describe(`Project: ${project.name}`, () => {
test.setTimeout(60_000)
// ------ Overview ------
test('overview page loads', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}`)
await assertNoAppError(page)
await expect(page.locator('h1')).toContainText(project.name, { timeout: 15000 })
})
test('overview — status workflow visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}`)
// Status workflow or risk summary should be visible
await expect(
page.locator('text=Projektstatus').or(page.locator('text=Risikozusammenfassung'))
).toBeVisible({ timeout: 10000 })
})
test('overview — risk summary section', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}`)
await expect(
page.locator('text=Risikozusammenfassung')
).toBeVisible({ timeout: 10000 })
})
test('overview — component/hazard/measure counters', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}`)
// The risk summary card has three counters
const body = await page.innerText('body')
expect(body).toContain('Komponenten')
expect(body).toContain('Gefaehrdungen')
})
test('overview — completeness gates section', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}`)
await expect(
page.locator('text=Completeness Gates')
).toBeVisible({ timeout: 10000 })
})
test('overview — quick actions present', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}`)
await expect(page.locator('text=Schnellzugriff')).toBeVisible({ timeout: 10000 })
await expect(page.locator('text=Komponenten verwalten')).toBeVisible({ timeout: 10000 })
await expect(page.locator('text=Hazard Log oeffnen')).toBeVisible({ timeout: 10000 })
})
test('overview — IACE sidebar navigation visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}`)
// Sidebar with nav items (may be in aside or nav element)
await expect(
page.locator('text=Uebersicht').or(page.locator('text=Hazard Log')).first()
).toBeVisible({ timeout: 10000 })
})
test('overview — no crash from norms API', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}`)
await assertNoAppError(page)
})
// ------ Components ------
test('components tab loads', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/components`)
await assertNoAppError(page)
await expect(page.locator('h1')).toContainText('Komponenten', { timeout: 10000 })
})
test('components — "Aus Bibliothek waehlen" button', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/components`)
await expect(
page.locator('button', { hasText: 'Aus Bibliothek waehlen' })
).toBeVisible({ timeout: 10000 })
})
test('components — "Komponente hinzufuegen" button', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/components`)
await expect(
page.locator('button', { hasText: 'Komponente hinzufuegen' })
).toBeVisible({ timeout: 10000 })
})
// ------ Hazards ------
test('hazards tab loads', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/hazards`)
await assertNoAppError(page)
await expect(page.locator('h1')).toContainText('Hazard Log', { timeout: 10000 })
})
test('hazards — view toggle exists', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/hazards`)
await expect(
page.locator('button', { hasText: 'Hazard-Liste' })
).toBeVisible({ timeout: 10000 })
await expect(
page.locator('button', { hasText: 'Risikobewertung' })
).toBeVisible({ timeout: 10000 })
})
test('hazards — switch to risk assessment view', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/hazards`)
// Click the "Risikobewertung" toggle
await page.locator('button', { hasText: 'Risikobewertung' }).click()
// Wait for the RiskAssessmentTable to render (fetches mitigations)
await page.waitForTimeout(3000)
await assertNoAppError(page)
// Risk assessment table renders <select> elements (S/E/P/A dropdowns)
const selects = page.locator('select')
await expect(selects.first()).toBeVisible({ timeout: 15000 })
const selectCount = await selects.count()
expect(selectCount).toBeGreaterThan(0)
})
test('hazards — Auto-Erkennung button visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/hazards`)
await expect(
page.locator('button', { hasText: 'Auto-Erkennung' })
).toBeVisible({ timeout: 10000 })
})
test('hazards — Auto-Erkennung click no crash', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/hazards`)
await page.locator('button', { hasText: 'Auto-Erkennung' }).click()
await page.waitForTimeout(5000)
await assertNoAppError(page)
})
test('hazards — risk level stats visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/hazards`)
await expect(
page.locator('text=Gesamt').first()
).toBeVisible({ timeout: 10000 })
})
// ------ Mitigations ------
test('mitigations tab loads', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/mitigations`)
await assertNoAppError(page)
await expect(page.locator('h1')).toContainText('Massnahmen', { timeout: 10000 })
})
test('mitigations — 3-column layout visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/mitigations`)
await expect(page.locator('text=Stufe 1: Design')).toBeVisible({ timeout: 10000 })
await expect(page.locator('text=Stufe 2: Schutz')).toBeVisible({ timeout: 10000 })
await expect(page.locator('text=Stufe 3: Information')).toBeVisible({ timeout: 10000 })
})
test('mitigations — column descriptions visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/mitigations`)
// Check for any of the 3-step hierarchy descriptions
await expect(
page.locator('text=Inhaerent sichere Konstruktion').or(page.locator('text=Stufe 1'))
).toBeVisible({ timeout: 10000 })
await expect(
page.locator('text=Technische Schutzmassnahmen').or(page.locator('text=Stufe 2'))
).toBeVisible({ timeout: 10000 })
await expect(
page.locator('text=Hinweise und Schulungen')
).toBeVisible({ timeout: 10000 })
})
test('mitigations — add buttons per column', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/mitigations`)
const addButtons = page.locator('button', { hasText: '+ Hinzufuegen' })
const count = await addButtons.count()
expect(count).toBe(3)
})
test('mitigations — "Massnahme hinzufuegen" button', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/mitigations`)
await expect(
page.locator('button', { hasText: 'Massnahme hinzufuegen' })
).toBeVisible({ timeout: 10000 })
})
test('mitigations — "Bibliothek" button', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/mitigations`)
await expect(
page.locator('button', { hasText: 'Bibliothek' }).first()
).toBeVisible({ timeout: 10000 })
})
// ------ Verification ------
test('verification tab loads', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/verification`)
await assertNoAppError(page)
await expect(page.locator('h1')).toContainText('Verifikationsplan', { timeout: 10000 })
})
// ------ Evidence ------
test('evidence tab loads', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/evidence`)
await assertNoAppError(page)
await expect(page.locator('h1')).toContainText('Nachweise', { timeout: 10000 })
})
// ------ Tech File ------
test('tech-file tab loads', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/tech-file`)
await assertNoAppError(page)
await expect(page.locator('h1')).toContainText('CE-Akte', { timeout: 10000 })
})
// ------ Monitoring ------
test('monitoring tab loads', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/monitoring`)
await assertNoAppError(page)
await expect(page.locator('h1')).toContainText('Monitoring', { timeout: 10000 })
})
})
}