This document explains the structure of the test/ directory in the Webpack project using Jest. The directory is organized into multiple folders and files, each serving a specific purpose in testing various aspects of Webpack’s functionality.
Directories come first, in alphabetical order, then the individual files worth their own note. Add a new entry where the alphabet puts it — the sections are deliberately unnumbered, so nothing has to be renumbered around it.
- Purpose: Stores Jest snapshot files for comparing output consistency over time.
- Usage: Used for testing UI components, serialized data, or expected module outputs.
- Purpose: Contains test cases for benchmarking Webpack's performance.
- Usage: Measures build times, memory usage, and optimization impact.
- Kinds of case (picked from the directory name):
- build (default, e.g.
many-modules-esm/) — awebpack.config.mjsplus an entry; measures one build per scenario (mode-development,mode-development-rebuild,mode-production). *-unit(e.g.js-parser-unit/) — anindex.bench.mjsexportingdefault (bench) => {…}; measures a piece oflib/directly, with no scenarios.*-runtime(e.g.many-modules-interop-runtime/) — measures the emitted bundle, not the build. The case is compiled once per scenario outside any measured region; theexectask then instantiates the output (runtime bootstrap plus every module factory that runs at import time) and calls the entry's exportedrunon it. The rebuild scenario is skipped, since it emits the same output.
- build (default, e.g.
- Writing a
*-runtimecase: the entry must exportrun(seed), do its work with that opaque seed, and return the result — otherwise the compiler folds the workload away and the bench measures nothing (both are checked and fail the run). The harness forcestarget: "node"and acommonjs2library so the output can be instantiated in-process; generate large fixtures fromoptions.mjs(setup()), as the build cases do.
exec is reported per scenario, so a development/production pair shows what scope hoisting and minification are worth at runtime.
- Purpose: General test cases covering core functionalities.
- Usage: Includes unit and integration tests for various modules and features.
- Purpose: Tests related to Webpack configurations.
- Usage: Ensures that Webpack’s configuration (e.g., loaders, plugins) functions correctly.
- Purpose: Every git submodule webpack checks out for testing — today the four spec corpora below and terser's, swc's and oxc's own tests. Nothing here is webpack's to edit: each directory belongs to its upstream project, and this repository only pins a commit.
- Purpose: ECMAScript test262 conformance test cases.
- Usage: Git submodule — initialize with
git submodule update --init test/external/test262-cases. Test runners:test/specCases/test262.spectest.js, which builds and runs each test in development, in production unminified (a failure there is webpack's own, not a minimizer's), and in production once per JavaScript minimizer: terser, and webpack's printer asexperiments.futureDefaultsswitches it. The minified modes skip thefn-namecases a renamed binding changes and keep function and class names and turnpure_gettersoff, as test262 reads names and properties that throw; the printer also runs withpure_conversionsandpure_heritageoff, as test262 converts and extends values that run code or throw on purpose. What each minimizer still fails is listed by reason in itsMINIFIED_FAILUREStable, and a listed case that passes fails the suite until removed. Andtest/specCases/minify-corpora.spectest.js, which minifies each test with webpack's printer and with terser.
- Purpose: WHATWG html5lib-tests tokenizer conformance cases for
lib/html/syntax. - Usage: Git submodule — initialize with
git submodule update --init --depth 1 test/external/html5lib-tests test/external/wpt: the runner reads both corpora. Test runner:test/specCases/html5lib.spectest.js(yarn test:html5lib) compiles every input as a webpack HTML entry to confirm the full pipeline handles it without crashing.
- Purpose: web-platform-tests, read two ways.
html/syntax/parsing/resources/*.datis the HTML tree-construction conformance corpus forparseHtml(html5lib-tests dropped its copy in224991e). The.htmldocuments underhtml/,conformance-checkers/anddom/nodes, plus the declarations thecss/**/parsing/tests state a verdict for, are the printers' corpus: minifying must not change the DOM webpack's parser builds, the DOM Chrome builds, or the style Chrome computes. - Usage: Git submodule — initialize with
git submodule update --init --depth 1 test/external/wpt(the repository is ~161k files, so keep it shallow). Test runners:test/specCases/html5lib.spectest.js(yarn test:html5lib), which also readstest/external/html5lib-tests— initialize both to run the whole suite — andtest/specCases/syntaxEquivalence.spectest.js(yarn test:syntax-equivalence), whose browser tiers need a Chrome (CHROME_EXECUTABLE_PATHpicks a binary other than the pinned build). A document that is not UTF-8 is skipped: the encoding fixtures are UTF-16, which no string API can read as source.
- Purpose: CSS Syntax Level 3 conformance corpus for
lib/css/syntax. - Usage: Git submodule — initialize with
git submodule update --init test/external/css-parsing-tests. Test runner:test/specCases/cssParsing-webpack.spectest.js(yarn test:css-parsing) compiles every input as a webpack CSS entry to confirm the full pipeline handles it without crashing.
- Purpose: the W3C XML Conformance Test Suite (lddubeau's packaging) for
parseHtml(…, { xml: true })and the XML printer. - Usage: Git submodule — initialize with
git submodule update --init --depth 1 test/external/xml-conformance-suite. Test runner:test/specCases/xmlconf.spectest.js(yarn test:xmlconf) compares every document the suite gives an expected output for to James Clark's canonical form, and checks that every document, minified or beautified, reads back to the canonical form of its source. Documents needing an external entity or DTD, and XML 1.1 ones, are counted rather than run. Without the submodule the suite registers a no-op and passes, so a green run then says nothing.
- Purpose: swc's repository, read only under
crates/swc_ecma_minifier/tests: its fixtures with their configs, the testsexec.rsandmangle.rswrite inline, and the libraries it measures itself on. Each is held to terser's bytes like the terser corpora; a test with anexpected.stdout, and everyexec.rstest, also has its outputs run, and webpack's output may be no bigger than each fixture's recordedoutput.js, nor than what swc's port of a terser case records when itspassing.txtlists it, unlessSWC_SMALLERlists why. - Usage: Git submodule — initialize with
git submodule update --init --depth 1 test/external/swc. Test runner:test/specCases/minify-corpora.spectest.js(yarn test:minify-corpora).
- Purpose: oxc's repository, pinned to the commit that released webpack's
oxc-minifydevDependency and read only undercrates/oxc_minifier/tests: the source eachtest…(helper call in its Rust tests passes first, but TypeScript. Each is held to terser's bytes like the other corpora, and under the default minimizer's options every source of every corpus but test262 is also minified byoxc-minify: webpack's output may be no bigger than oxc's unlessOXC_SMALLERlists why, and an oxc output that misprints is no lead. - Usage: Git submodule — initialize with
git submodule update --init --depth 1 test/external/oxc. Test runner:test/specCases/minify-corpora.spectest.js(yarn test:minify-corpora), which also fails when the pin and the installedoxc-minifydisagree.
- Purpose: terser's own repository, pinned to webpack's
terserdevDependency. Itstest/compresscases andtest/inputfiles are two of the seven corporalib/javascript/syntax-printer.jsis held to (test262, swc's fixtures,exec.rsandmangle.rstests, and oxc's tests are the other five): each source is minified by terser as published and by webpack's printer under several option sets, and the outputs, or the errors, must be byte-for-byte the same, but for what webpack'scorrectphase fixes. A case stating itsexpect_stdoutalso has each output run in terser's sandbox, which must print it — or, forexpect_stdout: true, do whatever its input does, a throw or silence included. - Usage: Git submodule — initialize with
git submodule update --init --depth 1 test/external/terser. Test runner:test/specCases/minify-corpora.spectest.js(yarn test:minify-corpora), which also fails when the pin and the installedterserdisagree, so bumping the dependency means moving the pin with it. A new corpus is one entry in itsCORPORAlist.
- Purpose: Stores sample/mock data used in tests.
- Usage: Helps in creating consistent test cases with predefined inputs.
- Purpose: What runs the suites, as opposed to what they test. Jest's lifecycle (
globalSetup.js,globalTeardown.js,setupTestFramework.js), thepatch-node-env.jstest environment,runtimeCrashReporter.js, the caserunner/, thesnapshot/resolver and matchers,benchmark/, andruntimes/. runtimes/: The preload and setup files that let jest run under a non-Node runtime —bun-preload.jsandbun-sandbox-setup.jsforyarn test:bun,deno-worker-setup.jsanddeno-import-map.jsonforyarn test:deno. Wired in throughjest.config.jsand thetest:base:bun/test:base:denoscripts.- Not helpers: a reusable assertion or fixture belongs in
helpers/;harness/is only for machinery the test runner itself loads.
- Purpose: Utility functions and scripts to assist in testing.
- Usage: Provides reusable functions for mock data generation, cleanup, and assertions.
- Purpose: Focuses on Webpack’s Hot Module Replacement (HMR) functionality.
- Usage: Ensures live reloading and hot updates work correctly.
- Every HMR change is covered twice: a case for the default output (JSONP on
web,requireonnode,importScriptsonwebworker) and a twin underhotCases/esm-output/withoutput.module: trueandchunkFormat: "module". The ES module hot update wraps runtime code in a function, so it breaks in ways the default output never shows. A Worker twin there needspublicPath: "auto": a Node ES module worker can't import the harness'shttps://default.
- Purpose: A hand-driven counterpart to
hotCases/— those assert what an update does, this one lets you watch it happen in a browser. One panel per module type and ECMAScript form: an html entry, CommonJS, CSS, CSS modules, JSON via an import attribute, all three asset types, async and source-phase WebAssembly,import deferand an async chunk, plus a panel driving the HMR API itself (data/dispose,addStatusHandler,check,invalidate). Nothing here runs in CI. - Usage:
yarn playgroundserves it onhttp://localhost:8080with hot reloading on; edit any file and watch the page. Seetest/hotPlayground/README.mdfor what each file demonstrates.
- Purpose: Contains test cases related to memory limits.
- Usage: Ensures Webpack doesn’t exceed memory constraints.
- Purpose: Whole applications for
yarn test:size(below), whose report gives each one's total download in a Sample apps table. - A case is a directory with a
webpack.config.mjs(or.js; an array builds one compiler per entry, each reported on its own row) and, optionally, anoptions.mjswhose exportedsetup()runs first to generate or install its sources (intogenerated/, which git ignores). The harness sets onlycontext,output.path,mode: "production"when unset and quiet logging; everything else is the application's own configuration. - Today, each target with script (or CommonJS) and ES module output (
-esm):web:react-100,react-1k,react-5kandreact-1k-esm, React apps of that many components (lazy routes, CSS modules, JSON, SVG) from therealistic-appbenchmark's generator (test/harness/benchmark/create-app.mjs).node:node-ssrandnode-ssr-esm, that app rendered on the server by anhttpserver withreact-dom/server(test/harness/size/create-server.mjs).webworker:webworkerandwebworker-esm, a message loop lazily loading 40 data tasks built onlodash-esanddate-fns(test/harness/size/create-worker.mjs).library: one small library built once peroutput.library.type, the types read from the schema.
- Purpose: Holds the runners for specification-conformance suites.
- Files:
test262.spectest.js—yarn test:test262 --parts=3: defined whole, its ~175k tests overflow Jest's stack, so it always runs in parts, as CI doestest262-parser.spectest.js—yarn test:test262-parserhtml5lib.spectest.js—yarn test:html5libsyntaxEquivalence.spectest.js—yarn test:syntax-equivalencecssParsing-webpack.spectest.js—yarn test:css-parsingxmlconf.spectest.js—yarn test:xmlconfminify-corpora.spectest.js—yarn test:minify-corpora; CI runs it in three parts, each claiming the next 100 sources of a group no other part took, and the last part to finish checks no table entry went unmetjs-fuzz.spectest.js—yarn test:js-fuzz: programs fromtest/helpers/jsFuzzProgram.js, in the sloppy ES5 terser's ufuzz writes, in modern syntax, as modules importing a fixeddepand as webpack-shaped bundles, minified under ufuzz's option sets, webpack's and random ones; the parser is held to acorn's tree on each program and output, the printer to the tree it read and to its own output, each output to what its program prints and exports, the default set's source map to its output and source, and the output to what it prints minified again. Needs no submodule;JS_FUZZ_SEED=<n>replays one seed,JS_FUZZ_SEED=randomruns a new one named in the test's title, andJS_FUZZ_PROGRAMSsets the programs per seed.
- Purpose: Tests focused on Webpack’s statistical outputs.
- Usage: Verifies correct bundle sizes, dependencies, and optimizations.
- Purpose: The suite drivers —
TestCases.js,ConfigTestCases.js,HotTestCases.js,WatchTestCases.js— each exportingdescribeCases(config). - Usage: The
*.test.js/*.basictest.js/*.longtest.jsfiles at the top oftest/are thin shims that calldescribeCaseswith one suite's options, so one driver serves every variant (targets, devtools, cache modes). Jest parallelizes per file, which is why the variants stay separate files rather than being folded into one. - Variants:
variants.jsholds the option sets theTestCases*andHotTestCases*shims run under, re-exported from each driver asvariants. Add a variant by adding an entry there and one shim naming it — the shims stay separate files because jest parallelizes per file. - Note:
templates/is a sibling of the case directories it runs (cases/,configCases/,hotCases/,watchCases/), so a driver resolves paths from the sharedtest/root viatestRootDirectory(path.join(__dirname, "..")), never against__dirname.
- Purpose: Type-checking tests, likely for TypeScript integration.
- Usage: Ensures proper type definitions and compliance.
- Purpose: Contains
*.unittest.jsunit tests for various functionalities. - Usage: Ensures individual modules and functions work as expected.
- Layout: the syntax tests sit in one folder per language —
javascript/(tokenizer,parser,scope,printer),css/andhtml/(tokenizer,parser,printer),xml/(printer) andjson/(printer,web-manifest). A printer's file holds both of its modes and the language's minifier, which drives the printer in itsminifymode. The tests of thetooling/scripts sit intooling/. The rest, webpack's own classes such asJavascriptParser, stay at the top level.
- Purpose: Tests for Webpack’s watch mode functionality.
- Usage: Ensures file changes trigger correct rebuild behavior.
- Purpose: Measures how large the code webpack generates is, so a change to
lib/that grows (or shrinks) every bundle is visible. - Usage:
yarn test:sizebuilds everyconfigCases/case, plus every application insizeCoreReports/(reported asapps/<case>) — one plain Node.js process, outside jest, no worker pool — and writes a JSON report of what each case emitted: the raw, gzip, brotli and zstd size of every asset, plus a per-runtime-module breakdown (total bytes over the suite, how many cases emit it, the biggest single instance) — which is what shows which runtime grew, which is no longer emitted at all, and which one is simply large. The CI job (.github/workflows/code-size.yml) compares the report against the onemainlast uploaded, posts it as a pull request comment (updated in place on every push) and repeats it in the job summary. - What the comparison shows: how many cases, runtime modules and assets are changed / new / deleted / unchanged, then one row per changed asset — raw before → after, and the percentage each of gzip, brotli and zstd moved. A suite-wide total is deliberately not reported: a single number over 1400 cases hides which file moved, which is the only actionable part. The applications are the exception: a Sample apps table gives each one's whole download (gzip and raw, summed over its assets), which per-asset rows hide once code moves between chunks. With no baseline to compare against, the same table ranks the biggest assets instead.
- It never fails. There is nothing to assert here and no size budget to breach: a growing bundle is information, not a defect. Cases whose expected result is a build error emit nothing and are reported as such, with the reason, rather than counted as failures.
- Options:
--output <file>(report path),--baseline <file>(report to compare against),--summary <file>(append the markdown comparison, e.g.$GITHUB_STEP_SUMMARY),--filter/--negative-filter(regexps matched against<category>/<case>, also read fromFILTER/NEGATIVE_FILTER). - Note: the cases are built with the defaults a user gets — minification on, no
output.pathinfo— not with theConfigTestCasesones. Needs Node.js >= 22.15 for zstd.
- Purpose: Tests Webpack’s
BannerPluginfunctionality. - Usage: Ensures that the plugin correctly adds banners to the bundled files.
Behavior across rebuilds is a watchCases/ case, never a unit test calling compiler.run in a loop — output.clean, HMR update files, caches, anything one build leaves for the next. Each numbered step directory is one rebuild; its tests read WATCH_STEP, STATS_JSON and COMPILER, and the config can read the step from test/helpers/currentWatchStep. What the output can't show (which files a plugin touched) is recorded by a plugin in the config, which pushes a compilation.errors entry when it differs (watchCases/clean/removed-assets). A step needing time to pass waits in its own it with a longer timeout (watchCases/clean/hot-update-slow-rebuild).
Snapshot printed code; assert everything else. When the thing tested is generated output — bundles, minified CSS/HTML, serialized ASTs, stats text — use toMatchSnapshot(), not expect(...).toBe(...) on fragments (which pins one substring and ignores every other byte). For behavior, invariants, equivalences and error paths use explicit expects; a snapshot there only records what happened to be true. Never snapshot a value some machine can't produce (a snapshot skipped without an optional browser or native binary is reported obsolete and fails the run there), and keep control characters out of snapshots (one NUL makes git treat the file as binary and hide its diff).
Many Webpack tests simulate small projects that are compiled during the test run.
For example, a configuration test may look like:
test/configCases/entry/simple/ index.js webpack.config.js expected.txt
Explanation:
- index.js – entry file for the test project
- webpack.config.js – configuration used by webpack
- expected.txt – expected output or snapshot comparison
During the test run, webpack compiles this project and compares the result with the expected output to ensure behavior remains consistent.
- Jest is used for running tests.
- Snapshots help maintain consistency in output.
- Unit tests verify individual module functionality.
- Integration tests ensure multiple components work together.
Jest runs each file in one worker, so a long suite in one file leaves the runner's other cores idle. --parts=<n> (read by jest.config.js) runs every matched file in n workers at once, as n projects whose coverage merges as usual; global setup and teardown run once. A file splits its own work with test/helpers/testParts.js: isInPart(key) tells whether this part runs the work named key, by a hash of the name, so nothing to measure or store; claimInPart(key) gives the work to the first part that asks, so a part that ends early takes more and the parts end together even where costs vary; and currentPart() returns [part, parts], e.g. to run a check once in part 1. Without the flag a file is one part and runs everything; a file that doesn't use the helper runs whole in every part, so pass the flag only where it runs files using it — e.g. CI's yarn cover:spec --parts=auto --maxWorkers=100%, where auto takes one part per core and 100% one Jest worker per core, as Jest's default leaves one core idle. CI's spec job also runs the CSS, HTML and XML suites, which take a minute together and so run whole in every part.
unitCases/ProfilingPlugin.unittest.js, specCases/syntaxEquivalence.spectest.js and
WebpackDevServer.longtest.js launch a browser through test/helpers/launchBrowser.js,
which drives all three engines with playwright-core.
A browser that will not launch fails the suite — it is never skipped, so no
environment can report these checks as passing without having run them. The
helper only launches the builds playwright-core pins (Chrome for Testing for
Chrome), never the machine's own Chrome, which moves with the machine rather than
the lockfile: run yarn setup:browsers chrome first, or set
CHROME_EXECUTABLE_PATH to point at another binary.
syntaxEquivalence.spectest.js is the one that reads more than one engine:
EQUIVALENCE_BROWSER takes chrome, firefox or webkit, or a list of them
that gives each part of a --parts run its
own engine: CI runs EQUIVALENCE_BROWSER=chrome,firefox,webkit with
--parts=3, all three at once on one runner. Fetch the browser first — yarn setup:browsers (all three,
or name one; --with-deps also installs the system libraries Firefox and WebKit
need) — which installs into playwright's own cache (PLAYWRIGHT_BROWSERS_PATH,
defaulting to ~/.cache/ms-playwright on Linux). Firefox and WebKit are
playwright's own builds of those engines, patched for automation, so
FIREFOX_EXECUTABLE_PATH and WEBKIT_EXECUTABLE_PATH must point at such a
build, never a stock browser; Chrome is the stock Chrome for Testing, and
CHROME_EXECUTABLE_PATH takes any Chrome. It compares
effects, never CSSOM text: a stylesheet by the styles elements built from its
selectors compute, at the widths its queries switch on; a page by its DOM and
its render, with the dark color scheme read through each frame's own. Media is
emulated on Chromium only, the engine the filed defects were measured on, so only
there is a stylesheet naming print also read under it. A defect only one
engine has is filed against it with a reason opening <engine> only: rather
than tolerated in all three, and each list must match exactly, so a defect the
engine fixes fails until its entry goes. A workaround in the suite's own code
gets the same treatment through a probe in ENGINE_QUIRKS.
Every engine is the build the lockfile pins: the ones the installed
playwright-core ships. Dependabot bumps it in a browsers group of its own, so
a new engine arrives in one pull request, and a defect or quirk it fixes fails
there rather than under unrelated changes.
test:bun and test:deno run the Chrome-driven ProfilingPlugin.unittest.js
and WebpackDevServer.longtest.js too, on the same pinned Chrome.
No old browser runs in CI, so test/helpers/ecmaConformance.js asks the two
questions one would answer, over every configCases case in ConfigTestCases
and ConfigCacheTestCases. See that file for how it reads an environment.
- Will it parse? Always on: every runtime module a build renders is parsed against the environment it was rendered for.
- Will it run?
restrictEnvironment: trueintest.config.jsremoves from the bundle's realm what the target lacks, so webpack's guards are taken rather than stepped over. Web targets only.
Two more test.config.js fields: ecmaConformance: true widens the parse check
to every emitted asset, so the case's own sources are held to its
output.environment too, and
ecmaConformanceExpected declares findings deliberate as regexps, each with its
reason — one that stops matching fails the case.
test/helpers/analyzableConformance.js walks every output.module case the way
another tool would: it takes the specifiers a lexer reports plus the
new URL(…, import.meta.url) names only an AST sees, follows the literal ones
from each entry, and asks two questions of what it reaches.
- Does every chunk have a name someone else can follow? A chunk only
__webpack_require__.ereaches is a finding, unless the build recorded why it kept the runtime form —performance.analyzableBailoutsreads the same reasons — or anevaldevtool hid every specifier inside a string. - Does every name it writes exist? A specifier naming a place inside the output that neither the assets nor the directory holds is a finding.
analyzableConformanceExpected in test.config.js declares findings deliberate
as regexps, each with its reason — one that stops matching fails the case.
Under ecmaVersion/, the es5-* cases cover one runtime-emitting feature each
(jsonp, importScripts, require and read-file chunk loading, workers and
asset urls, css, hot updates, wasm, the library wrappers, Module Federation,
the neutral platform);
es-versions sweeps every rung of the esX ladder; environment-flags turns
each flag off on its own against an otherwise current target, which a version
sweep cannot do; and esm-environment repeats both over ESM output.
Upstream test suites webpack runs against but doesn't maintain — upstream's to change, ours only to pin.
Git submodules, all under test/external/, checked out on demand: yarn setup doesn't fetch them, and each CI job fetches only its own, one commit deep.
test/external/test262-cases— tc39/test262; fetched bytest262,spectest/external/html5lib-tests— html5lib/html5lib-tests; fetched byspectest/external/wpt— web-platform-tests/wpt; fetched byspec,syntax-equivalence(browsers)test/external/css-parsing-tests— CourtBouillon/css-parsing-tests; fetched byspectest/external/xml-conformance-suite— lddubeau/xml-conformance-suite (the W3C XML Conformance Test Suite); fetched byspectest/external/terser— terser/terser, pinned to the installedterser's version; fetched byspectest/external/swc— swc-project/swc, read only undercrates/swc_ecma_minifier/tests; fetched byspectest/external/oxc— oxc-project/oxc, pinned to the installedoxc-minify's release and read only undercrates/oxc_minifier/tests; fetched byspec
git submodule update --init --recursive --depth 1 # check out the commits the repo pins
git submodule update --init --recursive --remote --depth 1 # move every pin to its upstream tipKeep --depth 1 (wpt alone is ~161k files). --remote changes the recorded commits, so git status shows the paths modified — commit that only once CI is green on them, or git submodule update back to the pins.
One upstream corpus is vendored rather than pinned as a submodule:
test/fixtures/acorn-corpus.json— acorn's test suite — the one upstream corpus vendored rather than pinned, because acorn's npm tarball ships no tests.unitCases/javascript/parser.unittest.jsholds both webpack parser entry points to it and owns recording it (no generator script orpackage.jsonentry): it replays acorn'stest/tests*.jsagainst a recording driver, keeping sources and options but never expected trees, which come from acorn itself. Bumping theacorndevDependency moves the corpus; to refresh, clone acorn at the new version intonode_modules/.cache/acorn-<version>and re-run withWEBPACK_UPDATE_ACORN_CORPUS=1. With that checkout present the run checks the vendored corpus against it; without it (CI, most machines) the corpus stands on the version it names, which the run pins to the installed acorn.
Run one integration case by name (<category> <case-name>, e.g. css basic):
yarn test:basic --testPathPatterns="ConfigTestCases" --testNamePattern="<category> <case>"Swap in StatsTestCases, HotTestCases, WatchTestCases, … (full matrix in below). Prefix WEBPACK_TEST_STATS=1 to also write each build's verbose stats.txt (stats.<step>.txt for watch cases) next to its output under test/js/; it's off by default because it costs about as much as the build. The test262, html5lib, syntax-equivalence and css-parsing suites need submodules — run git submodule update --init --depth 1 test/external/test262-cases test/external/html5lib-tests test/external/wpt test/external/css-parsing-tests first, or they fail confusingly.
A configCases/ case is a mini project: index.js (assertions; a throw fails) plus webpack.config.js; the emitted bundle is executed, so it must run. Optional: errors.js / warnings.js export matcher arrays for expected diagnostics (otherwise any error/warning fails the case); test.filter.js returns false to skip (e.g. by Node version when the fixture itself needs newer syntax — see Target the Node baseline); test.config.js customizes the run (e.g. findBundle). Put DOM changes such as a server-rendered <link> in setupDocument(document, { options, index, target }), which runs once per run before any bundle executes, never in moduleScope, which runs again for every module and so repeats them.
Every command is a package.json script; AGENTS.md lists the few whose use isn't obvious. The rest:
yarn setup— Install dependencies and link the checkout aswebpack; non-interactive off a TTY.yarn tsc— Type check thelib/JSDoc.yarn validate:changeset— Validate pending.changeset/files.yarn test:unit— All*.unittest.js.yarn test:integration— Integration suites (basictest/longtest/test).yarn test:test262/test:html5lib/test:css-parsing/test:xmlconf— Spec-conformance suites.yarn test:minify-corpora— webpack's JS minifier vs the published one it replaces, byte for byte, over every JS corpus (details).yarn test:syntax-equivalence— HTML/CSS printers vs a real browser's reading of their output (configCases,wpt).yarn test:size— Generated-code size over allconfigCases/(per asset, plus runtime modules per runtime).yarn cover:unit— Unit-test coverage.yarn types:cover— Share oflib/that is precisely typed.yarn build:examples— Buildexamples/(verify after changing options).
To execute all tests:
yarn testChoose test command based on modified directory:
| Modified directory/file | Command |
|---|---|
test/unitCases/**/*.unittest.js |
yarn test:base --testPathPatterns="<filename>" |
test/specCases/ |
Run the matching yarn test:<suite> command |
test/cases/ |
yarn test:basic |
test/configCases/ |
yarn test:basic --testPathPatterns="ConfigTestCases" |
test/statsCases/ |
yarn test:basic --testPathPatterns="StatsTestCases" |
test/watchCases/ |
yarn test:base --testPathPatterns="WatchTestCases" |
test/hotCases/ |
yarn test:base --testPathPatterns="HotTestCases" |
test/benchmarkCases/ |
FILTER="<case-name>" yarn benchmark |
lib/runtime/ |
yarn test:size (size of the generated code; --filter "<category>/" narrows it) |
test/external/test262-cases/ |
yarn test:test262 (requires git submodule update --init test/external/test262-cases first) |
test/external/html5lib-tests/ |
yarn test:html5lib (requires git submodule update --init --depth 1 test/external/html5lib-tests test/external/wpt first) |
test/external/wpt/ |
yarn test:html5lib + yarn test:syntax-equivalence (require git submodule update --init --depth 1 test/external/html5lib-tests test/external/wpt first) |
test/external/css-parsing-tests/ |
yarn test:css-parsing (requires git submodule update --init test/external/css-parsing-tests first) |
test/external/xml-conformance-suite/ |
yarn test:xmlconf (requires git submodule update --init --depth 1 test/external/xml-conformance-suite first; without it the suite passes as a no-op) |
Running a single test case with --testNamePattern. The test name format is <category> <case-name> (e.g., css basic, asset url):
yarn test:basic --testPathPatterns="ConfigTestCases" --testNamePattern="css basic"Multiple patterns can be combined with |:
yarn test:basic --testPathPatterns="ConfigTestCases" --testNamePattern="css basic|css url"- Add new test cases in the appropriate folder.
- Use Jest assertions and mocks for consistency.
- Run
yarn testbefore pushing changes to validate functionality.