test(ci): add quality gates, e2e data prep, and i18n integration coverage

This commit is contained in:
2026-02-10 21:17:41 +01:00
parent 4ac7410148
commit 4d4b583cf4
9 changed files with 150 additions and 8 deletions

70
.gitea/workflows/ci.yml Normal file
View File

@@ -0,0 +1,70 @@
name: CMS CI
on:
pull_request:
push:
branches:
- dev
- staging
- main
workflow_dispatch:
env:
BUN_VERSION: "1.3.5"
NODE_ENV: "test"
DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/cms?schema=public"
BETTER_AUTH_SECRET: "ci-test-secret-change-me"
BETTER_AUTH_URL: "http://localhost:3001"
CMS_ADMIN_ORIGIN: "http://127.0.0.1:3001"
CMS_WEB_ORIGIN: "http://127.0.0.1:3000"
CMS_ADMIN_SELF_REGISTRATION_ENABLED: "false"
CMS_SUPPORT_USERNAME: "support"
CMS_SUPPORT_EMAIL: "support@cms.local"
CMS_SUPPORT_PASSWORD: "support-ci-password"
CMS_SUPPORT_NAME: "Technical Support"
CMS_SUPPORT_LOGIN_KEY: "support-access"
jobs:
quality:
name: Lint Typecheck Unit E2E
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_DB: cms
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U postgres -d cms"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: ${{ env.BUN_VERSION }}
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Install Playwright browser deps
run: bunx playwright install --with-deps chromium
- name: Lint and format checks
run: bun run check
- name: Typecheck
run: bun run typecheck
- name: Unit and integration tests
run: bun run test
- name: E2E tests
run: bun run test:e2e

View File

@@ -69,6 +69,7 @@ bun run dev
- `bun run test` - `bun run test`
- `bun run test:watch` - `bun run test:watch`
- `bun run test:coverage` - `bun run test:coverage`
- `bun run test:e2e:prepare`
- `bun run test:e2e` - `bun run test:e2e`
- `bun run lint` - `bun run lint`
- `bun run typecheck` - `bun run typecheck`
@@ -85,6 +86,7 @@ bun run dev
- Unit/integration/component: Vitest + Testing Library + MSW - Unit/integration/component: Vitest + Testing Library + MSW
- E2E: Playwright (separate projects for `web` and `admin`) - E2E: Playwright (separate projects for `web` and `admin`)
- Use `bun run test` and `bun run test:e2e` (not plain `bun test`, which uses Bun's runner) - Use `bun run test` and `bun run test:e2e` (not plain `bun test`, which uses Bun's runner)
- E2E data prep (migrations + seed): `bun run test:e2e:prepare`
One-time Playwright browser install: One-time Playwright browser install:
@@ -97,6 +99,7 @@ bunx playwright install
The repo includes a theoretical CI/CD and deployment baseline: The repo includes a theoretical CI/CD and deployment baseline:
- Gitea workflow: `.gitea/workflows/ci-cd-theoretical.yml` - Gitea workflow: `.gitea/workflows/ci-cd-theoretical.yml`
- Real quality gate workflow: `.gitea/workflows/ci.yml`
- App images: - App images:
- `apps/web/Dockerfile` - `apps/web/Dockerfile`
- `apps/admin/Dockerfile` - `apps/admin/Dockerfile`

10
TODO.md
View File

@@ -61,11 +61,11 @@ This file is the single source of truth for roadmap and delivery progress.
- [x] [P1] Vitest + Testing Library + MSW baseline - [x] [P1] Vitest + Testing Library + MSW baseline
- [x] [P1] Playwright baseline with web/admin projects - [x] [P1] Playwright baseline with web/admin projects
- [ ] [P1] CI workflow for lint/typecheck/unit/e2e gates - [x] [P1] CI workflow for lint/typecheck/unit/e2e gates
- [ ] [P1] Test data strategy (seed fixtures + isolated e2e data) - [x] [P1] Test data strategy (seed fixtures + isolated e2e data)
- [~] [P1] RBAC policy unit tests and permission regression suite - [~] [P1] RBAC policy unit tests and permission regression suite
- [ ] [P1] i18n unit tests (locale resolution, fallback, message key loading) - [ ] [P1] i18n unit tests (locale resolution, fallback, message key loading)
- [ ] [P1] i18n integration tests (admin/public locale switch and persistence) - [x] [P1] i18n integration tests (admin/public locale switch and persistence)
- [ ] [P1] i18n e2e smoke tests (localized headings/content per route) - [ ] [P1] i18n e2e smoke tests (localized headings/content per route)
- [ ] [P1] CRUD contract tests for shared service patterns - [ ] [P1] CRUD contract tests for shared service patterns
@@ -160,6 +160,8 @@ This file is the single source of truth for roadmap and delivery progress.
- [ ] [P1] Forgot password/reset password pipeline and support tooling - [ ] [P1] Forgot password/reset password pipeline and support tooling
- [ ] [P2] GUI page to edit role-permission mappings with safety guardrails - [ ] [P2] GUI page to edit role-permission mappings with safety guardrails
- [ ] [P2] Translation management UI for admin (language toggles, key coverage, missing translation markers) - [ ] [P2] Translation management UI for admin (language toggles, key coverage, missing translation markers)
- [ ] [P2] Time-boxed support access keys generated by privileged admins; while active, disable direct support-user password login on the regular auth form
- [ ] [P2] Keep permanent emergency support key fallback via env (`CMS_SUPPORT_LOGIN_KEY`)
- [ ] [P2] Error boundaries and UX fallback states - [ ] [P2] Error boundaries and UX fallback states
### Public App ### Public App
@@ -199,6 +201,8 @@ This file is the single source of truth for roadmap and delivery progress.
- [2026-02-10] Admin dashboard includes a temporary posts CRUD sandbox (create/update/delete) to validate the shared CRUD base through the real app UI. - [2026-02-10] Admin dashboard includes a temporary posts CRUD sandbox (create/update/delete) to validate the shared CRUD base through the real app UI.
- [2026-02-10] Admin i18n baseline now resolves locale from cookie and loads runtime message dictionaries in root layout; admin locale switcher is active on auth and dashboard views. - [2026-02-10] Admin i18n baseline now resolves locale from cookie and loads runtime message dictionaries in root layout; admin locale switcher is active on auth and dashboard views.
- [2026-02-10] Admin self-registration policy is now managed via `/settings` and persisted in `system_setting`; env var is fallback/default only. - [2026-02-10] Admin self-registration policy is now managed via `/settings` and persisted in `system_setting`; env var is fallback/default only.
- [2026-02-10] E2E now runs with deterministic preparation (`test:e2e:prepare`: generate + migrate deploy + seed) before Playwright execution.
- [2026-02-10] CI quality workflow `.gitea/workflows/ci.yml` enforces `check`, `typecheck`, `test`, and `test:e2e` against a PostgreSQL service.
## How We Use This File ## How We Use This File

View File

@@ -23,6 +23,7 @@ export default defineConfig({
{ text: "CRUD Baseline", link: "/product-engineering/crud-baseline" }, { text: "CRUD Baseline", link: "/product-engineering/crud-baseline" },
{ text: "i18n Baseline", link: "/product-engineering/i18n-baseline" }, { text: "i18n Baseline", link: "/product-engineering/i18n-baseline" },
{ text: "RBAC And Permissions", link: "/product-engineering/rbac-permission-model" }, { text: "RBAC And Permissions", link: "/product-engineering/rbac-permission-model" },
{ text: "Testing Strategy", link: "/product-engineering/testing-strategy" },
{ text: "Workflow", link: "/workflow" }, { text: "Workflow", link: "/workflow" },
], ],
}, },

View File

@@ -8,6 +8,7 @@ This section covers platform and implementation documentation for engineers and
- [Architecture](/architecture) - [Architecture](/architecture)
- [Better Auth Baseline](/product-engineering/auth-baseline) - [Better Auth Baseline](/product-engineering/auth-baseline)
- [RBAC And Permissions](/product-engineering/rbac-permission-model) - [RBAC And Permissions](/product-engineering/rbac-permission-model)
- [Testing Strategy Baseline](/product-engineering/testing-strategy)
- [Workflow](/workflow) - [Workflow](/workflow)
## Scope ## Scope

View File

@@ -0,0 +1,33 @@
# Testing Strategy Baseline
## Goals
- Keep lint, typecheck, unit/integration, and e2e as mandatory quality gates.
- Make e2e runs deterministic by preparing schema and seeded data before test execution.
- Keep test data isolated per environment (`dev` local, CI database service in workflow).
## Current Gate Stack
- `bun run check`
- `bun run typecheck`
- `bun run test`
- `bun run test:e2e`
## Data Preparation
- `bun run test:e2e:prepare` runs:
- Prisma client generation
- migration deploy
- seed data (including support user bootstrap)
- `bun run test:e2e` and related scripts call `test:e2e:prepare` automatically.
## Locale Integration Coverage
- `e2e/i18n.pw.ts` covers:
- web locale switch + persistence
- admin locale switch + persistence
## CI
- Real quality workflow: `.gitea/workflows/ci.yml`
- Uses a PostgreSQL service container and runs the full gate stack, including e2e.

View File

@@ -15,10 +15,10 @@ Follow `BRANCHING.md`:
## Quality Gates ## Quality Gates
- `bun run lint` - `bun run check`
- `bun run typecheck` - `bun run typecheck`
- `bun run test` - `bun run test`
- `bun run test:e2e --list` - `bun run test:e2e`
## Changelog ## Changelog

29
e2e/i18n.pw.ts Normal file
View File

@@ -0,0 +1,29 @@
import { expect, test } from "@playwright/test"
test.describe("i18n integration", () => {
test("web language switcher updates and persists locale", async ({ page }, testInfo) => {
test.skip(testInfo.project.name !== "web-chromium")
await page.goto("/")
await expect(page.getByRole("heading", { name: /your next\.js cms frontend/i })).toBeVisible()
await page.locator("select").first().selectOption("de")
await expect(page.getByRole("heading", { name: /dein next\.js cms frontend/i })).toBeVisible()
await page.reload()
await expect(page.getByRole("heading", { name: /dein next\.js cms frontend/i })).toBeVisible()
})
test("admin language switcher updates and persists locale", async ({ page }, testInfo) => {
test.skip(testInfo.project.name !== "admin-chromium")
await page.goto("/login")
await expect(page.getByRole("heading", { name: /sign in to cms admin/i })).toBeVisible()
await page.locator("select").first().selectOption("de")
await expect(page.getByRole("heading", { name: /bei cms admin anmelden/i })).toBeVisible()
await page.reload()
await expect(page.getByRole("heading", { name: /bei cms admin anmelden/i })).toBeVisible()
})
})

View File

@@ -18,9 +18,10 @@
"test": "vitest run", "test": "vitest run",
"test:watch": "vitest", "test:watch": "vitest",
"test:coverage": "vitest run --coverage", "test:coverage": "vitest run --coverage",
"test:e2e": "bun run db:generate && playwright test", "test:e2e:prepare": "bun run db:generate && bun run db:migrate:deploy && bun run db:seed",
"test:e2e:headed": "bun run db:generate && playwright test --headed", "test:e2e": "bun run test:e2e:prepare && playwright test",
"test:e2e:ui": "bun run db:generate && playwright test --ui", "test:e2e:headed": "bun run test:e2e:prepare && playwright test --headed",
"test:e2e:ui": "bun run test:e2e:prepare && playwright test --ui",
"commitlint": "commitlint --last", "commitlint": "commitlint --last",
"changelog:preview": "conventional-changelog -p conventionalcommits -r 0", "changelog:preview": "conventional-changelog -p conventionalcommits -r 0",
"changelog:release": "conventional-changelog -p conventionalcommits -i CHANGELOG.md -s", "changelog:release": "conventional-changelog -p conventionalcommits -i CHANGELOG.md -s",