Skip to content

test: establish vitest suite, CI, and coverage reporting (44%→83%)#24

Merged
rrbe merged 12 commits into
mainfrom
test/setup-vitest
Jun 1, 2026
Merged

test: establish vitest suite, CI, and coverage reporting (44%→83%)#24
rrbe merged 12 commits into
mainfrom
test/setup-vitest

Conversation

@rrbe
Copy link
Copy Markdown
Owner

@rrbe rrbe commented Jun 1, 2026

Background

The project previously had no tests and no CI — every change had to be verified by hand-loading the extension against real GitHub pages. This PR stands up the test infrastructure from scratch, wires up CI and coverage reporting, and raises coverage from ~44% to ~83%.

Overview

1. Test infrastructure

  • vitest + happy-dom (same toolchain as esbuild; native TS/ESM, no extra transpile)
  • happy-dom's setURL (wrapped in src/test-utils/url.ts) lets the page-detection logic switch location without touching source
  • Scripts: test / test:watch / test:coverage / typecheck

2. CI

  • .github/workflows/ci.yml: runs typecheck + test + build on PR/push (pinned pnpm major to match local; actions bumped to node24-compatible versions)

3. Coverage reporting

  • @vitest/coverage-v8 with text/html/lcov reporters; coverage/ is gitignored
  • Thresholds are a regression floor (statements 78 / branches 62 / functions 78 / lines 82), enforced only under --coverage so plain pnpm test/CI is unaffected — meant to be ratcheted up as coverage grows

4. Test coverage (173 tests / 28 files)

Mocked at the right data seam per layer, rather than one blunt approach:

Layer Mock Modules
feature → github-api vi.mock(\"../lib/github-api\") with canned data; assert rendered DOM pr-branch-names, pr-diff-stats, pr-review-status, commit-tags, commit-diff-stats, pr-approve-now, watch-fork-star (hover)
github-apichrome.runtime stub chrome.runtime.sendMessage github-api.ts (9 wrappers + invalidated-context / lastError)
service-worker → fetch mock fetch + chrome.storage request coalescing, cache TTL, token-change cache clear, stargazers/watchers/forks, GraphQL reviews/commit-diff, tags REST fallback and GraphQL success path
pure logic / DOM / infra driven directly page-detect, commit-dom, diff-stats-badge, info-row(-skeleton), default-sort, release-tab, pr-collapse-expand, better-top-repos, file-age-color, pr-label-position, feature-flags, navigation, early-sort-redirect, options, content (removeFeatureElements)

5. Real bugs found and fixed while writing tests

  • Disabling the watch/fork/star popup deleted GitHub's native counters: FEATURE_CLASSES listed bg-wfs-counter-wrap — the class we add to GitHub's own counters — as a removable element, so removeFeatureElements ran remove() on the native watch/fork/star counts on disable. Fixed to only remove our own bg-wfs-popup plus a new cleanupWatchForkStarPopup, guarded by an integration test.
  • options.ts FEATURE_KEYS was missing feature-pr-collapse-expand: caught by the registry-sync test that keeps the three feature lists aligned.
  • Also fixed pre-existing type errors that surfaced once tsc actually ran (status shadowing window.status, missing generics on chrome.storage.local.get<T>(), etc.).

Quality (not coverage for its own sake)

  • Every new test asserts observable behavior (rendered DOM, return values, mock call args), and was validated by a mutation self-check: deliberately break one source line → confirm the test goes red → restore byte-for-byte.
  • Fixed cross-test listener leakage + stderr noise caused by navigation.ts's non-teardownable module-level listeners.

Coverage

Metric Before After
Statements 44% 83%
Branches 36% 67%
Functions 50% 84%
Lines 47% 87%

Verification

  • pnpm typecheck → 0 errors
  • pnpm test → 28 files / 173 tests pass
  • pnpm test:coverage → meets thresholds (exit 0)
  • pnpm build → clean, no test files leak into dist/

Still lower / optional follow-up

watch-fork-star-popup 56%, release-tab 74%, pr-collapse-expand / content ~72% — the remainder is mostly GitHub's "new experience" DOM branches with diminishing returns; not forced in this PR.

🤖 Generated with Claude Code

rrbe and others added 12 commits June 1, 2026 12:03
These were never surfaced because esbuild strips types without checking;
adding a typecheck step exposed them.

- options.ts: rename top-level `status` to `statusEl` so it no longer
  shadows the DOM global `window.status` (a string), which broke
  `.className`/`.textContent` access
- pass the generic type arg to `chrome.storage.local.get<T>()` in
  service-worker.ts and better-top-repos.ts so results are typed instead
  of defaulting to an index signature (which truthy-narrowed to `{}`)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Set up the first batch of tests for the extension, covering the pure
page/URL/string logic in src/lib with zero source changes.

- vitest + happy-dom; happy-dom's setURL (wrapped in src/test/url.ts)
  lets page-detect read `location` without refactoring
- 35 unit tests across page-detect, page-marker, and utils
- scripts: test, test:watch, typecheck
- GitHub Actions CI runs typecheck + test + build on PR/push

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
These plan/spec notes for already-shipped features are no longer useful.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The dir holds a shared test helper (setUrl), not test files — the old
name read like a tests location next to the co-located *.test.ts files.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the checked-in editor settings and ignore the dir going forward so
local IDE config stays out of the repo.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- service-worker: add a negative case so the token-change guard
  (area === "local" && "githubToken" in changes) is actually exercised;
  a non-token local change and a token change in the sync area must
  leave the session cache intact.
- content: export removeFeatureElements and add a test driving the
  watch-fork-star removal path directly, guarding the original bug where
  FEATURE_CLASSES listed "bg-wfs-counter-wrap" (the class on GitHub's own
  counters) and disabling the feature deleted native counts.
- release-tab: clone an *active* reference tab so the selected/aria-current
  stripping logic is no longer a no-op in the tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enable the @vitest/coverage-v8 provider with text/html/lcov reporters and
a `test:coverage` script. Thresholds are set as a floor just below current
numbers (stmts 40 / branch 32 / funcs 45 / lines 42) to guard against large
regressions and be ratcheted up over time. They only apply under --coverage,
so the plain `pnpm test` used in CI stays unaffected. coverage/ is gitignored.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ules

Adds tests for the previously-untested modules, lifting coverage from
~44% to ~83% statements / ~87% lines.

Two data seams are mocked:
- Feature modules (pr-branch-names, pr-diff-stats, pr-review-status,
  commit-tags, commit-diff-stats, pr-approve-now, watch-fork-star hover)
  mock "../lib/github-api" and assert the rendered DOM.
- github-api.ts mocks chrome.runtime.sendMessage; service-worker mocks
  fetch (extended to stargazers/watchers/forks, GraphQL reviews/commit
  diff stats, and the tags GraphQL success path).

Pure/infra modules covered directly: better-top-repos, file-age-color,
pr-label-position, feature-flags, navigation, early-sort-redirect, options.

options.ts gains a trailing `export {}` so tests can import() it (no
runtime effect). Coverage thresholds raised to a new floor (78/62/78/82).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@rrbe rrbe changed the title test: add vitest suite with happy-dom and CI test: 建立 vitest 测试体系、CI 与覆盖率报告,核心模块覆盖率 44%→83% Jun 1, 2026
@rrbe rrbe changed the title test: 建立 vitest 测试体系、CI 与覆盖率报告,核心模块覆盖率 44%→83% test: establish vitest suite, CI, and coverage reporting (44%→83%) Jun 1, 2026
@rrbe rrbe merged commit 80f194e into main Jun 1, 2026
1 check passed
@rrbe rrbe deleted the test/setup-vitest branch June 1, 2026 08:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant