Files
breakpilot-lehrer/admin-lehrer/e2e/fixtures/sdk-fixtures.ts
Benjamin Boenisch 5a31f52310 Initial commit: breakpilot-lehrer - Lehrer KI Platform
Services: Admin-Lehrer, Backend-Lehrer, Studio v2, Website,
Klausur-Service, School-Service, Voice-Service, Geo-Service,
BreakPilot Drive, Agent-Core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 23:47:26 +01:00

242 lines
6.9 KiB
TypeScript

/**
* Playwright Test Fixtures for AI Compliance SDK
*
* These fixtures provide reusable setup for SDK E2E tests.
* Demo data is seeded via the API (same storage path as real data).
*/
import { test as base, expect, Page } from '@playwright/test'
// Selectors for SDK components
export const selectors = {
// Dashboard
dashboard: '[data-testid="sdk-dashboard"]',
loadDemoDataButton: '[data-testid="load-demo-data"]',
progressBar: '[data-testid="progress-bar"]',
// Sidebar
sidebar: '[data-testid="sdk-sidebar"]',
sidebarPhase1: '[data-testid="sidebar-phase-1"]',
sidebarPhase2: '[data-testid="sidebar-phase-2"]',
sidebarStep: (stepId: string) => `[data-testid="sidebar-step-${stepId}"]`,
// Command Bar
commandBar: '[data-testid="command-bar"]',
commandInput: '[data-testid="command-input"]',
commandSuggestion: (index: number) => `[data-testid="command-suggestion-${index}"]`,
// Navigation
prevButton: '[data-testid="prev-step-button"]',
nextButton: '[data-testid="next-step-button"]',
stepHeader: '[data-testid="step-header"]',
// Use Case Workshop
useCaseCard: (index: number) => `[data-testid="use-case-card-${index}"]`,
addUseCaseButton: '[data-testid="add-use-case-button"]',
useCaseForm: '[data-testid="use-case-form"]',
// Risk Matrix
riskCard: (riskId: string) => `[data-testid="risk-card-${riskId}"]`,
riskMatrix: '[data-testid="risk-matrix"]',
addRiskButton: '[data-testid="add-risk-button"]',
// Controls
controlCard: (controlId: string) => `[data-testid="control-card-${controlId}"]`,
controlsGrid: '[data-testid="controls-grid"]',
// Export
exportButton: '[data-testid="export-button"]',
exportPdfButton: '[data-testid="export-pdf-button"]',
exportZipButton: '[data-testid="export-zip-button"]',
}
// Type for SDK test fixtures
type SDKFixtures = {
sdkPage: Page
withDemoData: Page
commandBar: Page
}
/**
* Extended test with SDK-specific fixtures
*/
export const test = base.extend<SDKFixtures>({
// Basic SDK page (navigates to dashboard)
sdkPage: async ({ page }, use) => {
await page.goto('/sdk')
// Wait for page to be ready
await page.waitForLoadState('networkidle')
await use(page)
},
// SDK page with demo data loaded
withDemoData: async ({ page }, use) => {
// Navigate to SDK
await page.goto('/sdk')
await page.waitForLoadState('networkidle')
// Seed demo data via API (same storage path as real data)
await page.evaluate(async () => {
const response = await fetch('/api/sdk/v1/demo/seed', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tenantId: 'e2e-test-tenant' }),
})
if (!response.ok) {
// If endpoint doesn't exist, try seeding via localStorage
// This is a fallback for development
console.warn('Demo seed endpoint not available, using localStorage fallback')
}
})
// Reload page to pick up demo data
await page.reload()
await page.waitForLoadState('networkidle')
await use(page)
// Cleanup: Clear demo data after test
await page.evaluate(async () => {
try {
await fetch('/api/sdk/v1/demo/clear', {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tenantId: 'e2e-test-tenant' }),
})
} catch {
// Ignore cleanup errors
}
})
},
// SDK page with command bar opened
commandBar: async ({ page }, use) => {
await page.goto('/sdk')
await page.waitForLoadState('networkidle')
// Open command bar with keyboard shortcut
await page.keyboard.press('Meta+k')
// Wait for command bar to be visible
await page.waitForSelector(selectors.commandBar, { state: 'visible' })
await use(page)
},
})
// Re-export expect for convenience
export { expect }
/**
* Helper: Wait for SDK to initialize
*/
export async function waitForSDKInit(page: Page): Promise<void> {
await page.waitForLoadState('networkidle')
// Wait for React hydration
await page.waitForFunction(() => {
return document.querySelector('[data-testid="sdk-dashboard"]') !== null ||
document.querySelector('[data-testid="step-header"]') !== null
}, { timeout: 10000 })
}
/**
* Helper: Navigate to a specific SDK step
*/
export async function navigateToStep(page: Page, stepId: string): Promise<void> {
const stepUrl = getStepUrl(stepId)
await page.goto(stepUrl)
await waitForSDKInit(page)
}
/**
* Helper: Get URL for a step
*/
export function getStepUrl(stepId: string): string {
const stepUrls: Record<string, string> = {
'use-case-workshop': '/sdk/advisory-board',
'screening': '/sdk/screening',
'modules': '/sdk/modules',
'requirements': '/sdk/requirements',
'controls': '/sdk/controls',
'evidence': '/sdk/evidence',
'audit-checklist': '/sdk/audit-checklist',
'risks': '/sdk/risks',
'ai-act': '/sdk/ai-act',
'obligations': '/sdk/obligations',
'dsfa': '/sdk/dsfa',
'tom': '/sdk/tom',
'loeschfristen': '/sdk/loeschfristen',
'vvt': '/sdk/vvt',
'consent': '/sdk/consent',
'cookie-banner': '/sdk/cookie-banner',
'einwilligungen': '/sdk/einwilligungen',
'dsr': '/sdk/dsr',
'escalations': '/sdk/escalations',
}
return stepUrls[stepId] || '/sdk'
}
/**
* Helper: Use command bar to navigate
*/
export async function useCommandBarToNavigate(page: Page, searchTerm: string): Promise<void> {
// Open command bar
await page.keyboard.press('Meta+k')
await page.waitForSelector(selectors.commandBar, { state: 'visible' })
// Type search term
await page.fill(selectors.commandInput, searchTerm)
// Wait for suggestions
await page.waitForSelector(selectors.commandSuggestion(0), { timeout: 5000 })
// Click first suggestion
await page.click(selectors.commandSuggestion(0))
// Wait for navigation
await page.waitForLoadState('networkidle')
}
/**
* Helper: Seed demo data programmatically
*/
export async function seedDemoData(page: Page, tenantId: string = 'e2e-test'): Promise<boolean> {
const result = await page.evaluate(async (tid) => {
try {
const response = await fetch('/api/sdk/v1/demo/seed', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tenantId: tid }),
})
return response.ok
} catch {
return false
}
}, tenantId)
if (result) {
await page.reload()
await waitForSDKInit(page)
}
return result
}
/**
* Helper: Clear demo data
*/
export async function clearDemoData(page: Page, tenantId: string = 'e2e-test'): Promise<boolean> {
return await page.evaluate(async (tid) => {
try {
const response = await fetch('/api/sdk/v1/demo/clear', {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tenantId: tid }),
})
return response.ok
} catch {
return false
}
}, tenantId)
}