test: add full-stack Playwright integration test infrastructure
Sprint 12 Phase 2: Real integration tests with seed DB - R__seed_test_data.sql (Flyway repeatable, 7 members, strains, batches, docs, board, events) - TestResetController (profile-gated per-test DB reset) - docker-compose.test.yml (self-contained, tmpfs Postgres) - Dockerfile.playwright (v1.60.0, pre-installed deps) - 13 integration spec files, 70+ test cases (@smoke + @full) - seed-constants.ts, selectors.ts, api-client.ts test helpers
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
import { expect, test } from "@playwright/test"
|
||||
|
||||
import { ApiClient } from "../api-client"
|
||||
import { SEED } from "../seed-constants"
|
||||
|
||||
const apiClient = new ApiClient()
|
||||
|
||||
test.describe("Calendar Page @full", () => {
|
||||
test.beforeEach(async () => {
|
||||
await apiClient.login(SEED.admin.email, SEED.admin.password)
|
||||
await apiClient.resetDb()
|
||||
})
|
||||
|
||||
test("renders current month", async ({ page }) => {
|
||||
await page.goto("/calendar")
|
||||
|
||||
// Calendar should show current month name
|
||||
const now = new Date()
|
||||
const monthNames = [
|
||||
"Januar",
|
||||
"Februar",
|
||||
"März",
|
||||
"April",
|
||||
"Mai",
|
||||
"Juni",
|
||||
"Juli",
|
||||
"August",
|
||||
"September",
|
||||
"Oktober",
|
||||
"November",
|
||||
"Dezember",
|
||||
]
|
||||
const currentMonth = monthNames[now.getMonth()]
|
||||
const currentYear = now.getFullYear().toString()
|
||||
|
||||
await expect(
|
||||
page
|
||||
.getByText(currentMonth, { exact: false })
|
||||
.or(page.getByText(currentYear))
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test("seed events are visible", async ({ page }) => {
|
||||
await page.goto("/calendar")
|
||||
|
||||
// There should be an upcoming assembly event (~14 days from now)
|
||||
// and a past social event (~30 days ago) — look for event indicators
|
||||
await expect(
|
||||
page
|
||||
.getByText(/versammlung|assembly/i)
|
||||
.or(page.locator("[data-testid*='event']").first())
|
||||
).toBeVisible()
|
||||
})
|
||||
|
||||
test("month navigation works", async ({ page }) => {
|
||||
await page.goto("/calendar")
|
||||
|
||||
// Find prev/next month buttons
|
||||
const nextBtn = page.getByRole("button", { name: /next|vor|nächst|›|>/i })
|
||||
const prevBtn = page.getByRole("button", {
|
||||
name: /prev|zurück|vorig|‹|</i,
|
||||
})
|
||||
|
||||
// Navigate forward
|
||||
if (await nextBtn.isVisible()) {
|
||||
await nextBtn.click()
|
||||
await page.waitForTimeout(300)
|
||||
// Page should still render without error
|
||||
await expect(page.locator("body")).toBeVisible()
|
||||
}
|
||||
|
||||
// Navigate backward twice (back to previous month)
|
||||
if (await prevBtn.isVisible()) {
|
||||
await prevBtn.click()
|
||||
await page.waitForTimeout(300)
|
||||
await prevBtn.click()
|
||||
await page.waitForTimeout(300)
|
||||
await expect(page.locator("body")).toBeVisible()
|
||||
}
|
||||
})
|
||||
|
||||
test("create event opens dialog with form fields", async ({ page }) => {
|
||||
await page.goto("/calendar")
|
||||
|
||||
const createBtn = page
|
||||
.getByRole("button", { name: /erstellen|create|neues event|neu/i })
|
||||
.or(page.locator('[data-testid="calendar-create-event"]'))
|
||||
|
||||
if (await createBtn.isVisible()) {
|
||||
await createBtn.click()
|
||||
// Dialog should have form fields for event creation
|
||||
await expect(
|
||||
page.getByRole("dialog").or(page.locator("[role='dialog']"))
|
||||
).toBeVisible()
|
||||
// Expect title/name field
|
||||
await expect(
|
||||
page
|
||||
.getByLabel(/titel|name|bezeichnung/i)
|
||||
.or(page.locator("input[name*='title']"))
|
||||
).toBeVisible()
|
||||
}
|
||||
})
|
||||
|
||||
test("cancel event button shows confirmation", async ({ page }) => {
|
||||
await page.goto("/calendar")
|
||||
|
||||
// Click on an existing event to open detail
|
||||
const eventEl = page.locator("[data-testid*='event']").first()
|
||||
|
||||
if (await eventEl.isVisible()) {
|
||||
await eventEl.click()
|
||||
await page.waitForTimeout(300)
|
||||
|
||||
// Look for cancel/delete button
|
||||
const cancelBtn = page.getByRole("button", {
|
||||
name: /absagen|löschen|cancel|delete/i,
|
||||
})
|
||||
|
||||
if (await cancelBtn.isVisible()) {
|
||||
await cancelBtn.click()
|
||||
// Should show confirmation dialog
|
||||
await expect(
|
||||
page.getByRole("alertdialog").or(page.getByText(/bestätigen|sicher/i))
|
||||
).toBeVisible()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user