ci: bring test/ into the typecheck - #127
Conversation
The five node:test suites under test/ were excluded from tsconfig.json, so tsc --noEmit never looked at them. Drop the exclusion. The suites import source modules with an explicit .ts extension (required by node --experimental-strip-types), which needs allowImportingTsExtensions, and that option in turn requires noEmit. Nothing emits through tsc here anyway — webpack + Babel do the build. @types/node becomes a declared devDependency: the suites import node:test and node:assert/strict, which the typecheck can only resolve through it. Part of #126
Workflow patch for a maintainer to apply
Add this job to test:
name: Unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
# node --test --experimental-strip-types needs Node >= 22.6, so this job
# cannot share the check job's Node 20.
- uses: actions/setup-node@v5
with:
node-version: 22
cache: npm
- run: npm ci
- name: Unit tests (node:test)
run: npm testAs a unified diff against master, if you prefer diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 559ed48..b2aff24 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -38,6 +38,24 @@ jobs:
- name: Validate manifest
run: npm run validate
+ test:
+ name: Unit tests
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v6
+
+ # node --test --experimental-strip-types needs Node >= 22.6, so this job
+ # cannot share the check job's Node 20.
+ - uses: actions/setup-node@v5
+ with:
+ node-version: 22
+ cache: npm
+
+ - run: npm ci
+
+ - name: Unit tests (node:test)
+ run: npm test
+
nginx:
name: nginx config test
runs-on: ubuntu-latestWhy Node 22 and not the The |
…a transitive reference
The `types` array switches off automatic inclusion of every `@types/*`
package, so declaring `@types/node` in devDependencies did not put it in
the program. The only thing resolving `node:test` / `node:assert/strict`
was a `/// <reference types="node" />` inside the office-addin-dev-certs
and undici-types declarations, reached via webpack.config.js. Any
unrelated dep bump could have dropped that link and broken CI.
# test/ in isolation, before
$ npx tsc -p tsconfig.testonly.json --noEmit
test/encryption-flow.test.ts(13,22): error TS2591: Cannot find name 'node:test'.
... 10 errors across all five suites
# after
$ npx tsc -p tsconfig.testonly.json --noEmit ; echo $?
0
$ npx tsc --noEmit ; echo $?
0
Also drop `noEmitOnError`, `outDir` and `sourceMap`, which `noEmit`
makes inert. The only tsc invocation in the repo is `npx tsc --noEmit`
in ci.yml. Verified tsc still exits 2 on a type error in either src/ or
test/ without noEmitOnError, and writes no lib/.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
VERDICT: approve
Rule sweep plus the handed-forward review findings. One blocking issue, confirmed and fixed on the branch in 893016b; CI green on the new HEAD.
The blocker (fixed, not looped)
"types": ["office-js", "office-runtime"] switches off automatic inclusion of every other @types/* package, so declaring @types/node in devDependencies did not actually put it in the program. Reproduced against 72568f2 with a tsconfig.testonly.json that extends tsconfig.json and includes only test/**/*.ts:
$ npx tsc -p tsconfig.testonly.json --noEmit
test/encryption-flow.test.ts(13,22): error TS2591: Cannot find name 'node:test'.
test/encryption-flow.test.ts(14,20): error TS2591: Cannot find name 'node:assert/strict'.
... 10 errors across all five suites
exit=2
--explainFiles names the real carriers:
node_modules/@types/node/index.d.ts
Type library referenced via 'node' from 'node_modules/office-addin-dev-certs/lib/httpsServerOptions.d.ts'
Type library referenced via 'node' from 'node_modules/undici-types/formdata.d.ts'
Both reached through webpack.config.js, which is in the program via allowJs. So the typecheck was passing on a /// <reference types="node" /> buried in an unrelated dependency's declarations. Latent rather than live, but it would have surfaced as Cannot find name 'process' in src/lib/pkg-client.ts and src/taskpane/settings-view.ts alongside the five suites on the next dep bump that moved that chain.
Fixed by adding "node" to the array. Verified both directions: test/ in isolation exits 0, and the full program exits 0 with no dom/node global collisions.
Also folded in
noEmit made noEmitOnError, outDir: "lib" and sourceMap inert. Confirmed the only tsc invocation in the repo is npx tsc --noEmit at ci.yml:33 and that lib/ is never produced, so all three are gone. Checked the one thing that could regress: without noEmitOnError, tsc still exits 2 on a type error in either src/ or test/, and writes no lib/. CI's failure behaviour is unchanged.
CLAUDE.md:111 recorded the incomplete rationale as fact ("@types/node is a declared devDependency ... without it the typecheck cannot resolve them" — true, but it read as a guarantee that was not there). Rewritten to say both the devDependency and the types entry are needed, and why.
Non-blocking, out of scope
test/render-body.test.ts and test/verified-sender.test.ts have Prettier violations that also fail on master:
$ git checkout master && npx prettier --check "test/**/*.ts"
[warn] test/render-body.test.ts
[warn] test/verified-sender.test.ts
Pre-existing, not introduced here, and invisible to CI because the lint and prettier steps are scoped to src/**/*.{ts,js} — which CLAUDE.md:107 now documents accurately. This PR closes the typecheck half of test/ coverage; the lint/format half is still open. Better as a follow-up than a change to this diff.
Rule sweep
Checked the rules that apply to a TypeScript build-config diff. All clean:
- Conventional-commit PR title —
pr-title.ymlis present on this repo and the check passes. Part of #126is deliberate and correct: thenpm testjob needs theworkflowspermission the bot does not have, so it ships as a patch in a comment.closingIssuesReferencesverified empty via GraphQL, so merging will not orphan the issue.- Cross-repo reference to postguard#247 uses the full markdown link form.
- Reviewer assigned (rubenhensen).
@types/nodeis genuinely consumed rather than dead weight, now thattypeslists it.npm test: 39 pass, 0 fail, 0 cancelled, exit 0 — checked for cancelled tests, not just# fail 0.
The two em-dash flags the sweep raised on CLAUDE.md were dropped: master already carries 28 em-dashes in 2200 words and every bullet in that command list is written command — description. Matching the file is right; making the new line inconsistent with its neighbours would not be.
Full local gate on 893016b: eslint 0, prettier 0, tsc --noEmit 0, npm test exit 0, npm run build 0 (pre-existing entrypoint size warnings only), npm run validate valid.
| "target": "es2020", | ||
| "lib": ["es2022", "dom"], | ||
| "types": ["office-js", "office-runtime"] | ||
| "types": ["office-js", "office-runtime", "node"] |
There was a problem hiding this comment.
Blocking issue found here and fixed on the branch in 893016b — "node" added to the array.
Without it this line switched off automatic inclusion of every other @types/* package, @types/node included, so the @types/node devDependency this PR declares never reached the program. test/ typechecked only because webpack.config.js (in the program via allowJs) drags in dependency declarations that carry /// <reference types="node" /> — office-addin-dev-certs/lib/httpsServerOptions.d.ts and undici-types. Verified with --explainFiles; verified the fix with test/ in isolation (exit 0) and the full program (exit 0).
|
|
||
| Those `.ts` import specifiers are why `tsconfig.json` sets `allowImportingTsExtensions` (and therefore `noEmit`, which the option requires). Nothing emits through `tsc` anyway, webpack + Babel do the build, so `outDir` / `sourceMap` / `noEmitOnError` are not set. | ||
|
|
||
| The suites import `node:test` / `node:assert/strict`, so `@types/node` is a declared devDependency and `node` is listed in the `types` compiler option. Both are needed: `types` is set explicitly, which switches off automatic inclusion of every `@types/*` package, so installing `@types/node` on its own does not put it in the program. Before `node` was added to that array the only thing pulling node types in was a `/// <reference types="node" />` deep inside the `office-addin-dev-certs` and `undici-types` declarations, reached through `webpack.config.js` (in the program via `allowJs`). That link would have broken on any unrelated dependency bump. |
There was a problem hiding this comment.
Rewritten in 893016b. The original wording ("@types/node is a declared devDependency ... without it the typecheck cannot resolve them") was true but read as a guarantee that was not there — the devDependency alone did not put node types in the program, because types is set explicitly. Recording both halves so the next person editing tsconfig.json does not drop the types entry and reintroduce the accidental transitive link.
Issue #126 has two halves. One lands here; the other needs a human.
In this PR
tsconfig.jsonexcludedtest/, sotsc --noEmitnever looked at the fivenode:testsuites. Dropping that exclusion pulls in two more options:allowImportingTsExtensions, because the suites import source modules as../src/lib/foo.tsand the extension is mandatory undernode --experimental-strip-types. AndnoEmit, which TypeScript requires wheneverallowImportingTsExtensionsis set. Nothing in this repo emits throughtscanyway, webpack and Babel do the build.@types/nodegoes from a transitive dep of theoffice-addin-*chain to a declared devDependency. The suites importnode:testandnode:assert/strict; the typecheck resolves those only through it, so it should not depend on whatever the next dep-scan sweep does to that chain.Before and after, with a deliberate type error appended to
test/render-body.test.ts:The suites themselves needed no changes; they already typecheck clean.
Not in this PR
The
npm testjob.dobby-coder[bot]has noworkflowspermission, and the remote rejects the push:The patch is in a comment below. Please leave #126 open until someone applies it, which is why this PR says
Part ofrather thanCloses.--experimental-strip-typesneeds Node 22.6 or newer, so the new job cannot reuse thecheckjob's Node 20. The patch pins it to 22 and leavescheckuntouched.Verified locally on Node 22.23.1
Every step the
checkjob runs, plus the suite:npm ci, cleannpx eslint -c eslint.config.mjs --max-warnings=0 "src/**/*.{ts,js}", 0npx prettier --check "src/**/*.{ts,js}", 0npx tsc --noEmit, 0 (now oversrc/andtest/)npm run build, 0, with the pre-existing entrypoint size warnings ontaskpane.jsandyivi-dialog.jsnpm run validate, manifest validnpm test, 39 pass, 0 fail, 0 cancelled, exit 0CLAUDE.mdclaimed there were no automated tests and thattest/sat in the tsconfigexclude. Both updated.Part of #126
Part of encryption4all/postguard#247 (workstream A)