@alwaysmeticulous/cli 2.38.0 → 2.40.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/commands/bootstrap/bootstrap.command.js +1 -1
- package/dist/commands/create-test/create-test.command.d.ts +0 -4
- package/dist/commands/create-test/create-test.command.js +1 -6
- package/dist/commands/replay/replay.command.d.ts +1 -6
- package/dist/commands/replay/replay.command.js +1 -6
- package/dist/commands/run-all-tests/run-all-tests.command.d.ts +5 -5
- package/dist/commands/run-all-tests/run-all-tests.command.js +7 -8
- package/dist/commands/screenshot-diff/utils/__tests__/get-screenshot-identifier.spec.d.ts +1 -0
- package/dist/commands/screenshot-diff/utils/__tests__/get-screenshot-identifier.spec.js +25 -0
- package/dist/commands/screenshot-diff/utils/get-screenshot-filename.js +6 -2
- package/dist/commands/screenshot-diff/utils/get-screenshot-identifier.js +16 -1
- package/dist/config/config.js +1 -2
- package/dist/parallel-tests/__tests__/merge-test-results.spec.js +4 -2
- package/dist/parallel-tests/__tests__/parrallel-tests.handler.spec.js +30 -0
- package/dist/parallel-tests/merge-test-results.d.ts +3 -1
- package/dist/parallel-tests/merge-test-results.js +19 -23
- package/dist/parallel-tests/messages.types.d.ts +2 -2
- package/dist/parallel-tests/parallel-replay.handler.d.ts +3 -0
- package/dist/{deflake-tests/deflake-tests.handler.js → parallel-tests/parallel-replay.handler.js} +3 -23
- package/dist/{deflake-tests/deflake-tests.handler.d.ts → parallel-tests/parallel-replay.types.d.ts} +1 -7
- package/dist/parallel-tests/parallel-replay.types.js +2 -0
- package/dist/parallel-tests/parallel-tests.handler.d.ts +1 -0
- package/dist/parallel-tests/parallel-tests.handler.js +30 -7
- package/dist/parallel-tests/run-all-tests.d.ts +11 -2
- package/dist/parallel-tests/run-all-tests.js +3 -7
- package/dist/parallel-tests/task.handler.js +2 -2
- package/package.json +4 -4
|
@@ -22,7 +22,7 @@ const handler = async () => {
|
|
|
22
22
|
logger.info(`Setting up ${chalk_1.default.green("test:meticulous")} script...`);
|
|
23
23
|
await (0, npm_set_script_utils_1.npmSetScript)({
|
|
24
24
|
script: "test:meticulous",
|
|
25
|
-
command: "meticulous run-all-tests --headless --parallelize
|
|
25
|
+
command: "meticulous run-all-tests --headless --parallelize",
|
|
26
26
|
});
|
|
27
27
|
};
|
|
28
28
|
exports.bootstrapCommand = (0, command_builder_1.buildCommand)("bootstrap")
|
|
@@ -78,10 +78,6 @@ export declare const createTestCommand: import("yargs").CommandModule<unknown, i
|
|
|
78
78
|
boolean: true;
|
|
79
79
|
description: string;
|
|
80
80
|
};
|
|
81
|
-
screenshotSelector: {
|
|
82
|
-
string: true;
|
|
83
|
-
description: string;
|
|
84
|
-
};
|
|
85
81
|
moveBeforeClick: {
|
|
86
82
|
readonly boolean: true;
|
|
87
83
|
readonly description: "Simulate mouse movement before clicking";
|
|
@@ -59,7 +59,7 @@ apiToken, commitSha, devTools, bypassCSP,
|
|
|
59
59
|
// Record options
|
|
60
60
|
width, height, uploadIntervalMs, incognito, trace,
|
|
61
61
|
// Replay options
|
|
62
|
-
headless,
|
|
62
|
+
headless, shiftTime, networkStubbing, moveBeforeClick, cookiesFile, disableRemoteFonts, noSandbox, skipPauses, }) => {
|
|
63
63
|
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
64
64
|
logger.info("Creating a new Meticulous test");
|
|
65
65
|
logger.info("Step 1: record a new test");
|
|
@@ -95,7 +95,6 @@ headless, screenshotSelector, shiftTime, networkStubbing, moveBeforeClick, cooki
|
|
|
95
95
|
devTools,
|
|
96
96
|
bypassCSP,
|
|
97
97
|
screenshot: true,
|
|
98
|
-
screenshotSelector,
|
|
99
98
|
shiftTime,
|
|
100
99
|
disableRemoteFonts,
|
|
101
100
|
noSandbox,
|
|
@@ -148,10 +147,6 @@ exports.createTestCommand = (0, command_builder_1.buildCommand)("create-test")
|
|
|
148
147
|
description: "Enable verbose logging",
|
|
149
148
|
},
|
|
150
149
|
// Replay options
|
|
151
|
-
screenshotSelector: {
|
|
152
|
-
string: true,
|
|
153
|
-
description: "Query selector to screenshot a specific DOM element instead of the whole page",
|
|
154
|
-
},
|
|
155
150
|
moveBeforeClick: common_options_1.OPTIONS.moveBeforeClick,
|
|
156
151
|
cookiesFile: {
|
|
157
152
|
string: true,
|
|
@@ -23,7 +23,6 @@ export interface RawReplayCommandHandlerOptions extends ScreenshotDiffOptions, O
|
|
|
23
23
|
screenshot: boolean;
|
|
24
24
|
appUrl: string | null | undefined;
|
|
25
25
|
simulationIdForAssets: string | null | undefined;
|
|
26
|
-
screenshotSelector: string | null | undefined;
|
|
27
26
|
maxDurationMs: number | null | undefined;
|
|
28
27
|
maxEventCount: number | null | undefined;
|
|
29
28
|
storyboard: boolean;
|
|
@@ -36,7 +35,7 @@ interface AdditionalReplayOptions {
|
|
|
36
35
|
cookiesFile: string | null | undefined;
|
|
37
36
|
debugger: boolean;
|
|
38
37
|
}
|
|
39
|
-
export declare const rawReplayCommandHandler: ({ apiToken, commitSha, sessionId, appUrl, simulationIdForAssets, headless, devTools, bypassCSP, screenshot,
|
|
38
|
+
export declare const rawReplayCommandHandler: ({ apiToken, commitSha, sessionId, appUrl, simulationIdForAssets, headless, devTools, bypassCSP, screenshot, diffThreshold, diffPixelThreshold, shiftTime, networkStubbing, moveBeforeClick, cookiesFile, disableRemoteFonts, noSandbox, skipPauses, maxDurationMs, maxEventCount, storyboard, essentialFeaturesOnly, debugger: enableStepThroughDebugger, }: RawReplayCommandHandlerOptions) => Promise<Replay>;
|
|
40
39
|
export declare const getReplayTarget: ({ appUrl, simulationIdForAssets, }: {
|
|
41
40
|
appUrl: string | null;
|
|
42
41
|
simulationIdForAssets: string | null;
|
|
@@ -134,10 +133,6 @@ export declare const replayCommand: import("yargs").CommandModule<unknown, impor
|
|
|
134
133
|
description: string;
|
|
135
134
|
default: boolean;
|
|
136
135
|
};
|
|
137
|
-
screenshotSelector: {
|
|
138
|
-
string: true;
|
|
139
|
-
description: string;
|
|
140
|
-
};
|
|
141
136
|
debugger: {
|
|
142
137
|
boolean: true;
|
|
143
138
|
description: string;
|
|
@@ -232,7 +232,7 @@ const serveOrGetAppUrl = async (client, replayTarget) => {
|
|
|
232
232
|
const unknownReplayTargetType = (replayTarget) => {
|
|
233
233
|
throw new Error(`Unknown type of replay target: ${JSON.stringify(replayTarget)}`);
|
|
234
234
|
};
|
|
235
|
-
const rawReplayCommandHandler = async ({ apiToken, commitSha, sessionId, appUrl, simulationIdForAssets, headless, devTools, bypassCSP, screenshot,
|
|
235
|
+
const rawReplayCommandHandler = async ({ apiToken, commitSha, sessionId, appUrl, simulationIdForAssets, headless, devTools, bypassCSP, screenshot, diffThreshold, diffPixelThreshold, shiftTime, networkStubbing, moveBeforeClick, cookiesFile, disableRemoteFonts, noSandbox, skipPauses, maxDurationMs, maxEventCount, storyboard, essentialFeaturesOnly, debugger: enableStepThroughDebugger, }) => {
|
|
236
236
|
const executionOptions = {
|
|
237
237
|
headless,
|
|
238
238
|
devTools,
|
|
@@ -254,7 +254,6 @@ const rawReplayCommandHandler = async ({ apiToken, commitSha, sessionId, appUrl,
|
|
|
254
254
|
const screenshottingOptions = screenshot
|
|
255
255
|
? {
|
|
256
256
|
enabled: true,
|
|
257
|
-
screenshotSelector: screenshotSelector !== null && screenshotSelector !== void 0 ? screenshotSelector : null,
|
|
258
257
|
diffOptions: { diffPixelThreshold, diffThreshold },
|
|
259
258
|
storyboardOptions,
|
|
260
259
|
}
|
|
@@ -317,10 +316,6 @@ exports.replayCommand = (0, command_builder_1.buildCommand)("simulate")
|
|
|
317
316
|
description: "Take a screenshot at the end of simulation",
|
|
318
317
|
default: true,
|
|
319
318
|
},
|
|
320
|
-
screenshotSelector: {
|
|
321
|
-
string: true,
|
|
322
|
-
description: "Query selector to screenshot a specific DOM element instead of the whole page",
|
|
323
|
-
},
|
|
324
319
|
debugger: {
|
|
325
320
|
boolean: true,
|
|
326
321
|
description: "Opens a step through debugger to advance through the replay event by event",
|
|
@@ -97,16 +97,16 @@ export declare const runAllTestsCommand: import("yargs").CommandModule<unknown,
|
|
|
97
97
|
readonly description: "Number of tasks to run in parallel (defaults to two per CPU)";
|
|
98
98
|
readonly coerce: (value: number | null | undefined) => number | null | undefined;
|
|
99
99
|
};
|
|
100
|
-
readonly deflake: {
|
|
101
|
-
readonly boolean: true;
|
|
102
|
-
readonly description: "Attempt to deflake failing tests";
|
|
103
|
-
readonly default: false;
|
|
104
|
-
};
|
|
105
100
|
readonly maxRetriesOnFailure: {
|
|
106
101
|
readonly number: true;
|
|
107
102
|
readonly description: "If set to a value greater than 0 then will re-run any replays that give a screenshot diff and mark them as a flake if the screenshot generated on one of the retryed replays differs from that in the first replay.";
|
|
108
103
|
readonly default: 0;
|
|
109
104
|
};
|
|
105
|
+
readonly rerunTestsNTimes: {
|
|
106
|
+
readonly number: true;
|
|
107
|
+
readonly description: "If set to a value greater than 0 then will re-run all replays the specified number of times and mark them as a flake if the screenshot generated on one of the retryed replays differs from that in the first replay.";
|
|
108
|
+
readonly default: 0;
|
|
109
|
+
};
|
|
110
110
|
readonly useCache: {
|
|
111
111
|
readonly boolean: true;
|
|
112
112
|
readonly description: "Use result cache";
|
|
@@ -7,7 +7,7 @@ const command_builder_1 = require("../../command-utils/command-builder");
|
|
|
7
7
|
const common_options_1 = require("../../command-utils/common-options");
|
|
8
8
|
const run_all_tests_1 = require("../../parallel-tests/run-all-tests");
|
|
9
9
|
const commit_sha_utils_1 = require("../../utils/commit-sha.utils");
|
|
10
|
-
const handler = async ({ apiToken, commitSha: commitSha_, baseCommitSha, appUrl, headless, devTools, bypassCSP, diffThreshold, diffPixelThreshold, shiftTime, networkStubbing, githubSummary, parallelize, parallelTasks: parrelelTasks_,
|
|
10
|
+
const handler = async ({ apiToken, commitSha: commitSha_, baseCommitSha, appUrl, headless, devTools, bypassCSP, diffThreshold, diffPixelThreshold, shiftTime, networkStubbing, githubSummary, parallelize, parallelTasks: parrelelTasks_, maxRetriesOnFailure, rerunTestsNTimes, useCache, testsFile, disableRemoteFonts, noSandbox, skipPauses, moveBeforeClick, maxDurationMs, maxEventCount, storyboard, essentialFeaturesOnly, baseTestRunId, }) => {
|
|
11
11
|
const executionOptions = {
|
|
12
12
|
headless,
|
|
13
13
|
devTools,
|
|
@@ -27,7 +27,6 @@ const handler = async ({ apiToken, commitSha: commitSha_, baseCommitSha, appUrl,
|
|
|
27
27
|
: { enabled: false };
|
|
28
28
|
const screenshottingOptions = {
|
|
29
29
|
enabled: true,
|
|
30
|
-
screenshotSelector: null,
|
|
31
30
|
diffOptions: { diffPixelThreshold, diffThreshold },
|
|
32
31
|
storyboardOptions,
|
|
33
32
|
};
|
|
@@ -47,8 +46,8 @@ const handler = async ({ apiToken, commitSha: commitSha_, baseCommitSha, appUrl,
|
|
|
47
46
|
baseTestRunId: baseTestRunId !== null && baseTestRunId !== void 0 ? baseTestRunId : null,
|
|
48
47
|
appUrl: appUrl !== null && appUrl !== void 0 ? appUrl : null,
|
|
49
48
|
parallelTasks: parrelelTasks !== null && parrelelTasks !== void 0 ? parrelelTasks : null,
|
|
50
|
-
deflake,
|
|
51
49
|
maxRetriesOnFailure,
|
|
50
|
+
rerunTestsNTimes,
|
|
52
51
|
cachedTestRunResults,
|
|
53
52
|
githubSummary,
|
|
54
53
|
});
|
|
@@ -89,16 +88,16 @@ exports.runAllTestsCommand = (0, command_builder_1.buildCommand)("run-all-tests"
|
|
|
89
88
|
return value;
|
|
90
89
|
},
|
|
91
90
|
},
|
|
92
|
-
deflake: {
|
|
93
|
-
boolean: true,
|
|
94
|
-
description: "Attempt to deflake failing tests",
|
|
95
|
-
default: false,
|
|
96
|
-
},
|
|
97
91
|
maxRetriesOnFailure: {
|
|
98
92
|
number: true,
|
|
99
93
|
description: "If set to a value greater than 0 then will re-run any replays that give a screenshot diff and mark them as a flake if the screenshot generated on one of the retryed replays differs from that in the first replay.",
|
|
100
94
|
default: 0,
|
|
101
95
|
},
|
|
96
|
+
rerunTestsNTimes: {
|
|
97
|
+
number: true,
|
|
98
|
+
description: "If set to a value greater than 0 then will re-run all replays the specified number of times and mark them as a flake if the screenshot generated on one of the retryed replays differs from that in the first replay.",
|
|
99
|
+
default: 0,
|
|
100
|
+
},
|
|
102
101
|
useCache: {
|
|
103
102
|
boolean: true,
|
|
104
103
|
description: "Use result cache",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const get_screenshot_identifier_1 = require("../get-screenshot-identifier");
|
|
4
|
+
describe("get-screenshot-identifier", () => {
|
|
5
|
+
it("can parse identifiers without version numbers", () => {
|
|
6
|
+
expect((0, get_screenshot_identifier_1.getScreenshotIdentifier)("final-state.png")).toEqual({
|
|
7
|
+
type: "end-state",
|
|
8
|
+
});
|
|
9
|
+
expect((0, get_screenshot_identifier_1.getScreenshotIdentifier)("screenshot-after-event-7.png")).toEqual({
|
|
10
|
+
type: "after-event",
|
|
11
|
+
eventNumber: 7,
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
it("can parse identifiers with version numbers", () => {
|
|
15
|
+
expect((0, get_screenshot_identifier_1.getScreenshotIdentifier)("final-state-v2.png")).toEqual({
|
|
16
|
+
type: "end-state",
|
|
17
|
+
logicVersion: 2,
|
|
18
|
+
});
|
|
19
|
+
expect((0, get_screenshot_identifier_1.getScreenshotIdentifier)("screenshot-after-event-7-v2.png")).toEqual({
|
|
20
|
+
type: "after-event",
|
|
21
|
+
eventNumber: 7,
|
|
22
|
+
logicVersion: 2,
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -5,11 +5,15 @@ exports.getScreenshotFilename = void 0;
|
|
|
5
5
|
// in replay-node, and in https://github.com/alwaysmeticulous/meticulous-sdk/blob/395af4394dc51d9b51ba1136fc26b23fcbba5604/packages/replayer/src/screenshot.utils.ts#L42
|
|
6
6
|
const getScreenshotFilename = (identifier) => {
|
|
7
7
|
if (identifier.type === "end-state") {
|
|
8
|
-
return
|
|
8
|
+
return identifier.logicVersion == null
|
|
9
|
+
? "final-state.png"
|
|
10
|
+
: `final-state-v${identifier.logicVersion}.png`;
|
|
9
11
|
}
|
|
10
12
|
else if (identifier.type === "after-event") {
|
|
11
13
|
const eventIndexStr = identifier.eventNumber.toString().padStart(5, "0");
|
|
12
|
-
return
|
|
14
|
+
return identifier.logicVersion == null
|
|
15
|
+
? `screenshot-after-event-${eventIndexStr}.png`
|
|
16
|
+
: `screenshot-after-event-${eventIndexStr}-v${identifier.logicVersion}.png`;
|
|
13
17
|
}
|
|
14
18
|
else {
|
|
15
19
|
throw new Error("Unexpected screenshot identifier: " + JSON.stringify(identifier));
|
|
@@ -3,19 +3,34 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getScreenshotIdentifier = void 0;
|
|
4
4
|
const path_1 = require("path");
|
|
5
5
|
const getScreenshotIdentifier = (filename) => {
|
|
6
|
+
var _a;
|
|
6
7
|
const name = (0, path_1.basename)(filename);
|
|
7
8
|
if (name === "final-state.png") {
|
|
8
9
|
return {
|
|
9
10
|
type: "end-state",
|
|
10
11
|
};
|
|
11
12
|
}
|
|
13
|
+
if (name.startsWith("final-state-v")) {
|
|
14
|
+
const match = name.match(/^final-state-v(\d+)[.]png$/);
|
|
15
|
+
const logicVersionNumber = match ? parseInt(match[1], 10) : undefined;
|
|
16
|
+
if (match && logicVersionNumber != null && !isNaN(logicVersionNumber)) {
|
|
17
|
+
return {
|
|
18
|
+
type: "end-state",
|
|
19
|
+
logicVersion: logicVersionNumber,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
12
23
|
if (name.startsWith("screenshot-after-event")) {
|
|
13
|
-
const match = name.match(/^(?:.*)-(\d+)[.]png$/);
|
|
24
|
+
const match = (_a = name.match(/^(?:.*)-(\d+)-v(\d+)?[.]png$/)) !== null && _a !== void 0 ? _a : name.match(/^(?:.*)-(\d+)[.]png$/);
|
|
14
25
|
const eventNumber = match ? parseInt(match[1], 10) : undefined;
|
|
26
|
+
const logicVersionNumber = match ? parseInt(match[2], 10) : undefined;
|
|
15
27
|
if (match && eventNumber != null && !isNaN(eventNumber)) {
|
|
16
28
|
return {
|
|
17
29
|
type: "after-event",
|
|
18
30
|
eventNumber,
|
|
31
|
+
...(logicVersionNumber != null && !isNaN(logicVersionNumber)
|
|
32
|
+
? { logicVersion: logicVersionNumber }
|
|
33
|
+
: {}),
|
|
19
34
|
};
|
|
20
35
|
}
|
|
21
36
|
}
|
package/dist/config/config.js
CHANGED
|
@@ -22,10 +22,9 @@ const getConfigFilePath = async () => {
|
|
|
22
22
|
return configFilePath;
|
|
23
23
|
};
|
|
24
24
|
const validateReplayOptions = (prevOptions) => {
|
|
25
|
-
const { appUrl,
|
|
25
|
+
const { appUrl, diffThreshold, diffPixelThreshold, moveBeforeClick, simulationIdForAssets, } = prevOptions;
|
|
26
26
|
return {
|
|
27
27
|
...(appUrl != null ? { appUrl } : {}),
|
|
28
|
-
...(screenshotSelector != null ? { screenshotSelector } : {}),
|
|
29
28
|
...(diffThreshold != null ? { diffThreshold } : {}),
|
|
30
29
|
...(diffPixelThreshold != null ? { diffPixelThreshold } : {}),
|
|
31
30
|
...(moveBeforeClick != null ? { moveBeforeClick } : {}),
|
|
@@ -12,14 +12,16 @@ describe("mergeResults", () => {
|
|
|
12
12
|
});
|
|
13
13
|
expect(mergedResult).toEqual((0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.diff)(0), (0, mock_test_results_1.noDiff)(1)]));
|
|
14
14
|
});
|
|
15
|
-
it("
|
|
15
|
+
it("doesn't ignore diffs to screenshots which originally passed", () => {
|
|
16
16
|
const currentResult = (0, mock_test_results_1.testResult)("pass", [(0, mock_test_results_1.noDiff)(0), (0, mock_test_results_1.noDiff)(1)]);
|
|
17
17
|
const comparisonToHeadReplay = (0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.noDiff)(0), (0, mock_test_results_1.diff)(1)]);
|
|
18
18
|
const mergedResult = (0, merge_test_results_1.mergeResults)({
|
|
19
19
|
currentResult,
|
|
20
20
|
comparisonToHeadReplay,
|
|
21
21
|
});
|
|
22
|
-
expect(mergedResult).toEqual(
|
|
22
|
+
expect(mergedResult).toEqual(
|
|
23
|
+
// All screenshots are either "no-diff" or "flake" thus the overall result is "flake"
|
|
24
|
+
(0, mock_test_results_1.testResult)("flake", [(0, mock_test_results_1.noDiff)(0), (0, mock_test_results_1.flake)(1, (0, mock_test_results_1.noDiff)(), [(0, mock_test_results_1.diff)()])]));
|
|
23
25
|
});
|
|
24
26
|
it("marks screenshots as flakes if the screenshot comparison originally failed, but the second retry gives a different screenshot again", () => {
|
|
25
27
|
const currentResult = (0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.noDiff)(0), (0, mock_test_results_1.diff)(1), (0, mock_test_results_1.diff)(2)]);
|
|
@@ -17,6 +17,7 @@ describe("runAllTestsInParallel", () => {
|
|
|
17
17
|
testsToRun: [testCase(0), testCase(1), testCase(2), testCase(3)],
|
|
18
18
|
parallelTasks: 2,
|
|
19
19
|
maxRetriesOnFailure: 0,
|
|
20
|
+
rerunTestsNTimes: 0,
|
|
20
21
|
executeTest: async (testCase) => {
|
|
21
22
|
var _a;
|
|
22
23
|
testsStarted++;
|
|
@@ -77,6 +78,7 @@ describe("runAllTestsInParallel", () => {
|
|
|
77
78
|
testsToRun: [testCase(0), testCase(1), testCase(2), testCase(3)],
|
|
78
79
|
parallelTasks: 2,
|
|
79
80
|
maxRetriesOnFailure: 0,
|
|
81
|
+
rerunTestsNTimes: 0,
|
|
80
82
|
executeTest: async (testCase) => {
|
|
81
83
|
var _a;
|
|
82
84
|
await testRunPromises[parseInt((_a = testCase.title) !== null && _a !== void 0 ? _a : "")].promise;
|
|
@@ -109,6 +111,7 @@ describe("runAllTestsInParallel", () => {
|
|
|
109
111
|
testsToRun: [testCase(0)],
|
|
110
112
|
parallelTasks: 2,
|
|
111
113
|
maxRetriesOnFailure: 0,
|
|
114
|
+
rerunTestsNTimes: 0,
|
|
112
115
|
executeTest: async (testCase) => {
|
|
113
116
|
return (0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.diff)(0)], testCase);
|
|
114
117
|
},
|
|
@@ -121,6 +124,7 @@ describe("runAllTestsInParallel", () => {
|
|
|
121
124
|
testsToRun: [testCase(0)],
|
|
122
125
|
parallelTasks: 2,
|
|
123
126
|
maxRetriesOnFailure: 10,
|
|
127
|
+
rerunTestsNTimes: 0,
|
|
124
128
|
executeTest: async (testCase, isRetry) => {
|
|
125
129
|
if (!isRetry) {
|
|
126
130
|
return (0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.diff)(0), (0, mock_test_results_1.diff)(1)], testCase);
|
|
@@ -145,6 +149,7 @@ describe("runAllTestsInParallel", () => {
|
|
|
145
149
|
testsToRun: [testCase(0)],
|
|
146
150
|
parallelTasks: 2,
|
|
147
151
|
maxRetriesOnFailure: 3,
|
|
152
|
+
rerunTestsNTimes: 0,
|
|
148
153
|
executeTest: async (testCase, isRetry) => {
|
|
149
154
|
if (!isRetry) {
|
|
150
155
|
return (0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.diff)(0), (0, mock_test_results_1.diff)(1)], testCase);
|
|
@@ -156,6 +161,31 @@ describe("runAllTestsInParallel", () => {
|
|
|
156
161
|
(0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.diff)(0), (0, mock_test_results_1.flake)(1, (0, mock_test_results_1.diff)(), [(0, mock_test_results_1.diff)(), (0, mock_test_results_1.diff)(), (0, mock_test_results_1.diff)()])], testCase(0)),
|
|
157
162
|
]);
|
|
158
163
|
});
|
|
164
|
+
it("retries N times if rerunTestsNTimes > 0", async () => {
|
|
165
|
+
let rerunNum = 0;
|
|
166
|
+
const results = await (0, parallel_tests_handler_1.runAllTestsInParallel)({
|
|
167
|
+
testsToRun: [testCase(0)],
|
|
168
|
+
parallelTasks: 2,
|
|
169
|
+
maxRetriesOnFailure: 0,
|
|
170
|
+
rerunTestsNTimes: 3,
|
|
171
|
+
executeTest: async (testCase, isRetry) => {
|
|
172
|
+
if (!isRetry) {
|
|
173
|
+
return (0, mock_test_results_1.testResult)("pass", [(0, mock_test_results_1.diff)(0), (0, mock_test_results_1.noDiff)(1)], testCase);
|
|
174
|
+
}
|
|
175
|
+
if (rerunNum === 2) {
|
|
176
|
+
return (0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.missingHead)(0), (0, mock_test_results_1.diff)(1)], testCase);
|
|
177
|
+
}
|
|
178
|
+
rerunNum++;
|
|
179
|
+
return (0, mock_test_results_1.testResult)("pass", [(0, mock_test_results_1.noDiff)(0), (0, mock_test_results_1.noDiff)(1)], testCase);
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
expect(rerunNum).toEqual(2);
|
|
183
|
+
expect(results).toEqual([
|
|
184
|
+
(0, mock_test_results_1.testResult)(
|
|
185
|
+
// Flake outcome is returned iff all screenshots are flaky.
|
|
186
|
+
"flake", [(0, mock_test_results_1.flake)(0, (0, mock_test_results_1.diff)(), [(0, mock_test_results_1.missingHead)()]), (0, mock_test_results_1.flake)(1, (0, mock_test_results_1.noDiff)(), [(0, mock_test_results_1.diff)()])], testCase(0)),
|
|
187
|
+
]);
|
|
188
|
+
});
|
|
159
189
|
});
|
|
160
190
|
const testCase = (num) => ({
|
|
161
191
|
sessionId: "mock-session-id",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ScreenshotDiffResult } from "@alwaysmeticulous/api";
|
|
2
|
+
import { DetailedTestCaseResult, TestCaseResult } from "../config/config.types";
|
|
2
3
|
export interface ResultsToMerge {
|
|
3
4
|
currentResult: DetailedTestCaseResult;
|
|
4
5
|
/**
|
|
@@ -8,3 +9,4 @@ export interface ResultsToMerge {
|
|
|
8
9
|
comparisonToHeadReplay: DetailedTestCaseResult;
|
|
9
10
|
}
|
|
10
11
|
export declare const mergeResults: ({ currentResult, comparisonToHeadReplay, }: ResultsToMerge) => DetailedTestCaseResult;
|
|
12
|
+
export declare const testRunOutcomeFromDiffResults: (currentResult: TestCaseResult["result"], newScreenshotDiffResults: ScreenshotDiffResult[]) => TestCaseResult["result"];
|
|
@@ -3,9 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.mergeResults = void 0;
|
|
7
|
-
const utils_1 = require("@sentry/utils");
|
|
6
|
+
exports.testRunOutcomeFromDiffResults = exports.mergeResults = void 0;
|
|
8
7
|
const fast_json_stable_stringify_1 = __importDefault(require("fast-json-stable-stringify"));
|
|
8
|
+
const has_notable_differences_1 = require("../commands/screenshot-diff/utils/has-notable-differences");
|
|
9
9
|
const screenshot_diff_results_utils_1 = require("./screenshot-diff-results.utils");
|
|
10
10
|
const mergeResults = ({ currentResult, comparisonToHeadReplay, }) => {
|
|
11
11
|
// If any of the screenshots diffs in comparisonToHeadReplay show a diff against one
|
|
@@ -20,9 +20,6 @@ const mergeResults = ({ currentResult, comparisonToHeadReplay, }) => {
|
|
|
20
20
|
retryDiffById.set(hash, result);
|
|
21
21
|
});
|
|
22
22
|
const newScreenshotDiffResults = (0, screenshot_diff_results_utils_1.flattenScreenshotDiffResults)(currentResult).map((currentDiff) => {
|
|
23
|
-
if (currentDiff.outcome === "no-diff") {
|
|
24
|
-
return currentDiff;
|
|
25
|
-
}
|
|
26
23
|
const hash = hashScreenshotIdentifier(currentDiff.identifier);
|
|
27
24
|
const diffWhenRetrying = retryDiffById.get(hash);
|
|
28
25
|
// diffWhenRetrying is null in the case that there is a base screenshot for the base replay,
|
|
@@ -75,12 +72,9 @@ const mergeResults = ({ currentResult, comparisonToHeadReplay, }) => {
|
|
|
75
72
|
};
|
|
76
73
|
}
|
|
77
74
|
});
|
|
78
|
-
const noLongerHasFailures = newScreenshotDiffResults.every(({ outcome }) => outcome === "flake" || outcome === "no-diff");
|
|
79
75
|
return {
|
|
80
76
|
...currentResult,
|
|
81
|
-
result: currentResult.result
|
|
82
|
-
? "flake"
|
|
83
|
-
: currentResult.result,
|
|
77
|
+
result: (0, exports.testRunOutcomeFromDiffResults)(currentResult.result, newScreenshotDiffResults),
|
|
84
78
|
screenshotDiffResultsByBaseReplayId: (0, screenshot_diff_results_utils_1.groupScreenshotDiffResults)(newScreenshotDiffResults),
|
|
85
79
|
};
|
|
86
80
|
};
|
|
@@ -91,20 +85,22 @@ identifier: _identifier,
|
|
|
91
85
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
92
86
|
baseReplayId: _baseReplayId, ...rest }) => rest;
|
|
93
87
|
const hashScreenshotIdentifier = (identifier) => {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
88
|
+
return (0, fast_json_stable_stringify_1.default)(identifier);
|
|
89
|
+
};
|
|
90
|
+
const testRunOutcomeFromDiffResults = (currentResult, newScreenshotDiffResults) => {
|
|
91
|
+
// If a test run is already flakey, we don't want to overwrite that with a 'fail' result.
|
|
92
|
+
if (currentResult === "flake") {
|
|
93
|
+
return "flake";
|
|
99
94
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
// and we're on an old version of the CLI. Our best bet is to stringify it
|
|
104
|
-
// and use that as a hash.
|
|
105
|
-
return (0, fast_json_stable_stringify_1.default)(identifier);
|
|
95
|
+
const hasOnlyFlakes = newScreenshotDiffResults.every(({ outcome }) => outcome === "flake" || outcome === "no-diff");
|
|
96
|
+
if (hasOnlyFlakes) {
|
|
97
|
+
return "flake";
|
|
106
98
|
}
|
|
99
|
+
// If we've had a test run has already failed, we don't want to overwrite that with a 'pass' result.
|
|
100
|
+
// Otherwise, if there are any notable differences, we want to fail the test run.
|
|
101
|
+
return currentResult === "fail" ||
|
|
102
|
+
(0, has_notable_differences_1.hasNotableDifferences)(newScreenshotDiffResults)
|
|
103
|
+
? "fail"
|
|
104
|
+
: "pass";
|
|
107
105
|
};
|
|
108
|
-
|
|
109
|
-
utils_1.logger.error(`Unknown type of screenshot identifier: ${JSON.stringify(identifier)}`);
|
|
110
|
-
};
|
|
106
|
+
exports.testRunOutcomeFromDiffResults = testRunOutcomeFromDiffResults;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import log from "loglevel";
|
|
2
2
|
import { DetailedTestCaseResult } from "../config/config.types";
|
|
3
|
-
import {
|
|
3
|
+
import { ParallelTestsReplayOptions } from "./parallel-replay.types";
|
|
4
4
|
export interface InitMessage {
|
|
5
5
|
kind: "init";
|
|
6
6
|
data: {
|
|
7
7
|
logLevel: log.LogLevel[keyof log.LogLevel];
|
|
8
8
|
dataDir: string;
|
|
9
|
-
replayOptions:
|
|
9
|
+
replayOptions: ParallelTestsReplayOptions;
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
12
|
export interface ResultMessage {
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { DetailedTestCaseResult } from "../config/config.types";
|
|
2
|
+
import { ParallelTestsReplayOptions } from "./parallel-replay.types";
|
|
3
|
+
export declare const handleReplay: ({ testCase, replayTarget, executionOptions, screenshottingOptions, apiToken, commitSha, generatedBy, testRunId, replayEventsDependencies, suppressScreenshotDiffLogging, baseTestRunId, }: ParallelTestsReplayOptions) => Promise<DetailedTestCaseResult>;
|
package/dist/{deflake-tests/deflake-tests.handler.js → parallel-tests/parallel-replay.handler.js}
RENAMED
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
7
|
-
const common_1 = require("@alwaysmeticulous/common");
|
|
8
|
-
const loglevel_1 = __importDefault(require("loglevel"));
|
|
3
|
+
exports.handleReplay = void 0;
|
|
9
4
|
const replay_command_1 = require("../commands/replay/replay.command");
|
|
10
5
|
const has_notable_differences_1 = require("../commands/screenshot-diff/utils/has-notable-differences");
|
|
11
6
|
const handleReplay = async ({ testCase, replayTarget, executionOptions, screenshottingOptions, apiToken, commitSha, generatedBy, testRunId, replayEventsDependencies, suppressScreenshotDiffLogging, baseTestRunId, }) => {
|
|
@@ -35,21 +30,7 @@ const handleReplay = async ({ testCase, replayTarget, executionOptions, screensh
|
|
|
35
30
|
screenshotDiffResultsByBaseReplayId,
|
|
36
31
|
};
|
|
37
32
|
};
|
|
38
|
-
|
|
39
|
-
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
40
|
-
const firstResult = await handleReplay(otherOptions);
|
|
41
|
-
if (firstResult.result === "pass" || !deflake) {
|
|
42
|
-
return firstResult;
|
|
43
|
-
}
|
|
44
|
-
const secondResult = await handleReplay(otherOptions);
|
|
45
|
-
if (secondResult.result === "fail") {
|
|
46
|
-
return secondResult;
|
|
47
|
-
}
|
|
48
|
-
const thirdResult = await handleReplay(otherOptions);
|
|
49
|
-
logger.info(`FLAKE: ${thirdResult.title} => ${thirdResult.result}`);
|
|
50
|
-
return thirdResult;
|
|
51
|
-
};
|
|
52
|
-
exports.deflakeReplayCommandHandler = deflakeReplayCommandHandler;
|
|
33
|
+
exports.handleReplay = handleReplay;
|
|
53
34
|
const applyTestCaseExecutionOptionOverrides = (executionOptionsFromCliFlags, overridesFromTestCase) => {
|
|
54
35
|
var _a;
|
|
55
36
|
// Options specified in the test case override those passed as CLI flags
|
|
@@ -60,7 +41,7 @@ const applyTestCaseExecutionOptionOverrides = (executionOptionsFromCliFlags, ove
|
|
|
60
41
|
};
|
|
61
42
|
};
|
|
62
43
|
const applyTestCaseScreenshottingOptionsOverrides = (screenshottingOptionsFromCliFlags, overridesFromTestCase) => {
|
|
63
|
-
var _a, _b
|
|
44
|
+
var _a, _b;
|
|
64
45
|
// Options specified in the test case override those passed as CLI flags
|
|
65
46
|
// (CLI flags set the defaults)
|
|
66
47
|
const diffOptions = {
|
|
@@ -69,7 +50,6 @@ const applyTestCaseScreenshottingOptionsOverrides = (screenshottingOptionsFromCl
|
|
|
69
50
|
};
|
|
70
51
|
return {
|
|
71
52
|
enabled: true,
|
|
72
|
-
screenshotSelector: (_c = overridesFromTestCase.screenshotSelector) !== null && _c !== void 0 ? _c : screenshottingOptionsFromCliFlags.screenshotSelector,
|
|
73
53
|
diffOptions,
|
|
74
54
|
storyboardOptions: screenshottingOptionsFromCliFlags.storyboardOptions,
|
|
75
55
|
};
|
package/dist/{deflake-tests/deflake-tests.handler.d.ts → parallel-tests/parallel-replay.types.d.ts}
RENAMED
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
import { TestCase } from "@alwaysmeticulous/api";
|
|
2
2
|
import { GeneratedBy, ReplayEventsDependencies, ReplayExecutionOptions, ReplayTarget } from "@alwaysmeticulous/common";
|
|
3
3
|
import { ScreenshotAssertionsEnabledOptions } from "../command-utils/common-types";
|
|
4
|
-
|
|
5
|
-
export interface DeflakeReplayCommandHandlerOptions extends HandleReplayOptions {
|
|
6
|
-
deflake: boolean;
|
|
7
|
-
}
|
|
8
|
-
interface HandleReplayOptions {
|
|
4
|
+
export interface ParallelTestsReplayOptions {
|
|
9
5
|
replayTarget: ReplayTarget;
|
|
10
6
|
executionOptions: ReplayExecutionOptions;
|
|
11
7
|
screenshottingOptions: ScreenshotAssertionsEnabledOptions;
|
|
@@ -18,5 +14,3 @@ interface HandleReplayOptions {
|
|
|
18
14
|
replayEventsDependencies: ReplayEventsDependencies;
|
|
19
15
|
suppressScreenshotDiffLogging: boolean;
|
|
20
16
|
}
|
|
21
|
-
export declare const deflakeReplayCommandHandler: ({ deflake, ...otherOptions }: DeflakeReplayCommandHandlerOptions) => Promise<DetailedTestCaseResult>;
|
|
22
|
-
export {};
|
|
@@ -5,6 +5,7 @@ export interface RunAllTestsInParallelOptions {
|
|
|
5
5
|
testsToRun: TestCase[];
|
|
6
6
|
parallelTasks: number | null;
|
|
7
7
|
maxRetriesOnFailure: number;
|
|
8
|
+
rerunTestsNTimes: number;
|
|
8
9
|
executeTest: (testCase: TestCase, isRetry: boolean) => Promise<DetailedTestCaseResult>;
|
|
9
10
|
onTestFinished?: (progress: TestRunProgress, resultsSoFar: DetailedTestCaseResult[]) => Promise<void>;
|
|
10
11
|
onTestFailedToRun?: (progress: TestRunProgress) => Promise<void>;
|
|
@@ -10,7 +10,10 @@ const loglevel_1 = __importDefault(require("loglevel"));
|
|
|
10
10
|
const merge_test_results_1 = require("./merge-test-results");
|
|
11
11
|
const screenshot_diff_results_utils_1 = require("./screenshot-diff-results.utils");
|
|
12
12
|
/** Handler for running Meticulous tests in parallel using child processes */
|
|
13
|
-
const runAllTestsInParallel = async ({ testsToRun, parallelTasks, maxRetriesOnFailure, executeTest, onTestFinished, onTestFailedToRun, }) => {
|
|
13
|
+
const runAllTestsInParallel = async ({ testsToRun, parallelTasks, maxRetriesOnFailure, rerunTestsNTimes, executeTest, onTestFinished, onTestFailedToRun, }) => {
|
|
14
|
+
if (maxRetriesOnFailure && rerunTestsNTimes) {
|
|
15
|
+
throw new Error("maxRetriesOnFailure and rerunTestsNTimes are mutually exclusive.");
|
|
16
|
+
}
|
|
14
17
|
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
15
18
|
let nextId = 0;
|
|
16
19
|
let queue = testsToRun.map((test) => ({
|
|
@@ -47,8 +50,16 @@ const runAllTestsInParallel = async ({ testsToRun, parallelTasks, maxRetriesOnFa
|
|
|
47
50
|
.then(async (result) => {
|
|
48
51
|
var _a;
|
|
49
52
|
const resultsForTestCase = resultsByTestId.get(id);
|
|
53
|
+
// Re-run the test if needed, comparing to the base replay.
|
|
54
|
+
if (!isRetry) {
|
|
55
|
+
queue.push(...Array.from(new Array(rerunTestsNTimes)).map(() => ({
|
|
56
|
+
...testCase,
|
|
57
|
+
id,
|
|
58
|
+
baseReplayId: result === null || result === void 0 ? void 0 : result.headReplayId,
|
|
59
|
+
})));
|
|
60
|
+
}
|
|
50
61
|
if (resultsForTestCase != null && result != null) {
|
|
51
|
-
logRetrySummary(testName({ id, ...testCase }), result);
|
|
62
|
+
logRetrySummary(testName({ id, ...testCase }), result, resultsForTestCase.currentResult);
|
|
52
63
|
}
|
|
53
64
|
if (resultsForTestCase != null &&
|
|
54
65
|
resultsForTestCase.currentResult.result === "flake") {
|
|
@@ -81,8 +92,15 @@ const runAllTestsInParallel = async ({ testsToRun, parallelTasks, maxRetriesOnFa
|
|
|
81
92
|
// Our work is done for this test case if the first result was a pass,
|
|
82
93
|
// we've performed all the retries, or one of the retries already proved
|
|
83
94
|
// the result as flakey
|
|
84
|
-
|
|
85
|
-
|
|
95
|
+
let isFinalResult;
|
|
96
|
+
if (rerunTestsNTimes > 0) {
|
|
97
|
+
isFinalResult = numberOfRetriesExecuted >= rerunTestsNTimes;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
isFinalResult =
|
|
101
|
+
mergedResult.result !== "fail" ||
|
|
102
|
+
numberOfRetriesExecuted >= maxRetriesOnFailure;
|
|
103
|
+
}
|
|
86
104
|
if (isFinalResult) {
|
|
87
105
|
// Cancel any replays that are still scheduled
|
|
88
106
|
queue = queue.filter((otherReplay) => otherReplay.id !== id);
|
|
@@ -119,7 +137,12 @@ const runAllTestsInParallel = async ({ testsToRun, parallelTasks, maxRetriesOnFa
|
|
|
119
137
|
}
|
|
120
138
|
++inProgress;
|
|
121
139
|
if (resultsByTestId.has(testCase.id)) {
|
|
122
|
-
|
|
140
|
+
if (maxRetriesOnFailure > 0) {
|
|
141
|
+
logger.info(`Test ${testName(testCase)} failed. Retrying to check for flakes...`);
|
|
142
|
+
}
|
|
143
|
+
else if (rerunTestsNTimes > 0) {
|
|
144
|
+
logger.info(`Re-running ${testName(testCase)} to check for flakes...`);
|
|
145
|
+
}
|
|
123
146
|
}
|
|
124
147
|
startTask(testCase);
|
|
125
148
|
process.nextTick(checkNextTask);
|
|
@@ -129,10 +152,10 @@ const runAllTestsInParallel = async ({ testsToRun, parallelTasks, maxRetriesOnFa
|
|
|
129
152
|
return finalResults;
|
|
130
153
|
};
|
|
131
154
|
exports.runAllTestsInParallel = runAllTestsInParallel;
|
|
132
|
-
const logRetrySummary = (nameOfTest, retryResult) => {
|
|
155
|
+
const logRetrySummary = (nameOfTest, retryResult, resultSoFar) => {
|
|
133
156
|
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
134
157
|
if (retryResult.result === "pass") {
|
|
135
|
-
logger.info(`Retried taking screenshots for failed test ${nameOfTest}, but got the same results`);
|
|
158
|
+
logger.info(`Retried taking screenshots for${resultSoFar.result == "fail" ? " failed" : ""} test ${nameOfTest}, but got the same results`);
|
|
136
159
|
}
|
|
137
160
|
else {
|
|
138
161
|
const numDifferingScreenshots = (0, screenshot_diff_results_utils_1.flattenScreenshotDiffResults)(retryResult).filter((result) => result.outcome !== "no-diff").length;
|
|
@@ -25,13 +25,22 @@ export interface Options {
|
|
|
25
25
|
* Set to 1 to disable parralelism.
|
|
26
26
|
*/
|
|
27
27
|
parallelTasks: number | null;
|
|
28
|
-
deflake: boolean;
|
|
29
28
|
/**
|
|
30
29
|
* If set to a value greater than 1 then will re-run any replays that give a screenshot diff
|
|
31
30
|
* and mark them as a flake if the screenshot generated on one of the retryed replays differs from that
|
|
32
31
|
* in the first replay.
|
|
33
32
|
*/
|
|
34
33
|
maxRetriesOnFailure: number;
|
|
34
|
+
/**
|
|
35
|
+
* If set to a value greater than 0 then will re-run all replays the specified number of times
|
|
36
|
+
* and mark them as a flake if the screenshot generated on one of the retryed replays differs from that
|
|
37
|
+
* in the first replay.
|
|
38
|
+
*
|
|
39
|
+
* This is useful for checking flake rates.
|
|
40
|
+
*
|
|
41
|
+
* This option is mutually exclusive with maxRetriesOnFailure.
|
|
42
|
+
*/
|
|
43
|
+
rerunTestsNTimes: number;
|
|
35
44
|
githubSummary: boolean;
|
|
36
45
|
/**
|
|
37
46
|
* If provided it will incorportate the cachedTestRunResults in any calls to store
|
|
@@ -61,4 +70,4 @@ export interface RunAllTestsResult {
|
|
|
61
70
|
* Runs all the test cases in the provided file.
|
|
62
71
|
* @returns The results of the tests that were executed (note that this does not include results from any cachedTestRunResults passed in)
|
|
63
72
|
*/
|
|
64
|
-
export declare const runAllTests: ({ testsFile, apiToken, commitSha, baseCommitSha, appUrl, executionOptions, screenshottingOptions, parallelTasks,
|
|
73
|
+
export declare const runAllTests: ({ testsFile, apiToken, commitSha, baseCommitSha, appUrl, executionOptions, screenshottingOptions, parallelTasks, maxRetriesOnFailure, rerunTestsNTimes, cachedTestRunResults: cachedTestRunResults_, githubSummary, environment, baseTestRunId, onTestRunCreated, onTestFinished: onTestFinished_, }: Options) => Promise<RunAllTestsResult>;
|
|
@@ -12,21 +12,18 @@ const replay_diff_api_1 = require("../api/replay-diff.api");
|
|
|
12
12
|
const test_run_api_1 = require("../api/test-run.api");
|
|
13
13
|
const config_1 = require("../config/config");
|
|
14
14
|
const replay_assets_1 = require("../local-data/replay-assets");
|
|
15
|
-
const parallel_tests_handler_1 = require("../parallel-tests/parallel-tests.handler");
|
|
16
15
|
const config_utils_1 = require("../utils/config.utils");
|
|
17
16
|
const github_summary_utils_1 = require("../utils/github-summary.utils");
|
|
18
17
|
const run_all_tests_utils_1 = require("../utils/run-all-tests.utils");
|
|
19
18
|
const test_run_environment_utils_1 = require("../utils/test-run-environment.utils");
|
|
20
19
|
const version_utils_1 = require("../utils/version.utils");
|
|
21
20
|
const execute_test_in_child_process_1 = require("./execute-test-in-child-process");
|
|
21
|
+
const parallel_tests_handler_1 = require("./parallel-tests.handler");
|
|
22
22
|
/**
|
|
23
23
|
* Runs all the test cases in the provided file.
|
|
24
24
|
* @returns The results of the tests that were executed (note that this does not include results from any cachedTestRunResults passed in)
|
|
25
25
|
*/
|
|
26
|
-
const runAllTests = async ({ testsFile, apiToken, commitSha, baseCommitSha, appUrl, executionOptions, screenshottingOptions, parallelTasks,
|
|
27
|
-
if (deflake && maxRetriesOnFailure > 1) {
|
|
28
|
-
throw new Error("Arguments deflake and maxRetriesOnFailure are mutually exclusive");
|
|
29
|
-
}
|
|
26
|
+
const runAllTests = async ({ testsFile, apiToken, commitSha, baseCommitSha, appUrl, executionOptions, screenshottingOptions, parallelTasks, maxRetriesOnFailure, rerunTestsNTimes, cachedTestRunResults: cachedTestRunResults_, githubSummary, environment, baseTestRunId, onTestRunCreated, onTestFinished: onTestFinished_, }) => {
|
|
30
27
|
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
31
28
|
const client = (0, client_1.createClient)({ apiToken });
|
|
32
29
|
const project = await (0, project_api_1.getProject)(client);
|
|
@@ -60,7 +57,6 @@ const runAllTests = async ({ testsFile, apiToken, commitSha, baseCommitSha, appU
|
|
|
60
57
|
baseCommitSha,
|
|
61
58
|
appUrl,
|
|
62
59
|
parallelTasks,
|
|
63
|
-
deflake,
|
|
64
60
|
githubSummary,
|
|
65
61
|
},
|
|
66
62
|
environment: (0, test_run_environment_utils_1.getEnvironment)(environment),
|
|
@@ -145,6 +141,7 @@ const runAllTests = async ({ testsFile, apiToken, commitSha, baseCommitSha, appU
|
|
|
145
141
|
testsToRun,
|
|
146
142
|
parallelTasks,
|
|
147
143
|
maxRetriesOnFailure,
|
|
144
|
+
rerunTestsNTimes,
|
|
148
145
|
executeTest: (testCase, isRetry) => {
|
|
149
146
|
var _a;
|
|
150
147
|
const initMessage = {
|
|
@@ -156,7 +153,6 @@ const runAllTests = async ({ testsFile, apiToken, commitSha, baseCommitSha, appU
|
|
|
156
153
|
apiToken,
|
|
157
154
|
commitSha,
|
|
158
155
|
testCase,
|
|
159
|
-
deflake,
|
|
160
156
|
replayTarget: (0, config_utils_1.getReplayTargetForTestCase)({
|
|
161
157
|
appUrl,
|
|
162
158
|
testCase,
|
|
@@ -30,9 +30,9 @@ const common_1 = require("@alwaysmeticulous/common");
|
|
|
30
30
|
const Sentry = __importStar(require("@sentry/node"));
|
|
31
31
|
const loglevel_1 = __importDefault(require("loglevel"));
|
|
32
32
|
const luxon_1 = require("luxon");
|
|
33
|
-
const deflake_tests_handler_1 = require("../deflake-tests/deflake-tests.handler");
|
|
34
33
|
const logger_utils_1 = require("../utils/logger.utils");
|
|
35
34
|
const sentry_utils_1 = require("../utils/sentry.utils");
|
|
35
|
+
const parallel_replay_handler_1 = require("./parallel-replay.handler");
|
|
36
36
|
const INIT_TIMEOUT = luxon_1.Duration.fromObject({ second: 1 });
|
|
37
37
|
const waitForInitMessage = () => {
|
|
38
38
|
return Promise.race([
|
|
@@ -67,7 +67,7 @@ const main = async () => {
|
|
|
67
67
|
const { logLevel, dataDir, replayOptions } = initMessage.data;
|
|
68
68
|
logger.setLevel(logLevel);
|
|
69
69
|
(0, common_1.setMeticulousLocalDataDir)(dataDir);
|
|
70
|
-
const result = await (0,
|
|
70
|
+
const result = await (0, parallel_replay_handler_1.handleReplay)(replayOptions);
|
|
71
71
|
const resultMessage = {
|
|
72
72
|
kind: "result",
|
|
73
73
|
data: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alwaysmeticulous/cli",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.40.0",
|
|
4
4
|
"description": "The Meticulous CLI",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"test": "jest"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@alwaysmeticulous/common": "^2.
|
|
29
|
+
"@alwaysmeticulous/common": "^2.40.0",
|
|
30
30
|
"@sentry/node": "^7.36.0",
|
|
31
31
|
"@sentry/tracing": "^7.36.0",
|
|
32
32
|
"adm-zip": "^0.5.9",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"yargs": "^17.5.1"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@alwaysmeticulous/api": "^2.
|
|
48
|
+
"@alwaysmeticulous/api": "^2.40.0",
|
|
49
49
|
"@types/express": "^4.17.14",
|
|
50
50
|
"@types/proper-lockfile": "^4.1.2"
|
|
51
51
|
},
|
|
@@ -95,5 +95,5 @@
|
|
|
95
95
|
"coverageDirectory": "../coverage",
|
|
96
96
|
"testEnvironment": "node"
|
|
97
97
|
},
|
|
98
|
-
"gitHead": "
|
|
98
|
+
"gitHead": "46c8a774b634b1e43ca8e792a6ed573adba296ef"
|
|
99
99
|
}
|