webpack/test/TestCases.template.js

443 lines
12 KiB
JavaScript
Raw Normal View History

"use strict";
const path = require("path");
const fs = require("graceful-fs");
const vm = require("vm");
2021-06-24 16:05:37 +08:00
const { pathToFileURL, URL } = require("url");
2018-10-18 04:15:46 +08:00
const rimraf = require("rimraf");
const webpack = require("..");
const TerserPlugin = require("terser-webpack-plugin");
const checkArrayExpectation = require("./checkArrayExpectation");
const createLazyTestEnv = require("./helpers/createLazyTestEnv");
const deprecationTracking = require("./helpers/deprecationTracking");
const captureStdio = require("./helpers/captureStdio");
const asModule = require("./helpers/asModule");
const terserForTesting = new TerserPlugin({
2020-10-15 15:18:42 +08:00
parallel: false
});
const DEFAULT_OPTIMIZATIONS = {
removeAvailableModules: true,
removeEmptyChunks: true,
mergeDuplicateChunks: true,
flagIncludedChunks: true,
sideEffects: true,
providedExports: true,
usedExports: true,
2019-01-28 17:40:32 +08:00
mangleExports: true,
emitOnErrors: true,
concatenateModules: false,
moduleIds: "size",
chunkIds: "size",
minimizer: [terserForTesting]
};
const NO_EMIT_ON_ERRORS_OPTIMIZATIONS = {
emitOnErrors: true,
minimizer: [terserForTesting]
};
const casesPath = path.join(__dirname, "cases");
let categories = fs.readdirSync(casesPath);
2018-02-25 18:46:17 +08:00
categories = categories.map(cat => {
return {
name: cat,
2018-02-25 18:46:17 +08:00
tests: fs
.readdirSync(path.join(casesPath, cat))
.filter(folder => folder.indexOf("_") < 0)
};
});
2018-02-25 18:46:17 +08:00
const describeCases = config => {
describe(config.name, () => {
let stderr;
beforeEach(() => {
stderr = captureStdio(process.stderr, true);
});
afterEach(() => {
stderr.restore();
});
2018-02-25 18:46:17 +08:00
categories.forEach(category => {
2020-03-29 06:10:15 +08:00
describe(category.name, function () {
jest.setTimeout(20000);
2018-02-25 18:46:17 +08:00
category.tests
.filter(test => {
const testDirectory = path.join(casesPath, category.name, test);
const filterPath = path.join(testDirectory, "test.filter.js");
if (fs.existsSync(filterPath) && !require(filterPath)(config)) {
describe.skip(test, () => {
it("filtered", () => {});
});
2018-02-25 18:46:17 +08:00
return false;
}
return true;
})
.forEach(testName => {
describe(testName, () => {
const testDirectory = path.join(
casesPath,
category.name,
testName
);
const outputDirectory = path.join(
__dirname,
"js",
config.name,
category.name,
testName
);
2018-10-18 04:15:46 +08:00
const cacheDirectory = path.join(
__dirname,
"js/.cache",
config.name,
category.name,
testName
);
2021-06-29 18:06:45 +08:00
let testConfig = {};
const testConfigPath = path.join(testDirectory, "test.config.js");
if (fs.existsSync(testConfigPath)) {
testConfig = require(testConfigPath);
}
2018-02-25 18:46:17 +08:00
const options = {
context: casesPath,
entry: "./" + category.name + "/" + testName + "/",
2021-06-24 16:05:37 +08:00
target: config.target || "async-node",
2018-02-25 18:46:17 +08:00
devtool: config.devtool,
mode: config.mode || "none",
optimization: config.mode
? {
...NO_EMIT_ON_ERRORS_OPTIMIZATIONS,
...config.optimization
}
: {
...DEFAULT_OPTIMIZATIONS,
...config.optimization
},
2018-02-25 18:46:17 +08:00
performance: {
hints: false
},
2019-05-22 21:54:44 +08:00
node: {
__dirname: "mock",
__filename: "mock"
},
cache: config.cache && {
cacheDirectory,
...config.cache
},
2018-02-25 18:46:17 +08:00
output: {
pathinfo: "verbose",
2018-02-25 18:46:17 +08:00
path: outputDirectory,
filename: config.module ? "bundle.mjs" : "bundle.js"
2018-02-25 18:46:17 +08:00
},
resolve: {
modules: ["web_modules", "node_modules"],
mainFields: [
"webpack",
"browser",
"web",
"browserify",
["jam", "main"],
"main"
],
aliasFields: ["browser"],
extensions: [".webpack.js", ".web.js", ".js", ".json"]
2018-02-25 18:46:17 +08:00
},
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"
},
{
2018-05-10 15:13:17 +08:00
test: /\.pug/,
loader: "pug-loader"
2018-05-24 20:12:11 +08:00
},
{
test: /\.wat$/i,
loader: "wast-loader",
type: "webassembly/async"
2018-02-25 18:46:17 +08:00
}
]
},
2020-03-29 06:10:15 +08:00
plugins: (config.plugins || []).concat(function () {
2018-02-25 18:46:17 +08:00
this.hooks.compilation.tap("TestCasesTest", compilation => {
[
"optimize",
"optimizeModules",
"optimizeChunks",
2018-02-25 18:46:17 +08:00
"afterOptimizeTree",
"afterOptimizeAssets"
].forEach(hook => {
compilation.hooks[hook].tap("TestCasesTest", () =>
compilation.checkConstraints()
);
});
});
2019-06-04 17:26:04 +08:00
}),
experiments: {
asyncWebAssembly: true,
topLevelAwait: true,
...(config.module ? { outputModule: true } : {})
2019-06-04 17:26:04 +08:00
}
};
2018-10-18 04:15:46 +08:00
beforeAll(done => {
rimraf(cacheDirectory, done);
});
if (config.cache) {
2021-06-29 18:06:45 +08:00
it(
`${testName} should pre-compile to fill disk cache (1st)`,
done => {
const oldPath = options.output.path;
options.output.path = path.join(
options.output.path,
"cache1"
);
const deprecationTracker = deprecationTracking.start();
webpack(options, err => {
deprecationTracker();
options.output.path = oldPath;
if (err) return done(err);
done();
});
},
testConfig.timeout || 60000
);
it(
`${testName} should pre-compile to fill disk cache (2nd)`,
done => {
const oldPath = options.output.path;
options.output.path = path.join(
options.output.path,
"cache2"
);
const deprecationTracker = deprecationTracking.start();
webpack(options, err => {
deprecationTracker();
options.output.path = oldPath;
if (err) return done(err);
done();
});
},
testConfig.cachedTimeout || testConfig.timeout || 10000
);
}
2018-03-06 05:48:18 +08:00
it(
testName + " should compile",
done => {
const compiler = webpack(options);
const run = () => {
const deprecationTracker = deprecationTracking.start();
compiler.run((err, stats) => {
const deprecations = deprecationTracker();
if (err) return done(err);
compiler.close(err => {
if (err) return done(err);
const statOptions = {
preset: "verbose",
colors: false,
modules: true
2018-03-06 05:48:18 +08:00
};
fs.mkdirSync(outputDirectory, { recursive: true });
fs.writeFileSync(
path.join(outputDirectory, "stats.txt"),
stats.toString(statOptions),
"utf-8"
2018-03-06 05:48:18 +08:00
);
const jsonStats = stats.toJson({
2021-05-20 19:06:22 +08:00
errorDetails: true,
modules: false,
assets: false,
chunks: false
});
if (
checkArrayExpectation(
testDirectory,
jsonStats,
"error",
"Error",
done
)
) {
return;
}
if (
checkArrayExpectation(
testDirectory,
jsonStats,
"warning",
"Warning",
done
)
) {
return;
}
const infrastructureLogging = stderr.toString();
if (infrastructureLogging) {
done(
new Error(
"Errors/Warnings during build:\n" +
infrastructureLogging
)
);
}
expect(deprecations).toEqual(config.deprecations || []);
Promise.resolve().then(done);
});
});
};
if (config.cache) {
// pre-compile to fill memory cache
const deprecationTracker = deprecationTracking.start();
compiler.run(err => {
deprecationTracker();
if (err) return done(err);
run();
});
} else {
run();
}
2018-03-06 05:48:18 +08:00
},
2021-06-29 18:06:45 +08:00
testConfig.cachedTimeout ||
testConfig.timeout ||
(config.cache ? 20000 : 60000)
2018-02-25 18:46:17 +08:00
);
it(
testName + " should load the compiled tests",
done => {
2021-06-24 16:05:37 +08:00
const esmContext = vm.createContext({
it: _it,
expect,
process,
global,
URL,
Buffer,
setTimeout,
setImmediate,
nsObj: function (m) {
Object.defineProperty(m, Symbol.toStringTag, {
value: "Module"
});
return m;
}
});
function _require(module, esmMode) {
if (module.substr(0, 2) === "./") {
const p = path.join(outputDirectory, module);
2021-06-24 16:05:37 +08:00
const content = fs.readFileSync(p, "utf-8");
if (p.endsWith(".mjs")) {
2021-06-24 16:05:37 +08:00
let esm;
try {
esm = new vm.SourceTextModule(content, {
identifier: p,
2021-06-24 16:05:37 +08:00
context: esmContext,
initializeImportMeta: (meta, module) => {
2021-06-24 16:05:37 +08:00
meta.url = pathToFileURL(p).href;
},
2021-06-24 16:05:37 +08:00
importModuleDynamically: async (
specifier,
module
) => {
const result = await _require(
specifier,
"evaluated"
);
return await asModule(result, module.context);
}
2021-06-24 16:05:37 +08:00
});
} catch (e) {
console.log(e);
e.message += `\nwhile parsing ${p}`;
throw e;
}
if (esmMode === "unlinked") return esm;
return (async () => {
await esm.link(async (specifier, module) => {
return await asModule(
await _require(specifier, "unlinked"),
module.context,
true
);
2021-06-24 16:05:37 +08:00
});
// node.js 10 needs instantiate
if (esm.instantiate) esm.instantiate();
await esm.evaluate();
if (esmMode === "evaluated") return esm;
const ns = esm.namespace;
return ns.default && ns.default instanceof Promise
? ns.default
: ns;
})();
} else {
const fn = vm.runInThisContext(
"(function(require, module, exports, __dirname, __filename, it, expect) {" +
"global.expect = expect;" +
'function nsObj(m) { Object.defineProperty(m, Symbol.toStringTag, { value: "Module" }); return m; }' +
2021-06-24 16:05:37 +08:00
content +
"\n})",
p
);
const m = {
exports: {},
webpackTestSuiteModule: true
};
fn.call(
m.exports,
_require,
m,
m.exports,
outputDirectory,
p,
_it,
expect
);
return m.exports;
}
} else return require(module);
}
_require.webpackTestSuiteRequire = true;
2021-06-24 16:05:37 +08:00
Promise.resolve()
.then(() => _require("./" + options.output.filename))
.then(() => {
if (getNumberOfTests() === 0)
return done(
new Error("No tests exported by test case")
);
done();
}, done);
},
10000
);
const { it: _it, getNumberOfTests } = createLazyTestEnv(
jasmine.getEnv(),
2021-06-29 18:06:45 +08:00
testConfig.timeout || 10000
);
2018-02-25 18:46:17 +08:00
});
});
});
});
});
};
exports.describeCases = describeCases;