Skip to content

Commit 26f10a3

Browse files
robertsLandoclaude
andauthored
test: split unit tests into node:test suite, add c8 coverage (#271)
* test: split unit tests into node:test suite and add c8 coverage Three tests under test/test-XX-*/ were already in-process assertions (sea-picker, common, config-parse) — they required('../../lib-es5/...') without ever spawning pkg or producing a binary. Keeping them in the e2e harness paid the per-directory spawn cost for trivial checks and mixed "lib regression" with "build regression" under one runner. Migrate them to test/unit/*.test.ts on Node's built-in node:test runner, loaded via esbuild-register so the lib/ TS source is imported directly — no yarn build required. 96 assertions run in ~1.3s. Coverage is wired through c8 (thin reporter over NODE_V8_COVERAGE). utils.pkg.sync already spreads process.env into the spawned pkg CLI, so coverage:e2e captures subprocess coverage with zero code changes. A second c8 run with --clean=false merges unit + e2e into one report. Baseline from yarn coverage:unit: lib/ 25.49% statements / 85.78% branch. Highest per-file: config.ts 66.73%, common.ts 57.60%, sea.ts 25.86%. CI: yarn test:unit rides the existing build matrix (3 OS × Node 22/24), giving common.test.ts's win32 branch and Node 24 coverage at ~1s/cell. Closes #267. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * test(unit): extend coverage — compress_type, detector, esm-transformer, common, config Add three new test files and extend the two existing ones. Focus is on the pure-pure modules — AST helpers, path predicates, enum gates, esbuild-based transformation — without touching the integration-heavy ones (walker, producer, packer, fabricator, resolver, sea-assets) that inherently need fixtures pkg already stresses via the e2e suite. New: test/unit/compress-type.test.ts CompressType enum layout (bytecode-format stability guard) and getZstdCompressSync/Stream availability branches. New: test/unit/detector.test.ts parse() tolerant flags (decorator-legacy, top-level return, import.meta), detect() warn-don't-throw + descent control, and every public visitor (successful / nonLiteral / malformed / useSCWD) with literal, dynamic-expr, and must/may-exclude hints. New: test/unit/esm-transformer.test.ts rewriteMjsRequirePaths regex across quote styles + relative/bare specifiers; transformESMtoCJS for top-level-await wrapping (with and without exports), import.meta shim injection, and esbuild-failure handling. Extended: test/unit/common.test.ts isPackageJson / isDot{JS,JSON,NODE} / unlikelyJavascript / replaceSlashes / isRootPath / isESMPackage / isESMFile (latter two use an mkdtemp fixture — still ~10ms, no subprocess). Extended: test/unit/config-parse.test.ts isConfiguration, stringifyTarget, PKGRC_FILENAMES precedence, and findPkgrc against an mkdtemp fixture. Coverage baseline (yarn coverage:unit): lib/ overall: 25.49% → 35.62% stmts / 79.78% branch common.ts: 57.60% → 79.20% detector.ts: 13.64% → 80.53% esm-transformer: 20.04% → 74.88% compress_type.ts: 59.09% → 77.27% Totals: 162 pass / 0 fail in ~1.7s (up from 96 in ~1.3s). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * test(unit): cover target parser, TLA-with-imports, detector + realpath edges Hot path: parseTargets is called on every pkg invocation. Previously untestable because it was internal — exported alongside differentParts and stringifyTargetForOutput with a test-only comment, matching the pattern already used for pickMatchingHostTargetIndex. config.ts target parser: 'host' short-circuit, single-token host-fill (platform/arch/ nodeRange), full triple in any token order, repeated-dash empty tokens, fancy-alias ('windows' → 'win'), unknown-token error with offending spec in message, list-order preservation, round-trip with stringifyTarget, differentParts on every dimension combo, stringifyTargetForOutput with each subset of varying axes. esm-transformer.ts closes the remaining success branch: Top-level await + imports but no exports — imports are kept at top level so esbuild can rewrite them, body goes into the async IIFE. Also: for-await-of at top level triggers the same wrap; await inside a function does NOT trigger it. detector.ts edges: visitorMalformed on require(dynamicExpr); visitorUseSCWD with multi-arg reconstruction, zero args (empty alias), 'url.resolve' (non-path object), bare resolve() call. common.ts toNormalizedRealPath: missing-path pass-through, existing-dir realpath, symlink → target (symlink subtest skipped on Windows — requires admin/Dev Mode). Coverage baseline: 35.62% → 37.42% stmts / 82.08% branch esm-transformer.ts: 74.88% → 87.09% (TLA branch closed) config.ts: 67.69% → 73.51% (target parser exercised) common.ts: 79.20% → 81.33% (realpath covered) detector.ts: 80.53% → 81.69% Totals: 193 pass / 0 fail in ~1.8s (up from 162 in ~1.7s). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * test(unit): address Copilot review — drop unused infoed, clarify coverage docs - test/unit/config-parse.test.ts: the `infoed` capture buffer was never asserted. Dropped it and silenced `log.info` directly so resolveTargetList/parseTargets fallback paths don't print during tests. - docs-site/development.md: `yarn coverage:e2e` uses `--clean=false` and therefore accumulates on top of whatever is in `coverage/tmp` — it is not "e2e-only" in general. Docs now state the actual behavior: fresh → e2e-only, after `yarn coverage:unit` → merged. No script change — the `--clean=false` design is intentional per #267 (unit + e2e accumulate via a single temp dir). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci(codecov): upload unit-suite lcov on every PR + README badge Per-PR coverage signal via Codecov. Unit suite only — `yarn coverage` for the full merged (unit + e2e) report stays a follow-up job (30+ min is too expensive per PR; issue #267 plans a nightly or on-label job for that). - Adds `yarn coverage:unit` step to build_artifact (Linux, Node 22) after lint/build, then uploads coverage/lcov.info via codecov-action@v5. build_artifact already runs on every PR and produces the distributable bundle, so coverage fits naturally without adding a separate job. - codecov.yml: informational status (never fail a PR on coverage swings, since e2e coverage isn't uploaded yet), ignore compiled output / tests / docs-site / dictionary, carryforward disabled on the `unit` flag. - README: Codecov badge next to CI badge, labeled "Coverage (unit)" so the scope is obvious at a glance. Requires: Codecov GitHub App installed on yao-pkg and CODECOV_TOKEN set as a repo secret (optional on public repos but recommended for PR uploads). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci(codecov): add slug + step name, keep unit-flag upload Pins uploads to the yao-pkg/pkg Codecov project (prevents mis-routing on forks) and adds a readable step label. Path stays coverage/lcov.info (what .c8rc.json emits); flags: unit tags this as the unit-suite upload so a future e2e upload can use a separate flag. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci(codecov): add nightly e2e-coverage job, drop "(unit)" from badge alt Standing up the missing half of #267's Phase 2 acceptance criteria: "CI runs the unit suite on every PR (cheap) and the full coverage job on-label or nightly (expensive — full e2e)." - .github/workflows/coverage-nightly.yml: daily 03:00 UTC job (also workflow_dispatch) that runs yarn build + yarn coverage:e2e on Node 22 / Linux and uploads coverage/lcov.info to Codecov with `flags: e2e`. Reuses the same ~/.pkg-cache key as test.yml so it doesn't refetch all pkg-fetch binaries each night. - README: badge alt text is now "Coverage" — once the first nightly lands, Codecov merges the `unit` (from PR / main pushes) and `e2e` flag uploads per commit, so main-branch reports reflect the combined picture. PRs still show unit-only signal via flags. Codecov's flag-based merge means this doesn't touch the per-PR upload path — the existing build_artifact step keeps uploading unit with the fast ~2s feedback, and e2e drifts in once per day. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci(codecov): carryforward both flags so PRs report merged unit+e2e totals Without carryforward, Codecov treats each commit independently. PR commits only have a `unit` upload (~37% of lib/) because the nightly e2e job runs on main, not on PR branches — so PR reports and status were showing unit-only numbers and the user perceived "no merge". carryforward: true tells Codecov to reuse the most recent upload tagged with that flag when the current commit doesn't have its own. A PR now sees: - unit flag: direct from this PR's build_artifact run - e2e flag: carried forward from the last nightly on main - Total: union of the two → the merged number we want Trade-off: carried-forward e2e data is slightly stale for PR-new code, but e2e covers integration-heavy modules (walker/producer/packer/fabricator) that PRs rarely touch at the test-contributing level. Close enough to truth to be useful, and the nightly refresh catches drift within 24 hours. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * docs: refresh stale "follow-up" comments — nightly e2e already landed ci.yml and codecov.yml were written when the e2e coverage upload was still a TODO. That job now ships in coverage-nightly.yml and the flag-merge via carryforward is already live. Update comments to describe the current state instead of a past plan. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci(codecov): collect e2e coverage from existing Ubuntu cells, drop nightly We already run the full e2e suite on every PR — 18 cells across test_22/test_24/test_host × 3 OS × Node 22/24. Instead of re-running it nightly under c8, wrap two canonical existing cells (ubuntu × matching host/target) so they double as coverage producers: test_22 / (22.x, ubuntu-latest) → yarn coverage:e2e → upload `flags: e2e` test_24 / (24.x, ubuntu-latest) → yarn coverage:e2e:24 → upload `flags: e2e` Codecov merges same-flag uploads per commit (max per line), so both Node versions contribute. Windows/macOS cells stay unchanged — they exercise the same lib/ paths, so piping them through c8 would just duplicate data. Benefits over the nightly approach: - e2e coverage is per-PR real-time, no 24h lag. - Merged unit+e2e shows on every PR natively (both flags on same commit), no reliance on carryforward to fake the merge. - No separate workflow to maintain. Overhead: c8 adds ~10-20% to the wrapped cells' runtime (e.g. 4m → 5m). carryforward stays enabled as a safety net for transient upload failures, but it's no longer load-bearing for PR reports. Changes: - package.json: add coverage:e2e:24 (Node 24 target variant). - test.yml: canonical-cell detection via a computed step output; run yarn coverage:e2e[:24] on canonical cells, yarn <npm_command> elsewhere, then upload with flags: e2e from canonical only. - codecov.yml: refreshed comments; both flags keep carryforward as a safety net rather than the primary merge mechanism. - Delete .github/workflows/coverage-nightly.yml — redundant. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * ci(codecov): forward secrets to test.yml, fix c8 remap config Two bugs in the prior run: 1. CODECOV_TOKEN was empty in test.yml — reusable workflows don't inherit secrets automatically. Codecov rejected the e2e upload with "Token required because branch is protected". Adding `secrets: inherit` to the three test_* callers in ci.yml forwards all repo/org secrets. 2. c8 produced an empty coverage table despite seeing 479 V8 dumps. Root cause: .c8rc.json had "lib-es5/**" in exclude, which (depending on the c8 version's default for `exclude-after-remap`) can drop V8 dumps for the executed compiled files BEFORE sourcemap-remapping them back to lib/*.ts source. Result: everything got filtered, 0 files reported. Fix: drop "lib-es5/**" from exclude (the `include: ["lib/**"]` already restricts output to TS source after remap, so this was redundant and actively harmful) and set `exclude-after-remap: true` explicitly so we don't depend on the default across c8 versions. Unit coverage unaffected (still 37.42% locally) — remove+explicit-setting is a pure no-op for the unit path because unit tests execute lib/*.ts directly via esbuild-register; lib-es5 was never touched there. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> * test(detector): clarify descent-stop test, fix CI comment ref Copilot review feedback: - detector.test.ts: visitor returns false on the root File node, not Program. Update wording, add a second test that descends one level (returns false at Program) so both branches are documented. - ci.yml: stale comment referenced coverage-nightly.yml; e2e coverage now comes from the canonical Ubuntu cells in test.yml. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]> --------- Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
1 parent 376e2a4 commit 26f10a3

25 files changed

Lines changed: 2234 additions & 848 deletions

.c8rc.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"include": ["lib/**"],
3+
"exclude": [
4+
"lib/log.js",
5+
"lib/**/*.d.ts",
6+
"test/**",
7+
"prelude/**",
8+
"scripts/**",
9+
"dictionary/**"
10+
],
11+
"exclude-after-remap": true,
12+
"reports-dir": "coverage",
13+
"all": true,
14+
"temp-directory": "coverage/tmp",
15+
"reporter": ["text", "lcov"]
16+
}

.claude/rules/testing.md

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,44 @@ paths:
99
## Commands
1010

1111
```bash
12-
yarn build # Always build first
13-
yarn test:22 # Test with Node.js 22
14-
yarn test:host # Test with host Node.js version
15-
node test/test.js node22 no-npm test-50-* # Run specific test pattern
12+
yarn test:unit # Fast in-process unit suite (node:test, ~1s, no build)
13+
yarn test:unit:watch # Unit suite in watch mode
14+
15+
yarn build # Required before e2e suite
16+
yarn test:22 # E2E with Node.js 22
17+
yarn test:host # E2E with host Node.js version
18+
node test/test.js node22 no-npm test-50-* # Run specific e2e pattern
19+
20+
yarn coverage:unit # c8 unit coverage → coverage/lcov.info
21+
yarn coverage # Merged unit + e2e coverage (slow)
1622
```
1723

1824
## Organization
1925

20-
- Tests live in `test/test-XX-descriptive-name/` directories (XX = execution order).
21-
- Each test has a `main.js` entry point using utilities from `test/utils.js`.
26+
Two suites:
27+
28+
- **Unit suite** (`test/unit/*.test.ts`) — `node:test` runner, imports `lib/*.ts` via `esbuild-register`. Pure in-process, no binaries produced.
29+
- **E2E suite** (`test/test-XX-descriptive-name/main.js`) — each directory spawns the `pkg` CLI and asserts on the produced binary. XX = execution order.
30+
31+
Special e2e tests:
32+
2233
- `test-79-npm/` — npm package integration tests (only-npm).
2334
- `test-42-fetch-all/` — verifies patches exist for all Node.js versions.
2435

2536
## Writing Tests
2637

27-
1. Create `test/test-XX-descriptive-name/` with a `main.js`
28-
2. Use `utils.pkg.sync()` to invoke pkg
29-
3. Verify outputs, clean up with `utils.filesAfter()`
38+
Prefer a unit test when the thing under test is a pure function in `lib/*.ts` (parsers, path helpers, selectors, etc.) — they're ~1s and give much better iteration speed than a full build+spawn cycle.
39+
40+
**Unit test** (`test/unit/your-thing.test.ts`):
41+
42+
1. Use `import { describe, it } from 'node:test'` and `import assert from 'node:assert/strict'`.
43+
2. Import the thing directly from `../../lib/...` — no need for `lib-es5/`.
44+
3. For platform-specific logic, skip the wrong half with `describe(..., { skip: !onWin }, ...)`.
45+
46+
**E2E test** (`test/test-XX-descriptive-name/main.js`):
47+
48+
1. Create the directory with a `main.js`.
49+
2. Use `utils.pkg.sync()` to invoke pkg.
50+
3. Verify outputs, clean up with `utils.filesAfter()`.
3051

3152
Test artifacts (`*.exe`, `*-linux`, `*-macos`, `*-win.exe`) must be cleaned from test directories before committing.

.github/copilot-instructions.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,13 @@ This workflow ensures code quality, prevents accidental commits of test artifact
104104
The test suite is extensive and organized in numbered directories:
105105

106106
```bash
107-
# Build first (required)
107+
# Build first (required for e2e)
108108
yarn build
109109

110+
# Fast in-process unit suite (node:test + esbuild-register, ~1s, no build)
111+
yarn test:unit
112+
yarn test:unit:watch
113+
110114
# Run all tests
111115
yarn test
112116

@@ -117,13 +121,18 @@ yarn test:host # Test with host Node.js version
117121

118122
# Run specific test pattern
119123
node test/test.js node20 no-npm test-50-*
124+
125+
# Coverage (c8; include lib/**, merged unit + e2e into coverage/lcov.info)
126+
yarn coverage:unit
127+
yarn coverage:e2e
128+
yarn coverage
120129
```
121130

122131
#### Test Organization
123132

124-
- Tests are in `test/test-XX-*/` directories where XX indicates execution order
125-
- Each test directory contains a `main.js` file that runs the test
126-
- Tests use `utils.js` for common testing utilities
133+
- `test/unit/*.test.ts` in-process unit suite on Node's built-in `node:test` runner. Imports `lib/*.ts` directly via `esbuild-register` (no build required).
134+
- `test/test-XX-*/main.js` — e2e suite: each directory spawns the pkg CLI and asserts on produced binaries. XX indicates execution order.
135+
- Tests use `utils.js` for common testing utilities.
127136
- Special tests:
128137
- `test-79-npm/`: Tests integration with popular npm packages (only-npm)
129138
- `test-42-fetch-all/`: Verifies patches exist for all Node.js versions

.github/workflows/ci.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,24 @@ jobs:
5757
prelude/sea-bootstrap.bundle.js
5858
retention-days: 1
5959

60+
# Coverage upload — unit-suite only on every PR (~2s). E2E coverage is
61+
# uploaded from the canonical Ubuntu cells in `.github/workflows/test.yml`
62+
# with `flags: e2e`; Codecov merges the two flags via carryforward (see
63+
# codecov.yml) so PR reports reflect unit + e2e combined. The `build`
64+
# matrix runs `yarn test:unit` on 3 OS × Node 22/24 to verify platform
65+
# behavior; this is the single canonical unit-coverage upload.
66+
- if: needs.changes.outputs.code == 'true'
67+
run: yarn coverage:unit
68+
- if: needs.changes.outputs.code == 'true'
69+
name: Upload coverage reports to Codecov
70+
uses: codecov/codecov-action@v5
71+
with:
72+
token: ${{ secrets.CODECOV_TOKEN }}
73+
slug: yao-pkg/pkg
74+
files: coverage/lcov.info
75+
flags: unit
76+
fail_ci_if_error: false
77+
6078
# Sanity check: `yarn build` succeeds on every supported OS + Node combo.
6179
# Runs in parallel with tests (they don't consume this job's output) so
6280
# failures still mark the PR red without gating the test jobs.
@@ -81,24 +99,33 @@ jobs:
8199
run: yarn install
82100
- if: needs.changes.outputs.code == 'true'
83101
run: yarn build
102+
# Unit suite is in-process (node:test + esbuild-register) and takes
103+
# ~1s, so it rides the existing per-(os,node) build matrix. This gives
104+
# Windows- and Node 24-specific path/runtime coverage for free — both
105+
# matter for common.test.ts's win32 branch.
106+
- if: needs.changes.outputs.code == 'true'
107+
run: yarn test:unit
84108

85109
test_host:
86110
needs: [changes, build_artifact]
87111
uses: ./.github/workflows/test.yml
112+
secrets: inherit
88113
with:
89114
npm_command: test:host
90115
should_run: ${{ needs.changes.outputs.code }}
91116

92117
test_22:
93118
needs: [changes, build_artifact]
94119
uses: ./.github/workflows/test.yml
120+
secrets: inherit
95121
with:
96122
npm_command: test:22
97123
should_run: ${{ needs.changes.outputs.code }}
98124

99125
test_24:
100126
needs: [changes, build_artifact]
101127
uses: ./.github/workflows/test.yml
128+
secrets: inherit
102129
with:
103130
npm_command: test:24
104131
should_run: ${{ needs.changes.outputs.code }}

.github/workflows/test.yml

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,48 @@ jobs:
4747
with:
4848
name: build-output
4949

50-
- if: inputs.should_run == 'true'
50+
# Two canonical Ubuntu cells (Node 22 host + test:22 target; Node 24
51+
# host + test:24 target) wrap their e2e run in c8 to capture coverage.
52+
# Every other cell (Windows, macOS, mixed host/target) runs the test
53+
# command plainly — the canonical cells already exercise the same
54+
# lib/ code paths, so extra uploads from them would just duplicate
55+
# data for Codecov to merge and discard.
56+
- name: Canonical cell?
57+
id: cov
58+
shell: bash
59+
if: inputs.should_run == 'true'
60+
run: |
61+
if [[ "${{ matrix.os }}" == "ubuntu-latest" && \
62+
( ( "${{ inputs.npm_command }}" == "test:22" && "${{ matrix.node-version }}" == "22.x" ) || \
63+
( "${{ inputs.npm_command }}" == "test:24" && "${{ matrix.node-version }}" == "24.x" ) ) ]]; then
64+
echo "coverage=true" >> "$GITHUB_OUTPUT"
65+
else
66+
echo "coverage=false" >> "$GITHUB_OUTPUT"
67+
fi
68+
69+
- if: inputs.should_run == 'true' && steps.cov.outputs.coverage != 'true'
5170
run: yarn ${{ inputs.npm_command }}
5271
env:
5372
CI: true
5473
timeout-minutes: 30
74+
75+
- if: inputs.should_run == 'true' && steps.cov.outputs.coverage == 'true' && inputs.npm_command == 'test:22'
76+
run: yarn coverage:e2e
77+
env:
78+
CI: true
79+
timeout-minutes: 40
80+
- if: inputs.should_run == 'true' && steps.cov.outputs.coverage == 'true' && inputs.npm_command == 'test:24'
81+
run: yarn coverage:e2e:24
82+
env:
83+
CI: true
84+
timeout-minutes: 40
85+
86+
- if: inputs.should_run == 'true' && steps.cov.outputs.coverage == 'true'
87+
name: Upload e2e coverage to Codecov
88+
uses: codecov/codecov-action@v5
89+
with:
90+
token: ${{ secrets.CODECOV_TOKEN }}
91+
slug: yao-pkg/pkg
92+
files: coverage/lcov.info
93+
flags: e2e
94+
fail_ci_if_error: false

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
# dependencies
55
/node_modules
66

7+
# coverage reports (c8)
8+
/coverage
9+
710
# pkg uses yarn — a root package-lock.json is always accidental
811
/package-lock.json
912

CLAUDE.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
## Quick Reference
44

55
```bash
6-
yarn build # Build (required before testing)
6+
yarn build # Build (required before e2e testing)
77
yarn lint # Check lint + formatting
88
yarn fix # Auto-fix lint + formatting
99
yarn start # Watch mode (rebuild on change)
10-
yarn test:22 # Run tests for Node.js 22
10+
yarn test:unit # Fast in-process unit suite (node:test, ~1s)
11+
yarn test:22 # Run e2e tests for Node.js 22
1112
```
1213

1314
> `pkg` uses **yarn** for dependency management. `npm` is only used in `docs-site/` (the VitePress docs). Do not create a root `package-lock.json`.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
<p align="center">
1212
<a href="https://github.com/yao-pkg/pkg/actions/workflows/ci.yml"><img src="https://github.com/yao-pkg/pkg/actions/workflows/ci.yml/badge.svg" alt="Build Status" /></a>
13+
<a href="https://codecov.io/gh/yao-pkg/pkg"><img src="https://codecov.io/gh/yao-pkg/pkg/branch/main/graph/badge.svg" alt="Coverage" /></a>
1314
<a href="https://www.npmjs.com/package/@yao-pkg/pkg"><img src="https://img.shields.io/npm/v/@yao-pkg/pkg" alt="npm version" /></a>
1415
<a href="https://www.npmjs.com/package/@yao-pkg/pkg"><img src="https://img.shields.io/npm/dm/@yao-pkg/pkg" alt="npm downloads" /></a>
1516
<a href="https://github.com/yao-pkg/pkg/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@yao-pkg/pkg" alt="license" /></a>

codecov.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Codecov config — keep coverage informational, don't gate PRs on it.
2+
# Every PR uploads BOTH flags: `unit` from build_artifact (fast), and `e2e`
3+
# from the two canonical Ubuntu e2e cells in test.yml (Node 22 + Node 24).
4+
# Codecov merges flags per commit, so the total reflects unit + e2e natively.
5+
coverage:
6+
status:
7+
project:
8+
default:
9+
# Report but never fail the PR. Coverage swings naturally with the
10+
# unit-vs-e2e split and the 24h lag on e2e refreshes.
11+
target: auto
12+
threshold: 5%
13+
informational: true
14+
patch:
15+
default:
16+
# New lines should be covered by the unit suite when reasonable, but
17+
# many changes land in integration-heavy modules (walker, producer,
18+
# packer) that stay e2e-tested. Warn, don't fail.
19+
informational: true
20+
21+
comment:
22+
layout: "reach, diff, files"
23+
behavior: default
24+
require_changes: true
25+
26+
ignore:
27+
- "lib-es5/**"
28+
- "test/**"
29+
- "prelude/**"
30+
- "dictionary/**"
31+
- "scripts/**"
32+
- "docs-site/**"
33+
- "**/*.d.ts"
34+
35+
# carryforward is a safety net: if one of the two flag uploads fails (e.g.
36+
# Codecov outage mid-run, an e2e cell crashes), Codecov reuses the last known
37+
# value for that flag on the new commit. With both flags uploaded per PR the
38+
# normal case doesn't depend on it, but keeping it set avoids a zeroed-out
39+
# report on transient upload failures.
40+
flags:
41+
unit:
42+
paths:
43+
- lib/
44+
carryforward: true
45+
e2e:
46+
paths:
47+
- lib/
48+
carryforward: true

docs-site/development.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,21 @@ This command starts an interactive process that guides you through the release u
2323

2424
## Testing
2525

26-
Before running tests, ensure you have built the project by running:
26+
`pkg` has two test suites:
27+
28+
1. **Unit suite** (`test/unit/*.test.ts`) — in-process assertions using Node's built-in [`node:test`](https://nodejs.org/api/test.html) runner. Imports `lib/*.ts` directly via `esbuild-register`, so no `yarn build` is required. Runs in ~1 second.
29+
2. **E2E suite** (`test/test-XX-*/`) — each directory spawns the `pkg` CLI and asserts on the produced binaries. Requires a prior `yarn build`.
30+
31+
### Unit suite
32+
33+
```bash
34+
yarn test:unit # run once
35+
yarn test:unit:watch # re-run on change
36+
```
37+
38+
### E2E suite
39+
40+
Before running e2e tests, ensure you have built the project by running:
2741

2842
```bash
2943
yarn build
@@ -42,11 +56,29 @@ node test/test.js <target> [no-npm | only-npm | all] [<flavor>]
4256
- `[no-npm | only-npm | all]` to specify which tests to run. `no-npm` will run tests that don't require npm, `only-npm` will run against some specific npm modules, and `all` will run all tests.
4357
- `<flavor>` to use when you want to run only tests matching a specific pattern. Example: `node test/test.js all test-99-*`. You can also set this by using `FLAVOR` environment variable.
4458

45-
Each test is located inside `test` directory into a dedicated folder named following the pattern `test-XX-*`. The `XX` is a number that represents the order the tests will run.
59+
Each e2e test is located inside `test` directory into a dedicated folder named following the pattern `test-XX-*`. The `XX` is a number that represents the order the tests will run.
4660

4761
When running `node test/test.js all`, based on the options, each test will be run consecutively by running `main.js` file inside the test folder.
4862

49-
### Example test
63+
### Coverage
64+
65+
`pkg` uses [c8](https://github.com/bcoe/c8) as a thin reporter over V8's built-in coverage. `NODE_V8_COVERAGE` propagates through child processes, so e2e coverage captures the `pkg` CLI executions spawned by the harness.
66+
67+
```bash
68+
# Run unit coverage alone — always deterministic; clears coverage/tmp first.
69+
yarn coverage:unit
70+
71+
# Append e2e coverage on top of whatever is in coverage/tmp (uses `c8 --clean=false`).
72+
# After a fresh `yarn coverage:unit`, this produces a merged report. Without
73+
# a prior run it produces an e2e-only report. For a guaranteed e2e-only view,
74+
# delete `coverage/` first. (Slow — runs the full e2e matrix.)
75+
yarn coverage:e2e
76+
77+
# Full merged report: cleans, runs unit, then appends e2e, then emits lcov+text.
78+
yarn coverage
79+
```
80+
81+
### Example e2e test
5082

5183
Create a directory named `test-XX-<name>` and inside it create a `main.js` file with the following content:
5284

0 commit comments

Comments
 (0)