@jsenv/test 2.1.3 → 3.0.1

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.
Files changed (46) hide show
  1. package/README.md +2 -1
  2. package/dist/js/exception.js +908 -0
  3. package/dist/js/execute_using_dynamic_import.js +76 -167
  4. package/dist/js/v8_coverage.js +4 -482
  5. package/dist/jsenv_test.js +4608 -2917
  6. package/dist/node_child_process_controlled.mjs +95 -76
  7. package/dist/node_worker_thread_controlled.mjs +72 -54
  8. package/package.json +23 -14
  9. package/src/coverage/generate_coverage.js +186 -0
  10. package/src/coverage/list_files_not_covered.js +2 -2
  11. package/src/coverage/missing_coverage.js +2 -2
  12. package/src/coverage/report_coverage_as_html.js +45 -0
  13. package/src/coverage/report_coverage_as_json.js +20 -0
  14. package/src/coverage/{coverage_reporter_text_log.js → report_coverage_in_console.js} +13 -7
  15. package/src/coverage/v8_and_istanbul.js +4 -4
  16. package/src/coverage/v8_coverage.js +2 -2
  17. package/src/execution/exception.js +151 -68
  18. package/src/execution/execute.js +24 -11
  19. package/src/execution/execute_test_plan.js +834 -750
  20. package/src/execution/format_error_for_terminal.js +187 -0
  21. package/src/execution/github_annotation_from_error.js +3 -3
  22. package/src/execution/junit_xml_file/report_as_junit_xml.js +149 -0
  23. package/src/execution/junit_xml_file/xml_generator.js +141 -0
  24. package/src/execution/reporters/reporter_list.js +687 -0
  25. package/src/execution/run.js +92 -30
  26. package/src/execution/web_server/start_using_command.js +2 -2
  27. package/src/execution/web_server_param.js +5 -5
  28. package/src/helpers/cpu_usage.js +142 -0
  29. package/src/helpers/cpu_usage.test_manual.js +7 -0
  30. package/src/main.js +8 -0
  31. package/src/runtime_browsers/chromium.js +1 -3
  32. package/src/runtime_browsers/firefox.js +0 -3
  33. package/src/runtime_browsers/middleware_istanbul.js +2 -2
  34. package/src/runtime_browsers/using_playwright.js +296 -220
  35. package/src/runtime_browsers/webkit.js +0 -3
  36. package/src/runtime_node/execute_using_dynamic_import.js +75 -28
  37. package/src/runtime_node/node_child_process.js +250 -139
  38. package/src/runtime_node/node_child_process_controlled.mjs +94 -75
  39. package/src/runtime_node/node_worker_thread.js +206 -120
  40. package/src/runtime_node/node_worker_thread_controlled.mjs +70 -53
  41. package/src/coverage/coverage_reporter_html_directory.js +0 -32
  42. package/src/coverage/coverage_reporter_json_file.js +0 -17
  43. package/src/coverage/report_to_coverage.js +0 -198
  44. package/src/execution/execution_colors.js +0 -10
  45. package/src/execution/execution_steps.js +0 -65
  46. 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
- // measurePerformance,
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
- coverageConfig,
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
- --- file ---
65
- ${fileUrl}
66
- --- web server root directory url ---
67
- ${webServer.rootDirectoryUrl}`);
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 cleanupCallbackList = createCallbackListNotifiedOnce();
69
+ const cleanupCallbackSet = new Set();
71
70
  const cleanup = memoize(async (reason) => {
72
- await cleanupCallbackList.notify({ reason });
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
- stopOnExit: true,
85
- playwrightLaunchOptions: {
86
- ...playwrightLaunchOptions,
87
- headless: headful === undefined ? !keepRunning : !headful,
88
- },
109
+ playwrightLaunchOptions: options,
89
110
  });
90
- if (browser._initializer.version) {
91
- runtime.version = browser._initializer.version;
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
- cleanupCallbackList.add(() => {
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
- coverageConfig,
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 callbacks = [];
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
- callbacks.push(async () => {
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
- coverageConfig,
243
+ coverageInclude,
194
244
  },
195
245
  );
196
246
  writeFileSync(
@@ -199,7 +249,7 @@ export const createRuntimeUsingPlaywright = ({
199
249
  );
200
250
  });
201
251
  } else {
202
- callbacks.push(() => {
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
- callbacks.push(() => {
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
- callbacks.push(async () => {
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
- cleanupCallbackList.add(removeConsoleListener);
357
+ cleanupCallbackSet.add(removeConsoleListener);
266
358
  const actionOperation = Abort.startOperation();
267
359
  actionOperation.addAbortSignal(signal);
268
360
 
269
- const winnerPromise = new Promise((resolve, reject) => {
270
- raceCallbacks(
271
- {
272
- aborted: (cb) => {
273
- return actionOperation.addAbortCallback(cb);
274
- },
275
- // https://github.com/GoogleChrome/puppeteer/blob/v1.4.0/docs/api.md#event-error
276
- error: (cb) => {
277
- return registerEvent({
278
- object: page,
279
- eventType: "error",
280
- callback: (error) => {
281
- if (shouldIgnoreError(error, "error")) {
282
- return;
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
- const executionResultFromJsenvSupervisor =
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
- /* eslint-enable no-undef */
358
- );
359
- cb(returnValue);
360
- } catch (e) {
361
- reject(e);
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
- try {
414
- await writeResult();
415
- if (collectPerformance) {
416
- result.performance = performance;
417
- }
418
- await callbacks.reduce(async (previous, callback) => {
419
- await previous;
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
- }, Promise.resolve());
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 playwright in your dependencies to use "${browserName}"`,
580
+ `"playwright" not found. You need ${dependencyName} in your dependencies to use "${browserName}"`,
505
581
  {
506
- suggestion: `npm install --save-dev playwright`,
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.