/
githubmirror
/
webpack
Обзор
Документация
Войти
/
githubmirror
/
webpack
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
test/HotTestCases.template.js
393 строки
13 KB
Alexander Akait
Reduce skipped tests across Node, Deno and Bun and prune dead WebAssembly filters (#21257)
24 июн 2026, 01:02
Не верифицирован
24 июн 2026, 01:02
efeb5ce
Код
Авторство
О чём код?
"use strict"; require("./helpers/warmup-webpack"); /** @typedef {{ name: string, tests: string[] }} Category */ /** * @typedef {object} SuiteConfig * @property {string} name suite name * @property {string | string[]=} target target */ /** * @typedef {object} HotTestConfig * @property {((scope: EXPECTED_ANY, options: import("../").Configuration) => void)=} moduleScope */ const path = require("path"); const fs = require("graceful-fs"); /** @type {{ sync: (p: string) => void }} */ const rimraf = require("rimraf"); const checkArrayExpectation = require("./checkArrayExpectation"); const { TestRunner } = require("./harness/runner"); const createLazyTestEnv = require("./helpers/createLazyTestEnv"); const deprecationTracking = require("./helpers/deprecationTracking"); const supportsObjectHasOwn = require("./helpers/supportsObjectHasOwn"); const supportsOptionalChaining = require("./helpers/supportsOptionalChaining"); const casesPath = path.join(__dirname, "hotCases"); /** @type {Category[]} */ const categories = fs .readdirSync(casesPath) .filter((dir) => fs.statSync(path.join(casesPath, dir)).isDirectory()) .map((cat) => ({ name: cat, tests: fs .readdirSync(path.join(casesPath, cat)) .filter((folder) => !folder.includes("_")) })); /** * @param {SuiteConfig} config suite config */ const describeCases = (config) => { // universal targets run the same (ESM) bundle once per environment; the // suite forces module output, so probe with `output.module` set const isUniversal = TestRunner.isUniversalTarget({ target: config.target, output: { module: true } }); describe(config.name, () => { for (const category of categories) { // `universal` cases only run in the universal suite, and vice versa. if ((category.name === "universal") !== isUniversal) { continue; } describe(category.name, () => { for (const testName of category.tests) { const testDirectory = path.join(casesPath, category.name, testName); const filterPath = path.join(testDirectory, "test.filter.js"); if (fs.existsSync(filterPath) && !require(filterPath)(config)) { // eslint-disable-next-line jest/no-disabled-tests describe.skip(testName, () => { it("filtered", () => {}); }); continue; } describe(testName, () => { /** @type {import("../").Compiler} */ let compiler; afterAll((/** @type {EXPECTED_ANY} */ callback) => { compiler.close(callback); compiler = /** @type {EXPECTED_ANY} */ (undefined); }); it(`${testName} should compile`, (done) => { const webpack = require(".."); const outputDirectory = path.join( __dirname, "js", `hot-cases-${config.name}`, category.name, testName ); rimraf.sync(outputDirectory); const recordsPath = path.join(outputDirectory, "records.json"); const fakeUpdateLoaderOptions = { updateIndex: 0 }; const configPath = path.join(testDirectory, "webpack.config.js"); /** @type {import("../").Configuration} */ let options = /** @type {import("../").Configuration} */ ({}); if (fs.existsSync(configPath)) options = require(configPath); if ( typeof (/** @type {EXPECTED_ANY} */ (options)) === "function" ) { options = /** @type {EXPECTED_ANY} */ (options)({ config }); } if (!options.mode) options.mode = "development"; if (!options.devtool) options.devtool = false; if (!options.context) options.context = testDirectory; if (!options.entry) options.entry = "./index.js"; if (!options.output) options.output = {}; if (isUniversal) { // universal target requires ESM output to run in node and web if (!options.experiments) options.experiments = {}; if (options.experiments.outputModule === undefined) { options.experiments.outputModule = true; } if (options.output.module === undefined) { options.output.module = true; } if (options.output.chunkFormat === undefined) { options.output.chunkFormat = "module"; } } if (!options.output.environment) options.output.environment = {}; if ( options.output.environment.optionalChaining === undefined && !supportsOptionalChaining() ) { // generated runtime runs in this Node.js process; avoid `?.` on Node < 14 options.output.environment.optionalChaining = false; } if ( options.output.environment.hasOwn === undefined && !supportsObjectHasOwn() ) { // generated runtime runs in this Node.js process; avoid `Object.hasOwn` on Node < 16.9 options.output.environment.hasOwn = false; } if (!options.output.path) options.output.path = outputDirectory; if (!options.output.filename) { options.output.filename = `bundle${ options.experiments && options.experiments.outputModule ? ".mjs" : ".js" }`; } if (!options.output.chunkFilename) { options.output.chunkFilename = `[name].chunk.[fullhash]${ options.experiments && options.experiments.outputModule ? ".mjs" : ".js" }`; } if (options.output.pathinfo === undefined) { options.output.pathinfo = true; } if (options.output.publicPath === undefined) { options.output.publicPath = "https://test.cases/path/"; } if (options.output.library === undefined) { options.output.library = { type: options.experiments && options.experiments.outputModule ? "module" : "commonjs2" }; } if (!options.optimization) options.optimization = {}; if (!options.optimization.moduleIds) { options.optimization.moduleIds = "named"; } if (!options.module) options.module = {}; if (!options.module.rules) options.module.rules = []; options.module.rules.push({ loader: path.join( __dirname, "hotCases", "fake-update-loader.js" ), enforce: "pre" }); if (!options.target) options.target = config.target; if (!options.plugins) options.plugins = []; options.plugins.push( new webpack.HotModuleReplacementPlugin(), new webpack.LoaderOptionsPlugin(fakeUpdateLoaderOptions) ); if (!options.recordsPath) options.recordsPath = recordsPath; /** @type {HotTestConfig} */ let testConfig = {}; try { // try to load a test file testConfig = Object.assign( testConfig, require(path.join(testDirectory, "test.config.js")) ); } catch (_err) { // ignored } const onCompiled = ( /** @type {Error | null} */ err, /** @type {import("../").Stats} */ stats ) => { const deprecations = deprecationTracker(); if (err) return done(err); const jsonStats = stats.toJson({ errorDetails: true }); if ( checkArrayExpectation( testDirectory, jsonStats, "error", "Error", options, done ) ) { return; } if ( checkArrayExpectation( testDirectory, jsonStats, "warning", "Warning", options, done ) ) { return; } if ( checkArrayExpectation( testDirectory, { deprecations }, "deprecation", "Deprecation", options, done ) ) { return; } function runCompiler( /** @type {(err: EXPECTED_ANY, stats?: EXPECTED_ANY) => void} */ callback ) { fakeUpdateLoaderOptions.updateIndex++; const deprecationTracker = deprecationTracking.start(); compiler.run((err, _stats) => { const stats = /** @type {import("../").Stats} */ (_stats); const deprecations = deprecationTracker(); if (err) return callback(err); const jsonStats = stats.toJson({ errorDetails: true }); if ( checkArrayExpectation( testDirectory, jsonStats, "error", `errors${fakeUpdateLoaderOptions.updateIndex}`, "Error", options, callback ) ) { return; } if ( checkArrayExpectation( testDirectory, jsonStats, "warning", `warnings${fakeUpdateLoaderOptions.updateIndex}`, "Warning", options, callback ) ) { return; } if ( checkArrayExpectation( testDirectory, { deprecations }, "deprecation", `deprecations${fakeUpdateLoaderOptions.updateIndex}`, "Deprecation", options, callback ) ) { return; } callback(null, jsonStats); }); } const _stats = stats.toJson({ all: false, entrypoints: true }); const { results } = TestRunner.runBundles({ optionsArr: [options], outputDirectory, testConfig: { ...testConfig, evaluateScriptOnAttached: true }, category, testName, setupRunner: ({ runner }) => { if (testConfig.moduleScope) { testConfig.moduleScope(runner._moduleScope, options); } runner.mergeModuleScope({ it: _it, beforeEach: _beforeEach, afterEach: _afterEach, STATE: jsonStats, NEXT: runCompiler, NEXT_DEFERRED: (/** @type {EXPECTED_ANY} */ cb) => { // https://github.com/webpack/webpack/actions/runs/22039709807/job/63678606467?pr=20412 // When lazyCompilation is enabled, delay the first compilation re-run by 1000ms during HMR // to ensure that HTTP requests from dynamic imports (e.g., const promiseA = import("./moduleA")) // have already reached lazyCompilationBackend. This prevents NEXT from triggering // a recompilation while moduleA is still not marked as Activated and still returns // LazyCompilationProxyModule, which would cause a "No update available" error. setTimeout(() => { runCompiler(cb); }, 1000); }, // Re-run the same compilation version (without advancing the // fake-update index): lets a test wait out the lazy-compilation // activation race instead of relying on a single fixed delay. NEXT_RETRY: (/** @type {EXPECTED_ANY} */ cb) => { fakeUpdateLoaderOptions.updateIndex--; runCompiler(cb); } }); }, getBundlePaths: (_i, _options, runner) => { const bundles = /** @type {EXPECTED_ANY[]} */ ( /** @type {EXPECTED_ANY} */ (_stats.entrypoints).main .assets ).map((/** @type {EXPECTED_ANY} */ i) => i.name); // universal expands to one runner per target; pick by its target const isWeb = isUniversal ? runner.hasWebTarget() : config.target === "web"; if (isWeb) { return bundles; } // node runs the JS entry only; skip CSS/other assets that may sort last const jsBundles = bundles.filter( (/** @type {string} */ n) => /\.[cm]?js$/.test(n) ); const nodeBundles = jsBundles.length > 0 ? jsBundles : bundles; return [nodeBundles[nodeBundles.length - 1]]; } }); Promise.all(results).then( () => { if (getNumberOfTests() < 1) { return done(new Error("No tests exported by test case")); } done(); }, (err) => { console.log(err); done(err); } ); }; const deprecationTracker = deprecationTracking.start(); compiler = webpack(options); compiler.run(/** @type {EXPECTED_ANY} */ (onCompiled)); }, 20000); const { it: _it, beforeEach: _beforeEach, afterEach: _afterEach, getNumberOfTests } = createLazyTestEnv(20000); }); } }); } }); }; // eslint-disable-next-line jest/no-export module.exports.describeCases = describeCases;