@jsenv/core 34.0.2 → 34.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/jsenv.js +260 -179
- package/package.json +1 -1
- package/src/execute/runtimes/browsers/from_playwright.js +23 -116
- package/src/execute/runtimes/browsers/middleware_istanbul.js +69 -0
- package/src/execute/runtimes/browsers/middleware_js_supervisor.js +100 -0
- package/src/kitchen/kitchen.js +9 -0
- package/src/kitchen/url_graph/url_info_transformations.js +0 -1
- package/src/plugins/transpilation/babel/jsenv_plugin_babel.js +0 -19
- package/src/test/execute_steps.js +0 -2
- package/src/test/execute_test_plan.js +21 -2
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ It has naturally evolved to cover the core needs of a JavaScript project: develo
|
|
|
7
7
|
- :sparkles: Same tooling for dev, tests and build.
|
|
8
8
|
- :exploding_head: Can execute tests on Chrome, Firefox, Safari and Node.js.
|
|
9
9
|
|
|
10
|
-
[Documentation](https://github.com/jsenv/jsenv-core/wiki/A
|
|
10
|
+
[Documentation](<https://github.com/jsenv/jsenv-core/wiki/A)-Getting-started>)
|
|
11
11
|
|
|
12
12
|
# Installation
|
|
13
13
|
|
package/dist/jsenv.js
CHANGED
|
@@ -7910,7 +7910,6 @@ const createUrlInfoTransformer = ({
|
|
|
7910
7910
|
await context.cook(sourcemapUrlInfo, {
|
|
7911
7911
|
reference: sourcemapReference
|
|
7912
7912
|
});
|
|
7913
|
-
sourcemapUrlInfo.isInline = sourcemaps === "inline";
|
|
7914
7913
|
const sourcemapRaw = JSON.parse(sourcemapUrlInfo.content);
|
|
7915
7914
|
const sourcemap = normalizeSourcemap(urlInfo, sourcemapRaw);
|
|
7916
7915
|
urlInfo.sourcemap = sourcemap;
|
|
@@ -9003,6 +9002,7 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
9003
9002
|
url: urlInfo.url
|
|
9004
9003
|
},
|
|
9005
9004
|
type: "sourcemap_comment",
|
|
9005
|
+
expectedType: "sourcemap",
|
|
9006
9006
|
subtype: urlInfo.contentType === "text/javascript" ? "js" : "css",
|
|
9007
9007
|
parentUrl: urlInfo.url,
|
|
9008
9008
|
specifier
|
|
@@ -9026,11 +9026,15 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
9026
9026
|
const [sourcemapReference, sourcemapUrlInfo] = resolveReference(createReference({
|
|
9027
9027
|
trace: traceFromUrlSite(sourcemapUrlSite),
|
|
9028
9028
|
type,
|
|
9029
|
+
expectedType: "sourcemap",
|
|
9029
9030
|
parentUrl: urlInfo.url,
|
|
9030
9031
|
specifier,
|
|
9031
9032
|
specifierLine,
|
|
9032
9033
|
specifierColumn
|
|
9033
9034
|
}));
|
|
9035
|
+
if (sourcemapReference.isInline) {
|
|
9036
|
+
sourcemapUrlInfo.isInline = true;
|
|
9037
|
+
}
|
|
9034
9038
|
sourcemapUrlInfo.type = "sourcemap";
|
|
9035
9039
|
return [sourcemapReference, sourcemapUrlInfo];
|
|
9036
9040
|
}
|
|
@@ -9541,6 +9545,12 @@ const adjustUrlSite = (urlInfo, {
|
|
|
9541
9545
|
}, urlInfo);
|
|
9542
9546
|
};
|
|
9543
9547
|
const inferUrlInfoType = urlInfo => {
|
|
9548
|
+
const {
|
|
9549
|
+
type
|
|
9550
|
+
} = urlInfo;
|
|
9551
|
+
if (type === "sourcemap") {
|
|
9552
|
+
return "sourcemap";
|
|
9553
|
+
}
|
|
9544
9554
|
const {
|
|
9545
9555
|
contentType
|
|
9546
9556
|
} = urlInfo;
|
|
@@ -18937,54 +18947,6 @@ const splitFileExtension$1 = filename => {
|
|
|
18937
18947
|
return [filename.slice(0, dotLastIndex), filename.slice(dotLastIndex)];
|
|
18938
18948
|
};
|
|
18939
18949
|
|
|
18940
|
-
// https://github.com/istanbuljs/babel-plugin-istanbul/blob/321740f7b25d803f881466ea819d870f7ed6a254/src/index.js
|
|
18941
|
-
|
|
18942
|
-
const babelPluginInstrument = (api, {
|
|
18943
|
-
useInlineSourceMaps = false
|
|
18944
|
-
}) => {
|
|
18945
|
-
const {
|
|
18946
|
-
programVisitor
|
|
18947
|
-
} = requireFromJsenv("istanbul-lib-instrument");
|
|
18948
|
-
const {
|
|
18949
|
-
types
|
|
18950
|
-
} = api;
|
|
18951
|
-
return {
|
|
18952
|
-
name: "transform-instrument",
|
|
18953
|
-
visitor: {
|
|
18954
|
-
Program: {
|
|
18955
|
-
enter(path) {
|
|
18956
|
-
const {
|
|
18957
|
-
file
|
|
18958
|
-
} = this;
|
|
18959
|
-
const {
|
|
18960
|
-
opts
|
|
18961
|
-
} = file;
|
|
18962
|
-
let inputSourceMap;
|
|
18963
|
-
if (useInlineSourceMaps) {
|
|
18964
|
-
// https://github.com/istanbuljs/babel-plugin-istanbul/commit/a9e15643d249a2985e4387e4308022053b2cd0ad#diff-1fdf421c05c1140f6d71444ea2b27638R65
|
|
18965
|
-
inputSourceMap = opts.inputSourceMap || file.inputMap ? file.inputMap.sourcemap : null;
|
|
18966
|
-
} else {
|
|
18967
|
-
inputSourceMap = opts.inputSourceMap;
|
|
18968
|
-
}
|
|
18969
|
-
this.__dv__ = programVisitor(types, opts.filenameRelative || opts.filename, {
|
|
18970
|
-
coverageVariable: "__coverage__",
|
|
18971
|
-
inputSourceMap
|
|
18972
|
-
});
|
|
18973
|
-
this.__dv__.enter(path);
|
|
18974
|
-
},
|
|
18975
|
-
exit(path) {
|
|
18976
|
-
if (!this.__dv__) {
|
|
18977
|
-
return;
|
|
18978
|
-
}
|
|
18979
|
-
const object = this.__dv__.exit(path);
|
|
18980
|
-
// object got two properties: fileCoverage and sourceMappingURL
|
|
18981
|
-
this.file.metadata.coverage = object.fileCoverage;
|
|
18982
|
-
}
|
|
18983
|
-
}
|
|
18984
|
-
}
|
|
18985
|
-
};
|
|
18986
|
-
};
|
|
18987
|
-
|
|
18988
18950
|
/*
|
|
18989
18951
|
* Generated helpers
|
|
18990
18952
|
* - https://github.com/babel/babel/commits/main/packages/babel-helpers/src/helpers.ts
|
|
@@ -19794,21 +19756,6 @@ const jsenvPluginBabel = ({
|
|
|
19794
19756
|
isJsModule,
|
|
19795
19757
|
getImportSpecifier
|
|
19796
19758
|
});
|
|
19797
|
-
if (context.dev) {
|
|
19798
|
-
const requestHeaders = context.request.headers;
|
|
19799
|
-
if (requestHeaders["x-coverage-instanbul"]) {
|
|
19800
|
-
const coverageConfig = JSON.parse(requestHeaders["x-coverage-instanbul"]);
|
|
19801
|
-
const associations = URL_META.resolveAssociations({
|
|
19802
|
-
cover: coverageConfig
|
|
19803
|
-
}, context.rootDirectoryUrl);
|
|
19804
|
-
if (URL_META.applyAssociations({
|
|
19805
|
-
url: urlInfo.url,
|
|
19806
|
-
associations
|
|
19807
|
-
}).cover) {
|
|
19808
|
-
babelPluginStructure["transform-instrument"] = [babelPluginInstrument];
|
|
19809
|
-
}
|
|
19810
|
-
}
|
|
19811
|
-
}
|
|
19812
19759
|
if (getCustomBabelPlugins) {
|
|
19813
19760
|
Object.assign(babelPluginStructure, getCustomBabelPlugins(context));
|
|
19814
19761
|
}
|
|
@@ -23911,6 +23858,54 @@ const listRelativeFileUrlToCover = async ({
|
|
|
23911
23858
|
}) => relativeUrl);
|
|
23912
23859
|
};
|
|
23913
23860
|
|
|
23861
|
+
// https://github.com/istanbuljs/babel-plugin-istanbul/blob/321740f7b25d803f881466ea819d870f7ed6a254/src/index.js
|
|
23862
|
+
|
|
23863
|
+
const babelPluginInstrument = (api, {
|
|
23864
|
+
useInlineSourceMaps = false
|
|
23865
|
+
}) => {
|
|
23866
|
+
const {
|
|
23867
|
+
programVisitor
|
|
23868
|
+
} = requireFromJsenv("istanbul-lib-instrument");
|
|
23869
|
+
const {
|
|
23870
|
+
types
|
|
23871
|
+
} = api;
|
|
23872
|
+
return {
|
|
23873
|
+
name: "transform-instrument",
|
|
23874
|
+
visitor: {
|
|
23875
|
+
Program: {
|
|
23876
|
+
enter(path) {
|
|
23877
|
+
const {
|
|
23878
|
+
file
|
|
23879
|
+
} = this;
|
|
23880
|
+
const {
|
|
23881
|
+
opts
|
|
23882
|
+
} = file;
|
|
23883
|
+
let inputSourceMap;
|
|
23884
|
+
if (useInlineSourceMaps) {
|
|
23885
|
+
// https://github.com/istanbuljs/babel-plugin-istanbul/commit/a9e15643d249a2985e4387e4308022053b2cd0ad#diff-1fdf421c05c1140f6d71444ea2b27638R65
|
|
23886
|
+
inputSourceMap = opts.inputSourceMap || file.inputMap ? file.inputMap.sourcemap : null;
|
|
23887
|
+
} else {
|
|
23888
|
+
inputSourceMap = opts.inputSourceMap;
|
|
23889
|
+
}
|
|
23890
|
+
this.__dv__ = programVisitor(types, opts.filenameRelative || opts.filename, {
|
|
23891
|
+
coverageVariable: "__coverage__",
|
|
23892
|
+
inputSourceMap
|
|
23893
|
+
});
|
|
23894
|
+
this.__dv__.enter(path);
|
|
23895
|
+
},
|
|
23896
|
+
exit(path) {
|
|
23897
|
+
if (!this.__dv__) {
|
|
23898
|
+
return;
|
|
23899
|
+
}
|
|
23900
|
+
const object = this.__dv__.exit(path);
|
|
23901
|
+
// object got two properties: fileCoverage and sourceMappingURL
|
|
23902
|
+
this.file.metadata.coverage = object.fileCoverage;
|
|
23903
|
+
}
|
|
23904
|
+
}
|
|
23905
|
+
}
|
|
23906
|
+
};
|
|
23907
|
+
};
|
|
23908
|
+
|
|
23914
23909
|
const relativeUrlToEmptyCoverage = async (relativeUrl, {
|
|
23915
23910
|
signal,
|
|
23916
23911
|
rootDirectoryUrl
|
|
@@ -24790,7 +24785,6 @@ const executeSteps = async (executionSteps, {
|
|
|
24790
24785
|
process.exitCode !== 1;
|
|
24791
24786
|
const startMs = Date.now();
|
|
24792
24787
|
let rawOutput = "";
|
|
24793
|
-
logger.info("");
|
|
24794
24788
|
let executionLog = createLog({
|
|
24795
24789
|
newLine: ""
|
|
24796
24790
|
});
|
|
@@ -25099,7 +25093,10 @@ const executeTestPlan = async ({
|
|
|
25099
25093
|
"file:///**/.*": false,
|
|
25100
25094
|
"file:///**/.*/": false,
|
|
25101
25095
|
"file:///**/node_modules/": false,
|
|
25102
|
-
"./**/src
|
|
25096
|
+
"./**/src/**/*.js": true,
|
|
25097
|
+
"./**/src/**/*.ts": true,
|
|
25098
|
+
"./**/src/**/*.jsx": true,
|
|
25099
|
+
"./**/src/**/*.tsx": true,
|
|
25103
25100
|
"./**/tests/": false,
|
|
25104
25101
|
"./**/*.test.html": false,
|
|
25105
25102
|
"./**/*.test.js": false,
|
|
@@ -25108,8 +25105,15 @@ const executeTestPlan = async ({
|
|
|
25108
25105
|
coverageIncludeMissing = true,
|
|
25109
25106
|
coverageAndExecutionAllowed = false,
|
|
25110
25107
|
coverageMethodForNodeJs = process.env.NODE_V8_COVERAGE ? "NODE_V8_COVERAGE" : "Profiler",
|
|
25111
|
-
|
|
25112
|
-
//
|
|
25108
|
+
// - When chromium only -> coverage generated by v8
|
|
25109
|
+
// - When chromium + node -> coverage generated by v8 are merged
|
|
25110
|
+
// - When firefox only -> coverage generated by babel+istanbul
|
|
25111
|
+
// - When chromium + firefox
|
|
25112
|
+
// -> by default only coverage from chromium is used
|
|
25113
|
+
// and a warning is logged according to coverageV8ConflictWarning
|
|
25114
|
+
// -> to collect coverage from both browsers, pass coverageMethodForBrowsers: "istanbul"
|
|
25115
|
+
coverageMethodForBrowsers,
|
|
25116
|
+
// undefined | "playwright" | "istanbul"
|
|
25113
25117
|
coverageV8ConflictWarning = true,
|
|
25114
25118
|
coverageTempDirectoryUrl,
|
|
25115
25119
|
// skip empty means empty files won't appear in the coverage reports (json and html)
|
|
@@ -25124,6 +25128,7 @@ const executeTestPlan = async ({
|
|
|
25124
25128
|
...rest
|
|
25125
25129
|
}) => {
|
|
25126
25130
|
let someNeedsServer = false;
|
|
25131
|
+
let someHasCoverageV8 = false;
|
|
25127
25132
|
let someNodeRuntime = false;
|
|
25128
25133
|
const runtimes = {};
|
|
25129
25134
|
// param validation
|
|
@@ -25150,6 +25155,9 @@ const executeTestPlan = async ({
|
|
|
25150
25155
|
if (runtime) {
|
|
25151
25156
|
runtimes[runtime.name] = runtime.version;
|
|
25152
25157
|
if (runtime.type === "browser") {
|
|
25158
|
+
if (runtime.capabilities && runtime.capabilities.coverageV8) {
|
|
25159
|
+
someHasCoverageV8 = true;
|
|
25160
|
+
}
|
|
25153
25161
|
someNeedsServer = true;
|
|
25154
25162
|
}
|
|
25155
25163
|
if (runtime.type === "node") {
|
|
@@ -25162,6 +25170,9 @@ const executeTestPlan = async ({
|
|
|
25162
25170
|
await assertAndNormalizeWebServer(webServer);
|
|
25163
25171
|
}
|
|
25164
25172
|
if (coverageEnabled) {
|
|
25173
|
+
if (coverageMethodForBrowsers === undefined) {
|
|
25174
|
+
coverageMethodForBrowsers = someHasCoverageV8 ? "playwright" : "istanbul";
|
|
25175
|
+
}
|
|
25165
25176
|
if (typeof coverageConfig !== "object") {
|
|
25166
25177
|
throw new TypeError(`coverageConfig must be an object, got ${coverageConfig}`);
|
|
25167
25178
|
}
|
|
@@ -25322,6 +25333,172 @@ const executeTestPlan = async ({
|
|
|
25322
25333
|
};
|
|
25323
25334
|
};
|
|
25324
25335
|
|
|
25336
|
+
const initJsSupervisorMiddleware = async (page, {
|
|
25337
|
+
webServer,
|
|
25338
|
+
fileUrl,
|
|
25339
|
+
fileServerUrl
|
|
25340
|
+
}) => {
|
|
25341
|
+
const inlineScriptContents = new Map();
|
|
25342
|
+
const interceptHtmlToExecute = async ({
|
|
25343
|
+
route
|
|
25344
|
+
}) => {
|
|
25345
|
+
const response = await route.fetch();
|
|
25346
|
+
const originalBody = await response.text();
|
|
25347
|
+
const injectionResult = await injectSupervisorIntoHTML({
|
|
25348
|
+
content: originalBody,
|
|
25349
|
+
url: fileUrl
|
|
25350
|
+
}, {
|
|
25351
|
+
supervisorScriptSrc: `/@fs/${supervisorFileUrl$1.slice("file:///".length)}`,
|
|
25352
|
+
supervisorOptions: {},
|
|
25353
|
+
inlineAsRemote: true,
|
|
25354
|
+
webServer,
|
|
25355
|
+
onInlineScript: ({
|
|
25356
|
+
src,
|
|
25357
|
+
textContent
|
|
25358
|
+
}) => {
|
|
25359
|
+
const inlineScriptWebUrl = new URL(src, `${webServer.origin}/`).href;
|
|
25360
|
+
inlineScriptContents.set(inlineScriptWebUrl, textContent);
|
|
25361
|
+
}
|
|
25362
|
+
});
|
|
25363
|
+
route.fulfill({
|
|
25364
|
+
response,
|
|
25365
|
+
body: injectionResult.content,
|
|
25366
|
+
headers: {
|
|
25367
|
+
...response.headers(),
|
|
25368
|
+
"content-length": Buffer.byteLength(injectionResult.content)
|
|
25369
|
+
}
|
|
25370
|
+
});
|
|
25371
|
+
};
|
|
25372
|
+
const interceptInlineScript = ({
|
|
25373
|
+
url,
|
|
25374
|
+
route
|
|
25375
|
+
}) => {
|
|
25376
|
+
const inlineScriptContent = inlineScriptContents.get(url);
|
|
25377
|
+
route.fulfill({
|
|
25378
|
+
status: 200,
|
|
25379
|
+
body: inlineScriptContent,
|
|
25380
|
+
headers: {
|
|
25381
|
+
"content-type": "text/javascript",
|
|
25382
|
+
"content-length": Buffer.byteLength(inlineScriptContent)
|
|
25383
|
+
}
|
|
25384
|
+
});
|
|
25385
|
+
};
|
|
25386
|
+
const interceptFileSystemUrl = ({
|
|
25387
|
+
url,
|
|
25388
|
+
route
|
|
25389
|
+
}) => {
|
|
25390
|
+
const relativeUrl = url.slice(webServer.origin.length);
|
|
25391
|
+
const fsPath = relativeUrl.slice("/@fs/".length);
|
|
25392
|
+
const fsUrl = `file:///${fsPath}`;
|
|
25393
|
+
const fileContent = readFileSync$1(new URL(fsUrl), "utf8");
|
|
25394
|
+
route.fulfill({
|
|
25395
|
+
status: 200,
|
|
25396
|
+
body: fileContent,
|
|
25397
|
+
headers: {
|
|
25398
|
+
"content-type": "text/javascript",
|
|
25399
|
+
"content-length": Buffer.byteLength(fileContent)
|
|
25400
|
+
}
|
|
25401
|
+
});
|
|
25402
|
+
};
|
|
25403
|
+
await page.route("**", async route => {
|
|
25404
|
+
const request = route.request();
|
|
25405
|
+
const url = request.url();
|
|
25406
|
+
if (url === fileServerUrl && urlToExtension$1(url) === ".html") {
|
|
25407
|
+
interceptHtmlToExecute({
|
|
25408
|
+
url,
|
|
25409
|
+
request,
|
|
25410
|
+
route
|
|
25411
|
+
});
|
|
25412
|
+
return;
|
|
25413
|
+
}
|
|
25414
|
+
if (inlineScriptContents.has(url)) {
|
|
25415
|
+
interceptInlineScript({
|
|
25416
|
+
url,
|
|
25417
|
+
request,
|
|
25418
|
+
route
|
|
25419
|
+
});
|
|
25420
|
+
return;
|
|
25421
|
+
}
|
|
25422
|
+
const fsServerUrl = new URL("/@fs/", webServer.origin);
|
|
25423
|
+
if (url.startsWith(fsServerUrl)) {
|
|
25424
|
+
interceptFileSystemUrl({
|
|
25425
|
+
url,
|
|
25426
|
+
request,
|
|
25427
|
+
route
|
|
25428
|
+
});
|
|
25429
|
+
return;
|
|
25430
|
+
}
|
|
25431
|
+
route.fallback();
|
|
25432
|
+
});
|
|
25433
|
+
};
|
|
25434
|
+
|
|
25435
|
+
const initIstanbulMiddleware = async (page, {
|
|
25436
|
+
webServer,
|
|
25437
|
+
rootDirectoryUrl,
|
|
25438
|
+
coverageConfig
|
|
25439
|
+
}) => {
|
|
25440
|
+
const associations = URL_META.resolveAssociations({
|
|
25441
|
+
cover: coverageConfig
|
|
25442
|
+
}, rootDirectoryUrl);
|
|
25443
|
+
await page.route("**", async route => {
|
|
25444
|
+
const request = route.request();
|
|
25445
|
+
const url = request.url(); // transform into a local url
|
|
25446
|
+
const fileUrl = moveUrl({
|
|
25447
|
+
url,
|
|
25448
|
+
from: `${webServer.origin}/`,
|
|
25449
|
+
to: rootDirectoryUrl
|
|
25450
|
+
});
|
|
25451
|
+
const needsInstrumentation = URL_META.applyAssociations({
|
|
25452
|
+
url: fileUrl,
|
|
25453
|
+
associations
|
|
25454
|
+
}).cover;
|
|
25455
|
+
if (!needsInstrumentation) {
|
|
25456
|
+
route.fallback();
|
|
25457
|
+
return;
|
|
25458
|
+
}
|
|
25459
|
+
const response = await route.fetch();
|
|
25460
|
+
const originalBody = await response.text();
|
|
25461
|
+
try {
|
|
25462
|
+
const result = await applyBabelPlugins({
|
|
25463
|
+
babelPlugins: [babelPluginInstrument],
|
|
25464
|
+
urlInfo: {
|
|
25465
|
+
originalUrl: fileUrl,
|
|
25466
|
+
// jsenv server could send info to know it's a js module or js classic
|
|
25467
|
+
// but in the end it's not super important
|
|
25468
|
+
// - it's ok to parse js classic as js module considering it's only for istanbul instrumentation
|
|
25469
|
+
type: "js_module",
|
|
25470
|
+
content: originalBody
|
|
25471
|
+
}
|
|
25472
|
+
});
|
|
25473
|
+
let code = result.code;
|
|
25474
|
+
code = SOURCEMAP.writeComment({
|
|
25475
|
+
contentType: "text/javascript",
|
|
25476
|
+
content: code,
|
|
25477
|
+
specifier: generateSourcemapDataUrl(result.map)
|
|
25478
|
+
});
|
|
25479
|
+
route.fulfill({
|
|
25480
|
+
response,
|
|
25481
|
+
body: code,
|
|
25482
|
+
headers: {
|
|
25483
|
+
...response.headers(),
|
|
25484
|
+
"content-length": Buffer.byteLength(code)
|
|
25485
|
+
}
|
|
25486
|
+
});
|
|
25487
|
+
} catch (e) {
|
|
25488
|
+
if (e.code === "PARSE_ERROR") {
|
|
25489
|
+
route.fulfill({
|
|
25490
|
+
response
|
|
25491
|
+
});
|
|
25492
|
+
} else {
|
|
25493
|
+
console.error(e);
|
|
25494
|
+
route.fulfill({
|
|
25495
|
+
response
|
|
25496
|
+
});
|
|
25497
|
+
}
|
|
25498
|
+
}
|
|
25499
|
+
});
|
|
25500
|
+
};
|
|
25501
|
+
|
|
25325
25502
|
const createRuntimeFromPlaywright = ({
|
|
25326
25503
|
browserName,
|
|
25327
25504
|
browserVersion,
|
|
@@ -25333,7 +25510,10 @@ const createRuntimeFromPlaywright = ({
|
|
|
25333
25510
|
const runtime = {
|
|
25334
25511
|
type: "browser",
|
|
25335
25512
|
name: browserName,
|
|
25336
|
-
version: browserVersion
|
|
25513
|
+
version: browserVersion,
|
|
25514
|
+
capabilities: {
|
|
25515
|
+
coverageV8: coveragePlaywrightAPIAvailable
|
|
25516
|
+
}
|
|
25337
25517
|
};
|
|
25338
25518
|
let browserAndContextPromise;
|
|
25339
25519
|
runtime.run = async ({
|
|
@@ -25424,16 +25604,17 @@ ${webServer.rootDirectoryUrl}`);
|
|
|
25424
25604
|
}
|
|
25425
25605
|
await disconnected;
|
|
25426
25606
|
};
|
|
25427
|
-
const
|
|
25428
|
-
const
|
|
25429
|
-
|
|
25430
|
-
|
|
25431
|
-
|
|
25432
|
-
|
|
25433
|
-
|
|
25434
|
-
|
|
25607
|
+
const page = await browserContext.newPage();
|
|
25608
|
+
const istanbulInstrumentationEnabled = coverageEnabled && (!runtime.capabilities.coverageV8 || coverageMethodForBrowsers === "istanbul");
|
|
25609
|
+
if (istanbulInstrumentationEnabled) {
|
|
25610
|
+
await initIstanbulMiddleware(page, {
|
|
25611
|
+
webServer,
|
|
25612
|
+
rootDirectoryUrl,
|
|
25613
|
+
coverageConfig
|
|
25614
|
+
});
|
|
25615
|
+
}
|
|
25435
25616
|
if (!webServer.isJsenvDevServer) {
|
|
25436
|
-
await
|
|
25617
|
+
await initJsSupervisorMiddleware(page, {
|
|
25437
25618
|
webServer,
|
|
25438
25619
|
fileUrl,
|
|
25439
25620
|
fileServerUrl
|
|
@@ -25456,7 +25637,7 @@ ${webServer.rootDirectoryUrl}`);
|
|
|
25456
25637
|
};
|
|
25457
25638
|
const callbacks = [];
|
|
25458
25639
|
if (coverageEnabled) {
|
|
25459
|
-
if (
|
|
25640
|
+
if (runtime.capabilities.coverageV8 && coverageMethodForBrowsers === "playwright") {
|
|
25460
25641
|
await page.coverage.startJSCoverage({
|
|
25461
25642
|
// reportAnonymousScripts: true,
|
|
25462
25643
|
});
|
|
@@ -25838,106 +26019,6 @@ const extractTextFromConsoleMessage = consoleMessage => {
|
|
|
25838
26019
|
// return text
|
|
25839
26020
|
};
|
|
25840
26021
|
|
|
25841
|
-
const initJsExecutionMiddleware = async (page, {
|
|
25842
|
-
webServer,
|
|
25843
|
-
fileUrl,
|
|
25844
|
-
fileServerUrl
|
|
25845
|
-
}) => {
|
|
25846
|
-
const inlineScriptContents = new Map();
|
|
25847
|
-
const interceptHtmlToExecute = async ({
|
|
25848
|
-
route
|
|
25849
|
-
}) => {
|
|
25850
|
-
// Fetch original response.
|
|
25851
|
-
const response = await route.fetch();
|
|
25852
|
-
// Add a prefix to the title.
|
|
25853
|
-
const originalBody = await response.text();
|
|
25854
|
-
const injectionResult = await injectSupervisorIntoHTML({
|
|
25855
|
-
content: originalBody,
|
|
25856
|
-
url: fileUrl
|
|
25857
|
-
}, {
|
|
25858
|
-
supervisorScriptSrc: `/@fs/${supervisorFileUrl$1.slice("file:///".length)}`,
|
|
25859
|
-
supervisorOptions: {},
|
|
25860
|
-
inlineAsRemote: true,
|
|
25861
|
-
webServer,
|
|
25862
|
-
onInlineScript: ({
|
|
25863
|
-
src,
|
|
25864
|
-
textContent
|
|
25865
|
-
}) => {
|
|
25866
|
-
const inlineScriptWebUrl = new URL(src, `${webServer.origin}/`).href;
|
|
25867
|
-
inlineScriptContents.set(inlineScriptWebUrl, textContent);
|
|
25868
|
-
}
|
|
25869
|
-
});
|
|
25870
|
-
route.fulfill({
|
|
25871
|
-
response,
|
|
25872
|
-
body: injectionResult.content,
|
|
25873
|
-
headers: {
|
|
25874
|
-
...response.headers(),
|
|
25875
|
-
"content-length": Buffer.byteLength(injectionResult.content)
|
|
25876
|
-
}
|
|
25877
|
-
});
|
|
25878
|
-
};
|
|
25879
|
-
const interceptInlineScript = ({
|
|
25880
|
-
url,
|
|
25881
|
-
route
|
|
25882
|
-
}) => {
|
|
25883
|
-
const inlineScriptContent = inlineScriptContents.get(url);
|
|
25884
|
-
route.fulfill({
|
|
25885
|
-
status: 200,
|
|
25886
|
-
body: inlineScriptContent,
|
|
25887
|
-
headers: {
|
|
25888
|
-
"content-type": "text/javascript",
|
|
25889
|
-
"content-length": Buffer.byteLength(inlineScriptContent)
|
|
25890
|
-
}
|
|
25891
|
-
});
|
|
25892
|
-
};
|
|
25893
|
-
const interceptFileSystemUrl = ({
|
|
25894
|
-
url,
|
|
25895
|
-
route
|
|
25896
|
-
}) => {
|
|
25897
|
-
const relativeUrl = url.slice(webServer.origin.length);
|
|
25898
|
-
const fsPath = relativeUrl.slice("/@fs/".length);
|
|
25899
|
-
const fsUrl = `file:///${fsPath}`;
|
|
25900
|
-
const fileContent = readFileSync$1(new URL(fsUrl), "utf8");
|
|
25901
|
-
route.fulfill({
|
|
25902
|
-
status: 200,
|
|
25903
|
-
body: fileContent,
|
|
25904
|
-
headers: {
|
|
25905
|
-
"content-type": "text/javascript",
|
|
25906
|
-
"content-length": Buffer.byteLength(fileContent)
|
|
25907
|
-
}
|
|
25908
|
-
});
|
|
25909
|
-
};
|
|
25910
|
-
await page.route("**", async route => {
|
|
25911
|
-
const request = route.request();
|
|
25912
|
-
const url = request.url();
|
|
25913
|
-
if (url === fileServerUrl && urlToExtension$1(url) === ".html") {
|
|
25914
|
-
interceptHtmlToExecute({
|
|
25915
|
-
url,
|
|
25916
|
-
request,
|
|
25917
|
-
route
|
|
25918
|
-
});
|
|
25919
|
-
return;
|
|
25920
|
-
}
|
|
25921
|
-
if (inlineScriptContents.has(url)) {
|
|
25922
|
-
interceptInlineScript({
|
|
25923
|
-
url,
|
|
25924
|
-
request,
|
|
25925
|
-
route
|
|
25926
|
-
});
|
|
25927
|
-
return;
|
|
25928
|
-
}
|
|
25929
|
-
const fsServerUrl = new URL("/@fs/", webServer.origin);
|
|
25930
|
-
if (url.startsWith(fsServerUrl)) {
|
|
25931
|
-
interceptFileSystemUrl({
|
|
25932
|
-
url,
|
|
25933
|
-
request,
|
|
25934
|
-
route
|
|
25935
|
-
});
|
|
25936
|
-
return;
|
|
25937
|
-
}
|
|
25938
|
-
route.fallback();
|
|
25939
|
-
});
|
|
25940
|
-
};
|
|
25941
26022
|
const registerEvent = ({
|
|
25942
26023
|
object,
|
|
25943
26024
|
eventType,
|
package/package.json
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { writeFileSync } from "node:fs"
|
|
3
2
|
import { createDetailedMessage } from "@jsenv/log"
|
|
4
3
|
import {
|
|
5
4
|
Abort,
|
|
@@ -7,15 +6,13 @@ import {
|
|
|
7
6
|
raceProcessTeardownEvents,
|
|
8
7
|
raceCallbacks,
|
|
9
8
|
} from "@jsenv/abort"
|
|
10
|
-
import { moveUrl, urlIsInsideOf
|
|
9
|
+
import { moveUrl, urlIsInsideOf } from "@jsenv/urls"
|
|
11
10
|
import { memoize } from "@jsenv/utils/src/memoize/memoize.js"
|
|
12
11
|
|
|
12
|
+
import { initJsSupervisorMiddleware } from "./middleware_js_supervisor.js"
|
|
13
|
+
import { initIstanbulMiddleware } from "./middleware_istanbul.js"
|
|
13
14
|
import { filterV8Coverage } from "@jsenv/core/src/test/coverage/v8_coverage.js"
|
|
14
15
|
import { composeTwoFileByFileIstanbulCoverages } from "@jsenv/core/src/test/coverage/istanbul_coverage_composition.js"
|
|
15
|
-
import {
|
|
16
|
-
injectSupervisorIntoHTML,
|
|
17
|
-
supervisorFileUrl,
|
|
18
|
-
} from "../../../plugins/supervisor/html_supervisor_injection.js"
|
|
19
16
|
|
|
20
17
|
export const createRuntimeFromPlaywright = ({
|
|
21
18
|
browserName,
|
|
@@ -29,6 +26,9 @@ export const createRuntimeFromPlaywright = ({
|
|
|
29
26
|
type: "browser",
|
|
30
27
|
name: browserName,
|
|
31
28
|
version: browserVersion,
|
|
29
|
+
capabilities: {
|
|
30
|
+
coverageV8: coveragePlaywrightAPIAvailable,
|
|
31
|
+
},
|
|
32
32
|
}
|
|
33
33
|
let browserAndContextPromise
|
|
34
34
|
runtime.run = async ({
|
|
@@ -116,21 +116,22 @@ ${webServer.rootDirectoryUrl}`)
|
|
|
116
116
|
}
|
|
117
117
|
await disconnected
|
|
118
118
|
}
|
|
119
|
-
|
|
119
|
+
|
|
120
|
+
const page = await browserContext.newPage()
|
|
121
|
+
|
|
122
|
+
const istanbulInstrumentationEnabled =
|
|
120
123
|
coverageEnabled &&
|
|
121
|
-
(!
|
|
122
|
-
coverageMethodForBrowsers
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
},
|
|
131
|
-
})
|
|
124
|
+
(!runtime.capabilities.coverageV8 ||
|
|
125
|
+
coverageMethodForBrowsers === "istanbul")
|
|
126
|
+
if (istanbulInstrumentationEnabled) {
|
|
127
|
+
await initIstanbulMiddleware(page, {
|
|
128
|
+
webServer,
|
|
129
|
+
rootDirectoryUrl,
|
|
130
|
+
coverageConfig,
|
|
131
|
+
})
|
|
132
|
+
}
|
|
132
133
|
if (!webServer.isJsenvDevServer) {
|
|
133
|
-
await
|
|
134
|
+
await initJsSupervisorMiddleware(page, {
|
|
134
135
|
webServer,
|
|
135
136
|
fileUrl,
|
|
136
137
|
fileServerUrl,
|
|
@@ -155,8 +156,8 @@ ${webServer.rootDirectoryUrl}`)
|
|
|
155
156
|
const callbacks = []
|
|
156
157
|
if (coverageEnabled) {
|
|
157
158
|
if (
|
|
158
|
-
|
|
159
|
-
coverageMethodForBrowsers === "
|
|
159
|
+
runtime.capabilities.coverageV8 &&
|
|
160
|
+
coverageMethodForBrowsers === "playwright"
|
|
160
161
|
) {
|
|
161
162
|
await page.coverage.startJSCoverage({
|
|
162
163
|
// reportAnonymousScripts: true,
|
|
@@ -570,100 +571,6 @@ const extractTextFromConsoleMessage = (consoleMessage) => {
|
|
|
570
571
|
// return text
|
|
571
572
|
}
|
|
572
573
|
|
|
573
|
-
const initJsExecutionMiddleware = async (
|
|
574
|
-
page,
|
|
575
|
-
{ webServer, fileUrl, fileServerUrl },
|
|
576
|
-
) => {
|
|
577
|
-
const inlineScriptContents = new Map()
|
|
578
|
-
|
|
579
|
-
const interceptHtmlToExecute = async ({ route }) => {
|
|
580
|
-
// Fetch original response.
|
|
581
|
-
const response = await route.fetch()
|
|
582
|
-
// Add a prefix to the title.
|
|
583
|
-
const originalBody = await response.text()
|
|
584
|
-
const injectionResult = await injectSupervisorIntoHTML(
|
|
585
|
-
{
|
|
586
|
-
content: originalBody,
|
|
587
|
-
url: fileUrl,
|
|
588
|
-
},
|
|
589
|
-
{
|
|
590
|
-
supervisorScriptSrc: `/@fs/${supervisorFileUrl.slice(
|
|
591
|
-
"file:///".length,
|
|
592
|
-
)}`,
|
|
593
|
-
supervisorOptions: {},
|
|
594
|
-
inlineAsRemote: true,
|
|
595
|
-
webServer,
|
|
596
|
-
onInlineScript: ({ src, textContent }) => {
|
|
597
|
-
const inlineScriptWebUrl = new URL(src, `${webServer.origin}/`).href
|
|
598
|
-
inlineScriptContents.set(inlineScriptWebUrl, textContent)
|
|
599
|
-
},
|
|
600
|
-
},
|
|
601
|
-
)
|
|
602
|
-
route.fulfill({
|
|
603
|
-
response,
|
|
604
|
-
body: injectionResult.content,
|
|
605
|
-
headers: {
|
|
606
|
-
...response.headers(),
|
|
607
|
-
"content-length": Buffer.byteLength(injectionResult.content),
|
|
608
|
-
},
|
|
609
|
-
})
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
const interceptInlineScript = ({ url, route }) => {
|
|
613
|
-
const inlineScriptContent = inlineScriptContents.get(url)
|
|
614
|
-
route.fulfill({
|
|
615
|
-
status: 200,
|
|
616
|
-
body: inlineScriptContent,
|
|
617
|
-
headers: {
|
|
618
|
-
"content-type": "text/javascript",
|
|
619
|
-
"content-length": Buffer.byteLength(inlineScriptContent),
|
|
620
|
-
},
|
|
621
|
-
})
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
const interceptFileSystemUrl = ({ url, route }) => {
|
|
625
|
-
const relativeUrl = url.slice(webServer.origin.length)
|
|
626
|
-
const fsPath = relativeUrl.slice("/@fs/".length)
|
|
627
|
-
const fsUrl = `file:///${fsPath}`
|
|
628
|
-
const fileContent = readFileSync(new URL(fsUrl), "utf8")
|
|
629
|
-
route.fulfill({
|
|
630
|
-
status: 200,
|
|
631
|
-
body: fileContent,
|
|
632
|
-
headers: {
|
|
633
|
-
"content-type": "text/javascript",
|
|
634
|
-
"content-length": Buffer.byteLength(fileContent),
|
|
635
|
-
},
|
|
636
|
-
})
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
await page.route("**", async (route) => {
|
|
640
|
-
const request = route.request()
|
|
641
|
-
const url = request.url()
|
|
642
|
-
if (url === fileServerUrl && urlToExtension(url) === ".html") {
|
|
643
|
-
interceptHtmlToExecute({
|
|
644
|
-
url,
|
|
645
|
-
request,
|
|
646
|
-
route,
|
|
647
|
-
})
|
|
648
|
-
return
|
|
649
|
-
}
|
|
650
|
-
if (inlineScriptContents.has(url)) {
|
|
651
|
-
interceptInlineScript({
|
|
652
|
-
url,
|
|
653
|
-
request,
|
|
654
|
-
route,
|
|
655
|
-
})
|
|
656
|
-
return
|
|
657
|
-
}
|
|
658
|
-
const fsServerUrl = new URL("/@fs/", webServer.origin)
|
|
659
|
-
if (url.startsWith(fsServerUrl)) {
|
|
660
|
-
interceptFileSystemUrl({ url, request, route })
|
|
661
|
-
return
|
|
662
|
-
}
|
|
663
|
-
route.fallback()
|
|
664
|
-
})
|
|
665
|
-
}
|
|
666
|
-
|
|
667
574
|
const registerEvent = ({ object, eventType, callback }) => {
|
|
668
575
|
object.on(eventType, callback)
|
|
669
576
|
return () => {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { URL_META } from "@jsenv/url-meta"
|
|
2
|
+
import { moveUrl } from "@jsenv/urls"
|
|
3
|
+
import { applyBabelPlugins } from "@jsenv/ast"
|
|
4
|
+
import { SOURCEMAP, generateSourcemapDataUrl } from "@jsenv/sourcemap"
|
|
5
|
+
|
|
6
|
+
import { babelPluginInstrument } from "../../../test/coverage/babel_plugin_instrument.js"
|
|
7
|
+
|
|
8
|
+
export const initIstanbulMiddleware = async (
|
|
9
|
+
page,
|
|
10
|
+
{ webServer, rootDirectoryUrl, coverageConfig },
|
|
11
|
+
) => {
|
|
12
|
+
const associations = URL_META.resolveAssociations(
|
|
13
|
+
{ cover: coverageConfig },
|
|
14
|
+
rootDirectoryUrl,
|
|
15
|
+
)
|
|
16
|
+
await page.route("**", async (route) => {
|
|
17
|
+
const request = route.request()
|
|
18
|
+
const url = request.url() // transform into a local url
|
|
19
|
+
const fileUrl = moveUrl({
|
|
20
|
+
url,
|
|
21
|
+
from: `${webServer.origin}/`,
|
|
22
|
+
to: rootDirectoryUrl,
|
|
23
|
+
})
|
|
24
|
+
const needsInstrumentation = URL_META.applyAssociations({
|
|
25
|
+
url: fileUrl,
|
|
26
|
+
associations,
|
|
27
|
+
}).cover
|
|
28
|
+
if (!needsInstrumentation) {
|
|
29
|
+
route.fallback()
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
const response = await route.fetch()
|
|
33
|
+
const originalBody = await response.text()
|
|
34
|
+
try {
|
|
35
|
+
const result = await applyBabelPlugins({
|
|
36
|
+
babelPlugins: [babelPluginInstrument],
|
|
37
|
+
urlInfo: {
|
|
38
|
+
originalUrl: fileUrl,
|
|
39
|
+
// jsenv server could send info to know it's a js module or js classic
|
|
40
|
+
// but in the end it's not super important
|
|
41
|
+
// - it's ok to parse js classic as js module considering it's only for istanbul instrumentation
|
|
42
|
+
type: "js_module",
|
|
43
|
+
content: originalBody,
|
|
44
|
+
},
|
|
45
|
+
})
|
|
46
|
+
let code = result.code
|
|
47
|
+
code = SOURCEMAP.writeComment({
|
|
48
|
+
contentType: "text/javascript",
|
|
49
|
+
content: code,
|
|
50
|
+
specifier: generateSourcemapDataUrl(result.map),
|
|
51
|
+
})
|
|
52
|
+
route.fulfill({
|
|
53
|
+
response,
|
|
54
|
+
body: code,
|
|
55
|
+
headers: {
|
|
56
|
+
...response.headers(),
|
|
57
|
+
"content-length": Buffer.byteLength(code),
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
} catch (e) {
|
|
61
|
+
if (e.code === "PARSE_ERROR") {
|
|
62
|
+
route.fulfill({ response })
|
|
63
|
+
} else {
|
|
64
|
+
console.error(e)
|
|
65
|
+
route.fulfill({ response })
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs"
|
|
2
|
+
|
|
3
|
+
import { urlToExtension } from "@jsenv/urls"
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
injectSupervisorIntoHTML,
|
|
7
|
+
supervisorFileUrl,
|
|
8
|
+
} from "../../../plugins/supervisor/html_supervisor_injection.js"
|
|
9
|
+
|
|
10
|
+
export const initJsSupervisorMiddleware = async (
|
|
11
|
+
page,
|
|
12
|
+
{ webServer, fileUrl, fileServerUrl },
|
|
13
|
+
) => {
|
|
14
|
+
const inlineScriptContents = new Map()
|
|
15
|
+
|
|
16
|
+
const interceptHtmlToExecute = async ({ route }) => {
|
|
17
|
+
const response = await route.fetch()
|
|
18
|
+
const originalBody = await response.text()
|
|
19
|
+
const injectionResult = await injectSupervisorIntoHTML(
|
|
20
|
+
{
|
|
21
|
+
content: originalBody,
|
|
22
|
+
url: fileUrl,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
supervisorScriptSrc: `/@fs/${supervisorFileUrl.slice(
|
|
26
|
+
"file:///".length,
|
|
27
|
+
)}`,
|
|
28
|
+
supervisorOptions: {},
|
|
29
|
+
inlineAsRemote: true,
|
|
30
|
+
webServer,
|
|
31
|
+
onInlineScript: ({ src, textContent }) => {
|
|
32
|
+
const inlineScriptWebUrl = new URL(src, `${webServer.origin}/`).href
|
|
33
|
+
inlineScriptContents.set(inlineScriptWebUrl, textContent)
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
)
|
|
37
|
+
route.fulfill({
|
|
38
|
+
response,
|
|
39
|
+
body: injectionResult.content,
|
|
40
|
+
headers: {
|
|
41
|
+
...response.headers(),
|
|
42
|
+
"content-length": Buffer.byteLength(injectionResult.content),
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const interceptInlineScript = ({ url, route }) => {
|
|
48
|
+
const inlineScriptContent = inlineScriptContents.get(url)
|
|
49
|
+
route.fulfill({
|
|
50
|
+
status: 200,
|
|
51
|
+
body: inlineScriptContent,
|
|
52
|
+
headers: {
|
|
53
|
+
"content-type": "text/javascript",
|
|
54
|
+
"content-length": Buffer.byteLength(inlineScriptContent),
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const interceptFileSystemUrl = ({ url, route }) => {
|
|
60
|
+
const relativeUrl = url.slice(webServer.origin.length)
|
|
61
|
+
const fsPath = relativeUrl.slice("/@fs/".length)
|
|
62
|
+
const fsUrl = `file:///${fsPath}`
|
|
63
|
+
const fileContent = readFileSync(new URL(fsUrl), "utf8")
|
|
64
|
+
route.fulfill({
|
|
65
|
+
status: 200,
|
|
66
|
+
body: fileContent,
|
|
67
|
+
headers: {
|
|
68
|
+
"content-type": "text/javascript",
|
|
69
|
+
"content-length": Buffer.byteLength(fileContent),
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await page.route("**", async (route) => {
|
|
75
|
+
const request = route.request()
|
|
76
|
+
const url = request.url()
|
|
77
|
+
if (url === fileServerUrl && urlToExtension(url) === ".html") {
|
|
78
|
+
interceptHtmlToExecute({
|
|
79
|
+
url,
|
|
80
|
+
request,
|
|
81
|
+
route,
|
|
82
|
+
})
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
if (inlineScriptContents.has(url)) {
|
|
86
|
+
interceptInlineScript({
|
|
87
|
+
url,
|
|
88
|
+
request,
|
|
89
|
+
route,
|
|
90
|
+
})
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
const fsServerUrl = new URL("/@fs/", webServer.origin)
|
|
94
|
+
if (url.startsWith(fsServerUrl)) {
|
|
95
|
+
interceptFileSystemUrl({ url, request, route })
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
route.fallback()
|
|
99
|
+
})
|
|
100
|
+
}
|
package/src/kitchen/kitchen.js
CHANGED
|
@@ -291,6 +291,7 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
291
291
|
url: urlInfo.url,
|
|
292
292
|
},
|
|
293
293
|
type: "sourcemap_comment",
|
|
294
|
+
expectedType: "sourcemap",
|
|
294
295
|
subtype: urlInfo.contentType === "text/javascript" ? "js" : "css",
|
|
295
296
|
parentUrl: urlInfo.url,
|
|
296
297
|
specifier,
|
|
@@ -316,12 +317,16 @@ ${ANSI.color(normalizedReturnValue, ANSI.YELLOW)}
|
|
|
316
317
|
createReference({
|
|
317
318
|
trace: traceFromUrlSite(sourcemapUrlSite),
|
|
318
319
|
type,
|
|
320
|
+
expectedType: "sourcemap",
|
|
319
321
|
parentUrl: urlInfo.url,
|
|
320
322
|
specifier,
|
|
321
323
|
specifierLine,
|
|
322
324
|
specifierColumn,
|
|
323
325
|
}),
|
|
324
326
|
)
|
|
327
|
+
if (sourcemapReference.isInline) {
|
|
328
|
+
sourcemapUrlInfo.isInline = true
|
|
329
|
+
}
|
|
325
330
|
sourcemapUrlInfo.type = "sourcemap"
|
|
326
331
|
return [sourcemapReference, sourcemapUrlInfo]
|
|
327
332
|
},
|
|
@@ -913,6 +918,10 @@ const adjustUrlSite = (urlInfo, { urlGraph, url, line, column }) => {
|
|
|
913
918
|
}
|
|
914
919
|
|
|
915
920
|
const inferUrlInfoType = (urlInfo) => {
|
|
921
|
+
const { type } = urlInfo
|
|
922
|
+
if (type === "sourcemap") {
|
|
923
|
+
return "sourcemap"
|
|
924
|
+
}
|
|
916
925
|
const { contentType } = urlInfo
|
|
917
926
|
if (contentType === "text/html") {
|
|
918
927
|
return "html"
|
|
@@ -123,7 +123,6 @@ export const createUrlInfoTransformer = ({
|
|
|
123
123
|
})
|
|
124
124
|
try {
|
|
125
125
|
await context.cook(sourcemapUrlInfo, { reference: sourcemapReference })
|
|
126
|
-
sourcemapUrlInfo.isInline = sourcemaps === "inline"
|
|
127
126
|
const sourcemapRaw = JSON.parse(sourcemapUrlInfo.content)
|
|
128
127
|
const sourcemap = normalizeSourcemap(urlInfo, sourcemapRaw)
|
|
129
128
|
urlInfo.sourcemap = sourcemap
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { applyBabelPlugins } from "@jsenv/ast"
|
|
2
|
-
import { URL_META } from "@jsenv/url-meta"
|
|
3
2
|
|
|
4
|
-
import { babelPluginInstrument } from "@jsenv/core/src/test/coverage/babel_plugin_instrument.js"
|
|
5
3
|
import { RUNTIME_COMPAT } from "@jsenv/core/src/kitchen/compat/runtime_compat.js"
|
|
6
4
|
import { getBaseBabelPluginStructure } from "./helpers/babel_plugin_structure.js"
|
|
7
5
|
import { babelPluginBabelHelpersAsJsenvImports } from "./helpers/babel_plugin_babel_helpers_as_jsenv_imports.js"
|
|
@@ -33,23 +31,6 @@ export const jsenvPluginBabel = ({
|
|
|
33
31
|
isJsModule,
|
|
34
32
|
getImportSpecifier,
|
|
35
33
|
})
|
|
36
|
-
if (context.dev) {
|
|
37
|
-
const requestHeaders = context.request.headers
|
|
38
|
-
if (requestHeaders["x-coverage-instanbul"]) {
|
|
39
|
-
const coverageConfig = JSON.parse(
|
|
40
|
-
requestHeaders["x-coverage-instanbul"],
|
|
41
|
-
)
|
|
42
|
-
const associations = URL_META.resolveAssociations(
|
|
43
|
-
{ cover: coverageConfig },
|
|
44
|
-
context.rootDirectoryUrl,
|
|
45
|
-
)
|
|
46
|
-
if (
|
|
47
|
-
URL_META.applyAssociations({ url: urlInfo.url, associations }).cover
|
|
48
|
-
) {
|
|
49
|
-
babelPluginStructure["transform-instrument"] = [babelPluginInstrument]
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
34
|
if (getCustomBabelPlugins) {
|
|
54
35
|
Object.assign(babelPluginStructure, getCustomBabelPlugins(context))
|
|
55
36
|
}
|
|
@@ -67,7 +67,10 @@ export const executeTestPlan = async ({
|
|
|
67
67
|
"file:///**/.*": false,
|
|
68
68
|
"file:///**/.*/": false,
|
|
69
69
|
"file:///**/node_modules/": false,
|
|
70
|
-
"./**/src
|
|
70
|
+
"./**/src/**/*.js": true,
|
|
71
|
+
"./**/src/**/*.ts": true,
|
|
72
|
+
"./**/src/**/*.jsx": true,
|
|
73
|
+
"./**/src/**/*.tsx": true,
|
|
71
74
|
"./**/tests/": false,
|
|
72
75
|
"./**/*.test.html": false,
|
|
73
76
|
"./**/*.test.js": false,
|
|
@@ -78,7 +81,14 @@ export const executeTestPlan = async ({
|
|
|
78
81
|
coverageMethodForNodeJs = process.env.NODE_V8_COVERAGE
|
|
79
82
|
? "NODE_V8_COVERAGE"
|
|
80
83
|
: "Profiler",
|
|
81
|
-
|
|
84
|
+
// - When chromium only -> coverage generated by v8
|
|
85
|
+
// - When chromium + node -> coverage generated by v8 are merged
|
|
86
|
+
// - When firefox only -> coverage generated by babel+istanbul
|
|
87
|
+
// - When chromium + firefox
|
|
88
|
+
// -> by default only coverage from chromium is used
|
|
89
|
+
// and a warning is logged according to coverageV8ConflictWarning
|
|
90
|
+
// -> to collect coverage from both browsers, pass coverageMethodForBrowsers: "istanbul"
|
|
91
|
+
coverageMethodForBrowsers, // undefined | "playwright" | "istanbul"
|
|
82
92
|
coverageV8ConflictWarning = true,
|
|
83
93
|
coverageTempDirectoryUrl,
|
|
84
94
|
// skip empty means empty files won't appear in the coverage reports (json and html)
|
|
@@ -93,6 +103,7 @@ export const executeTestPlan = async ({
|
|
|
93
103
|
...rest
|
|
94
104
|
}) => {
|
|
95
105
|
let someNeedsServer = false
|
|
106
|
+
let someHasCoverageV8 = false
|
|
96
107
|
let someNodeRuntime = false
|
|
97
108
|
const runtimes = {}
|
|
98
109
|
// param validation
|
|
@@ -123,6 +134,9 @@ export const executeTestPlan = async ({
|
|
|
123
134
|
if (runtime) {
|
|
124
135
|
runtimes[runtime.name] = runtime.version
|
|
125
136
|
if (runtime.type === "browser") {
|
|
137
|
+
if (runtime.capabilities && runtime.capabilities.coverageV8) {
|
|
138
|
+
someHasCoverageV8 = true
|
|
139
|
+
}
|
|
126
140
|
someNeedsServer = true
|
|
127
141
|
}
|
|
128
142
|
if (runtime.type === "node") {
|
|
@@ -137,6 +151,11 @@ export const executeTestPlan = async ({
|
|
|
137
151
|
}
|
|
138
152
|
|
|
139
153
|
if (coverageEnabled) {
|
|
154
|
+
if (coverageMethodForBrowsers === undefined) {
|
|
155
|
+
coverageMethodForBrowsers = someHasCoverageV8
|
|
156
|
+
? "playwright"
|
|
157
|
+
: "istanbul"
|
|
158
|
+
}
|
|
140
159
|
if (typeof coverageConfig !== "object") {
|
|
141
160
|
throw new TypeError(
|
|
142
161
|
`coverageConfig must be an object, got ${coverageConfig}`,
|