split TestCases into mulitple files for better batching in jest

This commit is contained in:
Bazyli Brzóska 2018-02-25 01:16:11 +01:00
parent 14d6497337
commit 47a16ebea6
20 changed files with 369 additions and 238 deletions

View File

@ -1,12 +1,12 @@
"use strict";
/* globals describe expect it fit */
/* globals describe expect it beforeAll */
const path = require("path");
const fs = require("fs");
const vm = require("vm");
const mkdirp = require("mkdirp");
const checkArrayExpectation = require("./checkArrayExpectation");
const async = require("async");
// const async = require("async");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
@ -36,10 +36,15 @@ describe("ConfigTestCases", () => {
describe(category.name, () => {
category.tests.forEach((testName) => {
describe(testName, () => {
it(testName + " should compile", (done) => {
const testDirectory = path.join(casesPath, category.name, testName);
const outputDirectory = path.join(__dirname, "js", "config", category.name, testName);
const options = prepareOptions(require(path.join(testDirectory, "webpack.config.js")));
const testDirectory = path.join(casesPath, category.name, testName);
const outputDirectory = path.join(__dirname, "js", "config", category.name, testName);
const exportedTests = [];
beforeAll(() => new Promise((resolve, reject) => {
const done = (err) => {
if(err) return reject(err);
resolve();
};
const options = prepareOptions(require(path.join(testDirectory, "webpack.config.js")), { testPath: outputDirectory });
const optionsArr = [].concat(options);
optionsArr.forEach((options, idx) => {
if(!options.context) options.context = testDirectory;
@ -72,7 +77,7 @@ describe("ConfigTestCases", () => {
errors: [err.stack]
};
if(checkArrayExpectation(testDirectory, fakeStats, "error", "Error", done)) return;
// Wait for uncatched errors to occur
// Wait for uncaught errors to occur
return setTimeout(done, 200);
}
const statOptions = Stats.presetToOptions("verbose");
@ -84,10 +89,9 @@ describe("ConfigTestCases", () => {
});
if(checkArrayExpectation(testDirectory, jsonStats, "error", "Error", done)) return;
if(checkArrayExpectation(testDirectory, jsonStats, "warning", "Warning", done)) return;
let exportedTests = [];
function _it(title, fn) {
exportedTests.push(fit(title, fn, testConfig.timeout));
exportedTests.push({ title, fn, timeout: testConfig.timeout });
}
const globalContext = {
@ -137,12 +141,11 @@ describe("ConfigTestCases", () => {
// give a free pass to compilation that generated an error
if(!jsonStats.errors.length && filesCount !== optionsArr.length) return done(new Error("Should have found at least one bundle file per webpack config"));
if(exportedTests.length < filesCount) return done(new Error("No tests exported by test case"));
async.waterfall(
exportedTests.map(test => (callback) => test.execute(callback, true)),
done
);
done();
});
});
}));
it(testName + " should compile", () => {});
exportedTests.forEach(({ title, fn, timeout }) => it(title, fn, timeout));
});
});
});

166
test/TestCases.template.js Normal file
View File

@ -0,0 +1,166 @@
/* global describe it beforeAll expect */
"use strict";
const path = require("path");
const fs = require("fs");
const vm = require("vm");
const mkdirp = require("mkdirp");
const checkArrayExpectation = require("./checkArrayExpectation");
// const async = require("async");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
const DEFAULT_OPTIMIZATIONS = {
removeAvailableModules: true,
removeEmptyChunks: true,
mergedDuplicateChunks: true,
flagIncludedChunks: true,
occurrenceOrder: true,
sideEffects: true,
providedExports: true,
usedExports: true,
noEmitOnErrors: false,
concatenateModules: false,
namedModules: false,
};
const NO_EMIT_ON_ERRORS_OPTIMIZATIONS = {
noEmitOnErrors: false
};
const casesPath = path.join(__dirname, "cases");
let categories = fs.readdirSync(casesPath);
categories = categories.map((cat) => {
return {
name: cat,
tests: fs.readdirSync(path.join(casesPath, cat)).filter((folder) => folder.indexOf("_") < 0)
};
});
const describeCases = (config) => {
describe(config.name, () => {
categories.forEach((category) => {
describe(category.name, function() {
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"));
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);
const options = {
context: casesPath,
entry: "./" + category.name + "/" + testName + "/index",
target: "async-node",
devtool: config.devtool,
mode: config.mode || "none",
optimization: config.mode ? NO_EMIT_ON_ERRORS_OPTIMIZATIONS : Object.assign({}, config.optimization, DEFAULT_OPTIMIZATIONS),
performance: {
hints: false
},
output: {
pathinfo: true,
path: outputDirectory,
filename: "bundle.js"
},
resolve: {
modules: ["web_modules", "node_modules"],
mainFields: ["webpack", "browser", "web", "browserify", ["jam", "main"], "main"],
aliasFields: ["browser"],
extensions: [".mjs", ".webpack.js", ".web.js", ".js", ".json"],
concord: true
},
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: /\.jade$/,
loader: "jade-loader"
}]
},
plugins: (config.plugins || []).concat(function() {
this.hooks.compilation.tap("TestCasesTest", (compilation) => {
["optimize", "optimizeModulesBasic", "optimizeChunksBasic", "afterOptimizeTree", "afterOptimizeAssets"].forEach((hook) => {
compilation.hooks[hook].tap("TestCasesTest", () => compilation.checkConstraints());
});
});
})
};
// it(testName + " should compile",
let exportedTests = [];
beforeAll(() => new Promise((resolve, reject) => {
const done = (err) => {
if(err) return reject(err);
resolve();
};
// console.log("starting compiling", category.name, "/", config.name, "/", testName);
webpack(options, (err, stats) => {
if(err) throw err;
// console.log("compiled case:", category.name, "/", config.name, "/", testName);
const statOptions = Stats.presetToOptions("verbose");
statOptions.colors = false;
mkdirp.sync(outputDirectory);
fs.writeFileSync(path.join(outputDirectory, "stats.txt"), stats.toString(statOptions), "utf-8");
const jsonStats = stats.toJson({
errorDetails: true
});
if(checkArrayExpectation(testDirectory, jsonStats, "error", "Error", done)) return;
if(checkArrayExpectation(testDirectory, jsonStats, "warning", "Warning", done)) return;
function _it(title, fn) {
exportedTests.push({ title, fn, timeout: 5000 });
// TODO: is this necessary in 'jest'?
// WORKAROUND for a v8 bug
// Error objects retrain all scopes in the stacktrace
// test._trace = test._trace.message;
}
function _require(module) {
if(module.substr(0, 2) === "./") {
const p = path.join(outputDirectory, module);
const fn = vm.runInThisContext("(function(require, module, exports, __dirname, it, expect) {" + fs.readFileSync(p, "utf-8") + "\n})", p);
const m = {
exports: {},
webpackTestSuiteModule: true
};
fn.call(m.exports, _require, m, m.exports, outputDirectory, _it, expect);
return m.exports;
} else return require(module);
}
_require.webpackTestSuiteRequire = true;
_require("./bundle.js");
if(exportedTests.length === 0) throw new Error("No tests exported by test case");
done();
// async.waterfall(
// exportedTests.map(({ title, name, fn }) => (callback) => {
// console.log("Starting:", name);
// console.log(fn.toString());
// // test.execute(callback, true);
// }),
// done
// );
});
}), 30000);
it(testName + " should compile", () => {});
exportedTests.forEach(({ title, fn, timeout }) => it(title, fn, timeout));
});
});
});
});
});
};
module.exports.describeCases = describeCases;

View File

@ -1,224 +0,0 @@
/* global describe it fit expect */
"use strict";
const path = require("path");
const fs = require("fs");
const vm = require("vm");
const mkdirp = require("mkdirp");
const checkArrayExpectation = require("./checkArrayExpectation");
const async = require("async");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
const DEFAULT_OPTIMIZATIONS = {
removeAvailableModules: true,
removeEmptyChunks: true,
mergedDuplicateChunks: true,
flagIncludedChunks: true,
occurrenceOrder: true,
sideEffects: true,
providedExports: true,
usedExports: true,
noEmitOnErrors: false,
concatenateModules: false,
namedModules: false,
};
const NO_EMIT_ON_ERRORS_OPTIMIZATIONS = {
noEmitOnErrors: false
};
describe("TestCases", () => {
const casesPath = path.join(__dirname, "cases");
let categories = fs.readdirSync(casesPath);
categories = categories.map((cat) => {
return {
name: cat,
tests: fs.readdirSync(path.join(casesPath, cat)).filter((folder) => folder.indexOf("_") < 0)
};
});
[{
name: "normal",
}, {
name: "production",
mode: "production",
}, {
name: "development",
mode: "development",
devtool: "none"
}, {
name: "hot",
plugins: [
new webpack.HotModuleReplacementPlugin()
]
}, {
name: "hot-multi-step",
plugins: [
new webpack.HotModuleReplacementPlugin({
multiStep: true
})
]
}, {
name: "devtool-eval",
devtool: "eval"
}, {
name: "devtool-eval-named-modules",
devtool: "eval",
plugins: [
new webpack.NamedModulesPlugin()
]
}, {
name: "devtool-eval-source-map",
devtool: "#eval-source-map"
}, {
name: "devtool-inline-source-map",
devtool: "inline-source-map"
}, {
name: "devtool-source-map",
devtool: "#@source-map"
}, {
name: "devtool-cheap-inline-source-map",
devtool: "cheap-inline-source-map"
}, {
name: "devtool-cheap-eval-source-map",
devtool: "cheap-eval-source-map"
}, {
name: "devtool-cheap-eval-module-source-map",
devtool: "cheap-eval-module-source-map"
}, {
name: "devtool-cheap-source-map",
devtool: "cheap-source-map"
}, {
name: "minimized-source-map",
mode: "production",
devtool: "eval-cheap-module-source-map",
minimize: true
}, {
name: "minimized-hashed-modules",
mode: "production",
minimize: true,
plugins: [
new webpack.HashedModuleIdsPlugin()
]
}, {
name: "all-combined",
mode: "production",
devtool: "#@source-map",
minimize: true,
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
new webpack.NamedChunksPlugin()
]
}].forEach((config) => {
describe(config.name, () => {
categories.forEach((category) => {
describe(category.name, function() {
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"));
return false;
}
return true;
}).forEach((testName) => {
describe(testName, () => {
it(testName + " should compile", (done) => {
const testDirectory = path.join(casesPath, category.name, testName);
const outputDirectory = path.join(__dirname, "js", config.name, category.name, testName);
const options = {
context: casesPath,
entry: "./" + category.name + "/" + testName + "/index",
target: "async-node",
devtool: config.devtool,
mode: config.mode || "none",
optimization: config.mode ? NO_EMIT_ON_ERRORS_OPTIMIZATIONS : Object.assign({}, config.optimization, DEFAULT_OPTIMIZATIONS),
performance: {
hints: false
},
output: {
pathinfo: true,
path: outputDirectory,
filename: "bundle.js"
},
resolve: {
modules: ["web_modules", "node_modules"],
mainFields: ["webpack", "browser", "web", "browserify", ["jam", "main"], "main"],
aliasFields: ["browser"],
extensions: [".mjs", ".webpack.js", ".web.js", ".js", ".json"],
concord: true
},
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: /\.jade$/,
loader: "jade-loader"
}]
},
plugins: (config.plugins || []).concat(function() {
this.hooks.compilation.tap("TestCasesTest", (compilation) => {
["optimize", "optimizeModulesBasic", "optimizeChunksBasic", "afterOptimizeTree", "afterOptimizeAssets"].forEach((hook) => {
compilation.hooks[hook].tap("TestCasesTest", () => compilation.checkConstraints());
});
});
})
};
webpack(options, (err, stats) => {
if(err) return done(err);
console.log("compiled case:", category.name, "/", config.name, "/", testName);
const statOptions = Stats.presetToOptions("verbose");
statOptions.colors = false;
mkdirp.sync(outputDirectory);
fs.writeFileSync(path.join(outputDirectory, "stats.txt"), stats.toString(statOptions), "utf-8");
const jsonStats = stats.toJson({
errorDetails: true
});
if(checkArrayExpectation(testDirectory, jsonStats, "error", "Error", done)) return;
if(checkArrayExpectation(testDirectory, jsonStats, "warning", "Warning", done)) return;
let exportedTests = [];
function _it(title, fn) {
exportedTests.push(fit(title, fn));
// TODO: is this necessary in 'jest'?
// WORKAROUND for a v8 bug
// Error objects retrain all scopes in the stacktrace
// test._trace = test._trace.message;
}
function _require(module) {
if(module.substr(0, 2) === "./") {
const p = path.join(outputDirectory, module);
const fn = vm.runInThisContext("(function(require, module, exports, __dirname, it, expect) {" + fs.readFileSync(p, "utf-8") + "\n})", p);
const m = {
exports: {},
webpackTestSuiteModule: true
};
fn.call(m.exports, _require, m, m.exports, outputDirectory, _it, expect);
return m.exports;
} else return require(module);
}
_require.webpackTestSuiteRequire = true;
_require("./bundle.js");
if(exportedTests.length === 0) return done(new Error("No tests exported by test case"));
async.waterfall(
exportedTests.map(test => (callback) => test.execute(callback, true)),
done
);
});
}, 30000);
});
});
});
});
});
});
});

View File

@ -0,0 +1,17 @@
const { describeCases } = require("./TestCases.template");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
describe("TestCases", () => {
describeCases({
name: "all-combined",
mode: "production",
devtool: "#@source-map",
minimize: true,
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
new webpack.NamedChunksPlugin()
]
});
});

View File

@ -0,0 +1,9 @@
const { describeCases } = require("./TestCases.template");
describe("TestCases", () => {
describeCases({
name: "development",
mode: "development",
devtool: "none"
});
});

View File

@ -0,0 +1,10 @@
const { describeCases } = require("./TestCases.template");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
describe("TestCases", () => {
describeCases({
name: "devtool-cheap-eval-module-source-map",
devtool: "cheap-eval-module-source-map"
});
});

View File

@ -0,0 +1,10 @@
const { describeCases } = require("./TestCases.template");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
describe("TestCases", () => {
describeCases({
name: "devtool-cheap-eval-source-map",
devtool: "cheap-eval-source-map"
});
});

View File

@ -0,0 +1,10 @@
const { describeCases } = require("./TestCases.template");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
describe("TestCases", () => {
describeCases({
name: "devtool-cheap-inline-source-map",
devtool: "cheap-inline-source-map"
});
});

View File

@ -0,0 +1,10 @@
const { describeCases } = require("./TestCases.template");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
describe("TestCases", () => {
describeCases({
name: "devtool-cheap-source-map",
devtool: "cheap-source-map"
});
});

View File

@ -0,0 +1,10 @@
const { describeCases } = require("./TestCases.template");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
describe("TestCases", () => {
describeCases({
name: "devtool-eval",
devtool: "eval"
});
});

View File

@ -0,0 +1,13 @@
const { describeCases } = require("./TestCases.template");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
describe("TestCases", () => {
describeCases({
name: "devtool-eval-named-modules",
devtool: "eval",
plugins: [
new webpack.NamedModulesPlugin()
]
});
});

View File

@ -0,0 +1,10 @@
const { describeCases } = require("./TestCases.template");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
describe("TestCases", () => {
describeCases({
name: "devtool-eval-source-map",
devtool: "#eval-source-map"
});
});

View File

@ -0,0 +1,10 @@
const { describeCases } = require("./TestCases.template");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
describe("TestCases", () => {
describeCases({
name: "devtool-inline-source-map",
devtool: "inline-source-map"
});
});

View File

@ -0,0 +1,10 @@
const { describeCases } = require("./TestCases.template");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
describe("TestCases", () => {
describeCases({
name: "devtool-source-map",
devtool: "#@source-map"
});
});

12
test/TestCasesHot.test.js Normal file
View File

@ -0,0 +1,12 @@
const { describeCases } = require("./TestCases.template");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
describe("TestCases", () => {
describeCases({
name: "hot",
plugins: [
new webpack.HotModuleReplacementPlugin()
]
});
});

View File

@ -0,0 +1,14 @@
const { describeCases } = require("./TestCases.template");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
describe("TestCases", () => {
describeCases({
name: "hot-multi-step",
plugins: [
new webpack.HotModuleReplacementPlugin({
multiStep: true
})
]
});
});

View File

@ -0,0 +1,14 @@
const { describeCases } = require("./TestCases.template");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
describe("TestCases", () => {
describeCases({
name: "minimized-hashed-modules",
mode: "production",
minimize: true,
plugins: [
new webpack.HashedModuleIdsPlugin()
]
});
});

View File

@ -0,0 +1,12 @@
const { describeCases } = require("./TestCases.template");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
describe("TestCases", () => {
describeCases({
name: "minimized-source-map",
mode: "production",
devtool: "eval-cheap-module-source-map",
minimize: true
});
});

View File

@ -0,0 +1,7 @@
const { describeCases } = require("./TestCases.template");
describe("TestCases", () => {
describeCases({
name: "normal",
});
});

View File

@ -0,0 +1,8 @@
const { describeCases } = require("./TestCases.template");
describe("TestCases", () => {
describeCases({
name: "production",
mode: "production",
});
});