ci: fix runner image (node:20), add Playwright smoke tests
Some checks failed
CI · Test & Deploy / Playwright tests (push) Failing after 3m55s
CI · Test & Deploy / Deploy to VPS (push) Has been skipped

- Change runner to node:20 (has bash) instead of node:20-alpine
- Add tests/smoke.spec.js: 7 tests covering title, welcome screen,
  COMARCA_PATHS count (43), Lluçanès/Moianès presence, and
  filter counts (coastal=12, interior=21, mountain=10)
- Add playwright.config.js
- Fix CI workflow: use 'serve' for static server, proper SSH key setup
This commit is contained in:
Jaume Garriga Maestre
2026-05-14 11:11:39 +02:00
parent 8c9b1ccfce
commit 2f90d41be3
3 changed files with 106 additions and 14 deletions

View File

@@ -6,7 +6,7 @@ on:
jobs: jobs:
# ───────────────────────────────────────────── # ─────────────────────────────────────────────
# JOB 1: Playwright tests # JOB 1: Playwright smoke tests
# ───────────────────────────────────────────── # ─────────────────────────────────────────────
test: test:
name: Playwright tests name: Playwright tests
@@ -19,21 +19,19 @@ jobs:
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: '20' node-version: '20'
cache: 'npm'
- name: Install dependencies - 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 run: npx playwright install chromium --with-deps
- name: Start app (static HTTP server) - name: Start static HTTP server
run: | run: |
npm install --save-dev http-server npx --yes serve . -l 9090 &
npx http-server . -p 9090 & sleep 3
sleep 2
- name: Run Playwright tests - name: Run smoke tests
run: npx playwright test --reporter=list run: npx playwright test --reporter=list
env: env:
APP_URL: http://localhost:9090 APP_URL: http://localhost:9090
@@ -52,9 +50,11 @@ jobs:
- name: Setup SSH key - name: Setup SSH key
run: | run: |
mkdir -p ~/.ssh 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 chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H 80.225.185.50 >> ~/.ssh/known_hosts ssh-keyscan -H 80.225.185.50 >> ~/.ssh/known_hosts
env:
VPS_SSH_KEY: ${{ secrets.VPS_SSH_KEY }}
- name: Upload static files - name: Upload static files
run: | run: |
@@ -63,15 +63,16 @@ jobs:
- name: Upload server files & rebuild - name: Upload server files & rebuild
run: | 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 \ scp -i ~/.ssh/id_ed25519 server.js package.json Dockerfile \
ubuntu@80.225.185.50:/srv/docker/builds/comarques/ ubuntu@80.225.185.50:/srv/docker/builds/comarques/
ssh -i ~/.ssh/id_ed25519 ubuntu@80.225.185.50 \ 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/builds/comarques \
&& cd /srv/docker/compose && docker compose up -d comarques" && docker build -t comarques-de-catalunya:latest . \
&& cd /srv/docker/compose \
&& docker compose up -d comarques"
- name: Health check - name: Health check
run: | run: |
sleep 5 sleep 5
curl -fs https://comarques.jaumegar.work/api/health \ curl -fs https://comarques.jaumegar.work/api/health \
|| (echo "Health check failed after deploy" && exit 1) || (echo "Health check failed" && exit 1)

16
playwright.config.js Normal file
View File

@@ -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'] } },
],
});

75
tests/smoke.spec.js Normal file
View File

@@ -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);
});
});