@jsenv/test 1.6.9 → 1.7.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.
@@ -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,11 +8,11 @@ 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";
@@ -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,96 @@ 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
+ if (label) {
4081
+ message += label;
4082
+ }
4083
+ Object.keys(details).forEach((key) => {
4084
+ if (message.length > 0) {
4085
+ message += "\n";
4086
+ }
4087
+ message += `${key}: ${details[key]}`;
4088
+ });
4089
+ if (consoleOutput) {
4090
+ message += `\n${consoleOutput}`;
4091
+ }
4092
+ if (errorsOutput) {
4093
+ message += `\n${errorsOutput}`;
4094
+ }
4095
+ return message;
4096
+ };
4097
+
4098
+ const formatExecutionLabel = (
4099
+ {
4100
+ executionIndex,
4101
+ executionParams,
4102
+ executionResult,
4103
+ timeEllapsed,
4104
+ memoryHeap,
4105
+ counters,
4106
+ },
4107
+ { logTimeUsage, logMemoryHeapUsage } = {},
4108
+ ) => {
4109
+ const { status } = executionResult;
4110
+ const descriptionFormatter = descriptionFormatters[status];
4111
+ const description = descriptionFormatter({
4112
+ index: executionIndex,
4113
+ total: counters.total,
4114
+ executionParams,
4115
+ });
4116
+ const intermediateSummaryText = createIntermediateSummary({
4117
+ executionIndex,
4118
+ counters,
4119
+ timeEllapsed,
4120
+ memoryHeap,
4121
+ logTimeUsage,
4122
+ logMemoryHeapUsage,
4123
+ });
4124
+ return `${description}${intermediateSummaryText}`;
4125
+ };
4126
+
4047
4127
  const formatErrors = (errors) => {
4048
4128
  if (errors.length === 0) {
4049
4129
  return "";
4050
4130
  }
4051
- const formatError = (error) => error.stack || error.message || error;
4131
+ const formatError = (error) => {
4132
+ if (error === null || error === undefined) {
4133
+ return String(error);
4134
+ }
4135
+ if (error) {
4136
+ return error.stack || error.message || error;
4137
+ }
4138
+ return error;
4139
+ };
4052
4140
 
4053
4141
  if (errors.length === 1) {
4054
4142
  return `${ANSI.color(`-------- error --------`, ANSI.RED)}
@@ -4071,13 +4159,17 @@ ${output.join(`\n`)}
4071
4159
  ${ANSI.color(`-------------------------`, ANSI.RED)}`;
4072
4160
  };
4073
4161
 
4074
- const createSummaryLog = (
4162
+ const formatSummaryLog = (
4075
4163
  summary,
4076
4164
  ) => `-------------- summary -----------------
4077
- ${createAllExecutionsSummary(summary)}
4078
- total duration: ${msAsDuration(summary.duration)}
4165
+ ${formatSummary(summary)}
4079
4166
  ----------------------------------------`;
4080
4167
 
4168
+ const formatSummary = (summary) => `${createAllExecutionsSummary(
4169
+ summary,
4170
+ )}
4171
+ total duration: ${msAsDuration(summary.duration)}`;
4172
+
4081
4173
  const createAllExecutionsSummary = ({ counters }) => {
4082
4174
  if (counters.total === 0) {
4083
4175
  return `no execution`;
@@ -4094,6 +4186,8 @@ const createIntermediateSummary = ({
4094
4186
  counters,
4095
4187
  memoryHeap,
4096
4188
  timeEllapsed,
4189
+ logTimeUsage,
4190
+ logMemoryHeapUsage,
4097
4191
  }) => {
4098
4192
  const parts = [];
4099
4193
  if (executionIndex > 0 || counters.done > 0) {
@@ -4106,10 +4200,10 @@ const createIntermediateSummary = ({
4106
4200
  }),
4107
4201
  );
4108
4202
  }
4109
- if (timeEllapsed) {
4203
+ if (logTimeUsage && timeEllapsed) {
4110
4204
  parts.push(`duration: ${msAsEllapsedTime(timeEllapsed)}`);
4111
4205
  }
4112
- if (memoryHeap) {
4206
+ if (logMemoryHeapUsage && memoryHeap) {
4113
4207
  parts.push(`memory heap: ${byteAsMemoryUsage(memoryHeap)}`);
4114
4208
  }
4115
4209
  if (parts.length === 0) {
@@ -4363,27 +4457,6 @@ const formatConsoleSummary = (repartition) => {
4363
4457
  return `console (${parts.join(" ")})`;
4364
4458
  };
4365
4459
 
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
4460
  const executeSteps = async (
4388
4461
  executionSteps,
4389
4462
  {
@@ -4549,6 +4622,9 @@ const executeSteps = async (
4549
4622
  executionResult: {
4550
4623
  status: "executing",
4551
4624
  },
4625
+ counters,
4626
+ timeEllapsed: Date.now() - startMs,
4627
+ memoryHeap: memoryUsage().heapUsed,
4552
4628
  };
4553
4629
  if (typeof executionParams.allocatedMs === "function") {
4554
4630
  executionParams.allocatedMs =
@@ -4560,17 +4636,10 @@ const executeSteps = async (
4560
4636
  log: executionLog,
4561
4637
  render: () => {
4562
4638
  return createExecutionLog(beforeExecutionInfo, {
4563
- counters,
4564
4639
  logRuntime,
4565
4640
  logEachDuration,
4566
- ...(logTimeUsage
4567
- ? {
4568
- timeEllapsed: Date.now() - startMs,
4569
- }
4570
- : {}),
4571
- ...(logMemoryHeapUsage
4572
- ? { memoryHeap: memoryUsage().heapUsed }
4573
- : {}),
4641
+ logTimeUsage,
4642
+ logMemoryHeapUsage,
4574
4643
  });
4575
4644
  },
4576
4645
  });
@@ -4621,7 +4690,6 @@ const executeSteps = async (
4621
4690
  endMs: Date.now(),
4622
4691
  executionResult,
4623
4692
  };
4624
- afterExecutionCallback(afterExecutionInfo);
4625
4693
 
4626
4694
  if (executionResult.status === "aborted") {
4627
4695
  counters.aborted++;
@@ -4638,7 +4706,6 @@ const executeSteps = async (
4638
4706
  if (executionLogsEnabled) {
4639
4707
  const log = createExecutionLog(afterExecutionInfo, {
4640
4708
  logShortForCompletedExecutions,
4641
- counters,
4642
4709
  logRuntime,
4643
4710
  logEachDuration,
4644
4711
  ...(logTimeUsage
@@ -4666,6 +4733,7 @@ const executeSteps = async (
4666
4733
  executionLog = createLog({ newLine: "" });
4667
4734
  }
4668
4735
  }
4736
+ afterExecutionCallback(afterExecutionInfo);
4669
4737
  const isLastExecutionLog = executionIndex === executionSteps.length - 1;
4670
4738
  const cancelRemaining =
4671
4739
  failFast &&
@@ -4693,7 +4761,7 @@ const executeSteps = async (
4693
4761
  duration: Date.now() - startMs,
4694
4762
  };
4695
4763
  if (logSummary) {
4696
- const summaryLog = createSummaryLog(summary);
4764
+ const summaryLog = formatSummaryLog(summary);
4697
4765
  rawOutput += stripAnsi(summaryLog);
4698
4766
  logger.info(summaryLog);
4699
4767
  }
@@ -4785,6 +4853,137 @@ const executeInParallel = async ({
4785
4853
  return executionResults;
4786
4854
  };
4787
4855
 
4856
+ const githubAnnotationFromError = (
4857
+ error,
4858
+ { rootDirectoryUrl, executionInfo },
4859
+ ) => {
4860
+ const annotation = {
4861
+ annotation_level: "failure",
4862
+ path: executionInfo.fileRelativeUrl,
4863
+ start_line: 1,
4864
+ end_line: 1,
4865
+ title: `Error while executing ${executionInfo.fileRelativeUrl} on ${executionInfo.runtimeName}@${executionInfo.runtimeVersion}`,
4866
+ };
4867
+ const exception = asException(error, { rootDirectoryUrl });
4868
+ if (typeof exception.site.line === "number") {
4869
+ annotation.path = urlToRelativeUrl(exception.site.url, rootDirectoryUrl);
4870
+ annotation.start_line = exception.site.line;
4871
+ annotation.end_line = exception.site.line;
4872
+ annotation.start_column = exception.site.column;
4873
+ annotation.end_column = exception.site.column;
4874
+ }
4875
+ annotation.message = exception.stack;
4876
+ return annotation;
4877
+ };
4878
+
4879
+ const asException = (error, { rootDirectoryUrl }) => {
4880
+ const exception = {
4881
+ isException: true,
4882
+ stack: "",
4883
+ site: {},
4884
+ };
4885
+ if (error === null || error === undefined || typeof error === "string") {
4886
+ exception.message = String(error);
4887
+ return exception;
4888
+ }
4889
+ if (error) {
4890
+ exception.message = error.message;
4891
+ if (error.isException) {
4892
+ Object.assign(exception, error);
4893
+ exception.stack = replaceUrls(
4894
+ error.stack,
4895
+ ({ match, url, line = 1, column = 1 }) => {
4896
+ if (urlIsInsideOf(url, rootDirectoryUrl)) {
4897
+ const relativeUrl = urlToRelativeUrl(url, rootDirectoryUrl);
4898
+ match = stringifyUrlSite({ url: relativeUrl, line, column });
4899
+ }
4900
+ return match;
4901
+ },
4902
+ );
4903
+ } else if (error.stack) {
4904
+ let firstSite = true;
4905
+ exception.stack = replaceUrls(
4906
+ error.stack,
4907
+ ({ match, url, line = 1, column = 1 }) => {
4908
+ if (urlIsInsideOf(url, rootDirectoryUrl)) {
4909
+ const relativeUrl = urlToRelativeUrl(url, rootDirectoryUrl);
4910
+ match = stringifyUrlSite({ url: relativeUrl, line, column });
4911
+ }
4912
+ if (firstSite) {
4913
+ firstSite = false;
4914
+ exception.site.url = url;
4915
+ exception.site.line = line;
4916
+ exception.site.column = column;
4917
+ }
4918
+ return match;
4919
+ },
4920
+ );
4921
+ }
4922
+
4923
+ return exception;
4924
+ }
4925
+ exception.message = error;
4926
+ return exception;
4927
+ };
4928
+
4929
+ const stringifyUrlSite = ({ url, line, column }) => {
4930
+ let string = url;
4931
+ if (typeof line === "number") {
4932
+ string += `:${line}`;
4933
+ if (typeof column === "number") {
4934
+ string += `:${column}`;
4935
+ }
4936
+ }
4937
+ return string;
4938
+ };
4939
+
4940
+ const replaceUrls = (source, replace) => {
4941
+ return source.replace(/(?:https?|ftp|file):\/\/\S+/gm, (match) => {
4942
+ let replacement = "";
4943
+ const lastChar = match[match.length - 1];
4944
+
4945
+ // hotfix because our url regex sucks a bit
4946
+ const endsWithSeparationChar = lastChar === ")" || lastChar === ":";
4947
+ if (endsWithSeparationChar) {
4948
+ match = match.slice(0, -1);
4949
+ }
4950
+
4951
+ const lineAndColumnPattern = /:([0-9]+):([0-9]+)$/;
4952
+ const lineAndColumMatch = match.match(lineAndColumnPattern);
4953
+ if (lineAndColumMatch) {
4954
+ const lineAndColumnString = lineAndColumMatch[0];
4955
+ const lineString = lineAndColumMatch[1];
4956
+ const columnString = lineAndColumMatch[2];
4957
+ replacement = replace({
4958
+ match: lineAndColumMatch,
4959
+ url: match.slice(0, -lineAndColumnString.length),
4960
+ line: lineString ? parseInt(lineString) : undefined,
4961
+ column: columnString ? parseInt(columnString) : undefined,
4962
+ });
4963
+ } else {
4964
+ const linePattern = /:([0-9]+)$/;
4965
+ const lineMatch = match.match(linePattern);
4966
+ if (lineMatch) {
4967
+ const lineString = lineMatch[0];
4968
+ replacement = replace({
4969
+ match: lineMatch,
4970
+ url: match.slice(0, -lineString.length),
4971
+ line: lineString ? parseInt(lineString) : undefined,
4972
+ });
4973
+ } else {
4974
+ replacement = replace({
4975
+ match: lineMatch,
4976
+ url: match,
4977
+ });
4978
+ }
4979
+ }
4980
+ if (endsWithSeparationChar) {
4981
+ return `${replacement}${lastChar}`;
4982
+ }
4983
+ return replacement;
4984
+ });
4985
+ };
4986
+
4788
4987
  /**
4789
4988
  * Execute a list of files and log how it goes.
4790
4989
  * @param {Object} testPlanParameters
@@ -4832,6 +5031,15 @@ const executeTestPlan = async ({
4832
5031
  cooldownBetweenExecutions = 0,
4833
5032
  gcBetweenExecutions = logMemoryHeapUsage,
4834
5033
 
5034
+ githubCheckEnabled = Boolean(process.env.GITHUB_WORKFLOW),
5035
+ githubCheckLogLevel,
5036
+ githubCheckName = "Jsenv tests",
5037
+ githubCheckTitle,
5038
+ githubCheckToken,
5039
+ githubCheckRepositoryOwner,
5040
+ githubCheckRepositoryName,
5041
+ githubCheckCommitSha,
5042
+
4835
5043
  coverageEnabled = process.argv.includes("--coverage"),
4836
5044
  coverageConfig = {
4837
5045
  "file:///**/node_modules/": false,
@@ -4946,6 +5154,47 @@ const executeTestPlan = async ({
4946
5154
  });
4947
5155
  }
4948
5156
 
5157
+ if (githubCheckEnabled && !process.env.GITHUB_TOKEN) {
5158
+ githubCheckEnabled = false;
5159
+ const suggestions = [];
5160
+ if (process.env.GITHUB_WORKFLOW_REF) {
5161
+ const workflowFileRef = process.env.GITHUB_WORKFLOW_REF;
5162
+ const refsIndex = workflowFileRef.indexOf("@refs/");
5163
+ // see "GITHUB_WORKFLOW_REF" in https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
5164
+ const workflowFilePath =
5165
+ refsIndex === -1
5166
+ ? workflowFileRef
5167
+ : workflowFileRef.slice(0, refsIndex);
5168
+ suggestions.push(`Pass github token in ${workflowFilePath} during job "${process.env.GITHUB_JOB}"
5169
+ \`\`\`yml
5170
+ env:
5171
+ GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
5172
+ \`\`\``);
5173
+ }
5174
+ suggestions.push(`Disable github check with githubCheckEnabled: false`);
5175
+ logger.warn(
5176
+ `${
5177
+ UNICODE.WARNING
5178
+ } githubCheckEnabled but process.env.GITHUB_TOKEN is missing.
5179
+ Integration with Github check API is disabled
5180
+ To fix this warning:
5181
+ - ${suggestions.join("\n- ")}
5182
+ `,
5183
+ );
5184
+ }
5185
+ if (githubCheckEnabled) {
5186
+ const githubCheckInfoFromEnv = process.env.GITHUB_WORKFLOW
5187
+ ? readGitHubWorkflowEnv()
5188
+ : {};
5189
+ githubCheckToken = githubCheckToken || githubCheckInfoFromEnv.githubToken;
5190
+ githubCheckRepositoryOwner =
5191
+ githubCheckRepositoryOwner || githubCheckInfoFromEnv.repositoryOwner;
5192
+ githubCheckRepositoryName =
5193
+ githubCheckRepositoryName || githubCheckInfoFromEnv.repositoryName;
5194
+ githubCheckCommitSha =
5195
+ githubCheckCommitSha || githubCheckInfoFromEnv.commitSha;
5196
+ }
5197
+
4949
5198
  if (coverageEnabled) {
4950
5199
  if (coverageMethodForBrowsers === undefined) {
4951
5200
  coverageMethodForBrowsers = someHasCoverageV8
@@ -5079,6 +5328,61 @@ const executeTestPlan = async ({
5079
5328
  rootDirectoryUrl,
5080
5329
  });
5081
5330
  logger.debug(`${executionSteps.length} executions planned`);
5331
+ let beforeExecutionCallback;
5332
+ let afterExecutionCallback;
5333
+ let afterAllExecutionCallback = () => {};
5334
+ if (githubCheckEnabled) {
5335
+ const githubCheckRun = await startGithubCheckRun({
5336
+ logLevel: githubCheckLogLevel,
5337
+ githubToken: githubCheckToken,
5338
+ repositoryOwner: githubCheckRepositoryOwner,
5339
+ repositoryName: githubCheckRepositoryName,
5340
+ commitSha: githubCheckCommitSha,
5341
+ checkName: githubCheckName,
5342
+ checkTitle: `Tests executions`,
5343
+ checkSummary: `${executionSteps.length} files will be executed`,
5344
+ });
5345
+ afterExecutionCallback = (afterExecutionInfo) => {
5346
+ const summary = stripAnsi(
5347
+ formatExecutionLabel(afterExecutionInfo, {
5348
+ logTimeUsage,
5349
+ logMemoryHeapUsage,
5350
+ }),
5351
+ );
5352
+ const annotations = [];
5353
+ const { executionResult } = afterExecutionInfo;
5354
+ const { errors = [] } = executionResult;
5355
+ for (const error of errors) {
5356
+ const annotation = githubAnnotationFromError(error, {
5357
+ rootDirectoryUrl,
5358
+ executionInfo: afterExecutionInfo,
5359
+ });
5360
+ annotations.push(annotation);
5361
+ }
5362
+ githubCheckRun.progress({
5363
+ title: "Jsenv test executions",
5364
+ summary,
5365
+ annotations,
5366
+ });
5367
+ };
5368
+ afterAllExecutionCallback = async ({ testPlanSummary }) => {
5369
+ const title = "Jsenv test results";
5370
+ const summary = stripAnsi(formatSummary(testPlanSummary));
5371
+ if (
5372
+ testPlanSummary.counters.total !== testPlanSummary.counters.completed
5373
+ ) {
5374
+ await githubCheckRun.fail({
5375
+ title,
5376
+ summary,
5377
+ });
5378
+ return;
5379
+ }
5380
+ await githubCheckRun.pass({
5381
+ title,
5382
+ summary,
5383
+ });
5384
+ };
5385
+ }
5082
5386
 
5083
5387
  const result = await executeSteps(executionSteps, {
5084
5388
  signal,
@@ -5103,6 +5407,14 @@ const executeTestPlan = async ({
5103
5407
  cooldownBetweenExecutions,
5104
5408
  gcBetweenExecutions,
5105
5409
 
5410
+ githubCheckEnabled,
5411
+ githubCheckName,
5412
+ githubCheckTitle,
5413
+ githubCheckToken,
5414
+ githubCheckRepositoryOwner,
5415
+ githubCheckRepositoryName,
5416
+ githubCheckCommitSha,
5417
+
5106
5418
  coverageEnabled,
5107
5419
  coverageConfig,
5108
5420
  coverageIncludeMissing,
@@ -5110,11 +5422,14 @@ const executeTestPlan = async ({
5110
5422
  coverageMethodForNodeJs,
5111
5423
  coverageV8ConflictWarning,
5112
5424
  coverageTempDirectoryUrl,
5425
+
5426
+ beforeExecutionCallback,
5427
+ afterExecutionCallback,
5113
5428
  });
5114
- if (
5115
- updateProcessExitCode &&
5116
- result.planSummary.counters.total !== result.planSummary.counters.completed
5117
- ) {
5429
+
5430
+ const hasFailed =
5431
+ result.planSummary.counters.total !== result.planSummary.counters.completed;
5432
+ if (updateProcessExitCode && hasFailed) {
5118
5433
  process.exitCode = 1;
5119
5434
  }
5120
5435
  const planCoverage = result.planCoverage;
@@ -5161,12 +5476,15 @@ const executeTestPlan = async ({
5161
5476
  }
5162
5477
  await Promise.all(promises);
5163
5478
  }
5164
- return {
5479
+
5480
+ const returnValue = {
5165
5481
  testPlanAborted: result.aborted,
5166
5482
  testPlanSummary: result.planSummary,
5167
5483
  testPlanReport: result.planReport,
5168
5484
  testPlanCoverage: planCoverage,
5169
5485
  };
5486
+ await afterAllExecutionCallback(returnValue);
5487
+ return returnValue;
5170
5488
  };
5171
5489
 
5172
5490
  const memoize = (compute) => {
@@ -5756,15 +6074,9 @@ const createRuntimeUsingPlaywright = ({
5756
6074
  if (executionResult.status === "failed") {
5757
6075
  result.status = "failed";
5758
6076
  if (executionResult.exception) {
5759
- result.errors.push({
5760
- ...executionResult.exception,
5761
- stack: executionResult.exception.text,
5762
- });
6077
+ result.errors.push(executionResult.exception);
5763
6078
  } else {
5764
- result.errors.push({
5765
- ...executionResult.error,
5766
- stack: executionResult.error.stack,
5767
- });
6079
+ result.errors.push(executionResult.error);
5768
6080
  }
5769
6081
  }
5770
6082
  });
@@ -6340,8 +6652,8 @@ const NO_EXPERIMENTAL_WARNING_FILE_URL = new URL(
6340
6652
  import.meta.url,
6341
6653
  ).href;
6342
6654
 
6343
- const CONTROLLABLE_CHILD_PROCESS_URL = new URL(
6344
- "./controllable_child_process.mjs",
6655
+ const CONTROLLED_CHILD_PROCESS_URL = new URL(
6656
+ "./node_child_process_controlled.mjs",
6345
6657
  import.meta.url,
6346
6658
  ).href;
6347
6659
 
@@ -6428,10 +6740,10 @@ const nodeChildProcess = ({
6428
6740
  };
6429
6741
  logger[logProcessCommand ? "info" : "debug"](
6430
6742
  `${process.argv[0]} ${execArgv.join(" ")} ${fileURLToPath(
6431
- CONTROLLABLE_CHILD_PROCESS_URL,
6743
+ CONTROLLED_CHILD_PROCESS_URL,
6432
6744
  )}`,
6433
6745
  );
6434
- const childProcess = fork(fileURLToPath(CONTROLLABLE_CHILD_PROCESS_URL), {
6746
+ const childProcess = fork(fileURLToPath(CONTROLLED_CHILD_PROCESS_URL), {
6435
6747
  execArgv,
6436
6748
  // silent: true
6437
6749
  stdio: ["pipe", "pipe", "pipe", "ipc"],
@@ -6681,8 +6993,7 @@ const onceChildProcessMessage = (childProcess, type, callback) => {
6681
6993
  const onmessage = (message) => {
6682
6994
  if (message && message.jsenv && message.type === type) {
6683
6995
  childProcess.removeListener("message", onmessage);
6684
- // eslint-disable-next-line no-eval
6685
- callback(message.data ? eval(`(${message.data})`) : "");
6996
+ callback(message.data ? JSON.parse(message.data) : "");
6686
6997
  }
6687
6998
  };
6688
6999
  childProcess.on("message", onmessage);
@@ -6702,8 +7013,8 @@ const onceChildProcessEvent = (childProcess, type, callback) => {
6702
7013
  // https://nodejs.org/api/worker_threads.html
6703
7014
  // https://github.com/avajs/ava/blob/576f534b345259055c95fa0c2b33bef10847a2af/lib/worker/base.js
6704
7015
 
6705
- const CONTROLLABLE_WORKER_THREAD_URL = new URL(
6706
- "./controllable_worker_thread.mjs",
7016
+ const CONTROLLED_WORKER_THREAD_URL = new URL(
7017
+ "./node_worker_thread_controlled.mjs",
6707
7018
  import.meta.url,
6708
7019
  ).href;
6709
7020
 
@@ -6780,7 +7091,7 @@ const nodeWorkerThread = ({
6780
7091
  actionOperation.addAbortSignal(signal);
6781
7092
  // https://nodejs.org/api/worker_threads.html#new-workerfilename-options
6782
7093
  const workerThread = new Worker(
6783
- fileURLToPath(CONTROLLABLE_WORKER_THREAD_URL),
7094
+ fileURLToPath(CONTROLLED_WORKER_THREAD_URL),
6784
7095
  {
6785
7096
  env: envForWorkerThread,
6786
7097
  execArgv: execArgvForWorkerThread,
@@ -6967,8 +7278,7 @@ const onceWorkerThreadMessage = (workerThread, type, callback) => {
6967
7278
  const onmessage = (message) => {
6968
7279
  if (message && message.jsenv && message.type === type) {
6969
7280
  workerThread.removeListener("message", onmessage);
6970
- // eslint-disable-next-line no-eval
6971
- callback(message.data ? eval(`(${message.data})`) : undefined);
7281
+ callback(message.data ? JSON.parse(message.data) : undefined);
6972
7282
  }
6973
7283
  };
6974
7284
  workerThread.on("message", onmessage);