@alwaysmeticulous/cli 2.3.4 → 2.4.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/command-utils/command-utils.d.ts +9 -0
- package/dist/command-utils/command-utils.js +18 -0
- package/dist/command-utils/common-options.d.ts +110 -0
- package/dist/command-utils/common-options.js +87 -0
- package/dist/command-utils/common-types.d.ts +20 -0
- package/dist/command-utils/common-types.js +2 -0
- package/dist/commands/create-test/create-test.command.d.ts +2 -2
- package/dist/commands/create-test/create-test.command.js +23 -40
- package/dist/commands/record/record.command.d.ts +9 -9
- package/dist/commands/record/record.command.js +2 -1
- package/dist/commands/replay/replay.command.d.ts +24 -26
- package/dist/commands/replay/replay.command.js +84 -76
- package/dist/commands/run-all-tests/run-all-tests.command.d.ts +11 -18
- package/dist/commands/run-all-tests/run-all-tests.command.js +38 -77
- package/dist/commands/screenshot-diff/screenshot-diff.command.d.ts +4 -4
- package/dist/commands/screenshot-diff/screenshot-diff.command.js +15 -19
- package/dist/config/config.js +2 -3
- package/dist/config/config.types.d.ts +7 -8
- package/dist/deflake-tests/deflake-tests.handler.d.ts +12 -3
- package/dist/deflake-tests/deflake-tests.handler.js +41 -6
- package/dist/image/diff.utils.d.ts +8 -1
- package/dist/image/diff.utils.js +1 -5
- package/dist/parallel-tests/messages.types.d.ts +3 -18
- package/dist/parallel-tests/parallel-tests.handler.d.ts +9 -14
- package/dist/parallel-tests/parallel-tests.handler.js +11 -15
- package/dist/parallel-tests/task.handler.js +2 -26
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/config.utils.d.ts +6 -1
- package/dist/utils/config.utils.js +17 -8
- package/package.json +6 -6
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
diffPixelThreshold?: number;
|
|
5
|
-
cookies?: Record<string, any>[];
|
|
6
|
-
moveBeforeClick?: boolean;
|
|
1
|
+
import { ScreenshotDiffOptions } from "../command-utils/common-types";
|
|
2
|
+
export interface TestCaseReplayOptions extends Partial<ScreenshotDiffOptions> {
|
|
3
|
+
appUrl?: string | null | undefined;
|
|
7
4
|
/**
|
|
8
5
|
* If present will run the session against a local server serving up previously snapshotted assets (HTML, JS, CSS etc.) from the specified prior replay, instead of against a URL.
|
|
9
6
|
*/
|
|
10
|
-
|
|
7
|
+
simulationIdForAssets?: string | undefined;
|
|
8
|
+
screenshotSelector?: string;
|
|
9
|
+
moveBeforeClick?: boolean;
|
|
11
10
|
}
|
|
12
11
|
export interface TestCase {
|
|
13
12
|
title: string;
|
|
14
13
|
sessionId: string;
|
|
15
14
|
baseReplayId: string;
|
|
16
|
-
options?:
|
|
15
|
+
options?: TestCaseReplayOptions;
|
|
17
16
|
}
|
|
18
17
|
export interface MeticulousCliConfig {
|
|
19
18
|
testCases?: TestCase[];
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ReplayExecutionOptions, ReplayTarget } from "@alwaysmeticulous/common/dist/types/replay.types";
|
|
2
|
+
import { ScreenshotAssertionsEnabledOptions } from "../command-utils/common-types";
|
|
2
3
|
import { TestCase, TestCaseResult } from "../config/config.types";
|
|
3
|
-
export interface DeflakeReplayCommandHandlerOptions extends
|
|
4
|
-
testCase: TestCase;
|
|
4
|
+
export interface DeflakeReplayCommandHandlerOptions extends HandleReplayOptions {
|
|
5
5
|
deflake: boolean;
|
|
6
6
|
}
|
|
7
|
+
interface HandleReplayOptions {
|
|
8
|
+
replayTarget: ReplayTarget;
|
|
9
|
+
executionOptions: ReplayExecutionOptions;
|
|
10
|
+
screenshottingOptions: ScreenshotAssertionsEnabledOptions;
|
|
11
|
+
testCase: TestCase;
|
|
12
|
+
apiToken: string | undefined;
|
|
13
|
+
commitSha: string;
|
|
14
|
+
}
|
|
7
15
|
export declare const deflakeReplayCommandHandler: (options: DeflakeReplayCommandHandlerOptions) => Promise<TestCaseResult>;
|
|
16
|
+
export {};
|
|
@@ -8,9 +8,21 @@ 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
10
|
const screenshot_diff_command_1 = require("../commands/screenshot-diff/screenshot-diff.command");
|
|
11
|
-
const handleReplay = async ({ testCase,
|
|
11
|
+
const handleReplay = async ({ testCase, replayTarget, executionOptions, screenshottingOptions, apiToken, commitSha, }) => {
|
|
12
|
+
var _a, _b;
|
|
12
13
|
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
13
|
-
const replayPromise = (0, replay_command_1.replayCommandHandler)(
|
|
14
|
+
const replayPromise = (0, replay_command_1.replayCommandHandler)({
|
|
15
|
+
replayTarget,
|
|
16
|
+
executionOptions: applyTestCaseExecutionOptionOverrides(executionOptions, (_a = testCase.options) !== null && _a !== void 0 ? _a : {}),
|
|
17
|
+
screenshottingOptions: applyTestCaseScreenshottingOptionsOverrides(screenshottingOptions, (_b = testCase.options) !== null && _b !== void 0 ? _b : {}),
|
|
18
|
+
apiToken,
|
|
19
|
+
commitSha,
|
|
20
|
+
sessionId: testCase.sessionId,
|
|
21
|
+
baseSimulationId: testCase.baseReplayId,
|
|
22
|
+
save: false,
|
|
23
|
+
exitOnMismatch: false,
|
|
24
|
+
cookiesFile: undefined,
|
|
25
|
+
});
|
|
14
26
|
const result = await replayPromise
|
|
15
27
|
.then((replay) => ({
|
|
16
28
|
...testCase,
|
|
@@ -30,18 +42,41 @@ const handleReplay = async ({ testCase, options }) => {
|
|
|
30
42
|
});
|
|
31
43
|
return result;
|
|
32
44
|
};
|
|
33
|
-
const deflakeReplayCommandHandler = async ({
|
|
45
|
+
const deflakeReplayCommandHandler = async ({ deflake, ...otherOptions }) => {
|
|
34
46
|
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
35
|
-
const firstResult = await handleReplay(
|
|
47
|
+
const firstResult = await handleReplay(otherOptions);
|
|
36
48
|
if (firstResult.result === "pass" || !deflake) {
|
|
37
49
|
return firstResult;
|
|
38
50
|
}
|
|
39
|
-
const secondResult = await handleReplay(
|
|
51
|
+
const secondResult = await handleReplay(otherOptions);
|
|
40
52
|
if (secondResult.result === "fail") {
|
|
41
53
|
return secondResult;
|
|
42
54
|
}
|
|
43
|
-
const thirdResult = await handleReplay(
|
|
55
|
+
const thirdResult = await handleReplay(otherOptions);
|
|
44
56
|
logger.info(`FLAKE: ${thirdResult.title} => ${thirdResult.result}`);
|
|
45
57
|
return thirdResult;
|
|
46
58
|
};
|
|
47
59
|
exports.deflakeReplayCommandHandler = deflakeReplayCommandHandler;
|
|
60
|
+
const applyTestCaseExecutionOptionOverrides = (executionOptionsFromCliFlags, overridesFromTestCase) => {
|
|
61
|
+
var _a;
|
|
62
|
+
// Options specified in the test case override those passed as CLI flags
|
|
63
|
+
// (CLI flags set the defaults)
|
|
64
|
+
return {
|
|
65
|
+
...executionOptionsFromCliFlags,
|
|
66
|
+
moveBeforeClick: (_a = overridesFromTestCase.moveBeforeClick) !== null && _a !== void 0 ? _a : executionOptionsFromCliFlags.moveBeforeClick,
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
const applyTestCaseScreenshottingOptionsOverrides = (screenshottingOptionsFromCliFlags, overridesFromTestCase) => {
|
|
70
|
+
var _a, _b, _c;
|
|
71
|
+
// Options specified in the test case override those passed as CLI flags
|
|
72
|
+
// (CLI flags set the defaults)
|
|
73
|
+
const diffOptions = {
|
|
74
|
+
diffThreshold: (_a = overridesFromTestCase.diffThreshold) !== null && _a !== void 0 ? _a : screenshottingOptionsFromCliFlags.diffOptions.diffThreshold,
|
|
75
|
+
diffPixelThreshold: (_b = overridesFromTestCase.diffPixelThreshold) !== null && _b !== void 0 ? _b : screenshottingOptionsFromCliFlags.diffOptions.diffPixelThreshold,
|
|
76
|
+
};
|
|
77
|
+
return {
|
|
78
|
+
enabled: true,
|
|
79
|
+
screenshotSelector: (_c = overridesFromTestCase.screenshotSelector) !== null && _c !== void 0 ? _c : screenshottingOptionsFromCliFlags.screenshotSelector,
|
|
80
|
+
diffOptions,
|
|
81
|
+
};
|
|
82
|
+
};
|
|
@@ -2,7 +2,14 @@ import { PNG } from "pngjs";
|
|
|
2
2
|
export interface CompareImageOptions {
|
|
3
3
|
base: PNG;
|
|
4
4
|
head: PNG;
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Maximum colour distance a given pixel is allowed to differ before counting two
|
|
7
|
+
* pixels as different.
|
|
8
|
+
*
|
|
9
|
+
* Measure is based on "Measuring perceived color difference using YIQ NTSC transmission color space
|
|
10
|
+
* in mobile applications" by Y. Kotsarenko and F. Ramos
|
|
11
|
+
*/
|
|
12
|
+
pixelThreshold: number;
|
|
6
13
|
}
|
|
7
14
|
export interface CompareImageResult {
|
|
8
15
|
mismatchPixels: number;
|
package/dist/image/diff.utils.js
CHANGED
|
@@ -7,17 +7,13 @@ exports.compareImages = void 0;
|
|
|
7
7
|
const pixelmatch_1 = __importDefault(require("pixelmatch"));
|
|
8
8
|
const pngjs_1 = require("pngjs");
|
|
9
9
|
const compareImages = ({ base, head, pixelThreshold }) => {
|
|
10
|
-
const pixelmatchOptions = pixelThreshold
|
|
11
|
-
? { threshold: pixelThreshold }
|
|
12
|
-
: null;
|
|
13
10
|
if (base.width !== head.width || base.height !== head.height) {
|
|
14
11
|
throw new Error("Cannot handle different size yet");
|
|
15
12
|
}
|
|
16
13
|
const { width, height } = base;
|
|
17
14
|
const diff = new pngjs_1.PNG({ width, height });
|
|
18
|
-
const threshold = (pixelmatchOptions === null || pixelmatchOptions === void 0 ? void 0 : pixelmatchOptions.threshold) || 0.01;
|
|
19
15
|
const mismatchPixels = (0, pixelmatch_1.default)(base.data, head.data, diff.data, width, height, {
|
|
20
|
-
threshold,
|
|
16
|
+
threshold: pixelThreshold,
|
|
21
17
|
});
|
|
22
18
|
const mismatchFraction = mismatchPixels / (width * height);
|
|
23
19
|
return {
|
|
@@ -1,27 +1,12 @@
|
|
|
1
1
|
import log from "loglevel";
|
|
2
|
-
import {
|
|
2
|
+
import { TestCaseResult } from "../config/config.types";
|
|
3
|
+
import { DeflakeReplayCommandHandlerOptions } from "../deflake-tests/deflake-tests.handler";
|
|
3
4
|
export interface InitMessage {
|
|
4
5
|
kind: "init";
|
|
5
6
|
data: {
|
|
6
7
|
logLevel: log.LogLevel[keyof log.LogLevel];
|
|
7
8
|
dataDir: string;
|
|
8
|
-
|
|
9
|
-
apiToken: string | null | undefined;
|
|
10
|
-
commitSha: string | null | undefined;
|
|
11
|
-
appUrl: string | null | undefined;
|
|
12
|
-
simulationIdForAssets?: string | null | undefined;
|
|
13
|
-
headless: boolean | null | undefined;
|
|
14
|
-
devTools: boolean | null | undefined;
|
|
15
|
-
bypassCSP: boolean | null | undefined;
|
|
16
|
-
diffThreshold: number | null | undefined;
|
|
17
|
-
diffPixelThreshold: number | null | undefined;
|
|
18
|
-
padTime: boolean;
|
|
19
|
-
shiftTime: boolean;
|
|
20
|
-
networkStubbing: boolean;
|
|
21
|
-
accelerate: boolean;
|
|
22
|
-
};
|
|
23
|
-
testCase: TestCase;
|
|
24
|
-
deflake: boolean;
|
|
9
|
+
replayOptions: DeflakeReplayCommandHandlerOptions;
|
|
25
10
|
};
|
|
26
11
|
}
|
|
27
12
|
export interface ResultMessage {
|
|
@@ -1,26 +1,21 @@
|
|
|
1
|
+
import { ReplayExecutionOptions } from "@alwaysmeticulous/common";
|
|
1
2
|
import { AxiosInstance } from "axios";
|
|
2
3
|
import { TestRun } from "../api/test-run.api";
|
|
4
|
+
import { ScreenshotAssertionsEnabledOptions } from "../command-utils/common-types";
|
|
3
5
|
import { MeticulousCliConfig, TestCaseResult } from "../config/config.types";
|
|
4
6
|
export interface RunAllTestsInParallelOptions {
|
|
5
7
|
config: MeticulousCliConfig;
|
|
6
8
|
client: AxiosInstance;
|
|
7
9
|
testRun: TestRun;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
diffThreshold: number | null | undefined;
|
|
16
|
-
diffPixelThreshold: number | null | undefined;
|
|
17
|
-
padTime: boolean;
|
|
18
|
-
shiftTime: boolean;
|
|
19
|
-
networkStubbing: boolean;
|
|
20
|
-
parallelTasks: number | null | undefined;
|
|
10
|
+
executionOptions: ReplayExecutionOptions;
|
|
11
|
+
screenshottingOptions: ScreenshotAssertionsEnabledOptions;
|
|
12
|
+
apiToken: string | undefined;
|
|
13
|
+
commitSha: string;
|
|
14
|
+
appUrl: string | undefined;
|
|
15
|
+
useAssetsSnapshottedInBaseSimulation: boolean;
|
|
16
|
+
parallelTasks: number | undefined;
|
|
21
17
|
deflake: boolean;
|
|
22
18
|
cachedTestRunResults: TestCaseResult[];
|
|
23
|
-
accelerate: boolean;
|
|
24
19
|
}
|
|
25
20
|
/** Handler for running Meticulous tests in parallel using child processes */
|
|
26
21
|
export declare const runAllTestsInParallel: (options: RunAllTestsInParallelOptions) => Promise<TestCaseResult[]>;
|
|
@@ -13,7 +13,7 @@ const test_run_api_1 = require("../api/test-run.api");
|
|
|
13
13
|
const config_utils_1 = require("../utils/config.utils");
|
|
14
14
|
const run_all_tests_utils_1 = require("../utils/run-all-tests.utils");
|
|
15
15
|
/** Handler for running Meticulous tests in parallel using child processes */
|
|
16
|
-
const runAllTestsInParallel = async ({ config, client, testRun, apiToken, commitSha, appUrl, useAssetsSnapshottedInBaseSimulation,
|
|
16
|
+
const runAllTestsInParallel = async ({ config, client, testRun, apiToken, commitSha, appUrl, useAssetsSnapshottedInBaseSimulation, executionOptions, screenshottingOptions, parallelTasks, deflake, cachedTestRunResults, }) => {
|
|
17
17
|
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
18
18
|
const results = [...cachedTestRunResults];
|
|
19
19
|
const queue = (0, run_all_tests_utils_1.getTestsToRun)({
|
|
@@ -58,23 +58,19 @@ const runAllTestsInParallel = async ({ config, client, testRun, apiToken, commit
|
|
|
58
58
|
data: {
|
|
59
59
|
logLevel: logger.getLevel(),
|
|
60
60
|
dataDir: (0, common_1.getMeticulousLocalDataDir)(),
|
|
61
|
-
|
|
61
|
+
replayOptions: {
|
|
62
62
|
apiToken,
|
|
63
63
|
commitSha,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
simulationIdForAssets: (0, config_utils_1.getSimulationIdForAssets)(testCase, useAssetsSnapshottedInBaseSimulation),
|
|
74
|
-
accelerate,
|
|
64
|
+
testCase,
|
|
65
|
+
deflake,
|
|
66
|
+
replayTarget: (0, config_utils_1.getReplayTargetForTestCase)({
|
|
67
|
+
useAssetsSnapshottedInBaseSimulation,
|
|
68
|
+
appUrl,
|
|
69
|
+
testCase,
|
|
70
|
+
}),
|
|
71
|
+
executionOptions,
|
|
72
|
+
screenshottingOptions,
|
|
75
73
|
},
|
|
76
|
-
testCase,
|
|
77
|
-
deflake,
|
|
78
74
|
},
|
|
79
75
|
};
|
|
80
76
|
child.send(initMessage);
|
|
@@ -38,34 +38,10 @@ const main = async () => {
|
|
|
38
38
|
process.exit(1);
|
|
39
39
|
}
|
|
40
40
|
const initMessage = await waitForInitMessage();
|
|
41
|
-
const { logLevel, dataDir,
|
|
41
|
+
const { logLevel, dataDir, replayOptions } = initMessage.data;
|
|
42
42
|
logger.setLevel(logLevel);
|
|
43
43
|
(0, common_1.setMeticulousLocalDataDir)(dataDir);
|
|
44
|
-
const
|
|
45
|
-
const { sessionId, baseReplayId, options } = testCase;
|
|
46
|
-
const result = await (0, deflake_tests_handler_1.deflakeReplayCommandHandler)({
|
|
47
|
-
testCase,
|
|
48
|
-
deflake,
|
|
49
|
-
apiToken,
|
|
50
|
-
commitSha,
|
|
51
|
-
sessionId,
|
|
52
|
-
appUrl,
|
|
53
|
-
simulationIdForAssets,
|
|
54
|
-
headless,
|
|
55
|
-
devTools,
|
|
56
|
-
bypassCSP,
|
|
57
|
-
screenshot: true,
|
|
58
|
-
baseSimulationId: baseReplayId,
|
|
59
|
-
diffThreshold,
|
|
60
|
-
diffPixelThreshold,
|
|
61
|
-
save: false,
|
|
62
|
-
exitOnMismatch: false,
|
|
63
|
-
padTime,
|
|
64
|
-
shiftTime,
|
|
65
|
-
networkStubbing,
|
|
66
|
-
accelerate,
|
|
67
|
-
...options,
|
|
68
|
-
});
|
|
44
|
+
const result = await (0, deflake_tests_handler_1.deflakeReplayCommandHandler)(replayOptions);
|
|
69
45
|
const resultMessage = {
|
|
70
46
|
kind: "result",
|
|
71
47
|
data: {
|