From 9c6174cc1ff36a13e6d25217059ebb134ba6ceed Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Sat, 11 Oct 2025 15:53:05 -0400 Subject: [PATCH] Add consolidated CI pipeline with cached deps and tests --- .github/workflows/ci.yml | 78 ++++++++++++++++++++++++++++++++++++++ .github/workflows/css.yml | 67 -------------------------------- README.md | 14 +++---- package.json | 3 +- scripts/run-unit-tests.mjs | 76 +++++++++++++++++++++++++++++++++++++ 5 files changed, 163 insertions(+), 75 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/css.yml create mode 100644 scripts/run-unit-tests.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..72c235b9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,78 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [20] + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Cache npm directory + uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ matrix.node-version }}-npm-${{ hashFiles('package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ matrix.node-version }}-npm- + + - name: Cache node_modules + id: node-modules-cache + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-node-${{ matrix.node-version }}-modules-${{ hashFiles('package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ matrix.node-version }}-modules- + + - name: Install dependencies + if: steps.node-modules-cache.outputs.cache-hit != 'true' + run: npm ci + + - name: Run CSS lint + run: npm run lint:css + + - name: Build CSS + run: npm run build:css + + - name: Install Playwright browsers + run: npx playwright install --with-deps chromium + + - name: Run visual regression tests + id: visual + run: npm run test:visual + continue-on-error: true + + - name: Upload Playwright artifacts + if: steps.visual.outcome == 'failure' + uses: actions/upload-artifact@v4 + with: + name: playwright-visual-diffs + path: | + playwright-report + test-results + if-no-files-found: ignore + + - name: Run unit tests + run: npm run test:unit + + - name: Check CSS artifacts are clean + run: npm run check:css + + - name: Fail if visual regressions detected + if: steps.visual.outcome == 'failure' + run: exit 1 diff --git a/.github/workflows/css.yml b/.github/workflows/css.yml deleted file mode 100644 index 87620fa4..00000000 --- a/.github/workflows/css.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: CSS & Visual Regression - -on: - pull_request: - paths: - - "css/**" - - "docs/**" - - "tests/visual/**" - - "package-lock.json" - - "package.json" - - "playwright.config.ts" - - "tailwind.config.cjs" - - ".github/workflows/css.yml" - push: - branches: - - main - paths: - - "css/**" - - "docs/**" - - "tests/visual/**" - - "package-lock.json" - - "package.json" - - "playwright.config.ts" - - "tailwind.config.cjs" - - ".github/workflows/css.yml" - -jobs: - css: - runs-on: ubuntu-latest - - steps: - - name: Check out repository - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: npm - - - name: Install dependencies - run: npm ci - - - name: Check CSS build artifacts are committed - run: npm run check:css - - - name: Install Playwright browsers - run: npx playwright install --with-deps chromium - - - name: Run visual regression tests - id: visual - run: npm run test:visual - continue-on-error: true - - - name: Upload Playwright artifacts - if: steps.visual.outcome == 'failure' - uses: actions/upload-artifact@v4 - with: - name: playwright-visual-diffs - path: | - playwright-report - test-results - if-no-files-found: ignore - - - name: Fail if visual regressions detected - if: steps.visual.outcome == 'failure' - run: exit 1 diff --git a/README.md b/README.md index 4ac8098f..9f31120b 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,13 @@ placeholder at “—” and development builds log a warning—so mixed deploym ## Testing -Use the manual QA checklist before releases or when altering upload/playback flows: +Continuous integration runs CSS linting/builds, the Playwright kitchen-sink snapshots, and the Node-based unit tests on every push. +Before pushing, run `npm run check:css` locally so `css/tailwind.generated.css` stays clean. Pair that with `npm run test:unit` for +application logic changes and `npm run test:visual` for presentation updates to mirror the CI surface area. + +### Manual QA checklist + +Use this checklist before releases or when altering upload/playback flows: 1. Open the Upload modal, confirm validation (title plus URL or magnet), and test submissions for URL-only, magnet-only, and combined entries. 2. Publish a post with both URL and magnet, verify the player streams the hosted URL, then simulate a URL failure and confirm WebTorrent playback. @@ -173,12 +179,6 @@ Use the manual QA checklist before releases or when altering upload/playback flo See [`docs/qa.md`](docs/qa.md) for the copy/paste-friendly checklist we share with QA. -For automated DOM-focused regression coverage, run the Node-based test suite: - -```bash -node tests/profile-modal-controller.test.mjs -``` - --- ## Acknowledgments diff --git a/package.json b/package.json index b1f30be0..51782d83 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "lint:hex": "node scripts/check-hex.js", "check:css": "npm run build:css && git diff --exit-code -- css/tailwind.generated.css", "test:visual": "npm run build:css && npx playwright test", - "test:visual:update": "npm run build:css && cross-env UPDATE_VISUAL_BASELINES=1 npx playwright test" + "test:visual:update": "npm run build:css && cross-env UPDATE_VISUAL_BASELINES=1 npx playwright test", + "test:unit": "node scripts/run-unit-tests.mjs" }, "devDependencies": { "@playwright/test": "^1.56.0", diff --git a/scripts/run-unit-tests.mjs b/scripts/run-unit-tests.mjs new file mode 100644 index 00000000..ff860c0e --- /dev/null +++ b/scripts/run-unit-tests.mjs @@ -0,0 +1,76 @@ +import { spawn } from "node:child_process"; +import { readdir, readFile } from "node:fs/promises"; +import { fileURLToPath } from "node:url"; +import path from "node:path"; + +const rootDir = path.dirname(fileURLToPath(new URL("../package.json", import.meta.url))); +const testsDir = path.join(rootDir, "tests"); +const testFiles = []; + +async function collectTests(dir) { + const entries = await readdir(dir, { withFileTypes: true }); + + for (const entry of entries) { + if (entry.name.startsWith(".")) { + continue; + } + + const fullPath = path.join(dir, entry.name); + + if (entry.isDirectory()) { + if (entry.name === "visual") { + continue; + } + + await collectTests(fullPath); + continue; + } + + if (entry.isFile() && entry.name.endsWith(".test.mjs")) { + const content = await readFile(fullPath, "utf8"); + if (content.includes("node:test")) { + testFiles.push(fullPath); + } + } + } +} + +await collectTests(testsDir); + +testFiles.sort((a, b) => a.localeCompare(b)); + +if (testFiles.length === 0) { + console.log("No unit tests found."); + process.exit(0); +} + +for (const testFile of testFiles) { + const relativePath = path.relative(rootDir, testFile); + console.log(`\n→ Running ${relativePath}`); + + await new Promise((resolve, reject) => { + const child = spawn(process.execPath, [testFile], { + stdio: "inherit", + }); + + child.on("close", (code, signal) => { + if (signal) { + reject(new Error(`${relativePath} exited with signal ${signal}`)); + return; + } + + if (code !== 0) { + reject(new Error(`${relativePath} failed with exit code ${code}`)); + return; + } + + resolve(); + }); + + child.on("error", (error) => { + reject(error); + }); + }); +} + +console.log("\n✔ All unit tests passed");