From 1429bdae6911d20120eec1fe397a30d3962e9884 Mon Sep 17 00:00:00 2001 From: Clifford <33897029+cray-com@users.noreply.github.com> Date: Sun, 30 Aug 2026 20:48:57 +0200 Subject: [PATCH] ci: validate Astro and browser contracts portably --- .github/workflows/validate.yml | 25 +++++++++++++ package-lock.json | 64 ++++++++++++++++++++++++++++++++++ package.json | 1 + test/browser-smoke.test.mjs | 44 ++++++++--------------- 4 files changed, 104 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/validate.yml diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..89bd6eb --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,25 @@ +name: Validate + +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - run: npm ci + - run: npx playwright install --with-deps chromium + - run: npm run check + - run: npm test + - run: npm run build diff --git a/package-lock.json b/package-lock.json index d26c29f..01d589b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.1", "license": "MIT", "devDependencies": { + "@playwright/test": "^1.61.1", "astro": "^7.1.3", "esbuild": "^0.25.12", "typescript": "^6.0.3" @@ -1573,6 +1574,22 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@playwright/test": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz", + "integrity": "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@rolldown/binding-android-arm-eabi": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.6.tgz", @@ -4108,6 +4125,53 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.61.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/postcss": { "version": "8.5.26", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", diff --git a/package.json b/package.json index 4914fe6..4e91a2c 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "astro": ">=5" }, "devDependencies": { + "@playwright/test": "^1.61.1", "astro": "^7.1.3", "esbuild": "^0.25.12", "typescript": "^6.0.3" diff --git a/test/browser-smoke.test.mjs b/test/browser-smoke.test.mjs index 6c4dd3b..3188f93 100644 --- a/test/browser-smoke.test.mjs +++ b/test/browser-smoke.test.mjs @@ -1,33 +1,8 @@ import test from 'node:test'; import assert from 'node:assert/strict'; -import { mkdtemp, readFile, rm } from 'node:fs/promises'; -import { tmpdir } from 'node:os'; -import { join } from 'node:path'; -import { spawn } from 'node:child_process'; - -async function chromium(url) { - const profile = await mkdtemp(join(tmpdir(), 'foundation-devtools-chromium-')); - try { - return await new Promise((resolve, reject) => { - const child = spawn('chromium', [ - '--headless=new', '--no-sandbox', '--disable-gpu', '--allow-file-access-from-files', '--force-prefers-reduced-motion', - `--user-data-dir=${profile}`, '--virtual-time-budget=1000', '--dump-dom', url, - ], { stdio: ['ignore', 'pipe', 'pipe'] }); - const timeout = setTimeout(() => child.kill('SIGKILL'), 30000); - let output = ''; - let errors = ''; - child.stdout.on('data', (chunk) => { output += chunk; }); - child.stderr.on('data', (chunk) => { errors += chunk; }); - child.on('error', reject); - child.on('close', (code) => { - clearTimeout(timeout); - code === 0 ? resolve(output) : reject(new Error(errors || `Chromium exited with ${code}`)); - }); - }); - } finally { - await rm(profile, { force: true, recursive: true }); - } -} +import { readFile } from 'node:fs/promises'; +import { pathToFileURL } from 'node:url'; +import { chromium } from '@playwright/test'; test('Astro fixture covers production and browser contracts', async () => { const html = await readFile('fixture/index.html', 'utf8'); @@ -37,6 +12,15 @@ test('Astro fixture covers production and browser contracts', async () => { const production = await readFile('fixture/astro/dist/index.html', 'utf8'); assert.doesNotMatch(production, /foundation-devtools|data-fd-config/); - const output = await chromium(`file://${process.cwd()}/fixture/smoke.html`); - assert.match(output, /