import { test, expect, Page } from '@playwright/test' /** * IACE Erweiterungen 2-5 — E2E Tests * FMEA Worksheet, Delta Analysis, Knowledge Graph * * Run with: * npx playwright test e2e/specs/iace-extensions.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' }, { id: 'a4c4031e-75a5-461e-a575-159f1eabd6b3', name: 'EIGENBAU-Zelle (Cobot)' }, { id: 'c43af8df-14e0-43ff-b26f-ab425f803e53', name: 'Gleichstrom-/Asynchronmotor' }, { id: '3e0808b2-2eed-4e82-b35d-6dd6857bc379', name: 'Schwingarm-Rundtaktanlage' }, ] as const async function dismissCookieBanner(page: Page) { 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) } } catch { /* not present */ } } async function goTo(page: Page, path: string) { await page.goto(`${BASE}${path}`, { waitUntil: 'domcontentloaded', timeout: 30000 }) await dismissCookieBanner(page) try { await page.locator('h1').first().waitFor({ state: 'visible', timeout: 15000 }) } catch { /* ignore */ } await page.waitForTimeout(2000) await dismissCookieBanner(page) } 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. FMEA Worksheet (Erweiterung 2) // --------------------------------------------------------------------------- for (const project of PROJECTS) { test.describe(`FMEA: ${project.name}`, () => { test.setTimeout(60_000) test('page loads', async ({ page }) => { await goTo(page, `/sdk/iace/${project.id}/fmea`) await assertNoAppError(page) await expect(page.locator('h1')).toContainText('FMEA', { timeout: 15000 }) }) test('stats cards visible', async ({ page }) => { await goTo(page, `/sdk/iace/${project.id}/fmea`) const body = await page.innerText('body') expect(body).toContain('Gesamt') expect(body).toContain('RPZ') }) test('table or empty state visible', async ({ page }) => { await goTo(page, `/sdk/iace/${project.id}/fmea`) await page.waitForTimeout(5000) const body = await page.innerText('body') const hasTable = body.includes('Fehlerart') || body.includes('Komponente') const hasEmpty = body.includes('Keine Failure Modes') expect(hasTable || hasEmpty).toBeTruthy() }) test('RPZ threshold info visible', async ({ page }) => { await goTo(page, `/sdk/iace/${project.id}/fmea`) const body = await page.innerText('body') expect(body).toContain('Handlungsbedarf') expect(body).toContain('Akzeptabel') }) }) } // --------------------------------------------------------------------------- // 2. Knowledge Graph (Erweiterung 5) // --------------------------------------------------------------------------- test.describe('Knowledge Graph', () => { test.setTimeout(60_000) test('page loads', async ({ page }) => { await goTo(page, `/sdk/iace/${PROJECTS[0].id}/knowledge-graph`) await assertNoAppError(page) await expect(page.locator('h1')).toContainText('Knowledge Graph', { timeout: 15000 }) }) test('legend shows 3 node types', async ({ page }) => { await goTo(page, `/sdk/iace/${PROJECTS[0].id}/knowledge-graph`) const body = await page.innerText('body') expect(body).toContain('Komponente') expect(body).toContain('Gefaehrdung') expect(body).toContain('Massnahme') }) for (const project of PROJECTS) { test(`${project.name} — loads without error`, async ({ page }) => { await goTo(page, `/sdk/iace/${project.id}/knowledge-graph`) await assertNoAppError(page) }) } }) // --------------------------------------------------------------------------- // 3. Sidebar — FMEA + Knowledge Graph entries // --------------------------------------------------------------------------- test.describe('Sidebar Extensions', () => { test.setTimeout(60_000) test('FMEA nav entry visible', async ({ page }) => { await goTo(page, `/sdk/iace/${PROJECTS[0].id}`) await expect(page.locator('a', { hasText: 'FMEA' })).toBeVisible({ timeout: 10000 }) }) test('Knowledge Graph page accessible', async ({ page }) => { await goTo(page, `/sdk/iace/${PROJECTS[0].id}/knowledge-graph`) await page.waitForTimeout(5000) const body = await page.innerText('body') expect(body.includes('Knowledge Graph') || body.includes('Komponente')).toBeTruthy() }) }) // --------------------------------------------------------------------------- // 4. Delta Analysis API — returns valid structure // --------------------------------------------------------------------------- test.describe('Delta Analysis API', () => { test.setTimeout(60_000) const API = 'https://macmini:8093/sdk/v1/iace' const HEADERS = { 'Content-Type': 'application/json', 'X-Tenant-Id': '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e' } test('returns valid structure with component tags', async ({ request }) => { const tags = ['moving_mechanical_parts', 'electric_motor', 'controller', 'sensor'] const res = await request.post(`${API}/projects/${PROJECTS[0].id}/delta-analysis`, { headers: HEADERS, data: { current: { component_library_ids: tags, custom_tags: tags, operational_states: [] }, proposed: { component_library_ids: tags, custom_tags: tags, operational_states: ['automatic_operation', 'maintenance'] }, }, }) expect(res.ok()).toBeTruthy() const data = await res.json() expect(data).toHaveProperty('added_patterns') expect(data).toHaveProperty('removed_patterns') }) test('empty input returns empty result', async ({ request }) => { const res = await request.post(`${API}/projects/${PROJECTS[0].id}/delta-analysis`, { headers: HEADERS, data: { current: { component_library_ids: [], operational_states: [] }, proposed: { component_library_ids: [], operational_states: ['maintenance'] }, }, }) expect(res.ok()).toBeTruthy() }) }) // --------------------------------------------------------------------------- // 5. Failure Modes API — returns entries per component type // --------------------------------------------------------------------------- test.describe('Failure Modes API', () => { test.setTimeout(30_000) const API = 'https://macmini:8093/sdk/v1/iace' const HEADERS = { 'X-Tenant-Id': '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e' } test('GET /failure-modes returns 150 FMs', async ({ request }) => { const res = await request.get(`${API}/failure-modes`, { headers: HEADERS }) expect(res.ok()).toBeTruthy() const data = await res.json() expect(data.total).toBeGreaterThanOrEqual(150) }) test('filter by sensor type', async ({ request }) => { const res = await request.get(`${API}/failure-modes?component_type=sensor`, { headers: HEADERS }) expect(res.ok()).toBeTruthy() const data = await res.json() expect(data.total).toBeGreaterThan(0) for (const fm of data.failure_modes) { expect(fm.component_type).toBe('sensor') } }) }) // --------------------------------------------------------------------------- // 6. Operational States — delta produces non-zero results // --------------------------------------------------------------------------- test.describe('Op States Delta — with tags', () => { test.setTimeout(60_000) test.describe.configure({ retries: 1 }) test('delta section visible on page', async ({ page }) => { await goTo(page, `/sdk/iace/${PROJECTS[1].id}/operational-states`) await page.waitForTimeout(8000) const body = await page.innerText('body') expect(body.includes('Delta-Vorschau') || body.includes('Delta berechnen')).toBeTruthy() }) })