@jsenv/test 2.1.3 → 3.0.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.
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,484 +1,11 @@
1
- const assertUrlLike = (value, name = "url") => {
2
- if (typeof value !== "string") {
3
- throw new TypeError(`${name} must be a url string, got ${value}`);
4
- }
5
- if (isWindowsPathnameSpecifier(value)) {
6
- throw new TypeError(
7
- `${name} must be a url but looks like a windows pathname, got ${value}`,
8
- );
9
- }
10
- if (!hasScheme(value)) {
11
- throw new TypeError(
12
- `${name} must be a url and no scheme found, got ${value}`,
13
- );
14
- }
15
- };
16
-
17
- const isPlainObject = (value) => {
18
- if (value === null) {
19
- return false;
20
- }
21
- if (typeof value === "object") {
22
- if (Array.isArray(value)) {
23
- return false;
24
- }
25
- return true;
26
- }
27
- return false;
28
- };
29
-
30
- const isWindowsPathnameSpecifier = (specifier) => {
31
- const firstChar = specifier[0];
32
- if (!/[a-zA-Z]/.test(firstChar)) return false;
33
- const secondChar = specifier[1];
34
- if (secondChar !== ":") return false;
35
- const thirdChar = specifier[2];
36
- return thirdChar === "/" || thirdChar === "\\";
37
- };
38
-
39
- const hasScheme = (specifier) => /^[a-zA-Z]+:/.test(specifier);
40
-
41
- const resolveAssociations = (associations, baseUrl) => {
42
- if (baseUrl && typeof baseUrl.href === "string") baseUrl = baseUrl.href;
43
- assertUrlLike(baseUrl, "baseUrl");
44
- const associationsResolved = {};
45
- Object.keys(associations).forEach((key) => {
46
- const value = associations[key];
47
- if (typeof value === "object" && value !== null) {
48
- const valueMapResolved = {};
49
- Object.keys(value).forEach((pattern) => {
50
- const valueAssociated = value[pattern];
51
- const patternResolved = normalizeUrlPattern(pattern, baseUrl);
52
- valueMapResolved[patternResolved] = valueAssociated;
53
- });
54
- associationsResolved[key] = valueMapResolved;
55
- } else {
56
- associationsResolved[key] = value;
57
- }
58
- });
59
- return associationsResolved;
60
- };
61
-
62
- const normalizeUrlPattern = (urlPattern, baseUrl) => {
63
- try {
64
- return String(new URL(urlPattern, baseUrl));
65
- } catch (e) {
66
- // it's not really an url, no need to perform url resolution nor encoding
67
- return urlPattern;
68
- }
69
- };
70
-
71
- const asFlatAssociations = (associations) => {
72
- if (!isPlainObject(associations)) {
73
- throw new TypeError(
74
- `associations must be a plain object, got ${associations}`,
75
- );
76
- }
77
- const flatAssociations = {};
78
- Object.keys(associations).forEach((associationName) => {
79
- const associationValue = associations[associationName];
80
- if (isPlainObject(associationValue)) {
81
- Object.keys(associationValue).forEach((pattern) => {
82
- const patternValue = associationValue[pattern];
83
- const previousValue = flatAssociations[pattern];
84
- if (isPlainObject(previousValue)) {
85
- flatAssociations[pattern] = {
86
- ...previousValue,
87
- [associationName]: patternValue,
88
- };
89
- } else {
90
- flatAssociations[pattern] = {
91
- [associationName]: patternValue,
92
- };
93
- }
94
- });
95
- }
96
- });
97
- return flatAssociations;
98
- };
99
-
100
- /*
101
- * Link to things doing pattern matching:
102
- * https://git-scm.com/docs/gitignore
103
- * https://github.com/kaelzhang/node-ignore
104
- */
105
-
106
-
107
- /** @module jsenv_url_meta **/
108
- /**
109
- * An object representing the result of applying a pattern to an url
110
- * @typedef {Object} MatchResult
111
- * @property {boolean} matched Indicates if url matched pattern
112
- * @property {number} patternIndex Index where pattern stopped matching url, otherwise pattern.length
113
- * @property {number} urlIndex Index where url stopped matching pattern, otherwise url.length
114
- * @property {Array} matchGroups Array of strings captured during pattern matching
115
- */
116
-
117
- /**
118
- * Apply a pattern to an url
119
- * @param {Object} applyPatternMatchingParams
120
- * @param {string} applyPatternMatchingParams.pattern "*", "**" and trailing slash have special meaning
121
- * @param {string} applyPatternMatchingParams.url a string representing an url
122
- * @return {MatchResult}
123
- */
124
- const applyPatternMatching = ({ url, pattern }) => {
125
- assertUrlLike(pattern, "pattern");
126
- if (url && typeof url.href === "string") url = url.href;
127
- assertUrlLike(url, "url");
128
- const { matched, patternIndex, index, groups } = applyMatching(pattern, url);
129
- const matchGroups = [];
130
- let groupIndex = 0;
131
- groups.forEach((group) => {
132
- if (group.name) {
133
- matchGroups[group.name] = group.string;
134
- } else {
135
- matchGroups[groupIndex] = group.string;
136
- groupIndex++;
137
- }
138
- });
139
- return {
140
- matched,
141
- patternIndex,
142
- urlIndex: index,
143
- matchGroups,
144
- };
145
- };
146
-
147
- const applyMatching = (pattern, string) => {
148
- const groups = [];
149
- let patternIndex = 0;
150
- let index = 0;
151
- let remainingPattern = pattern;
152
- let remainingString = string;
153
- let restoreIndexes = true;
154
-
155
- const consumePattern = (count) => {
156
- const subpattern = remainingPattern.slice(0, count);
157
- remainingPattern = remainingPattern.slice(count);
158
- patternIndex += count;
159
- return subpattern;
160
- };
161
- const consumeString = (count) => {
162
- const substring = remainingString.slice(0, count);
163
- remainingString = remainingString.slice(count);
164
- index += count;
165
- return substring;
166
- };
167
- const consumeRemainingString = () => {
168
- return consumeString(remainingString.length);
169
- };
170
-
171
- let matched;
172
- const iterate = () => {
173
- const patternIndexBefore = patternIndex;
174
- const indexBefore = index;
175
- matched = matchOne();
176
- if (matched === undefined) {
177
- consumePattern(1);
178
- consumeString(1);
179
- iterate();
180
- return;
181
- }
182
- if (matched === false && restoreIndexes) {
183
- patternIndex = patternIndexBefore;
184
- index = indexBefore;
185
- }
186
- };
187
- const matchOne = () => {
188
- // pattern consumed and string consumed
189
- if (remainingPattern === "" && remainingString === "") {
190
- return true; // string fully matched pattern
191
- }
192
- // pattern consumed, string not consumed
193
- if (remainingPattern === "" && remainingString !== "") {
194
- return false; // fails because string longer than expected
195
- }
196
- // -- from this point pattern is not consumed --
197
- // string consumed, pattern not consumed
198
- if (remainingString === "") {
199
- if (remainingPattern === "**") {
200
- // trailing "**" is optional
201
- consumePattern(2);
202
- return true;
203
- }
204
- if (remainingPattern === "*") {
205
- groups.push({ string: "" });
206
- }
207
- return false; // fail because string shorter than expected
208
- }
209
- // -- from this point pattern and string are not consumed --
210
- // fast path trailing slash
211
- if (remainingPattern === "/") {
212
- if (remainingString[0] === "/") {
213
- // trailing slash match remaining
214
- consumePattern(1);
215
- groups.push({ string: consumeRemainingString() });
216
- return true;
217
- }
218
- return false;
219
- }
220
- // fast path trailing '**'
221
- if (remainingPattern === "**") {
222
- consumePattern(2);
223
- consumeRemainingString();
224
- return true;
225
- }
226
- // pattern leading **
227
- if (remainingPattern.slice(0, 2) === "**") {
228
- consumePattern(2); // consumes "**"
229
- let skipAllowed = true;
230
- if (remainingPattern[0] === "/") {
231
- consumePattern(1); // consumes "/"
232
- // when remainingPattern was preceeded by "**/"
233
- // and remainingString have no "/"
234
- // then skip is not allowed, a regular match will be performed
235
- if (!remainingString.includes("/")) {
236
- skipAllowed = false;
237
- }
238
- }
239
- // pattern ending with "**" or "**/" match remaining string
240
- if (remainingPattern === "") {
241
- consumeRemainingString();
242
- return true;
243
- }
244
- if (skipAllowed) {
245
- const skipResult = skipUntilMatch({
246
- pattern: remainingPattern,
247
- string: remainingString,
248
- canSkipSlash: true,
249
- });
250
- groups.push(...skipResult.groups);
251
- consumePattern(skipResult.patternIndex);
252
- consumeRemainingString();
253
- restoreIndexes = false;
254
- return skipResult.matched;
255
- }
256
- }
257
- if (remainingPattern[0] === "*") {
258
- consumePattern(1); // consumes "*"
259
- if (remainingPattern === "") {
260
- // matches everything except "/"
261
- const slashIndex = remainingString.indexOf("/");
262
- if (slashIndex === -1) {
263
- groups.push({ string: consumeRemainingString() });
264
- return true;
265
- }
266
- groups.push({ string: consumeString(slashIndex) });
267
- return false;
268
- }
269
- // the next char must not the one expected by remainingPattern[0]
270
- // because * is greedy and expect to skip at least one char
271
- if (remainingPattern[0] === remainingString[0]) {
272
- groups.push({ string: "" });
273
- patternIndex = patternIndex - 1;
274
- return false;
275
- }
276
- const skipResult = skipUntilMatch({
277
- pattern: remainingPattern,
278
- string: remainingString,
279
- canSkipSlash: false,
280
- });
281
- groups.push(skipResult.group, ...skipResult.groups);
282
- consumePattern(skipResult.patternIndex);
283
- consumeString(skipResult.index);
284
- restoreIndexes = false;
285
- return skipResult.matched;
286
- }
287
- if (remainingPattern[0] !== remainingString[0]) {
288
- return false;
289
- }
290
- return undefined;
291
- };
292
- iterate();
293
-
294
- return {
295
- matched,
296
- patternIndex,
297
- index,
298
- groups,
299
- };
300
- };
301
-
302
- const skipUntilMatch = ({ pattern, string, canSkipSlash }) => {
303
- let index = 0;
304
- let remainingString = string;
305
- let longestAttemptRange = null;
306
- let isLastAttempt = false;
307
-
308
- const failure = () => {
309
- return {
310
- matched: false,
311
- patternIndex: longestAttemptRange.patternIndex,
312
- index: longestAttemptRange.index + longestAttemptRange.length,
313
- groups: longestAttemptRange.groups,
314
- group: {
315
- string: string.slice(0, longestAttemptRange.index),
316
- },
317
- };
318
- };
319
-
320
- const tryToMatch = () => {
321
- const matchAttempt = applyMatching(pattern, remainingString);
322
- if (matchAttempt.matched) {
323
- return {
324
- matched: true,
325
- patternIndex: matchAttempt.patternIndex,
326
- index: index + matchAttempt.index,
327
- groups: matchAttempt.groups,
328
- group: {
329
- string:
330
- remainingString === ""
331
- ? string
332
- : string.slice(0, -remainingString.length),
333
- },
334
- };
335
- }
336
- const attemptIndex = matchAttempt.index;
337
- const attemptRange = {
338
- patternIndex: matchAttempt.patternIndex,
339
- index,
340
- length: attemptIndex,
341
- groups: matchAttempt.groups,
342
- };
343
- if (
344
- !longestAttemptRange ||
345
- longestAttemptRange.length < attemptRange.length
346
- ) {
347
- longestAttemptRange = attemptRange;
348
- }
349
- if (isLastAttempt) {
350
- return failure();
351
- }
352
- const nextIndex = attemptIndex + 1;
353
- if (nextIndex >= remainingString.length) {
354
- return failure();
355
- }
356
- if (remainingString[0] === "/") {
357
- if (!canSkipSlash) {
358
- return failure();
359
- }
360
- // when it's the last slash, the next attempt is the last
361
- if (remainingString.indexOf("/", 1) === -1) {
362
- isLastAttempt = true;
363
- }
364
- }
365
- // search against the next unattempted string
366
- index += nextIndex;
367
- remainingString = remainingString.slice(nextIndex);
368
- return tryToMatch();
369
- };
370
- return tryToMatch();
371
- };
372
-
373
- const applyAssociations = ({ url, associations }) => {
374
- if (url && typeof url.href === "string") url = url.href;
375
- assertUrlLike(url);
376
- const flatAssociations = asFlatAssociations(associations);
377
- return Object.keys(flatAssociations).reduce((previousValue, pattern) => {
378
- const { matched } = applyPatternMatching({
379
- pattern,
380
- url,
381
- });
382
- if (matched) {
383
- const value = flatAssociations[pattern];
384
- if (isPlainObject(previousValue) && isPlainObject(value)) {
385
- return {
386
- ...previousValue,
387
- ...value,
388
- };
389
- }
390
- return value;
391
- }
392
- return previousValue;
393
- }, {});
394
- };
395
-
396
- const applyAliases = ({ url, aliases }) => {
397
- let aliasFullMatchResult;
398
- const aliasMatchingKey = Object.keys(aliases).find((key) => {
399
- const aliasMatchResult = applyPatternMatching({
400
- pattern: key,
401
- url,
402
- });
403
- if (aliasMatchResult.matched) {
404
- aliasFullMatchResult = aliasMatchResult;
405
- return true;
406
- }
407
- return false;
408
- });
409
- if (!aliasMatchingKey) {
410
- return url;
411
- }
412
- const { matchGroups } = aliasFullMatchResult;
413
- const alias = aliases[aliasMatchingKey];
414
- const parts = alias.split("*");
415
- const newUrl = parts.reduce((previous, value, index) => {
416
- return `${previous}${value}${
417
- index === parts.length - 1 ? "" : matchGroups[index]
418
- }`;
419
- }, "");
420
- return newUrl;
421
- };
422
-
423
- const urlChildMayMatch = ({ url, associations, predicate }) => {
424
- if (url && typeof url.href === "string") url = url.href;
425
- assertUrlLike(url, "url");
426
- // the function was meants to be used on url ending with '/'
427
- if (!url.endsWith("/")) {
428
- throw new Error(`url should end with /, got ${url}`);
429
- }
430
- if (typeof predicate !== "function") {
431
- throw new TypeError(`predicate must be a function, got ${predicate}`);
432
- }
433
- const flatAssociations = asFlatAssociations(associations);
434
- // for full match we must create an object to allow pattern to override previous ones
435
- let fullMatchMeta = {};
436
- let someFullMatch = false;
437
- // for partial match, any meta satisfying predicate will be valid because
438
- // we don't know for sure if pattern will still match for a file inside pathname
439
- const partialMatchMetaArray = [];
440
- Object.keys(flatAssociations).forEach((pattern) => {
441
- const value = flatAssociations[pattern];
442
- const matchResult = applyPatternMatching({
443
- pattern,
444
- url,
445
- });
446
- if (matchResult.matched) {
447
- someFullMatch = true;
448
- if (isPlainObject(fullMatchMeta) && isPlainObject(value)) {
449
- fullMatchMeta = {
450
- ...fullMatchMeta,
451
- ...value,
452
- };
453
- } else {
454
- fullMatchMeta = value;
455
- }
456
- } else if (someFullMatch === false && matchResult.urlIndex >= url.length) {
457
- partialMatchMetaArray.push(value);
458
- }
459
- });
460
- if (someFullMatch) {
461
- return Boolean(predicate(fullMatchMeta));
462
- }
463
- return partialMatchMetaArray.some((partialMatchMeta) =>
464
- predicate(partialMatchMeta),
465
- );
466
- };
467
-
468
- const URL_META = {
469
- resolveAssociations,
470
- applyAssociations,
471
- urlChildMayMatch,
472
- applyPatternMatching,
473
- applyAliases,
474
- };
1
+ import { URL_META } from "./exception.js";
475
2
 
476
3
  const filterV8Coverage = async (
477
4
  v8Coverage,
478
- { rootDirectoryUrl, coverageConfig },
5
+ { rootDirectoryUrl, coverageInclude },
479
6
  ) => {
480
7
  const associations = URL_META.resolveAssociations(
481
- { cover: coverageConfig },
8
+ { cover: coverageInclude },
482
9
  rootDirectoryUrl,
483
10
  );
484
11
  const urlShouldBeCovered = (url) => {
@@ -498,9 +25,4 @@ const filterV8Coverage = async (
498
25
  return v8CoverageFiltered;
499
26
  };
500
27
 
501
- const v8_coverage = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
502
- __proto__: null,
503
- filterV8Coverage
504
- }, Symbol.toStringTag, { value: 'Module' }));
505
-
506
- export { URL_META, filterV8Coverage, v8_coverage };
28
+ export { filterV8Coverage };