@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
|
@@ -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,92 @@ 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
|
+
message += label;
|
|
122
|
+
Object.keys(details).forEach((key) => {
|
|
123
|
+
message += `
|
|
124
|
+
${key}: ${details[key]}`;
|
|
125
|
+
});
|
|
126
|
+
if (consoleOutput) {
|
|
127
|
+
message += `\n${consoleOutput}`;
|
|
128
|
+
}
|
|
129
|
+
if (errorsOutput) {
|
|
130
|
+
message += `\n${errorsOutput}`;
|
|
131
|
+
}
|
|
132
|
+
return message;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export const formatExecutionLabel = (
|
|
136
|
+
{
|
|
137
|
+
executionIndex,
|
|
138
|
+
executionParams,
|
|
139
|
+
executionResult,
|
|
140
|
+
timeEllapsed,
|
|
141
|
+
memoryHeap,
|
|
142
|
+
counters,
|
|
143
|
+
},
|
|
144
|
+
{ logTimeUsage, logMemoryHeapUsage } = {},
|
|
145
|
+
) => {
|
|
146
|
+
const { status } = executionResult;
|
|
147
|
+
const descriptionFormatter = descriptionFormatters[status];
|
|
148
|
+
const description = descriptionFormatter({
|
|
149
|
+
index: executionIndex,
|
|
150
|
+
total: counters.total,
|
|
151
|
+
executionParams,
|
|
152
|
+
});
|
|
153
|
+
const intermediateSummaryText = createIntermediateSummary({
|
|
154
|
+
executionIndex,
|
|
155
|
+
counters,
|
|
156
|
+
timeEllapsed,
|
|
157
|
+
memoryHeap,
|
|
158
|
+
logTimeUsage,
|
|
159
|
+
logMemoryHeapUsage,
|
|
160
|
+
});
|
|
161
|
+
return `${description}${intermediateSummaryText}`;
|
|
162
|
+
};
|
|
163
|
+
|
|
89
164
|
const formatErrors = (errors) => {
|
|
90
165
|
if (errors.length === 0) {
|
|
91
166
|
return "";
|
|
92
167
|
}
|
|
93
|
-
const formatError = (error) =>
|
|
168
|
+
const formatError = (error) => {
|
|
169
|
+
if (error === null || error === undefined) {
|
|
170
|
+
return String(error);
|
|
171
|
+
}
|
|
172
|
+
if (error) {
|
|
173
|
+
return error.stack || error.message || error;
|
|
174
|
+
}
|
|
175
|
+
return error;
|
|
176
|
+
};
|
|
94
177
|
|
|
95
178
|
if (errors.length === 1) {
|
|
96
179
|
return `${ANSI.color(`-------- error --------`, ANSI.RED)}
|
|
@@ -113,13 +196,17 @@ ${output.join(`\n`)}
|
|
|
113
196
|
${ANSI.color(`-------------------------`, ANSI.RED)}`;
|
|
114
197
|
};
|
|
115
198
|
|
|
116
|
-
export const
|
|
199
|
+
export const formatSummaryLog = (
|
|
117
200
|
summary,
|
|
118
201
|
) => `-------------- summary -----------------
|
|
119
|
-
${
|
|
120
|
-
total duration: ${msAsDuration(summary.duration)}
|
|
202
|
+
${formatSummary(summary)}
|
|
121
203
|
----------------------------------------`;
|
|
122
204
|
|
|
205
|
+
export const formatSummary = (summary) => `${createAllExecutionsSummary(
|
|
206
|
+
summary,
|
|
207
|
+
)}
|
|
208
|
+
total duration: ${msAsDuration(summary.duration)}`;
|
|
209
|
+
|
|
123
210
|
const createAllExecutionsSummary = ({ counters }) => {
|
|
124
211
|
if (counters.total === 0) {
|
|
125
212
|
return `no execution`;
|
|
@@ -136,6 +223,8 @@ const createIntermediateSummary = ({
|
|
|
136
223
|
counters,
|
|
137
224
|
memoryHeap,
|
|
138
225
|
timeEllapsed,
|
|
226
|
+
logTimeUsage,
|
|
227
|
+
logMemoryHeapUsage,
|
|
139
228
|
}) => {
|
|
140
229
|
const parts = [];
|
|
141
230
|
if (executionIndex > 0 || counters.done > 0) {
|
|
@@ -148,10 +237,10 @@ const createIntermediateSummary = ({
|
|
|
148
237
|
}),
|
|
149
238
|
);
|
|
150
239
|
}
|
|
151
|
-
if (timeEllapsed) {
|
|
240
|
+
if (logTimeUsage && timeEllapsed) {
|
|
152
241
|
parts.push(`duration: ${msAsEllapsedTime(timeEllapsed)}`);
|
|
153
242
|
}
|
|
154
|
-
if (memoryHeap) {
|
|
243
|
+
if (logMemoryHeapUsage && memoryHeap) {
|
|
155
244
|
parts.push(`memory heap: ${byteAsMemoryUsage(memoryHeap)}`);
|
|
156
245
|
}
|
|
157
246
|
if (parts.length === 0) {
|
|
@@ -404,24 +493,3 @@ const formatConsoleSummary = (repartition) => {
|
|
|
404
493
|
}
|
|
405
494
|
return `console (${parts.join(" ")})`;
|
|
406
495
|
};
|
|
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
|
-
}
|