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