The device routes were given a rule refusing unencrypted requests unless DEVICE_ALLOW_HTTP says otherwise. The test harness serves plain http on localhost, so /api/display started answering 403 and "changing today's hours reaches the panel" failed. The harness now sets the flag, which is honest: it has no TLS to offer. The refusal itself stays covered by lib/device/transport.test.ts, where the transport can be varied per request rather than per server. This is the failure CI existed to catch, and it caught it. I ran the unit tests after adding that rule and not the end-to-end suite, then pushed three more times on top. The device API is exactly the surface where only the end-to-end tests exercise the real request path. The workflow actions are bumped at the same time: checkout and setup-node v4 target Node 20 and were being forced onto Node 24, which the run annotated as deprecated on every build. A warning nobody reads becomes a failure eventually. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012cSY9pVhZmJUKNN7wf1Myd
92 lines
3.5 KiB
TypeScript
92 lines
3.5 KiB
TypeScript
// Loaded here, not in globalSetup: this file is evaluated first, and the
|
|
// webServer environment below is read from it. Without this, TEST_DATABASE_URL
|
|
// is still undefined when the server is started and every page fails on a
|
|
// missing DATABASE_URL.
|
|
import 'dotenv/config';
|
|
|
|
import { defineConfig, devices } from '@playwright/test';
|
|
|
|
import { E2E_AUTH_SECRET, E2E_BASE_URL, E2E_PORT } from './e2e/constants';
|
|
|
|
/**
|
|
* End-to-end tests.
|
|
*
|
|
* They run against a real server and a real database — the E2E database, which
|
|
* the global setup empties. That is the whole point: these are the only tests
|
|
* that exercise the middleware, the server actions and the rendering together,
|
|
* the way a person in the shop does.
|
|
*
|
|
* Port 3020 so a development server on 3010 can stay running.
|
|
*/
|
|
const PORT = E2E_PORT;
|
|
const BASE_URL = E2E_BASE_URL;
|
|
|
|
export default defineConfig({
|
|
testDir: './e2e',
|
|
fullyParallel: false,
|
|
workers: 1,
|
|
forbidOnly: !!process.env.CI,
|
|
retries: process.env.CI ? 1 : 0,
|
|
reporter: process.env.CI ? [['github'], ['list']] : 'list',
|
|
globalSetup: './e2e/global-setup.ts',
|
|
|
|
use: {
|
|
baseURL: BASE_URL,
|
|
trace: 'retain-on-failure',
|
|
screenshot: 'only-on-failure',
|
|
locale: 'fr-CH',
|
|
timezoneId: 'Europe/Zurich',
|
|
},
|
|
|
|
projects: [
|
|
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
|
{
|
|
// The shop uses a phone behind the counter, so the narrow viewport is
|
|
// the primary case, not an afterthought.
|
|
//
|
|
// Run on Chromium rather than WebKit: WebKit needs system packages that
|
|
// only root can install, and a suite nobody can run locally is a suite
|
|
// nobody runs. This still covers the layout, the touch targets and the
|
|
// mobile keyboard types. To exercise the real Safari engine — worth
|
|
// doing before trusting an iPhone-only bug report — install the
|
|
// dependencies once (`sudo npx playwright install-deps webkit`) and drop
|
|
// the browserName override.
|
|
name: 'mobile',
|
|
use: { ...devices['iPhone 15'], browserName: 'chromium' },
|
|
},
|
|
],
|
|
|
|
webServer: {
|
|
// A production build rather than the dev server: it is what actually ships,
|
|
// and Next refuses to start a second dev server in the same directory, so
|
|
// this also lets the suite run while someone is developing.
|
|
command: 'sh scripts/e2e-server.sh',
|
|
url: `${BASE_URL}/api/health`,
|
|
reuseExistingServer: !process.env.CI,
|
|
timeout: 120_000,
|
|
env: {
|
|
PORT: String(PORT),
|
|
NODE_ENV: 'production',
|
|
// Never the development database: the setup empties this one.
|
|
DATABASE_URL: process.env.TEST_DATABASE_URL ?? '',
|
|
AUTH_URL: BASE_URL,
|
|
AUTH_SECRET: E2E_AUTH_SECRET,
|
|
AUTHENTIK_ADMIN_GROUP: 'horaires-admins',
|
|
// The harness serves over plain http on localhost, so the device routes
|
|
// would refuse every request. The refusal itself is covered by the unit
|
|
// tests in lib/device/transport.test.ts, where the transport can be
|
|
// varied per request instead of per server.
|
|
DEVICE_ALLOW_HTTP: 'true',
|
|
// Placeholders, never contacted: the suite mints its own session cookie.
|
|
// They exist so the provider builds and the sign-in page renders its
|
|
// normal button — CI has no .env to inherit these from.
|
|
AUTH_AUTHENTIK_ID: 'e2e-client-id',
|
|
AUTH_AUTHENTIK_SECRET: 'e2e-client-secret',
|
|
AUTH_AUTHENTIK_ISSUER: 'https://auth.example.test/application/o/e2e/',
|
|
// The translation service is stubbed per-test; never called for real.
|
|
TRANSLATION_API_URL: '',
|
|
TRANSLATION_API_KEY: '',
|
|
},
|
|
},
|
|
});
|