webpack/test/TestCases.template.js

476 lines
12 KiB
JavaScript
Raw Normal View History

"use strict";
require("./helpers/warmup-webpack");
const path = require("path");
2025-07-03 17:06:45 +08:00
const fs = require("graceful-fs");
2018-10-18 04:15:46 +08:00
const rimraf = require("rimraf");
const checkArrayExpectation = require("./checkArrayExpectation");
2025-07-03 17:06:45 +08:00
const captureStdio = require("./helpers/captureStdio");
const createLazyTestEnv = require("./helpers/createLazyTestEnv");
const deprecationTracking = require("./helpers/deprecationTracking");
2022-02-15 02:31:59 +08:00
const filterInfraStructureErrors = require("./helpers/infrastructureLogErrors");
2025-07-08 22:46:17 +08:00
const { TestRunner } = require("./runner/index");
2022-02-11 14:24:35 +08:00
const casesPath = path.join(__dirname, "cases");
let categories = fs.readdirSync(casesPath);
categories = categories.map((cat) => ({
2024-07-31 11:31:11 +08:00
name: cat,
tests: fs
.readdirSync(path.join(casesPath, cat))
.filter((folder) => !folder.includes("_"))
2024-07-31 11:31:11 +08:00
}));
const createLogger = (appendTarget) => ({
log: (l) => appendTarget.push(l),
debug: (l) => appendTarget.push(l),
trace: (l) => appendTarget.push(l),
info: (l) => appendTarget.push(l),
2024-07-31 11:31:11 +08:00
warn: console.warn.bind(console),
error: console.error.bind(console),
logTime: () => {},
group: () => {},
groupCollapsed: () => {},
groupEnd: () => {},
profile: () => {},
profileEnd: () => {},
clear: () => {},
status: () => {}
});
2022-02-11 14:24:35 +08:00
const describeCases = (config) => {
describe(config.name, () => {
let stderr;
beforeEach(() => {
stderr = captureStdio(process.stderr, true);
});
afterEach(() => {
stderr.restore();
});
2024-08-02 02:36:27 +08:00
for (const category of categories) {
// eslint-disable-next-line no-loop-func
describe(category.name, () => {
2025-07-03 17:06:45 +08:00
jest.setTimeout(30000);
for (const testName of category.tests.filter((test) => {
2024-08-02 02:36:27 +08:00
const testDirectory = path.join(casesPath, category.name, test);
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(test, () => {
it("filtered", () => {});
});
2024-08-02 02:36:27 +08:00
return false;
}
return true;
})) {
const infraStructureLog = [];
2022-02-11 14:24:35 +08:00
2024-08-02 02:36:27 +08:00
// eslint-disable-next-line no-loop-func
describe(testName, () => {
const testDirectory = path.join(casesPath, category.name, testName);
const outputDirectory = path.join(
__dirname,
"js",
config.name,
category.name,
testName
);
const cacheDirectory = path.join(
__dirname,
"js/.cache",
config.name,
category.name,
testName
);
let testConfig = {};
const testConfigPath = path.join(testDirectory, "test.config.js");
if (fs.existsSync(testConfigPath)) {
testConfig = require(testConfigPath);
}
2024-08-02 02:36:27 +08:00
const TerserPlugin = require("terser-webpack-plugin");
2024-08-02 02:36:27 +08:00
const terserForTesting = new TerserPlugin({
parallel: false
});
let options = {
context: casesPath,
entry: `./${category.name}/${testName}/`,
target: config.target || "async-node",
devtool: config.devtool,
mode: config.mode || "none",
optimization: config.mode
? {
emitOnErrors: true,
minimizer: [terserForTesting],
...config.optimization
}
: {
removeAvailableModules: true,
removeEmptyChunks: true,
mergeDuplicateChunks: true,
flagIncludedChunks: true,
sideEffects: true,
providedExports: true,
usedExports: true,
mangleExports: true,
emitOnErrors: true,
concatenateModules: false,
moduleIds: "size",
chunkIds: "size",
minimizer: [terserForTesting],
...config.optimization
},
performance: {
hints: false
},
node: {
__dirname: "mock",
__filename: "mock"
},
cache: config.cache && {
cacheDirectory,
...config.cache
},
output: {
pathinfo: "verbose",
path: outputDirectory,
filename: config.module ? "bundle.mjs" : "bundle.js"
},
resolve: {
modules: ["web_modules", "node_modules"],
mainFields: [
"webpack",
"browser",
"web",
"browserify",
["jam", "main"],
"main"
],
aliasFields: ["browser"],
extensions: [".webpack.js", ".web.js", ".js", ".json"]
},
resolveLoader: {
modules: [
"web_loaders",
"web_modules",
"node_loaders",
"node_modules"
],
mainFields: ["webpackLoader", "webLoader", "loader", "main"],
extensions: [
".webpack-loader.js",
".web-loader.js",
".loader.js",
".js"
]
},
module: {
rules: [
{
test: /\.coffee$/,
loader: "coffee-loader"
},
{
test: /\.pug/,
loader: "pug-loader"
},
{
test: /\.wat$/i,
loader: "wast-loader",
type: "webassembly/async"
}
]
},
2025-07-03 17:06:45 +08:00
plugins: [
...(config.plugins || []),
function testCasesTest() {
this.hooks.compilation.tap("TestCasesTest", (compilation) => {
2025-07-03 17:06:45 +08:00
for (const hook of [
"optimize",
"optimizeModules",
"optimizeChunks",
"afterOptimizeTree",
"afterOptimizeAssets"
]) {
compilation.hooks[hook].tap("TestCasesTest", () =>
compilation.checkConstraints()
);
}
});
}
],
2024-08-02 02:36:27 +08:00
experiments: {
asyncWebAssembly: true,
topLevelAwait: true,
backCompat: false,
...(config.module ? { outputModule: true } : {})
},
infrastructureLogging: config.cache && {
debug: true,
console: createLogger(infraStructureLog)
2021-06-29 18:06:45 +08:00
}
2024-08-02 02:36:27 +08:00
};
beforeAll((done) => {
rimraf(cacheDirectory, done);
});
2024-08-02 02:36:27 +08:00
const cleanups = [];
2024-08-02 02:36:27 +08:00
afterAll(() => {
options = undefined;
testConfig = undefined;
for (const fn of cleanups) fn();
});
2024-08-02 02:36:27 +08:00
if (config.cache) {
it(
`${testName} should pre-compile to fill disk cache (1st)`,
(done) => {
2024-08-02 02:36:27 +08:00
const oldPath = options.output.path;
options.output.path = path.join(
options.output.path,
"cache1"
);
infraStructureLog.length = 0;
const deprecationTracker = deprecationTracking.start();
2024-08-02 02:36:27 +08:00
const webpack = require("..");
webpack(options, (err) => {
2024-08-02 02:36:27 +08:00
deprecationTracker();
options.output.path = oldPath;
if (err) return done(err);
const infrastructureLogErrors = filterInfraStructureErrors(
infraStructureLog,
{
run: 1,
options
}
);
if (
infrastructureLogErrors.length &&
checkArrayExpectation(
testDirectory,
{ infrastructureLogs: infrastructureLogErrors },
"infrastructureLog",
"infrastructure-log",
"InfrastructureLog",
2024-11-01 21:55:49 +08:00
options,
2024-08-02 02:36:27 +08:00
done
)
) {
return;
2024-07-31 05:43:19 +08:00
}
2024-08-02 02:36:27 +08:00
done();
});
2018-02-25 18:46:17 +08:00
},
2024-08-02 02:36:27 +08:00
testConfig.timeout || 60000
);
2024-08-02 02:36:27 +08:00
it(
`${testName} should pre-compile to fill disk cache (2nd)`,
(done) => {
2024-08-02 02:36:27 +08:00
const oldPath = options.output.path;
options.output.path = path.join(
options.output.path,
"cache2"
);
infraStructureLog.length = 0;
const deprecationTracker = deprecationTracking.start();
2024-08-02 02:36:27 +08:00
const webpack = require("..");
webpack(options, (err) => {
2024-08-02 02:36:27 +08:00
deprecationTracker();
options.output.path = oldPath;
if (err) return done(err);
const infrastructureLogErrors = filterInfraStructureErrors(
infraStructureLog,
{
run: 2,
options
}
);
if (
infrastructureLogErrors.length &&
checkArrayExpectation(
testDirectory,
{ infrastructureLogs: infrastructureLogErrors },
"infrastructureLog",
"infrastructure-log",
"InfrastructureLog",
2024-11-01 21:55:49 +08:00
options,
2024-08-02 02:36:27 +08:00
done
)
) {
return;
2018-02-25 18:46:17 +08:00
}
2024-08-02 02:36:27 +08:00
done();
});
2022-02-11 14:24:35 +08:00
},
2024-08-02 02:36:27 +08:00
testConfig.cachedTimeout || testConfig.timeout || 10000
);
}
2024-08-02 02:36:27 +08:00
it(
`${testName} should compile`,
(done) => {
2024-08-02 02:36:27 +08:00
infraStructureLog.length = 0;
2024-08-02 02:36:27 +08:00
const webpack = require("..");
2024-08-02 02:36:27 +08:00
const compiler = webpack(options);
const run = () => {
const deprecationTracker = deprecationTracking.start();
compiler.run((err, stats) => {
const deprecations = deprecationTracker();
if (err) return done(err);
const infrastructureLogErrors = filterInfraStructureErrors(
infraStructureLog,
{
run: 3,
options
2022-02-11 14:24:35 +08:00
}
2021-06-29 18:06:45 +08:00
);
2024-08-02 02:36:27 +08:00
if (
infrastructureLogErrors.length &&
checkArrayExpectation(
testDirectory,
{ infrastructureLogs: infrastructureLogErrors },
"infrastructureLog",
"infrastructure-log",
"InfrastructureLog",
2024-11-01 21:55:49 +08:00
options,
2024-08-02 02:36:27 +08:00
done
)
) {
return;
}
compiler.close((err) => {
2021-06-29 18:06:45 +08:00
if (err) return done(err);
2024-08-02 02:36:27 +08:00
const statOptions = {
preset: "verbose",
colors: false,
modules: true,
reasonsSpace: 1000
};
fs.mkdirSync(outputDirectory, { recursive: true });
fs.writeFileSync(
path.join(outputDirectory, "stats.txt"),
stats.toString(statOptions),
"utf8"
2024-08-02 02:36:27 +08:00
);
const jsonStats = stats.toJson({
errorDetails: true,
modules: false,
assets: false,
chunks: false
});
2022-02-15 02:31:59 +08:00
if (
checkArrayExpectation(
testDirectory,
2024-08-02 02:36:27 +08:00
jsonStats,
"error",
"Error",
2024-11-01 21:55:49 +08:00
options,
2022-02-15 02:31:59 +08:00
done
)
) {
return;
2022-02-11 14:24:35 +08:00
}
2022-02-15 02:31:59 +08:00
if (
checkArrayExpectation(
testDirectory,
2024-08-02 02:36:27 +08:00
jsonStats,
"warning",
"Warning",
2024-11-01 21:55:49 +08:00
options,
2022-02-15 02:31:59 +08:00
done
)
) {
return;
2022-02-11 14:24:35 +08:00
}
2024-08-02 02:36:27 +08:00
const infrastructureLogging = stderr.toString();
if (infrastructureLogging) {
done(
new Error(
`Errors/Warnings during build:\n${
infrastructureLogging
}`
)
2024-08-02 02:36:27 +08:00
);
}
2024-08-02 02:36:27 +08:00
expect(deprecations).toEqual(config.deprecations || []);
2024-08-02 02:36:27 +08:00
Promise.resolve().then(done);
});
2024-08-02 02:36:27 +08:00
});
};
if (config.cache) {
// pre-compile to fill memory cache
const deprecationTracker = deprecationTracking.start();
compiler.run((err) => {
2024-08-02 02:36:27 +08:00
deprecationTracker();
if (err) return done(err);
run();
2024-08-02 02:36:27 +08:00
});
} else {
run();
}
},
testConfig.cachedTimeout ||
testConfig.timeout ||
(config.cache ? 20000 : 60000)
);
it(`${testName} should load the compiled tests`, (done) => {
2025-07-08 20:52:43 +08:00
const runner = new TestRunner({
target: options.target,
outputDirectory,
testMeta: {
category: category.name,
name: testName
},
testConfig,
webpackOptions: options
2024-08-02 02:36:27 +08:00
});
2025-07-08 20:52:43 +08:00
runner.mergeModuleScope({
it: _it
});
if (testConfig.moduleScope) {
testConfig.moduleScope(runner._moduleScope, options);
2024-08-02 02:36:27 +08:00
}
2025-07-08 20:52:43 +08:00
runner.require.webpackTestSuiteRequire = true;
const results = [];
results.push(
runner.require(outputDirectory, `./${options.output.filename}`)
);
Promise.all(results).then(() => {
if (getNumberOfTests() === 0) {
return done(new Error("No tests exported by test case"));
}
done();
}, done);
2024-08-02 02:36:27 +08:00
}, 10000);
2024-08-02 02:36:27 +08:00
const { it: _it, getNumberOfTests } = createLazyTestEnv(
testConfig.timeout || 10000
);
});
2024-08-02 02:36:27 +08:00
}
});
2024-08-02 02:36:27 +08:00
}
});
};
2024-07-31 04:54:55 +08:00
// eslint-disable-next-line jest/no-export
module.exports.describeCases = describeCases;