@jsenv/test 1.6.8 → 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";
@@ -2610,6 +2611,7 @@ const createLog = ({
2610
2611
  write,
2611
2612
  dynamicWrite,
2612
2613
  destroy,
2614
+ stream,
2613
2615
  });
2614
2616
  return log;
2615
2617
  };
@@ -2644,7 +2646,7 @@ const startSpinner = ({
2644
2646
  stopOnVerticalOverflow = true,
2645
2647
  render = () => "",
2646
2648
  effect = () => {},
2647
- animated = true,
2649
+ animated = log.stream.isTTY,
2648
2650
  }) => {
2649
2651
  let frameIndex = 0;
2650
2652
  let interval;
@@ -3976,53 +3978,55 @@ const createExecutionLog = (
3976
3978
  executionResult,
3977
3979
  startMs,
3978
3980
  endMs,
3981
+ nowMs,
3982
+ timeEllapsed,
3983
+ memoryHeap,
3984
+ counters,
3979
3985
  },
3980
3986
  {
3981
3987
  logShortForCompletedExecutions,
3982
- counters,
3983
3988
  logRuntime,
3984
3989
  logEachDuration,
3985
- timeEllapsed,
3986
- memoryHeap,
3990
+ logTimeUsage,
3991
+ logMemoryHeapUsage,
3987
3992
  },
3988
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
+
3989
4009
  const { status } = executionResult;
3990
- const descriptionFormatter = descriptionFormatters[status];
3991
- const description = descriptionFormatter({
3992
- index: executionIndex,
3993
- total: counters.total,
3994
- executionParams,
3995
- });
3996
- const summary = createIntermediateSummary({
3997
- executionIndex,
3998
- counters,
3999
- timeEllapsed,
4000
- memoryHeap,
4001
- });
4002
4010
  let log;
4003
4011
  if (logShortForCompletedExecutions && status === "completed") {
4004
- log = `${description}${summary}`;
4012
+ log = label;
4005
4013
  } else {
4006
- const { consoleCalls = [], errors = [] } = executionResult;
4007
- const consoleOutput = formatConsoleCalls(consoleCalls);
4008
- const errorsOutput = formatErrors(errors);
4009
- log = formatExecution({
4010
- label: `${description}${summary}`,
4011
- details: {
4012
- file: fileRelativeUrl,
4013
- ...(logRuntime ? { runtime: `${runtimeName}/${runtimeVersion}` } : {}),
4014
- ...(logEachDuration
4015
- ? {
4016
- duration:
4017
- status === "executing"
4018
- ? msAsEllapsedTime(Date.now() - startMs)
4019
- : msAsDuration(endMs - startMs),
4020
- }
4021
- : {}),
4014
+ log = formatExecution(
4015
+ {
4016
+ fileRelativeUrl,
4017
+ runtimeName,
4018
+ runtimeVersion,
4019
+ executionResult,
4020
+ startMs,
4021
+ endMs,
4022
+ nowMs,
4022
4023
  },
4023
- consoleOutput,
4024
- errorsOutput,
4025
- });
4024
+ {
4025
+ label,
4026
+ logRuntime,
4027
+ logEachDuration,
4028
+ },
4029
+ );
4026
4030
  }
4027
4031
 
4028
4032
  const { columns = 80 } = process.stdout;
@@ -4043,11 +4047,96 @@ const createExecutionLog = (
4043
4047
  return log;
4044
4048
  };
4045
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
+
4046
4127
  const formatErrors = (errors) => {
4047
4128
  if (errors.length === 0) {
4048
4129
  return "";
4049
4130
  }
4050
- 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
+ };
4051
4140
 
4052
4141
  if (errors.length === 1) {
4053
4142
  return `${ANSI.color(`-------- error --------`, ANSI.RED)}
@@ -4070,13 +4159,17 @@ ${output.join(`\n`)}
4070
4159
  ${ANSI.color(`-------------------------`, ANSI.RED)}`;
4071
4160
  };
4072
4161
 
4073
- const createSummaryLog = (
4162
+ const formatSummaryLog = (
4074
4163
  summary,
4075
4164
  ) => `-------------- summary -----------------
4076
- ${createAllExecutionsSummary(summary)}
4077
- total duration: ${msAsDuration(summary.duration)}
4165
+ ${formatSummary(summary)}
4078
4166
  ----------------------------------------`;
4079
4167
 
4168
+ const formatSummary = (summary) => `${createAllExecutionsSummary(
4169
+ summary,
4170
+ )}
4171
+ total duration: ${msAsDuration(summary.duration)}`;
4172
+
4080
4173
  const createAllExecutionsSummary = ({ counters }) => {
4081
4174
  if (counters.total === 0) {
4082
4175
  return `no execution`;
@@ -4093,6 +4186,8 @@ const createIntermediateSummary = ({
4093
4186
  counters,
4094
4187
  memoryHeap,
4095
4188
  timeEllapsed,
4189
+ logTimeUsage,
4190
+ logMemoryHeapUsage,
4096
4191
  }) => {
4097
4192
  const parts = [];
4098
4193
  if (executionIndex > 0 || counters.done > 0) {
@@ -4105,10 +4200,10 @@ const createIntermediateSummary = ({
4105
4200
  }),
4106
4201
  );
4107
4202
  }
4108
- if (timeEllapsed) {
4203
+ if (logTimeUsage && timeEllapsed) {
4109
4204
  parts.push(`duration: ${msAsEllapsedTime(timeEllapsed)}`);
4110
4205
  }
4111
- if (memoryHeap) {
4206
+ if (logMemoryHeapUsage && memoryHeap) {
4112
4207
  parts.push(`memory heap: ${byteAsMemoryUsage(memoryHeap)}`);
4113
4208
  }
4114
4209
  if (parts.length === 0) {
@@ -4362,27 +4457,6 @@ const formatConsoleSummary = (repartition) => {
4362
4457
  return `console (${parts.join(" ")})`;
4363
4458
  };
4364
4459
 
4365
- const formatExecution = ({
4366
- label,
4367
- details = {},
4368
- consoleOutput,
4369
- errorsOutput,
4370
- }) => {
4371
- let message = ``;
4372
- message += label;
4373
- Object.keys(details).forEach((key) => {
4374
- message += `
4375
- ${key}: ${details[key]}`;
4376
- });
4377
- if (consoleOutput) {
4378
- message += `\n${consoleOutput}`;
4379
- }
4380
- if (errorsOutput) {
4381
- message += `\n${errorsOutput}`;
4382
- }
4383
- return message;
4384
- };
4385
-
4386
4460
  const executeSteps = async (
4387
4461
  executionSteps,
4388
4462
  {
@@ -4548,6 +4622,9 @@ const executeSteps = async (
4548
4622
  executionResult: {
4549
4623
  status: "executing",
4550
4624
  },
4625
+ counters,
4626
+ timeEllapsed: Date.now() - startMs,
4627
+ memoryHeap: memoryUsage().heapUsed,
4551
4628
  };
4552
4629
  if (typeof executionParams.allocatedMs === "function") {
4553
4630
  executionParams.allocatedMs =
@@ -4559,17 +4636,10 @@ const executeSteps = async (
4559
4636
  log: executionLog,
4560
4637
  render: () => {
4561
4638
  return createExecutionLog(beforeExecutionInfo, {
4562
- counters,
4563
4639
  logRuntime,
4564
4640
  logEachDuration,
4565
- ...(logTimeUsage
4566
- ? {
4567
- timeEllapsed: Date.now() - startMs,
4568
- }
4569
- : {}),
4570
- ...(logMemoryHeapUsage
4571
- ? { memoryHeap: memoryUsage().heapUsed }
4572
- : {}),
4641
+ logTimeUsage,
4642
+ logMemoryHeapUsage,
4573
4643
  });
4574
4644
  },
4575
4645
  });
@@ -4620,7 +4690,6 @@ const executeSteps = async (
4620
4690
  endMs: Date.now(),
4621
4691
  executionResult,
4622
4692
  };
4623
- afterExecutionCallback(afterExecutionInfo);
4624
4693
 
4625
4694
  if (executionResult.status === "aborted") {
4626
4695
  counters.aborted++;
@@ -4637,7 +4706,6 @@ const executeSteps = async (
4637
4706
  if (executionLogsEnabled) {
4638
4707
  const log = createExecutionLog(afterExecutionInfo, {
4639
4708
  logShortForCompletedExecutions,
4640
- counters,
4641
4709
  logRuntime,
4642
4710
  logEachDuration,
4643
4711
  ...(logTimeUsage
@@ -4665,6 +4733,7 @@ const executeSteps = async (
4665
4733
  executionLog = createLog({ newLine: "" });
4666
4734
  }
4667
4735
  }
4736
+ afterExecutionCallback(afterExecutionInfo);
4668
4737
  const isLastExecutionLog = executionIndex === executionSteps.length - 1;
4669
4738
  const cancelRemaining =
4670
4739
  failFast &&
@@ -4692,7 +4761,7 @@ const executeSteps = async (
4692
4761
  duration: Date.now() - startMs,
4693
4762
  };
4694
4763
  if (logSummary) {
4695
- const summaryLog = createSummaryLog(summary);
4764
+ const summaryLog = formatSummaryLog(summary);
4696
4765
  rawOutput += stripAnsi(summaryLog);
4697
4766
  logger.info(summaryLog);
4698
4767
  }
@@ -4784,6 +4853,137 @@ const executeInParallel = async ({
4784
4853
  return executionResults;
4785
4854
  };
4786
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
+
4787
4987
  /**
4788
4988
  * Execute a list of files and log how it goes.
4789
4989
  * @param {Object} testPlanParameters
@@ -4831,6 +5031,15 @@ const executeTestPlan = async ({
4831
5031
  cooldownBetweenExecutions = 0,
4832
5032
  gcBetweenExecutions = logMemoryHeapUsage,
4833
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
+
4834
5043
  coverageEnabled = process.argv.includes("--coverage"),
4835
5044
  coverageConfig = {
4836
5045
  "file:///**/node_modules/": false,
@@ -4945,6 +5154,47 @@ const executeTestPlan = async ({
4945
5154
  });
4946
5155
  }
4947
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
+
4948
5198
  if (coverageEnabled) {
4949
5199
  if (coverageMethodForBrowsers === undefined) {
4950
5200
  coverageMethodForBrowsers = someHasCoverageV8
@@ -5078,6 +5328,61 @@ const executeTestPlan = async ({
5078
5328
  rootDirectoryUrl,
5079
5329
  });
5080
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
+ }
5081
5386
 
5082
5387
  const result = await executeSteps(executionSteps, {
5083
5388
  signal,
@@ -5102,6 +5407,14 @@ const executeTestPlan = async ({
5102
5407
  cooldownBetweenExecutions,
5103
5408
  gcBetweenExecutions,
5104
5409
 
5410
+ githubCheckEnabled,
5411
+ githubCheckName,
5412
+ githubCheckTitle,
5413
+ githubCheckToken,
5414
+ githubCheckRepositoryOwner,
5415
+ githubCheckRepositoryName,
5416
+ githubCheckCommitSha,
5417
+
5105
5418
  coverageEnabled,
5106
5419
  coverageConfig,
5107
5420
  coverageIncludeMissing,
@@ -5109,11 +5422,14 @@ const executeTestPlan = async ({
5109
5422
  coverageMethodForNodeJs,
5110
5423
  coverageV8ConflictWarning,
5111
5424
  coverageTempDirectoryUrl,
5425
+
5426
+ beforeExecutionCallback,
5427
+ afterExecutionCallback,
5112
5428
  });
5113
- if (
5114
- updateProcessExitCode &&
5115
- result.planSummary.counters.total !== result.planSummary.counters.completed
5116
- ) {
5429
+
5430
+ const hasFailed =
5431
+ result.planSummary.counters.total !== result.planSummary.counters.completed;
5432
+ if (updateProcessExitCode && hasFailed) {
5117
5433
  process.exitCode = 1;
5118
5434
  }
5119
5435
  const planCoverage = result.planCoverage;
@@ -5160,12 +5476,15 @@ const executeTestPlan = async ({
5160
5476
  }
5161
5477
  await Promise.all(promises);
5162
5478
  }
5163
- return {
5479
+
5480
+ const returnValue = {
5164
5481
  testPlanAborted: result.aborted,
5165
5482
  testPlanSummary: result.planSummary,
5166
5483
  testPlanReport: result.planReport,
5167
5484
  testPlanCoverage: planCoverage,
5168
5485
  };
5486
+ await afterAllExecutionCallback(returnValue);
5487
+ return returnValue;
5169
5488
  };
5170
5489
 
5171
5490
  const memoize = (compute) => {
@@ -5755,15 +6074,9 @@ const createRuntimeUsingPlaywright = ({
5755
6074
  if (executionResult.status === "failed") {
5756
6075
  result.status = "failed";
5757
6076
  if (executionResult.exception) {
5758
- result.errors.push({
5759
- ...executionResult.exception,
5760
- stack: executionResult.exception.text,
5761
- });
6077
+ result.errors.push(executionResult.exception);
5762
6078
  } else {
5763
- result.errors.push({
5764
- ...executionResult.error,
5765
- stack: executionResult.error.stack,
5766
- });
6079
+ result.errors.push(executionResult.error);
5767
6080
  }
5768
6081
  }
5769
6082
  });
@@ -6339,8 +6652,8 @@ const NO_EXPERIMENTAL_WARNING_FILE_URL = new URL(
6339
6652
  import.meta.url,
6340
6653
  ).href;
6341
6654
 
6342
- const CONTROLLABLE_CHILD_PROCESS_URL = new URL(
6343
- "./controllable_child_process.mjs",
6655
+ const CONTROLLED_CHILD_PROCESS_URL = new URL(
6656
+ "./node_child_process_controlled.mjs",
6344
6657
  import.meta.url,
6345
6658
  ).href;
6346
6659
 
@@ -6427,10 +6740,10 @@ const nodeChildProcess = ({
6427
6740
  };
6428
6741
  logger[logProcessCommand ? "info" : "debug"](
6429
6742
  `${process.argv[0]} ${execArgv.join(" ")} ${fileURLToPath(
6430
- CONTROLLABLE_CHILD_PROCESS_URL,
6743
+ CONTROLLED_CHILD_PROCESS_URL,
6431
6744
  )}`,
6432
6745
  );
6433
- const childProcess = fork(fileURLToPath(CONTROLLABLE_CHILD_PROCESS_URL), {
6746
+ const childProcess = fork(fileURLToPath(CONTROLLED_CHILD_PROCESS_URL), {
6434
6747
  execArgv,
6435
6748
  // silent: true
6436
6749
  stdio: ["pipe", "pipe", "pipe", "ipc"],
@@ -6680,8 +6993,7 @@ const onceChildProcessMessage = (childProcess, type, callback) => {
6680
6993
  const onmessage = (message) => {
6681
6994
  if (message && message.jsenv && message.type === type) {
6682
6995
  childProcess.removeListener("message", onmessage);
6683
- // eslint-disable-next-line no-eval
6684
- callback(message.data ? eval(`(${message.data})`) : "");
6996
+ callback(message.data ? JSON.parse(message.data) : "");
6685
6997
  }
6686
6998
  };
6687
6999
  childProcess.on("message", onmessage);
@@ -6701,8 +7013,8 @@ const onceChildProcessEvent = (childProcess, type, callback) => {
6701
7013
  // https://nodejs.org/api/worker_threads.html
6702
7014
  // https://github.com/avajs/ava/blob/576f534b345259055c95fa0c2b33bef10847a2af/lib/worker/base.js
6703
7015
 
6704
- const CONTROLLABLE_WORKER_THREAD_URL = new URL(
6705
- "./controllable_worker_thread.mjs",
7016
+ const CONTROLLED_WORKER_THREAD_URL = new URL(
7017
+ "./node_worker_thread_controlled.mjs",
6706
7018
  import.meta.url,
6707
7019
  ).href;
6708
7020
 
@@ -6779,7 +7091,7 @@ const nodeWorkerThread = ({
6779
7091
  actionOperation.addAbortSignal(signal);
6780
7092
  // https://nodejs.org/api/worker_threads.html#new-workerfilename-options
6781
7093
  const workerThread = new Worker(
6782
- fileURLToPath(CONTROLLABLE_WORKER_THREAD_URL),
7094
+ fileURLToPath(CONTROLLED_WORKER_THREAD_URL),
6783
7095
  {
6784
7096
  env: envForWorkerThread,
6785
7097
  execArgv: execArgvForWorkerThread,
@@ -6966,8 +7278,7 @@ const onceWorkerThreadMessage = (workerThread, type, callback) => {
6966
7278
  const onmessage = (message) => {
6967
7279
  if (message && message.jsenv && message.type === type) {
6968
7280
  workerThread.removeListener("message", onmessage);
6969
- // eslint-disable-next-line no-eval
6970
- callback(message.data ? eval(`(${message.data})`) : undefined);
7281
+ callback(message.data ? JSON.parse(message.data) : undefined);
6971
7282
  }
6972
7283
  };
6973
7284
  workerThread.on("message", onmessage);