@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.
- package/dist/js/execute_using_dynamic_import.js +118 -771
- package/dist/jsenv_test.js +411 -100
- 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 +4 -4
- 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";
|
|
@@ -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 =
|
|
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
|
-
|
|
3986
|
-
|
|
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 =
|
|
4012
|
+
log = label;
|
|
4005
4013
|
} else {
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
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
|
-
|
|
4024
|
-
|
|
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) =>
|
|
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
|
|
4162
|
+
const formatSummaryLog = (
|
|
4074
4163
|
summary,
|
|
4075
4164
|
) => `-------------- summary -----------------
|
|
4076
|
-
${
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
5114
|
-
|
|
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
|
-
|
|
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
|
|
6343
|
-
"./
|
|
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
|
-
|
|
6743
|
+
CONTROLLED_CHILD_PROCESS_URL,
|
|
6431
6744
|
)}`,
|
|
6432
6745
|
);
|
|
6433
|
-
const childProcess = fork(fileURLToPath(
|
|
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
|
-
|
|
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
|
|
6705
|
-
"./
|
|
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(
|
|
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
|
-
|
|
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);
|