From 1ae8bc42cd4073792b27fab3acdaa1077274d6b1 Mon Sep 17 00:00:00 2001 From: blogcastAI Date: Mon, 3 Aug 2026 07:25:20 -0500 Subject: [PATCH] Make the puppeteer test suite run headless with no local server validate.test.cjs previously required a manually started server on localhost:8000 and a headed browser (headless: false), so npm test always failed 26 tests out of the box and the suite could not run in CI. - jest-puppeteer.config.cjs: headless by default; HEADFUL=1 restores a visible browser for debugging; pass --no-sandbox in CI containers. - validate.test.cjs: navigate to an intercepted http://ableplayer.test/ origin fulfilled from memory instead of localhost:8000. A real http(s) origin is still required because isProtocolSafe() resolves relative URLs against window.location.origin (opaque on about:blank), but no server process is needed. - ci.yml: new test-browser job runs the puppeteer project (after npm run build, which produces build/test/validate.umd.js). npm test now passes 81/81 locally with zero setup. Build artifacts intentionally omitted per contributing.md. Co-Authored-By: Claude Fable 5 --- .github/workflows/ci.yml | 24 +++++++++++++++++++----- jest-puppeteer.config.cjs | 6 +++++- scripts/__tests__/validate.test.cjs | 18 +++++++++++++++++- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index edc7f0d7..c234e7ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,10 +1,10 @@ # Continuous integration for Able Player # -# Runs ESLint and the jsdom Jest project on every push and pull request. -# The puppeteer Jest project (validate.test.cjs) needs a demo server on -# localhost:8000 and a headed browser, so it is not run here yet — see -# jest.config.cjs. Build artifacts are compiled to confirm Grunt + Rollup -# succeed, but are never committed (per contributing.md). +# Runs ESLint and both Jest projects (jsdom + puppeteer) on every push and +# pull request. The puppeteer project runs headless with request +# interception, so no demo server is needed. Build artifacts are compiled +# to confirm Grunt + Rollup succeed, but are never committed (per +# contributing.md). name: CI on: @@ -40,6 +40,20 @@ jobs: - run: npm ci - run: npx jest --selectProjects jsdom + test-browser: + name: Jest (puppeteer, headless) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + - run: npm ci + # validate.test.cjs loads build/test/validate.umd.js + - run: npm run build + - run: npx jest --selectProjects puppeteer + build: name: Build (Grunt + Rollup) runs-on: ubuntu-latest diff --git a/jest-puppeteer.config.cjs b/jest-puppeteer.config.cjs index 00aa6e72..84e96807 100644 --- a/jest-puppeteer.config.cjs +++ b/jest-puppeteer.config.cjs @@ -1,6 +1,10 @@ // jest-puppeteer.config.js +// Headless by default so `npm test` runs unattended (locally and in CI). +// Set HEADFUL=1 to watch the browser while debugging. module.exports = { launch: { - headless: false, // Set to true to run tests in headless mode + headless: process.env.HEADFUL ? false : true, + // Chromium's sandbox is unavailable in most CI containers. + args: process.env.CI ? ["--no-sandbox", "--disable-setuid-sandbox"] : [], }, }; diff --git a/scripts/__tests__/validate.test.cjs b/scripts/__tests__/validate.test.cjs index a9315fa0..1325330d 100644 --- a/scripts/__tests__/validate.test.cjs +++ b/scripts/__tests__/validate.test.cjs @@ -8,7 +8,23 @@ const path = require("path"); */ describe("validate.js tests", () => { beforeAll(async () => { - await page.goto("http://localhost:8000"); // Replace with your test URL + // The suite needs a real http(s) origin (isProtocolSafe resolves + // relative URLs against window.location.origin, which is opaque on + // about:blank), but no actual server: intercept the navigation and + // fulfill it with an empty page. + await page.setRequestInterception(true); + page.on("request", (request) => { + if (request.url().startsWith("http://ableplayer.test/")) { + request.respond({ + status: 200, + contentType: "text/html", + body: "", + }); + } else { + request.continue(); + } + }); + await page.goto("http://ableplayer.test/"); const validatePath = path.resolve(__dirname, "../../build/test/validate.umd.js"); // Add DOMPurify script const domPurifyPath = path.resolve(