Services: Admin-Compliance, Backend-Compliance, AI-Compliance-SDK, Consent-SDK, Developer-Portal, PCA-Platform, DSMS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
374 lines
15 KiB
TypeScript
374 lines
15 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import { navigateToSDK, waitForPageLoad, waitForElement, countElements } from '../utils/test-helpers'
|
|
|
|
/**
|
|
* DSR (Data Subject Request) Module E2E Tests
|
|
* Tests for GDPR Art. 15-21 functionality
|
|
*/
|
|
|
|
test.describe('DSR Dashboard', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await navigateToSDK(page, '/dsr')
|
|
})
|
|
|
|
test('should load DSR dashboard', async ({ page }) => {
|
|
// Check page title/header exists (use heading role for specificity)
|
|
await expect(page.getByRole('heading', { name: 'DSR Portal' })).toBeVisible({ timeout: 10000 })
|
|
})
|
|
|
|
test('should display tab navigation', async ({ page }) => {
|
|
// Check all tabs are present - use exact text matching
|
|
await expect(page.getByRole('button', { name: /Uebersicht/ })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: /Eingang \d+/ })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: /In Bearbeitung \d+/ })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: /Abgeschlossen \d+/ })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: 'Einstellungen' })).toBeVisible()
|
|
})
|
|
|
|
test('should display statistics cards', async ({ page }) => {
|
|
// Check statistics cards exist - use exact text matching
|
|
await expect(page.getByText('Gesamt', { exact: true })).toBeVisible()
|
|
await expect(page.getByText('Neue Anfragen')).toBeVisible()
|
|
await expect(page.getByText('Ueberfaellig', { exact: true })).toBeVisible()
|
|
})
|
|
|
|
test('should have "Anfrage erfassen" button', async ({ page }) => {
|
|
const button = page.getByRole('link', { name: 'Anfrage erfassen' })
|
|
await expect(button).toBeVisible()
|
|
await expect(button).toHaveAttribute('href', '/sdk/dsr/new')
|
|
})
|
|
|
|
test('should display mock DSR requests', async ({ page }) => {
|
|
// Wait for requests to load
|
|
await page.waitForTimeout(1000)
|
|
|
|
// Check that request cards are displayed (using Link elements)
|
|
const requestCards = page.locator('a[href^="/sdk/dsr/dsr-"]')
|
|
const count = await requestCards.count()
|
|
expect(count).toBeGreaterThan(0)
|
|
})
|
|
|
|
test('should filter by tab - Eingang', async ({ page }) => {
|
|
await page.getByRole('button', { name: /Eingang \d+/ }).click()
|
|
await page.waitForTimeout(500)
|
|
|
|
// Check that the tab is active (has border-purple-600)
|
|
await expect(page.getByRole('button', { name: /Eingang \d+/ })).toHaveClass(/border-purple-600/)
|
|
})
|
|
|
|
test('should filter by tab - In Bearbeitung', async ({ page }) => {
|
|
await page.getByRole('button', { name: /In Bearbeitung \d+/ }).click()
|
|
await page.waitForTimeout(500)
|
|
|
|
await expect(page.getByRole('button', { name: /In Bearbeitung \d+/ })).toHaveClass(/border-purple-600/)
|
|
})
|
|
|
|
test('should filter by tab - Abgeschlossen', async ({ page }) => {
|
|
await page.getByRole('button', { name: /Abgeschlossen \d+/ }).click()
|
|
await page.waitForTimeout(500)
|
|
|
|
await expect(page.getByRole('button', { name: /Abgeschlossen \d+/ })).toHaveClass(/border-purple-600/)
|
|
})
|
|
|
|
test('should have filter dropdowns', async ({ page }) => {
|
|
// Check filter controls exist using select elements
|
|
await expect(page.locator('select').first()).toBeVisible()
|
|
// Verify we have filter dropdowns
|
|
const selects = page.locator('select')
|
|
const count = await selects.count()
|
|
expect(count).toBeGreaterThanOrEqual(3)
|
|
})
|
|
|
|
test('should filter by type', async ({ page }) => {
|
|
// Select "Auskunft" type from the first dropdown
|
|
await page.locator('select').first().selectOption('access')
|
|
await page.waitForTimeout(500)
|
|
|
|
// Check filter is applied
|
|
const typeDropdown = page.locator('select').first()
|
|
await expect(typeDropdown).toHaveValue('access')
|
|
})
|
|
|
|
test('should show info box about deadlines', async ({ page }) => {
|
|
await expect(page.getByText('Fristen beachten')).toBeVisible()
|
|
await expect(page.getByText('Art. 12 DSGVO')).toBeVisible()
|
|
})
|
|
|
|
test('should navigate to request details on click', async ({ page }) => {
|
|
// Click on first request card
|
|
const firstCard = page.locator('a[href^="/sdk/dsr/dsr-"]').first()
|
|
await firstCard.click()
|
|
await waitForPageLoad(page)
|
|
|
|
// Check URL changed to detail page
|
|
await expect(page).toHaveURL(/\/sdk\/dsr\/dsr-/)
|
|
})
|
|
})
|
|
|
|
test.describe('DSR New Request Page', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await navigateToSDK(page, '/dsr/new')
|
|
})
|
|
|
|
test('should load new request page', async ({ page }) => {
|
|
await expect(page.getByRole('heading', { name: 'Neue Anfrage erfassen' })).toBeVisible()
|
|
})
|
|
|
|
test('should display all DSR types (Art. 15-21)', async ({ page }) => {
|
|
// Check for Art. numbers in buttons
|
|
await expect(page.getByRole('button', { name: /15.*Auskunft/ })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: /16.*Berichtigung/ })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: /17.*Loeschung/ })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: /18.*Einschraenkung/ })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: /20.*Uebertragung/ })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: /21.*Widerspruch/ })).toBeVisible()
|
|
})
|
|
|
|
test('should have requester input fields', async ({ page }) => {
|
|
// Check for name and email inputs
|
|
await expect(page.locator('input').first()).toBeVisible()
|
|
await expect(page.locator('input[type="email"]')).toBeVisible()
|
|
})
|
|
|
|
test('should have source selection buttons', async ({ page }) => {
|
|
await expect(page.getByRole('button', { name: 'Webformular' })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: 'E-Mail' })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: 'Brief' })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: 'Telefon' })).toBeVisible()
|
|
})
|
|
|
|
test('should have priority buttons', async ({ page }) => {
|
|
await expect(page.getByRole('button', { name: 'Niedrig' })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: 'Normal' })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: 'Hoch' })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: 'Kritisch' })).toBeVisible()
|
|
})
|
|
|
|
test('should select DSR type', async ({ page }) => {
|
|
// Click on Art. 15 Auskunft
|
|
await page.getByRole('button', { name: /Auskunft.*15/ }).click()
|
|
await page.waitForTimeout(300)
|
|
|
|
// Check type is selected (should show description)
|
|
await expect(page.getByText('Auskunftsrecht')).toBeVisible()
|
|
await expect(page.getByText('30 Tage')).toBeVisible()
|
|
})
|
|
|
|
test('should validate required fields', async ({ page }) => {
|
|
// Try to submit without filling required fields
|
|
await page.getByRole('button', { name: 'Anfrage erfassen' }).click()
|
|
await page.waitForTimeout(500)
|
|
|
|
// Should show validation errors (at least one)
|
|
await expect(page.getByText(/Bitte waehlen Sie/).first()).toBeVisible()
|
|
})
|
|
|
|
test('should fill form and show no errors', async ({ page }) => {
|
|
// Select type
|
|
await page.getByRole('button', { name: /Auskunft.*15/ }).click()
|
|
|
|
// Fill requester info
|
|
await page.locator('input').first().fill('Test Person')
|
|
await page.locator('input[type="email"]').fill('test@example.com')
|
|
|
|
// Select source
|
|
await page.getByRole('button', { name: 'E-Mail' }).click()
|
|
|
|
// Form should be valid now - check that error messages are gone
|
|
await page.getByRole('button', { name: 'Anfrage erfassen' }).click()
|
|
await page.waitForTimeout(500)
|
|
|
|
// Should not show type error anymore
|
|
const typeError = page.getByText('Bitte waehlen Sie den Anfragetyp')
|
|
await expect(typeError).not.toBeVisible()
|
|
})
|
|
|
|
test('should navigate back to dashboard', async ({ page }) => {
|
|
// Click back/cancel link (labeled "Abbrechen" with href to /sdk/dsr)
|
|
await page.getByRole('link', { name: 'Abbrechen' }).click()
|
|
await waitForPageLoad(page)
|
|
|
|
await expect(page).toHaveURL(/\/sdk\/dsr$/)
|
|
})
|
|
})
|
|
|
|
test.describe('DSR Detail Page', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
// Navigate to first mock request
|
|
await navigateToSDK(page, '/dsr/dsr-001')
|
|
})
|
|
|
|
test('should load detail page', async ({ page }) => {
|
|
// Check reference number is displayed
|
|
await expect(page.getByText('DSR-2025-000001')).toBeVisible()
|
|
})
|
|
|
|
test('should display workflow stepper', async ({ page }) => {
|
|
// Check workflow steps exist - use first() for duplicates
|
|
await expect(page.getByText('Eingang').first()).toBeVisible()
|
|
await expect(page.getByText('ID-Pruefung').first()).toBeVisible()
|
|
await expect(page.getByText('Bearbeitung').first()).toBeVisible()
|
|
await expect(page.getByText('Abschluss').first()).toBeVisible()
|
|
})
|
|
|
|
test('should display requester information', async ({ page }) => {
|
|
await expect(page.getByRole('heading', { name: 'Max Mustermann' })).toBeVisible()
|
|
await expect(page.getByText('max.mustermann@example.de')).toBeVisible()
|
|
})
|
|
|
|
test('should have content tabs', async ({ page }) => {
|
|
await expect(page.getByRole('button', { name: 'Details' })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: 'Kommunikation' })).toBeVisible()
|
|
})
|
|
|
|
test('should display identity verification status', async ({ page }) => {
|
|
// For dsr-001, identity is not verified
|
|
await expect(page.getByText('Identitaetspruefung').first()).toBeVisible()
|
|
})
|
|
|
|
test('should have sidebar with status info', async ({ page }) => {
|
|
await expect(page.getByText('Status').first()).toBeVisible()
|
|
await expect(page.getByText('Prioritaet').first()).toBeVisible()
|
|
await expect(page.getByText('Zugewiesen an')).toBeVisible()
|
|
})
|
|
|
|
test('should have action buttons', async ({ page }) => {
|
|
await expect(page.getByText('Aktionen').first()).toBeVisible()
|
|
// For unverified request, should have verify identity button
|
|
await expect(page.getByRole('button', { name: 'Identitaet verifizieren' })).toBeVisible()
|
|
})
|
|
|
|
test('should display audit log', async ({ page }) => {
|
|
await expect(page.getByText('Aktivitaeten')).toBeVisible()
|
|
await expect(page.getByText('Erstellt').first()).toBeVisible()
|
|
})
|
|
|
|
test('should switch to communication tab', async ({ page }) => {
|
|
await page.getByRole('button', { name: 'Kommunikation' }).click()
|
|
await page.waitForTimeout(500)
|
|
|
|
await expect(page.getByRole('heading', { name: 'Kommunikation' })).toBeVisible()
|
|
})
|
|
|
|
test('should have back navigation', async ({ page }) => {
|
|
// Click back link (icon link in header area, first link to /sdk/dsr in main content)
|
|
await page.locator('main a[href="/sdk/dsr"]').first().click()
|
|
await waitForPageLoad(page)
|
|
|
|
await expect(page).toHaveURL(/\/sdk\/dsr$/)
|
|
})
|
|
|
|
test('should open identity verification modal', async ({ page }) => {
|
|
await page.getByRole('button', { name: 'Identitaet verifizieren' }).click()
|
|
await page.waitForTimeout(500)
|
|
|
|
// Modal should open - use level 2 heading to be specific
|
|
await expect(page.locator('h2:has-text("Identitaet verifizieren")')).toBeVisible()
|
|
await expect(page.getByText('Verifizierungsmethode', { exact: true })).toBeVisible()
|
|
})
|
|
})
|
|
|
|
test.describe('DSR Identity Modal', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await navigateToSDK(page, '/dsr/dsr-001')
|
|
await page.getByRole('button', { name: 'Identitaet verifizieren' }).click()
|
|
await page.waitForTimeout(500)
|
|
})
|
|
|
|
test('should display verification methods', async ({ page }) => {
|
|
await expect(page.getByRole('button', { name: /Ausweisdokument/ })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: /E-Mail-Bestaetigung/ })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: /Bestehendes Konto/ })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: /Telefonische Bestaetigung/ })).toBeVisible()
|
|
})
|
|
|
|
test('should select verification method', async ({ page }) => {
|
|
await page.getByRole('button', { name: /Ausweisdokument/ }).click()
|
|
await page.waitForTimeout(300)
|
|
|
|
// Check selection is visible
|
|
const selectedButton = page.getByRole('button', { name: /Ausweisdokument/ })
|
|
await expect(selectedButton).toHaveClass(/border-purple-500/)
|
|
})
|
|
|
|
test('should close modal on cancel', async ({ page }) => {
|
|
await page.getByRole('button', { name: 'Abbrechen' }).click()
|
|
await page.waitForTimeout(300)
|
|
|
|
// Modal should be closed
|
|
await expect(page.getByText('Verifizierungsmethode')).not.toBeVisible()
|
|
})
|
|
|
|
test('should close modal on cancel button', async ({ page }) => {
|
|
// Click on cancel button to close modal
|
|
await page.getByRole('button', { name: 'Abbrechen' }).click()
|
|
await page.waitForTimeout(300)
|
|
|
|
await expect(page.getByText('Verifizierungsmethode')).not.toBeVisible()
|
|
})
|
|
|
|
test('should verify identity and update UI', async ({ page }) => {
|
|
// Select method
|
|
await page.getByRole('button', { name: /Ausweisdokument/ }).click()
|
|
await page.waitForTimeout(200)
|
|
|
|
// Click verify button
|
|
await page.getByRole('button', { name: 'Identitaet bestaetigen' }).click()
|
|
await page.waitForTimeout(1000)
|
|
|
|
// Modal should close and status should update
|
|
await expect(page.getByText('Verifizierungsmethode')).not.toBeVisible()
|
|
await expect(page.getByText('Identitaet verifiziert').first()).toBeVisible()
|
|
})
|
|
})
|
|
|
|
test.describe('DSR Type-Specific Features', () => {
|
|
test('should show erasure checklist for Art. 17 request', async ({ page }) => {
|
|
await navigateToSDK(page, '/dsr/dsr-002') // Erasure request
|
|
await page.waitForTimeout(500)
|
|
|
|
// Click on type-specific tab
|
|
const typeTab = page.getByRole('button', { name: 'Loeschung' })
|
|
if (await typeTab.isVisible()) {
|
|
await typeTab.click()
|
|
await page.waitForTimeout(500)
|
|
|
|
await expect(page.getByRole('heading', { name: /Art\. 17\(3\)/ })).toBeVisible()
|
|
}
|
|
})
|
|
|
|
test('should show data export for Art. 15 request', async ({ page }) => {
|
|
await navigateToSDK(page, '/dsr/dsr-001') // Access request
|
|
await page.waitForTimeout(500)
|
|
|
|
// Click on type-specific tab
|
|
const typeTab = page.getByRole('button', { name: 'Auskunft' })
|
|
if (await typeTab.isVisible()) {
|
|
await typeTab.click()
|
|
await page.waitForTimeout(500)
|
|
|
|
// Should show export options
|
|
await expect(page.getByText('Export-Format')).toBeVisible()
|
|
}
|
|
})
|
|
})
|
|
|
|
test.describe('DSR Responsive Design', () => {
|
|
test('should be responsive on mobile', async ({ page }) => {
|
|
await page.setViewportSize({ width: 375, height: 667 })
|
|
await navigateToSDK(page, '/dsr')
|
|
|
|
// Dashboard should still be usable
|
|
await expect(page.getByRole('heading', { name: 'DSR Portal' })).toBeVisible()
|
|
await expect(page.getByRole('link', { name: 'Anfrage erfassen' })).toBeVisible()
|
|
})
|
|
|
|
test('should be responsive on tablet', async ({ page }) => {
|
|
await page.setViewportSize({ width: 768, height: 1024 })
|
|
await navigateToSDK(page, '/dsr')
|
|
|
|
await expect(page.getByRole('heading', { name: 'DSR Portal' })).toBeVisible()
|
|
await expect(page.getByRole('button', { name: /Uebersicht/ })).toBeVisible()
|
|
})
|
|
})
|