From 40d7118e179ae46670d0180460a1255d4f63f903 Mon Sep 17 00:00:00 2001 From: Johannes Millan Date: Sun, 4 Jan 2026 17:09:39 +0100 Subject: [PATCH] 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 --- Dockerfile.e2e.dev | 20 ++++++++++++++++++ docker-compose.e2e.yaml | 44 ++++++++++++++++++++++++++++++++++++++++ e2e/playwright.config.ts | 23 ++++++++++++--------- package.json | 2 ++ scripts/wait-for-app.sh | 21 +++++++++++++++++++ 5 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 Dockerfile.e2e.dev create mode 100644 docker-compose.e2e.yaml create mode 100755 scripts/wait-for-app.sh diff --git a/Dockerfile.e2e.dev b/Dockerfile.e2e.dev new file mode 100644 index 000000000..f30073179 --- /dev/null +++ b/Dockerfile.e2e.dev @@ -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"] diff --git a/docker-compose.e2e.yaml b/docker-compose.e2e.yaml new file mode 100644 index 000000000..46cecc5d3 --- /dev/null +++ b/docker-compose.e2e.yaml @@ -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: diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index eb0efeb80..356cff383 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -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'), diff --git a/package.json b/package.json index 476b4b831..cdda3b533 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/scripts/wait-for-app.sh b/scripts/wait-for-app.sh new file mode 100755 index 000000000..2e043ca49 --- /dev/null +++ b/scripts/wait-for-app.sh @@ -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!"