@jsenv/test 3.2.1 → 3.3.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/jsenv_test.js +471 -160
- package/package.json +7 -7
- package/src/execution/execute_test_plan.js +156 -64
- package/src/execution/format_error_for_terminal.js +2 -51
- package/src/execution/is_inside_fragment.js +32 -0
- package/src/execution/junit_xml_file/report_as_junit_xml.js +10 -6
- package/src/execution/replace_urls.js +50 -0
- package/src/execution/report_as_json.js +121 -0
- package/src/execution/reporters/reporter_list.js +91 -24
- package/src/main.js +1 -0
- package/src/runtime_browsers/firefox.js +2 -1
- package/src/runtime_node/node_child_process.js +3 -7
- package/src/runtime_node/node_worker_thread.js +3 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/test",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -38,13 +38,13 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@c88/v8-coverage": "0.1.1",
|
|
40
40
|
"@jsenv/abort": "4.3.0",
|
|
41
|
-
"@jsenv/ast": "6.1.
|
|
42
|
-
"@jsenv/filesystem": "4.7.
|
|
43
|
-
"@jsenv/github-check-run": "0.1.
|
|
44
|
-
"@jsenv/humanize": "1.
|
|
45
|
-
"@jsenv/plugin-supervisor": "1.4.
|
|
41
|
+
"@jsenv/ast": "6.1.2",
|
|
42
|
+
"@jsenv/filesystem": "4.7.3",
|
|
43
|
+
"@jsenv/github-check-run": "0.1.18",
|
|
44
|
+
"@jsenv/humanize": "1.2.0",
|
|
45
|
+
"@jsenv/plugin-supervisor": "1.4.14",
|
|
46
46
|
"@jsenv/url-meta": "8.4.2",
|
|
47
|
-
"@jsenv/urls": "2.2.
|
|
47
|
+
"@jsenv/urls": "2.2.8",
|
|
48
48
|
"@jsenv/utils": "2.1.1",
|
|
49
49
|
"istanbul-lib-coverage": "3.2.2",
|
|
50
50
|
"istanbul-lib-instrument": "6.0.3",
|
|
@@ -33,6 +33,7 @@ import { assertAndNormalizeWebServer } from "./web_server_param.js";
|
|
|
33
33
|
import { githubAnnotationFromError } from "./github_annotation_from_error.js";
|
|
34
34
|
import { run } from "./run.js";
|
|
35
35
|
import { reporterList, renderOutroContent } from "./reporters/reporter_list.js";
|
|
36
|
+
import { createIsInsideFragment } from "./is_inside_fragment.js";
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* Execute a list of files and log how it goes.
|
|
@@ -114,6 +115,11 @@ export const executeTestPlan = async ({
|
|
|
114
115
|
handleSIGTERM = true,
|
|
115
116
|
updateProcessExitCode = true,
|
|
116
117
|
parallel = parallelDefault,
|
|
118
|
+
// https://github.com/avajs/ava/blob/main/docs/recipes/splitting-tests-ci.md
|
|
119
|
+
// https://playwright.dev/docs/test-sharding
|
|
120
|
+
fragment,
|
|
121
|
+
fragmentByRuntime,
|
|
122
|
+
fragmentBy,
|
|
117
123
|
defaultMsAllocatedPerExecution = 30_000,
|
|
118
124
|
failFast = false,
|
|
119
125
|
// keepRunning: false to ensure runtime is stopped once executed
|
|
@@ -207,6 +213,9 @@ export const executeTestPlan = async ({
|
|
|
207
213
|
rootDirectoryUrl: String(rootDirectoryUrl),
|
|
208
214
|
patterns: Object.keys(testPlan),
|
|
209
215
|
groups: {},
|
|
216
|
+
fragment,
|
|
217
|
+
fragmentStart: undefined,
|
|
218
|
+
fragmentEnd: undefined,
|
|
210
219
|
counters: {
|
|
211
220
|
planified: 0,
|
|
212
221
|
remaining: 0,
|
|
@@ -214,6 +223,7 @@ export const executeTestPlan = async ({
|
|
|
214
223
|
executing: 0,
|
|
215
224
|
executed: 0,
|
|
216
225
|
|
|
226
|
+
skipped: 0,
|
|
217
227
|
aborted: 0,
|
|
218
228
|
cancelled: 0,
|
|
219
229
|
timedout: 0,
|
|
@@ -243,6 +253,36 @@ export const executeTestPlan = async ({
|
|
|
243
253
|
};
|
|
244
254
|
const beforeEachCallbackSet = new Set();
|
|
245
255
|
const beforeEachInOrderCallbackSet = new Set();
|
|
256
|
+
const triggerBeforeEach = (execution, testPlanResult, testPlanHelpers) => {
|
|
257
|
+
for (const beforeEachCallback of beforeEachCallbackSet) {
|
|
258
|
+
const returnValue = beforeEachCallback(
|
|
259
|
+
execution,
|
|
260
|
+
testPlanResult,
|
|
261
|
+
testPlanHelpers,
|
|
262
|
+
);
|
|
263
|
+
if (typeof returnValue === "function") {
|
|
264
|
+
const callback = (...args) => {
|
|
265
|
+
afterEachCallbackSet.delete(callback);
|
|
266
|
+
return returnValue(...args);
|
|
267
|
+
};
|
|
268
|
+
afterEachCallbackSet.add(callback);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
for (const beforeEachInOrderCallback of beforeEachInOrderCallbackSet) {
|
|
272
|
+
const returnValue = beforeEachInOrderCallback(
|
|
273
|
+
execution,
|
|
274
|
+
testPlanResult,
|
|
275
|
+
testPlanHelpers,
|
|
276
|
+
);
|
|
277
|
+
if (typeof returnValue === "function") {
|
|
278
|
+
const callback = (...args) => {
|
|
279
|
+
afterEachInOrderCallbackSet.delete(callback);
|
|
280
|
+
return returnValue(...args);
|
|
281
|
+
};
|
|
282
|
+
afterEachInOrderCallbackSet.add(callback);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
};
|
|
246
286
|
const afterEachCallbackSet = new Set();
|
|
247
287
|
const afterEachInOrderCallbackSet = new Set();
|
|
248
288
|
const afterAllCallbackSet = new Set();
|
|
@@ -375,6 +415,16 @@ export const executeTestPlan = async ({
|
|
|
375
415
|
);
|
|
376
416
|
}
|
|
377
417
|
}
|
|
418
|
+
// fragment/fragmentByRuntime
|
|
419
|
+
{
|
|
420
|
+
if (fragment) {
|
|
421
|
+
if (!/[0-9]+\/[0-9]+/.test(fragment)) {
|
|
422
|
+
throw new TypeError(
|
|
423
|
+
`fragment must look like number/number, received ${fragment}`,
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
378
428
|
// testPlan
|
|
379
429
|
{
|
|
380
430
|
if (typeof testPlan !== "object") {
|
|
@@ -579,9 +629,21 @@ To fix this warning:
|
|
|
579
629
|
}
|
|
580
630
|
}
|
|
581
631
|
|
|
582
|
-
const
|
|
632
|
+
const executionPlanifiedArray = [];
|
|
633
|
+
const getPreviousExecution = (execution) => {
|
|
634
|
+
const index = execution.index;
|
|
635
|
+
return executionPlanifiedArray[index - 1] || null;
|
|
636
|
+
};
|
|
637
|
+
const getNextExecution = (execution) => {
|
|
638
|
+
const index = execution.index;
|
|
639
|
+
return executionPlanifiedArray[index + 1] || null;
|
|
640
|
+
};
|
|
641
|
+
const testPlanHelpers = {
|
|
642
|
+
getPreviousExecution,
|
|
643
|
+
getNextExecution,
|
|
644
|
+
};
|
|
583
645
|
|
|
584
|
-
// collect files to execute + fill
|
|
646
|
+
// collect files to execute + fill executionPlanifiedArray
|
|
585
647
|
{
|
|
586
648
|
const fileResultArray = await collectFiles({
|
|
587
649
|
signal,
|
|
@@ -589,6 +651,11 @@ To fix this warning:
|
|
|
589
651
|
associations: { testPlan },
|
|
590
652
|
predicate: ({ testPlan }) => testPlan,
|
|
591
653
|
});
|
|
654
|
+
|
|
655
|
+
// idéalement dans les logs on aurait un truc comme ceci:
|
|
656
|
+
// --- 100 execution found ---
|
|
657
|
+
// Executing only from 33 to 66 according to fragment: "2/3"
|
|
658
|
+
|
|
592
659
|
let index = 0;
|
|
593
660
|
let lastExecution;
|
|
594
661
|
const fileExecutionCountMap = new Map();
|
|
@@ -611,10 +678,6 @@ To fix this warning:
|
|
|
611
678
|
),
|
|
612
679
|
);
|
|
613
680
|
}
|
|
614
|
-
if (stepConfig.runtime?.disabled) {
|
|
615
|
-
continue;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
681
|
const {
|
|
619
682
|
runtime,
|
|
620
683
|
runtimeParams,
|
|
@@ -671,6 +734,8 @@ To fix this warning:
|
|
|
671
734
|
runtimeName,
|
|
672
735
|
runtimeVersion,
|
|
673
736
|
params,
|
|
737
|
+
skipped: false,
|
|
738
|
+
skipReason: "",
|
|
674
739
|
|
|
675
740
|
// will be set by run()
|
|
676
741
|
status: "planified",
|
|
@@ -681,7 +746,7 @@ To fix this warning:
|
|
|
681
746
|
}
|
|
682
747
|
|
|
683
748
|
lastExecution = execution;
|
|
684
|
-
|
|
749
|
+
executionPlanifiedArray.push(execution);
|
|
685
750
|
const existingResults = results[relativeUrl];
|
|
686
751
|
if (existingResults) {
|
|
687
752
|
existingResults[groupName] = execution.result;
|
|
@@ -701,9 +766,48 @@ To fix this warning:
|
|
|
701
766
|
runtimeVersion,
|
|
702
767
|
};
|
|
703
768
|
}
|
|
769
|
+
|
|
770
|
+
if (runtime?.disabled) {
|
|
771
|
+
execution.skipped = true;
|
|
772
|
+
execution.skipReason =
|
|
773
|
+
runtime.disabledReason || "reason not specified";
|
|
774
|
+
} else if (fragmentByRuntime && runtime.name !== fragmentByRuntime) {
|
|
775
|
+
execution.skipped = true;
|
|
776
|
+
execution.skipReason = "runtime disabled";
|
|
777
|
+
} else if (fragmentBy) {
|
|
778
|
+
const fragmentByResult = fragmentBy(execution);
|
|
779
|
+
if (fragmentByResult) {
|
|
780
|
+
execution.skipped = true;
|
|
781
|
+
execution.skipReason =
|
|
782
|
+
typeof fragmentByResult === "string"
|
|
783
|
+
? fragmentByResult
|
|
784
|
+
: `reason not specified`;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
704
787
|
index++;
|
|
705
788
|
}
|
|
706
789
|
}
|
|
790
|
+
|
|
791
|
+
if (fragment) {
|
|
792
|
+
const total = executionPlanifiedArray.length;
|
|
793
|
+
const isInsideFragment = createIsInsideFragment(fragment, total);
|
|
794
|
+
for (const execution of executionPlanifiedArray) {
|
|
795
|
+
if (isInsideFragment(execution.index)) {
|
|
796
|
+
const executionNumber = execution.index + 1;
|
|
797
|
+
if (testPlanResult.fragmentStart === undefined) {
|
|
798
|
+
testPlanResult.fragmentStart = executionNumber;
|
|
799
|
+
}
|
|
800
|
+
if (
|
|
801
|
+
testPlanResult.fragmentEnd === undefined ||
|
|
802
|
+
executionNumber > testPlanResult.fragmentEnd
|
|
803
|
+
) {
|
|
804
|
+
testPlanResult.fragmentEnd = executionNumber;
|
|
805
|
+
}
|
|
806
|
+
} else {
|
|
807
|
+
execution.skipped = true;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
707
811
|
fileResultArray.length = 0;
|
|
708
812
|
fileExecutionCountMap.clear();
|
|
709
813
|
if (lastExecution) {
|
|
@@ -714,11 +818,11 @@ To fix this warning:
|
|
|
714
818
|
counters.planified =
|
|
715
819
|
counters.remaining =
|
|
716
820
|
counters.waiting =
|
|
717
|
-
|
|
821
|
+
executionPlanifiedArray.length;
|
|
718
822
|
countersInOrder.planified =
|
|
719
823
|
countersInOrder.remaining =
|
|
720
824
|
countersInOrder.waiting =
|
|
721
|
-
|
|
825
|
+
executionPlanifiedArray.length;
|
|
722
826
|
if (githubCheck) {
|
|
723
827
|
const githubCheckRun = await startGithubCheckRun({
|
|
724
828
|
logLevel: githubCheck.logLevel,
|
|
@@ -728,7 +832,7 @@ To fix this warning:
|
|
|
728
832
|
commitSha: githubCheck.commitSha,
|
|
729
833
|
checkName: githubCheck.name,
|
|
730
834
|
checkTitle: githubCheck.title,
|
|
731
|
-
checkSummary: `${
|
|
835
|
+
checkSummary: `${executionPlanifiedArray.length} files will be executed`,
|
|
732
836
|
});
|
|
733
837
|
const annotations = [];
|
|
734
838
|
reporters.push({
|
|
@@ -808,80 +912,66 @@ To fix this warning:
|
|
|
808
912
|
|
|
809
913
|
const callWhenPreviousExecutionAreDone = createCallOrderer();
|
|
810
914
|
|
|
811
|
-
const executionRemainingSet = new Set(
|
|
915
|
+
const executionRemainingSet = new Set(executionPlanifiedArray);
|
|
812
916
|
const executionExecutingSet = new Set();
|
|
813
917
|
const usedTagSet = new Set();
|
|
814
918
|
const start = async (execution) => {
|
|
815
|
-
if (execution.params.uses) {
|
|
816
|
-
for (const tagThatWillBeUsed of execution.params.uses) {
|
|
817
|
-
usedTagSet.add(tagThatWillBeUsed);
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
919
|
execution.fileExecutionCount = Object.keys(
|
|
821
920
|
testPlanResult.results[execution.fileRelativeUrl],
|
|
822
921
|
).length;
|
|
823
922
|
mutateCountersBeforeExecutionStarts(counters, execution);
|
|
824
923
|
mutateCountersBeforeExecutionStarts(countersInOrder, execution);
|
|
825
|
-
|
|
826
|
-
execution.status = "executing";
|
|
827
924
|
executionRemainingSet.delete(execution);
|
|
828
925
|
executionExecutingSet.add(execution);
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
926
|
+
triggerBeforeEach(execution, testPlanResult, testPlanHelpers);
|
|
927
|
+
if (execution.skipped) {
|
|
928
|
+
execution.result.status = "skipped";
|
|
929
|
+
execution.result.value = execution.skipReason;
|
|
930
|
+
} else {
|
|
931
|
+
if (execution.params.uses) {
|
|
932
|
+
for (const tagThatWillBeUsed of execution.params.uses) {
|
|
933
|
+
usedTagSet.add(tagThatWillBeUsed);
|
|
934
|
+
}
|
|
837
935
|
}
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
936
|
+
execution.status = "executing";
|
|
937
|
+
const executionResult = await run({
|
|
938
|
+
...execution.params,
|
|
939
|
+
signal: operation.signal,
|
|
940
|
+
logger,
|
|
941
|
+
keepRunning,
|
|
942
|
+
mirrorConsole: false, // might be executed in parallel: log would be a mess to read
|
|
943
|
+
coverageEnabled: Boolean(coverage),
|
|
944
|
+
coverageTempDirectoryUrl: coverage?.tempDirectoryUrl,
|
|
945
|
+
});
|
|
946
|
+
Object.assign(execution.result, executionResult);
|
|
947
|
+
execution.status = "executed";
|
|
948
|
+
if (execution.params.uses) {
|
|
949
|
+
for (const tagNoLongerInUse of execution.params.uses) {
|
|
950
|
+
usedTagSet.delete(tagNoLongerInUse);
|
|
951
|
+
}
|
|
850
952
|
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
keepRunning,
|
|
857
|
-
mirrorConsole: false, // might be executed in parallel: log would be a mess to read
|
|
858
|
-
coverageEnabled: Boolean(coverage),
|
|
859
|
-
coverageTempDirectoryUrl: coverage?.tempDirectoryUrl,
|
|
860
|
-
});
|
|
861
|
-
Object.assign(execution.result, executionResult);
|
|
862
|
-
execution.status = "executed";
|
|
863
|
-
executionExecutingSet.delete(execution);
|
|
864
|
-
mutateCountersAfterExecutionEnds(counters, execution);
|
|
865
|
-
if (execution.result.status !== "completed") {
|
|
866
|
-
testPlanResult.failed = true;
|
|
867
|
-
if (updateProcessExitCode) {
|
|
868
|
-
process.exitCode = 1;
|
|
953
|
+
if (execution.result.status !== "completed") {
|
|
954
|
+
testPlanResult.failed = true;
|
|
955
|
+
if (updateProcessExitCode) {
|
|
956
|
+
process.exitCode = 1;
|
|
957
|
+
}
|
|
869
958
|
}
|
|
870
959
|
}
|
|
960
|
+
mutateCountersAfterExecutionEnds(counters, execution);
|
|
961
|
+
executionExecutingSet.delete(execution);
|
|
871
962
|
for (const afterEachCallback of afterEachCallbackSet) {
|
|
872
|
-
afterEachCallback(execution, testPlanResult);
|
|
963
|
+
afterEachCallback(execution, testPlanResult, testPlanHelpers);
|
|
873
964
|
}
|
|
874
965
|
callWhenPreviousExecutionAreDone(execution.index, () => {
|
|
875
966
|
mutateCountersAfterExecutionEnds(countersInOrder, execution);
|
|
876
967
|
for (const afterEachInOrderCallback of afterEachInOrderCallbackSet) {
|
|
877
|
-
afterEachInOrderCallback(
|
|
968
|
+
afterEachInOrderCallback(
|
|
969
|
+
execution,
|
|
970
|
+
testPlanResult,
|
|
971
|
+
testPlanHelpers,
|
|
972
|
+
);
|
|
878
973
|
}
|
|
879
974
|
});
|
|
880
|
-
if (execution.params.uses) {
|
|
881
|
-
for (const tagNoLongerInUse of execution.params.uses) {
|
|
882
|
-
usedTagSet.delete(tagNoLongerInUse);
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
975
|
if (testPlanResult.failed && failFast && counters.remaining) {
|
|
886
976
|
logger.info(`"failFast" enabled -> cancel remaining executions`);
|
|
887
977
|
failFastAbortController.abort();
|
|
@@ -1096,7 +1186,9 @@ const mutateCountersAfterExecutionEnds = (counters, execution) => {
|
|
|
1096
1186
|
counters.executing--;
|
|
1097
1187
|
counters.executed++;
|
|
1098
1188
|
counters.remaining--;
|
|
1099
|
-
if (execution.result.status === "
|
|
1189
|
+
if (execution.result.status === "skipped") {
|
|
1190
|
+
counters.skipped++;
|
|
1191
|
+
} else if (execution.result.status === "aborted") {
|
|
1100
1192
|
counters.aborted++;
|
|
1101
1193
|
} else if (execution.result.status === "timedout") {
|
|
1102
1194
|
counters.timedout++;
|
|
@@ -2,6 +2,8 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { generateContentFrame, ANSI } from "@jsenv/humanize";
|
|
3
3
|
import { urlToFileSystemPath } from "@jsenv/urls";
|
|
4
4
|
|
|
5
|
+
import { replaceUrls } from "./replace_urls.js";
|
|
6
|
+
|
|
5
7
|
export const formatErrorForTerminal = (
|
|
6
8
|
error,
|
|
7
9
|
{ rootDirectoryUrl, mainFileRelativeUrl, mockFluctuatingValues, tryColors },
|
|
@@ -133,54 +135,3 @@ export const formatErrorForTerminal = (
|
|
|
133
135
|
|
|
134
136
|
return text;
|
|
135
137
|
};
|
|
136
|
-
|
|
137
|
-
// `Error: yo
|
|
138
|
-
// at Object.execute (http://127.0.0.1:57300/build/src/__test__/file-throw.js:9:13)
|
|
139
|
-
// at doExec (http://127.0.0.1:3000/src/__test__/file-throw.js:452:38)
|
|
140
|
-
// at postOrderExec (http://127.0.0.1:3000/src/__test__/file-throw.js:448:16)
|
|
141
|
-
// at http://127.0.0.1:3000/src/__test__/file-throw.js:399:18`.replace(/(?:https?|ftp|file):\/\/(.*+)$/gm, (...args) => {
|
|
142
|
-
// debugger
|
|
143
|
-
// })
|
|
144
|
-
const replaceUrls = (source, replace) => {
|
|
145
|
-
return source.replace(/(?:https?|ftp|file):\/\/\S+/g, (match) => {
|
|
146
|
-
let replacement = "";
|
|
147
|
-
const lastChar = match[match.length - 1];
|
|
148
|
-
|
|
149
|
-
// hotfix because our url regex sucks a bit
|
|
150
|
-
const endsWithSeparationChar = lastChar === ")" || lastChar === ":";
|
|
151
|
-
if (endsWithSeparationChar) {
|
|
152
|
-
match = match.slice(0, -1);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const lineAndColumnPattern = /:([0-9]+):([0-9]+)$/;
|
|
156
|
-
const lineAndColumMatch = match.match(lineAndColumnPattern);
|
|
157
|
-
if (lineAndColumMatch) {
|
|
158
|
-
const lineAndColumnString = lineAndColumMatch[0];
|
|
159
|
-
const lineString = lineAndColumMatch[1];
|
|
160
|
-
const columnString = lineAndColumMatch[2];
|
|
161
|
-
replacement = replace({
|
|
162
|
-
url: match.slice(0, -lineAndColumnString.length),
|
|
163
|
-
line: lineString ? parseInt(lineString) : null,
|
|
164
|
-
column: columnString ? parseInt(columnString) : null,
|
|
165
|
-
});
|
|
166
|
-
} else {
|
|
167
|
-
const linePattern = /:([0-9]+)$/;
|
|
168
|
-
const lineMatch = match.match(linePattern);
|
|
169
|
-
if (lineMatch) {
|
|
170
|
-
const lineString = lineMatch[0];
|
|
171
|
-
replacement = replace({
|
|
172
|
-
url: match.slice(0, -lineString.length),
|
|
173
|
-
line: lineString ? parseInt(lineString) : null,
|
|
174
|
-
});
|
|
175
|
-
} else {
|
|
176
|
-
replacement = replace({
|
|
177
|
-
url: match,
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
if (endsWithSeparationChar) {
|
|
182
|
-
return `${replacement}${lastChar}`;
|
|
183
|
-
}
|
|
184
|
-
return replacement;
|
|
185
|
-
});
|
|
186
|
-
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const createIsInsideFragment = (fragment, total) => {
|
|
2
|
+
let [dividend, divisor] = fragment.split("/");
|
|
3
|
+
dividend = parseInt(dividend);
|
|
4
|
+
divisor = parseInt(divisor);
|
|
5
|
+
const groupSize = Math.ceil(total / divisor);
|
|
6
|
+
let from;
|
|
7
|
+
let to;
|
|
8
|
+
if (groupSize === 0) {
|
|
9
|
+
if (dividend === 1) {
|
|
10
|
+
from = 0;
|
|
11
|
+
to = 0;
|
|
12
|
+
} else {
|
|
13
|
+
from = Infinity;
|
|
14
|
+
to = -1;
|
|
15
|
+
}
|
|
16
|
+
} else if (dividend === 1) {
|
|
17
|
+
from = 0;
|
|
18
|
+
to = groupSize;
|
|
19
|
+
} else {
|
|
20
|
+
from = (dividend - 1) * groupSize;
|
|
21
|
+
if (dividend === divisor) {
|
|
22
|
+
to = total;
|
|
23
|
+
} else {
|
|
24
|
+
to = from + groupSize;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return (index) => {
|
|
28
|
+
if (index < from) return false;
|
|
29
|
+
if (index >= to) return false;
|
|
30
|
+
return true;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
@@ -46,22 +46,26 @@ export const reportAsJunitXml = async (
|
|
|
46
46
|
});
|
|
47
47
|
testSuite.appendChild(testCase);
|
|
48
48
|
|
|
49
|
-
if (executionResult.status === "
|
|
49
|
+
if (executionResult.status === "skipped") {
|
|
50
|
+
testCase.attributes.skipped = 1;
|
|
51
|
+
const skipped = testCase.createNode("skipped", {
|
|
52
|
+
message: "Execution was skipped",
|
|
53
|
+
});
|
|
54
|
+
testSuite.appendChild(skipped);
|
|
55
|
+
} else if (executionResult.status === "aborted") {
|
|
50
56
|
testCase.attributes.skipped = 1;
|
|
51
57
|
const skipped = testCase.createNode("skipped", {
|
|
52
58
|
message: "Execution was aborted",
|
|
53
59
|
});
|
|
54
60
|
testSuite.appendChild(skipped);
|
|
55
|
-
}
|
|
56
|
-
if (executionResult.status === "timedout") {
|
|
61
|
+
} else if (executionResult.status === "timedout") {
|
|
57
62
|
testCase.attributes.failures = 1;
|
|
58
63
|
const failure = testCase.createNode("failure", {
|
|
59
64
|
message: `Execution timeout after ${executionResult.params.allocatedMs}ms"`,
|
|
60
65
|
type: "timeout",
|
|
61
66
|
});
|
|
62
67
|
testSuite.appendChild(failure);
|
|
63
|
-
}
|
|
64
|
-
if (executionResult.status === "failed") {
|
|
68
|
+
} else if (executionResult.status === "failed") {
|
|
65
69
|
const [error] = executionResult.errors;
|
|
66
70
|
if (
|
|
67
71
|
error &&
|
|
@@ -115,7 +119,7 @@ export const reportAsJunitXml = async (
|
|
|
115
119
|
propertiesNode.appendChild(propertyNode);
|
|
116
120
|
}
|
|
117
121
|
|
|
118
|
-
const { consoleCalls } = executionResult;
|
|
122
|
+
const { consoleCalls = [] } = executionResult;
|
|
119
123
|
let stdout = "";
|
|
120
124
|
let stderr = "";
|
|
121
125
|
for (const consoleCall of consoleCalls) {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// `Error: yo
|
|
2
|
+
// at Object.execute (http://127.0.0.1:57300/build/src/__test__/file-throw.js:9:13)
|
|
3
|
+
// at doExec (http://127.0.0.1:3000/src/__test__/file-throw.js:452:38)
|
|
4
|
+
// at postOrderExec (http://127.0.0.1:3000/src/__test__/file-throw.js:448:16)
|
|
5
|
+
// at http://127.0.0.1:3000/src/__test__/file-throw.js:399:18`.replace(/(?:https?|ftp|file):\/\/(.*+)$/gm, (...args) => {
|
|
6
|
+
// debugger
|
|
7
|
+
// })
|
|
8
|
+
export const replaceUrls = (source, replace) => {
|
|
9
|
+
return source.replace(/(?:https?|ftp|file):\/\/\S+/g, (match) => {
|
|
10
|
+
let replacement = "";
|
|
11
|
+
const lastChar = match[match.length - 1];
|
|
12
|
+
|
|
13
|
+
// hotfix because our url regex sucks a bit
|
|
14
|
+
const endsWithSeparationChar = lastChar === ")" || lastChar === ":";
|
|
15
|
+
if (endsWithSeparationChar) {
|
|
16
|
+
match = match.slice(0, -1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const lineAndColumnPattern = /:([0-9]+):([0-9]+)$/;
|
|
20
|
+
const lineAndColumMatch = match.match(lineAndColumnPattern);
|
|
21
|
+
if (lineAndColumMatch) {
|
|
22
|
+
const lineAndColumnString = lineAndColumMatch[0];
|
|
23
|
+
const lineString = lineAndColumMatch[1];
|
|
24
|
+
const columnString = lineAndColumMatch[2];
|
|
25
|
+
replacement = replace({
|
|
26
|
+
url: match.slice(0, -lineAndColumnString.length),
|
|
27
|
+
line: lineString ? parseInt(lineString) : null,
|
|
28
|
+
column: columnString ? parseInt(columnString) : null,
|
|
29
|
+
});
|
|
30
|
+
} else {
|
|
31
|
+
const linePattern = /:([0-9]+)$/;
|
|
32
|
+
const lineMatch = match.match(linePattern);
|
|
33
|
+
if (lineMatch) {
|
|
34
|
+
const lineString = lineMatch[0];
|
|
35
|
+
replacement = replace({
|
|
36
|
+
url: match.slice(0, -lineString.length),
|
|
37
|
+
line: lineString ? parseInt(lineString) : null,
|
|
38
|
+
});
|
|
39
|
+
} else {
|
|
40
|
+
replacement = replace({
|
|
41
|
+
url: match,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (endsWithSeparationChar) {
|
|
46
|
+
return `${replacement}${lastChar}`;
|
|
47
|
+
}
|
|
48
|
+
return replacement;
|
|
49
|
+
});
|
|
50
|
+
};
|