diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 9486d2d..3460e36 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -6,7 +6,7 @@ on: jobs: # ───────────────────────────────────────────── - # JOB 1: Playwright tests + # JOB 1: Playwright smoke tests # ───────────────────────────────────────────── test: name: Playwright tests @@ -19,21 +19,19 @@ jobs: uses: actions/setup-node@v4 with: node-version: '20' - cache: 'npm' - name: Install dependencies - run: npm ci --omit=dev + run: npm ci - - name: Install Playwright browsers + - name: Install Playwright + Chromium system deps run: npx playwright install chromium --with-deps - - name: Start app (static HTTP server) + - name: Start static HTTP server run: | - npm install --save-dev http-server - npx http-server . -p 9090 & - sleep 2 + npx --yes serve . -l 9090 & + sleep 3 - - name: Run Playwright tests + - name: Run smoke tests run: npx playwright test --reporter=list env: APP_URL: http://localhost:9090 @@ -52,9 +50,11 @@ jobs: - name: Setup SSH key run: | mkdir -p ~/.ssh - echo "${{ secrets.VPS_SSH_KEY }}" > ~/.ssh/id_ed25519 + printf '%s\n' "$VPS_SSH_KEY" > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 ssh-keyscan -H 80.225.185.50 >> ~/.ssh/known_hosts + env: + VPS_SSH_KEY: ${{ secrets.VPS_SSH_KEY }} - name: Upload static files run: | @@ -63,15 +63,16 @@ jobs: - name: Upload server files & rebuild run: | - ssh -i ~/.ssh/id_ed25519 ubuntu@80.225.185.50 "mkdir -p /srv/docker/builds/comarques" scp -i ~/.ssh/id_ed25519 server.js package.json Dockerfile \ ubuntu@80.225.185.50:/srv/docker/builds/comarques/ ssh -i ~/.ssh/id_ed25519 ubuntu@80.225.185.50 \ - "cd /srv/docker/builds/comarques && docker build -t comarques-de-catalunya:latest . \ - && cd /srv/docker/compose && docker compose up -d comarques" + "cd /srv/docker/builds/comarques \ + && docker build -t comarques-de-catalunya:latest . \ + && cd /srv/docker/compose \ + && docker compose up -d comarques" - name: Health check run: | sleep 5 curl -fs https://comarques.jaumegar.work/api/health \ - || (echo "❌ Health check failed after deploy" && exit 1) + || (echo "Health check failed" && exit 1) diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 0000000..bac570a --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,16 @@ +// playwright.config.js +const { defineConfig, devices } = require('@playwright/test'); + +module.exports = defineConfig({ + testDir: './tests', + timeout: 30_000, + retries: 0, + reporter: 'list', + use: { + baseURL: process.env.APP_URL || 'http://localhost:9090', + headless: true, + }, + projects: [ + { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, + ], +}); diff --git a/tests/smoke.spec.js b/tests/smoke.spec.js new file mode 100644 index 0000000..bfbd51a --- /dev/null +++ b/tests/smoke.spec.js @@ -0,0 +1,75 @@ +// smoke.spec.js — Basic smoke tests for comarques app +// Runs against a static HTTP server (no DB required). +// Playwright is invoked with chromium via npx. + +const { test, expect } = require('@playwright/test'); + +const BASE = process.env.APP_URL || 'http://localhost:9090'; + +test.describe('App loads', () => { + test('title is correct', async ({ page }) => { + await page.goto(BASE + '/index.html'); + await expect(page).toHaveTitle(/Comarques de Catalunya/); + }); + + test('welcome screen is shown on first load', async ({ page }) => { + await page.goto(BASE + '/index.html'); + // Clear any existing player so we always land on welcome + await page.evaluate(() => localStorage.clear()); + await page.reload(); + const welcome = page.locator('#screen-welcome'); + await expect(welcome).toBeVisible(); + }); +}); + +test.describe('comarca-paths.js', () => { + test('COMARCA_PATHS loads and has 43 entries', async ({ page }) => { + await page.goto(BASE + '/index.html'); + await page.waitForTimeout(500); + const count = await page.evaluate(() => Object.keys(COMARCA_PATHS).length); + expect(count).toBe(43); + }); + + test('Lluçanès and Moianès have map paths', async ({ page }) => { + await page.goto(BASE + '/index.html'); + await page.waitForTimeout(500); + const result = await page.evaluate(() => ({ + llucanes: !!COMARCA_PATHS['Lluçanès'], + moianes: !!COMARCA_PATHS['Moianès'], + })); + expect(result.llucanes).toBe(true); + expect(result.moianes).toBe(true); + }); +}); + +test.describe('Filters', () => { + test('coastal filter returns 12 comarques', async ({ page }) => { + await page.goto(BASE + '/index.html'); + await page.waitForTimeout(500); + const count = await page.evaluate(() => { + activeGroups = new Set(['coastal']); + return getActiveComarques().length; + }); + expect(count).toBe(12); + }); + + test('interior filter returns 21 comarques', async ({ page }) => { + await page.goto(BASE + '/index.html'); + await page.waitForTimeout(500); + const count = await page.evaluate(() => { + activeGroups = new Set(['interior']); + return getActiveComarques().length; + }); + expect(count).toBe(21); + }); + + test('mountain filter returns 10 comarques', async ({ page }) => { + await page.goto(BASE + '/index.html'); + await page.waitForTimeout(500); + const count = await page.evaluate(() => { + activeGroups = new Set(['mountain']); + return getActiveComarques().length; + }); + expect(count).toBe(10); + }); +});