From bdb1911e74fdb9d4ca720faeac7916ae9686ebf2 Mon Sep 17 00:00:00 2001 From: StroepWafel Date: Sat, 27 Jun 2026 15:20:52 +0930 Subject: [PATCH] feat(CI): modular testing and linting system + cursor rule --- .github/actions/build-extension/action.yml | 10 +--- .github/actions/run-lint/action.yml | 11 +++++ .github/actions/run-smoke-tests/action.yml | 9 ++++ .github/actions/run-unit-tests/action.yml | 9 ++++ .github/actions/setup-node-deps/action.yml | 14 ++++++ .github/workflows/mvp.yml | 36 -------------- .github/workflows/pr-ci.yml | 47 ++++++++++++------- jest.config.js | 1 + package.json | 5 +- .../src/indexing/selfTests.test.ts | 12 +++++ .../globalSearch/src/indexing/selfTests.ts | 10 ++-- .../globalSearch/src/indexing/utils.ts | 2 +- src/test/jest.setup.ts | 5 ++ src/test/mocks/webextension-polyfill.ts | 12 ++++- 14 files changed, 114 insertions(+), 69 deletions(-) create mode 100644 .github/actions/run-lint/action.yml create mode 100644 .github/actions/run-smoke-tests/action.yml create mode 100644 .github/actions/run-unit-tests/action.yml create mode 100644 .github/actions/setup-node-deps/action.yml delete mode 100644 .github/workflows/mvp.yml create mode 100644 src/plugins/built-in/globalSearch/src/indexing/selfTests.test.ts create mode 100644 src/test/jest.setup.ts diff --git a/.github/actions/build-extension/action.yml b/.github/actions/build-extension/action.yml index 6d71a1b8..57918b56 100644 --- a/.github/actions/build-extension/action.yml +++ b/.github/actions/build-extension/action.yml @@ -33,14 +33,8 @@ outputs: runs: using: composite steps: - - name: Use Node.js 22.x - uses: actions/setup-node@v4 - with: - node-version: 22.x - - - name: Install dependencies - shell: bash - run: npm install --legacy-peer-deps + - name: Setup Node and dependencies + uses: ./.github/actions/setup-node-deps - name: Read version id: version diff --git a/.github/actions/run-lint/action.yml b/.github/actions/run-lint/action.yml new file mode 100644 index 00000000..6f9e9ae4 --- /dev/null +++ b/.github/actions/run-lint/action.yml @@ -0,0 +1,11 @@ +name: Run lint +description: Run ESLint on src. + +runs: + using: composite + steps: + - name: Lint + shell: bash + run: npm run lint + env: + ESLINT_USE_FLAT_CONFIG: "false" diff --git a/.github/actions/run-smoke-tests/action.yml b/.github/actions/run-smoke-tests/action.yml new file mode 100644 index 00000000..4723f5ce --- /dev/null +++ b/.github/actions/run-smoke-tests/action.yml @@ -0,0 +1,9 @@ +name: Run smoke tests +description: Verify built extension dist output. + +runs: + using: composite + steps: + - name: Smoke tests + shell: bash + run: npm run test:smoke diff --git a/.github/actions/run-unit-tests/action.yml b/.github/actions/run-unit-tests/action.yml new file mode 100644 index 00000000..6c92a48a --- /dev/null +++ b/.github/actions/run-unit-tests/action.yml @@ -0,0 +1,9 @@ +name: Run unit tests +description: Run Jest unit tests. + +runs: + using: composite + steps: + - name: Unit tests + shell: bash + run: npm run test:unit diff --git a/.github/actions/setup-node-deps/action.yml b/.github/actions/setup-node-deps/action.yml new file mode 100644 index 00000000..c1e247c6 --- /dev/null +++ b/.github/actions/setup-node-deps/action.yml @@ -0,0 +1,14 @@ +name: Setup Node and dependencies +description: Install Node.js 22.x and npm dependencies. + +runs: + using: composite + steps: + - name: Use Node.js 22.x + uses: actions/setup-node@v4 + with: + node-version: 22.x + + - name: Install dependencies + shell: bash + run: npm install --legacy-peer-deps diff --git a/.github/workflows/mvp.yml b/.github/workflows/mvp.yml deleted file mode 100644 index c461ef9f..00000000 --- a/.github/workflows/mvp.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: NodeJS Build - -on: - push: - branches: ["main"] - -jobs: - build: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [20.x] - - steps: - - uses: actions/checkout@v4 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - - name: Build - run: | - npm install --legacy-peer-deps - npm run build - - - name: Zip dist folder - run: | - zip -r dist.zip dist - - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: dist-zip - path: dist.zip diff --git a/.github/workflows/pr-ci.yml b/.github/workflows/pr-ci.yml index 1744162a..fbf2ce76 100644 --- a/.github/workflows/pr-ci.yml +++ b/.github/workflows/pr-ci.yml @@ -1,11 +1,13 @@ -name: PR CI +name: CI on: pull_request: branches: ["main"] + push: + branches: ["main"] jobs: - ci: + lint: # windows-latest: Vite/Svelte build fails on Linux CI for layerchart vendor .svelte (see nightly.yml). runs-on: windows-latest defaults: @@ -14,21 +16,34 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Use Node.js 22.x - uses: actions/setup-node@v4 - with: - node-version: 22.x + - name: Setup Node and dependencies + uses: ./.github/actions/setup-node-deps - - name: Install dependencies - run: npm install --legacy-peer-deps + - name: Run lint + uses: ./.github/actions/run-lint - - name: Lint - run: npm run lint - env: - ESLINT_USE_FLAT_CONFIG: "false" + unit-tests: + runs-on: windows-latest + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 - - name: Unit tests - run: npm test + - name: Setup Node and dependencies + uses: ./.github/actions/setup-node-deps + + - name: Run unit tests + uses: ./.github/actions/run-unit-tests + + build-and-smoke: + needs: [lint, unit-tests] + runs-on: windows-latest + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 - name: Build extension id: build @@ -44,5 +59,5 @@ jobs: ${{ steps.build.outputs.chrome_zip }} ${{ steps.build.outputs.firefox_zip }} - - name: Smoke tests - run: npm run test:smoke + - name: Run smoke tests + uses: ./.github/actions/run-smoke-tests diff --git a/jest.config.js b/jest.config.js index 3903c51f..b581d02a 100644 --- a/jest.config.js +++ b/jest.config.js @@ -17,6 +17,7 @@ export default { '^@/(.*)$': '/src/$1', '^webextension-polyfill$': '/src/test/mocks/webextension-polyfill.ts', }, + setupFilesAfterEnv: ['/src/test/jest.setup.ts'], moduleFileExtensions: ['ts', 'js', 'json'], collectCoverageFrom: [ 'src/**/*.ts', diff --git a/package.json b/package.json index cddffb3b..3318f047 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,10 @@ "convert:safari": "xcrun safari-web-extension-converter dist/safari --project-location . --app-name $npm_package_name-safari", "dependency-graph": "depcruise src --include-only \"^src\" --output-type dot | dot -T svg > dependency-graph.svg", "lint": "cross-env ESLINT_USE_FLAT_CONFIG=false eslint \"src/**/*.{js,ts}\"", - "test": "jest", + "test": "npm run test:unit", + "test:unit": "jest", "test:smoke": "node scripts/smoke-test.mjs", + "test:ci": "npm run test:unit && npm run build && npm run test:smoke", "release": "gh release create $npm_package_version --repo BetterSEQTA/BetterSEQTA-Plus ./dist/*.zip --generate-notes", "publish": "bun lib/publish.js --b", "zip": "bedframe zip" @@ -58,6 +60,7 @@ "eslint-plugin-import": "^2.31.0", "glob": "^11.0.1", "jest": "^30.4.2", + "jest-environment-jsdom": "^30.4.1", "mime-types": "^3.0.1", "prettier": "^3.5.3", "process": "^0.11.10", diff --git a/src/plugins/built-in/globalSearch/src/indexing/selfTests.test.ts b/src/plugins/built-in/globalSearch/src/indexing/selfTests.test.ts new file mode 100644 index 00000000..b6c6b7f5 --- /dev/null +++ b/src/plugins/built-in/globalSearch/src/indexing/selfTests.test.ts @@ -0,0 +1,12 @@ +/** + * @jest-environment jsdom + */ +import { runGlobalSearchSelfTests } from "./selfTests"; + +describe("globalSearch selfTests", () => { + it("all in-process cases pass", async () => { + const report = await runGlobalSearchSelfTests(); + expect(report.failed).toBe(0); + expect(report.failures).toEqual([]); + }); +}); diff --git a/src/plugins/built-in/globalSearch/src/indexing/selfTests.ts b/src/plugins/built-in/globalSearch/src/indexing/selfTests.ts index f85d6abe..27f46175 100644 --- a/src/plugins/built-in/globalSearch/src/indexing/selfTests.ts +++ b/src/plugins/built-in/globalSearch/src/indexing/selfTests.ts @@ -22,12 +22,10 @@ import { /** * Lightweight in-process self-tests for the global-search overhaul. * - * The repository does not (yet) ship with a test runner, so we instead - * expose a deterministic suite of assertions over the pure helpers that - * back active jobs and the passive observer. This is intentionally - * dependency-free so it can run inside the extension page (`window. - * globalSearchDebug.runSelfTests()`) and from any future Vitest harness - * without modification. + * Exposes a deterministic suite of assertions over the pure helpers that + * back active jobs and the passive observer. Runs in Jest via + * `selfTests.test.ts`, and inside the extension page via + * `window.globalSearchDebug.runSelfTests()`. */ interface TestCase { diff --git a/src/plugins/built-in/globalSearch/src/indexing/utils.ts b/src/plugins/built-in/globalSearch/src/indexing/utils.ts index 9de11592..0ee9a245 100644 --- a/src/plugins/built-in/globalSearch/src/indexing/utils.ts +++ b/src/plugins/built-in/globalSearch/src/indexing/utils.ts @@ -141,7 +141,7 @@ export function htmlToPlainText(rawHtml: string): string { } }); - let text = body.innerText || ""; + let text = body.textContent || body.innerText || ""; text = text .replace(/\u00A0/g, " ") diff --git a/src/test/jest.setup.ts b/src/test/jest.setup.ts new file mode 100644 index 00000000..ab105650 --- /dev/null +++ b/src/test/jest.setup.ts @@ -0,0 +1,5 @@ +import { __resetBrowserStorageMock } from "./mocks/webextension-polyfill"; + +afterEach(() => { + __resetBrowserStorageMock(); +}); diff --git a/src/test/mocks/webextension-polyfill.ts b/src/test/mocks/webextension-polyfill.ts index 6171d913..0acee899 100644 --- a/src/test/mocks/webextension-polyfill.ts +++ b/src/test/mocks/webextension-polyfill.ts @@ -23,8 +23,16 @@ const local = { }), }; +const onChanged = { + addListener: jest.fn(), + removeListener: jest.fn(), +}; + export default { - storage: { local }, + storage: { local, onChanged }, + runtime: { + sendMessage: jest.fn(async () => undefined), + }, }; export function __resetBrowserStorageMock() { @@ -32,4 +40,6 @@ export function __resetBrowserStorageMock() { local.get.mockClear(); local.set.mockClear(); local.remove.mockClear(); + onChanged.addListener.mockClear(); + onChanged.removeListener.mockClear(); }