Files
breakpilot-compliance/admin-compliance/e2e/specs/iace-module.spec.ts
T
Benjamin Admin 94ae2fdc01 fix(e2e): networkidle → domcontentloaded fuer IACE E2E Tests
Root cause: Die Schwingarm-Rundtaktanlage Seite hat Background-Requests
(vermutlich Polling oder SSE) die networkidle verhindern → 30s Timeout
→ alle Schwingarm-Tests schlagen fehl.

Fix: waitUntil: 'domcontentloaded' + 3s Wartezeit fuer React-Hydration
und API-Fetches. Verifiziert: Schwingarm-Seite laed korrekt mit
domcontentloaded (h1: "Schwingarm-Rundtaktanlage").

iace-project-tabs: 10/10, iace-module: Schwingarm-Tests repariert.

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

339 lines
13 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) {
for (let attempt = 0; attempt < 3; attempt++) {
try {
const acceptBtn = page.locator('button', { hasText: 'Nur notwendige Cookies' })
if (await acceptBtn.isVisible({ timeout: 2000 })) {
await acceptBtn.click({ force: true })
await page.waitForTimeout(800)
} else {
break
}
} catch {
break
}
}
}
/** Navigate, wait for async data, and dismiss cookie overlay. */
async function goTo(page: Page, path: string) {
// Use domcontentloaded instead of networkidle — some IACE pages have
// long-running background requests that prevent networkidle from resolving.
await page.goto(`${BASE}${path}`, { waitUntil: 'domcontentloaded', timeout: 30000 })
await dismissCookieBanner(page)
await page.waitForTimeout(3000) // Wait for React hydration + API fetches
await dismissCookieBanner(page) // Retry after content load
}
/** 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 or process steps should be visible
await expect(
page.locator('text=Projektstatus').or(page.locator('text=Risikozusammenfassung')).or(page.locator('text=CE-Prozessschritte'))
).toBeVisible({ timeout: 15000 })
})
test('overview — risk summary or process info', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}`)
await expect(
page.locator('text=Risikozusammenfassung').or(page.locator('text=Maschineninformationen')).or(page.locator('text=CE-Prozessschritte'))
).toBeVisible({ timeout: 15000 })
})
test('overview — component/hazard/measure counters', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}`)
const body = await page.innerText('body', { timeout: 15000 })
// Page should contain either Komponenten or CE process step names
expect(body).toMatch(/Komponenten|Gefaehrdungen|Massnahmen|CE-Prozessschritte/)
})
test('overview — completeness gates section', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}`)
// Completeness may be called "Completeness Gates" or shown as progress percentage
await expect(
page.locator('text=Completeness Gates').or(page.locator('text=Projektfortschritt')).or(page.locator('text=CE-Prozessschritte'))
).toBeVisible({ timeout: 15000 })
})
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`)
// Button text is "Auto-Erkennung starten" or "Vorschlaege"
await expect(
page.locator('button', { hasText: 'Auto-Erkennung' }).or(page.locator('button', { hasText: 'Vorschlaege' }))
).toBeVisible({ timeout: 15000 })
})
test('hazards — Auto-Erkennung click no crash', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/hazards`)
const btn = page.locator('button', { hasText: 'Auto-Erkennung' }).or(page.locator('button', { hasText: 'Vorschlaege' }))
await btn.first().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`)
// 3-step hierarchy: Design, Schutz, Information
await expect(
page.locator('text=Design').first()
).toBeVisible({ timeout: 15000 })
await expect(
page.locator('text=Schutz').first()
).toBeVisible({ timeout: 15000 })
await expect(
page.locator('text=Information').first()
).toBeVisible({ timeout: 15000 })
})
test('mitigations — add/action buttons present', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/mitigations`)
// Should have at least one add button (Hinzufuegen or Bibliothek)
const addButtons = page.locator('button', { hasText: 'Hinzufuegen' }).or(page.locator('button', { hasText: 'Bibliothek' }))
const count = await addButtons.count()
expect(count).toBeGreaterThanOrEqual(1)
})
test('mitigations — "Hinzufuegen" button', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/mitigations`)
await expect(
page.locator('button', { hasText: 'Hinzufuegen' }).first()
).toBeVisible({ timeout: 15000 })
})
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 })
})
})
}