@jsenv/test 2.1.3 → 3.0.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.
Files changed (46) hide show
  1. package/README.md +2 -1
  2. package/dist/js/exception.js +908 -0
  3. package/dist/js/execute_using_dynamic_import.js +76 -167
  4. package/dist/js/v8_coverage.js +4 -482
  5. package/dist/jsenv_test.js +4608 -2917
  6. package/dist/node_child_process_controlled.mjs +95 -76
  7. package/dist/node_worker_thread_controlled.mjs +72 -54
  8. package/package.json +23 -14
  9. package/src/coverage/generate_coverage.js +186 -0
  10. package/src/coverage/list_files_not_covered.js +2 -2
  11. package/src/coverage/missing_coverage.js +2 -2
  12. package/src/coverage/report_coverage_as_html.js +45 -0
  13. package/src/coverage/report_coverage_as_json.js +20 -0
  14. package/src/coverage/{coverage_reporter_text_log.js → report_coverage_in_console.js} +13 -7
  15. package/src/coverage/v8_and_istanbul.js +4 -4
  16. package/src/coverage/v8_coverage.js +2 -2
  17. package/src/execution/exception.js +151 -68
  18. package/src/execution/execute.js +24 -11
  19. package/src/execution/execute_test_plan.js +834 -750
  20. package/src/execution/format_error_for_terminal.js +187 -0
  21. package/src/execution/github_annotation_from_error.js +3 -3
  22. package/src/execution/junit_xml_file/report_as_junit_xml.js +149 -0
  23. package/src/execution/junit_xml_file/xml_generator.js +141 -0
  24. package/src/execution/reporters/reporter_list.js +687 -0
  25. package/src/execution/run.js +92 -30
  26. package/src/execution/web_server/start_using_command.js +2 -2
  27. package/src/execution/web_server_param.js +5 -5
  28. package/src/helpers/cpu_usage.js +142 -0
  29. package/src/helpers/cpu_usage.test_manual.js +7 -0
  30. package/src/main.js +8 -0
  31. package/src/runtime_browsers/chromium.js +1 -3
  32. package/src/runtime_browsers/firefox.js +0 -3
  33. package/src/runtime_browsers/middleware_istanbul.js +2 -2
  34. package/src/runtime_browsers/using_playwright.js +296 -220
  35. package/src/runtime_browsers/webkit.js +0 -3
  36. package/src/runtime_node/execute_using_dynamic_import.js +75 -28
  37. package/src/runtime_node/node_child_process.js +250 -139
  38. package/src/runtime_node/node_child_process_controlled.mjs +94 -75
  39. package/src/runtime_node/node_worker_thread.js +206 -120
  40. package/src/runtime_node/node_worker_thread_controlled.mjs +70 -53
  41. package/src/coverage/coverage_reporter_html_directory.js +0 -32
  42. package/src/coverage/coverage_reporter_json_file.js +0 -17
  43. package/src/coverage/report_to_coverage.js +0 -198
  44. package/src/execution/execution_colors.js +0 -10
  45. package/src/execution/execution_steps.js +0 -65
  46. package/src/execution/logs_file_execution.js +0 -495
@@ -1,144 +1,8 @@
1
1
  import { writeFileSync } from "node:fs";
2
+ import { memoryUsage } from "node:process";
2
3
  import { Session } from "node:inspector";
3
4
  import { PerformanceObserver, performance } from "node:perf_hooks";
4
-
5
- /*
6
- * Exception are objects used to wrap a value that is thrown
7
- * Usually they wrap error but the value can be anything as
8
- * throw "toto" can be used in JS
9
- *
10
- * - provide a common API to interact with value that can be thrown
11
- * - enrich usual errors with a bit more information (line, column)
12
- * - normalize error properties
13
- * - exception.stackTrace: only the stack trace as string (no error.name or error.message)
14
- * - error.stack: error.name + error.message + error.stackTrace
15
- *
16
- * It is used mostly internally by jsenv but can also be found
17
- * value returned by "executeTestPlan" public export (for failed executions)
18
- * and value returned by "execute" public export (when execution fails)
19
- *
20
- * This file is responsible to wrap error hapenning in Node.js runtime
21
- * The browser part can be found in "supervisor.js"
22
- */
23
-
24
- const createException = (reason) => {
25
- const exception = {
26
- isException: true,
27
- isError: false,
28
- name: "",
29
- message: "",
30
- stack: "",
31
- stackTrace: "",
32
- site: null,
33
- };
34
-
35
- if (reason === undefined) {
36
- exception.message = "undefined";
37
- return exception;
38
- }
39
- if (reason === undefined) {
40
- exception.message = "undefined";
41
- return exception;
42
- }
43
- if (typeof reason === "string") {
44
- exception.message = reason;
45
- return exception;
46
- }
47
- if (reason instanceof Error) {
48
- exception.isError = true;
49
- exception.name = reason.name;
50
- exception.message = reason.message;
51
- const stackInfo = getStackInfo(reason);
52
- if (stackInfo) {
53
- const { stackTrace, stackObject } = stackInfo;
54
- exception.stackTrace = stackTrace;
55
- const [firstCallSite] = stackObject;
56
- writePropertiesFromCallSite(exception, firstCallSite);
57
- }
58
- exception.stack = reason.stack;
59
- return exception;
60
- }
61
- if (typeof reason === "object") {
62
- exception.code = reason.code;
63
- exception.message = reason.message;
64
- exception.stack = reason.stack;
65
- return exception;
66
- }
67
- exception.message = JSON.stringify(reason);
68
- return exception;
69
- };
70
-
71
- const getStackInfo = (reason) => {
72
- if (reason instanceof Error) {
73
- const { prepareStackTrace } = Error;
74
- let stackTrace;
75
- let stackObject;
76
- Error.prepareStackTrace = (e, secondArg) => {
77
- Error.prepareStackTrace = prepareStackTrace;
78
- stackObject = secondArg;
79
- stackTrace = secondArg.map((callSite) => ` at ${callSite}`).join("\n");
80
- const name = e.name || "Error";
81
- const message = e.message || "";
82
- let stack = `${name}: ${message}`;
83
- if (stackTrace) {
84
- stack += `\n${stackTrace}`;
85
- }
86
- return stack;
87
- };
88
- // eslint-disable-next-line no-unused-expressions
89
- reason.stack;
90
- if (stackTrace === undefined) {
91
- return null;
92
- }
93
- return {
94
- stackTrace,
95
- stackObject,
96
- };
97
- }
98
- return null;
99
- };
100
- const writePropertiesFromCallSite = (exception, callSite) => {
101
- const source = callSite.getFileName() || callSite.getScriptNameOrSourceURL();
102
- if (source) {
103
- const line = callSite.getLineNumber();
104
- const column = callSite.getColumnNumber() - 1;
105
- exception.site = {
106
- url: source,
107
- line,
108
- column,
109
- };
110
- return;
111
- }
112
- // Code called using eval() needs special handling
113
- if (callSite.isEval()) {
114
- const origin = callSite.getEvalOrigin();
115
- if (origin) {
116
- writePropertiesFromEvalOrigin(exception, origin);
117
- }
118
- }
119
- };
120
- const writePropertiesFromEvalOrigin = (exception, origin) => {
121
- // Most eval() calls are in this format
122
- const topLevelEvalMatch = /^eval at ([^(]+) \((.+):(\d+):(\d+)\)$/.exec(
123
- origin,
124
- );
125
- if (topLevelEvalMatch) {
126
- const source = topLevelEvalMatch[2];
127
- const line = Number(topLevelEvalMatch[3]);
128
- const column = topLevelEvalMatch[4] - 1;
129
- exception.site = {
130
- url: source,
131
- line,
132
- column,
133
- };
134
- return;
135
- }
136
- // Parse nested eval() calls using recursion
137
- const nestedEvalMatch = /^eval at ([^(]+) \((.+)\)$/.exec(origin);
138
- if (nestedEvalMatch) {
139
- writePropertiesFromEvalOrigin(exception, nestedEvalMatch[2]);
140
- }
141
- };
5
+ import { createException } from "./exception.js";
142
6
 
143
7
  /*
144
8
  * Calling Profiler.startPreciseCoverage DO NOT propagate to
@@ -265,50 +129,95 @@ const measuresFromMeasureEntries = (measureEntries) => {
265
129
  const executeUsingDynamicImport = async ({
266
130
  rootDirectoryUrl,
267
131
  fileUrl,
132
+ measureMemoryUsage,
268
133
  collectPerformance,
269
134
  coverageEnabled,
270
- coverageConfig,
135
+ coverageInclude,
271
136
  coverageMethodForNodeJs,
272
137
  coverageFileUrl,
273
138
  }) => {
274
- const result = {};
275
- const afterImportCallbacks = [];
139
+ const result = {
140
+ timings: {
141
+ start: null,
142
+ end: null,
143
+ },
144
+ errors: [],
145
+ namespace: null,
146
+ memoryUsage: null,
147
+ performance: null,
148
+ };
149
+
150
+ let finalizePerformance;
151
+ if (collectPerformance) {
152
+ const getPerformance = startObservingPerformances();
153
+ finalizePerformance = async () => {
154
+ const performance = await getPerformance();
155
+ result.performance = performance;
156
+ };
157
+ }
158
+
159
+ let finalizeCoverage;
276
160
  if (coverageEnabled && coverageMethodForNodeJs === "Profiler") {
277
- const { filterV8Coverage } = await import("./v8_coverage.js").then(n => n.v8_coverage);
278
161
  const { stopJsCoverage } = await startJsCoverage();
279
- afterImportCallbacks.push(async () => {
280
- const coverage = await stopJsCoverage();
162
+ finalizeCoverage = async () => {
163
+ const [coverage, { filterV8Coverage }] = await Promise.all([
164
+ stopJsCoverage(),
165
+ import("./v8_coverage.js"),
166
+ ]);
281
167
  const coverageLight = await filterV8Coverage(coverage, {
282
168
  rootDirectoryUrl,
283
- coverageConfig,
169
+ coverageInclude,
284
170
  });
285
171
  writeFileSync(
286
172
  new URL(coverageFileUrl),
287
173
  JSON.stringify(coverageLight, null, " "),
288
174
  );
289
- });
175
+ };
290
176
  }
291
- if (collectPerformance) {
292
- const getPerformance = startObservingPerformances();
293
- afterImportCallbacks.push(async () => {
294
- const performance = await getPerformance();
295
- result.performance = performance;
296
- });
177
+
178
+ let finalizeMemoryUsage;
179
+ if (measureMemoryUsage) {
180
+ global.gc();
181
+ const memoryHeapUsedBeforeExecution = memoryUsage().heapUsed;
182
+ finalizeMemoryUsage = () => {
183
+ global.gc();
184
+ const memoryHeapUsedAfterExecution = memoryUsage().heapUsed;
185
+ result.memoryUsage =
186
+ memoryHeapUsedAfterExecution - memoryHeapUsedBeforeExecution;
187
+ };
188
+ }
189
+
190
+ result.timings.start = Date.now();
191
+ try {
192
+ const namespace = await import(fileUrl);
193
+ const namespaceResolved = {};
194
+ await Promise.all(
195
+ Object.keys(namespace).map(async (key) => {
196
+ const value = await namespace[key];
197
+ namespaceResolved[key] = value;
198
+ }),
199
+ );
200
+ result.status = "completed";
201
+ result.namespace = namespaceResolved;
202
+ } catch (e) {
203
+ result.status = "failed";
204
+ result.errors.push(createException(e, { rootDirectoryUrl }));
205
+ } finally {
206
+ result.timings.end = Date.now();
207
+ if (finalizeCoverage) {
208
+ await finalizeCoverage();
209
+ finalizeCoverage = null;
210
+ }
211
+ if (finalizePerformance) {
212
+ await finalizePerformance();
213
+ finalizePerformance = null;
214
+ }
215
+ if (finalizeMemoryUsage) {
216
+ finalizeMemoryUsage();
217
+ finalizeMemoryUsage = null;
218
+ }
219
+ return result;
297
220
  }
298
- const namespace = await import(fileUrl);
299
- const namespaceResolved = {};
300
- await Promise.all(
301
- Object.keys(namespace).map(async (key) => {
302
- const value = await namespace[key];
303
- namespaceResolved[key] = value;
304
- }),
305
- );
306
- result.namespace = namespaceResolved;
307
- await afterImportCallbacks.reduce(async (previous, afterImportCallback) => {
308
- await previous;
309
- await afterImportCallback();
310
- }, Promise.resolve());
311
- return result;
312
221
  };
313
222
 
314
- export { createException, executeUsingDynamicImport };
223
+ export { executeUsingDynamicImport };