@alwaysmeticulous/cli 2.17.0 → 2.18.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/replay/replay.command.d.ts +8 -2
- package/dist/commands/replay/replay.command.js +60 -83
- package/dist/commands/replay/utils/compute-and-save-diff.d.ts +15 -0
- package/dist/commands/replay/utils/compute-and-save-diff.js +46 -0
- package/dist/commands/run-all-tests/run-all-tests.command.js +13 -5
- package/dist/commands/screenshot-diff/screenshot-diff.command.d.ts +11 -11
- package/dist/commands/screenshot-diff/screenshot-diff.command.js +20 -17
- package/dist/config/config.types.d.ts +4 -0
- package/dist/deflake-tests/deflake-tests.handler.d.ts +2 -2
- package/dist/deflake-tests/deflake-tests.handler.js +8 -20
- package/dist/parallel-tests/messages.types.d.ts +2 -2
- package/dist/parallel-tests/parallel-tests.handler.d.ts +4 -10
- package/dist/parallel-tests/parallel-tests.handler.js +6 -20
- package/dist/parallel-tests/run-all-tests.d.ts +14 -5
- package/dist/parallel-tests/run-all-tests.js +57 -46
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/run-all-tests.utils.d.ts +4 -5
- package/dist/utils/run-all-tests.utils.js +4 -7
- package/package.json +2 -2
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
/// <reference types="yargs" />
|
|
2
|
+
import { ScreenshotDiffResult } from "@alwaysmeticulous/api";
|
|
2
3
|
import { GeneratedBy, Replay, ReplayEventsDependencies, ReplayExecutionOptions, ReplayTarget } from "@alwaysmeticulous/common";
|
|
3
4
|
import { ScreenshotAssertionsOptions, ScreenshotDiffOptions } from "../../command-utils/common-types";
|
|
5
|
+
import { ScreenshotDiffsSummary } from "../screenshot-diff/screenshot-diff.command";
|
|
4
6
|
export interface ReplayOptions extends AdditionalReplayOptions {
|
|
5
7
|
replayTarget: ReplayTarget;
|
|
6
8
|
executionOptions: ReplayExecutionOptions;
|
|
7
9
|
screenshottingOptions: ScreenshotAssertionsOptions;
|
|
8
|
-
exitOnMismatch: boolean;
|
|
9
10
|
generatedBy: GeneratedBy;
|
|
10
11
|
testRunId: string | null;
|
|
11
12
|
replayEventsDependencies: ReplayEventsDependencies;
|
|
12
13
|
}
|
|
13
|
-
export
|
|
14
|
+
export interface ReplayResult {
|
|
15
|
+
replay: Replay;
|
|
16
|
+
screenshotDiffResults: ScreenshotDiffResult[] | null;
|
|
17
|
+
screenshotDiffsSummary: ScreenshotDiffsSummary;
|
|
18
|
+
}
|
|
19
|
+
export declare const replayCommandHandler: ({ replayTarget, executionOptions, screenshottingOptions, apiToken, sessionId, commitSha: commitSha_, save, baseSimulationId: baseReplayId_, cookiesFile, generatedBy, testRunId, replayEventsDependencies, }: ReplayOptions) => Promise<ReplayResult>;
|
|
14
20
|
export interface RawReplayCommandHandlerOptions extends ScreenshotDiffOptions, Omit<ReplayExecutionOptions, "maxDurationMs" | "maxEventCount">, AdditionalReplayOptions {
|
|
15
21
|
screenshot: boolean;
|
|
16
22
|
appUrl: string | null | undefined;
|
|
@@ -10,7 +10,6 @@ const common_1 = require("@alwaysmeticulous/common");
|
|
|
10
10
|
const loglevel_1 = __importDefault(require("loglevel"));
|
|
11
11
|
const luxon_1 = require("luxon");
|
|
12
12
|
const client_1 = require("../../api/client");
|
|
13
|
-
const replay_diff_api_1 = require("../../api/replay-diff.api");
|
|
14
13
|
const replay_api_1 = require("../../api/replay.api");
|
|
15
14
|
const upload_1 = require("../../api/upload");
|
|
16
15
|
const archive_1 = require("../../archive/archive");
|
|
@@ -18,14 +17,13 @@ const command_builder_1 = require("../../command-utils/command-builder");
|
|
|
18
17
|
const common_options_1 = require("../../command-utils/common-options");
|
|
19
18
|
const local_data_utils_1 = require("../../local-data/local-data.utils");
|
|
20
19
|
const replay_assets_1 = require("../../local-data/replay-assets");
|
|
21
|
-
const replays_1 = require("../../local-data/replays");
|
|
22
20
|
const serve_assets_from_simulation_1 = require("../../local-data/serve-assets-from-simulation");
|
|
23
21
|
const sessions_1 = require("../../local-data/sessions");
|
|
24
22
|
const commit_sha_utils_1 = require("../../utils/commit-sha.utils");
|
|
25
23
|
const config_utils_1 = require("../../utils/config.utils");
|
|
26
24
|
const version_utils_1 = require("../../utils/version.utils");
|
|
27
|
-
const
|
|
28
|
-
const replayCommandHandler = async ({ replayTarget, executionOptions, screenshottingOptions, apiToken, sessionId, commitSha: commitSha_, save,
|
|
25
|
+
const compute_and_save_diff_1 = require("./utils/compute-and-save-diff");
|
|
26
|
+
const replayCommandHandler = async ({ replayTarget, executionOptions, screenshottingOptions, apiToken, sessionId, commitSha: commitSha_, save, baseSimulationId: baseReplayId_, cookiesFile, generatedBy, testRunId, replayEventsDependencies, }) => {
|
|
29
27
|
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
30
28
|
const client = (0, client_1.createClient)({ apiToken });
|
|
31
29
|
// 1. Check session files
|
|
@@ -82,93 +80,69 @@ const replayCommandHandler = async ({ replayTarget, executionOptions, screenshot
|
|
|
82
80
|
logger.info("Sending simulation results to Meticulous");
|
|
83
81
|
// 8. Create a Zip archive containing the replay files
|
|
84
82
|
const archivePath = await (0, archive_1.createReplayArchive)(tempDir);
|
|
85
|
-
// 9. Get upload URL
|
|
86
|
-
const replay = await (0, replay_api_1.createReplay)({
|
|
87
|
-
client,
|
|
88
|
-
commitSha,
|
|
89
|
-
sessionId,
|
|
90
|
-
meticulousSha,
|
|
91
|
-
version: "v2",
|
|
92
|
-
metadata: { generatedBy },
|
|
93
|
-
});
|
|
94
|
-
const uploadUrlData = await (0, replay_api_1.getReplayPushUrl)(client, replay.id);
|
|
95
|
-
if (!uploadUrlData) {
|
|
96
|
-
logger.error("Error: Could not get a push URL from the Meticulous API");
|
|
97
|
-
process.exit(1);
|
|
98
|
-
}
|
|
99
|
-
const uploadUrl = uploadUrlData.pushUrl;
|
|
100
|
-
// 10. Send archive to S3
|
|
101
83
|
try {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
catch (error) {
|
|
105
|
-
await (0, replay_api_1.putReplayPushedStatus)(client, replay.id, "failure", replayCommandId).catch(logger.error);
|
|
106
|
-
logger.error(error);
|
|
107
|
-
process.exit(1);
|
|
108
|
-
}
|
|
109
|
-
// 11. Report successful upload to Meticulous
|
|
110
|
-
const updatedProjectBuild = await (0, replay_api_1.putReplayPushedStatus)(client, replay.id, "success", replayCommandId);
|
|
111
|
-
logger.info("Simulation artifacts successfully sent to Meticulous");
|
|
112
|
-
logger.debug(updatedProjectBuild);
|
|
113
|
-
const replayUrl = (0, replay_api_1.getReplayUrl)(replay);
|
|
114
|
-
logger.info("=======");
|
|
115
|
-
logger.info(`View simulation at: ${replayUrl}`);
|
|
116
|
-
logger.info("=======");
|
|
117
|
-
// 12. Diff against base replay screenshot if one is provided
|
|
118
|
-
const baseReplayId = baseReplayId_ || "";
|
|
119
|
-
if (screenshottingOptions.enabled && baseReplayId) {
|
|
120
|
-
logger.info(`Diffing screenshots against replay ${baseReplayId}`);
|
|
121
|
-
await (0, replays_1.getOrFetchReplay)(client, baseReplayId);
|
|
122
|
-
await (0, replays_1.getOrFetchReplayArchive)(client, baseReplayId);
|
|
123
|
-
const baseReplayScreenshotsDir = (0, replays_1.getScreenshotsDir)((0, replays_1.getReplayDir)(baseReplayId));
|
|
124
|
-
const headReplayScreenshotsDir = (0, replays_1.getScreenshotsDir)(tempDir);
|
|
125
|
-
const screenshotDiffResults = await (0, screenshot_diff_command_1.diffScreenshots)({
|
|
84
|
+
// 9. Get upload URL
|
|
85
|
+
const replay = await (0, replay_api_1.createReplay)({
|
|
126
86
|
client,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
});
|
|
133
|
-
const replayDiff = await (0, replay_diff_api_1.createReplayDiff)({
|
|
134
|
-
client,
|
|
135
|
-
headReplayId: replay.id,
|
|
136
|
-
baseReplayId,
|
|
137
|
-
testRunId,
|
|
138
|
-
data: {
|
|
139
|
-
screenshotAssertionsOptions: screenshottingOptions,
|
|
140
|
-
screenshotDiffResults,
|
|
141
|
-
},
|
|
87
|
+
commitSha,
|
|
88
|
+
sessionId,
|
|
89
|
+
meticulousSha,
|
|
90
|
+
version: "v2",
|
|
91
|
+
metadata: { generatedBy },
|
|
142
92
|
});
|
|
143
|
-
|
|
93
|
+
const uploadUrlData = await (0, replay_api_1.getReplayPushUrl)(client, replay.id);
|
|
94
|
+
if (!uploadUrlData) {
|
|
95
|
+
logger.error("Error: Could not get a push URL from the Meticulous API");
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
const uploadUrl = uploadUrlData.pushUrl;
|
|
99
|
+
// 10. Send archive to S3
|
|
144
100
|
try {
|
|
145
|
-
(0,
|
|
146
|
-
baseReplayId,
|
|
147
|
-
headReplayId: replay.id,
|
|
148
|
-
results: screenshotDiffResults,
|
|
149
|
-
diffOptions: screenshottingOptions.diffOptions,
|
|
150
|
-
});
|
|
101
|
+
await (0, upload_1.uploadArchive)(uploadUrl, archivePath);
|
|
151
102
|
}
|
|
152
103
|
catch (error) {
|
|
153
|
-
|
|
154
|
-
|
|
104
|
+
await (0, replay_api_1.putReplayPushedStatus)(client, replay.id, "failure", replayCommandId).catch(logger.error);
|
|
105
|
+
logger.error(error);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
// 11. Report successful upload to Meticulous
|
|
109
|
+
const updatedProjectBuild = await (0, replay_api_1.putReplayPushedStatus)(client, replay.id, "success", replayCommandId);
|
|
110
|
+
logger.info("Simulation artifacts successfully sent to Meticulous");
|
|
111
|
+
logger.debug(updatedProjectBuild);
|
|
112
|
+
const replayUrl = (0, replay_api_1.getReplayUrl)(replay);
|
|
113
|
+
logger.info("=======");
|
|
114
|
+
logger.info(`View simulation at: ${replayUrl}`);
|
|
115
|
+
logger.info("=======");
|
|
116
|
+
// 12. Diff against base replay screenshot if one is provided
|
|
117
|
+
const { screenshotDiffResults, screenshotDiffsSummary } = screenshottingOptions.enabled && baseReplayId_
|
|
118
|
+
? await (0, compute_and_save_diff_1.computeAndSaveDiff)({
|
|
119
|
+
client,
|
|
120
|
+
baseReplayId: baseReplayId_ !== null && baseReplayId_ !== void 0 ? baseReplayId_ : "",
|
|
121
|
+
headReplayId: replay.id,
|
|
122
|
+
tempDir,
|
|
123
|
+
screenshottingOptions,
|
|
124
|
+
testRunId,
|
|
125
|
+
})
|
|
126
|
+
: {
|
|
127
|
+
screenshotDiffResults: null,
|
|
128
|
+
screenshotDiffsSummary: { hasDiffs: false },
|
|
129
|
+
};
|
|
130
|
+
// 13. Add test case to meticulous.json if --save option is passed
|
|
131
|
+
if (save) {
|
|
132
|
+
if (!screenshottingOptions.enabled) {
|
|
133
|
+
logger.error("Warning: saving a new test case without screenshot enabled.");
|
|
155
134
|
}
|
|
156
|
-
|
|
135
|
+
await (0, config_utils_1.addTestCase)({
|
|
136
|
+
title: `${sessionId} | ${replay.id}`,
|
|
137
|
+
sessionId,
|
|
138
|
+
baseReplayId: replay.id,
|
|
139
|
+
});
|
|
157
140
|
}
|
|
141
|
+
return { replay, screenshotDiffResults, screenshotDiffsSummary };
|
|
158
142
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (!screenshottingOptions.enabled) {
|
|
162
|
-
logger.error("Warning: saving a new test case without screenshot enabled.");
|
|
163
|
-
}
|
|
164
|
-
await (0, config_utils_1.addTestCase)({
|
|
165
|
-
title: `${sessionId} | ${replay.id}`,
|
|
166
|
-
sessionId,
|
|
167
|
-
baseReplayId: replay.id,
|
|
168
|
-
});
|
|
143
|
+
finally {
|
|
144
|
+
await (0, archive_1.deleteArchive)(archivePath);
|
|
169
145
|
}
|
|
170
|
-
await (0, archive_1.deleteArchive)(archivePath);
|
|
171
|
-
return replay;
|
|
172
146
|
};
|
|
173
147
|
exports.replayCommandHandler = replayCommandHandler;
|
|
174
148
|
const serveOrGetAppUrl = async (client, replayTarget) => {
|
|
@@ -218,7 +192,7 @@ const rawReplayCommandHandler = async ({ apiToken, commitSha, sessionId, appUrl,
|
|
|
218
192
|
}
|
|
219
193
|
: { enabled: false };
|
|
220
194
|
const replayEventsDependencies = await (0, replay_assets_1.loadReplayEventsDependencies)();
|
|
221
|
-
|
|
195
|
+
const { replay, screenshotDiffsSummary } = await (0, exports.replayCommandHandler)({
|
|
222
196
|
replayTarget: (0, exports.getReplayTarget)({
|
|
223
197
|
appUrl: appUrl !== null && appUrl !== void 0 ? appUrl : null,
|
|
224
198
|
simulationIdForAssets: simulationIdForAssets !== null && simulationIdForAssets !== void 0 ? simulationIdForAssets : null,
|
|
@@ -231,11 +205,14 @@ const rawReplayCommandHandler = async ({ apiToken, commitSha, sessionId, appUrl,
|
|
|
231
205
|
sessionId,
|
|
232
206
|
baseSimulationId,
|
|
233
207
|
save,
|
|
234
|
-
exitOnMismatch: true,
|
|
235
208
|
generatedBy: generatedByOption,
|
|
236
209
|
testRunId: null,
|
|
237
210
|
replayEventsDependencies,
|
|
238
211
|
});
|
|
212
|
+
if (screenshotDiffsSummary.hasDiffs) {
|
|
213
|
+
process.exit(1);
|
|
214
|
+
}
|
|
215
|
+
return replay;
|
|
239
216
|
};
|
|
240
217
|
exports.rawReplayCommandHandler = rawReplayCommandHandler;
|
|
241
218
|
const getReplayTarget = ({ appUrl, simulationIdForAssets, }) => {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ScreenshotAssertionsEnabledOptions, ScreenshotDiffResult } from "@alwaysmeticulous/api";
|
|
2
|
+
import { AxiosInstance } from "axios";
|
|
3
|
+
import { ScreenshotDiffsSummary } from "../../screenshot-diff/screenshot-diff.command";
|
|
4
|
+
export interface ComputeAndSaveDiffOptions {
|
|
5
|
+
client: AxiosInstance;
|
|
6
|
+
testRunId: string | null;
|
|
7
|
+
baseReplayId: string;
|
|
8
|
+
headReplayId: string;
|
|
9
|
+
tempDir: string;
|
|
10
|
+
screenshottingOptions: ScreenshotAssertionsEnabledOptions;
|
|
11
|
+
}
|
|
12
|
+
export declare const computeAndSaveDiff: ({ client, baseReplayId, tempDir, headReplayId, screenshottingOptions, testRunId, }: ComputeAndSaveDiffOptions) => Promise<{
|
|
13
|
+
screenshotDiffResults: ScreenshotDiffResult[];
|
|
14
|
+
screenshotDiffsSummary: ScreenshotDiffsSummary;
|
|
15
|
+
}>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.computeAndSaveDiff = void 0;
|
|
7
|
+
const common_1 = require("@alwaysmeticulous/common");
|
|
8
|
+
const loglevel_1 = __importDefault(require("loglevel"));
|
|
9
|
+
const replay_diff_api_1 = require("../../../api/replay-diff.api");
|
|
10
|
+
const replays_1 = require("../../../local-data/replays");
|
|
11
|
+
const screenshot_diff_command_1 = require("../../screenshot-diff/screenshot-diff.command");
|
|
12
|
+
const computeAndSaveDiff = async ({ client, baseReplayId, tempDir, headReplayId, screenshottingOptions, testRunId, }) => {
|
|
13
|
+
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
14
|
+
logger.info(`Diffing screenshots against replay ${baseReplayId}`);
|
|
15
|
+
await (0, replays_1.getOrFetchReplay)(client, baseReplayId);
|
|
16
|
+
await (0, replays_1.getOrFetchReplayArchive)(client, baseReplayId);
|
|
17
|
+
const baseReplayScreenshotsDir = (0, replays_1.getScreenshotsDir)((0, replays_1.getReplayDir)(baseReplayId));
|
|
18
|
+
const headReplayScreenshotsDir = (0, replays_1.getScreenshotsDir)(tempDir);
|
|
19
|
+
const screenshotDiffResults = await (0, screenshot_diff_command_1.diffScreenshots)({
|
|
20
|
+
client,
|
|
21
|
+
baseReplayId,
|
|
22
|
+
headReplayId,
|
|
23
|
+
baseScreenshotsDir: baseReplayScreenshotsDir,
|
|
24
|
+
headScreenshotsDir: headReplayScreenshotsDir,
|
|
25
|
+
diffOptions: screenshottingOptions.diffOptions,
|
|
26
|
+
});
|
|
27
|
+
const replayDiff = await (0, replay_diff_api_1.createReplayDiff)({
|
|
28
|
+
client,
|
|
29
|
+
headReplayId,
|
|
30
|
+
baseReplayId,
|
|
31
|
+
testRunId,
|
|
32
|
+
data: {
|
|
33
|
+
screenshotAssertionsOptions: screenshottingOptions,
|
|
34
|
+
screenshotDiffResults,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
logger.debug(replayDiff);
|
|
38
|
+
const screenshotDiffsSummary = (0, screenshot_diff_command_1.summarizeDifferences)({
|
|
39
|
+
baseReplayId,
|
|
40
|
+
headReplayId,
|
|
41
|
+
results: screenshotDiffResults,
|
|
42
|
+
diffOptions: screenshottingOptions.diffOptions,
|
|
43
|
+
});
|
|
44
|
+
return { screenshotDiffResults, screenshotDiffsSummary };
|
|
45
|
+
};
|
|
46
|
+
exports.computeAndSaveDiff = computeAndSaveDiff;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.runAllTestsCommand = void 0;
|
|
4
|
+
const client_1 = require("../../api/client");
|
|
5
|
+
const test_run_api_1 = require("../../api/test-run.api");
|
|
4
6
|
const command_builder_1 = require("../../command-utils/command-builder");
|
|
5
7
|
const common_options_1 = require("../../command-utils/common-options");
|
|
6
8
|
const run_all_tests_1 = require("../../parallel-tests/run-all-tests");
|
|
7
|
-
const
|
|
9
|
+
const commit_sha_utils_1 = require("../../utils/commit-sha.utils");
|
|
10
|
+
const handler = async ({ apiToken, commitSha: commitSha_, baseCommitSha, appUrl, useAssetsSnapshottedInBaseSimulation, headless, devTools, bypassCSP, diffThreshold, diffPixelThreshold, padTime, shiftTime, networkStubbing, githubSummary, parallelize, parallelTasks: parrelelTasks_, deflake, useCache, testsFile, disableRemoteFonts, noSandbox, skipPauses, moveBeforeClick, maxDurationMs, maxEventCount, storyboard, }) => {
|
|
8
11
|
const executionOptions = {
|
|
9
12
|
headless,
|
|
10
13
|
devTools,
|
|
@@ -29,21 +32,26 @@ const handler = async ({ apiToken, commitSha, baseCommitSha, appUrl, useAssetsSn
|
|
|
29
32
|
storyboardOptions,
|
|
30
33
|
};
|
|
31
34
|
const parrelelTasks = parallelize ? parrelelTasks_ : 1;
|
|
32
|
-
const
|
|
35
|
+
const commitSha = (await (0, commit_sha_utils_1.getCommitSha)(commitSha_)) || "unknown";
|
|
36
|
+
const client = (0, client_1.createClient)({ apiToken });
|
|
37
|
+
const cachedTestRunResults = useCache
|
|
38
|
+
? await (0, test_run_api_1.getCachedTestRunResults)({ client, commitSha })
|
|
39
|
+
: [];
|
|
40
|
+
const { testRun } = await (0, run_all_tests_1.runAllTests)({
|
|
33
41
|
testsFile: testsFile !== null && testsFile !== void 0 ? testsFile : null,
|
|
34
42
|
executionOptions,
|
|
35
43
|
screenshottingOptions,
|
|
36
44
|
apiToken: apiToken !== null && apiToken !== void 0 ? apiToken : null,
|
|
37
|
-
commitSha
|
|
45
|
+
commitSha,
|
|
38
46
|
baseCommitSha: baseCommitSha !== null && baseCommitSha !== void 0 ? baseCommitSha : null,
|
|
39
47
|
appUrl: appUrl !== null && appUrl !== void 0 ? appUrl : null,
|
|
40
48
|
useAssetsSnapshottedInBaseSimulation,
|
|
41
49
|
parallelTasks: parrelelTasks !== null && parrelelTasks !== void 0 ? parrelelTasks : null,
|
|
42
50
|
deflake,
|
|
43
|
-
|
|
51
|
+
cachedTestRunResults,
|
|
44
52
|
githubSummary,
|
|
45
53
|
});
|
|
46
|
-
if (
|
|
54
|
+
if (testRun.status === "Failure") {
|
|
47
55
|
process.exit(1);
|
|
48
56
|
}
|
|
49
57
|
};
|
|
@@ -9,21 +9,21 @@ export declare const diffScreenshots: ({ client, headReplayId, baseReplayId, hea
|
|
|
9
9
|
headScreenshotsDir: string;
|
|
10
10
|
diffOptions: ScreenshotDiffOptions;
|
|
11
11
|
}) => Promise<ScreenshotDiffResult[]>;
|
|
12
|
-
export declare const
|
|
12
|
+
export declare const summarizeDifferences: ({ baseReplayId, headReplayId, results, diffOptions, }: {
|
|
13
13
|
baseReplayId: string;
|
|
14
14
|
headReplayId: string;
|
|
15
15
|
results: ScreenshotDiffResult[];
|
|
16
16
|
diffOptions: ScreenshotDiffOptions;
|
|
17
|
-
}) =>
|
|
18
|
-
export declare
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
}) => ScreenshotDiffsSummary;
|
|
18
|
+
export declare type ScreenshotDiffsSummary = HasDiffsScreenshotDiffsResult | NoDiffsScreenshotDiffsResult;
|
|
19
|
+
export interface HasDiffsScreenshotDiffsResult {
|
|
20
|
+
hasDiffs: true;
|
|
21
|
+
summaryMessage: string;
|
|
22
|
+
baseReplayId: string;
|
|
23
|
+
headReplayId: string;
|
|
24
|
+
}
|
|
25
|
+
export interface NoDiffsScreenshotDiffsResult {
|
|
26
|
+
hasDiffs: false;
|
|
27
27
|
}
|
|
28
28
|
export declare const screenshotDiffCommand: import("yargs").CommandModule<unknown, import("yargs").InferredOptionTypes<{
|
|
29
29
|
apiToken: {
|
|
@@ -3,7 +3,7 @@ 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.screenshotDiffCommand = exports.
|
|
6
|
+
exports.screenshotDiffCommand = exports.summarizeDifferences = exports.diffScreenshots = void 0;
|
|
7
7
|
const path_1 = require("path");
|
|
8
8
|
const common_1 = require("@alwaysmeticulous/common");
|
|
9
9
|
const loglevel_1 = __importDefault(require("loglevel"));
|
|
@@ -99,7 +99,7 @@ const diffScreenshots = async ({ client, headReplayId, baseReplayId, headScreens
|
|
|
99
99
|
return [...missingHeadImagesResults, ...headDiffResults];
|
|
100
100
|
};
|
|
101
101
|
exports.diffScreenshots = diffScreenshots;
|
|
102
|
-
const
|
|
102
|
+
const summarizeDifferences = ({ baseReplayId, headReplayId, results, diffOptions, }) => {
|
|
103
103
|
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
104
104
|
const missingHeadImagesResults = results.flatMap((result) => result.outcome === "missing-head" ? [result] : []);
|
|
105
105
|
if (missingHeadImagesResults.length) {
|
|
@@ -107,10 +107,12 @@ const checkScreenshotDiffResult = ({ baseReplayId, headReplayId, results, diffOp
|
|
|
107
107
|
.map(({ baseScreenshotFile }) => (0, path_1.basename)(baseScreenshotFile))
|
|
108
108
|
.sort()} => FAIL!`;
|
|
109
109
|
logger.info(message);
|
|
110
|
-
|
|
110
|
+
return {
|
|
111
|
+
hasDiffs: true,
|
|
112
|
+
summaryMessage: message,
|
|
111
113
|
baseReplayId,
|
|
112
114
|
headReplayId,
|
|
113
|
-
}
|
|
115
|
+
};
|
|
114
116
|
}
|
|
115
117
|
const missingBaseImagesResults = results.flatMap((result) => result.outcome === "missing-base" ? [result] : []);
|
|
116
118
|
if (missingHeadImagesResults.length) {
|
|
@@ -124,10 +126,12 @@ const checkScreenshotDiffResult = ({ baseReplayId, headReplayId, results, diffOp
|
|
|
124
126
|
if (outcome === "different-size") {
|
|
125
127
|
const message = `Screenshots ${(0, path_1.basename)(result.headScreenshotFile)} have different sizes => FAIL!`;
|
|
126
128
|
logger.info(message);
|
|
127
|
-
|
|
129
|
+
return {
|
|
130
|
+
hasDiffs: true,
|
|
131
|
+
summaryMessage: message,
|
|
128
132
|
baseReplayId,
|
|
129
133
|
headReplayId,
|
|
130
|
-
}
|
|
134
|
+
};
|
|
131
135
|
}
|
|
132
136
|
if (outcome === "diff" || outcome === "no-diff") {
|
|
133
137
|
const mismatch = (result.mismatchFraction * 100).toFixed(3);
|
|
@@ -135,22 +139,18 @@ const checkScreenshotDiffResult = ({ baseReplayId, headReplayId, results, diffOp
|
|
|
135
139
|
const message = `${mismatch}% pixel mismatch for screenshot ${(0, path_1.basename)(result.headScreenshotFile)} (threshold is ${threshold}%) => ${outcome === "no-diff" ? "PASS" : "FAIL!"}`;
|
|
136
140
|
logger.info(message);
|
|
137
141
|
if (outcome === "diff") {
|
|
138
|
-
|
|
142
|
+
return {
|
|
143
|
+
hasDiffs: true,
|
|
144
|
+
summaryMessage: message,
|
|
139
145
|
baseReplayId,
|
|
140
146
|
headReplayId,
|
|
141
|
-
}
|
|
147
|
+
};
|
|
142
148
|
}
|
|
143
149
|
}
|
|
144
150
|
});
|
|
151
|
+
return { hasDiffs: false };
|
|
145
152
|
};
|
|
146
|
-
exports.
|
|
147
|
-
class ScreenshotDiffError extends Error {
|
|
148
|
-
constructor(message, extras) {
|
|
149
|
-
super(message);
|
|
150
|
-
this.extras = extras;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
exports.ScreenshotDiffError = ScreenshotDiffError;
|
|
153
|
+
exports.summarizeDifferences = summarizeDifferences;
|
|
154
154
|
const getScreenshotIdentifier = (filename) => {
|
|
155
155
|
const name = (0, path_1.basename)(filename);
|
|
156
156
|
if (name === "final-state.png") {
|
|
@@ -192,12 +192,15 @@ const handler = async ({ apiToken, baseSimulationId: baseReplayId, headSimulatio
|
|
|
192
192
|
diffOptions,
|
|
193
193
|
});
|
|
194
194
|
logger.debug(results);
|
|
195
|
-
(0, exports.
|
|
195
|
+
const diffSummary = (0, exports.summarizeDifferences)({
|
|
196
196
|
baseReplayId,
|
|
197
197
|
headReplayId,
|
|
198
198
|
results,
|
|
199
199
|
diffOptions,
|
|
200
200
|
});
|
|
201
|
+
if (diffSummary.hasDiffs) {
|
|
202
|
+
process.exit(1);
|
|
203
|
+
}
|
|
201
204
|
};
|
|
202
205
|
exports.screenshotDiffCommand = (0, command_builder_1.buildCommand)("screenshot-diff")
|
|
203
206
|
.details({ describe: "Diff two replay screenshots" })
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ScreenshotDiffResult } from "@alwaysmeticulous/api";
|
|
1
2
|
import { ScreenshotDiffOptions } from "../command-utils/common-types";
|
|
2
3
|
export interface TestCaseReplayOptions extends Partial<ScreenshotDiffOptions> {
|
|
3
4
|
appUrl?: string | null | undefined;
|
|
@@ -21,3 +22,6 @@ export interface TestCaseResult extends TestCase {
|
|
|
21
22
|
headReplayId: string;
|
|
22
23
|
result: "pass" | "fail";
|
|
23
24
|
}
|
|
25
|
+
export interface DetailedTestCaseResult extends TestCaseResult {
|
|
26
|
+
screenshotDiffResults: ScreenshotDiffResult[];
|
|
27
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GeneratedBy, ReplayEventsDependencies, ReplayExecutionOptions, ReplayTarget } from "@alwaysmeticulous/common";
|
|
2
2
|
import { ScreenshotAssertionsEnabledOptions } from "../command-utils/common-types";
|
|
3
|
-
import {
|
|
3
|
+
import { DetailedTestCaseResult, TestCase } from "../config/config.types";
|
|
4
4
|
export interface DeflakeReplayCommandHandlerOptions extends HandleReplayOptions {
|
|
5
5
|
deflake: boolean;
|
|
6
6
|
}
|
|
@@ -15,5 +15,5 @@ interface HandleReplayOptions {
|
|
|
15
15
|
testRunId: string | null;
|
|
16
16
|
replayEventsDependencies: ReplayEventsDependencies;
|
|
17
17
|
}
|
|
18
|
-
export declare const deflakeReplayCommandHandler: (
|
|
18
|
+
export declare const deflakeReplayCommandHandler: ({ deflake, ...otherOptions }: DeflakeReplayCommandHandlerOptions) => Promise<DetailedTestCaseResult>;
|
|
19
19
|
export {};
|
|
@@ -7,11 +7,9 @@ exports.deflakeReplayCommandHandler = void 0;
|
|
|
7
7
|
const common_1 = require("@alwaysmeticulous/common");
|
|
8
8
|
const loglevel_1 = __importDefault(require("loglevel"));
|
|
9
9
|
const replay_command_1 = require("../commands/replay/replay.command");
|
|
10
|
-
const screenshot_diff_command_1 = require("../commands/screenshot-diff/screenshot-diff.command");
|
|
11
10
|
const handleReplay = async ({ testCase, replayTarget, executionOptions, screenshottingOptions, apiToken, commitSha, generatedBy, testRunId, replayEventsDependencies, }) => {
|
|
12
11
|
var _a, _b;
|
|
13
|
-
const
|
|
14
|
-
const replayPromise = (0, replay_command_1.replayCommandHandler)({
|
|
12
|
+
const { replay, screenshotDiffResults, screenshotDiffsSummary } = await (0, replay_command_1.replayCommandHandler)({
|
|
15
13
|
replayTarget,
|
|
16
14
|
executionOptions: applyTestCaseExecutionOptionOverrides(executionOptions, (_a = testCase.options) !== null && _a !== void 0 ? _a : {}),
|
|
17
15
|
screenshottingOptions: applyTestCaseScreenshottingOptionsOverrides(screenshottingOptions, (_b = testCase.options) !== null && _b !== void 0 ? _b : {}),
|
|
@@ -20,30 +18,20 @@ const handleReplay = async ({ testCase, replayTarget, executionOptions, screensh
|
|
|
20
18
|
sessionId: testCase.sessionId,
|
|
21
19
|
baseSimulationId: testCase.baseReplayId,
|
|
22
20
|
save: false,
|
|
23
|
-
exitOnMismatch: false,
|
|
24
21
|
cookiesFile: null,
|
|
25
22
|
generatedBy,
|
|
26
23
|
testRunId,
|
|
27
24
|
replayEventsDependencies,
|
|
28
25
|
});
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
if (screenshotDiffResults == null) {
|
|
27
|
+
throw new Error(`replayCommandHandler returned a null screenshotDiffResults, but was called with screenshottingOptions.enabled = true`);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
31
30
|
...testCase,
|
|
32
31
|
headReplayId: replay.id,
|
|
33
|
-
result: "pass",
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (error instanceof screenshot_diff_command_1.ScreenshotDiffError && error.extras) {
|
|
37
|
-
return {
|
|
38
|
-
...testCase,
|
|
39
|
-
headReplayId: error.extras.headReplayId,
|
|
40
|
-
result: "fail",
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
logger.error(error);
|
|
44
|
-
return { ...testCase, headReplayId: "", result: "fail" };
|
|
45
|
-
});
|
|
46
|
-
return result;
|
|
32
|
+
result: screenshotDiffsSummary.hasDiffs ? "pass" : "fail",
|
|
33
|
+
screenshotDiffResults,
|
|
34
|
+
};
|
|
47
35
|
};
|
|
48
36
|
const deflakeReplayCommandHandler = async ({ deflake, ...otherOptions }) => {
|
|
49
37
|
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import log from "loglevel";
|
|
2
|
-
import {
|
|
2
|
+
import { DetailedTestCaseResult } from "../config/config.types";
|
|
3
3
|
import { DeflakeReplayCommandHandlerOptions } from "../deflake-tests/deflake-tests.handler";
|
|
4
4
|
export interface InitMessage {
|
|
5
5
|
kind: "init";
|
|
@@ -12,6 +12,6 @@ export interface InitMessage {
|
|
|
12
12
|
export interface ResultMessage {
|
|
13
13
|
kind: "result";
|
|
14
14
|
data: {
|
|
15
|
-
result:
|
|
15
|
+
result: DetailedTestCaseResult;
|
|
16
16
|
};
|
|
17
17
|
}
|
|
@@ -1,28 +1,22 @@
|
|
|
1
1
|
import { ReplayEventsDependencies, ReplayExecutionOptions } from "@alwaysmeticulous/common";
|
|
2
|
-
import { AxiosInstance } from "axios";
|
|
3
2
|
import { TestRun } from "../api/test-run.api";
|
|
4
3
|
import { ScreenshotAssertionsEnabledOptions } from "../command-utils/common-types";
|
|
5
|
-
import { MeticulousCliConfig,
|
|
4
|
+
import { DetailedTestCaseResult, MeticulousCliConfig, TestCase } from "../config/config.types";
|
|
6
5
|
import { TestRunProgress } from "./run-all-tests.types";
|
|
7
6
|
export interface RunAllTestsInParallelOptions {
|
|
8
7
|
config: MeticulousCliConfig;
|
|
9
|
-
client: AxiosInstance;
|
|
10
8
|
testRun: TestRun;
|
|
9
|
+
testsToRun: TestCase[];
|
|
11
10
|
executionOptions: ReplayExecutionOptions;
|
|
12
11
|
screenshottingOptions: ScreenshotAssertionsEnabledOptions;
|
|
13
12
|
apiToken: string | null;
|
|
14
13
|
commitSha: string;
|
|
15
|
-
/**
|
|
16
|
-
* The base commit to compare test results against for test cases that don't have a baseReplayId specified.
|
|
17
|
-
*/
|
|
18
|
-
baseCommitSha: string | null;
|
|
19
14
|
appUrl: string | null;
|
|
20
15
|
useAssetsSnapshottedInBaseSimulation: boolean;
|
|
21
16
|
parallelTasks: number | null;
|
|
22
17
|
deflake: boolean;
|
|
23
|
-
cachedTestRunResults: TestCaseResult[];
|
|
24
18
|
replayEventsDependencies: ReplayEventsDependencies;
|
|
25
|
-
onTestFinished?: (progress: TestRunProgress) => void
|
|
19
|
+
onTestFinished?: (progress: TestRunProgress, resultsSoFar: DetailedTestCaseResult[]) => Promise<void>;
|
|
26
20
|
}
|
|
27
21
|
/** Handler for running Meticulous tests in parallel using child processes */
|
|
28
|
-
export declare const runAllTestsInParallel: (options: RunAllTestsInParallelOptions) => Promise<
|
|
22
|
+
export declare const runAllTestsInParallel: (options: RunAllTestsInParallelOptions) => Promise<DetailedTestCaseResult[]>;
|
|
@@ -9,19 +9,12 @@ const os_1 = require("os");
|
|
|
9
9
|
const path_1 = require("path");
|
|
10
10
|
const common_1 = require("@alwaysmeticulous/common");
|
|
11
11
|
const loglevel_1 = __importDefault(require("loglevel"));
|
|
12
|
-
const test_run_api_1 = require("../api/test-run.api");
|
|
13
12
|
const config_utils_1 = require("../utils/config.utils");
|
|
14
13
|
const run_all_tests_utils_1 = require("../utils/run-all-tests.utils");
|
|
15
14
|
/** Handler for running Meticulous tests in parallel using child processes */
|
|
16
|
-
const runAllTestsInParallel = async ({ config,
|
|
15
|
+
const runAllTestsInParallel = async ({ config, testRun, testsToRun: queue, apiToken, commitSha, appUrl, useAssetsSnapshottedInBaseSimulation, executionOptions, screenshottingOptions, parallelTasks, deflake, replayEventsDependencies, onTestFinished, }) => {
|
|
17
16
|
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
18
|
-
const results = [
|
|
19
|
-
const queue = await (0, run_all_tests_utils_1.getTestsToRun)({
|
|
20
|
-
client,
|
|
21
|
-
testCases: config.testCases || [],
|
|
22
|
-
cachedTestRunResults,
|
|
23
|
-
baseCommitSha,
|
|
24
|
-
});
|
|
17
|
+
const results = [];
|
|
25
18
|
const progress = {
|
|
26
19
|
runningTestCases: queue.length,
|
|
27
20
|
failedTestCases: 0,
|
|
@@ -87,10 +80,13 @@ const runAllTestsInParallel = async ({ config, client, testRun, apiToken, commit
|
|
|
87
80
|
// Handle task completion
|
|
88
81
|
deferredResult.promise
|
|
89
82
|
.catch(() => {
|
|
83
|
+
// If it threw an error then it's something fatal, rather than just a failed diff
|
|
84
|
+
// (it resolves successfully on a failed diff)
|
|
90
85
|
const result = {
|
|
91
86
|
...testCase,
|
|
92
87
|
headReplayId: "",
|
|
93
88
|
result: "fail",
|
|
89
|
+
screenshotDiffResults: [],
|
|
94
90
|
};
|
|
95
91
|
return result;
|
|
96
92
|
})
|
|
@@ -100,17 +96,7 @@ const runAllTestsInParallel = async ({ config, client, testRun, apiToken, commit
|
|
|
100
96
|
progress.failedTestCases += result.result === "fail" ? 1 : 0;
|
|
101
97
|
progress.passedTestCases += result.result === "pass" ? 1 : 0;
|
|
102
98
|
--progress.runningTestCases;
|
|
103
|
-
onTestFinished === null || onTestFinished === void 0 ? void 0 : onTestFinished(progress)
|
|
104
|
-
(0, test_run_api_1.putTestRunResults)({
|
|
105
|
-
client,
|
|
106
|
-
testRunId: testRun.id,
|
|
107
|
-
status: "Running",
|
|
108
|
-
resultData: { results },
|
|
109
|
-
})
|
|
110
|
-
.catch((error) => {
|
|
111
|
-
logger.error(`Error while pushing partial results: ${error}`);
|
|
112
|
-
})
|
|
113
|
-
.then(() => {
|
|
99
|
+
onTestFinished === null || onTestFinished === void 0 ? void 0 : onTestFinished(progress, results).then(() => {
|
|
114
100
|
var _a;
|
|
115
101
|
if (results.length === (((_a = config.testCases) === null || _a === void 0 ? void 0 : _a.length) || 0)) {
|
|
116
102
|
allTasksDone.resolve();
|