@jsenv/test 1.6.9 → 1.7.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.
@@ -1,4 +1,5 @@
1
1
  import { chmod, stat, lstat, readdir, promises, unlink, openSync, closeSync, rmdir, readFile as readFile$1, writeFile as writeFile$1, writeFileSync as writeFileSync$1, mkdirSync, readFileSync, existsSync, readdirSync } from "node:fs";
2
+ import stripAnsi from "strip-ansi";
2
3
  import { URL_META, filterV8Coverage } from "./js/v8_coverage.js";
3
4
  import { pathToFileURL, fileURLToPath } from "node:url";
4
5
  import crypto from "node:crypto";
@@ -7,16 +8,16 @@ import process$1, { memoryUsage } from "node:process";
7
8
  import os from "node:os";
8
9
  import tty from "node:tty";
9
10
  import stringWidth from "string-width";
11
+ import { readGitHubWorkflowEnv, startGithubCheckRun } from "@jsenv/github-check-run";
10
12
  import { createRequire } from "node:module";
11
13
  import { spawn, spawnSync, fork } from "node:child_process";
12
14
  import { createServer } from "node:net";
13
15
  import v8, { takeCoverage } from "node:v8";
14
- import stripAnsi from "strip-ansi";
15
16
  import { applyBabelPlugins } from "@jsenv/ast";
16
17
  import { runInNewContext } from "node:vm";
17
18
  import wrapAnsi from "wrap-ansi";
18
19
  import { injectSupervisorIntoHTML, supervisorFileUrl } from "@jsenv/plugin-supervisor";
19
- import { SOURCEMAP, generateSourcemapDataUrl } from "@jsenv/sourcemap";
20
+ import { generateSourcemapDataUrl, SOURCEMAP } from "@jsenv/sourcemap";
20
21
  import { findFreePort } from "@jsenv/server";
21
22
  import { Worker } from "node:worker_threads";
22
23
 
@@ -3977,53 +3978,55 @@ const createExecutionLog = (
3977
3978
  executionResult,
3978
3979
  startMs,
3979
3980
  endMs,
3981
+ nowMs,
3982
+ timeEllapsed,
3983
+ memoryHeap,
3984
+ counters,
3980
3985
  },
3981
3986
  {
3982
3987
  logShortForCompletedExecutions,
3983
- counters,
3984
3988
  logRuntime,
3985
3989
  logEachDuration,
3986
- timeEllapsed,
3987
- memoryHeap,
3990
+ logTimeUsage,
3991
+ logMemoryHeapUsage,
3988
3992
  },
3989
3993
  ) => {
3994
+ const label = formatExecutionLabel(
3995
+ {
3996
+ executionIndex,
3997
+ executionParams,
3998
+ executionResult,
3999
+ timeEllapsed,
4000
+ memoryHeap,
4001
+ counters,
4002
+ },
4003
+ {
4004
+ logTimeUsage,
4005
+ logMemoryHeapUsage,
4006
+ },
4007
+ );
4008
+
3990
4009
  const { status } = executionResult;
3991
- const descriptionFormatter = descriptionFormatters[status];
3992
- const description = descriptionFormatter({
3993
- index: executionIndex,
3994
- total: counters.total,
3995
- executionParams,
3996
- });
3997
- const summary = createIntermediateSummary({
3998
- executionIndex,
3999
- counters,
4000
- timeEllapsed,
4001
- memoryHeap,
4002
- });
4003
4010
  let log;
4004
4011
  if (logShortForCompletedExecutions && status === "completed") {
4005
- log = `${description}${summary}`;
4012
+ log = label;
4006
4013
  } else {
4007
- const { consoleCalls = [], errors = [] } = executionResult;
4008
- const consoleOutput = formatConsoleCalls(consoleCalls);
4009
- const errorsOutput = formatErrors(errors);
4010
- log = formatExecution({
4011
- label: `${description}${summary}`,
4012
- details: {
4013
- file: fileRelativeUrl,
4014
- ...(logRuntime ? { runtime: `${runtimeName}/${runtimeVersion}` } : {}),
4015
- ...(logEachDuration
4016
- ? {
4017
- duration:
4018
- status === "executing"
4019
- ? msAsEllapsedTime(Date.now() - startMs)
4020
- : msAsDuration(endMs - startMs),
4021
- }
4022
- : {}),
4014
+ log = formatExecution(
4015
+ {
4016
+ fileRelativeUrl,
4017
+ runtimeName,
4018
+ runtimeVersion,
4019
+ executionResult,
4020
+ startMs,
4021
+ endMs,
4022
+ nowMs,
4023
4023
  },
4024
- consoleOutput,
4025
- errorsOutput,
4026
- });
4024
+ {
4025
+ label,
4026
+ logRuntime,
4027
+ logEachDuration,
4028
+ },
4029
+ );
4027
4030
  }
4028
4031
 
4029
4032
  const { columns = 80 } = process.stdout;
@@ -4044,11 +4047,92 @@ const createExecutionLog = (
4044
4047
  return log;
4045
4048
  };
4046
4049
 
4050
+ const formatExecution = (
4051
+ {
4052
+ fileRelativeUrl,
4053
+ runtimeName,
4054
+ runtimeVersion,
4055
+ executionResult,
4056
+ startMs,
4057
+ endMs,
4058
+ nowMs,
4059
+ },
4060
+ { label, logRuntime = true, logEachDuration = true } = {},
4061
+ ) => {
4062
+ const { status } = executionResult;
4063
+ const { consoleCalls = [], errors = [] } = executionResult;
4064
+ const consoleOutput = formatConsoleCalls(consoleCalls);
4065
+ const errorsOutput = formatErrors(errors);
4066
+
4067
+ const details = {
4068
+ file: fileRelativeUrl,
4069
+ ...(logRuntime ? { runtime: `${runtimeName}/${runtimeVersion}` } : {}),
4070
+ ...(logEachDuration
4071
+ ? {
4072
+ duration:
4073
+ status === "executing"
4074
+ ? msAsEllapsedTime((nowMs || Date.now()) - startMs)
4075
+ : msAsDuration(endMs - startMs),
4076
+ }
4077
+ : {}),
4078
+ };
4079
+ let message = ``;
4080
+ message += label;
4081
+ Object.keys(details).forEach((key) => {
4082
+ message += `
4083
+ ${key}: ${details[key]}`;
4084
+ });
4085
+ if (consoleOutput) {
4086
+ message += `\n${consoleOutput}`;
4087
+ }
4088
+ if (errorsOutput) {
4089
+ message += `\n${errorsOutput}`;
4090
+ }
4091
+ return message;
4092
+ };
4093
+
4094
+ const formatExecutionLabel = (
4095
+ {
4096
+ executionIndex,
4097
+ executionParams,
4098
+ executionResult,
4099
+ timeEllapsed,
4100
+ memoryHeap,
4101
+ counters,
4102
+ },
4103
+ { logTimeUsage, logMemoryHeapUsage } = {},
4104
+ ) => {
4105
+ const { status } = executionResult;
4106
+ const descriptionFormatter = descriptionFormatters[status];
4107
+ const description = descriptionFormatter({
4108
+ index: executionIndex,
4109
+ total: counters.total,
4110
+ executionParams,
4111
+ });
4112
+ const intermediateSummaryText = createIntermediateSummary({
4113
+ executionIndex,
4114
+ counters,
4115
+ timeEllapsed,
4116
+ memoryHeap,
4117
+ logTimeUsage,
4118
+ logMemoryHeapUsage,
4119
+ });
4120
+ return `${description}${intermediateSummaryText}`;
4121
+ };
4122
+
4047
4123
  const formatErrors = (errors) => {
4048
4124
  if (errors.length === 0) {
4049
4125
  return "";
4050
4126
  }
4051
- const formatError = (error) => error.stack || error.message || error;
4127
+ const formatError = (error) => {
4128
+ if (error === null || error === undefined) {
4129
+ return String(error);
4130
+ }
4131
+ if (error) {
4132
+ return error.stack || error.message || error;
4133
+ }
4134
+ return error;
4135
+ };
4052
4136
 
4053
4137
  if (errors.length === 1) {
4054
4138
  return `${ANSI.color(`-------- error --------`, ANSI.RED)}
@@ -4071,13 +4155,17 @@ ${output.join(`\n`)}
4071
4155
  ${ANSI.color(`-------------------------`, ANSI.RED)}`;
4072
4156
  };
4073
4157
 
4074
- const createSummaryLog = (
4158
+ const formatSummaryLog = (
4075
4159
  summary,
4076
4160
  ) => `-------------- summary -----------------
4077
- ${createAllExecutionsSummary(summary)}
4078
- total duration: ${msAsDuration(summary.duration)}
4161
+ ${formatSummary(summary)}
4079
4162
  ----------------------------------------`;
4080
4163
 
4164
+ const formatSummary = (summary) => `${createAllExecutionsSummary(
4165
+ summary,
4166
+ )}
4167
+ total duration: ${msAsDuration(summary.duration)}`;
4168
+
4081
4169
  const createAllExecutionsSummary = ({ counters }) => {
4082
4170
  if (counters.total === 0) {
4083
4171
  return `no execution`;
@@ -4094,6 +4182,8 @@ const createIntermediateSummary = ({
4094
4182
  counters,
4095
4183
  memoryHeap,
4096
4184
  timeEllapsed,
4185
+ logTimeUsage,
4186
+ logMemoryHeapUsage,
4097
4187
  }) => {
4098
4188
  const parts = [];
4099
4189
  if (executionIndex > 0 || counters.done > 0) {
@@ -4106,10 +4196,10 @@ const createIntermediateSummary = ({
4106
4196
  }),
4107
4197
  );
4108
4198
  }
4109
- if (timeEllapsed) {
4199
+ if (logTimeUsage && timeEllapsed) {
4110
4200
  parts.push(`duration: ${msAsEllapsedTime(timeEllapsed)}`);
4111
4201
  }
4112
- if (memoryHeap) {
4202
+ if (logMemoryHeapUsage && memoryHeap) {
4113
4203
  parts.push(`memory heap: ${byteAsMemoryUsage(memoryHeap)}`);
4114
4204
  }
4115
4205
  if (parts.length === 0) {
@@ -4363,27 +4453,6 @@ const formatConsoleSummary = (repartition) => {
4363
4453
  return `console (${parts.join(" ")})`;
4364
4454
  };
4365
4455
 
4366
- const formatExecution = ({
4367
- label,
4368
- details = {},
4369
- consoleOutput,
4370
- errorsOutput,
4371
- }) => {
4372
- let message = ``;
4373
- message += label;
4374
- Object.keys(details).forEach((key) => {
4375
- message += `
4376
- ${key}: ${details[key]}`;
4377
- });
4378
- if (consoleOutput) {
4379
- message += `\n${consoleOutput}`;
4380
- }
4381
- if (errorsOutput) {
4382
- message += `\n${errorsOutput}`;
4383
- }
4384
- return message;
4385
- };
4386
-
4387
4456
  const executeSteps = async (
4388
4457
  executionSteps,
4389
4458
  {
@@ -4549,6 +4618,9 @@ const executeSteps = async (
4549
4618
  executionResult: {
4550
4619
  status: "executing",
4551
4620
  },
4621
+ counters,
4622
+ timeEllapsed: Date.now() - startMs,
4623
+ memoryHeap: memoryUsage().heapUsed,
4552
4624
  };
4553
4625
  if (typeof executionParams.allocatedMs === "function") {
4554
4626
  executionParams.allocatedMs =
@@ -4560,17 +4632,10 @@ const executeSteps = async (
4560
4632
  log: executionLog,
4561
4633
  render: () => {
4562
4634
  return createExecutionLog(beforeExecutionInfo, {
4563
- counters,
4564
4635
  logRuntime,
4565
4636
  logEachDuration,
4566
- ...(logTimeUsage
4567
- ? {
4568
- timeEllapsed: Date.now() - startMs,
4569
- }
4570
- : {}),
4571
- ...(logMemoryHeapUsage
4572
- ? { memoryHeap: memoryUsage().heapUsed }
4573
- : {}),
4637
+ logTimeUsage,
4638
+ logMemoryHeapUsage,
4574
4639
  });
4575
4640
  },
4576
4641
  });
@@ -4621,7 +4686,6 @@ const executeSteps = async (
4621
4686
  endMs: Date.now(),
4622
4687
  executionResult,
4623
4688
  };
4624
- afterExecutionCallback(afterExecutionInfo);
4625
4689
 
4626
4690
  if (executionResult.status === "aborted") {
4627
4691
  counters.aborted++;
@@ -4638,7 +4702,6 @@ const executeSteps = async (
4638
4702
  if (executionLogsEnabled) {
4639
4703
  const log = createExecutionLog(afterExecutionInfo, {
4640
4704
  logShortForCompletedExecutions,
4641
- counters,
4642
4705
  logRuntime,
4643
4706
  logEachDuration,
4644
4707
  ...(logTimeUsage
@@ -4666,6 +4729,7 @@ const executeSteps = async (
4666
4729
  executionLog = createLog({ newLine: "" });
4667
4730
  }
4668
4731
  }
4732
+ afterExecutionCallback(afterExecutionInfo);
4669
4733
  const isLastExecutionLog = executionIndex === executionSteps.length - 1;
4670
4734
  const cancelRemaining =
4671
4735
  failFast &&
@@ -4693,7 +4757,7 @@ const executeSteps = async (
4693
4757
  duration: Date.now() - startMs,
4694
4758
  };
4695
4759
  if (logSummary) {
4696
- const summaryLog = createSummaryLog(summary);
4760
+ const summaryLog = formatSummaryLog(summary);
4697
4761
  rawOutput += stripAnsi(summaryLog);
4698
4762
  logger.info(summaryLog);
4699
4763
  }
@@ -4785,6 +4849,137 @@ const executeInParallel = async ({
4785
4849
  return executionResults;
4786
4850
  };
4787
4851
 
4852
+ const githubAnnotationFromError = (
4853
+ error,
4854
+ { rootDirectoryUrl, executionInfo },
4855
+ ) => {
4856
+ const annotation = {
4857
+ annotation_level: "failure",
4858
+ path: executionInfo.fileRelativeUrl,
4859
+ start_line: 1,
4860
+ end_line: 1,
4861
+ title: `Error while executing ${executionInfo.fileRelativeUrl} on ${executionInfo.runtimeName}@${executionInfo.runtimeVersion}`,
4862
+ };
4863
+ const exception = asException(error, { rootDirectoryUrl });
4864
+ if (typeof exception.site.line === "number") {
4865
+ annotation.path = urlToRelativeUrl(exception.site.url, rootDirectoryUrl);
4866
+ annotation.start_line = exception.site.line;
4867
+ annotation.end_line = exception.site.line;
4868
+ annotation.start_column = exception.site.column;
4869
+ annotation.end_column = exception.site.column;
4870
+ }
4871
+ annotation.message = exception.stack;
4872
+ return annotation;
4873
+ };
4874
+
4875
+ const asException = (error, { rootDirectoryUrl }) => {
4876
+ const exception = {
4877
+ isException: true,
4878
+ stack: "",
4879
+ site: {},
4880
+ };
4881
+ if (error === null || error === undefined || typeof error === "string") {
4882
+ exception.message = String(error);
4883
+ return exception;
4884
+ }
4885
+ if (error) {
4886
+ exception.message = error.message;
4887
+ if (error.isException) {
4888
+ Object.assign(exception, error);
4889
+ exception.stack = replaceUrls(
4890
+ error.stack,
4891
+ ({ match, url, line = 1, column = 1 }) => {
4892
+ if (urlIsInsideOf(url, rootDirectoryUrl)) {
4893
+ const relativeUrl = urlToRelativeUrl(url, rootDirectoryUrl);
4894
+ match = stringifyUrlSite({ url: relativeUrl, line, column });
4895
+ }
4896
+ return match;
4897
+ },
4898
+ );
4899
+ } else if (error.stack) {
4900
+ let firstSite = true;
4901
+ exception.stack = replaceUrls(
4902
+ error.stack,
4903
+ ({ match, url, line = 1, column = 1 }) => {
4904
+ if (urlIsInsideOf(url, rootDirectoryUrl)) {
4905
+ const relativeUrl = urlToRelativeUrl(url, rootDirectoryUrl);
4906
+ match = stringifyUrlSite({ url: relativeUrl, line, column });
4907
+ }
4908
+ if (firstSite) {
4909
+ firstSite = false;
4910
+ exception.site.url = url;
4911
+ exception.site.line = line;
4912
+ exception.site.column = column;
4913
+ }
4914
+ return match;
4915
+ },
4916
+ );
4917
+ }
4918
+
4919
+ return exception;
4920
+ }
4921
+ exception.message = error;
4922
+ return exception;
4923
+ };
4924
+
4925
+ const stringifyUrlSite = ({ url, line, column }) => {
4926
+ let string = url;
4927
+ if (typeof line === "number") {
4928
+ string += `:${line}`;
4929
+ if (typeof column === "number") {
4930
+ string += `:${column}`;
4931
+ }
4932
+ }
4933
+ return string;
4934
+ };
4935
+
4936
+ const replaceUrls = (source, replace) => {
4937
+ return source.replace(/(?:https?|ftp|file):\/\/\S+/gm, (match) => {
4938
+ let replacement = "";
4939
+ const lastChar = match[match.length - 1];
4940
+
4941
+ // hotfix because our url regex sucks a bit
4942
+ const endsWithSeparationChar = lastChar === ")" || lastChar === ":";
4943
+ if (endsWithSeparationChar) {
4944
+ match = match.slice(0, -1);
4945
+ }
4946
+
4947
+ const lineAndColumnPattern = /:([0-9]+):([0-9]+)$/;
4948
+ const lineAndColumMatch = match.match(lineAndColumnPattern);
4949
+ if (lineAndColumMatch) {
4950
+ const lineAndColumnString = lineAndColumMatch[0];
4951
+ const lineString = lineAndColumMatch[1];
4952
+ const columnString = lineAndColumMatch[2];
4953
+ replacement = replace({
4954
+ match: lineAndColumMatch,
4955
+ url: match.slice(0, -lineAndColumnString.length),
4956
+ line: lineString ? parseInt(lineString) : undefined,
4957
+ column: columnString ? parseInt(columnString) : undefined,
4958
+ });
4959
+ } else {
4960
+ const linePattern = /:([0-9]+)$/;
4961
+ const lineMatch = match.match(linePattern);
4962
+ if (lineMatch) {
4963
+ const lineString = lineMatch[0];
4964
+ replacement = replace({
4965
+ match: lineMatch,
4966
+ url: match.slice(0, -lineString.length),
4967
+ line: lineString ? parseInt(lineString) : undefined,
4968
+ });
4969
+ } else {
4970
+ replacement = replace({
4971
+ match: lineMatch,
4972
+ url: match,
4973
+ });
4974
+ }
4975
+ }
4976
+ if (endsWithSeparationChar) {
4977
+ return `${replacement}${lastChar}`;
4978
+ }
4979
+ return replacement;
4980
+ });
4981
+ };
4982
+
4788
4983
  /**
4789
4984
  * Execute a list of files and log how it goes.
4790
4985
  * @param {Object} testPlanParameters
@@ -4832,6 +5027,15 @@ const executeTestPlan = async ({
4832
5027
  cooldownBetweenExecutions = 0,
4833
5028
  gcBetweenExecutions = logMemoryHeapUsage,
4834
5029
 
5030
+ githubCheckEnabled = Boolean(process.env.GITHUB_WORKFLOW),
5031
+ githubCheckLogLevel,
5032
+ githubCheckName = "Jsenv tests",
5033
+ githubCheckTitle,
5034
+ githubCheckToken,
5035
+ githubCheckRepositoryOwner,
5036
+ githubCheckRepositoryName,
5037
+ githubCheckCommitSha,
5038
+
4835
5039
  coverageEnabled = process.argv.includes("--coverage"),
4836
5040
  coverageConfig = {
4837
5041
  "file:///**/node_modules/": false,
@@ -4946,6 +5150,47 @@ const executeTestPlan = async ({
4946
5150
  });
4947
5151
  }
4948
5152
 
5153
+ if (githubCheckEnabled && !process.env.GITHUB_TOKEN) {
5154
+ githubCheckEnabled = false;
5155
+ const suggestions = [];
5156
+ if (process.env.GITHUB_WORKFLOW_REF) {
5157
+ const workflowFileRef = process.env.GITHUB_WORKFLOW_REF;
5158
+ const refsIndex = workflowFileRef.indexOf("@refs/");
5159
+ // see "GITHUB_WORKFLOW_REF" in https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
5160
+ const workflowFilePath =
5161
+ refsIndex === -1
5162
+ ? workflowFileRef
5163
+ : workflowFileRef.slice(0, refsIndex);
5164
+ suggestions.push(`Pass github token in ${workflowFilePath} during job "${process.env.GITHUB_JOB}"
5165
+ \`\`\`yml
5166
+ env:
5167
+ GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
5168
+ \`\`\``);
5169
+ }
5170
+ suggestions.push(`Disable github check with githubCheckEnabled: false`);
5171
+ logger.warn(
5172
+ `${
5173
+ UNICODE.WARNING
5174
+ } githubCheckEnabled but process.env.GITHUB_TOKEN is missing.
5175
+ Integration with Github check API is disabled
5176
+ To fix this warning:
5177
+ - ${suggestions.join("\n- ")}
5178
+ `,
5179
+ );
5180
+ }
5181
+ if (githubCheckEnabled) {
5182
+ const githubCheckInfoFromEnv = process.env.GITHUB_WORKFLOW
5183
+ ? readGitHubWorkflowEnv()
5184
+ : {};
5185
+ githubCheckToken = githubCheckToken || githubCheckInfoFromEnv.githubToken;
5186
+ githubCheckRepositoryOwner =
5187
+ githubCheckRepositoryOwner || githubCheckInfoFromEnv.repositoryOwner;
5188
+ githubCheckRepositoryName =
5189
+ githubCheckRepositoryName || githubCheckInfoFromEnv.repositoryName;
5190
+ githubCheckCommitSha =
5191
+ githubCheckCommitSha || githubCheckInfoFromEnv.commitSha;
5192
+ }
5193
+
4949
5194
  if (coverageEnabled) {
4950
5195
  if (coverageMethodForBrowsers === undefined) {
4951
5196
  coverageMethodForBrowsers = someHasCoverageV8
@@ -5079,6 +5324,61 @@ const executeTestPlan = async ({
5079
5324
  rootDirectoryUrl,
5080
5325
  });
5081
5326
  logger.debug(`${executionSteps.length} executions planned`);
5327
+ let beforeExecutionCallback;
5328
+ let afterExecutionCallback;
5329
+ let afterAllExecutionCallback = () => {};
5330
+ if (githubCheckEnabled) {
5331
+ const githubCheckRun = await startGithubCheckRun({
5332
+ logLevel: githubCheckLogLevel,
5333
+ githubToken: githubCheckToken,
5334
+ repositoryOwner: githubCheckRepositoryOwner,
5335
+ repositoryName: githubCheckRepositoryName,
5336
+ commitSha: githubCheckCommitSha,
5337
+ checkName: githubCheckName,
5338
+ checkTitle: `Tests executions`,
5339
+ checkSummary: `${executionSteps.length} files will be executed`,
5340
+ });
5341
+ afterExecutionCallback = (afterExecutionInfo) => {
5342
+ const summary = stripAnsi(
5343
+ formatExecutionLabel(afterExecutionInfo, {
5344
+ logTimeUsage,
5345
+ logMemoryHeapUsage,
5346
+ }),
5347
+ );
5348
+ const annotations = [];
5349
+ const { executionResult } = afterExecutionInfo;
5350
+ const { errors = [] } = executionResult;
5351
+ for (const error of errors) {
5352
+ const annotation = githubAnnotationFromError(error, {
5353
+ rootDirectoryUrl,
5354
+ executionInfo: afterExecutionInfo,
5355
+ });
5356
+ annotations.push(annotation);
5357
+ }
5358
+ githubCheckRun.progress({
5359
+ title: "Jsenv test executions",
5360
+ summary,
5361
+ annotations,
5362
+ });
5363
+ };
5364
+ afterAllExecutionCallback = async ({ testPlanSummary }) => {
5365
+ const title = "Jsenv test results";
5366
+ const summary = stripAnsi(formatSummary(testPlanSummary));
5367
+ if (
5368
+ testPlanSummary.counters.total !== testPlanSummary.counters.completed
5369
+ ) {
5370
+ await githubCheckRun.fail({
5371
+ title,
5372
+ summary,
5373
+ });
5374
+ return;
5375
+ }
5376
+ await githubCheckRun.pass({
5377
+ title,
5378
+ summary,
5379
+ });
5380
+ };
5381
+ }
5082
5382
 
5083
5383
  const result = await executeSteps(executionSteps, {
5084
5384
  signal,
@@ -5103,6 +5403,14 @@ const executeTestPlan = async ({
5103
5403
  cooldownBetweenExecutions,
5104
5404
  gcBetweenExecutions,
5105
5405
 
5406
+ githubCheckEnabled,
5407
+ githubCheckName,
5408
+ githubCheckTitle,
5409
+ githubCheckToken,
5410
+ githubCheckRepositoryOwner,
5411
+ githubCheckRepositoryName,
5412
+ githubCheckCommitSha,
5413
+
5106
5414
  coverageEnabled,
5107
5415
  coverageConfig,
5108
5416
  coverageIncludeMissing,
@@ -5110,11 +5418,14 @@ const executeTestPlan = async ({
5110
5418
  coverageMethodForNodeJs,
5111
5419
  coverageV8ConflictWarning,
5112
5420
  coverageTempDirectoryUrl,
5421
+
5422
+ beforeExecutionCallback,
5423
+ afterExecutionCallback,
5113
5424
  });
5114
- if (
5115
- updateProcessExitCode &&
5116
- result.planSummary.counters.total !== result.planSummary.counters.completed
5117
- ) {
5425
+
5426
+ const hasFailed =
5427
+ result.planSummary.counters.total !== result.planSummary.counters.completed;
5428
+ if (updateProcessExitCode && hasFailed) {
5118
5429
  process.exitCode = 1;
5119
5430
  }
5120
5431
  const planCoverage = result.planCoverage;
@@ -5161,12 +5472,15 @@ const executeTestPlan = async ({
5161
5472
  }
5162
5473
  await Promise.all(promises);
5163
5474
  }
5164
- return {
5475
+
5476
+ const returnValue = {
5165
5477
  testPlanAborted: result.aborted,
5166
5478
  testPlanSummary: result.planSummary,
5167
5479
  testPlanReport: result.planReport,
5168
5480
  testPlanCoverage: planCoverage,
5169
5481
  };
5482
+ await afterAllExecutionCallback(returnValue);
5483
+ return returnValue;
5170
5484
  };
5171
5485
 
5172
5486
  const memoize = (compute) => {
@@ -5756,15 +6070,9 @@ const createRuntimeUsingPlaywright = ({
5756
6070
  if (executionResult.status === "failed") {
5757
6071
  result.status = "failed";
5758
6072
  if (executionResult.exception) {
5759
- result.errors.push({
5760
- ...executionResult.exception,
5761
- stack: executionResult.exception.text,
5762
- });
6073
+ result.errors.push(executionResult.exception);
5763
6074
  } else {
5764
- result.errors.push({
5765
- ...executionResult.error,
5766
- stack: executionResult.error.stack,
5767
- });
6075
+ result.errors.push(executionResult.error);
5768
6076
  }
5769
6077
  }
5770
6078
  });
@@ -6340,8 +6648,8 @@ const NO_EXPERIMENTAL_WARNING_FILE_URL = new URL(
6340
6648
  import.meta.url,
6341
6649
  ).href;
6342
6650
 
6343
- const CONTROLLABLE_CHILD_PROCESS_URL = new URL(
6344
- "./controllable_child_process.mjs",
6651
+ const CONTROLLED_CHILD_PROCESS_URL = new URL(
6652
+ "./node_child_process_controlled.mjs",
6345
6653
  import.meta.url,
6346
6654
  ).href;
6347
6655
 
@@ -6428,10 +6736,10 @@ const nodeChildProcess = ({
6428
6736
  };
6429
6737
  logger[logProcessCommand ? "info" : "debug"](
6430
6738
  `${process.argv[0]} ${execArgv.join(" ")} ${fileURLToPath(
6431
- CONTROLLABLE_CHILD_PROCESS_URL,
6739
+ CONTROLLED_CHILD_PROCESS_URL,
6432
6740
  )}`,
6433
6741
  );
6434
- const childProcess = fork(fileURLToPath(CONTROLLABLE_CHILD_PROCESS_URL), {
6742
+ const childProcess = fork(fileURLToPath(CONTROLLED_CHILD_PROCESS_URL), {
6435
6743
  execArgv,
6436
6744
  // silent: true
6437
6745
  stdio: ["pipe", "pipe", "pipe", "ipc"],
@@ -6681,8 +6989,7 @@ const onceChildProcessMessage = (childProcess, type, callback) => {
6681
6989
  const onmessage = (message) => {
6682
6990
  if (message && message.jsenv && message.type === type) {
6683
6991
  childProcess.removeListener("message", onmessage);
6684
- // eslint-disable-next-line no-eval
6685
- callback(message.data ? eval(`(${message.data})`) : "");
6992
+ callback(message.data ? JSON.parse(message.data) : "");
6686
6993
  }
6687
6994
  };
6688
6995
  childProcess.on("message", onmessage);
@@ -6702,8 +7009,8 @@ const onceChildProcessEvent = (childProcess, type, callback) => {
6702
7009
  // https://nodejs.org/api/worker_threads.html
6703
7010
  // https://github.com/avajs/ava/blob/576f534b345259055c95fa0c2b33bef10847a2af/lib/worker/base.js
6704
7011
 
6705
- const CONTROLLABLE_WORKER_THREAD_URL = new URL(
6706
- "./controllable_worker_thread.mjs",
7012
+ const CONTROLLED_WORKER_THREAD_URL = new URL(
7013
+ "./node_worker_thread_controlled.mjs",
6707
7014
  import.meta.url,
6708
7015
  ).href;
6709
7016
 
@@ -6780,7 +7087,7 @@ const nodeWorkerThread = ({
6780
7087
  actionOperation.addAbortSignal(signal);
6781
7088
  // https://nodejs.org/api/worker_threads.html#new-workerfilename-options
6782
7089
  const workerThread = new Worker(
6783
- fileURLToPath(CONTROLLABLE_WORKER_THREAD_URL),
7090
+ fileURLToPath(CONTROLLED_WORKER_THREAD_URL),
6784
7091
  {
6785
7092
  env: envForWorkerThread,
6786
7093
  execArgv: execArgvForWorkerThread,
@@ -6967,8 +7274,7 @@ const onceWorkerThreadMessage = (workerThread, type, callback) => {
6967
7274
  const onmessage = (message) => {
6968
7275
  if (message && message.jsenv && message.type === type) {
6969
7276
  workerThread.removeListener("message", onmessage);
6970
- // eslint-disable-next-line no-eval
6971
- callback(message.data ? eval(`(${message.data})`) : undefined);
7277
+ callback(message.data ? JSON.parse(message.data) : undefined);
6972
7278
  }
6973
7279
  };
6974
7280
  workerThread.on("message", onmessage);