Repository navigation
test: modernise the test packs (Node + Jasmine runner, Jasmine for comparison tests, Babel 7) - #1718
Merged
Merged
Conversation
- Rename execution test folders by feature, dropping the TypeScript
version prefixes (ts-loader needs TypeScript 7.1+, so they always
passed). Tests that need webpack 5 declare it in a test.json
({ "minWebpack": 5 }), which run-tests.js reads.
- Install @types/jasmine once at the root (v4, matching the
jasmine-core 4.6 that karma-jasmine loads). Remove the unused
per-test jasmine-core 2.x and root jasmine-core 6. 15 tests no
longer need their own package.json or lockfile.
- Move the execution and comparison tests from Babel 6 to Babel 7
with babel-loader 8 (the last version that supports webpack 4),
installed once at the root. Drop babel-polyfill: Babel 7.18+ inlines
the regenerator runtime. Regenerate the three Babel comparison
snapshots.
- Update README, AGENTS.md, knip.json and stale comments.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Benchmark (macOS)
PR branch = |
Benchmark (Ubuntu)
PR branch = |
Benchmark (Windows)
PR branch = |
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>
The comparison harness used mocha lightly (one describe, two its, a timeout and done callbacks), while the execution tests already use Jasmine. Standardise on Jasmine. - create-and-execute-test.js: the 90s timeout moves from this.timeout to it()'s third argument. Its arguments now follow Jasmine's `--` marker and are parsed strictly. - run-tests.js starts Jasmine with --random=false (keeps the transpileOnly run after the normal one) and --color, which picocolors also reads, so the colored snapshots are unchanged. - run-tests.js skips testLib (the library the npmLink test links to) and _IGNORE_ folders itself. Mocha passed them with zero specs; Jasmine reports that as incomplete. - Replace the hand-written mocha globals with @types/jasmine, remove mocha (16 packages), and update knip.json and .claude/settings.json. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- --single-test now applies the same skips as a full run: test.json's minWebpack and the TypeScript 7 unsupported tests (execution), and testLib / _IGNORE_ folders (comparison, via a shared isRunnableTest). --watch, --debug and the VS Code launch config all use --single-test. - An invalid test.json (bad JSON, or minWebpack not a positive whole number) now fails that test instead of stopping the whole run, or silently never skipping on a string like "5.0.0". - run-specs.js deletes its temp folder from an 'exit' handler, so it is also removed when webpack.config.js or webpack() throws, and on SIGINT/SIGTERM/SIGHUP in watch mode. - README: with --watch, specs run in a fresh process, so breakpoints in the specs aren't hit under --debug (ones in src still are). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The TypeScript 7 skips for loaderOptions and resolveModuleName move from a hard-coded map in run-tests.js into those tests' test.json, as `"skip": "<reason>"`. One isRunnableTest reads and validates test.json (minWebpack and skip) for both full and --single-test runs. - run-specs.js --watch stops the previous spec run as soon as webpack sees a change (so a stale run's output can't be mistaken for the new edit's, even if the rebuild fails), and before the temp folder is deleted on exit. - runTests no longer normalises the test name a second time; both callers pass a folder name. - README and AGENTS.md describe the test.json "skip" field. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- run-tests.js applies --match-test before reading test.json, so only the selected tests' test.json can skip or fail them. - run-specs.js exits once the specs finish (keeping the exit code), so a spec that leaves a timer or socket open can't hang the run, as Karma's single run couldn't. - The temp folder cleanup on exit no longer throws if the folder can't be removed (e.g. on Windows, when a stopped spec process still has the bundle open). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Jasmine treats spec paths as glob patterns, so a path containing `[` or
`{` matched nothing and every test failed with "No specs found".
- run-specs.js passes the bundle with addSpecFile (taken as-is) instead
of execute([path]), for temp directories with such characters.
- The comparison runner passes create-and-execute-test.js relative to
the working directory, for checkouts with such characters (mocha took
the path literally).
- The comparison runner applies --match-test first, like the execution
runner, so skip messages are only logged for the selected tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
react, babel-react and pnpm move from React 15/16 to 18.3.1, the newest React that webpack 4 can still bundle. - React 18 drops the shallow renderer and react-addons-test-utils, so the specs render with react-dom/server's renderToStaticMarkup and compare the HTML, which also runs in plain Node. - App components are plain functions (React.SFC is gone from the types); the unused app entry files use createRoot. - react compiles JSX with "jsx": "react-jsx"; babel-react keeps "preserve" and uses @babel/preset-react's automatic runtime. - pnpm pins React 18.3.1 exactly, as its spec checks the resolved version, and its lockfile is regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- babel-es2015 merges into babel-es2016: both put ts-loader's output through @babel/preset-env, so one TypeScript target covers them. All 7 specs are kept, in es2015.tests.ts and es2016.tests.ts (Jasmine 7 rejects duplicate top-level suite names, so each is named for what it checks). - babel-importCodeSplitting becomes babel-codeSplitting and takes the old babel-codeSplitting's require.ensure spec, so one test covers both ways webpack splits code. The old test's "a and b" spec duplicated one in app.tests.ts, so it isn't carried over. - New inertLoaderOptions test: a build that sets resolveModuleName, resolveTypeReferenceDirective and getCustomTransformers (accepted but unused on the TypeScript 7 API) still succeeds and runs. This keeps the backwards-compatibility promise covered while the resolveModuleName and loaderOptions tests are skipped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- New test/testConfig.js reads a test's test.json ("skip" reason and
"minWebpack"), used by both runners.
- The comparison runner's hard-coded skip list (projectReferences*,
customTransformer*, nolib, tsconfigInvalidFile) becomes a test.json
with the reason in each of those 23 tests, and the reason is printed
when skipping. The unused, undocumented _IGNORE_ marker is replaced
by "skip". --single-test now applies these skips too, and an invalid
test.json fails that test.
- Document the skip rules in the comparison README and AGENTS.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Modernises the test packs:
test.jsonskip rules between both packs.Webpack 4 is still tested throughout.
1. Dependencies and conventions
Folder names: drop the TypeScript version prefixes
3.0.1_projectReferences→projectReferences,4.7.0_5.0.0_node16-file-suffix→node16-file-suffix. ts-loader now needs TypeScript 7.1+, and every prefix was ≤ 5.6.2, so the TypeScript check always passed.node16-file-suffixandoptionsCaching). They now declare it in atest.json({ "minWebpack": 5 }), whichrun-tests.jsreads, comparing webpack's major version. The TypeScript check and theisLowerVersionhelper are removed.src/index.tshas a comment pointing atresolveModuleName, and it's updated for the new name.Shared test dependencies installed once
jasmineand@types/jasminelive in the rootpackage.json. The per-testjasmine-core ^2.3.4and@types/jasmine ^2.xare removed, and so is the rootjasmine-core ^6, which nothing loaded (Karma usedkarma-jasmine's own 4.6.1).package.jsonor lockfile of their own, so the runner skipsnpm installfor them.Babel 6 → Babel 7 (execution and comparison tests)
babel-core,babel-loader@7,babel-polyfillandbabel-preset-es2015/es2016/reactare replaced by@babel/core,@babel/preset-envand@babel/preset-react(^7.29.0) withbabel-loader@^8.4.1, shared by both test packs. Babel 8 is out, butbabel-loader@8(the last version that supports webpack 4) requires@babel/core7.package.jsonat all.@babel/preset-env(withmodules: false, as before) in place ofes2015+es2016, and@babel/preset-reactin place ofreact. Thesyntax-dynamic-importplugin is gone: Babel 7 parsesimport()by default.babel-polyfillis removed, including fromimportCodeSplitting/main.js, a non-Babel test that only worked because the root happened to install it. Babel 7.18+ inlines the regenerator runtime that the async tests need.babel-issue81,babel-issue92,babel-es6resolveParent) have regenerated snapshots. The diffs are Babel 7's helpers and formatting, with the same compiled code.pnpm/pnpm-lock.yamlis regenerated to remove the jasmine entries. CI's pnpm uses a frozen lockfile.2. Run the tests in Node with Jasmine instead of Karma
No test needed a browser: the two React specs use the shallow renderer, and the only uses of
documentare 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 byrun-tests.js:main.js(which loads its*.tests.tsspecs withrequire.context) withtarget: 'node'anddevtool: 'inline-source-map', into a temporary folder that's deleted afterwards. Themode,module,resolveandresolveLoadersettings come from the test'swebpack.config.js, the same partstest/karmaConfig.jspassed to Karma.exitOnCompletion = false). Source maps are enabled, so failure stacks point at the TypeScript source, e.g.webpack:///test/app.tests.ts:9:52.incomplete, as it does when a focusedfit/fdescribeis 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.--debugstill runsnode --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 bothsrc/and the specs.main.js(justoptionsCaching) are still built byrun-webpack.js.allowJs-entryFileIsJswas the one test whosekarma.conf.jslisted spec files with a glob instead ofmain.js. It now has amain.jslike 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.tsuseddeclare module externalLib { ... }, which TypeScript 7 rejects (TS1540). It's nowdeclare namespace externalLib.resolveModuleName: theresolveModuleNameloader option is accepted but does nothing on the TypeScript 7 API (as Support TypeScript 7.1 API #1704 notes), sofoo/bar/bazdon'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 asloaderOptions(which covers the also-inertgetCustomTransformers). Both tests now have"skip": "<reason>"in theirtest.json, so each can be turned back on by deleting that line if the API adds these hooks.Dependencies
karma,karma-chrome-launcher,karma-jasmine,karma-mocha-reporter,karma-sourcemap-loader,karma-webpack(181 packages out ofnode_modules), the 26 per-testkarma.conf.jsfiles andtest/karmaConfig.js.jasmine@^7.0.0and@types/jasmine@^7.0.0. All the existing specs type-check and run unchanged on Jasmine 7.webpack@^4.47.0, withoutkarma-webpack@^4.0.2.Notes
target: 'node'instead of Karma'swebchanges how webpack resolves packages: it ignores thebrowserfield, and code-split chunks load withrequireinstead of script tags. Every test passes either way. If a future test needs the browser build of a package, it can setresolve.aliasFields: ['browser']in its webpack config, andrun-specs.jspasses that through.karma-jasminedid. The default reporter prints dots plus failure details instead of each spec's name.3. Comparison tests: mocha → Jasmine
The comparison harness used mocha lightly (one
describe, twoits, a timeout anddonecallbacks), while the execution tests now use Jasmine. Moving the harness is a small change in two files; moving the execution specs to mocha would have meant rewriting about 70expectcalls. Startup time is about the same (Jasmine is ~18ms faster per process, about 1s across the 168s suite), so this is about having one framework, not speed.create-and-execute-test.js: the 90s timeout moves from mocha'sthis.timeout(90000)toit()'s third argument.donecallbacks work unchanged. Its own arguments now come after Jasmine's--marker (-- --test-to-run=<name> [--save-output]), so they're parsed withutil.parseArgsin strict mode instead of being mixed with the test runner's.run-tests.jsrunsnode node_modules/jasmine/bin/jasmine.js --random=false --color create-and-execute-test.js -- ...withexecFileSync(no shell).--random=falsekeeps thetranspileOnlyrun after the normal one. picocolors also reads the--colorflag, so webpack's output keeps its colours and the snapshots are unchanged.--debugstill pauses on port 5858.run-tests.jsnow skipstestLib(the library the npmLink test links to) and folders with an_IGNORE_file before starting them. Mocha passed them with zero specs, but Jasmine reports that asincomplete. So the suite count drops from 57 to 56; no real test is lost.globals.d.ts(hand-written mocha types) is replaced by@types/jasminein the comparison tests'tsconfig.json.mocha(16 packages). knip now sees thatjasmineand@types/jasmineare used, so they're off its ignore list along withmocha..claude/settings.jsonallows the Jasmine commands instead of mocha's.4. React 18, test merges, shared skip rules
React 15/16 → 18
react,babel-reactandpnpmmove to React 18.3.1, the newest React that webpack 4 can still bundle (verified on webpack 4.47).react-addons-test-utils, so the specs render withreact-dom/server'srenderToStaticMarkupand compare the HTML. App components are plain functions (React.SFCis gone from the types), and the app entry files, which the specs don't load, usecreateRoot.reactcompiles JSX with"jsx": "react-jsx".babel-reactkeeps"preserve"and uses@babel/preset-react's automatic runtime.pnpmpins React 18.3.1 exactly, because its spec checks the version resolved through pnpm's layout. Its lockfile is regenerated.Overlapping Babel tests merged (8 → 6)
babel-es2015merges intobabel-es2016. Both put ts-loader's output through@babel/preset-env, so one TypeScript target covers them. All 7 specs are kept, ines2015.tests.tsandes2016.tests.ts.babel-importCodeSplittingbecomesbabel-codeSplittingand takes the old test'srequire.ensurespec, so one test covers both ways webpack splits code. The old test's "a and b" spec duplicated one already inapp.tests.ts.New
inertLoaderOptionstestA build that sets
resolveModuleName,resolveTypeReferenceDirectiveandgetCustomTransformers(accepted but unused on the TypeScript 7 API) still succeeds and runs. Each option is set to something that would work if it were used. This keeps the backwards-compatibility promise covered while theresolveModuleNameandloaderOptionstests are skipped. Adding an option ts-loader doesn't accept makes it fail with "unexpected loader option".One set of skip rules for both packs
test/testConfig.jsreads a test'stest.json("skip": "<reason>"and"minWebpack"), used by both runners.projectReferences*,customTransformer*,nolib,tsconfigInvalidFile) becomes atest.jsonwith the reason in each of those 23 tests, and the reason is printed when skipping. The unused, undocumented_IGNORE_marker is replaced by"skip".--single-testnow applies these skips too. Before,--single-test projectReferencesran the test despite the skip list; now it says why it's skipped. To try a skipped test, remove its"skip"line.Other cleanup
Removed the unused
isNotBabelTestfilter fromrun-tests.js. It was commented out, and its message about webpack support was outdated.Rewrote
test/execution-tests/README.mdfor the new runner,test.json, and the root dependencies.AGENTS.mdis updated to match.knip.json:ignoreDependenciesnow listsjasmine,@types/jasmineand the Babel 7 packages instead of Karma, Babel 6 andjasmine-core.test.jsonholds every execution test skip rule:"minWebpack"and"skip": "<reason>". OneisRunnableTestinrun-tests.jsreads and validates it.--single-testnow applies the same skips as a full run:test.json(execution), andtestLib/_IGNORE_folders (comparison, through a sharedisRunnableTest).--watch,--debugand the VS Code launch configs all go through--single-test.An invalid
test.json(bad JSON, aminWebpackthat isn't a positive whole number, or an emptyskip) fails that test instead of stopping the whole run.run-specs.jsdeletes its temporary folder from anexithandler, so the folder is also removed whenwebpack.config.jsorwebpack()throws, and on SIGINT/SIGTERM/SIGHUP in watch mode.--watchstops the previous spec run as soon as webpack sees a change, so its output can't be mistaken for the new edit's (even if the rebuild fails), and stops it before the temporary folder is deleted on exit.The README says that with
--watchthe specs run in a fresh process, so breakpoints in the specs aren't hit under--debug(ones insrc/still are).--match-testis applied beforetest.jsonis read, so only the selected tests'test.jsoncan skip or fail them.run-specs.jsexits once the specs finish (keeping the exit code), so a spec that leaves a timer or socket open can't hang the run, as Karma's single run couldn't. The temp folder cleanup on exit no longer throws if the folder can't be removed (e.g. on Windows, when a stopped spec process still has the bundle open).Jasmine treats spec paths as glob patterns, so a path containing
[or{would match nothing.run-specs.jspasses the bundle withaddSpecFile(taken as-is), and the comparison runner passescreate-and-execute-test.jsrelative to the working directory, so neither the temp directory nor the checkout path can hide the specs.The comparison runner also applies
--match-testbefore its skip checks, so skip messages are only logged for the selected tests.Testing (local, macOS, Node 26, TypeScript 7.1.0-beta)
loaderOptionsandresolveModuleNameskipped). Before the Babel merges and the new test, every suite ran the same number of specs as under Karma onnext. 16s, down from about 50s onnext.node16-file-suffixandoptionsCachingalso skipped, viatest.json), including React 18.expect, a type error in a spec, and amain.jsthat 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.lint,knip,format:checkand the threecheck:scripts.🤖 Generated with Claude Code