3b8df0d294
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-school (push) Successful in 29s
CI / test-go-edu-search (push) Successful in 29s
CI / test-python-klausur (push) Failing after 2m39s
CI / test-python-agent-core (push) Successful in 19s
CI / test-nodejs-website (push) Successful in 22s
123 lines
4.8 KiB
TypeScript
123 lines
4.8 KiB
TypeScript
import { test, expect, Page } from '@playwright/test'
|
|
|
|
/**
|
|
* E2E tests for /schulkalender. Mocks the /api/school/calendar/* routes
|
|
* so the wizard, save flow and month grid render deterministically without
|
|
* the live backend or seed data.
|
|
*/
|
|
|
|
interface MockOpts {
|
|
config?: { user_id: string; bundesland: string } | null
|
|
holidays?: unknown[]
|
|
}
|
|
|
|
async function mockCalendarApi(page: Page, opts: MockOpts = {}) {
|
|
let config = opts.config ?? null
|
|
|
|
await page.route('**/api/school/calendar/config', async (route) => {
|
|
if (route.request().method() === 'GET') {
|
|
return route.fulfill({
|
|
status: 200, contentType: 'application/json',
|
|
body: JSON.stringify(config),
|
|
})
|
|
}
|
|
if (route.request().method() === 'PUT') {
|
|
const body = JSON.parse(route.request().postData() || '{}')
|
|
config = { user_id: 'dev', bundesland: body.bundesland }
|
|
return route.fulfill({
|
|
status: 201, contentType: 'application/json',
|
|
body: JSON.stringify(config),
|
|
})
|
|
}
|
|
return route.fulfill({ status: 405 })
|
|
})
|
|
|
|
await page.route(/\/api\/school\/calendar\/holidays(\?.*)?$/, async (route) => {
|
|
return route.fulfill({
|
|
status: 200, contentType: 'application/json',
|
|
body: JSON.stringify(opts.holidays ?? []),
|
|
})
|
|
})
|
|
}
|
|
|
|
test.describe('Schulkalender — Bundesland Wizard', () => {
|
|
test('wizard renders when no config exists', async ({ page }) => {
|
|
await mockCalendarApi(page, { config: null })
|
|
await page.goto('/schulkalender')
|
|
await page.waitForLoadState('networkidle')
|
|
await expect(page.getByTestId('bundesland-wizard')).toBeVisible()
|
|
await expect(page.getByText('Willkommen im Schulkalender')).toBeVisible()
|
|
})
|
|
|
|
test('saving a Bundesland switches to MonthView', async ({ page }) => {
|
|
await mockCalendarApi(page, { config: null })
|
|
await page.goto('/schulkalender')
|
|
await page.waitForLoadState('networkidle')
|
|
|
|
await page.getByTestId('bundesland-select').selectOption('DE-NI')
|
|
await page.getByTestId('bundesland-save').click()
|
|
|
|
await expect(page.getByTestId('month-view')).toBeVisible()
|
|
await expect(page.getByText('Niedersachsen')).toBeVisible()
|
|
})
|
|
})
|
|
|
|
test.describe('Schulkalender — Month View', () => {
|
|
test('shows MonthView when config is set', async ({ page }) => {
|
|
await mockCalendarApi(page, {
|
|
config: { user_id: 'dev', bundesland: 'DE-NI' },
|
|
holidays: [],
|
|
})
|
|
await page.goto('/schulkalender')
|
|
await page.waitForLoadState('networkidle')
|
|
await expect(page.getByTestId('month-view')).toBeVisible()
|
|
// Weekday header line.
|
|
for (const w of ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So']) {
|
|
await expect(page.getByText(w, { exact: true }).first()).toBeVisible()
|
|
}
|
|
})
|
|
|
|
test('colours holidays in the grid', async ({ page }) => {
|
|
// Fix today by mocking config with a deterministic month/year via prev/next.
|
|
await mockCalendarApi(page, {
|
|
config: { user_id: 'dev', bundesland: 'DE-NI' },
|
|
holidays: [
|
|
{ id: 'h1', region: 'DE-NI', event_type: 'public_holiday', name_de: 'Test-Feiertag', start_date: '2099-06-15', end_date: '2099-06-15' },
|
|
{ id: 'h2', region: 'DE-NI', event_type: 'school_holiday', name_de: 'Test-Ferien', start_date: '2099-06-20', end_date: '2099-06-21' },
|
|
],
|
|
})
|
|
await page.goto('/schulkalender')
|
|
await page.waitForLoadState('networkidle')
|
|
// Assert the legend rows — using exact text to avoid colliding with
|
|
// tooltips like "Tag der deutschen Einheit" that also contain 'tag'.
|
|
await expect(page.getByText('Feiertag', { exact: true })).toBeVisible()
|
|
await expect(page.getByText('Schulferien', { exact: true })).toBeVisible()
|
|
})
|
|
|
|
test('Heute button resets to current month', async ({ page }) => {
|
|
await mockCalendarApi(page, {
|
|
config: { user_id: 'dev', bundesland: 'DE-NI' },
|
|
holidays: [],
|
|
})
|
|
await page.goto('/schulkalender')
|
|
await page.waitForLoadState('networkidle')
|
|
await page.getByTestId('month-prev').click()
|
|
await page.getByTestId('month-prev').click()
|
|
await page.getByTestId('month-today').click()
|
|
// After clicking Heute, the current month name must appear in the heading.
|
|
const months = ['Januar', 'Februar', 'Maerz', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember']
|
|
const currentMonth = months[new Date().getMonth()]
|
|
await expect(page.getByRole('heading', { name: new RegExp(currentMonth) })).toBeVisible()
|
|
})
|
|
})
|
|
|
|
test.describe('Schulkalender — Sidebar entry', () => {
|
|
test('sidebar contains Schulkalender link', async ({ page }) => {
|
|
await mockCalendarApi(page)
|
|
await page.goto('/schulkalender')
|
|
await page.waitForLoadState('networkidle')
|
|
const sidebar = page.locator('aside').first()
|
|
await expect(sidebar.getByText(/Schulkalender|School Calendar/).first()).toBeVisible()
|
|
})
|
|
})
|