This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/studio-v2/e2e/korrektur-archiv.spec.ts
Benjamin Admin 21a844cb8a fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.

This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).

Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 09:51:32 +01:00

392 lines
14 KiB
TypeScript

import { test, expect } from '@playwright/test'
/**
* E2E Tests for /korrektur/archiv
*
* Tests the Abitur-Archiv functionality:
* - Page loads correctly
* - Search and filters work
* - Document cards are displayed
* - Preview modal opens/closes
* - "Verwenden" modal works
* - Navigation works
*/
test.describe('Korrektur Archiv', () => {
test.beforeEach(async ({ page }) => {
// Navigate to the archiv page
await page.goto('/korrektur/archiv')
// Wait for the page to be fully loaded
await page.waitForLoadState('networkidle')
})
// ==========================================================================
// PAGE LOAD TESTS
// ==========================================================================
test('page loads with correct title', async ({ page }) => {
// Check page title/header
await expect(page.getByRole('heading', { name: 'Abitur-Archiv' })).toBeVisible()
})
test('page shows subtitle', async ({ page }) => {
await expect(page.getByText('Zentralabitur-Materialien 2021-2025 durchsuchen')).toBeVisible()
})
test('back button is visible', async ({ page }) => {
// The back arrow button in the header
const backButton = page.locator('button').filter({ has: page.locator('svg path[d*="15 19l-7-7"]') }).first()
await expect(backButton).toBeVisible()
})
// ==========================================================================
// SEARCH TESTS
// ==========================================================================
test('search input is visible and functional', async ({ page }) => {
const searchInput = page.getByPlaceholder('Thema suchen')
await expect(searchInput).toBeVisible()
// Type a search query
await searchInput.fill('Kafka')
await expect(searchInput).toHaveValue('Kafka')
})
test('popular theme tags are displayed', async ({ page }) => {
// Check that popular theme tags are visible
await expect(page.getByRole('button', { name: 'Textanalyse' })).toBeVisible()
await expect(page.getByRole('button', { name: 'Gedichtanalyse' })).toBeVisible()
await expect(page.getByRole('button', { name: 'Eroerterung' })).toBeVisible()
})
test('clicking theme tag updates search', async ({ page }) => {
const searchInput = page.getByPlaceholder('Thema suchen')
const themeTag = page.getByRole('button', { name: 'Textanalyse' })
await themeTag.click()
// Search input should now have the theme
await expect(searchInput).toHaveValue('Textanalyse')
})
test('clear search button works', async ({ page }) => {
const searchInput = page.getByPlaceholder('Thema suchen')
// Type something
await searchInput.fill('Test')
// Find and click the clear button (X icon)
const clearButton = page.locator('button').filter({ has: page.locator('svg path[d*="6 18L18 6"]') }).first()
await clearButton.click()
// Search should be empty
await expect(searchInput).toHaveValue('')
})
// ==========================================================================
// FILTER TESTS
// ==========================================================================
test('filter dropdowns are visible', async ({ page }) => {
// All 5 filters should be present
await expect(page.getByLabel('Fach')).toBeVisible()
await expect(page.getByLabel('Jahr')).toBeVisible()
await expect(page.getByLabel('Bundesland')).toBeVisible()
await expect(page.getByLabel('Niveau')).toBeVisible()
await expect(page.getByLabel('Typ')).toBeVisible()
})
test('filter dropdown changes value', async ({ page }) => {
const fachFilter = page.getByLabel('Fach')
// Select a specific value (lowercase as returned by API)
await fachFilter.selectOption('deutsch')
await expect(fachFilter).toHaveValue('deutsch')
})
test('filter reset button appears when filters active', async ({ page }) => {
const fachFilter = page.getByLabel('Fach')
// Initially, reset button should not be visible (or show 0)
const resetButton = page.getByRole('button', { name: /Filter zuruecksetzen/ })
// Select a filter (lowercase as returned by API)
await fachFilter.selectOption('deutsch')
// Reset button should now be visible with count
await expect(resetButton).toBeVisible()
await expect(resetButton).toContainText('1')
})
test('filter reset button clears all filters', async ({ page }) => {
const fachFilter = page.getByLabel('Fach')
const jahrFilter = page.getByLabel('Jahr')
// Set multiple filters (lowercase as returned by API)
await fachFilter.selectOption('deutsch')
await jahrFilter.selectOption('2024')
// Click reset
const resetButton = page.getByRole('button', { name: /Filter zuruecksetzen/ })
await resetButton.click()
// Filters should be reset
await expect(fachFilter).toHaveValue('Alle')
await expect(jahrFilter).toHaveValue('Alle')
})
// ==========================================================================
// DOCUMENT CARD TESTS
// ==========================================================================
test('document cards are displayed', async ({ page }) => {
// Wait for cards to load
await page.waitForTimeout(600) // Wait for animation delay
// Should have document cards
const cards = page.locator('[class*="rounded-3xl"]').filter({ hasText: 'Deutsch' })
await expect(cards.first()).toBeVisible()
})
test('document card shows Vorschau button', async ({ page }) => {
await page.waitForTimeout(600)
const vorschauButton = page.getByRole('button', { name: 'Vorschau' }).first()
await expect(vorschauButton).toBeVisible()
})
test('document card shows Verwenden button', async ({ page }) => {
await page.waitForTimeout(600)
const verwendenButton = page.getByRole('button', { name: 'Verwenden' }).first()
await expect(verwendenButton).toBeVisible()
})
// ==========================================================================
// PREVIEW MODAL TESTS
// ==========================================================================
test('clicking Vorschau opens preview modal', async ({ page }) => {
await page.waitForTimeout(600)
// Click first Vorschau button
const vorschauButton = page.getByRole('button', { name: 'Vorschau' }).first()
await vorschauButton.click()
// Modal should be visible - check for the "Zurueck zum Archiv" button
await expect(page.getByRole('button', { name: 'Zurueck zum Archiv' })).toBeVisible()
})
test('preview modal shows document details', async ({ page }) => {
await page.waitForTimeout(600)
// Open preview
await page.getByRole('button', { name: 'Vorschau' }).first().click()
// Check for details section - look within the modal (fixed z-50)
const modal = page.locator('.fixed.inset-0.z-50')
await expect(modal.getByText('Details')).toBeVisible()
// Check that the details section has Fach info (using span in modal)
await expect(modal.locator('span').filter({ hasText: /^Fach$/ })).toBeVisible()
})
test('preview modal has zoom controls', async ({ page }) => {
await page.waitForTimeout(600)
// Open preview
await page.getByRole('button', { name: 'Vorschau' }).first().click()
// Should show zoom percentage
await expect(page.getByText('100%')).toBeVisible()
})
test('preview modal zoom in works', async ({ page }) => {
await page.waitForTimeout(600)
// Open preview
await page.getByRole('button', { name: 'Vorschau' }).first().click()
// Find zoom in button (+ icon)
const zoomInButton = page.locator('button').filter({ has: page.locator('svg path[d*="M12 4v16m8-8H4"]') }).first()
await zoomInButton.click()
// Zoom should increase to 125%
await expect(page.getByText('125%')).toBeVisible()
})
test('preview modal close button works', async ({ page }) => {
await page.waitForTimeout(600)
// Open preview
await page.getByRole('button', { name: 'Vorschau' }).first().click()
// Wait for modal
await expect(page.getByRole('button', { name: 'Zurueck zum Archiv' })).toBeVisible()
// Click close/back button
await page.getByRole('button', { name: 'Zurueck zum Archiv' }).click()
// Modal should be closed - back button should not be visible
await expect(page.getByRole('button', { name: 'Zurueck zum Archiv' })).not.toBeVisible()
})
test('preview modal backdrop click closes modal', async ({ page }) => {
await page.waitForTimeout(600)
// Open preview
await page.getByRole('button', { name: 'Vorschau' }).first().click()
// Wait for modal
await expect(page.getByRole('button', { name: 'Zurueck zum Archiv' })).toBeVisible()
// Click the backdrop (top-left corner of the page)
await page.mouse.click(10, 10)
// Modal should be closed
await expect(page.getByRole('button', { name: 'Zurueck zum Archiv' })).not.toBeVisible()
})
test('preview modal Herunterladen button exists', async ({ page }) => {
await page.waitForTimeout(600)
// Open preview
await page.getByRole('button', { name: 'Vorschau' }).first().click()
// Check for download button in sidebar
await expect(page.getByRole('link', { name: 'Herunterladen' })).toBeVisible()
})
test('preview modal Als Vorlage verwenden button exists', async ({ page }) => {
await page.waitForTimeout(600)
// Open preview
await page.getByRole('button', { name: 'Vorschau' }).first().click()
// Check for "Als Vorlage verwenden" button
await expect(page.getByRole('button', { name: 'Als Vorlage verwenden' })).toBeVisible()
})
// ==========================================================================
// CREATE KLAUSUR MODAL TESTS
// ==========================================================================
test('clicking Verwenden opens create modal', async ({ page }) => {
await page.waitForTimeout(600)
// Click first Verwenden button
await page.getByRole('button', { name: 'Verwenden' }).first().click()
// Create modal should be visible
await expect(page.getByText('Klausur aus Vorlage erstellen')).toBeVisible()
})
test('create modal has pre-filled title', async ({ page }) => {
await page.waitForTimeout(600)
// Click Verwenden
await page.getByRole('button', { name: 'Verwenden' }).first().click()
// Title input should have a value
const titleInput = page.getByPlaceholder('z.B. Deutsch LK Q4 - Kafka')
await expect(titleInput).toBeVisible()
await expect(titleInput).not.toHaveValue('')
})
test('create modal shows template info', async ({ page }) => {
await page.waitForTimeout(600)
// Click Verwenden
await page.getByRole('button', { name: 'Verwenden' }).first().click()
// Should show template details
await expect(page.getByText('Fach:')).toBeVisible()
await expect(page.getByText('Jahr:')).toBeVisible()
await expect(page.getByText('Niveau:')).toBeVisible()
})
test('create modal cancel button closes modal', async ({ page }) => {
await page.waitForTimeout(600)
// Click Verwenden
await page.getByRole('button', { name: 'Verwenden' }).first().click()
// Wait for modal
await expect(page.getByText('Klausur aus Vorlage erstellen')).toBeVisible()
// Click cancel
await page.getByRole('button', { name: 'Abbrechen' }).click()
// Modal should be closed
await expect(page.getByText('Klausur aus Vorlage erstellen')).not.toBeVisible()
})
test('create modal Klausur erstellen button exists', async ({ page }) => {
await page.waitForTimeout(600)
// Click Verwenden
await page.getByRole('button', { name: 'Verwenden' }).first().click()
// Should have create button
await expect(page.getByRole('button', { name: 'Klausur erstellen' })).toBeVisible()
})
// ==========================================================================
// NAVIGATION TESTS
// ==========================================================================
test('back button navigates to /korrektur', async ({ page }) => {
// Find the back button in the header
const backButton = page.locator('button').filter({ has: page.locator('svg path[d*="15 19l-7-7"]') }).first()
await backButton.click()
// Should navigate to /korrektur
await expect(page).toHaveURL(/\/korrektur$/)
})
// ==========================================================================
// DOCUMENT COUNT TESTS
// ==========================================================================
test('document count is displayed', async ({ page }) => {
await page.waitForTimeout(600)
// Should show document count
await expect(page.getByText(/\d+ Dokumente/)).toBeVisible()
})
test('filtering updates document count', async ({ page }) => {
await page.waitForTimeout(600)
// Get initial count
const countText = page.getByText(/\d+ Dokumente/)
const initialText = await countText.textContent()
// Apply a restrictive filter
const jahrFilter = page.getByLabel('Jahr')
await jahrFilter.selectOption('2024')
// Wait for filter to apply
await page.waitForTimeout(300)
// Count should potentially change (or stay same if all 2024)
// Just verify the count element still exists
await expect(page.getByText(/\d+ Dokumente/)).toBeVisible()
})
// ==========================================================================
// EMPTY STATE TESTS
// ==========================================================================
test('shows empty state when no results', async ({ page }) => {
// Search for something that won't match
const searchInput = page.getByPlaceholder('Thema suchen')
await searchInput.fill('xyznonexistent12345')
// Wait for filter
await page.waitForTimeout(300)
// Should show empty state
await expect(page.getByText('Keine Dokumente gefunden')).toBeVisible()
})
})