Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,28 @@ jobs:
- run: uv run python manage.py createcachetable
- run: uv run python manage.py makemigrations --check
- run: just test-coverage

playwright:
runs-on: ubuntu-latest
timeout-minutes: 60

env:
DJANGO_SETTINGS_MODULE: apps.guide.settings.e2e

steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version-file: '.nvmrc'
- run: npm ci
- run: npm run build
- uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0
- run: uv sync --frozen
- run: npm run e2e:install
- run: npx playwright test
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: playwright-report/
retention-days: 30
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,11 @@ apps/guide/settings/local.py
content/evals/translations-inspect_ai/logs/
# Generated glossaries (regenerate with `just eval-glossary <lang>`)
content/evals/translations-inspect_ai/glossary/

# Playwright
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
/playwright/.auth/
/db_e2e.sqlite3
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Important context for AI coding agents working on this project.
- `just lint`: Lint the project.
- `just format`: Format project files.
- `just test`: Run backend (Django) and frontend (Vitest) tests.
- `just e2e`: Run end-to-end tests with Playwright (requires `just e2e-install` first; stop any dev server on :8000 before running).
- `just translations`: Generate and compile translation strings.

## Setup & run commands
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ If you're a Python or Django developer, fork the repo and join us. You'll find a
- Run formatting (Ruff & Oxfmt) `just format`
- Run linting (Ruff, Oxlint, Oxfmt, Stylelint) `just lint`
- Run tests `just test`
- Run end-to-end tests `just e2e` (first-time setup: `just e2e-install`)

# Other Notes

Expand Down
14 changes: 14 additions & 0 deletions apps/guide/settings/e2e.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from .test import * # noqa: F403

DEBUG = True
ALLOWED_HOSTS = ["127.0.0.1", "localhost"]

CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}}

DATABASES = {
"default": dj_database_url.parse( # noqa: F405
f"sqlite:///{BASE_DIR / 'db_e2e.sqlite3'}" # noqa: F405
)
}

WAGTAILADMIN_BASE_URL = "http://127.0.0.1:8000"
18 changes: 18 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ test-coverage:
DJANGO_SETTINGS_MODULE=apps.guide.settings.test uv run coverage run manage.py test
uv run coverage report

# Install Playwright browsers (Chromium, Firefox, Edge).
e2e-install:
npm run e2e:install

# Run end-to-end tests with Playwright. Builds frontend assets first.
e2e:
npm run build
npm run e2e

# Run end-to-end tests interactively with the Playwright UI.
e2e-ui:
npm run build
npm run e2e:ui

# Open the last Playwright HTML report.
e2e-report:
npm run e2e:report

# Format the backend code with Ruff.
format-backend:
uv run ruff check . --fix
Expand Down
47 changes: 47 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,19 @@
"lint:js": "vp lint --report-unused-disable-directives",
"lint:format": "vp fmt --check",
"lint": "npm run lint:js && npm run lint:css && npm run lint:format",
"test": "vp test run"
"test": "vp test run",
"e2e": "playwright test",
"e2e:ui": "playwright test --ui",
"e2e:report": "playwright show-report",
"e2e:install": "playwright install --with-deps chromium firefox msedge"
},
"dependencies": {
"@popperjs/core": "^2.11.5",
"bootstrap": "^5.3.8"
},
"devDependencies": {
"@playwright/test": "^1.62.1",
"@types/node": "^26.2.0",
"@wagtail/stylelint-config-wagtail": "^2.0.1",
"browserslist": "^4.29.3",
"happy-dom": "^20.14.5",
Expand Down
41 changes: 41 additions & 0 deletions playwright.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// @ts-check
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './tests/e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [['list'], ['html', { open: 'never' }]],
use: {
baseURL: 'http://127.0.0.1:8000',
locale: 'en-US',
timezoneId: 'UTC',
trace: 'on-first-retry',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'msedge',
use: { ...devices['Desktop Edge'], channel: 'msedge' },
},
],
webServer: {
command:
'rm -f db_e2e.sqlite3 && uv run python manage.py migrate && uv run python manage.py buildfixtures && uv run python manage.py runserver --noreload 127.0.0.1:8000',
url: 'http://127.0.0.1:8000/en/',
reuseExistingServer: false,
timeout: 120_000,
env: {
DJANGO_SETTINGS_MODULE: 'apps.guide.settings.e2e',
},
},
});
40 changes: 40 additions & 0 deletions tests/e2e/smoke.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { expect, test } from '@playwright/test';

test('homepage loads with title and sections', async ({ page }) => {
await page.goto('/en/');
await expect(page).toHaveURL(/\/en\/$/);
await expect(
page.getByRole('heading', { name: /Wagtail User Guide/i }),
).toBeVisible();
await expect(
page.getByRole('link', { name: /Tutorial/i }).first(),
).toBeVisible();
});

test('tutorial section page loads', async ({ page }) => {
const response = await page.goto('/en/tutorial/');
expect(response?.status()).toBe(200);
await expect(
page.getByRole('heading', { name: /Tutorial/i }).first(),
).toBeVisible();
});

test('tutorial subpage loads', async ({ page }) => {
const response = await page.goto('/en/tutorial/getting-started/');
expect(response?.status()).toBe(200);
});

test('how-to subpage loads', async ({ page }) => {
const response = await page.goto('/en/how-to/add-a-user/');
expect(response?.status()).toBe(200);
});

test('unknown page returns 404', async ({ request }) => {
const response = await request.get('/en/does-not-exist/');
expect(response.status()).toBe(404);
});

test('dutch locale loads', async ({ page }) => {
const response = await page.goto('/nl/');
expect(response?.status()).toBe(200);
});
Loading