@jsenv/test 2.1.3 → 3.0.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 +2 -1
- package/dist/js/exception.js +908 -0
- package/dist/js/execute_using_dynamic_import.js +76 -167
- package/dist/js/v8_coverage.js +4 -482
- package/dist/jsenv_test.js +4608 -2917
- package/dist/node_child_process_controlled.mjs +95 -76
- package/dist/node_worker_thread_controlled.mjs +72 -54
- package/package.json +23 -14
- package/src/coverage/generate_coverage.js +186 -0
- package/src/coverage/list_files_not_covered.js +2 -2
- package/src/coverage/missing_coverage.js +2 -2
- package/src/coverage/report_coverage_as_html.js +45 -0
- package/src/coverage/report_coverage_as_json.js +20 -0
- package/src/coverage/{coverage_reporter_text_log.js → report_coverage_in_console.js} +13 -7
- package/src/coverage/v8_and_istanbul.js +4 -4
- package/src/coverage/v8_coverage.js +2 -2
- package/src/execution/exception.js +151 -68
- package/src/execution/execute.js +24 -11
- package/src/execution/execute_test_plan.js +834 -750
- package/src/execution/format_error_for_terminal.js +187 -0
- package/src/execution/github_annotation_from_error.js +3 -3
- package/src/execution/junit_xml_file/report_as_junit_xml.js +149 -0
- package/src/execution/junit_xml_file/xml_generator.js +141 -0
- package/src/execution/reporters/reporter_list.js +687 -0
- package/src/execution/run.js +92 -30
- package/src/execution/web_server/start_using_command.js +2 -2
- package/src/execution/web_server_param.js +5 -5
- package/src/helpers/cpu_usage.js +142 -0
- package/src/helpers/cpu_usage.test_manual.js +7 -0
- package/src/main.js +8 -0
- package/src/runtime_browsers/chromium.js +1 -3
- package/src/runtime_browsers/firefox.js +0 -3
- package/src/runtime_browsers/middleware_istanbul.js +2 -2
- package/src/runtime_browsers/using_playwright.js +296 -220
- package/src/runtime_browsers/webkit.js +0 -3
- package/src/runtime_node/execute_using_dynamic_import.js +75 -28
- package/src/runtime_node/node_child_process.js +250 -139
- package/src/runtime_node/node_child_process_controlled.mjs +94 -75
- package/src/runtime_node/node_worker_thread.js +206 -120
- package/src/runtime_node/node_worker_thread_controlled.mjs +70 -53
- package/src/coverage/coverage_reporter_html_directory.js +0 -32
- package/src/coverage/coverage_reporter_json_file.js +0 -17
- package/src/coverage/report_to_coverage.js +0 -198
- package/src/execution/execution_colors.js +0 -10
- package/src/execution/execution_steps.js +0 -65
- package/src/execution/logs_file_execution.js +0 -495
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import { writeFileSync } from "node:fs";
|
|
1
|
+
import { writeFileSync, readFileSync } from "node:fs";
|
|
2
2
|
import { createDetailedMessage } from "@jsenv/log";
|
|
3
|
-
import {
|
|
4
|
-
Abort,
|
|
5
|
-
createCallbackListNotifiedOnce,
|
|
6
|
-
raceProcessTeardownEvents,
|
|
7
|
-
raceCallbacks,
|
|
8
|
-
} from "@jsenv/abort";
|
|
3
|
+
import { Abort, raceCallbacks } from "@jsenv/abort";
|
|
9
4
|
import { urlIsInsideOf } from "@jsenv/urls";
|
|
10
5
|
import { memoize } from "@jsenv/utils/src/memoize/memoize.js";
|
|
11
6
|
|
|
@@ -19,8 +14,8 @@ const browserPromiseCache = new Map();
|
|
|
19
14
|
|
|
20
15
|
export const createRuntimeUsingPlaywright = ({
|
|
21
16
|
browserName,
|
|
22
|
-
browserVersion,
|
|
23
17
|
coveragePlaywrightAPIAvailable = false,
|
|
18
|
+
memoryUsageAPIAvailable = false,
|
|
24
19
|
shouldIgnoreError = () => false,
|
|
25
20
|
transformErrorHook = (error) => error,
|
|
26
21
|
isolatedTab = false,
|
|
@@ -28,6 +23,7 @@ export const createRuntimeUsingPlaywright = ({
|
|
|
28
23
|
playwrightLaunchOptions = {},
|
|
29
24
|
ignoreHTTPSErrors = true,
|
|
30
25
|
}) => {
|
|
26
|
+
const browserVersion = getBrowserVersion(browserName);
|
|
31
27
|
const label = `${browserName}${browserVersion}`;
|
|
32
28
|
const runtime = {
|
|
33
29
|
type: "browser",
|
|
@@ -45,31 +41,39 @@ export const createRuntimeUsingPlaywright = ({
|
|
|
45
41
|
webServer,
|
|
46
42
|
fileRelativeUrl,
|
|
47
43
|
|
|
48
|
-
|
|
44
|
+
keepRunning,
|
|
45
|
+
stopSignal,
|
|
46
|
+
onConsole,
|
|
47
|
+
onRuntimeStarted,
|
|
48
|
+
onRuntimeStopped,
|
|
49
|
+
teardownCallbackSet,
|
|
50
|
+
isTestPlan,
|
|
51
|
+
|
|
52
|
+
measureMemoryUsage,
|
|
53
|
+
onMeasureMemoryAvailable,
|
|
49
54
|
collectPerformance,
|
|
50
55
|
coverageEnabled = false,
|
|
51
|
-
|
|
56
|
+
coverageInclude,
|
|
52
57
|
coverageMethodForBrowsers,
|
|
53
58
|
coverageFileUrl,
|
|
54
|
-
|
|
55
|
-
teardown,
|
|
56
|
-
isTestPlan,
|
|
57
|
-
stopSignal,
|
|
58
|
-
keepRunning,
|
|
59
|
-
onConsole,
|
|
60
59
|
}) => {
|
|
61
60
|
const fileUrl = new URL(fileRelativeUrl, rootDirectoryUrl).href;
|
|
62
61
|
if (!urlIsInsideOf(fileUrl, webServer.rootDirectoryUrl)) {
|
|
63
62
|
throw new Error(`Cannot execute file that is outside web server root directory
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
63
|
+
--- file ---
|
|
64
|
+
${fileUrl}
|
|
65
|
+
--- web server root directory url ---
|
|
66
|
+
${webServer.rootDirectoryUrl}`);
|
|
68
67
|
}
|
|
69
68
|
const fileServerUrl = WEB_URL_CONVERTER.asWebUrl(fileUrl, webServer);
|
|
70
|
-
const
|
|
69
|
+
const cleanupCallbackSet = new Set();
|
|
71
70
|
const cleanup = memoize(async (reason) => {
|
|
72
|
-
|
|
71
|
+
const promises = [];
|
|
72
|
+
for (const cleanupCallback of cleanupCallbackSet) {
|
|
73
|
+
promises.push(cleanupCallback({ reason }));
|
|
74
|
+
}
|
|
75
|
+
cleanupCallbackSet.clear();
|
|
76
|
+
await Promise.all(promises);
|
|
73
77
|
});
|
|
74
78
|
|
|
75
79
|
const isBrowserDedicatedToExecution = isolatedTab || !isTestPlan;
|
|
@@ -78,24 +82,41 @@ export const createRuntimeUsingPlaywright = ({
|
|
|
78
82
|
: browserPromiseCache.get(label);
|
|
79
83
|
if (!browserAndContextPromise) {
|
|
80
84
|
browserAndContextPromise = (async () => {
|
|
85
|
+
const options = {
|
|
86
|
+
...playwrightLaunchOptions,
|
|
87
|
+
headless: headful === undefined ? !keepRunning : !headful,
|
|
88
|
+
};
|
|
89
|
+
if (memoryUsageAPIAvailable && measureMemoryUsage) {
|
|
90
|
+
const { ignoreDefaultArgs, args } = options;
|
|
91
|
+
if (ignoreDefaultArgs) {
|
|
92
|
+
if (!ignoreDefaultArgs.includes("--headless")) {
|
|
93
|
+
ignoreDefaultArgs.push("--headless");
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
options.ignoreDefaultArgs = ["--headless"];
|
|
97
|
+
}
|
|
98
|
+
if (args) {
|
|
99
|
+
if (!args.includes("--headless=new")) {
|
|
100
|
+
args.push("--headless=new");
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
options.args = ["--headless=new"];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
81
106
|
const browser = await launchBrowserUsingPlaywright({
|
|
82
107
|
signal,
|
|
83
108
|
browserName,
|
|
84
|
-
|
|
85
|
-
playwrightLaunchOptions: {
|
|
86
|
-
...playwrightLaunchOptions,
|
|
87
|
-
headless: headful === undefined ? !keepRunning : !headful,
|
|
88
|
-
},
|
|
109
|
+
playwrightLaunchOptions: options,
|
|
89
110
|
});
|
|
90
|
-
if (browser._initializer.version) {
|
|
91
|
-
|
|
92
|
-
}
|
|
111
|
+
// if (browser._initializer.version) {
|
|
112
|
+
// runtime.version = browser._initializer.version;
|
|
113
|
+
// }
|
|
93
114
|
const browserContext = await browser.newContext({ ignoreHTTPSErrors });
|
|
94
115
|
return { browser, browserContext };
|
|
95
116
|
})();
|
|
96
117
|
if (!isBrowserDedicatedToExecution) {
|
|
97
118
|
browserPromiseCache.set(label, browserAndContextPromise);
|
|
98
|
-
|
|
119
|
+
cleanupCallbackSet.add(() => {
|
|
99
120
|
browserPromiseCache.delete(label);
|
|
100
121
|
});
|
|
101
122
|
}
|
|
@@ -124,8 +145,44 @@ export const createRuntimeUsingPlaywright = ({
|
|
|
124
145
|
}
|
|
125
146
|
await disconnected;
|
|
126
147
|
};
|
|
148
|
+
// https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-disconnected
|
|
149
|
+
if (isBrowserDedicatedToExecution) {
|
|
150
|
+
cleanupCallbackSet.add(closeBrowser);
|
|
151
|
+
browser.on("disconnected", async () => {
|
|
152
|
+
onRuntimeStopped();
|
|
153
|
+
});
|
|
154
|
+
} else {
|
|
155
|
+
const disconnectedCallback = async () => {
|
|
156
|
+
throw new Error("browser disconnected during execution");
|
|
157
|
+
};
|
|
158
|
+
browser.on("disconnected", disconnectedCallback);
|
|
159
|
+
cleanupCallbackSet.add(() => {
|
|
160
|
+
browser.removeListener("disconnected", disconnectedCallback);
|
|
161
|
+
});
|
|
162
|
+
teardownCallbackSet.add(async () => {
|
|
163
|
+
browser.removeListener("disconnected", disconnectedCallback);
|
|
164
|
+
logger.debug(`testPlan teardown -> closing ${browserName}`);
|
|
165
|
+
await closeBrowser();
|
|
166
|
+
});
|
|
167
|
+
}
|
|
127
168
|
|
|
128
169
|
const page = await browserContext.newPage();
|
|
170
|
+
if (!isBrowserDedicatedToExecution) {
|
|
171
|
+
page.on("close", () => {
|
|
172
|
+
onRuntimeStopped();
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
onRuntimeStarted();
|
|
176
|
+
cleanupCallbackSet.add(async () => {
|
|
177
|
+
try {
|
|
178
|
+
await page.close();
|
|
179
|
+
} catch (e) {
|
|
180
|
+
if (isTargetClosedError(e)) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
throw e;
|
|
184
|
+
}
|
|
185
|
+
});
|
|
129
186
|
|
|
130
187
|
const istanbulInstrumentationEnabled =
|
|
131
188
|
coverageEnabled &&
|
|
@@ -135,7 +192,7 @@ export const createRuntimeUsingPlaywright = ({
|
|
|
135
192
|
await initIstanbulMiddleware(page, {
|
|
136
193
|
webServer,
|
|
137
194
|
rootDirectoryUrl,
|
|
138
|
-
|
|
195
|
+
coverageInclude,
|
|
139
196
|
});
|
|
140
197
|
}
|
|
141
198
|
if (!webServer.isJsenvDevServer) {
|
|
@@ -145,23 +202,16 @@ export const createRuntimeUsingPlaywright = ({
|
|
|
145
202
|
fileServerUrl,
|
|
146
203
|
});
|
|
147
204
|
}
|
|
148
|
-
const closePage = async () => {
|
|
149
|
-
try {
|
|
150
|
-
await page.close();
|
|
151
|
-
} catch (e) {
|
|
152
|
-
if (isTargetClosedError(e)) {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
throw e;
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
205
|
|
|
159
206
|
const result = {
|
|
160
207
|
status: "pending",
|
|
161
|
-
namespace: null,
|
|
162
208
|
errors: [],
|
|
209
|
+
namespace: null,
|
|
210
|
+
timings: {},
|
|
211
|
+
memoryUsage: null,
|
|
212
|
+
performance: null,
|
|
163
213
|
};
|
|
164
|
-
const
|
|
214
|
+
const callbackSet = new Set();
|
|
165
215
|
if (coverageEnabled) {
|
|
166
216
|
if (
|
|
167
217
|
runtime.capabilities.coverageV8 &&
|
|
@@ -170,7 +220,7 @@ export const createRuntimeUsingPlaywright = ({
|
|
|
170
220
|
await page.coverage.startJSCoverage({
|
|
171
221
|
// reportAnonymousScripts: true,
|
|
172
222
|
});
|
|
173
|
-
|
|
223
|
+
callbackSet.add(async () => {
|
|
174
224
|
const v8CoveragesWithWebUrls = await page.coverage.stopJSCoverage();
|
|
175
225
|
// we convert urls starting with http:// to file:// because we later
|
|
176
226
|
// convert the url to filesystem path in istanbulCoverageFromV8Coverage function
|
|
@@ -190,7 +240,7 @@ export const createRuntimeUsingPlaywright = ({
|
|
|
190
240
|
{ result: v8CoveragesWithFsUrls },
|
|
191
241
|
{
|
|
192
242
|
rootDirectoryUrl,
|
|
193
|
-
|
|
243
|
+
coverageInclude,
|
|
194
244
|
},
|
|
195
245
|
);
|
|
196
246
|
writeFileSync(
|
|
@@ -199,7 +249,7 @@ export const createRuntimeUsingPlaywright = ({
|
|
|
199
249
|
);
|
|
200
250
|
});
|
|
201
251
|
} else {
|
|
202
|
-
|
|
252
|
+
callbackSet.add(() => {
|
|
203
253
|
const scriptExecutionResults = result.namespace;
|
|
204
254
|
if (scriptExecutionResults) {
|
|
205
255
|
const coverage =
|
|
@@ -212,7 +262,7 @@ export const createRuntimeUsingPlaywright = ({
|
|
|
212
262
|
});
|
|
213
263
|
}
|
|
214
264
|
} else {
|
|
215
|
-
|
|
265
|
+
callbackSet.add(() => {
|
|
216
266
|
const scriptExecutionResults = result.namespace;
|
|
217
267
|
if (scriptExecutionResults) {
|
|
218
268
|
Object.keys(scriptExecutionResults).forEach((fileRelativeUrl) => {
|
|
@@ -222,8 +272,50 @@ export const createRuntimeUsingPlaywright = ({
|
|
|
222
272
|
});
|
|
223
273
|
}
|
|
224
274
|
|
|
275
|
+
if (memoryUsageAPIAvailable) {
|
|
276
|
+
const getMemoryUsage = async () => {
|
|
277
|
+
const memoryUsage = await page.evaluate(
|
|
278
|
+
/* eslint-disable no-undef */
|
|
279
|
+
/* istanbul ignore next */
|
|
280
|
+
async () => {
|
|
281
|
+
const { performance } = window;
|
|
282
|
+
if (!performance) {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
// performance.memory is less accurate but way faster
|
|
286
|
+
// https://web.dev/articles/monitor-total-page-memory-usage#legacy-api
|
|
287
|
+
if (performance.memory) {
|
|
288
|
+
return performance.memory.totalJSHeapSize;
|
|
289
|
+
}
|
|
290
|
+
// https://developer.mozilla.org/en-US/docs/Web/API/Performance/measureUserAgentSpecificMemory
|
|
291
|
+
if (
|
|
292
|
+
performance.measureUserAgentSpecificMemory &&
|
|
293
|
+
crossOriginIsolated
|
|
294
|
+
) {
|
|
295
|
+
const memorySample =
|
|
296
|
+
await performance.measureUserAgentSpecificMemory();
|
|
297
|
+
return memorySample;
|
|
298
|
+
}
|
|
299
|
+
return null;
|
|
300
|
+
},
|
|
301
|
+
/* eslint-enable no-undef */
|
|
302
|
+
);
|
|
303
|
+
return memoryUsage;
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
if (onMeasureMemoryAvailable) {
|
|
307
|
+
onMeasureMemoryAvailable(getMemoryUsage);
|
|
308
|
+
}
|
|
309
|
+
if (memoryUsageAPIAvailable && measureMemoryUsage) {
|
|
310
|
+
callbackSet.add(async () => {
|
|
311
|
+
const memoryUsage = await getMemoryUsage();
|
|
312
|
+
result.memoryUsage = memoryUsage;
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
225
317
|
if (collectPerformance) {
|
|
226
|
-
|
|
318
|
+
callbackSet.add(async () => {
|
|
227
319
|
const performance = await page.evaluate(
|
|
228
320
|
/* eslint-disable no-undef */
|
|
229
321
|
/* istanbul ignore next */
|
|
@@ -262,177 +354,175 @@ export const createRuntimeUsingPlaywright = ({
|
|
|
262
354
|
});
|
|
263
355
|
},
|
|
264
356
|
});
|
|
265
|
-
|
|
357
|
+
cleanupCallbackSet.add(removeConsoleListener);
|
|
266
358
|
const actionOperation = Abort.startOperation();
|
|
267
359
|
actionOperation.addAbortSignal(signal);
|
|
268
360
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
cb(transformErrorHook(error));
|
|
285
|
-
},
|
|
286
|
-
});
|
|
287
|
-
},
|
|
288
|
-
// https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-pageerror
|
|
289
|
-
// pageerror: () => {
|
|
290
|
-
// return registerEvent({
|
|
291
|
-
// object: page,
|
|
292
|
-
// eventType: "pageerror",
|
|
293
|
-
// callback: (error) => {
|
|
294
|
-
// if (
|
|
295
|
-
// webServer.isJsenvDevServer ||
|
|
296
|
-
// shouldIgnoreError(error, "pageerror")
|
|
297
|
-
// ) {
|
|
298
|
-
// return
|
|
299
|
-
// }
|
|
300
|
-
// result.errors.push(transformErrorHook(error))
|
|
301
|
-
// },
|
|
302
|
-
// })
|
|
303
|
-
// },
|
|
304
|
-
closed: (cb) => {
|
|
305
|
-
// https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-disconnected
|
|
306
|
-
if (isBrowserDedicatedToExecution) {
|
|
307
|
-
browser.on("disconnected", async () => {
|
|
308
|
-
cb({ reason: "browser disconnected" });
|
|
309
|
-
});
|
|
310
|
-
cleanupCallbackList.add(closePage);
|
|
311
|
-
cleanupCallbackList.add(closeBrowser);
|
|
312
|
-
} else {
|
|
313
|
-
const disconnectedCallback = async () => {
|
|
314
|
-
throw new Error("browser disconnected during execution");
|
|
315
|
-
};
|
|
316
|
-
browser.on("disconnected", disconnectedCallback);
|
|
317
|
-
page.on("close", () => {
|
|
318
|
-
cb({ reason: "page closed" });
|
|
319
|
-
});
|
|
320
|
-
cleanupCallbackList.add(closePage);
|
|
321
|
-
cleanupCallbackList.add(() => {
|
|
322
|
-
browser.removeListener("disconnected", disconnectedCallback);
|
|
323
|
-
});
|
|
324
|
-
teardown.addCallback(async () => {
|
|
325
|
-
browser.removeListener("disconnected", disconnectedCallback);
|
|
326
|
-
logger.debug(`testPlan teardown -> closing ${browserName}`);
|
|
327
|
-
await closeBrowser();
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
},
|
|
331
|
-
response: async (cb) => {
|
|
332
|
-
try {
|
|
333
|
-
await page.goto(fileServerUrl, { timeout: 0 });
|
|
334
|
-
const returnValue = await page.evaluate(
|
|
335
|
-
/* eslint-disable no-undef */
|
|
336
|
-
/* istanbul ignore next */
|
|
337
|
-
async () => {
|
|
338
|
-
let startTime;
|
|
339
|
-
try {
|
|
340
|
-
startTime = window.performance.timing.navigationStart;
|
|
341
|
-
} catch (e) {
|
|
342
|
-
startTime = Date.now();
|
|
343
|
-
}
|
|
344
|
-
if (!window.__supervisor__) {
|
|
345
|
-
throw new Error("window.__supervisor__ is undefined");
|
|
361
|
+
try {
|
|
362
|
+
const winnerPromise = new Promise((resolve, reject) => {
|
|
363
|
+
raceCallbacks(
|
|
364
|
+
{
|
|
365
|
+
aborted: (cb) => {
|
|
366
|
+
return actionOperation.addAbortCallback(cb);
|
|
367
|
+
},
|
|
368
|
+
// https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-error
|
|
369
|
+
error: (cb) => {
|
|
370
|
+
return registerEvent({
|
|
371
|
+
object: page,
|
|
372
|
+
eventType: "error",
|
|
373
|
+
callback: (error) => {
|
|
374
|
+
if (shouldIgnoreError(error, "error")) {
|
|
375
|
+
return;
|
|
346
376
|
}
|
|
347
|
-
|
|
348
|
-
await window.__supervisor__.getDocumentExecutionResult();
|
|
349
|
-
return {
|
|
350
|
-
type: "window_supervisor",
|
|
351
|
-
startTime,
|
|
352
|
-
endTime: Date.now(),
|
|
353
|
-
executionResults:
|
|
354
|
-
executionResultFromJsenvSupervisor.executionResults,
|
|
355
|
-
};
|
|
377
|
+
cb(transformErrorHook(error));
|
|
356
378
|
},
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
379
|
+
});
|
|
380
|
+
},
|
|
381
|
+
// https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-pageerror
|
|
382
|
+
// pageerror: () => {
|
|
383
|
+
// return registerEvent({
|
|
384
|
+
// object: page,
|
|
385
|
+
// eventType: "pageerror",
|
|
386
|
+
// callback: (error) => {
|
|
387
|
+
// if (
|
|
388
|
+
// webServer.isJsenvDevServer ||
|
|
389
|
+
// shouldIgnoreError(error, "pageerror")
|
|
390
|
+
// ) {
|
|
391
|
+
// return
|
|
392
|
+
// }
|
|
393
|
+
// result.errors.push(transformErrorHook(error))
|
|
394
|
+
// },
|
|
395
|
+
// })
|
|
396
|
+
// },
|
|
397
|
+
closed: (cb) => {
|
|
398
|
+
if (isBrowserDedicatedToExecution) {
|
|
399
|
+
browser.on("disconnected", async () => {
|
|
400
|
+
cb({ reason: "browser disconnected" });
|
|
401
|
+
});
|
|
402
|
+
} else {
|
|
403
|
+
page.on("close", () => {
|
|
404
|
+
cb({ reason: "page closed" });
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
response: async (cb) => {
|
|
409
|
+
try {
|
|
410
|
+
await page.goto(fileServerUrl, { timeout: 0 });
|
|
411
|
+
const returnValue = await page.evaluate(
|
|
412
|
+
/* eslint-disable no-undef */
|
|
413
|
+
/* istanbul ignore next */
|
|
414
|
+
async () => {
|
|
415
|
+
let startTime;
|
|
416
|
+
try {
|
|
417
|
+
startTime = window.performance.timing.navigationStart;
|
|
418
|
+
} catch (e) {
|
|
419
|
+
startTime = Date.now();
|
|
420
|
+
}
|
|
421
|
+
if (!window.__supervisor__) {
|
|
422
|
+
throw new Error("window.__supervisor__ is undefined");
|
|
423
|
+
}
|
|
424
|
+
const executionResultFromJsenvSupervisor =
|
|
425
|
+
await window.__supervisor__.getDocumentExecutionResult();
|
|
426
|
+
return {
|
|
427
|
+
type: "window_supervisor",
|
|
428
|
+
timings: {
|
|
429
|
+
start: startTime,
|
|
430
|
+
end: Date.now(),
|
|
431
|
+
},
|
|
432
|
+
executionResults:
|
|
433
|
+
executionResultFromJsenvSupervisor.executionResults,
|
|
434
|
+
};
|
|
435
|
+
},
|
|
436
|
+
/* eslint-enable no-undef */
|
|
437
|
+
);
|
|
438
|
+
cb(returnValue);
|
|
439
|
+
} catch (e) {
|
|
440
|
+
reject(e);
|
|
441
|
+
}
|
|
442
|
+
},
|
|
363
443
|
},
|
|
364
|
-
|
|
365
|
-
resolve,
|
|
366
|
-
);
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
const writeResult = async () => {
|
|
370
|
-
const winner = await winnerPromise;
|
|
371
|
-
if (winner.name === "aborted") {
|
|
372
|
-
result.status = "aborted";
|
|
373
|
-
return;
|
|
374
|
-
}
|
|
375
|
-
if (winner.name === "error") {
|
|
376
|
-
let error = winner.data;
|
|
377
|
-
result.status = "failed";
|
|
378
|
-
result.errors.push(error);
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
if (winner.name === "pageerror") {
|
|
382
|
-
let error = winner.data;
|
|
383
|
-
result.status = "failed";
|
|
384
|
-
result.errors.push(error);
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
if (winner.name === "closed") {
|
|
388
|
-
result.status = "failed";
|
|
389
|
-
result.errors.push(
|
|
390
|
-
isBrowserDedicatedToExecution
|
|
391
|
-
? new Error(`browser disconnected during execution`)
|
|
392
|
-
: new Error(`page closed during execution`),
|
|
444
|
+
resolve,
|
|
393
445
|
);
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
// winner.name === "response"
|
|
397
|
-
const { executionResults } = winner.data;
|
|
398
|
-
result.status = "completed";
|
|
399
|
-
result.namespace = executionResults;
|
|
400
|
-
Object.keys(executionResults).forEach((key) => {
|
|
401
|
-
const executionResult = executionResults[key];
|
|
402
|
-
if (executionResult.status === "failed") {
|
|
403
|
-
result.status = "failed";
|
|
404
|
-
if (executionResult.exception) {
|
|
405
|
-
result.errors.push(executionResult.exception);
|
|
406
|
-
} else {
|
|
407
|
-
result.errors.push(executionResult.error);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
446
|
});
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
447
|
+
const raceHandlers = {
|
|
448
|
+
aborted: () => {
|
|
449
|
+
result.status = "aborted";
|
|
450
|
+
},
|
|
451
|
+
error: (error) => {
|
|
452
|
+
result.status = "failed";
|
|
453
|
+
result.errors.push(error);
|
|
454
|
+
},
|
|
455
|
+
// pageerror: (error) => {
|
|
456
|
+
// result.status = "failed";
|
|
457
|
+
// result.errors.push(error);
|
|
458
|
+
// },
|
|
459
|
+
closed: () => {
|
|
460
|
+
result.status = "failed";
|
|
461
|
+
result.errors.push(
|
|
462
|
+
isBrowserDedicatedToExecution
|
|
463
|
+
? new Error(`browser disconnected during execution`)
|
|
464
|
+
: new Error(`page closed during execution`),
|
|
465
|
+
);
|
|
466
|
+
},
|
|
467
|
+
response: ({ executionResults, timings }) => {
|
|
468
|
+
result.status = "completed";
|
|
469
|
+
result.namespace = executionResults;
|
|
470
|
+
result.timings = timings;
|
|
471
|
+
Object.keys(executionResults).forEach((key) => {
|
|
472
|
+
const executionResult = executionResults[key];
|
|
473
|
+
if (executionResult.status === "failed") {
|
|
474
|
+
result.status = "failed";
|
|
475
|
+
if (executionResult.exception) {
|
|
476
|
+
result.errors.push(executionResult.exception);
|
|
477
|
+
} else {
|
|
478
|
+
result.errors.push(executionResult.error);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
const winner = await winnerPromise;
|
|
485
|
+
raceHandlers[winner.name](winner.data);
|
|
486
|
+
for (const callback of callbackSet) {
|
|
420
487
|
await callback();
|
|
421
|
-
}
|
|
488
|
+
}
|
|
489
|
+
callbackSet.clear();
|
|
422
490
|
} catch (e) {
|
|
423
491
|
result.status = "failed";
|
|
424
492
|
result.errors = [e];
|
|
493
|
+
} finally {
|
|
494
|
+
if (keepRunning) {
|
|
495
|
+
stopSignal.notify = cleanup;
|
|
496
|
+
} else {
|
|
497
|
+
await cleanup("execution done");
|
|
498
|
+
}
|
|
499
|
+
return result;
|
|
425
500
|
}
|
|
426
|
-
if (keepRunning) {
|
|
427
|
-
stopSignal.notify = cleanup;
|
|
428
|
-
} else {
|
|
429
|
-
await cleanup("execution done");
|
|
430
|
-
}
|
|
431
|
-
return result;
|
|
432
501
|
};
|
|
433
502
|
return runtime;
|
|
434
503
|
};
|
|
435
504
|
|
|
505
|
+
// see also https://github.com/microsoft/playwright/releases
|
|
506
|
+
const getBrowserVersion = (browserName) => {
|
|
507
|
+
const playwrightPackageJsonFileUrl = import.meta.resolve(
|
|
508
|
+
"playwright-core/package.json",
|
|
509
|
+
);
|
|
510
|
+
const playwrightBrowsersJsonFileUrl = new URL(
|
|
511
|
+
"./browsers.json",
|
|
512
|
+
playwrightPackageJsonFileUrl,
|
|
513
|
+
);
|
|
514
|
+
const browsersJson = JSON.parse(
|
|
515
|
+
readFileSync(playwrightBrowsersJsonFileUrl, "utf8"),
|
|
516
|
+
);
|
|
517
|
+
const { browsers } = browsersJson;
|
|
518
|
+
for (const browser of browsers) {
|
|
519
|
+
if (browser.name === browserName) {
|
|
520
|
+
return browser.browserVersion;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
return "unkown";
|
|
524
|
+
};
|
|
525
|
+
|
|
436
526
|
const generateCoverageForPage = (scriptExecutionResults) => {
|
|
437
527
|
let istanbulCoverageComposed = null;
|
|
438
528
|
Object.keys(scriptExecutionResults).forEach((fileRelativeUrl) => {
|
|
@@ -450,26 +540,11 @@ const generateCoverageForPage = (scriptExecutionResults) => {
|
|
|
450
540
|
const launchBrowserUsingPlaywright = async ({
|
|
451
541
|
signal,
|
|
452
542
|
browserName,
|
|
453
|
-
stopOnExit,
|
|
454
543
|
playwrightLaunchOptions,
|
|
455
544
|
}) => {
|
|
456
545
|
const launchBrowserOperation = Abort.startOperation();
|
|
457
546
|
launchBrowserOperation.addAbortSignal(signal);
|
|
458
547
|
const playwright = await importPlaywright({ browserName });
|
|
459
|
-
if (stopOnExit) {
|
|
460
|
-
launchBrowserOperation.addAbortSource((abort) => {
|
|
461
|
-
return raceProcessTeardownEvents(
|
|
462
|
-
{
|
|
463
|
-
SIGHUP: true,
|
|
464
|
-
SIGTERM: true,
|
|
465
|
-
SIGINT: true,
|
|
466
|
-
beforeExit: true,
|
|
467
|
-
exit: true,
|
|
468
|
-
},
|
|
469
|
-
abort,
|
|
470
|
-
);
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
548
|
const browserClass = playwright[browserName];
|
|
474
549
|
try {
|
|
475
550
|
const browser = await browserClass.launch({
|
|
@@ -499,11 +574,12 @@ const importPlaywright = async ({ browserName }) => {
|
|
|
499
574
|
return namespace;
|
|
500
575
|
} catch (e) {
|
|
501
576
|
if (e.code === "ERR_MODULE_NOT_FOUND") {
|
|
577
|
+
const dependencyName = `@playwright/browser-${browserName}`;
|
|
502
578
|
throw new Error(
|
|
503
579
|
createDetailedMessage(
|
|
504
|
-
`"playwright" not found. You need
|
|
580
|
+
`"playwright" not found. You need ${dependencyName} in your dependencies to use "${browserName}"`,
|
|
505
581
|
{
|
|
506
|
-
suggestion: `npm install --save-dev
|
|
582
|
+
suggestion: `npm install --save-dev ${dependencyName}`,
|
|
507
583
|
},
|
|
508
584
|
),
|
|
509
585
|
{ cause: e },
|
|
@@ -14,9 +14,6 @@ export const webkitIsolatedTab = (params) => {
|
|
|
14
14
|
const createWekbitRuntime = (params) => {
|
|
15
15
|
return createRuntimeUsingPlaywright({
|
|
16
16
|
browserName: "webkit",
|
|
17
|
-
// browserVersion will be set by "browser._initializer.version"
|
|
18
|
-
// see also https://github.com/microsoft/playwright/releases
|
|
19
|
-
browserVersion: "unset",
|
|
20
17
|
shouldIgnoreError: (error) => {
|
|
21
18
|
// we catch error during execution but safari throw unhandled rejection
|
|
22
19
|
// in a non-deterministic way.
|