@alwaysmeticulous/cli 2.40.0 → 2.40.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/dist/api/test-run.api.d.ts +3 -31
  2. package/dist/api/test-run.api.js +3 -96
  3. package/dist/commands/download-replay/download-replay.command.js +4 -4
  4. package/dist/commands/download-session/download-session.command.js +4 -4
  5. package/dist/commands/record/record.command.js +8 -11
  6. package/dist/commands/replay/replay.command.d.ts +3 -14
  7. package/dist/commands/replay/replay.command.js +3 -233
  8. package/dist/commands/run-all-tests/run-all-tests.command.js +5 -5
  9. package/dist/commands/show-project/show-project.command.js +2 -3
  10. package/dist/index.d.ts +0 -7
  11. package/dist/index.js +1 -13
  12. package/dist/main.js +5 -11
  13. package/dist/utils/sentry.utils.d.ts +0 -4
  14. package/dist/utils/sentry.utils.js +3 -23
  15. package/package.json +14 -32
  16. package/dist/api/client.d.ts +0 -5
  17. package/dist/api/client.js +0 -32
  18. package/dist/api/download.d.ts +0 -1
  19. package/dist/api/download.js +0 -21
  20. package/dist/api/project.api.d.ts +0 -3
  21. package/dist/api/project.api.js +0 -22
  22. package/dist/api/replay-diff.api.d.ts +0 -13
  23. package/dist/api/replay-diff.api.js +0 -32
  24. package/dist/api/replay.api.d.ts +0 -36
  25. package/dist/api/replay.api.js +0 -89
  26. package/dist/api/session.api.d.ts +0 -6
  27. package/dist/api/session.api.js +0 -45
  28. package/dist/api/types.d.ts +0 -39
  29. package/dist/api/types.js +0 -2
  30. package/dist/api/upload.d.ts +0 -1
  31. package/dist/api/upload.js +0 -18
  32. package/dist/archive/archive.d.ts +0 -4
  33. package/dist/archive/archive.js +0 -64
  34. package/dist/command-utils/common-types.d.ts +0 -20
  35. package/dist/command-utils/common-types.js +0 -2
  36. package/dist/commands/bootstrap/bootstrap.command.d.ts +0 -2
  37. package/dist/commands/bootstrap/bootstrap.command.js +0 -33
  38. package/dist/commands/create-test/create-test.command.d.ts +0 -90
  39. package/dist/commands/create-test/create-test.command.js +0 -165
  40. package/dist/commands/replay/utils/compute-diff.d.ts +0 -13
  41. package/dist/commands/replay/utils/compute-diff.js +0 -95
  42. package/dist/commands/replay/utils/exit-early-if-skip-upload-env-var-set.d.ts +0 -1
  43. package/dist/commands/replay/utils/exit-early-if-skip-upload-env-var-set.js +0 -27
  44. package/dist/commands/screenshot-diff/screenshot-diff.command.d.ts +0 -53
  45. package/dist/commands/screenshot-diff/screenshot-diff.command.js +0 -182
  46. package/dist/commands/screenshot-diff/utils/__tests__/get-screenshot-identifier.spec.d.ts +0 -1
  47. package/dist/commands/screenshot-diff/utils/__tests__/get-screenshot-identifier.spec.js +0 -25
  48. package/dist/commands/screenshot-diff/utils/get-screenshot-filename.d.ts +0 -2
  49. package/dist/commands/screenshot-diff/utils/get-screenshot-filename.js +0 -22
  50. package/dist/commands/screenshot-diff/utils/get-screenshot-identifier.d.ts +0 -2
  51. package/dist/commands/screenshot-diff/utils/get-screenshot-identifier.js +0 -39
  52. package/dist/commands/screenshot-diff/utils/has-notable-differences.d.ts +0 -2
  53. package/dist/commands/screenshot-diff/utils/has-notable-differences.js +0 -10
  54. package/dist/commands/serve/serve.command.d.ts +0 -10
  55. package/dist/commands/serve/serve.command.js +0 -31
  56. package/dist/commands/update-tests/update-tests.command.d.ts +0 -21
  57. package/dist/commands/update-tests/update-tests.command.js +0 -96
  58. package/dist/config/config.d.ts +0 -3
  59. package/dist/config/config.js +0 -69
  60. package/dist/config/config.types.d.ts +0 -16
  61. package/dist/config/config.types.js +0 -2
  62. package/dist/config/snippets.d.ts +0 -1
  63. package/dist/config/snippets.js +0 -18
  64. package/dist/errors/config.d.ts +0 -3
  65. package/dist/errors/config.js +0 -10
  66. package/dist/image/diff.utils.d.ts +0 -19
  67. package/dist/image/diff.utils.js +0 -25
  68. package/dist/image/io.utils.d.ts +0 -3
  69. package/dist/image/io.utils.js +0 -25
  70. package/dist/local-data/local-data.utils.d.ts +0 -20
  71. package/dist/local-data/local-data.utils.js +0 -92
  72. package/dist/local-data/replay-assets.d.ts +0 -3
  73. package/dist/local-data/replay-assets.js +0 -94
  74. package/dist/local-data/replays.d.ts +0 -18
  75. package/dist/local-data/replays.js +0 -87
  76. package/dist/local-data/screenshot-diffs.d.ts +0 -7
  77. package/dist/local-data/screenshot-diffs.js +0 -20
  78. package/dist/local-data/serve-assets-from-simulation.d.ts +0 -5
  79. package/dist/local-data/serve-assets-from-simulation.js +0 -52
  80. package/dist/local-data/sessions.d.ts +0 -10
  81. package/dist/local-data/sessions.js +0 -41
  82. package/dist/parallel-tests/__tests__/merge-test-results.spec.d.ts +0 -1
  83. package/dist/parallel-tests/__tests__/merge-test-results.spec.js +0 -104
  84. package/dist/parallel-tests/__tests__/mock-test-results.d.ts +0 -17
  85. package/dist/parallel-tests/__tests__/mock-test-results.js +0 -106
  86. package/dist/parallel-tests/__tests__/parrallel-tests.handler.spec.d.ts +0 -1
  87. package/dist/parallel-tests/__tests__/parrallel-tests.handler.spec.js +0 -205
  88. package/dist/parallel-tests/execute-test-in-child-process.d.ts +0 -3
  89. package/dist/parallel-tests/execute-test-in-child-process.js +0 -44
  90. package/dist/parallel-tests/merge-test-results.d.ts +0 -12
  91. package/dist/parallel-tests/merge-test-results.js +0 -106
  92. package/dist/parallel-tests/messages.types.d.ts +0 -17
  93. package/dist/parallel-tests/messages.types.js +0 -2
  94. package/dist/parallel-tests/parallel-replay.handler.d.ts +0 -3
  95. package/dist/parallel-tests/parallel-replay.handler.js +0 -56
  96. package/dist/parallel-tests/parallel-replay.types.d.ts +0 -16
  97. package/dist/parallel-tests/parallel-replay.types.js +0 -2
  98. package/dist/parallel-tests/parallel-tests.handler.d.ts +0 -14
  99. package/dist/parallel-tests/parallel-tests.handler.js +0 -194
  100. package/dist/parallel-tests/run-all-tests.d.ts +0 -73
  101. package/dist/parallel-tests/run-all-tests.js +0 -237
  102. package/dist/parallel-tests/run-all-tests.types.d.ts +0 -6
  103. package/dist/parallel-tests/run-all-tests.types.js +0 -2
  104. package/dist/parallel-tests/screenshot-diff-results.utils.d.ts +0 -7
  105. package/dist/parallel-tests/screenshot-diff-results.utils.js +0 -20
  106. package/dist/parallel-tests/task.handler.d.ts +0 -1
  107. package/dist/parallel-tests/task.handler.js +0 -86
  108. package/dist/utils/api-token.utils.d.ts +0 -1
  109. package/dist/utils/api-token.utils.js +0 -31
  110. package/dist/utils/commit-sha.utils.d.ts +0 -1
  111. package/dist/utils/commit-sha.utils.js +0 -42
  112. package/dist/utils/config.utils.d.ts +0 -7
  113. package/dist/utils/config.utils.js +0 -33
  114. package/dist/utils/github-summary.utils.d.ts +0 -5
  115. package/dist/utils/github-summary.utils.js +0 -38
  116. package/dist/utils/run-all-tests.utils.d.ts +0 -16
  117. package/dist/utils/run-all-tests.utils.js +0 -32
  118. package/dist/utils/test-run-environment.utils.d.ts +0 -2
  119. package/dist/utils/test-run-environment.utils.js +0 -25
  120. package/dist/utils/version.utils.d.ts +0 -1
  121. package/dist/utils/version.utils.js +0 -17
@@ -1,33 +0,0 @@
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.bootstrapCommand = void 0;
7
- const common_1 = require("@alwaysmeticulous/common");
8
- const chalk_1 = __importDefault(require("chalk"));
9
- const loglevel_1 = __importDefault(require("loglevel"));
10
- const command_builder_1 = require("../../command-utils/command-builder");
11
- const config_1 = require("../../config/config");
12
- const npm_set_script_utils_1 = require("../../utils/npm-set-script.utils");
13
- const handler = async () => {
14
- const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
15
- logger.info(`Setting up ${chalk_1.default.green("meticulous.json")}...`);
16
- const meticulousConfig = await (0, config_1.readConfig)();
17
- const newConfig = {
18
- ...meticulousConfig,
19
- testCases: [...(meticulousConfig.testCases || [])],
20
- };
21
- await (0, config_1.saveConfig)(newConfig);
22
- logger.info(`Setting up ${chalk_1.default.green("test:meticulous")} script...`);
23
- await (0, npm_set_script_utils_1.npmSetScript)({
24
- script: "test:meticulous",
25
- command: "meticulous run-all-tests --headless --parallelize",
26
- });
27
- };
28
- exports.bootstrapCommand = (0, command_builder_1.buildCommand)("bootstrap")
29
- .details({
30
- describe: "Bootstrap your project to use Meticulous",
31
- })
32
- .options({})
33
- .handler(handler);
@@ -1,90 +0,0 @@
1
- /// <reference types="yargs" />
2
- export declare const createTestCommand: import("yargs").CommandModule<unknown, import("yargs").InferredOptionTypes<{
3
- headless: {
4
- default: boolean;
5
- boolean: true;
6
- description: "Start browser in headless mode";
7
- };
8
- skipPauses: {
9
- description: string;
10
- boolean: true;
11
- default: true;
12
- };
13
- devTools: {
14
- readonly boolean: true;
15
- readonly description: "Open Chrome Dev Tools";
16
- readonly default: false;
17
- };
18
- bypassCSP: {
19
- readonly boolean: true;
20
- readonly description: "Enables bypass CSP in the browser (danger: this could mean you tests hit your production backend)";
21
- readonly default: false;
22
- };
23
- shiftTime: {
24
- readonly boolean: true;
25
- readonly description: "Shift time during simulation to be set as the recording time";
26
- readonly default: true;
27
- };
28
- networkStubbing: {
29
- readonly boolean: true;
30
- readonly description: "Stub network requests during replay";
31
- readonly default: true;
32
- };
33
- disableRemoteFonts: {
34
- readonly boolean: true;
35
- readonly description: "Pass the disable remote fonts flag into chromium";
36
- readonly default: false;
37
- };
38
- noSandbox: {
39
- readonly boolean: true;
40
- readonly description: "Pass the no sandbox flag into chromium";
41
- readonly default: false;
42
- };
43
- maxDurationMs: {
44
- readonly number: true;
45
- readonly description: "Maximum duration (in milliseconds) the simulation will run";
46
- };
47
- maxEventCount: {
48
- readonly number: true;
49
- readonly description: "Maximum number of events the simulation will run";
50
- };
51
- essentialFeaturesOnly: {
52
- readonly boolean: true;
53
- readonly description: "Disable any features that are non-essential for running tests/executing replays. This includes disabling recording a video of the replay, for playback in the web app. This flag is useful to reduce noise when debugging.";
54
- readonly default: false;
55
- };
56
- apiToken: {
57
- readonly string: true;
58
- };
59
- commitSha: {
60
- readonly string: true;
61
- };
62
- width: {
63
- number: true;
64
- };
65
- height: {
66
- number: true;
67
- };
68
- uploadIntervalMs: {
69
- number: true;
70
- description: string;
71
- };
72
- incognito: {
73
- boolean: true;
74
- description: string;
75
- default: boolean;
76
- };
77
- trace: {
78
- boolean: true;
79
- description: string;
80
- };
81
- moveBeforeClick: {
82
- readonly boolean: true;
83
- readonly description: "Simulate mouse movement before clicking";
84
- readonly default: true;
85
- };
86
- cookiesFile: {
87
- string: true;
88
- description: string;
89
- };
90
- }>>;
@@ -1,165 +0,0 @@
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.createTestCommand = void 0;
7
- const common_1 = require("@alwaysmeticulous/common");
8
- const chalk_1 = __importDefault(require("chalk"));
9
- const inquirer_1 = require("inquirer");
10
- const loglevel_1 = __importDefault(require("loglevel"));
11
- const command_builder_1 = require("../../command-utils/command-builder");
12
- const common_options_1 = require("../../command-utils/common-options");
13
- const config_utils_1 = require("../../utils/config.utils");
14
- const record_command_1 = require("../record/record.command");
15
- const replay_command_1 = require("../replay/replay.command");
16
- const handleTestCreation = async (replay, sessionId) => {
17
- const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
18
- const validationResponse = await (0, inquirer_1.prompt)([
19
- {
20
- type: "confirm",
21
- name: "value",
22
- message: "Does the end state screenshot match your expectation?",
23
- default: true,
24
- },
25
- ]);
26
- if (!validationResponse.value) {
27
- return;
28
- }
29
- const createTestResponse = await (0, inquirer_1.prompt)([
30
- {
31
- type: "confirm",
32
- name: "value",
33
- message: `Would you like to save this as a test to ${chalk_1.default.green("meticulous.json")}?`,
34
- default: true,
35
- },
36
- ]);
37
- if (!createTestResponse.value) {
38
- return;
39
- }
40
- const testNameResponse = await (0, inquirer_1.prompt)([
41
- {
42
- type: "input",
43
- name: "name",
44
- message: "Test name:",
45
- default: `${sessionId} | ${replay.id}`,
46
- },
47
- ]);
48
- await (0, config_utils_1.addTestCase)({
49
- title: testNameResponse.name,
50
- sessionId,
51
- });
52
- logger.info(chalk_1.default.bold.white(`Test saved to ${chalk_1.default.green("meticulous.json")}.`));
53
- };
54
- // The create-test handler combines recording a session and simulating it for
55
- // validation.
56
- const handler = async ({
57
- // Common options
58
- apiToken, commitSha, devTools, bypassCSP,
59
- // Record options
60
- width, height, uploadIntervalMs, incognito, trace,
61
- // Replay options
62
- headless, shiftTime, networkStubbing, moveBeforeClick, cookiesFile, disableRemoteFonts, noSandbox, skipPauses, }) => {
63
- const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
64
- logger.info("Creating a new Meticulous test");
65
- logger.info("Step 1: record a new test");
66
- let lastSessionId = "";
67
- const onDetectedSession = (sessionId) => {
68
- lastSessionId = sessionId;
69
- };
70
- // 1. Record
71
- const recordOptions = {
72
- apiToken,
73
- commitSha,
74
- devTools,
75
- bypassCSP,
76
- width,
77
- height,
78
- uploadIntervalMs,
79
- incognito,
80
- trace,
81
- onDetectedSession,
82
- };
83
- await (0, record_command_1.recordCommandHandler)(recordOptions);
84
- logger.debug(`lastSessionId = ${lastSessionId}`);
85
- if (!lastSessionId) {
86
- logger.error("No test was recorded!");
87
- process.exit(1);
88
- }
89
- logger.info("Step 2: validating the new test...");
90
- const replayOptions = {
91
- apiToken,
92
- commitSha,
93
- sessionId: lastSessionId,
94
- headless,
95
- devTools,
96
- bypassCSP,
97
- screenshot: true,
98
- shiftTime,
99
- disableRemoteFonts,
100
- noSandbox,
101
- networkStubbing,
102
- moveBeforeClick,
103
- cookiesFile,
104
- skipPauses,
105
- // We replay against the original recorded URL
106
- appUrl: null,
107
- simulationIdForAssets: null,
108
- // We don't try comparing to the original screenshot, so just set these to their defaults
109
- diffThreshold: common_options_1.OPTIONS.diffThreshold.default,
110
- diffPixelThreshold: common_options_1.OPTIONS.diffPixelThreshold.default,
111
- // We don't expose these options
112
- maxDurationMs: null,
113
- maxEventCount: null,
114
- storyboard: false,
115
- essentialFeaturesOnly: false,
116
- debugger: false,
117
- };
118
- const replay = await (0, replay_command_1.rawReplayCommandHandler)(replayOptions);
119
- await handleTestCreation(replay, lastSessionId);
120
- };
121
- exports.createTestCommand = (0, command_builder_1.buildCommand)("create-test")
122
- .details({
123
- describe: "Create a new test",
124
- })
125
- .options({
126
- // Common options
127
- apiToken: common_options_1.OPTIONS.apiToken,
128
- commitSha: common_options_1.OPTIONS.commitSha,
129
- // Record options
130
- width: {
131
- number: true,
132
- },
133
- height: {
134
- number: true,
135
- },
136
- uploadIntervalMs: {
137
- number: true,
138
- description: "Meticulous recording upload interval (in milliseconds)",
139
- },
140
- incognito: {
141
- boolean: true,
142
- description: "Use an incognito browsing context",
143
- default: true,
144
- },
145
- trace: {
146
- boolean: true,
147
- description: "Enable verbose logging",
148
- },
149
- // Replay options
150
- moveBeforeClick: common_options_1.OPTIONS.moveBeforeClick,
151
- cookiesFile: {
152
- string: true,
153
- description: "Path to cookies to inject before simulation",
154
- },
155
- ...common_options_1.COMMON_REPLAY_OPTIONS,
156
- headless: {
157
- ...common_options_1.COMMON_REPLAY_OPTIONS.headless,
158
- default: true,
159
- },
160
- skipPauses: {
161
- ...common_options_1.COMMON_REPLAY_OPTIONS.skipPauses,
162
- description: "Fast forward through any pauses to replay as fast as possible when replaying for the first time to create the test.",
163
- },
164
- })
165
- .handler(handler);
@@ -1,13 +0,0 @@
1
- import { ScreenshotAssertionsEnabledOptions, ScreenshotDiffResult } from "@alwaysmeticulous/api";
2
- import { AxiosInstance } from "axios";
3
- import log from "loglevel";
4
- export interface ComputeAndSaveDiffOptions {
5
- client: AxiosInstance;
6
- baseTestRunId: string;
7
- sessionId: string;
8
- headReplayId: string;
9
- tempDir: string;
10
- screenshottingOptions: ScreenshotAssertionsEnabledOptions;
11
- logger: log.Logger;
12
- }
13
- export declare const computeDiff: ({ client, baseTestRunId, sessionId, tempDir, headReplayId, screenshottingOptions, logger, }: ComputeAndSaveDiffOptions) => Promise<Record<string, ScreenshotDiffResult[]>>;
@@ -1,95 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.computeDiff = void 0;
4
- const promises_1 = require("fs/promises");
5
- const path_1 = require("path");
6
- const download_1 = require("../../../api/download");
7
- const test_run_api_1 = require("../../../api/test-run.api");
8
- const replays_1 = require("../../../local-data/replays");
9
- const screenshot_diff_command_1 = require("../../screenshot-diff/screenshot-diff.command");
10
- const get_screenshot_filename_1 = require("../../screenshot-diff/utils/get-screenshot-filename");
11
- const computeDiff = async ({ client, baseTestRunId, sessionId, tempDir, headReplayId, screenshottingOptions, logger, }) => {
12
- var _a;
13
- logger.info(`Diffing screenshots against replays of session ${sessionId} in test run ${baseTestRunId}`);
14
- const baseScreenshotsDir = (0, path_1.join)((0, replays_1.getScreenshotsDir)(tempDir), "base-screenshots");
15
- await (0, promises_1.mkdir)(baseScreenshotsDir, { recursive: true });
16
- const baseScreenshots = await (0, test_run_api_1.getBaseScreenshots)({
17
- client,
18
- testRunId: baseTestRunId,
19
- sessionId,
20
- });
21
- const screenshotIdentifiersByBaseReplayId = groupByBaseReplayId(baseScreenshots);
22
- await downloadScreenshots(baseScreenshots, baseScreenshotsDir);
23
- logger.debug(`Downloaded ${baseScreenshots.length} base screenshots to ${baseScreenshotsDir}}`);
24
- const headReplayScreenshotsDir = (0, replays_1.getScreenshotsDir)(tempDir);
25
- const headReplayScreenshots = await (0, replays_1.getScreenshotFiles)(headReplayScreenshotsDir);
26
- const screenshotDiffResultsByBaseReplayId = {};
27
- // Diff the screenshots for each base replay
28
- for (const baseReplayId of screenshotIdentifiersByBaseReplayId.keys()) {
29
- const baseScreenshotIdentifiers = (_a = screenshotIdentifiersByBaseReplayId.get(baseReplayId)) !== null && _a !== void 0 ? _a : [];
30
- const baseReplayScreenshots = baseScreenshotIdentifiers.map((identifier) => ({
31
- identifier,
32
- fileName: (0, get_screenshot_filename_1.getScreenshotFilename)(identifier),
33
- }));
34
- const resultsForBaseReplay = await (0, screenshot_diff_command_1.diffScreenshots)({
35
- baseReplayId,
36
- headReplayId,
37
- baseScreenshotsDir: baseScreenshotsDir,
38
- headScreenshotsDir: headReplayScreenshotsDir,
39
- headReplayScreenshots,
40
- baseReplayScreenshots,
41
- diffOptions: screenshottingOptions.diffOptions,
42
- });
43
- screenshotDiffResultsByBaseReplayId[baseReplayId] = resultsForBaseReplay;
44
- }
45
- (0, screenshot_diff_command_1.logDifferences)({
46
- results: Object.values(screenshotDiffResultsByBaseReplayId).flat(),
47
- diffOptions: screenshottingOptions.diffOptions,
48
- logger,
49
- });
50
- // Delete base screenshots directory and all files inside it
51
- // (we don't want to upload it to the backend)
52
- await deleteScreenshotsDirectory(baseScreenshotsDir);
53
- return screenshotDiffResultsByBaseReplayId;
54
- };
55
- exports.computeDiff = computeDiff;
56
- const deleteScreenshotsDirectory = async (directory) => {
57
- const files = await (0, promises_1.opendir)(directory);
58
- for await (const file of files) {
59
- if (file.isFile()) {
60
- await (0, promises_1.rm)((0, path_1.join)(directory, file.name));
61
- }
62
- else {
63
- throw new Error(`Expected screenshots directory to only contain screenshot files, but it contained: ${file.name}`);
64
- }
65
- }
66
- await (0, promises_1.rmdir)(directory);
67
- };
68
- const groupByBaseReplayId = (screenshots) => {
69
- const screenshotIdentifiersByBaseReplayId = new Map();
70
- screenshots.forEach(({ replayId, screenshotIdentifier }) => {
71
- var _a;
72
- const screenshotIdentifiers = (_a = screenshotIdentifiersByBaseReplayId.get(replayId)) !== null && _a !== void 0 ? _a : [];
73
- screenshotIdentifiers.push(screenshotIdentifier);
74
- screenshotIdentifiersByBaseReplayId.set(replayId, screenshotIdentifiers);
75
- });
76
- return screenshotIdentifiersByBaseReplayId;
77
- };
78
- const downloadScreenshots = async (screenshots, directory) => {
79
- // Download in batches of 20
80
- const screenshotBatches = chunk(screenshots, 20);
81
- for (const screenshotBatch of screenshotBatches) {
82
- const screenshotDownloads = screenshotBatch.map((screenshot) => {
83
- const filePath = (0, path_1.join)(directory, (0, get_screenshot_filename_1.getScreenshotFilename)(screenshot.screenshotIdentifier));
84
- return (0, download_1.downloadFile)(screenshot.screenshotUrl, filePath);
85
- });
86
- await Promise.all(screenshotDownloads);
87
- }
88
- };
89
- const chunk = (array, size) => {
90
- const chunkedArray = [];
91
- for (let i = 0; i < array.length; i += size) {
92
- chunkedArray.push(array.slice(i, i + size));
93
- }
94
- return chunkedArray;
95
- };
@@ -1 +0,0 @@
1
- export declare const exitEarlyIfSkipUploadEnvVarSet: (baseTestRunId: string | null | undefined) => void;
@@ -1,27 +0,0 @@
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.exitEarlyIfSkipUploadEnvVarSet = void 0;
7
- const common_1 = require("@alwaysmeticulous/common");
8
- const loglevel_1 = __importDefault(require("loglevel"));
9
- const METICULOUS_SKIP_UPLOAD_ENV_VAR = "METICULOUS_SKIP_UPLOAD";
10
- // Uploading the zip can take a long time, so we expose a env variable to support skipping it
11
- // We don't expose this as a first-class CLI option because we don't want to pollute the list of CLI options
12
- const exitEarlyIfSkipUploadEnvVarSet = (baseTestRunId) => {
13
- if (!shouldSkipUpload()) {
14
- return;
15
- }
16
- if (baseTestRunId != null) {
17
- throw new Error(`Cannot specify baseTestRunId and compare to base results when ${METICULOUS_SKIP_UPLOAD_ENV_VAR} is set to true`);
18
- }
19
- const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
20
- logger.info(`Skipping upload / exiting early since ${METICULOUS_SKIP_UPLOAD_ENV_VAR} is set to true`);
21
- process.exit(0);
22
- };
23
- exports.exitEarlyIfSkipUploadEnvVarSet = exitEarlyIfSkipUploadEnvVarSet;
24
- const shouldSkipUpload = () => {
25
- var _a;
26
- return (((_a = process.env[METICULOUS_SKIP_UPLOAD_ENV_VAR]) !== null && _a !== void 0 ? _a : "").toLowerCase() === "true");
27
- };
@@ -1,53 +0,0 @@
1
- /// <reference types="yargs" />
2
- import { ScreenshotDiffOptions, ScreenshotDiffResult } from "@alwaysmeticulous/api";
3
- import log from "loglevel";
4
- import { IdentifiedScreenshotFile } from "../../local-data/replays";
5
- export declare const diffScreenshots: ({ headReplayId, baseReplayId, baseScreenshotsDir, headScreenshotsDir, baseReplayScreenshots, headReplayScreenshots, diffOptions, }: {
6
- baseReplayId: string;
7
- headReplayId: string;
8
- baseScreenshotsDir: string;
9
- headScreenshotsDir: string;
10
- baseReplayScreenshots: IdentifiedScreenshotFile[];
11
- headReplayScreenshots: IdentifiedScreenshotFile[];
12
- diffOptions: ScreenshotDiffOptions;
13
- }) => Promise<ScreenshotDiffResult[]>;
14
- export declare const logDifferences: ({ results, diffOptions, logger, }: {
15
- results: ScreenshotDiffResult[];
16
- diffOptions: ScreenshotDiffOptions;
17
- logger: log.Logger;
18
- }) => void;
19
- export type ScreenshotDiffsSummary = HasDiffsScreenshotDiffsResult | NoDiffsScreenshotDiffsResult;
20
- export interface HasDiffsScreenshotDiffsResult {
21
- hasDiffs: true;
22
- summaryMessage: string;
23
- baseReplayId: string;
24
- headReplayId: string;
25
- }
26
- export interface NoDiffsScreenshotDiffsResult {
27
- hasDiffs: false;
28
- }
29
- export declare const screenshotDiffCommand: import("yargs").CommandModule<unknown, import("yargs").InferredOptionTypes<{
30
- apiToken: {
31
- string: true;
32
- };
33
- baseSimulationId: {
34
- string: true;
35
- demandOption: true;
36
- alias: string;
37
- };
38
- headSimulationId: {
39
- string: true;
40
- demandOption: true;
41
- alias: string;
42
- };
43
- threshold: {
44
- readonly number: true;
45
- readonly description: "Acceptable maximum proportion of changed pixels, between 0 and 1. If this proportion is exceeded then the test will fail.";
46
- readonly default: 0.01;
47
- };
48
- pixelThreshold: {
49
- readonly number: true;
50
- readonly description: "A number between 0 and 1. Color/brightness differences in individual pixels will be ignored if the difference is less than this threshold. A value of 1.0 would accept any difference in color, while a value of 0.0 would accept no difference in color.";
51
- readonly default: 0.01;
52
- };
53
- }>>;
@@ -1,182 +0,0 @@
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.screenshotDiffCommand = exports.logDifferences = exports.diffScreenshots = void 0;
7
- const path_1 = require("path");
8
- const common_1 = require("@alwaysmeticulous/common");
9
- const fast_json_stable_stringify_1 = __importDefault(require("fast-json-stable-stringify"));
10
- const loglevel_1 = __importDefault(require("loglevel"));
11
- const client_1 = require("../../api/client");
12
- const command_builder_1 = require("../../command-utils/command-builder");
13
- const common_options_1 = require("../../command-utils/common-options");
14
- const diff_utils_1 = require("../../image/diff.utils");
15
- const io_utils_1 = require("../../image/io.utils");
16
- const replays_1 = require("../../local-data/replays");
17
- const screenshot_diffs_1 = require("../../local-data/screenshot-diffs");
18
- const has_notable_differences_1 = require("./utils/has-notable-differences");
19
- const diffScreenshots = async ({ headReplayId, baseReplayId, baseScreenshotsDir, headScreenshotsDir, baseReplayScreenshots, headReplayScreenshots, diffOptions, }) => {
20
- const { diffThreshold, diffPixelThreshold } = diffOptions;
21
- const headReplayScreenshotsByIdentifier = new Map(headReplayScreenshots.map((screenshot) => [
22
- (0, fast_json_stable_stringify_1.default)(screenshot.identifier),
23
- screenshot,
24
- ]));
25
- const baseReplayScreenshotsByIdentifier = new Map(baseReplayScreenshots.map((screenshot) => [
26
- (0, fast_json_stable_stringify_1.default)(screenshot.identifier),
27
- screenshot,
28
- ]));
29
- const missingHeadImages = new Set(baseReplayScreenshots.filter((file) => !headReplayScreenshotsByIdentifier.has((0, fast_json_stable_stringify_1.default)(file.identifier))));
30
- const missingHeadImagesResults = Array.from(missingHeadImages).flatMap(({ identifier, fileName }) => {
31
- return [
32
- {
33
- identifier,
34
- outcome: "missing-head",
35
- baseScreenshotFile: `screenshots/${fileName}`,
36
- },
37
- ];
38
- });
39
- const diffAgainstBase = async (headScreenshot) => {
40
- const baseScreenshot = baseReplayScreenshotsByIdentifier.get((0, fast_json_stable_stringify_1.default)(headScreenshot.identifier));
41
- if (baseScreenshot == null) {
42
- return [
43
- {
44
- identifier: headScreenshot.identifier,
45
- outcome: "missing-base",
46
- headScreenshotFile: `screenshots/${headScreenshot.fileName}`,
47
- },
48
- ];
49
- }
50
- const baseScreenshotFile = (0, path_1.join)(baseScreenshotsDir, baseScreenshot.fileName);
51
- const headScreenshotFile = (0, path_1.join)(headScreenshotsDir, headScreenshot.fileName);
52
- const baseScreenshotContents = await (0, io_utils_1.readPng)(baseScreenshotFile);
53
- const headScreenshotContents = await (0, io_utils_1.readPng)(headScreenshotFile);
54
- if (baseScreenshotContents.width !== headScreenshotContents.width ||
55
- baseScreenshotContents.height !== headScreenshotContents.height) {
56
- return [
57
- {
58
- identifier: headScreenshot.identifier,
59
- outcome: "different-size",
60
- baseScreenshotFile: `screenshots/${baseScreenshot.fileName}`,
61
- headScreenshotFile: `screenshots/${headScreenshot.fileName}`,
62
- baseWidth: baseScreenshotContents.width,
63
- baseHeight: baseScreenshotContents.height,
64
- headWidth: headScreenshotContents.width,
65
- headHeight: headScreenshotContents.height,
66
- },
67
- ];
68
- }
69
- const comparisonResult = (0, diff_utils_1.compareImages)({
70
- base: baseScreenshotContents,
71
- head: headScreenshotContents,
72
- pixelThreshold: diffPixelThreshold,
73
- });
74
- await (0, screenshot_diffs_1.writeScreenshotDiff)({
75
- baseReplayId,
76
- headReplayId,
77
- screenshotFileName: headScreenshot.fileName,
78
- diff: comparisonResult.diff,
79
- });
80
- return [
81
- {
82
- identifier: headScreenshot.identifier,
83
- outcome: comparisonResult.mismatchFraction > diffThreshold
84
- ? "diff"
85
- : "no-diff",
86
- baseScreenshotFile: `screenshots/${baseScreenshot.fileName}`,
87
- headScreenshotFile: `screenshots/${headScreenshot.fileName}`,
88
- width: baseScreenshotContents.width,
89
- height: baseScreenshotContents.height,
90
- mismatchPixels: comparisonResult.mismatchPixels,
91
- mismatchFraction: comparisonResult.mismatchFraction,
92
- },
93
- ];
94
- };
95
- const headDiffResults = (await Promise.all(headReplayScreenshots.map(diffAgainstBase))).flat();
96
- return [...missingHeadImagesResults, ...headDiffResults];
97
- };
98
- exports.diffScreenshots = diffScreenshots;
99
- const logDifferences = ({ results, diffOptions, logger, }) => {
100
- const missingHeadImagesResults = results.flatMap((result) => result.outcome === "missing-head" ? [result] : []);
101
- if (missingHeadImagesResults.length) {
102
- const message = `Head replay is missing screenshots: ${missingHeadImagesResults
103
- .map(({ baseScreenshotFile }) => (0, path_1.basename)(baseScreenshotFile))
104
- .sort()} => FAIL!`;
105
- logger.info(message);
106
- }
107
- const missingBaseImagesResults = results.flatMap((result) => result.outcome === "missing-base" ? [result] : []);
108
- if (missingHeadImagesResults.length) {
109
- const message = `Notice: Base replay is missing screenshots: ${missingBaseImagesResults
110
- .map(({ headScreenshotFile }) => (0, path_1.basename)(headScreenshotFile))
111
- .sort()}`;
112
- logger.info(message);
113
- }
114
- results.forEach((result) => {
115
- const { outcome } = result;
116
- if (outcome === "different-size") {
117
- const message = `Screenshots ${(0, path_1.basename)(result.headScreenshotFile)} have different sizes => FAIL!`;
118
- logger.info(message);
119
- }
120
- if (outcome === "diff" || outcome === "no-diff") {
121
- const mismatch = (result.mismatchFraction * 100).toFixed(3);
122
- const threshold = (diffOptions.diffThreshold * 100).toFixed(3);
123
- const message = `${mismatch}% pixel mismatch for screenshot ${(0, path_1.basename)(result.headScreenshotFile)} (threshold is ${threshold}%) => ${outcome === "no-diff" ? "PASS" : "FAIL!"}`;
124
- logger.info(message);
125
- }
126
- });
127
- };
128
- exports.logDifferences = logDifferences;
129
- const handler = async ({ apiToken, baseSimulationId: baseReplayId, headSimulationId: headReplayId, threshold, pixelThreshold, }) => {
130
- const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
131
- const client = (0, client_1.createClient)({ apiToken });
132
- await (0, replays_1.getOrFetchReplay)(client, baseReplayId);
133
- await (0, replays_1.getOrFetchReplayArchive)(client, baseReplayId);
134
- await (0, replays_1.getOrFetchReplay)(client, headReplayId);
135
- await (0, replays_1.getOrFetchReplayArchive)(client, headReplayId);
136
- const baseScreenshotsDir = (0, replays_1.getScreenshotsDir)((0, replays_1.getReplayDir)(baseReplayId));
137
- const headScreenshotsDir = (0, replays_1.getScreenshotsDir)((0, replays_1.getReplayDir)(headReplayId));
138
- const diffOptions = {
139
- diffThreshold: threshold,
140
- diffPixelThreshold: pixelThreshold,
141
- };
142
- const baseReplayScreenshots = await (0, replays_1.getScreenshotFiles)(baseScreenshotsDir);
143
- const headReplayScreenshots = await (0, replays_1.getScreenshotFiles)(headScreenshotsDir);
144
- const results = await (0, exports.diffScreenshots)({
145
- baseReplayId,
146
- headReplayId,
147
- baseScreenshotsDir,
148
- headScreenshotsDir,
149
- baseReplayScreenshots,
150
- headReplayScreenshots,
151
- diffOptions,
152
- });
153
- logger.debug(results);
154
- (0, exports.logDifferences)({
155
- results,
156
- diffOptions,
157
- logger,
158
- });
159
- if ((0, has_notable_differences_1.hasNotableDifferences)(results)) {
160
- process.exit(1);
161
- }
162
- };
163
- exports.screenshotDiffCommand = (0, command_builder_1.buildCommand)("screenshot-diff")
164
- .details({ describe: "Diff two replay screenshots" })
165
- .options({
166
- apiToken: {
167
- string: true,
168
- },
169
- baseSimulationId: {
170
- string: true,
171
- demandOption: true,
172
- alias: "baseReplayId",
173
- },
174
- headSimulationId: {
175
- string: true,
176
- demandOption: true,
177
- alias: "headReplayId",
178
- },
179
- threshold: common_options_1.SCREENSHOT_DIFF_OPTIONS.diffThreshold,
180
- pixelThreshold: common_options_1.SCREENSHOT_DIFF_OPTIONS.diffPixelThreshold,
181
- })
182
- .handler(handler);