feat(e2e): add Docker-based E2E test isolation

Add Docker setup for running E2E tests with the Angular dev server
containerized while Playwright runs on the host. Supports multiple
instances via configurable ports.

New files:
- Dockerfile.e2e.dev: Dev server image
- docker-compose.e2e.yaml: E2E orchestration config
- scripts/wait-for-app.sh: Health check script

New npm scripts:
- e2e:docker: Run E2E with containerized app
- e2e:docker:webdav: Same but includes WebDAV for sync tests

Usage: APP_PORT=4343 npm run e2e:docker
This commit is contained in:
Johannes Millan 2026-01-04 17:09:39 +01:00
parent acedc67f2a
commit 40d7118e17
5 changed files with 100 additions and 10 deletions

20
Dockerfile.e2e.dev Normal file
View file

@ -0,0 +1,20 @@
FROM node:22-bookworm
# Install Angular CLI globally and curl for healthcheck
RUN npm install -g @angular/cli && apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Copy everything (source needed for prepare script during npm install)
COPY . .
# Install dependencies
RUN npm install --legacy-peer-deps
# Default port (can be overridden via environment variable)
ENV APP_PORT=4242
EXPOSE ${APP_PORT}
# Start Angular dev server with dynamic port
CMD ["sh", "-c", "ng serve --port ${APP_PORT} --host 0.0.0.0"]

44
docker-compose.e2e.yaml Normal file
View file

@ -0,0 +1,44 @@
services:
# Angular development server for E2E tests
app:
build:
context: .
dockerfile: Dockerfile.e2e.dev
ports:
- '${APP_PORT:-4242}:${APP_PORT:-4242}'
environment:
- APP_PORT=${APP_PORT:-4242}
healthcheck:
test: ['CMD', 'curl', '-sf', 'http://localhost:${APP_PORT:-4242}']
interval: 10s
timeout: 5s
retries: 30
start_period: 120s
# WebDAV sync server (for sync tests)
webdav:
image: hacdias/webdav:latest
ports:
- '${WEBDAV_PORT:-2345}:${WEBDAV_PORT:-2345}'
environment:
- PORT=${WEBDAV_PORT:-2345}
volumes:
- ./webdav.yaml:/config.yml:ro
- webdav_data:/data
healthcheck:
test:
[
'CMD',
'wget',
'--quiet',
'--tries=1',
'--spider',
'http://localhost:${WEBDAV_PORT:-2345}/',
]
interval: 10s
timeout: 5s
retries: 3
start_period: 10s
volumes:
webdav_data:

View file

@ -67,7 +67,7 @@ export default defineConfig({
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL: 'http://localhost:4242',
baseURL: process.env.E2E_BASE_URL || 'http://localhost:4242',
/* Collect trace on failure for better debugging. See https://playwright.dev/docs/trace-viewer */
trace: 'retain-on-failure',
@ -127,15 +127,18 @@ export default defineConfig({
],
/* Run your local dev server before starting the tests */
webServer: {
command: 'npm run startFrontend:e2e',
url: 'http://localhost:4242',
reuseExistingServer: !process.env.CI, // Don't reuse in CI to ensure clean state
// unfortunately for CI we need to wait long for this to go up :(
timeout: 3 * 60 * 1000, // Allow up to 3 minutes for slower CI starts
stdout: 'ignore', // Reduce log noise
stderr: 'pipe',
},
/* When E2E_BASE_URL is set (e.g., when using Docker), skip starting the server */
webServer: process.env.E2E_BASE_URL
? undefined
: {
command: 'npm run startFrontend:e2e',
url: 'http://localhost:4242',
reuseExistingServer: !process.env.CI, // Don't reuse in CI to ensure clean state
// unfortunately for CI we need to wait long for this to go up :(
timeout: 3 * 60 * 1000, // Allow up to 3 minutes for slower CI starts
stdout: 'ignore', // Reduce log noise
stderr: 'pipe',
},
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
outputDir: path.join(__dirname, '..', '.tmp', 'e2e-test-results', 'test-results'),

View file

@ -58,6 +58,8 @@
"e2e:show-report": "npx playwright show-report .tmp/e2e-test-results/playwright-report",
"e2e:report": "PLAYWRIGHT_HTML_REPORT=1 npx playwright test --config e2e/playwright.config.ts",
"e2e:webdav": "docker compose up -d webdav && ./scripts/wait-for-webdav.sh && npm run e2e -- --grep webdav; docker compose down",
"e2e:docker": "docker compose -f docker-compose.e2e.yaml up -d app && ./scripts/wait-for-app.sh && E2E_BASE_URL=http://localhost:${APP_PORT:-4242} npm run e2e; docker compose -f docker-compose.e2e.yaml down",
"e2e:docker:webdav": "docker compose -f docker-compose.e2e.yaml up -d && ./scripts/wait-for-app.sh && ./scripts/wait-for-webdav.sh && E2E_BASE_URL=http://localhost:${APP_PORT:-4242} npm run e2e; docker compose -f docker-compose.e2e.yaml down",
"electron": "NODE_ENV=PROD electron .",
"electron:build": "tsc -p electron/tsconfig.electron.json",
"electron:watch": "tsc -p electron/tsconfig.electron.json --watch",

21
scripts/wait-for-app.sh Executable file
View file

@ -0,0 +1,21 @@
#!/bin/bash
# Wait for the Angular dev server to be ready
PORT=${APP_PORT:-4242}
MAX_WAIT=${MAX_WAIT:-180}
INTERVAL=2
echo "Waiting for app on port $PORT (max ${MAX_WAIT}s)..."
elapsed=0
until curl -sf "http://localhost:$PORT" > /dev/null 2>&1; do
if [ $elapsed -ge $MAX_WAIT ]; then
echo "Timeout: App did not start within ${MAX_WAIT}s"
exit 1
fi
sleep $INTERVAL
elapsed=$((elapsed + INTERVAL))
echo " Still waiting... (${elapsed}s)"
done
echo "App is ready on port $PORT!"