Skip to content

test: run execution tests in Node with Jasmine instead of Karma - #1719

Merged
johnnyreilly merged 1 commit into
execution-tests-refinefrom
execution-tests-node-runner
Nov 8, 2026
Merged

johnnyreilly merged 1 commit into
execution-tests-refinefrom
execution-tests-node-runner

Conversation

@johnnyreilly

Copy link
Copy Markdown
Member

Stacked on #1718. Please review and merge that first, after which GitHub will retarget this PR to next.

Replaces Karma and headless Chrome with a Node-based runner. No test needed a browser: the two React specs use the shallow renderer, and the only uses of document are in app entry files that the specs never load.

How a test now runs

New test/execution-tests/run-specs.js, run from each test's directory by run-tests.js:

  • It builds the test's main.js (which loads its *.tests.ts specs with require.context) with target: 'node' and devtool: 'inline-source-map', into a temporary folder that's deleted afterwards. The mode, module, resolve and resolveLoader settings come from the test's webpack.config.js, the same parts test/karmaConfig.js passed to Karma.
  • It then runs the bundle with Jasmine 7, as a library in the same process (exitOnCompletion = false). Source maps are enabled, so failure stacks point at the TypeScript source, e.g. webpack:///test/app.tests.ts:9:52.
  • A test fails if the build has errors, if any spec fails, or if no specs ran. Jasmine reports the last case as incomplete, as it does when a focused fit/fdescribe is left in.
  • --watch (with --single-test) uses webpack's watch mode and runs the specs in a fresh child process after each successful build, so Jasmine's globals and the module cache start clean. Ctrl+C deletes the temporary folder.
  • --debug still runs node --inspect-brk=5858, so the VS Code "Run open execution test" config works unchanged. The build and the specs now run in one process, so breakpoints work in both src/ and the specs. Under Karma, the specs ran in Chrome.
  • Tests without a main.js (just optionsCaching) are still built by run-webpack.js, as before.
  • allowJs-entryFileIsJs was the one test whose karma.conf.js listed spec files with a glob instead of main.js. It now has a main.js like the rest.

Build errors now fail the test

Karma ran the specs even when webpack reported errors, which hid two broken tests:

  • basic: lib/externalLib.d.ts used declare module externalLib { ... }, which TypeScript 7 rejects (TS1540). It's now declare namespace externalLib.
  • resolveModuleName: the resolveModuleName loader option is accepted but does nothing on the TypeScript 7 API (as Support TypeScript 7.1 API #1704 notes), so foo/bar/baz don't resolve for type-checking (4 × TS2307). The specs passed only because webpack's own alias resolved the modules at runtime. It's now skipped, with the same reason as loaderOptions (which covers the also-inert getCustomTransformers). The two skips share an unsupportedTests map in run-tests.js, so both can be turned back on if the API adds these hooks.

Dependencies

  • Removed: karma, karma-chrome-launcher, karma-jasmine, karma-mocha-reporter, karma-sourcemap-loader, karma-webpack (181 packages out of node_modules), the 26 per-test karma.conf.js files and test/karmaConfig.js.
  • Added: jasmine@^7.0.0, and @types/jasmine moves from ^4.6.6 to ^7.0.0 to match. All the existing specs type-check and run unchanged on Jasmine 7.
  • CI's webpack 4 step now installs only webpack@^4.47.0, without karma-webpack@^4.0.2.
  • knip.json: ignoreDependencies drops the Karma packages and adds jasmine.

Docs

  • test/execution-tests/README.md now describes main.js, what run-specs.js does, when a test fails, that specs run in Node (so no browser APIs), and how watch and debug work.
  • AGENTS.md is updated to match, and no longer mentions localhost:9876.

Notes

  • Building with target: 'node' instead of Karma's web changes how webpack resolves packages: it ignores the browser field, and code-split chunks load with require instead of script tags. Every test passes either way. If a future test needs the browser build of a package, it can set resolve.aliasFields: ['browser'] in its webpack config, and run-specs.js passes that through.
  • Jasmine runs specs in random order by default, as karma-jasmine did. The default reporter prints dots plus failure details instead of each spec's name.

Testing (local, macOS, Node 26, TypeScript 7.1.0-beta)

  • Execution tests, webpack 5: 26/26 suites pass (loaderOptions and resolveModuleName skipped), with the same number of specs per suite as under Karma. 16s, down from 35s.
  • Execution tests, webpack 4.47: 24/24 pass (node16-file-suffix and optionsCaching also skipped, via test.json).
  • Checked that failures fail: a failing expect, a type error in a spec, and a main.js that matches no specs each exit with 1.
  • --watch: re-ran after a spec was edited, and cleaned up on Ctrl+C. --debug: pauses with the debugger listening on 5858.
  • Comparison tests 57/57, plus lint, knip, format:check and the three check: scripts.

🤖 Generated with Claude Code

Each test's main.js is now built for Node (target: 'node', inline source
map) using the module/resolve/resolveLoader settings from its
webpack.config.js, the same parts Karma used, and the bundle is run with
Jasmine 7 in-process. A test fails on build errors, failing specs, or no
specs running. --watch rebuilds and re-runs specs in a fresh process;
--debug still pauses on port 5858, now covering ts-loader and the specs.

- Remove karma, karma-chrome-launcher, karma-jasmine,
  karma-mocha-reporter, karma-sourcemap-loader, karma-webpack, the
  per-test karma.conf.js files and test/karmaConfig.js. Add jasmine 7
  and @types/jasmine 7.
- Karma ran tests even when the build had errors, which hid two:
  - basic: fix `declare module externalLib` (TS1540 on TypeScript 7)
    to `declare namespace`.
  - resolveModuleName: the option is inert on the TypeScript 7 API, so
    skip it alongside loaderOptions until the API supports it.
- allowJs-entryFileIsJs gets a main.js like the other tests.
- CI no longer installs karma-webpack@4 for the webpack 4 runs.
- Update README, AGENTS.md and knip.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@johnnyreilly
johnnyreilly merged commit 0b8432b into execution-tests-refine Nov 8, 2026
45 checks passed
@johnnyreilly

Copy link
Copy Markdown
Member Author

These changes are now part of #1718. GitHub marked this PR as merged because its commit was pushed to #1718's branch (this PR's base); nothing has gone into next yet.

@github-actions

github-actions Bot commented Nov 8, 2026

Copy link
Copy Markdown

Benchmark (macOS)

Scenario transpileOnly PR branch median (ms) base branch median (ms) Δ vs base branch
Cold build false 776.3 776.4 -0.0%
Incremental rebuild (leaf touch) false 49.2 49.2 +0.0%
Incremental rebuild (hub touch) false 300.6 301.6 -0.3%
Cold build true 205.3 204.3 +0.5%
Incremental rebuild (leaf touch) true 49.8 49.1 +1.3%
Incremental rebuild (hub touch) true 50.4 51.2 -1.7%
Cold build (all orphan files) false 791.7 795.0 -0.4%
Incremental rebuild (hub touch, all orphan files) false 337.0 325.4 +3.6%

PR branch = /Users/runner/work/ts-loader/ts-loader, base branch = /Users/runner/work/ts-loader/ts-loader-main. 2 warmup + 10 measured iterations per scenario, median reported. Report-only - no threshold fails this check.

@github-actions

github-actions Bot commented Nov 8, 2026

Copy link
Copy Markdown

Benchmark (Ubuntu)

Scenario transpileOnly PR branch median (ms) base branch median (ms) Δ vs base branch
Cold build false 1091.3 1090.8 +0.0%
Incremental rebuild (leaf touch) false 51.7 50.8 +1.8%
Incremental rebuild (hub touch) false 428.9 429.9 -0.2%
Cold build true 368.8 372.7 -1.0%
Incremental rebuild (leaf touch) true 32.0 31.9 +0.1%
Incremental rebuild (hub touch) true 33.3 32.5 +2.6%
Cold build (all orphan files) false 1204.2 1195.6 +0.7%
Incremental rebuild (hub touch, all orphan files) false 475.4 470.9 +1.0%

PR branch = /home/runner/work/ts-loader/ts-loader, base branch = /home/runner/work/ts-loader/ts-loader-main. 2 warmup + 10 measured iterations per scenario, median reported. Report-only - no threshold fails this check.

@github-actions

github-actions Bot commented Nov 8, 2026

Copy link
Copy Markdown

Benchmark (Windows)

Scenario transpileOnly PR branch median (ms) base branch median (ms) Δ vs base branch
Cold build false 2044.1 2035.4 +0.4%
Incremental rebuild (leaf touch) false 66.1 66.6 -0.8%
Incremental rebuild (hub touch) false 768.5 771.9 -0.4%
Cold build true 494.8 494.7 +0.0%
Incremental rebuild (leaf touch) true 39.1 38.0 +2.8%
Incremental rebuild (hub touch) true 38.6 38.3 +0.8%
Cold build (all orphan files) false 2024.6 2018.0 +0.3%
Incremental rebuild (hub touch, all orphan files) false 835.4 836.2 -0.1%

PR branch = C:\source\ts-loader, base branch = C:\source\ts-loader-main. 2 warmup + 10 measured iterations per scenario, median reported. Report-only - no threshold fails this check.

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