mirror of https://github.com/webpack/webpack.git
chore: refactor test (#19608)
Github Actions / lint (push) Has been cancelled
Details
Github Actions / validate-legacy-node (push) Has been cancelled
Details
Github Actions / benchmark (1/4) (push) Has been cancelled
Details
Github Actions / benchmark (2/4) (push) Has been cancelled
Details
Github Actions / benchmark (3/4) (push) Has been cancelled
Details
Github Actions / benchmark (4/4) (push) Has been cancelled
Details
Github Actions / basic (push) Has been cancelled
Details
Github Actions / unit (push) Has been cancelled
Details
Github Actions / integration (10.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (10.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (10.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (10.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (10.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (10.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (12.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (14.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (16.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (18.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (20.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (20.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (22.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (22.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (22.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (22.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (22.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (22.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (24.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (24.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (24.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (24.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (24.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (24.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (lts/*, ubuntu-latest, a, 1) (push) Has been cancelled
Details
Github Actions / integration (lts/*, ubuntu-latest, b, 1) (push) Has been cancelled
Details
Github Actions / lint (push) Has been cancelled
Details
Github Actions / validate-legacy-node (push) Has been cancelled
Details
Github Actions / benchmark (1/4) (push) Has been cancelled
Details
Github Actions / benchmark (2/4) (push) Has been cancelled
Details
Github Actions / benchmark (3/4) (push) Has been cancelled
Details
Github Actions / benchmark (4/4) (push) Has been cancelled
Details
Github Actions / basic (push) Has been cancelled
Details
Github Actions / unit (push) Has been cancelled
Details
Github Actions / integration (10.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (10.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (10.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (10.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (10.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (10.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (12.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (14.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (16.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (18.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (20.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (20.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (20.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (22.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (22.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (22.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (22.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (22.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (22.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (24.x, macos-latest, a) (push) Has been cancelled
Details
Github Actions / integration (24.x, macos-latest, b) (push) Has been cancelled
Details
Github Actions / integration (24.x, ubuntu-latest, a) (push) Has been cancelled
Details
Github Actions / integration (24.x, ubuntu-latest, b) (push) Has been cancelled
Details
Github Actions / integration (24.x, windows-latest, a) (push) Has been cancelled
Details
Github Actions / integration (24.x, windows-latest, b) (push) Has been cancelled
Details
Github Actions / integration (lts/*, ubuntu-latest, a, 1) (push) Has been cancelled
Details
Github Actions / integration (lts/*, ubuntu-latest, b, 1) (push) Has been cancelled
Details
This commit is contained in:
parent
0af28df145
commit
a55ac0ed6d
|
@ -16,6 +16,7 @@ test/**/*.*
|
|||
!test/**/infrastructure-log.js
|
||||
!test/*.md
|
||||
!test/helpers/*.*
|
||||
!test/runner/**/*.*
|
||||
!test/benchmarkCases/**/*.mjs
|
||||
test/js/**/*.*
|
||||
|
||||
|
|
|
@ -162,7 +162,7 @@
|
|||
"fmt": "yarn fmt:base --log-level warn --write",
|
||||
"fmt:check": "yarn fmt:base --check",
|
||||
"fmt:base": "node node_modules/prettier/bin/prettier.cjs --cache --ignore-unknown .",
|
||||
"fix": "yarn fix:code && fix:yarn && fix:special && yarn fmt",
|
||||
"fix": "yarn fix:code && yarn fix:yarn && yarn fix:special && yarn fmt",
|
||||
"fix:code": "yarn lint:code --fix",
|
||||
"fix:yarn": "yarn-deduplicate -s highest yarn.lock",
|
||||
"fix:special": "node node_modules/tooling/inherit-types --write && node node_modules/tooling/format-schemas --write && node tooling/generate-runtime-code.js --write && node tooling/generate-wasm-code.js --write && node node_modules/tooling/format-file-header --write && node node_modules/tooling/compile-to-definitions --write && node node_modules/tooling/precompile-schemas --write && node node_modules/tooling/generate-types --no-template-literals --write",
|
||||
|
|
|
@ -7,21 +7,15 @@ require("./helpers/warmup-webpack");
|
|||
|
||||
const path = require("path");
|
||||
const fs = require("graceful-fs");
|
||||
const vm = require("vm");
|
||||
const url = require("url");
|
||||
const { URL, pathToFileURL, fileURLToPath } = require("url");
|
||||
const rimraf = require("rimraf");
|
||||
const checkArrayExpectation = require("./checkArrayExpectation");
|
||||
const createLazyTestEnv = require("./helpers/createLazyTestEnv");
|
||||
const deprecationTracking = require("./helpers/deprecationTracking");
|
||||
const FakeDocument = require("./helpers/FakeDocument");
|
||||
const CurrentScript = require("./helpers/CurrentScript");
|
||||
|
||||
const prepareOptions = require("./helpers/prepareOptions");
|
||||
const { parseResource } = require("../lib/util/identifier");
|
||||
const captureStdio = require("./helpers/captureStdio");
|
||||
const asModule = require("./helpers/asModule");
|
||||
const filterInfraStructureErrors = require("./helpers/infrastructureLogErrors");
|
||||
const { TestRunner } = require("./runner");
|
||||
|
||||
const casesPath = path.join(__dirname, "configCases");
|
||||
const categories = fs.readdirSync(casesPath).map(cat => ({
|
||||
|
@ -420,290 +414,35 @@ const describeCases = config => {
|
|||
const bundlePath = testConfig.findBundle(i, optionsArr[i]);
|
||||
if (bundlePath) {
|
||||
filesCount++;
|
||||
const document = new FakeDocument(outputDirectory);
|
||||
const globalContext = {
|
||||
console,
|
||||
expect,
|
||||
setTimeout,
|
||||
clearTimeout,
|
||||
document,
|
||||
getComputedStyle:
|
||||
document.getComputedStyle.bind(document),
|
||||
location: {
|
||||
href: "https://test.cases/path/index.html",
|
||||
origin: "https://test.cases",
|
||||
toString() {
|
||||
return "https://test.cases/path/index.html";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const requireCache = Object.create(null);
|
||||
const esmCache = new Map();
|
||||
const esmIdentifier = `${category.name}-${testName}-${i}`;
|
||||
const baseModuleScope = {
|
||||
console,
|
||||
const runner = new TestRunner({
|
||||
target: options.target,
|
||||
outputDirectory,
|
||||
testMeta: {
|
||||
category: category.name,
|
||||
name: testName,
|
||||
round: i
|
||||
},
|
||||
testConfig,
|
||||
webpackOptions: options
|
||||
});
|
||||
runner.mergeModuleScope({
|
||||
it: _it,
|
||||
beforeEach: _beforeEach,
|
||||
afterEach: _afterEach,
|
||||
expect,
|
||||
jest,
|
||||
__STATS__: jsonStats,
|
||||
__STATS_I__: i,
|
||||
nsObj: m => {
|
||||
Object.defineProperty(m, Symbol.toStringTag, {
|
||||
value: "Module"
|
||||
});
|
||||
return m;
|
||||
}
|
||||
};
|
||||
|
||||
let runInNewContext = false;
|
||||
if (
|
||||
options.target === "web" ||
|
||||
options.target === "webworker" ||
|
||||
(Array.isArray(options.target) &&
|
||||
(options.target.includes("web") ||
|
||||
options.target.includes("webworker")))
|
||||
) {
|
||||
baseModuleScope.window = globalContext;
|
||||
baseModuleScope.self = globalContext;
|
||||
baseModuleScope.document = globalContext.document;
|
||||
baseModuleScope.setTimeout = globalContext.setTimeout;
|
||||
baseModuleScope.clearTimeout = globalContext.clearTimeout;
|
||||
baseModuleScope.getComputedStyle =
|
||||
globalContext.getComputedStyle;
|
||||
baseModuleScope.URL = URL;
|
||||
if (typeof Blob !== "undefined") {
|
||||
baseModuleScope.Blob = Blob;
|
||||
}
|
||||
baseModuleScope.Worker =
|
||||
require("./helpers/createFakeWorker")({
|
||||
outputDirectory
|
||||
});
|
||||
runInNewContext = true;
|
||||
}
|
||||
if (testConfig.moduleScope) {
|
||||
testConfig.moduleScope(baseModuleScope, options);
|
||||
}
|
||||
const esmContext = vm.createContext(baseModuleScope, {
|
||||
name: "context for esm"
|
||||
__STATS_I__: i
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-loop-func
|
||||
const _require = (
|
||||
currentDirectory,
|
||||
options,
|
||||
module,
|
||||
esmMode,
|
||||
parentModule
|
||||
) => {
|
||||
if (testConfig.resolveModule) {
|
||||
module = testConfig.resolveModule(module, i, options);
|
||||
}
|
||||
if (testConfig === undefined) {
|
||||
throw new Error(
|
||||
`_require(${module}) called after all tests from ${category.name} ${testName} have completed`
|
||||
);
|
||||
}
|
||||
|
||||
if (Array.isArray(module) || /^\.\.?\//.test(module)) {
|
||||
let content;
|
||||
let p;
|
||||
let subPath = "";
|
||||
if (Array.isArray(module)) {
|
||||
p = path.join(currentDirectory, ".array-require.js");
|
||||
content = `module.exports = (${module
|
||||
.map(
|
||||
arg => `require(${JSON.stringify(`./${arg}`)})`
|
||||
)
|
||||
.join(", ")});`;
|
||||
} else {
|
||||
p = path.join(currentDirectory, module);
|
||||
content = fs.readFileSync(p, "utf-8");
|
||||
const lastSlash = module.lastIndexOf("/");
|
||||
let firstSlash = module.indexOf("/");
|
||||
|
||||
if (lastSlash !== -1 && firstSlash !== lastSlash) {
|
||||
if (firstSlash !== -1) {
|
||||
let next = module.indexOf("/", firstSlash + 1);
|
||||
let dir = module.slice(firstSlash + 1, next);
|
||||
|
||||
while (dir === ".") {
|
||||
firstSlash = next;
|
||||
next = module.indexOf("/", firstSlash + 1);
|
||||
dir = module.slice(firstSlash + 1, next);
|
||||
}
|
||||
}
|
||||
|
||||
subPath = module.slice(
|
||||
firstSlash + 1,
|
||||
lastSlash + 1
|
||||
);
|
||||
}
|
||||
}
|
||||
const isModule =
|
||||
p.endsWith(".mjs") &&
|
||||
options.experiments &&
|
||||
options.experiments.outputModule;
|
||||
|
||||
if (isModule) {
|
||||
if (!vm.SourceTextModule)
|
||||
throw new Error(
|
||||
"Running this test requires '--experimental-vm-modules'.\nRun with 'node --experimental-vm-modules node_modules/jest-cli/bin/jest'."
|
||||
);
|
||||
let esm = esmCache.get(p);
|
||||
if (!esm) {
|
||||
esm = new vm.SourceTextModule(content, {
|
||||
identifier: `${esmIdentifier}-${p}`,
|
||||
url: `${pathToFileURL(p).href}?${esmIdentifier}`,
|
||||
context: esmContext,
|
||||
initializeImportMeta: (meta, module) => {
|
||||
meta.url = pathToFileURL(p).href;
|
||||
},
|
||||
importModuleDynamically: async (
|
||||
specifier,
|
||||
module
|
||||
) => {
|
||||
const normalizedSpecifier =
|
||||
specifier.startsWith("file:")
|
||||
? `./${path.relative(
|
||||
path.dirname(p),
|
||||
url.fileURLToPath(specifier)
|
||||
)}`
|
||||
: specifier.replace(
|
||||
/https:\/\/example.com\/public\/path\//,
|
||||
"./"
|
||||
);
|
||||
const result = await _require(
|
||||
path.dirname(p),
|
||||
options,
|
||||
normalizedSpecifier,
|
||||
"evaluated",
|
||||
module
|
||||
);
|
||||
return await asModule(result, module.context);
|
||||
}
|
||||
});
|
||||
esmCache.set(p, esm);
|
||||
}
|
||||
if (esmMode === "unlinked") return esm;
|
||||
return (async () => {
|
||||
if (esmMode === "unlinked") return esm;
|
||||
await esm.link(
|
||||
async (specifier, referencingModule) =>
|
||||
await asModule(
|
||||
await _require(
|
||||
path.dirname(
|
||||
referencingModule.identifier
|
||||
? referencingModule.identifier.slice(
|
||||
esmIdentifier.length + 1
|
||||
)
|
||||
: fileURLToPath(referencingModule.url)
|
||||
),
|
||||
options,
|
||||
specifier,
|
||||
"unlinked",
|
||||
referencingModule
|
||||
),
|
||||
referencingModule.context,
|
||||
true
|
||||
)
|
||||
);
|
||||
// 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;
|
||||
})();
|
||||
}
|
||||
const isJSON = p.endsWith(".json");
|
||||
if (isJSON) {
|
||||
return JSON.parse(content);
|
||||
}
|
||||
|
||||
if (p in requireCache) {
|
||||
return requireCache[p].exports;
|
||||
}
|
||||
const m = {
|
||||
exports: {}
|
||||
};
|
||||
requireCache[p] = m;
|
||||
|
||||
const moduleScope = {
|
||||
...baseModuleScope,
|
||||
require: _require.bind(
|
||||
null,
|
||||
path.dirname(p),
|
||||
options
|
||||
),
|
||||
importScripts: url => {
|
||||
expect(url).toMatch(
|
||||
/^https:\/\/test\.cases\/path\//
|
||||
);
|
||||
_require(
|
||||
outputDirectory,
|
||||
options,
|
||||
`.${url.slice("https://test.cases/path".length)}`
|
||||
);
|
||||
},
|
||||
module: m,
|
||||
exports: m.exports,
|
||||
__dirname: path.dirname(p),
|
||||
__filename: p,
|
||||
_globalAssign: { expect }
|
||||
};
|
||||
if (testConfig.moduleScope) {
|
||||
testConfig.moduleScope(moduleScope, options);
|
||||
}
|
||||
if (!runInNewContext)
|
||||
content = `Object.assign(global, _globalAssign); ${content}`;
|
||||
const args = Object.keys(moduleScope);
|
||||
const argValues = args.map(arg => moduleScope[arg]);
|
||||
const code = `(function(${args.join(
|
||||
", "
|
||||
)}) {${content}\n})`;
|
||||
|
||||
const oldCurrentScript = document.currentScript;
|
||||
document.currentScript = new CurrentScript(subPath);
|
||||
const fn = runInNewContext
|
||||
? vm.runInNewContext(code, globalContext, p)
|
||||
: vm.runInThisContext(code, p);
|
||||
fn.call(
|
||||
testConfig.nonEsmThis
|
||||
? testConfig.nonEsmThis(module)
|
||||
: m.exports,
|
||||
...argValues
|
||||
);
|
||||
document.currentScript = oldCurrentScript;
|
||||
return m.exports;
|
||||
} else if (
|
||||
testConfig.modules &&
|
||||
module in testConfig.modules
|
||||
) {
|
||||
return testConfig.modules[module];
|
||||
}
|
||||
return require(
|
||||
module.startsWith("node:") ? module.slice(5) : module
|
||||
);
|
||||
};
|
||||
if (testConfig.moduleScope) {
|
||||
testConfig.moduleScope(runner._moduleScope, options);
|
||||
}
|
||||
if (Array.isArray(bundlePath)) {
|
||||
for (const bundlePathItem of bundlePath) {
|
||||
results.push(
|
||||
_require(
|
||||
outputDirectory,
|
||||
options,
|
||||
`./${bundlePathItem}`
|
||||
)
|
||||
runner.require(outputDirectory, `./${bundlePathItem}`)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
results.push(
|
||||
_require(outputDirectory, options, bundlePath)
|
||||
);
|
||||
results.push(runner.require(outputDirectory, bundlePath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,11 +4,10 @@ require("./helpers/warmup-webpack");
|
|||
|
||||
const path = require("path");
|
||||
const fs = require("graceful-fs");
|
||||
const vm = require("vm");
|
||||
const rimraf = require("rimraf");
|
||||
const checkArrayExpectation = require("./checkArrayExpectation");
|
||||
const createLazyTestEnv = require("./helpers/createLazyTestEnv");
|
||||
const FakeDocument = require("./helpers/FakeDocument");
|
||||
const { TestRunner } = require("./runner");
|
||||
|
||||
const casesPath = path.join(__dirname, "hotCases");
|
||||
let categories = fs
|
||||
|
@ -69,7 +68,11 @@ const describeCases = config => {
|
|||
if (!options.output) options.output = {};
|
||||
if (!options.output.path) options.output.path = outputDirectory;
|
||||
if (!options.output.filename)
|
||||
options.output.filename = "bundle.js";
|
||||
options.output.filename = `bundle${
|
||||
options.experiments && options.experiments.outputModule
|
||||
? ".mjs"
|
||||
: ".js"
|
||||
}`;
|
||||
if (!options.output.chunkFilename)
|
||||
options.output.chunkFilename = "[name].chunk.[fullhash].js";
|
||||
if (options.output.pathinfo === undefined)
|
||||
|
@ -139,136 +142,20 @@ const describeCases = config => {
|
|||
return;
|
||||
}
|
||||
|
||||
const urlToPath = url => {
|
||||
if (url.startsWith("https://test.cases/path/"))
|
||||
url = url.slice(24);
|
||||
return path.resolve(outputDirectory, `./${url}`);
|
||||
};
|
||||
const urlToRelativePath = url => {
|
||||
if (url.startsWith("https://test.cases/path/"))
|
||||
url = url.slice(24);
|
||||
return `./${url}`;
|
||||
};
|
||||
const window = {
|
||||
_elements: [],
|
||||
fetch: async url => {
|
||||
try {
|
||||
const buffer = await new Promise((resolve, reject) => {
|
||||
fs.readFile(urlToPath(url), (err, b) =>
|
||||
err ? reject(err) : resolve(b)
|
||||
);
|
||||
});
|
||||
return {
|
||||
status: 200,
|
||||
ok: true,
|
||||
json: async () => JSON.parse(buffer.toString("utf-8"))
|
||||
};
|
||||
} catch (err) {
|
||||
if (err.code === "ENOENT") {
|
||||
return {
|
||||
status: 404,
|
||||
ok: false
|
||||
};
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
const runner = new TestRunner({
|
||||
target: options.target,
|
||||
outputDirectory,
|
||||
testMeta: {
|
||||
category: category.name,
|
||||
name: testName,
|
||||
env: "jsdom"
|
||||
},
|
||||
importScripts: url => {
|
||||
expect(url).toMatch(/^https:\/\/test\.cases\/path\//);
|
||||
_require(urlToRelativePath(url));
|
||||
testConfig: {
|
||||
...testConfig,
|
||||
evaluateScriptOnAttached: true
|
||||
},
|
||||
document: {
|
||||
createElement(type) {
|
||||
const ele = {
|
||||
_type: type,
|
||||
getAttribute(name) {
|
||||
return this[name];
|
||||
},
|
||||
setAttribute(name, value) {
|
||||
this[name] = value;
|
||||
},
|
||||
removeAttribute(name) {
|
||||
delete this[name];
|
||||
},
|
||||
parentNode: {
|
||||
removeChild(node) {
|
||||
window._elements = window._elements.filter(
|
||||
item => item !== node
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
ele.sheet =
|
||||
type === "link"
|
||||
? new FakeDocument.FakeSheet(ele, outputDirectory)
|
||||
: {};
|
||||
return ele;
|
||||
},
|
||||
head: {
|
||||
appendChild(element) {
|
||||
window._elements.push(element);
|
||||
|
||||
if (element._type === "script") {
|
||||
// run it
|
||||
Promise.resolve().then(() => {
|
||||
_require(urlToRelativePath(element.src));
|
||||
});
|
||||
} else if (element._type === "link") {
|
||||
Promise.resolve().then(() => {
|
||||
if (element.onload) {
|
||||
// run it
|
||||
element.onload({ type: "load" });
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
insertBefore(element, before) {
|
||||
window._elements.push(element);
|
||||
|
||||
if (element._type === "script") {
|
||||
// run it
|
||||
Promise.resolve().then(() => {
|
||||
_require(urlToRelativePath(element.src));
|
||||
});
|
||||
} else if (element._type === "link") {
|
||||
// run it
|
||||
Promise.resolve().then(() => {
|
||||
element.onload({ type: "load" });
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
getElementsByTagName(name) {
|
||||
if (name === "head") return [this.head];
|
||||
if (name === "script" || name === "link") {
|
||||
return window._elements.filter(
|
||||
item => item._type === name
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error("Not supported");
|
||||
}
|
||||
},
|
||||
Worker: require("./helpers/createFakeWorker")({
|
||||
outputDirectory
|
||||
}),
|
||||
EventSource: require("./helpers/EventSourceForNode"),
|
||||
location: {
|
||||
href: "https://test.cases/path/index.html",
|
||||
origin: "https://test.cases",
|
||||
toString() {
|
||||
return "https://test.cases/path/index.html";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const moduleScope = {
|
||||
window
|
||||
};
|
||||
|
||||
if (testConfig.moduleScope) {
|
||||
testConfig.moduleScope(moduleScope, options);
|
||||
}
|
||||
webpackOptions: options
|
||||
});
|
||||
|
||||
function _next(callback) {
|
||||
fakeUpdateLoaderOptions.updateIndex++;
|
||||
|
@ -307,74 +194,31 @@ const describeCases = config => {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {string} module module
|
||||
* @returns {EXPECTED_ANY} required module
|
||||
*/
|
||||
function _require(module) {
|
||||
if (module.startsWith("./")) {
|
||||
const p = path.join(outputDirectory, module);
|
||||
if (module.endsWith(".css")) {
|
||||
return fs.readFileSync(p, "utf-8");
|
||||
}
|
||||
if (module.endsWith(".json")) {
|
||||
return JSON.parse(fs.readFileSync(p, "utf-8"));
|
||||
}
|
||||
const fn = vm.runInThisContext(
|
||||
"(function(require, module, exports, __dirname, __filename, it, beforeEach, afterEach, expect, jest, self, window, fetch, document, importScripts, Worker, EventSource, NEXT, STATS) {" +
|
||||
"global.expect = expect;" +
|
||||
"global.it = it;" +
|
||||
`function nsObj(m) { Object.defineProperty(m, Symbol.toStringTag, { value: "Module" }); return m; }${fs.readFileSync(
|
||||
p,
|
||||
"utf-8"
|
||||
)}\n})`,
|
||||
p
|
||||
);
|
||||
const m = {
|
||||
exports: {}
|
||||
};
|
||||
fn.call(
|
||||
m.exports,
|
||||
_require,
|
||||
m,
|
||||
m.exports,
|
||||
outputDirectory,
|
||||
p,
|
||||
_it,
|
||||
_beforeEach,
|
||||
_afterEach,
|
||||
expect,
|
||||
jest,
|
||||
window,
|
||||
window,
|
||||
window.fetch,
|
||||
window.document,
|
||||
window.importScripts,
|
||||
window.Worker,
|
||||
window.EventSource,
|
||||
_next,
|
||||
jsonStats
|
||||
);
|
||||
return m.exports;
|
||||
}
|
||||
return require(module);
|
||||
}
|
||||
runner.mergeModuleScope({
|
||||
it: _it,
|
||||
beforeEach: _beforeEach,
|
||||
afterEach: _afterEach,
|
||||
STATE: jsonStats,
|
||||
NEXT: _next
|
||||
});
|
||||
|
||||
let promise = Promise.resolve();
|
||||
const info = stats.toJson({ all: false, entrypoints: true });
|
||||
if (config.target === "web") {
|
||||
for (const file of info.entrypoints.main.assets) {
|
||||
if (file.name.endsWith(".css")) {
|
||||
const link = window.document.createElement("link");
|
||||
link.href = path.join(outputDirectory, file.name);
|
||||
window.document.head.appendChild(link);
|
||||
const link =
|
||||
runner._moduleScope.document.createElement("link");
|
||||
link.href = file.name;
|
||||
runner._moduleScope.document.head.appendChild(link);
|
||||
} else {
|
||||
_require(`./${file.name}`);
|
||||
runner.require(outputDirectory, `./${file.name}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const assets = info.entrypoints.main.assets;
|
||||
const result = _require(
|
||||
const result = runner.require(
|
||||
outputDirectory,
|
||||
`./${assets[assets.length - 1].name}`
|
||||
);
|
||||
if (typeof result === "object" && "then" in result)
|
||||
|
|
|
@ -7,16 +7,13 @@ require("./helpers/warmup-webpack");
|
|||
|
||||
const path = require("path");
|
||||
const fs = require("graceful-fs");
|
||||
const vm = require("vm");
|
||||
const rimraf = require("rimraf");
|
||||
const { pathToFileURL, fileURLToPath } = require("url");
|
||||
const checkArrayExpectation = require("./checkArrayExpectation");
|
||||
const createLazyTestEnv = require("./helpers/createLazyTestEnv");
|
||||
const { remove } = require("./helpers/remove");
|
||||
const prepareOptions = require("./helpers/prepareOptions");
|
||||
const deprecationTracking = require("./helpers/deprecationTracking");
|
||||
const FakeDocument = require("./helpers/FakeDocument");
|
||||
const asModule = require("./helpers/asModule");
|
||||
const { TestRunner } = require("./runner");
|
||||
|
||||
/**
|
||||
* @param {string} src src
|
||||
|
@ -150,7 +147,11 @@ const describeCases = config => {
|
|||
if (typeof options.output.pathinfo === "undefined")
|
||||
options.output.pathinfo = true;
|
||||
if (!options.output.filename)
|
||||
options.output.filename = "bundle.js";
|
||||
options.output.filename = `bundle${
|
||||
options.experiments && options.experiments.outputModule
|
||||
? ".mjs"
|
||||
: ".js"
|
||||
}`;
|
||||
if (options.cache && options.cache.type === "filesystem") {
|
||||
const cacheDirectory = path.join(tempDirectory, ".cache");
|
||||
options.cache.cacheDirectory = cacheDirectory;
|
||||
|
@ -269,178 +270,6 @@ const describeCases = config => {
|
|||
)
|
||||
return;
|
||||
|
||||
const globalContext = {
|
||||
console,
|
||||
expect,
|
||||
setTimeout,
|
||||
clearTimeout,
|
||||
document: new FakeDocument()
|
||||
};
|
||||
|
||||
const baseModuleScope = {
|
||||
console,
|
||||
it: run.it,
|
||||
beforeEach: _beforeEach,
|
||||
afterEach: _afterEach,
|
||||
expect,
|
||||
jest,
|
||||
STATS_JSON: jsonStats,
|
||||
nsObj: m => {
|
||||
Object.defineProperty(m, Symbol.toStringTag, {
|
||||
value: "Module"
|
||||
});
|
||||
return m;
|
||||
},
|
||||
window: globalContext,
|
||||
self: globalContext,
|
||||
WATCH_STEP: run.name,
|
||||
STATE: state
|
||||
};
|
||||
|
||||
const esmCache = new Map();
|
||||
const esmIdentifier = `${category.name}-${testName}`;
|
||||
const esmContext = vm.createContext(baseModuleScope, {
|
||||
name: "context for esm"
|
||||
});
|
||||
// ESM
|
||||
const isModule =
|
||||
options.experiments && options.experiments.outputModule;
|
||||
|
||||
/**
|
||||
* @param {string} currentDirectory The directory to resolve relative paths from
|
||||
* @param {string} module The module to require
|
||||
* @param {("unlinked"|"evaluated")} esmMode The mode for ESM module handling
|
||||
* @returns {EXPECTED_ANY} required module
|
||||
* @private
|
||||
*/
|
||||
function _require(currentDirectory, module, esmMode) {
|
||||
if (/^\.\.?\//.test(module) || path.isAbsolute(module)) {
|
||||
let fn;
|
||||
const p = path.isAbsolute(module)
|
||||
? module
|
||||
: path.join(currentDirectory, module);
|
||||
const content = fs.readFileSync(p, "utf-8");
|
||||
|
||||
if (isModule) {
|
||||
if (!vm.SourceTextModule)
|
||||
throw new Error(
|
||||
"Running this test requires '--experimental-vm-modules'.\nRun with 'node --experimental-vm-modules node_modules/jest-cli/bin/jest'."
|
||||
);
|
||||
let esm = esmCache.get(p);
|
||||
if (!esm) {
|
||||
esm = new vm.SourceTextModule(content, {
|
||||
identifier: `${esmIdentifier}-${p}`,
|
||||
url: `${pathToFileURL(p).href}?${esmIdentifier}`,
|
||||
context: esmContext,
|
||||
initializeImportMeta: (meta, module) => {
|
||||
meta.url = pathToFileURL(p).href;
|
||||
},
|
||||
importModuleDynamically: async (
|
||||
specifier,
|
||||
module
|
||||
) => {
|
||||
const normalizedSpecifier =
|
||||
specifier.startsWith("file:")
|
||||
? `./${path.relative(
|
||||
path.dirname(p),
|
||||
fileURLToPath(specifier)
|
||||
)}`
|
||||
: specifier.replace(
|
||||
/https:\/\/test.cases\/path\//,
|
||||
"./"
|
||||
);
|
||||
const result = await _require(
|
||||
currentDirectory,
|
||||
normalizedSpecifier,
|
||||
"evaluated"
|
||||
);
|
||||
return await asModule(result, module.context);
|
||||
}
|
||||
});
|
||||
esmCache.set(p, esm);
|
||||
}
|
||||
if (esmMode === "unlinked") return esm;
|
||||
return (async () => {
|
||||
if (esmMode === "unlinked") return esm;
|
||||
await esm.link(
|
||||
async (specifier, referencingModule) =>
|
||||
await asModule(
|
||||
await _require(
|
||||
path.dirname(
|
||||
referencingModule.identifier
|
||||
? referencingModule.identifier.slice(
|
||||
esmIdentifier.length + 1
|
||||
)
|
||||
: fileURLToPath(referencingModule.url)
|
||||
),
|
||||
specifier,
|
||||
"unlinked"
|
||||
),
|
||||
referencingModule.context,
|
||||
true
|
||||
)
|
||||
);
|
||||
// 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;
|
||||
})();
|
||||
}
|
||||
|
||||
if (
|
||||
options.target === "web" ||
|
||||
options.target === "webworker"
|
||||
) {
|
||||
fn = vm.runInNewContext(
|
||||
"(function(require, module, exports, __dirname, __filename, it, WATCH_STEP, STATS_JSON, STATE, expect, window, self) {" +
|
||||
`function nsObj(m) { Object.defineProperty(m, Symbol.toStringTag, { value: "Module" }); return m; }${
|
||||
content
|
||||
}\n})`,
|
||||
globalContext,
|
||||
p
|
||||
);
|
||||
} else {
|
||||
fn = vm.runInThisContext(
|
||||
"(function(require, module, exports, __dirname, __filename, it, WATCH_STEP, STATS_JSON, STATE, expect) {" +
|
||||
"global.expect = expect;" +
|
||||
`function nsObj(m) { Object.defineProperty(m, Symbol.toStringTag, { value: "Module" }); return m; }${
|
||||
content
|
||||
}\n})`,
|
||||
p
|
||||
);
|
||||
}
|
||||
const m = {
|
||||
exports: {}
|
||||
};
|
||||
fn.call(
|
||||
m.exports,
|
||||
_require.bind(null, path.dirname(p)),
|
||||
m,
|
||||
m.exports,
|
||||
path.dirname(p),
|
||||
p,
|
||||
run.it,
|
||||
run.name,
|
||||
jsonStats,
|
||||
state,
|
||||
expect,
|
||||
globalContext,
|
||||
globalContext
|
||||
);
|
||||
return module.exports;
|
||||
} else if (
|
||||
testConfig.modules &&
|
||||
module in testConfig.modules
|
||||
) {
|
||||
return testConfig.modules[module];
|
||||
}
|
||||
return jest.requireActual(module);
|
||||
}
|
||||
|
||||
let testConfig = {};
|
||||
try {
|
||||
// try to load a test file
|
||||
|
@ -453,7 +282,28 @@ const describeCases = config => {
|
|||
|
||||
if (testConfig.noTests)
|
||||
return process.nextTick(compilationFinished);
|
||||
|
||||
const runner = new TestRunner({
|
||||
target: options.target,
|
||||
outputDirectory,
|
||||
testMeta: {
|
||||
category: category.name,
|
||||
name: testName,
|
||||
env: "jsdom"
|
||||
},
|
||||
testConfig: {
|
||||
...testConfig,
|
||||
evaluateScriptOnAttached: true
|
||||
},
|
||||
webpackOptions: options
|
||||
});
|
||||
runner.mergeModuleScope({
|
||||
it: run.it,
|
||||
beforeEach: _beforeEach,
|
||||
afterEach: _afterEach,
|
||||
STATS_JSON: jsonStats,
|
||||
STATE: state,
|
||||
WATCH_STEP: run.name
|
||||
});
|
||||
const getBundle = (outputDirectory, module) => {
|
||||
if (Array.isArray(module)) {
|
||||
return module.map(arg =>
|
||||
|
@ -475,7 +325,7 @@ const describeCases = config => {
|
|||
)) {
|
||||
promises.push(
|
||||
Promise.resolve().then(() =>
|
||||
_require(outputDirectory, p)
|
||||
runner.require(outputDirectory, p)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
|
||||
|
||||
exports[`ConfigTestCases css build-http exported tests should work with URLs in CSS 1`] = `
|
||||
Array [
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
// sharing global require cache between webpack.config.js and testing file
|
||||
modules: {
|
||||
[path.resolve(__dirname, "data.js")]: require("./data.js")
|
||||
}
|
||||
};
|
|
@ -1,6 +1,13 @@
|
|||
import "./style.css";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
it("creates source maps for .css output files by default", function() {
|
||||
var fs = require("fs");
|
||||
var source = fs.readFileSync(__filename, "utf-8");
|
||||
var match = /sourceMappingURL\s*=\s*(.*)\*\//.exec(source);
|
||||
expect(match[1]).toBe("bundle0.css.map");
|
||||
const css = fs.readFileSync(path.resolve(__dirname, "style.css"), "utf-8");
|
||||
const map = JSON.parse(fs.readFileSync(path.resolve(__dirname, "style.css.map"), "utf-8"));
|
||||
|
||||
var match = /sourceMappingURL\s*=\s*(.*)\*\//.exec(css);
|
||||
expect(match[1]).toBe("style.css.map");
|
||||
expect(map).toHaveProperty("version", 3);
|
||||
expect(map).toHaveProperty("file", "style.css");
|
||||
});
|
|
@ -0,0 +1,3 @@
|
|||
body {
|
||||
background-color: red;
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
var foo = {};
|
||||
|
||||
module.exports = foo;
|
|
@ -1,12 +1,36 @@
|
|||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
|
||||
/** @type {import("../../../../").Configuration} */
|
||||
module.exports = {
|
||||
mode: "development",
|
||||
output: {
|
||||
filename: "bundle0.css"
|
||||
filename: "bundle0.js"
|
||||
},
|
||||
node: {
|
||||
__dirname: false,
|
||||
__filename: false
|
||||
},
|
||||
devtool: "source-map"
|
||||
devtool: "source-map",
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.css$/i,
|
||||
use: [
|
||||
MiniCssExtractPlugin.loader,
|
||||
{
|
||||
loader: "css-loader",
|
||||
options: {
|
||||
sourceMap: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new MiniCssExtractPlugin({
|
||||
filename: "style.css",
|
||||
chunkFilename: "[id].css"
|
||||
})
|
||||
]
|
||||
};
|
||||
|
|
|
@ -14,6 +14,7 @@ module.exports = class EventSource {
|
|||
url,
|
||||
{
|
||||
agent: false,
|
||||
rejectUnauthorized: false,
|
||||
headers: { accept: "text/event-stream" }
|
||||
},
|
||||
res => {
|
||||
|
|
|
@ -73,17 +73,34 @@ class FakeElement {
|
|||
this.sheet = type === "link" ? new FakeSheet(this, basePath) : undefined;
|
||||
}
|
||||
|
||||
appendChild(node) {
|
||||
_attach(node) {
|
||||
this._document._onElementAttached(node);
|
||||
this._children.push(node);
|
||||
node.parentNode = this;
|
||||
}
|
||||
|
||||
_load(node) {
|
||||
if (node._type === "link") {
|
||||
setTimeout(() => {
|
||||
if (node.onload) node.onload({ type: "load", target: node });
|
||||
}, 100);
|
||||
} else if (node._type === "script" && this._document.onScript) {
|
||||
Promise.resolve().then(() => {
|
||||
this._document.onScript(node.src);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
insertBefore(node, before) {
|
||||
this._attach(node);
|
||||
this._load(node);
|
||||
}
|
||||
|
||||
appendChild(node) {
|
||||
this._attach(node);
|
||||
this._load(node);
|
||||
}
|
||||
|
||||
removeChild(node) {
|
||||
const idx = this._children.indexOf(node);
|
||||
if (idx >= 0) {
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
module.exports = {
|
||||
moduleScope(scope, options) {
|
||||
if (
|
||||
(options.target === "web" || options.target === "webworker") &&
|
||||
!scope.process
|
||||
) {
|
||||
scope.process = process;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -16,7 +16,7 @@ it("should wait until promises returned by status handlers are fulfilled", (done
|
|||
value = require("./file");
|
||||
});
|
||||
NEXT(require("../../update")(done, undefined, () => {
|
||||
expect(handler.mock.calls).toStrictEqual([['check'], ['prepare'], ['dispose'], ['apply'], ['idle']]);
|
||||
expect(handler.mock.calls).toEqual([['check'], ['prepare'], ['dispose'], ['apply'], ['idle']]);
|
||||
for (let result of handler.mock.results)
|
||||
expect(result.value.test).toHaveBeenCalledTimes(1);
|
||||
|
||||
|
|
|
@ -0,0 +1,484 @@
|
|||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const vm = require("vm");
|
||||
const { pathToFileURL, fileURLToPath } = require("url");
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @returns {string}
|
||||
*/
|
||||
const getSubPath = path => {
|
||||
let subPath = "";
|
||||
const lastSlash = path.lastIndexOf("/");
|
||||
let firstSlash = path.indexOf("/");
|
||||
if (lastSlash !== -1 && firstSlash !== lastSlash) {
|
||||
if (firstSlash !== -1) {
|
||||
let next = path.indexOf("/", firstSlash + 1);
|
||||
let dir = path.slice(firstSlash + 1, next);
|
||||
|
||||
while (dir === ".") {
|
||||
firstSlash = next;
|
||||
next = path.indexOf("/", firstSlash + 1);
|
||||
dir = path.slice(firstSlash + 1, next);
|
||||
}
|
||||
}
|
||||
subPath = path.slice(firstSlash + 1, lastSlash + 1);
|
||||
}
|
||||
return subPath;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isRelativePath = path => /^\.\.?\//.test(path);
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {string} outputDirectory
|
||||
* @returns {string}
|
||||
*/
|
||||
const urlToPath = (url, outputDirectory) => {
|
||||
if (url.startsWith("https://test.cases/path/")) url = url.slice(24);
|
||||
else if (url.startsWith("https://test.cases/")) url = url.slice(19);
|
||||
return path.resolve(outputDirectory, `./${url}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @returns {string}
|
||||
*/
|
||||
const urlToRelativePath = url => {
|
||||
if (url.startsWith("https://test.cases/path/")) url = url.slice(24);
|
||||
else if (url.startsWith("https://test.cases/")) url = url.slice(19);
|
||||
return `./${url}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {Object} TestMeta
|
||||
* @property {string} category
|
||||
* @property {string} name
|
||||
* @property {"jsdom"} [env]
|
||||
* @property {number} [round]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} TestConfig
|
||||
* @property {Function} [resolveModule]
|
||||
* @property {Function} [moduleScope]
|
||||
* @property {Function} [nonEsmThis]
|
||||
* @property {boolean} [evaluateScriptOnAttached]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} TestRunnerOptions
|
||||
* @property {string|string[]} target
|
||||
* @property {string} outputDirectory
|
||||
* @property {TestMeta} testMeta
|
||||
* @property {TestConfig} testConfig
|
||||
* @property {EXPECTED_ANY} webpackOptions
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ModuleInfo
|
||||
* @property {string} subPath
|
||||
* @property {string} modulePath
|
||||
* @property {string} content
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} RequireContext
|
||||
* @property {"unlinked"|"evaluated"} esmMode
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ModuleRunner
|
||||
* @property {(moduleInfo: ModuleInfo, context: RequireContext) => EXPECTED_ANY} cjs
|
||||
* @property {(moduleInfo: ModuleInfo, context: RequireContext) => Promise<EXPECTED_ANY>} esm
|
||||
* @property {(moduleInfo: ModuleInfo, context: RequireContext) => EXPECTED_ANY} json
|
||||
* @property {(moduleInfo: ModuleInfo, context: RequireContext) => EXPECTED_ANY} raw
|
||||
*/
|
||||
|
||||
class TestRunner {
|
||||
/**
|
||||
* @param {TestRunnerOptions} options
|
||||
*/
|
||||
constructor({
|
||||
target,
|
||||
outputDirectory,
|
||||
testMeta,
|
||||
testConfig,
|
||||
webpackOptions
|
||||
}) {
|
||||
this.target = target;
|
||||
this.outputDirectory = outputDirectory;
|
||||
this.testConfig = testConfig || {};
|
||||
this.testMeta = testMeta || {};
|
||||
this.webpackOptions = webpackOptions || {};
|
||||
this._runInNewContext = this.isTargetWeb();
|
||||
this._globalContext = this.createBaseGlobalContext();
|
||||
this._moduleScope = this.createBaseModuleScope();
|
||||
this._moduleRunners = this.createModuleRunners();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {ModuleRunner}
|
||||
*/
|
||||
createModuleRunners() {
|
||||
return {
|
||||
cjs: this.createCjsRunner(),
|
||||
esm: this.createEsmRunner(),
|
||||
json: this.createJSONRunner(),
|
||||
raw: this.createRawRunner()
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @returns {EXPECTED_ANY} globalContext
|
||||
*/
|
||||
createBaseGlobalContext() {
|
||||
let base = { console, expect, setTimeout, clearTimeout };
|
||||
Object.assign(base, this.setupEnv());
|
||||
return base;
|
||||
}
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isTargetWeb() {
|
||||
return (
|
||||
this.target === "web" ||
|
||||
this.target === "webworker" ||
|
||||
(Array.isArray(this.target) &&
|
||||
(this.target.includes("web") || this.target.includes("webworker")))
|
||||
);
|
||||
}
|
||||
/**
|
||||
* @returns {boolean}
|
||||
*/
|
||||
jsDom() {
|
||||
return this.testMeta.env === "jsdom" || this.isTargetWeb();
|
||||
}
|
||||
/**
|
||||
* @returns {EXPECTED_ANY} moduleScope
|
||||
*/
|
||||
createBaseModuleScope() {
|
||||
let base = {
|
||||
console,
|
||||
expect,
|
||||
jest,
|
||||
nsObj: m => {
|
||||
Object.defineProperty(m, Symbol.toStringTag, {
|
||||
value: "Module"
|
||||
});
|
||||
return m;
|
||||
}
|
||||
};
|
||||
if (this.jsDom()) {
|
||||
Object.assign(base, this._globalContext);
|
||||
base.window = this._globalContext;
|
||||
base.self = this._globalContext;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
/**
|
||||
* @param {EXPECTED_ANY} globalContext
|
||||
* @returns {EXPECTED_ANY}
|
||||
*/
|
||||
mergeGlobalContext(globalContext) {
|
||||
return Object.assign(this._globalContext, globalContext);
|
||||
}
|
||||
/**
|
||||
* @param {EXPECTED_ANY} moduleScope
|
||||
* @returns {EXPECTED_ANY}
|
||||
*/
|
||||
mergeModuleScope(moduleScope) {
|
||||
return Object.assign(this._moduleScope, moduleScope);
|
||||
}
|
||||
/**
|
||||
* @param {string} currentDirectory
|
||||
* @param {string|string[]} module
|
||||
* @returns {ModuleInfo}
|
||||
*/
|
||||
_resolveModule(currentDirectory, module) {
|
||||
if (Array.isArray(module)) {
|
||||
return {
|
||||
subPath: "",
|
||||
modulePath: path.join(currentDirectory, ".array-require.js"),
|
||||
content: `module.exports = (${module
|
||||
.map(arg => `require(${JSON.stringify(`./${arg}`)})`)
|
||||
.join(", ")});`
|
||||
};
|
||||
}
|
||||
if (isRelativePath(module)) {
|
||||
return {
|
||||
subPath: getSubPath(module),
|
||||
modulePath: path.join(currentDirectory, module),
|
||||
content: fs.readFileSync(path.join(currentDirectory, module), "utf-8")
|
||||
};
|
||||
}
|
||||
if (path.isAbsolute(module)) {
|
||||
return {
|
||||
subPath: "",
|
||||
modulePath: module,
|
||||
content: fs.readFileSync(module, "utf-8")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} currentDirectory
|
||||
* @param {string|string[]} module
|
||||
* @param {RequireContext} [context={}]
|
||||
* @returns {EXPECTED_ANY}
|
||||
*/
|
||||
require(currentDirectory, module, context = {}) {
|
||||
if (this.testConfig.modules && module in this.testConfig.modules) {
|
||||
return this.testConfig.modules[module];
|
||||
}
|
||||
if (this.testConfig.resolveModule) {
|
||||
module = this.testConfig.resolveModule(
|
||||
module,
|
||||
this.testMeta.round || 0,
|
||||
this.webpackOptions
|
||||
);
|
||||
}
|
||||
let moduleInfo = this._resolveModule(currentDirectory, module);
|
||||
if (!moduleInfo) {
|
||||
return require(module.startsWith("node:") ? module.slice(5) : module);
|
||||
}
|
||||
const { modulePath } = moduleInfo;
|
||||
if (
|
||||
modulePath.endsWith(".mjs") &&
|
||||
this.webpackOptions.experiments &&
|
||||
this.webpackOptions.experiments.outputModule
|
||||
) {
|
||||
return this._moduleRunners.esm(moduleInfo, context);
|
||||
}
|
||||
if (modulePath.endsWith(".json")) {
|
||||
return this._moduleRunners.json(moduleInfo, context);
|
||||
}
|
||||
if (["css"].includes(modulePath.split(".").pop())) {
|
||||
return this._moduleRunners.raw(moduleInfo, context);
|
||||
}
|
||||
return this._moduleRunners.cjs(moduleInfo, context);
|
||||
}
|
||||
/**
|
||||
* @returns {(moduleInfo: ModuleInfo, context: RequireContext) => EXPECTED_ANY}
|
||||
*/
|
||||
createCjsRunner() {
|
||||
const requireCache = Object.create(null);
|
||||
return (moduleInfo, context) => {
|
||||
const { modulePath, subPath, content } = moduleInfo;
|
||||
let _content = content;
|
||||
if (modulePath in requireCache) {
|
||||
return requireCache[modulePath].exports;
|
||||
}
|
||||
const mod = {
|
||||
exports: {}
|
||||
};
|
||||
requireCache[modulePath] = mod;
|
||||
const moduleScope = {
|
||||
...this._moduleScope,
|
||||
require: this.require.bind(this, path.dirname(modulePath)),
|
||||
importScripts: url => {
|
||||
expect(url).toMatch(/^https:\/\/test\.cases\/path\//);
|
||||
this.require(this.outputDirectory, urlToRelativePath(url));
|
||||
},
|
||||
module: mod,
|
||||
exports: mod.exports,
|
||||
__dirname: path.dirname(modulePath),
|
||||
__filename: modulePath,
|
||||
_globalAssign: { expect, it: this._moduleScope.it }
|
||||
};
|
||||
// Call again because some tests rely on `scope.module`
|
||||
if (this.testConfig.moduleScope) {
|
||||
this.testConfig.moduleScope(moduleScope, this.webpackOptions);
|
||||
}
|
||||
if (!this._runInNewContext)
|
||||
_content = `Object.assign(global, _globalAssign); ${content}`;
|
||||
const args = Object.keys(moduleScope);
|
||||
const argValues = args.map(arg => moduleScope[arg]);
|
||||
const code = `(function(${args.join(", ")}) {${_content}\n})`;
|
||||
const document = this._moduleScope.document;
|
||||
const fn = this._runInNewContext
|
||||
? vm.runInNewContext(code, this._globalContext, modulePath)
|
||||
: vm.runInThisContext(code, modulePath);
|
||||
const call = () => {
|
||||
fn.call(
|
||||
this.testConfig.nonEsmThis
|
||||
? this.testConfig.nonEsmThis(module)
|
||||
: mod.exports,
|
||||
...argValues
|
||||
);
|
||||
};
|
||||
if (document) {
|
||||
const CurrentScript = require("../helpers/CurrentScript");
|
||||
const oldCurrentScript = document.currentScript;
|
||||
document.currentScript = new CurrentScript(subPath);
|
||||
try {
|
||||
call();
|
||||
} finally {
|
||||
document.currentScript = oldCurrentScript;
|
||||
}
|
||||
} else {
|
||||
call();
|
||||
}
|
||||
return mod.exports;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @returns {(moduleInfo: ModuleInfo, context: RequireContext) => Promise<EXPECTED_ANY>}
|
||||
*/
|
||||
createEsmRunner() {
|
||||
const esmCache = new Map();
|
||||
const { category, name, round } = this.testMeta;
|
||||
const esmIdentifier = `${category.name}-${name}-${round || 0}`;
|
||||
let esmContext = null;
|
||||
return (moduleInfo, context) => {
|
||||
const asModule = require("../helpers/asModule");
|
||||
// lazy bind esm context
|
||||
if (!esmContext) {
|
||||
esmContext = vm.createContext(this._moduleScope, {
|
||||
name: "context for esm"
|
||||
});
|
||||
}
|
||||
const { modulePath, subPath, content } = moduleInfo;
|
||||
const { esmMode } = context;
|
||||
if (!vm.SourceTextModule)
|
||||
throw new Error(
|
||||
"Running this test requires '--experimental-vm-modules'.\nRun with 'node --experimental-vm-modules node_modules/jest-cli/bin/jest'."
|
||||
);
|
||||
let esm = esmCache.get(modulePath);
|
||||
if (!esm) {
|
||||
esm = new vm.SourceTextModule(content, {
|
||||
identifier: `${esmIdentifier}-${modulePath}`,
|
||||
url: `${pathToFileURL(modulePath).href}?${esmIdentifier}`,
|
||||
context: esmContext,
|
||||
initializeImportMeta: (meta, module) => {
|
||||
meta.url = pathToFileURL(modulePath).href;
|
||||
},
|
||||
importModuleDynamically: async (specifier, module) => {
|
||||
const normalizedSpecifier = specifier.startsWith("file:")
|
||||
? `./${path.relative(
|
||||
path.dirname(modulePath),
|
||||
fileURLToPath(specifier)
|
||||
)}`
|
||||
: specifier.replace(
|
||||
/https:\/\/example.com\/public\/path\//,
|
||||
"./"
|
||||
);
|
||||
const result = await this.require(
|
||||
path.dirname(modulePath),
|
||||
normalizedSpecifier,
|
||||
{
|
||||
esmMode: "evaluated"
|
||||
}
|
||||
);
|
||||
return await asModule(result, module.context);
|
||||
}
|
||||
});
|
||||
esmCache.set(modulePath, esm);
|
||||
}
|
||||
if (esmMode === "unlinked") return esm;
|
||||
return (async () => {
|
||||
if (esmMode === "unlinked") return esm;
|
||||
await esm.link(
|
||||
async (specifier, referencingModule) =>
|
||||
await asModule(
|
||||
await this.require(
|
||||
path.dirname(
|
||||
referencingModule.identifier
|
||||
? referencingModule.identifier.slice(
|
||||
esmIdentifier.length + 1
|
||||
)
|
||||
: fileURLToPath(referencingModule.url)
|
||||
),
|
||||
specifier,
|
||||
{ esmMode: "unlinked" }
|
||||
),
|
||||
referencingModule.context,
|
||||
true
|
||||
)
|
||||
);
|
||||
// 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;
|
||||
})();
|
||||
};
|
||||
}
|
||||
createJSONRunner() {
|
||||
return moduleInfo => {
|
||||
return JSON.parse(moduleInfo.content);
|
||||
};
|
||||
}
|
||||
createRawRunner() {
|
||||
return moduleInfo => {
|
||||
return moduleInfo.content;
|
||||
};
|
||||
}
|
||||
setupEnv() {
|
||||
if (this.jsDom()) {
|
||||
const outputDirectory = this.outputDirectory;
|
||||
const FakeDocument = require("../helpers/FakeDocument");
|
||||
const createFakeWorker = require("../helpers/createFakeWorker");
|
||||
const EventSource = require("../helpers/EventSourceForNode");
|
||||
const document = new FakeDocument(outputDirectory);
|
||||
if (this.testConfig.evaluateScriptOnAttached) {
|
||||
document.onScript = src => {
|
||||
this.require(outputDirectory, urlToRelativePath(src));
|
||||
};
|
||||
}
|
||||
const fetch = async url => {
|
||||
try {
|
||||
const buffer = await new Promise((resolve, reject) => {
|
||||
fs.readFile(urlToPath(url, this.outputDirectory), (err, b) =>
|
||||
err ? reject(err) : resolve(b)
|
||||
);
|
||||
});
|
||||
return {
|
||||
status: 200,
|
||||
ok: true,
|
||||
json: async () => JSON.parse(buffer.toString("utf-8"))
|
||||
};
|
||||
} catch (err) {
|
||||
if (err.code === "ENOENT") {
|
||||
return {
|
||||
status: 404,
|
||||
ok: false
|
||||
};
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
let env = {
|
||||
setTimeout,
|
||||
document,
|
||||
location: {
|
||||
href: "https://test.cases/path/index.html",
|
||||
origin: "https://test.cases",
|
||||
toString() {
|
||||
return "https://test.cases/path/index.html";
|
||||
}
|
||||
},
|
||||
getComputedStyle: document.getComputedStyle.bind(document),
|
||||
Worker: createFakeWorker({
|
||||
outputDirectory
|
||||
}),
|
||||
URL,
|
||||
EventSource,
|
||||
clearTimeout,
|
||||
fetch
|
||||
};
|
||||
if (typeof Blob !== "undefined") {
|
||||
// node.js >= 18
|
||||
env.Blob = Blob;
|
||||
}
|
||||
return env;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.TestRunner = TestRunner;
|
|
@ -25,7 +25,7 @@ module.exports = {
|
|||
}
|
||||
},
|
||||
output: {
|
||||
filename: "[name].[contenthash].js",
|
||||
chunkFilename: "[name].[contenthash].js"
|
||||
filename: "[name].[contenthash].mjs",
|
||||
chunkFilename: "[name].[contenthash].mjs"
|
||||
}
|
||||
};
|
||||
|
|
|
@ -129,7 +129,7 @@
|
|||
"@babel/template" "^7.27.2"
|
||||
"@babel/types" "^7.27.3"
|
||||
|
||||
"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.27.2", "@babel/parser@^7.27.3", "@babel/parser@^7.27.4", "@babel/parser@^7.27.5", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6":
|
||||
"@babel/parser@^7.1.0", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.27.2", "@babel/parser@^7.27.4", "@babel/parser@^7.27.5", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6":
|
||||
version "7.27.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.5.tgz#ed22f871f110aa285a6fd934a0efed621d118826"
|
||||
integrity sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==
|
||||
|
|
Loading…
Reference in New Issue