Files
breakpilot-compliance/admin-compliance/e2e/specs/iace-module.spec.ts
T
Benjamin Admin ac624f2e9b feat: Umfassende Playwright-Tests fuer alle IACE Features
Order, Grenzen, Compliance Alerts, Risk Assessment, Mitigations,
CE-Akte Export, Production Lines, Normenrecherche — alle getestet.

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

701 lines
29 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,
* Order, Interview (Grenzen & Verwendung), Compliance Alerts, Risk Assessment Table,
* CE-Akte Export, Production Lines, Normenrecherche.
*
* 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
/** The Cobot project ID — used for compliance alerts checks. */
const COBOT_PROJECT_ID = 'a4c4031e-75a5-461e-a575-159f1eabd6b3'
/** Seeded production line ID. */
const PRODUCTION_LINE_ID = 'c63b774e-22d4-4045-bb8d-646df626c42b'
// ---------------------------------------------------------------------------
// 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 (existing tabs + new features)
// ---------------------------------------------------------------------------
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)
})
// ------ Overview: Compliance Alerts ------
test('overview — compliance alerts section visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}`)
// ComplianceAlerts renders only when triggers > 0.
// Wait for content to settle, then check for either the alerts header or
// the absence of an error (some projects may have 0 triggers).
await page.waitForTimeout(3000)
await assertNoAppError(page)
const body = await page.innerText('body')
// At least one of these should be present on the overview page:
// The alerts section OR the norms section OR the quick actions section
const hasAlerts = body.includes('Compliance-Hinweise erkannt')
const hasNorms = body.includes('Normenrecherche')
const hasQuick = body.includes('Schnellzugriff')
expect(hasAlerts || hasNorms || hasQuick).toBeTruthy()
})
test('overview — regulation badges visible when alerts present', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}`)
await page.waitForTimeout(3000)
const body = await page.innerText('body')
if (body.includes('Compliance-Hinweise erkannt')) {
// Regulation badges should be rendered (DSGVO, AI Act, CRA, NIS2, Data Act)
const hasBadge = body.includes('DSGVO') || body.includes('AI Act') || body.includes('CRA')
expect(hasBadge).toBeTruthy()
}
})
// ------ Overview: Normenrecherche ------
test('overview — normenrecherche section visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}`)
await page.waitForTimeout(3000)
const body = await page.innerText('body')
// SuggestedNorms renders with the total count — verify it shows "relevante Normen"
if (body.includes('Normenrecherche')) {
expect(body).toContain('relevante Normen')
}
})
test('overview — norm add field visible when norms section open', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}`)
await page.waitForTimeout(3000)
// The SuggestedNorms section has a custom norm input with placeholder "z.B. ISO 13857:2019"
const addInput = page.locator('input[placeholder*="ISO 13857"]')
if (await addInput.count() > 0) {
await expect(addInput.first()).toBeVisible({ timeout: 10000 })
}
})
// ------ 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 })
})
// ------ Order ------
test('order tab loads', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/order`)
await assertNoAppError(page)
await expect(page.locator('h1')).toContainText('Auftrag', { timeout: 15000 })
})
test('order — form fields visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/order`)
// The Auftraggeber section with Firmenname and Ansprechpartner
await expect(page.locator('text=Auftraggeber')).toBeVisible({ timeout: 10000 })
await expect(page.locator('label:has-text("Firmenname")')).toBeVisible({ timeout: 10000 })
await expect(page.locator('label:has-text("Ansprechpartner")')).toBeVisible({ timeout: 10000 })
await expect(page.locator('label:has-text("E-Mail")')).toBeVisible({ timeout: 10000 })
await expect(page.locator('label:has-text("Telefon")')).toBeVisible({ timeout: 10000 })
})
test('order — status dropdown works', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/order`)
// The Angebot section has a status select with options
const statusSelect = page.locator('select')
await expect(statusSelect.first()).toBeVisible({ timeout: 10000 })
// Verify dropdown has the expected options
const options = statusSelect.first().locator('option')
const count = await options.count()
expect(count).toBeGreaterThanOrEqual(3) // offen, angenommen, abgelehnt, storniert
})
test('order — Auftrag and Angebot sections visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/order`)
await expect(page.locator('text=Auftrag').first()).toBeVisible({ timeout: 10000 })
await expect(page.locator('text=Angebot').first()).toBeVisible({ timeout: 10000 })
await expect(page.locator('text=Notizen')).toBeVisible({ timeout: 10000 })
})
test('order — scope checkboxes visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/order`)
// SCOPE_OPTIONS: Risikobeurteilung, Normenrecherche, Betriebsanleitung, CE-Kennzeichnung, Schulung
await expect(page.locator('text=Risikobeurteilung').first()).toBeVisible({ timeout: 10000 })
await expect(page.locator('text=CE-Kennzeichnung')).toBeVisible({ timeout: 10000 })
})
// ------ Interview (Grenzen & Verwendung) ------
test('interview tab loads', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/interview`)
await assertNoAppError(page)
await expect(page.locator('h1')).toContainText('Grenzen & Verwendung', { timeout: 15000 })
})
test('interview — form sections visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/interview`)
// The 6 collapsible section headers
await expect(page.locator('text=Allgemeine Produktbeschreibung')).toBeVisible({ timeout: 10000 })
await expect(page.locator('text=Bestimmungsgemasse Verwendung')).toBeVisible({ timeout: 10000 })
await expect(page.locator('text=Vorhersehbare Fehlanwendung')).toBeVisible({ timeout: 10000 })
await expect(page.locator('text=Grenzen der Maschine')).toBeVisible({ timeout: 10000 })
await expect(page.locator('text=Schnittstellen')).toBeVisible({ timeout: 10000 })
await expect(page.locator('text=Betroffene Personen')).toBeVisible({ timeout: 10000 })
})
test('interview — pre-filled data visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/interview`)
// First section is open by default and shows pre-filled machine name
await page.waitForTimeout(2000)
const body = await page.innerText('body')
// The machine name from the project should appear somewhere in the form
// (either in the input or in a "Vorausgefuellt" help text)
const hasProjectName = body.includes(project.name) || body.includes('Vorausgefuellt')
expect(hasProjectName).toBeTruthy()
})
test('interview — section collapse/expand works', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/interview`)
// "Bestimmungsgemasse Verwendung" section is collapsed by default
// Click to expand it
const sectionBtn = page.locator('button', { hasText: 'Bestimmungsgemasse Verwendung' })
await expect(sectionBtn).toBeVisible({ timeout: 10000 })
await sectionBtn.click()
await page.waitForTimeout(500)
// After expanding, we should see "Verwendungszweck" label inside
await expect(page.locator('text=Verwendungszweck')).toBeVisible({ timeout: 10000 })
// Click again to collapse
await sectionBtn.click()
await page.waitForTimeout(500)
// Content should no longer be visible
await expect(page.locator('label:has-text("Verwendungszweck")')).not.toBeVisible({ timeout: 3000 })
})
test('interview — completion badge visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/interview`)
// CompletionBadge shows "X% ausgefuellt"
await expect(page.locator('text=ausgefuellt')).toBeVisible({ timeout: 10000 })
})
test('interview — navigation buttons present', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/interview`)
await expect(page.locator('text=Zurueck zur Uebersicht')).toBeVisible({ timeout: 10000 })
await expect(page.locator('text=Weiter zu Komponenten')).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 — default view is Risikobewertung', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/hazards`)
// The "Risikobewertung" button should be the active one (has bg-purple-600)
const riskBtn = page.locator('button', { hasText: 'Risikobewertung' })
await expect(riskBtn).toBeVisible({ timeout: 10000 })
// Default state is 'risk', so the RiskAssessmentTable should be rendered
// Wait for it to load
await page.waitForTimeout(2000)
// Check that the risk assessment table header is visible
const body = await page.innerText('body')
expect(body).toContain('Risikobewertungstabelle')
})
test('hazards — S/E/P dropdowns visible with values > 1', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/hazards`)
// Default view is risk assessment — wait for table
await page.waitForTimeout(3000)
// RiskAssessmentTable renders <select> elements for S/E/P
const selects = page.locator('select')
await expect(selects.first()).toBeVisible({ timeout: 15000 })
const selectCount = await selects.count()
expect(selectCount).toBeGreaterThan(0)
// Check that at least one select has a value > 1
const firstValue = await selects.first().inputValue()
const numValue = parseInt(firstValue, 10)
expect(numValue).toBeGreaterThanOrEqual(1)
})
test('hazards — "Gefaehrdungen erkennen" button visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/hazards`)
await expect(
page.locator('button', { hasText: 'Gefaehrdungen erkennen' })
).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 accordion sections 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 — checkbox selection works', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/mitigations`)
await page.waitForTimeout(2000)
// Find the first checkbox in the mitigations table rows
const checkboxes = page.locator('input[type="checkbox"]')
const count = await checkboxes.count()
if (count > 0) {
// Click the first non-header checkbox to select an item
await checkboxes.first().click()
await page.waitForTimeout(500)
// After selecting, batch action buttons should appear: "ausgewaehlt" text
const body = await page.innerText('body')
expect(body).toContain('ausgewaehlt')
}
})
test('mitigations — batch buttons appear when items selected', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/mitigations`)
await page.waitForTimeout(2000)
const checkboxes = page.locator('input[type="checkbox"]')
const count = await checkboxes.count()
if (count > 0) {
await checkboxes.first().click()
await page.waitForTimeout(500)
// Batch action buttons: Verifizieren and Loeschen
await expect(
page.locator('button', { hasText: 'Verifizieren' })
).toBeVisible({ timeout: 10000 })
await expect(
page.locator('button', { hasText: 'Loeschen' })
).toBeVisible({ timeout: 10000 })
}
})
test('mitigations — add buttons visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/mitigations`)
await expect(
page.locator('button', { hasText: '+ 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 })
})
test('tech-file — "PDF exportieren" button visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/tech-file`)
await expect(
page.locator('button', { hasText: 'PDF exportieren' })
).toBeVisible({ timeout: 10000 })
})
test('tech-file — "Excel exportieren" button visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/tech-file`)
await expect(
page.locator('button', { hasText: 'Excel exportieren' })
).toBeVisible({ timeout: 10000 })
})
test('tech-file — progress bar and section list visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${project.id}/tech-file`)
await page.waitForTimeout(2000)
const body = await page.innerText('body')
// Progress section always renders
expect(body).toContain('Fortschritt')
// Either sections are listed or the empty state shows
const hasSections = body.includes('Generieren') || body.includes('Keine Abschnitte vorhanden')
expect(hasSections).toBeTruthy()
})
// ------ 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 })
})
})
}
// ---------------------------------------------------------------------------
// 10. Compliance Alerts — Cobot project specific
// ---------------------------------------------------------------------------
test.describe('Compliance Alerts — Cobot Project', () => {
test.setTimeout(60_000)
test('compliance alerts show trigger count > 0', async ({ page }) => {
await goTo(page, `/sdk/iace/${COBOT_PROJECT_ID}`)
await page.waitForTimeout(4000)
// ComplianceAlerts shows "X Compliance-Hinweise erkannt"
const alertsHeader = page.locator('text=Compliance-Hinweise erkannt')
if (await alertsHeader.isVisible({ timeout: 10000 })) {
const headerText = await alertsHeader.innerText()
// Extract the count from "X Compliance-Hinweise erkannt"
const match = headerText.match(/(\d+)/)
expect(match).not.toBeNull()
if (match) {
expect(parseInt(match[1], 10)).toBeGreaterThan(0)
}
}
})
test('compliance alerts — regulation badges visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${COBOT_PROJECT_ID}`)
await page.waitForTimeout(4000)
const body = await page.innerText('body')
if (body.includes('Compliance-Hinweise erkannt')) {
// At least some of: DSGVO, AI Act, CRA, NIS2, Data Act
const hasDSGVO = body.includes('DSGVO')
const hasAIAct = body.includes('AI Act')
const hasCRA = body.includes('CRA')
expect(hasDSGVO || hasAIAct || hasCRA).toBeTruthy()
}
})
})
// ---------------------------------------------------------------------------
// 11. Production Lines
// ---------------------------------------------------------------------------
test.describe('Production Lines', () => {
test.setTimeout(60_000)
test('lines list page loads', async ({ page }) => {
await goTo(page, '/sdk/iace/lines')
await assertNoAppError(page)
await expect(page.locator('h1')).toContainText('Produktionslinien', { timeout: 15000 })
})
test('lines — "Neue Produktionslinie" button visible', async ({ page }) => {
await goTo(page, '/sdk/iace/lines')
await expect(
page.locator('button', { hasText: 'Neue Produktionslinie' })
).toBeVisible({ timeout: 10000 })
})
test('lines — "Fertigungsstrasse Halle 3" visible', async ({ page }) => {
await goTo(page, '/sdk/iace/lines')
await page.waitForTimeout(3000)
const body = await page.innerText('body')
// The seeded line should appear in the list
expect(body).toContain('Fertigungsstrasse Halle 3')
})
test('line dashboard loads with stations', async ({ page }) => {
await goTo(page, `/sdk/iace/lines/${PRODUCTION_LINE_ID}`)
await assertNoAppError(page)
await page.waitForTimeout(3000)
// The dashboard should show "Stationsuebersicht" heading
await expect(
page.locator('text=Stationsuebersicht')
).toBeVisible({ timeout: 15000 })
})
test('line dashboard — station cards rendered', async ({ page }) => {
await goTo(page, `/sdk/iace/lines/${PRODUCTION_LINE_ID}`)
await page.waitForTimeout(3000)
// The dashboard renders StationCard components with project machine names
// At least one station should be present
const body = await page.innerText('body')
expect(body).toContain('Stationsuebersicht')
// Check that we have "Alle Produktionslinien" back link
await expect(
page.locator('text=Alle Produktionslinien')
).toBeVisible({ timeout: 10000 })
})
test('line dashboard — back link to lines list', async ({ page }) => {
await goTo(page, `/sdk/iace/lines/${PRODUCTION_LINE_ID}`)
const backLink = page.locator('a', { hasText: 'Alle Produktionslinien' })
await expect(backLink).toBeVisible({ timeout: 10000 })
})
})
// ---------------------------------------------------------------------------
// 12. Normenrecherche — large norm count
// ---------------------------------------------------------------------------
test.describe('Normenrecherche — Cobot Project', () => {
test.setTimeout(60_000)
test('normenrecherche shows large norm count', async ({ page }) => {
await goTo(page, `/sdk/iace/${COBOT_PROJECT_ID}`)
await page.waitForTimeout(4000)
const body = await page.innerText('body')
// SuggestedNorms shows "Normenrecherche — X relevante Normen"
if (body.includes('Normenrecherche')) {
const match = body.match(/Normenrecherche\s*[—-]\s*(\d+)\s*relevante Normen/)
if (match) {
const normCount = parseInt(match[1], 10)
// Should be a substantial number (not just 215 from the old fallback)
expect(normCount).toBeGreaterThan(100)
}
}
})
test('normenrecherche — add norm input visible', async ({ page }) => {
await goTo(page, `/sdk/iace/${COBOT_PROJECT_ID}`)
await page.waitForTimeout(4000)
// The "Weitere Norm ergaenzen" section has an input with ISO placeholder
const addInput = page.locator('input[placeholder*="ISO 13857"]')
if (await addInput.count() > 0) {
await expect(addInput.first()).toBeVisible({ timeout: 10000 })
}
})
})