@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.
- package/dist/js/execute_using_dynamic_import.js +118 -771
- package/dist/jsenv_test.js +409 -99
- package/dist/{controllable_child_process.mjs → node_child_process_controlled.mjs} +6 -13
- package/dist/{controllable_worker_thread.mjs → node_worker_thread_controlled.mjs} +6 -13
- package/package.json +3 -3
- package/src/execution/exception.js +137 -0
- package/src/execution/execute_steps.js +8 -13
- package/src/execution/execute_test_plan.js +132 -6
- package/src/execution/github_annotation_from_error.js +132 -0
- package/src/execution/logs_file_execution.js +134 -62
- package/src/runtime_browsers/using_playwright.js +2 -8
- package/src/runtime_node/node_child_process.js +5 -6
- package/src/runtime_node/{controllable_child_process.mjs → node_child_process_controlled.mjs} +6 -14
- package/src/runtime_node/node_worker_thread.js +4 -5
- package/src/runtime_node/{controllable_worker_thread.mjs → node_worker_thread_controlled.mjs} +6 -13
- package/src/execution/logs_file_execution.test.mjs +0 -41
package/dist/jsenv_test.js
CHANGED
|
@@ -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
|
-
|
|
3987
|
-
|
|
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 =
|
|
4012
|
+
log = label;
|
|
4006
4013
|
} else {
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
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
|
-
|
|
4025
|
-
|
|
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) =>
|
|
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
|
|
4162
|
+
const formatSummaryLog = (
|
|
4075
4163
|
summary,
|
|
4076
4164
|
) => `-------------- summary -----------------
|
|
4077
|
-
${
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
5115
|
-
|
|
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
|
-
|
|
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
|
|
6344
|
-
"./
|
|
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
|
-
|
|
6743
|
+
CONTROLLED_CHILD_PROCESS_URL,
|
|
6432
6744
|
)}`,
|
|
6433
6745
|
);
|
|
6434
|
-
const childProcess = fork(fileURLToPath(
|
|
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
|
-
|
|
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
|
|
6706
|
-
"./
|
|
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(
|
|
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
|
-
|
|
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);
|