@jsenv/core 34.0.3 → 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/dist/jsenv.js +249 -178
- 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/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/dist/jsenv.js
CHANGED
|
@@ -18947,54 +18947,6 @@ const splitFileExtension$1 = filename => {
|
|
|
18947
18947
|
return [filename.slice(0, dotLastIndex), filename.slice(dotLastIndex)];
|
|
18948
18948
|
};
|
|
18949
18949
|
|
|
18950
|
-
// https://github.com/istanbuljs/babel-plugin-istanbul/blob/321740f7b25d803f881466ea819d870f7ed6a254/src/index.js
|
|
18951
|
-
|
|
18952
|
-
const babelPluginInstrument = (api, {
|
|
18953
|
-
useInlineSourceMaps = false
|
|
18954
|
-
}) => {
|
|
18955
|
-
const {
|
|
18956
|
-
programVisitor
|
|
18957
|
-
} = requireFromJsenv("istanbul-lib-instrument");
|
|
18958
|
-
const {
|
|
18959
|
-
types
|
|
18960
|
-
} = api;
|
|
18961
|
-
return {
|
|
18962
|
-
name: "transform-instrument",
|
|
18963
|
-
visitor: {
|
|
18964
|
-
Program: {
|
|
18965
|
-
enter(path) {
|
|
18966
|
-
const {
|
|
18967
|
-
file
|
|
18968
|
-
} = this;
|
|
18969
|
-
const {
|
|
18970
|
-
opts
|
|
18971
|
-
} = file;
|
|
18972
|
-
let inputSourceMap;
|
|
18973
|
-
if (useInlineSourceMaps) {
|
|
18974
|
-
// https://github.com/istanbuljs/babel-plugin-istanbul/commit/a9e15643d249a2985e4387e4308022053b2cd0ad#diff-1fdf421c05c1140f6d71444ea2b27638R65
|
|
18975
|
-
inputSourceMap = opts.inputSourceMap || file.inputMap ? file.inputMap.sourcemap : null;
|
|
18976
|
-
} else {
|
|
18977
|
-
inputSourceMap = opts.inputSourceMap;
|
|
18978
|
-
}
|
|
18979
|
-
this.__dv__ = programVisitor(types, opts.filenameRelative || opts.filename, {
|
|
18980
|
-
coverageVariable: "__coverage__",
|
|
18981
|
-
inputSourceMap
|
|
18982
|
-
});
|
|
18983
|
-
this.__dv__.enter(path);
|
|
18984
|
-
},
|
|
18985
|
-
exit(path) {
|
|
18986
|
-
if (!this.__dv__) {
|
|
18987
|
-
return;
|
|
18988
|
-
}
|
|
18989
|
-
const object = this.__dv__.exit(path);
|
|
18990
|
-
// object got two properties: fileCoverage and sourceMappingURL
|
|
18991
|
-
this.file.metadata.coverage = object.fileCoverage;
|
|
18992
|
-
}
|
|
18993
|
-
}
|
|
18994
|
-
}
|
|
18995
|
-
};
|
|
18996
|
-
};
|
|
18997
|
-
|
|
18998
18950
|
/*
|
|
18999
18951
|
* Generated helpers
|
|
19000
18952
|
* - https://github.com/babel/babel/commits/main/packages/babel-helpers/src/helpers.ts
|
|
@@ -19804,21 +19756,6 @@ const jsenvPluginBabel = ({
|
|
|
19804
19756
|
isJsModule,
|
|
19805
19757
|
getImportSpecifier
|
|
19806
19758
|
});
|
|
19807
|
-
if (context.dev) {
|
|
19808
|
-
const requestHeaders = context.request.headers;
|
|
19809
|
-
if (requestHeaders["x-coverage-instanbul"]) {
|
|
19810
|
-
const coverageConfig = JSON.parse(requestHeaders["x-coverage-instanbul"]);
|
|
19811
|
-
const associations = URL_META.resolveAssociations({
|
|
19812
|
-
cover: coverageConfig
|
|
19813
|
-
}, context.rootDirectoryUrl);
|
|
19814
|
-
if (URL_META.applyAssociations({
|
|
19815
|
-
url: urlInfo.url,
|
|
19816
|
-
associations
|
|
19817
|
-
}).cover) {
|
|
19818
|
-
babelPluginStructure["transform-instrument"] = [babelPluginInstrument];
|
|
19819
|
-
}
|
|
19820
|
-
}
|
|
19821
|
-
}
|
|
19822
19759
|
if (getCustomBabelPlugins) {
|
|
19823
19760
|
Object.assign(babelPluginStructure, getCustomBabelPlugins(context));
|
|
19824
19761
|
}
|
|
@@ -23921,6 +23858,54 @@ const listRelativeFileUrlToCover = async ({
|
|
|
23921
23858
|
}) => relativeUrl);
|
|
23922
23859
|
};
|
|
23923
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
|
+
|
|
23924
23909
|
const relativeUrlToEmptyCoverage = async (relativeUrl, {
|
|
23925
23910
|
signal,
|
|
23926
23911
|
rootDirectoryUrl
|
|
@@ -24800,7 +24785,6 @@ const executeSteps = async (executionSteps, {
|
|
|
24800
24785
|
process.exitCode !== 1;
|
|
24801
24786
|
const startMs = Date.now();
|
|
24802
24787
|
let rawOutput = "";
|
|
24803
|
-
logger.info("");
|
|
24804
24788
|
let executionLog = createLog({
|
|
24805
24789
|
newLine: ""
|
|
24806
24790
|
});
|
|
@@ -25109,7 +25093,10 @@ const executeTestPlan = async ({
|
|
|
25109
25093
|
"file:///**/.*": false,
|
|
25110
25094
|
"file:///**/.*/": false,
|
|
25111
25095
|
"file:///**/node_modules/": false,
|
|
25112
|
-
"./**/src
|
|
25096
|
+
"./**/src/**/*.js": true,
|
|
25097
|
+
"./**/src/**/*.ts": true,
|
|
25098
|
+
"./**/src/**/*.jsx": true,
|
|
25099
|
+
"./**/src/**/*.tsx": true,
|
|
25113
25100
|
"./**/tests/": false,
|
|
25114
25101
|
"./**/*.test.html": false,
|
|
25115
25102
|
"./**/*.test.js": false,
|
|
@@ -25118,8 +25105,15 @@ const executeTestPlan = async ({
|
|
|
25118
25105
|
coverageIncludeMissing = true,
|
|
25119
25106
|
coverageAndExecutionAllowed = false,
|
|
25120
25107
|
coverageMethodForNodeJs = process.env.NODE_V8_COVERAGE ? "NODE_V8_COVERAGE" : "Profiler",
|
|
25121
|
-
|
|
25122
|
-
//
|
|
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"
|
|
25123
25117
|
coverageV8ConflictWarning = true,
|
|
25124
25118
|
coverageTempDirectoryUrl,
|
|
25125
25119
|
// skip empty means empty files won't appear in the coverage reports (json and html)
|
|
@@ -25134,6 +25128,7 @@ const executeTestPlan = async ({
|
|
|
25134
25128
|
...rest
|
|
25135
25129
|
}) => {
|
|
25136
25130
|
let someNeedsServer = false;
|
|
25131
|
+
let someHasCoverageV8 = false;
|
|
25137
25132
|
let someNodeRuntime = false;
|
|
25138
25133
|
const runtimes = {};
|
|
25139
25134
|
// param validation
|
|
@@ -25160,6 +25155,9 @@ const executeTestPlan = async ({
|
|
|
25160
25155
|
if (runtime) {
|
|
25161
25156
|
runtimes[runtime.name] = runtime.version;
|
|
25162
25157
|
if (runtime.type === "browser") {
|
|
25158
|
+
if (runtime.capabilities && runtime.capabilities.coverageV8) {
|
|
25159
|
+
someHasCoverageV8 = true;
|
|
25160
|
+
}
|
|
25163
25161
|
someNeedsServer = true;
|
|
25164
25162
|
}
|
|
25165
25163
|
if (runtime.type === "node") {
|
|
@@ -25172,6 +25170,9 @@ const executeTestPlan = async ({
|
|
|
25172
25170
|
await assertAndNormalizeWebServer(webServer);
|
|
25173
25171
|
}
|
|
25174
25172
|
if (coverageEnabled) {
|
|
25173
|
+
if (coverageMethodForBrowsers === undefined) {
|
|
25174
|
+
coverageMethodForBrowsers = someHasCoverageV8 ? "playwright" : "istanbul";
|
|
25175
|
+
}
|
|
25175
25176
|
if (typeof coverageConfig !== "object") {
|
|
25176
25177
|
throw new TypeError(`coverageConfig must be an object, got ${coverageConfig}`);
|
|
25177
25178
|
}
|
|
@@ -25332,6 +25333,172 @@ const executeTestPlan = async ({
|
|
|
25332
25333
|
};
|
|
25333
25334
|
};
|
|
25334
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
|
+
|
|
25335
25502
|
const createRuntimeFromPlaywright = ({
|
|
25336
25503
|
browserName,
|
|
25337
25504
|
browserVersion,
|
|
@@ -25343,7 +25510,10 @@ const createRuntimeFromPlaywright = ({
|
|
|
25343
25510
|
const runtime = {
|
|
25344
25511
|
type: "browser",
|
|
25345
25512
|
name: browserName,
|
|
25346
|
-
version: browserVersion
|
|
25513
|
+
version: browserVersion,
|
|
25514
|
+
capabilities: {
|
|
25515
|
+
coverageV8: coveragePlaywrightAPIAvailable
|
|
25516
|
+
}
|
|
25347
25517
|
};
|
|
25348
25518
|
let browserAndContextPromise;
|
|
25349
25519
|
runtime.run = async ({
|
|
@@ -25434,16 +25604,17 @@ ${webServer.rootDirectoryUrl}`);
|
|
|
25434
25604
|
}
|
|
25435
25605
|
await disconnected;
|
|
25436
25606
|
};
|
|
25437
|
-
const
|
|
25438
|
-
const
|
|
25439
|
-
|
|
25440
|
-
|
|
25441
|
-
|
|
25442
|
-
|
|
25443
|
-
|
|
25444
|
-
|
|
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
|
+
}
|
|
25445
25616
|
if (!webServer.isJsenvDevServer) {
|
|
25446
|
-
await
|
|
25617
|
+
await initJsSupervisorMiddleware(page, {
|
|
25447
25618
|
webServer,
|
|
25448
25619
|
fileUrl,
|
|
25449
25620
|
fileServerUrl
|
|
@@ -25466,7 +25637,7 @@ ${webServer.rootDirectoryUrl}`);
|
|
|
25466
25637
|
};
|
|
25467
25638
|
const callbacks = [];
|
|
25468
25639
|
if (coverageEnabled) {
|
|
25469
|
-
if (
|
|
25640
|
+
if (runtime.capabilities.coverageV8 && coverageMethodForBrowsers === "playwright") {
|
|
25470
25641
|
await page.coverage.startJSCoverage({
|
|
25471
25642
|
// reportAnonymousScripts: true,
|
|
25472
25643
|
});
|
|
@@ -25848,106 +26019,6 @@ const extractTextFromConsoleMessage = consoleMessage => {
|
|
|
25848
26019
|
// return text
|
|
25849
26020
|
};
|
|
25850
26021
|
|
|
25851
|
-
const initJsExecutionMiddleware = async (page, {
|
|
25852
|
-
webServer,
|
|
25853
|
-
fileUrl,
|
|
25854
|
-
fileServerUrl
|
|
25855
|
-
}) => {
|
|
25856
|
-
const inlineScriptContents = new Map();
|
|
25857
|
-
const interceptHtmlToExecute = async ({
|
|
25858
|
-
route
|
|
25859
|
-
}) => {
|
|
25860
|
-
// Fetch original response.
|
|
25861
|
-
const response = await route.fetch();
|
|
25862
|
-
// Add a prefix to the title.
|
|
25863
|
-
const originalBody = await response.text();
|
|
25864
|
-
const injectionResult = await injectSupervisorIntoHTML({
|
|
25865
|
-
content: originalBody,
|
|
25866
|
-
url: fileUrl
|
|
25867
|
-
}, {
|
|
25868
|
-
supervisorScriptSrc: `/@fs/${supervisorFileUrl$1.slice("file:///".length)}`,
|
|
25869
|
-
supervisorOptions: {},
|
|
25870
|
-
inlineAsRemote: true,
|
|
25871
|
-
webServer,
|
|
25872
|
-
onInlineScript: ({
|
|
25873
|
-
src,
|
|
25874
|
-
textContent
|
|
25875
|
-
}) => {
|
|
25876
|
-
const inlineScriptWebUrl = new URL(src, `${webServer.origin}/`).href;
|
|
25877
|
-
inlineScriptContents.set(inlineScriptWebUrl, textContent);
|
|
25878
|
-
}
|
|
25879
|
-
});
|
|
25880
|
-
route.fulfill({
|
|
25881
|
-
response,
|
|
25882
|
-
body: injectionResult.content,
|
|
25883
|
-
headers: {
|
|
25884
|
-
...response.headers(),
|
|
25885
|
-
"content-length": Buffer.byteLength(injectionResult.content)
|
|
25886
|
-
}
|
|
25887
|
-
});
|
|
25888
|
-
};
|
|
25889
|
-
const interceptInlineScript = ({
|
|
25890
|
-
url,
|
|
25891
|
-
route
|
|
25892
|
-
}) => {
|
|
25893
|
-
const inlineScriptContent = inlineScriptContents.get(url);
|
|
25894
|
-
route.fulfill({
|
|
25895
|
-
status: 200,
|
|
25896
|
-
body: inlineScriptContent,
|
|
25897
|
-
headers: {
|
|
25898
|
-
"content-type": "text/javascript",
|
|
25899
|
-
"content-length": Buffer.byteLength(inlineScriptContent)
|
|
25900
|
-
}
|
|
25901
|
-
});
|
|
25902
|
-
};
|
|
25903
|
-
const interceptFileSystemUrl = ({
|
|
25904
|
-
url,
|
|
25905
|
-
route
|
|
25906
|
-
}) => {
|
|
25907
|
-
const relativeUrl = url.slice(webServer.origin.length);
|
|
25908
|
-
const fsPath = relativeUrl.slice("/@fs/".length);
|
|
25909
|
-
const fsUrl = `file:///${fsPath}`;
|
|
25910
|
-
const fileContent = readFileSync$1(new URL(fsUrl), "utf8");
|
|
25911
|
-
route.fulfill({
|
|
25912
|
-
status: 200,
|
|
25913
|
-
body: fileContent,
|
|
25914
|
-
headers: {
|
|
25915
|
-
"content-type": "text/javascript",
|
|
25916
|
-
"content-length": Buffer.byteLength(fileContent)
|
|
25917
|
-
}
|
|
25918
|
-
});
|
|
25919
|
-
};
|
|
25920
|
-
await page.route("**", async route => {
|
|
25921
|
-
const request = route.request();
|
|
25922
|
-
const url = request.url();
|
|
25923
|
-
if (url === fileServerUrl && urlToExtension$1(url) === ".html") {
|
|
25924
|
-
interceptHtmlToExecute({
|
|
25925
|
-
url,
|
|
25926
|
-
request,
|
|
25927
|
-
route
|
|
25928
|
-
});
|
|
25929
|
-
return;
|
|
25930
|
-
}
|
|
25931
|
-
if (inlineScriptContents.has(url)) {
|
|
25932
|
-
interceptInlineScript({
|
|
25933
|
-
url,
|
|
25934
|
-
request,
|
|
25935
|
-
route
|
|
25936
|
-
});
|
|
25937
|
-
return;
|
|
25938
|
-
}
|
|
25939
|
-
const fsServerUrl = new URL("/@fs/", webServer.origin);
|
|
25940
|
-
if (url.startsWith(fsServerUrl)) {
|
|
25941
|
-
interceptFileSystemUrl({
|
|
25942
|
-
url,
|
|
25943
|
-
request,
|
|
25944
|
-
route
|
|
25945
|
-
});
|
|
25946
|
-
return;
|
|
25947
|
-
}
|
|
25948
|
-
route.fallback();
|
|
25949
|
-
});
|
|
25950
|
-
};
|
|
25951
26022
|
const registerEvent = ({
|
|
25952
26023
|
object,
|
|
25953
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
|
+
}
|
|
@@ -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}`,
|