@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
|
@@ -19,53 +19,55 @@ export const createExecutionLog = (
|
|
|
19
19
|
executionResult,
|
|
20
20
|
startMs,
|
|
21
21
|
endMs,
|
|
22
|
+
nowMs,
|
|
23
|
+
timeEllapsed,
|
|
24
|
+
memoryHeap,
|
|
25
|
+
counters,
|
|
22
26
|
},
|
|
23
27
|
{
|
|
24
28
|
logShortForCompletedExecutions,
|
|
25
|
-
counters,
|
|
26
29
|
logRuntime,
|
|
27
30
|
logEachDuration,
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
logTimeUsage,
|
|
32
|
+
logMemoryHeapUsage,
|
|
30
33
|
},
|
|
31
34
|
) => {
|
|
35
|
+
const label = formatExecutionLabel(
|
|
36
|
+
{
|
|
37
|
+
executionIndex,
|
|
38
|
+
executionParams,
|
|
39
|
+
executionResult,
|
|
40
|
+
timeEllapsed,
|
|
41
|
+
memoryHeap,
|
|
42
|
+
counters,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
logTimeUsage,
|
|
46
|
+
logMemoryHeapUsage,
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
|
|
32
50
|
const { status } = executionResult;
|
|
33
|
-
const descriptionFormatter = descriptionFormatters[status];
|
|
34
|
-
const description = descriptionFormatter({
|
|
35
|
-
index: executionIndex,
|
|
36
|
-
total: counters.total,
|
|
37
|
-
executionParams,
|
|
38
|
-
});
|
|
39
|
-
const summary = createIntermediateSummary({
|
|
40
|
-
executionIndex,
|
|
41
|
-
counters,
|
|
42
|
-
timeEllapsed,
|
|
43
|
-
memoryHeap,
|
|
44
|
-
});
|
|
45
51
|
let log;
|
|
46
52
|
if (logShortForCompletedExecutions && status === "completed") {
|
|
47
|
-
log =
|
|
53
|
+
log = label;
|
|
48
54
|
} else {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
? {
|
|
59
|
-
duration:
|
|
60
|
-
status === "executing"
|
|
61
|
-
? msAsEllapsedTime(Date.now() - startMs)
|
|
62
|
-
: msAsDuration(endMs - startMs),
|
|
63
|
-
}
|
|
64
|
-
: {}),
|
|
55
|
+
log = formatExecution(
|
|
56
|
+
{
|
|
57
|
+
fileRelativeUrl,
|
|
58
|
+
runtimeName,
|
|
59
|
+
runtimeVersion,
|
|
60
|
+
executionResult,
|
|
61
|
+
startMs,
|
|
62
|
+
endMs,
|
|
63
|
+
nowMs,
|
|
65
64
|
},
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
{
|
|
66
|
+
label,
|
|
67
|
+
logRuntime,
|
|
68
|
+
logEachDuration,
|
|
69
|
+
},
|
|
70
|
+
);
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
const { columns = 80 } = process.stdout;
|
|
@@ -86,11 +88,96 @@ export const createExecutionLog = (
|
|
|
86
88
|
return log;
|
|
87
89
|
};
|
|
88
90
|
|
|
91
|
+
export const formatExecution = (
|
|
92
|
+
{
|
|
93
|
+
fileRelativeUrl,
|
|
94
|
+
runtimeName,
|
|
95
|
+
runtimeVersion,
|
|
96
|
+
executionResult,
|
|
97
|
+
startMs,
|
|
98
|
+
endMs,
|
|
99
|
+
nowMs,
|
|
100
|
+
},
|
|
101
|
+
{ label, logRuntime = true, logEachDuration = true } = {},
|
|
102
|
+
) => {
|
|
103
|
+
const { status } = executionResult;
|
|
104
|
+
const { consoleCalls = [], errors = [] } = executionResult;
|
|
105
|
+
const consoleOutput = formatConsoleCalls(consoleCalls);
|
|
106
|
+
const errorsOutput = formatErrors(errors);
|
|
107
|
+
|
|
108
|
+
const details = {
|
|
109
|
+
file: fileRelativeUrl,
|
|
110
|
+
...(logRuntime ? { runtime: `${runtimeName}/${runtimeVersion}` } : {}),
|
|
111
|
+
...(logEachDuration
|
|
112
|
+
? {
|
|
113
|
+
duration:
|
|
114
|
+
status === "executing"
|
|
115
|
+
? msAsEllapsedTime((nowMs || Date.now()) - startMs)
|
|
116
|
+
: msAsDuration(endMs - startMs),
|
|
117
|
+
}
|
|
118
|
+
: {}),
|
|
119
|
+
};
|
|
120
|
+
let message = ``;
|
|
121
|
+
if (label) {
|
|
122
|
+
message += label;
|
|
123
|
+
}
|
|
124
|
+
Object.keys(details).forEach((key) => {
|
|
125
|
+
if (message.length > 0) {
|
|
126
|
+
message += "\n";
|
|
127
|
+
}
|
|
128
|
+
message += `${key}: ${details[key]}`;
|
|
129
|
+
});
|
|
130
|
+
if (consoleOutput) {
|
|
131
|
+
message += `\n${consoleOutput}`;
|
|
132
|
+
}
|
|
133
|
+
if (errorsOutput) {
|
|
134
|
+
message += `\n${errorsOutput}`;
|
|
135
|
+
}
|
|
136
|
+
return message;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export const formatExecutionLabel = (
|
|
140
|
+
{
|
|
141
|
+
executionIndex,
|
|
142
|
+
executionParams,
|
|
143
|
+
executionResult,
|
|
144
|
+
timeEllapsed,
|
|
145
|
+
memoryHeap,
|
|
146
|
+
counters,
|
|
147
|
+
},
|
|
148
|
+
{ logTimeUsage, logMemoryHeapUsage } = {},
|
|
149
|
+
) => {
|
|
150
|
+
const { status } = executionResult;
|
|
151
|
+
const descriptionFormatter = descriptionFormatters[status];
|
|
152
|
+
const description = descriptionFormatter({
|
|
153
|
+
index: executionIndex,
|
|
154
|
+
total: counters.total,
|
|
155
|
+
executionParams,
|
|
156
|
+
});
|
|
157
|
+
const intermediateSummaryText = createIntermediateSummary({
|
|
158
|
+
executionIndex,
|
|
159
|
+
counters,
|
|
160
|
+
timeEllapsed,
|
|
161
|
+
memoryHeap,
|
|
162
|
+
logTimeUsage,
|
|
163
|
+
logMemoryHeapUsage,
|
|
164
|
+
});
|
|
165
|
+
return `${description}${intermediateSummaryText}`;
|
|
166
|
+
};
|
|
167
|
+
|
|
89
168
|
const formatErrors = (errors) => {
|
|
90
169
|
if (errors.length === 0) {
|
|
91
170
|
return "";
|
|
92
171
|
}
|
|
93
|
-
const formatError = (error) =>
|
|
172
|
+
const formatError = (error) => {
|
|
173
|
+
if (error === null || error === undefined) {
|
|
174
|
+
return String(error);
|
|
175
|
+
}
|
|
176
|
+
if (error) {
|
|
177
|
+
return error.stack || error.message || error;
|
|
178
|
+
}
|
|
179
|
+
return error;
|
|
180
|
+
};
|
|
94
181
|
|
|
95
182
|
if (errors.length === 1) {
|
|
96
183
|
return `${ANSI.color(`-------- error --------`, ANSI.RED)}
|
|
@@ -113,13 +200,17 @@ ${output.join(`\n`)}
|
|
|
113
200
|
${ANSI.color(`-------------------------`, ANSI.RED)}`;
|
|
114
201
|
};
|
|
115
202
|
|
|
116
|
-
export const
|
|
203
|
+
export const formatSummaryLog = (
|
|
117
204
|
summary,
|
|
118
205
|
) => `-------------- summary -----------------
|
|
119
|
-
${
|
|
120
|
-
total duration: ${msAsDuration(summary.duration)}
|
|
206
|
+
${formatSummary(summary)}
|
|
121
207
|
----------------------------------------`;
|
|
122
208
|
|
|
209
|
+
export const formatSummary = (summary) => `${createAllExecutionsSummary(
|
|
210
|
+
summary,
|
|
211
|
+
)}
|
|
212
|
+
total duration: ${msAsDuration(summary.duration)}`;
|
|
213
|
+
|
|
123
214
|
const createAllExecutionsSummary = ({ counters }) => {
|
|
124
215
|
if (counters.total === 0) {
|
|
125
216
|
return `no execution`;
|
|
@@ -136,6 +227,8 @@ const createIntermediateSummary = ({
|
|
|
136
227
|
counters,
|
|
137
228
|
memoryHeap,
|
|
138
229
|
timeEllapsed,
|
|
230
|
+
logTimeUsage,
|
|
231
|
+
logMemoryHeapUsage,
|
|
139
232
|
}) => {
|
|
140
233
|
const parts = [];
|
|
141
234
|
if (executionIndex > 0 || counters.done > 0) {
|
|
@@ -148,10 +241,10 @@ const createIntermediateSummary = ({
|
|
|
148
241
|
}),
|
|
149
242
|
);
|
|
150
243
|
}
|
|
151
|
-
if (timeEllapsed) {
|
|
244
|
+
if (logTimeUsage && timeEllapsed) {
|
|
152
245
|
parts.push(`duration: ${msAsEllapsedTime(timeEllapsed)}`);
|
|
153
246
|
}
|
|
154
|
-
if (memoryHeap) {
|
|
247
|
+
if (logMemoryHeapUsage && memoryHeap) {
|
|
155
248
|
parts.push(`memory heap: ${byteAsMemoryUsage(memoryHeap)}`);
|
|
156
249
|
}
|
|
157
250
|
if (parts.length === 0) {
|
|
@@ -404,24 +497,3 @@ const formatConsoleSummary = (repartition) => {
|
|
|
404
497
|
}
|
|
405
498
|
return `console (${parts.join(" ")})`;
|
|
406
499
|
};
|
|
407
|
-
|
|
408
|
-
const formatExecution = ({
|
|
409
|
-
label,
|
|
410
|
-
details = {},
|
|
411
|
-
consoleOutput,
|
|
412
|
-
errorsOutput,
|
|
413
|
-
}) => {
|
|
414
|
-
let message = ``;
|
|
415
|
-
message += label;
|
|
416
|
-
Object.keys(details).forEach((key) => {
|
|
417
|
-
message += `
|
|
418
|
-
${key}: ${details[key]}`;
|
|
419
|
-
});
|
|
420
|
-
if (consoleOutput) {
|
|
421
|
-
message += `\n${consoleOutput}`;
|
|
422
|
-
}
|
|
423
|
-
if (errorsOutput) {
|
|
424
|
-
message += `\n${errorsOutput}`;
|
|
425
|
-
}
|
|
426
|
-
return message;
|
|
427
|
-
};
|
|
@@ -402,15 +402,9 @@ export const createRuntimeUsingPlaywright = ({
|
|
|
402
402
|
if (executionResult.status === "failed") {
|
|
403
403
|
result.status = "failed";
|
|
404
404
|
if (executionResult.exception) {
|
|
405
|
-
result.errors.push(
|
|
406
|
-
...executionResult.exception,
|
|
407
|
-
stack: executionResult.exception.text,
|
|
408
|
-
});
|
|
405
|
+
result.errors.push(executionResult.exception);
|
|
409
406
|
} else {
|
|
410
|
-
result.errors.push(
|
|
411
|
-
...executionResult.error,
|
|
412
|
-
stack: executionResult.error.stack,
|
|
413
|
-
});
|
|
407
|
+
result.errors.push(executionResult.error);
|
|
414
408
|
}
|
|
415
409
|
}
|
|
416
410
|
});
|
|
@@ -15,8 +15,8 @@ import { EXIT_CODES } from "./exit_codes.js";
|
|
|
15
15
|
import { IMPORTMAP_NODE_LOADER_FILE_URL } from "./importmap_node_loader_file_url.js";
|
|
16
16
|
import { NO_EXPERIMENTAL_WARNING_FILE_URL } from "./no_experimental_warnings_file_url.js";
|
|
17
17
|
|
|
18
|
-
const
|
|
19
|
-
"./
|
|
18
|
+
const CONTROLLED_CHILD_PROCESS_URL = new URL(
|
|
19
|
+
"./node_child_process_controlled.mjs?entry_point",
|
|
20
20
|
import.meta.url,
|
|
21
21
|
).href;
|
|
22
22
|
|
|
@@ -103,10 +103,10 @@ export const nodeChildProcess = ({
|
|
|
103
103
|
};
|
|
104
104
|
logger[logProcessCommand ? "info" : "debug"](
|
|
105
105
|
`${process.argv[0]} ${execArgv.join(" ")} ${fileURLToPath(
|
|
106
|
-
|
|
106
|
+
CONTROLLED_CHILD_PROCESS_URL,
|
|
107
107
|
)}`,
|
|
108
108
|
);
|
|
109
|
-
const childProcess = fork(fileURLToPath(
|
|
109
|
+
const childProcess = fork(fileURLToPath(CONTROLLED_CHILD_PROCESS_URL), {
|
|
110
110
|
execArgv,
|
|
111
111
|
// silent: true
|
|
112
112
|
stdio: ["pipe", "pipe", "pipe", "ipc"],
|
|
@@ -356,8 +356,7 @@ const onceChildProcessMessage = (childProcess, type, callback) => {
|
|
|
356
356
|
const onmessage = (message) => {
|
|
357
357
|
if (message && message.jsenv && message.type === type) {
|
|
358
358
|
childProcess.removeListener("message", onmessage);
|
|
359
|
-
|
|
360
|
-
callback(message.data ? eval(`(${message.data})`) : "");
|
|
359
|
+
callback(message.data ? JSON.parse(message.data) : "");
|
|
361
360
|
}
|
|
362
361
|
};
|
|
363
362
|
childProcess.on("message", onmessage);
|
package/src/runtime_node/{controllable_child_process.mjs → node_child_process_controlled.mjs}
RENAMED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { createException } from "../execution/exception.js";
|
|
3
2
|
import { executeUsingDynamicImport } from "./execute_using_dynamic_import.js";
|
|
4
3
|
|
|
5
4
|
const ACTIONS_AVAILABLE = {
|
|
@@ -27,20 +26,13 @@ const ACTION_RESPONSE_STATUS_FAILED = "action-failed";
|
|
|
27
26
|
const ACTION_RESPONSE_STATUS_COMPLETED = "action-completed";
|
|
28
27
|
|
|
29
28
|
const sendActionFailed = (error) => {
|
|
30
|
-
|
|
31
|
-
delete error.toString;
|
|
32
|
-
}
|
|
29
|
+
const exception = createException(error);
|
|
33
30
|
sendToParent(
|
|
34
31
|
ACTION_RESPONSE_EVENT_NAME,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
status: ACTION_RESPONSE_STATUS_FAILED,
|
|
40
|
-
value: error,
|
|
41
|
-
},
|
|
42
|
-
{ ignoreSymbols: true },
|
|
43
|
-
),
|
|
32
|
+
JSON.stringify({
|
|
33
|
+
status: ACTION_RESPONSE_STATUS_FAILED,
|
|
34
|
+
value: exception,
|
|
35
|
+
}),
|
|
44
36
|
);
|
|
45
37
|
};
|
|
46
38
|
|
|
@@ -16,8 +16,8 @@ import { EXIT_CODES } from "./exit_codes.js";
|
|
|
16
16
|
import { IMPORTMAP_NODE_LOADER_FILE_URL } from "./importmap_node_loader_file_url.js";
|
|
17
17
|
import { NO_EXPERIMENTAL_WARNING_FILE_URL } from "./no_experimental_warnings_file_url.js";
|
|
18
18
|
|
|
19
|
-
const
|
|
20
|
-
"./
|
|
19
|
+
const CONTROLLED_WORKER_THREAD_URL = new URL(
|
|
20
|
+
"./node_worker_thread_controlled.mjs?entry_point",
|
|
21
21
|
import.meta.url,
|
|
22
22
|
).href;
|
|
23
23
|
|
|
@@ -94,7 +94,7 @@ export const nodeWorkerThread = ({
|
|
|
94
94
|
actionOperation.addAbortSignal(signal);
|
|
95
95
|
// https://nodejs.org/api/worker_threads.html#new-workerfilename-options
|
|
96
96
|
const workerThread = new Worker(
|
|
97
|
-
fileURLToPath(
|
|
97
|
+
fileURLToPath(CONTROLLED_WORKER_THREAD_URL),
|
|
98
98
|
{
|
|
99
99
|
env: envForWorkerThread,
|
|
100
100
|
execArgv: execArgvForWorkerThread,
|
|
@@ -281,8 +281,7 @@ const onceWorkerThreadMessage = (workerThread, type, callback) => {
|
|
|
281
281
|
const onmessage = (message) => {
|
|
282
282
|
if (message && message.jsenv && message.type === type) {
|
|
283
283
|
workerThread.removeListener("message", onmessage);
|
|
284
|
-
|
|
285
|
-
callback(message.data ? eval(`(${message.data})`) : undefined);
|
|
284
|
+
callback(message.data ? JSON.parse(message.data) : undefined);
|
|
286
285
|
}
|
|
287
286
|
};
|
|
288
287
|
workerThread.on("message", onmessage);
|
package/src/runtime_node/{controllable_worker_thread.mjs → node_worker_thread_controlled.mjs}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parentPort } from "node:worker_threads";
|
|
2
|
-
import { uneval } from "@jsenv/uneval";
|
|
3
2
|
|
|
3
|
+
import { createException } from "../execution/exception.js";
|
|
4
4
|
import { executeUsingDynamicImport } from "./execute_using_dynamic_import.js";
|
|
5
5
|
|
|
6
6
|
const ACTIONS_AVAILABLE = {
|
|
@@ -12,20 +12,13 @@ const ACTION_RESPONSE_STATUS_FAILED = "action-failed";
|
|
|
12
12
|
const ACTION_RESPONSE_STATUS_COMPLETED = "action-completed";
|
|
13
13
|
|
|
14
14
|
const sendActionFailed = (error) => {
|
|
15
|
-
|
|
16
|
-
delete error.toString;
|
|
17
|
-
}
|
|
15
|
+
const exception = createException(error);
|
|
18
16
|
sendToParent(
|
|
19
17
|
ACTION_RESPONSE_EVENT_NAME,
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
status: ACTION_RESPONSE_STATUS_FAILED,
|
|
25
|
-
value: error,
|
|
26
|
-
},
|
|
27
|
-
{ ignoreSymbols: true },
|
|
28
|
-
),
|
|
18
|
+
JSON.stringify({
|
|
19
|
+
status: ACTION_RESPONSE_STATUS_FAILED,
|
|
20
|
+
value: exception,
|
|
21
|
+
}),
|
|
29
22
|
);
|
|
30
23
|
};
|
|
31
24
|
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { assert } from "@jsenv/assert";
|
|
2
|
-
|
|
3
|
-
import { formatConsoleOutput } from "./logs_file_execution.js";
|
|
4
|
-
|
|
5
|
-
{
|
|
6
|
-
const actual = formatConsoleOutput([
|
|
7
|
-
{ type: "log", text: "a\n" },
|
|
8
|
-
{ type: "log", text: "b\n" },
|
|
9
|
-
]);
|
|
10
|
-
const expected = ` a
|
|
11
|
-
b`;
|
|
12
|
-
assert({ actual, expected });
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
{
|
|
16
|
-
const actual = formatConsoleOutput([
|
|
17
|
-
{ type: "log", text: "a" },
|
|
18
|
-
{ type: "log", text: "b" },
|
|
19
|
-
]);
|
|
20
|
-
const expected = ` ab`;
|
|
21
|
-
assert({ actual, expected });
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
{
|
|
25
|
-
const actual = formatConsoleOutput([
|
|
26
|
-
{
|
|
27
|
-
type: "log",
|
|
28
|
-
text: `1
|
|
29
|
-
2`,
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
type: "log",
|
|
33
|
-
text: `alpha
|
|
34
|
-
beta`,
|
|
35
|
-
},
|
|
36
|
-
]);
|
|
37
|
-
const expected = ` 1
|
|
38
|
-
2alpha
|
|
39
|
-
beta`;
|
|
40
|
-
assert({ actual, expected });
|
|
41
|
-
}
|