@alwaysmeticulous/cli 2.35.0 → 2.37.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.
Files changed (45) hide show
  1. package/dist/api/replay.api.d.ts +0 -1
  2. package/dist/api/replay.api.js +1 -13
  3. package/dist/api/test-run.api.d.ts +8 -2
  4. package/dist/api/test-run.api.js +21 -3
  5. package/dist/api/types.d.ts +9 -0
  6. package/dist/commands/create-test/create-test.command.js +0 -3
  7. package/dist/commands/replay/replay.command.d.ts +5 -20
  8. package/dist/commands/replay/replay.command.js +10 -53
  9. package/dist/commands/replay/utils/compute-diff.d.ts +3 -6
  10. package/dist/commands/replay/utils/compute-diff.js +83 -18
  11. package/dist/commands/replay/utils/exit-early-if-skip-upload-env-var-set.d.ts +1 -1
  12. package/dist/commands/replay/utils/exit-early-if-skip-upload-env-var-set.js +3 -3
  13. package/dist/commands/run-all-tests/run-all-tests.command.d.ts +4 -5
  14. package/dist/commands/run-all-tests/run-all-tests.command.js +6 -9
  15. package/dist/commands/screenshot-diff/screenshot-diff.command.d.ts +6 -8
  16. package/dist/commands/screenshot-diff/screenshot-diff.command.js +50 -97
  17. package/dist/commands/screenshot-diff/utils/get-screenshot-filename.d.ts +2 -0
  18. package/dist/commands/screenshot-diff/utils/get-screenshot-filename.js +18 -0
  19. package/dist/commands/screenshot-diff/utils/get-screenshot-identifier.d.ts +2 -0
  20. package/dist/commands/screenshot-diff/utils/get-screenshot-identifier.js +24 -0
  21. package/dist/commands/screenshot-diff/utils/has-notable-differences.d.ts +2 -0
  22. package/dist/commands/screenshot-diff/utils/has-notable-differences.js +10 -0
  23. package/dist/config/config.js +2 -3
  24. package/dist/config/config.types.d.ts +1 -1
  25. package/dist/deflake-tests/deflake-tests.handler.d.ts +1 -0
  26. package/dist/deflake-tests/deflake-tests.handler.js +9 -5
  27. package/dist/index.d.ts +1 -1
  28. package/dist/index.js +1 -3
  29. package/dist/local-data/replays.d.ts +6 -1
  30. package/dist/local-data/replays.js +11 -3
  31. package/dist/local-data/screenshot-diffs.js +1 -1
  32. package/dist/local-data/serve-assets-from-simulation.js +1 -1
  33. package/dist/parallel-tests/__tests__/mock-test-results.js +3 -1
  34. package/dist/parallel-tests/__tests__/parrallel-tests.handler.spec.js +1 -1
  35. package/dist/parallel-tests/merge-test-results.js +18 -9
  36. package/dist/parallel-tests/parallel-tests.handler.js +2 -1
  37. package/dist/parallel-tests/run-all-tests.d.ts +2 -2
  38. package/dist/parallel-tests/run-all-tests.js +50 -23
  39. package/dist/parallel-tests/screenshot-diff-results.utils.d.ts +7 -0
  40. package/dist/parallel-tests/screenshot-diff-results.utils.js +20 -0
  41. package/dist/utils/config.utils.d.ts +1 -2
  42. package/dist/utils/config.utils.js +1 -10
  43. package/dist/utils/run-all-tests.utils.d.ts +0 -1
  44. package/dist/utils/run-all-tests.utils.js +3 -50
  45. package/package.json +6 -4
@@ -23,10 +23,7 @@ const execute_test_in_child_process_1 = require("./execute-test-in-child-process
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, useAssetsSnapshottedInBaseSimulation, executionOptions, screenshottingOptions, parallelTasks, deflake, maxRetriesOnFailure, cachedTestRunResults: cachedTestRunResults_, githubSummary, environment, onTestRunCreated, onTestFinished: onTestFinished_, }) => {
27
- if (appUrl != null && useAssetsSnapshottedInBaseSimulation) {
28
- throw new Error("Arguments useAssetsSnapshottedInBaseSimulation and appUrl are mutually exclusive");
29
- }
26
+ const runAllTests = async ({ testsFile, apiToken, commitSha, baseCommitSha, appUrl, executionOptions, screenshottingOptions, parallelTasks, deflake, maxRetriesOnFailure, cachedTestRunResults: cachedTestRunResults_, githubSummary, environment, baseTestRunId, onTestRunCreated, onTestFinished: onTestFinished_, }) => {
30
27
  if (deflake && maxRetriesOnFailure > 1) {
31
28
  throw new Error("Arguments deflake and maxRetriesOnFailure are mutually exclusive");
32
29
  }
@@ -44,8 +41,8 @@ const runAllTests = async ({ testsFile, apiToken, commitSha, baseCommitSha, appU
44
41
  throw new Error("Error! No test case defined");
45
42
  }
46
43
  // Only run the uncached test cases
47
- const testCases = allTestCases.filter(({ sessionId, baseReplayId, title }) => !cachedTestRunResults.find((cached) => cached.sessionId === sessionId &&
48
- cached.baseReplayId === baseReplayId &&
44
+ const testCases = allTestCases.filter(({ sessionId, baseTestRunId, title }) => !cachedTestRunResults.find((cached) => cached.sessionId === sessionId &&
45
+ cached.baseTestRunId === baseTestRunId &&
49
46
  cached.title === title));
50
47
  const meticulousSha = await (0, version_utils_1.getMeticulousVersion)();
51
48
  const replayEventsDependencies = await (0, replay_assets_1.loadReplayEventsDependencies)();
@@ -62,7 +59,6 @@ const runAllTests = async ({ testsFile, apiToken, commitSha, baseCommitSha, appU
62
59
  commitSha,
63
60
  baseCommitSha,
64
61
  appUrl,
65
- useAssetsSnapshottedInBaseSimulation,
66
62
  parallelTasks,
67
63
  deflake,
68
64
  githubSummary,
@@ -86,17 +82,19 @@ const runAllTests = async ({ testsFile, apiToken, commitSha, baseCommitSha, appU
86
82
  logger.info("");
87
83
  logger.info(`Test run URL: ${testRunUrl}`);
88
84
  logger.info("");
89
- const testsToRun = await (0, run_all_tests_utils_1.getTestsToRun)({
90
- testCases,
85
+ const testsToRun = await getTestCasesWithBaseTestRunId({
86
+ baseCommitSha,
87
+ baseTestRunId: baseTestRunId !== null && baseTestRunId !== void 0 ? baseTestRunId : null,
91
88
  client,
92
- baseCommitSha: baseCommitSha !== null && baseCommitSha !== void 0 ? baseCommitSha : null,
89
+ logger,
90
+ testCases,
93
91
  });
94
92
  const storeTestRunResults = async (status, resultsSoFar) => {
95
93
  const resultsToSendToBE = [
96
94
  ...cachedTestRunResults,
97
95
  ...resultsSoFar.map(
98
96
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
99
- ({ screenshotDiffResults, ...result }) => result),
97
+ ({ screenshotDiffResultsByBaseReplayId, ...result }) => result),
100
98
  ];
101
99
  try {
102
100
  await (0, test_run_api_1.putTestRunResults)({
@@ -127,17 +125,19 @@ const runAllTests = async ({ testsFile, apiToken, commitSha, baseCommitSha, appU
127
125
  const onTestFinished = async (progress, resultsSoFar) => {
128
126
  onProgressUpdated(progress);
129
127
  const newResult = resultsSoFar.at(-1);
130
- if ((newResult === null || newResult === void 0 ? void 0 : newResult.baseReplayId) != null) {
131
- await (0, replay_diff_api_1.createReplayDiff)({
132
- client,
133
- headReplayId: newResult.headReplayId,
134
- baseReplayId: newResult.baseReplayId,
135
- testRunId: testRun.id,
136
- data: {
137
- screenshotAssertionsOptions: screenshottingOptions,
138
- screenshotDiffResults: newResult.screenshotDiffResults,
139
- },
140
- });
128
+ if (newResult != null) {
129
+ for (const [baseReplayId, screenshotDiffResults] of Object.entries(newResult.screenshotDiffResultsByBaseReplayId)) {
130
+ await (0, replay_diff_api_1.createReplayDiff)({
131
+ client,
132
+ headReplayId: newResult.headReplayId,
133
+ baseReplayId: baseReplayId,
134
+ testRunId: testRun.id,
135
+ data: {
136
+ screenshotAssertionsOptions: screenshottingOptions,
137
+ screenshotDiffResults,
138
+ },
139
+ });
140
+ }
141
141
  }
142
142
  await storeTestRunResults("Running", resultsSoFar);
143
143
  };
@@ -146,6 +146,7 @@ const runAllTests = async ({ testsFile, apiToken, commitSha, baseCommitSha, appU
146
146
  parallelTasks,
147
147
  maxRetriesOnFailure,
148
148
  executeTest: (testCase, isRetry) => {
149
+ var _a;
149
150
  const initMessage = {
150
151
  kind: "init",
151
152
  data: {
@@ -157,7 +158,6 @@ const runAllTests = async ({ testsFile, apiToken, commitSha, baseCommitSha, appU
157
158
  testCase,
158
159
  deflake,
159
160
  replayTarget: (0, config_utils_1.getReplayTargetForTestCase)({
160
- useAssetsSnapshottedInBaseSimulation,
161
161
  appUrl,
162
162
  testCase,
163
163
  }),
@@ -165,6 +165,7 @@ const runAllTests = async ({ testsFile, apiToken, commitSha, baseCommitSha, appU
165
165
  screenshottingOptions,
166
166
  generatedBy: { type: "testRun", runId: testRun.id },
167
167
  testRunId: testRun.id,
168
+ baseTestRunId: (_a = testCase.baseTestRunId) !== null && _a !== void 0 ? _a : null,
168
169
  replayEventsDependencies,
169
170
  suppressScreenshotDiffLogging: isRetry,
170
171
  },
@@ -212,3 +213,29 @@ const runAllTests = async ({ testsFile, apiToken, commitSha, baseCommitSha, appU
212
213
  };
213
214
  };
214
215
  exports.runAllTests = runAllTests;
216
+ const getTestCasesWithBaseTestRunId = async ({ logger, client, baseCommitSha, baseTestRunId, testCases, }) => {
217
+ const defaultBaseTestRunId = baseCommitSha != null
218
+ ? await (0, test_run_api_1.getLatestTestRunId)({
219
+ client,
220
+ commitSha: baseCommitSha,
221
+ })
222
+ : null;
223
+ const testsToRun = testCases.map((test) => {
224
+ // We use the baseTestRunId specified in the test case if it exists, otherwise we use
225
+ // use the baseTestRunId specified from the CLI args if it exists, otherwise we use the
226
+ // baseTestRunId for the base commit if it exists, otherwise we use null (don't compare screenshots).
227
+ const fallbackTestRunId = baseTestRunId !== null && baseTestRunId !== void 0 ? baseTestRunId : defaultBaseTestRunId;
228
+ if (test.baseTestRunId != null || fallbackTestRunId == null) {
229
+ return test;
230
+ }
231
+ return { ...test, baseTestRunId: fallbackTestRunId };
232
+ });
233
+ if (baseCommitSha != null) {
234
+ testsToRun
235
+ .filter((test) => test.baseTestRunId == null)
236
+ .forEach((test) => {
237
+ logger.warn(`Skipping comparisons for test "${test.title}" since no result to compare against stored for base commit ${baseCommitSha}`);
238
+ });
239
+ }
240
+ return testsToRun;
241
+ };
@@ -0,0 +1,7 @@
1
+ import { ScreenshotDiffResult } from "@alwaysmeticulous/api";
2
+ import { DetailedTestCaseResult } from "../config/config.types";
3
+ export type ScreenshotDiffResultWithBaseReplayId = ScreenshotDiffResult & {
4
+ baseReplayId: string;
5
+ };
6
+ export declare const flattenScreenshotDiffResults: (testCaseResult: DetailedTestCaseResult) => ScreenshotDiffResultWithBaseReplayId[];
7
+ export declare const groupScreenshotDiffResults: (results: ScreenshotDiffResultWithBaseReplayId[]) => Record<string, ScreenshotDiffResult[]>;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.groupScreenshotDiffResults = exports.flattenScreenshotDiffResults = void 0;
4
+ const flattenScreenshotDiffResults = (testCaseResult) => {
5
+ return Object.entries(testCaseResult.screenshotDiffResultsByBaseReplayId).flatMap(([baseReplayId, diffs]) => {
6
+ return diffs.map((diff) => ({ ...diff, baseReplayId }));
7
+ });
8
+ };
9
+ exports.flattenScreenshotDiffResults = flattenScreenshotDiffResults;
10
+ const groupScreenshotDiffResults = (results) => {
11
+ const groupedResults = {};
12
+ results.forEach(({ baseReplayId, ...result }) => {
13
+ var _a;
14
+ const resultsForBaseReplayId = (_a = groupedResults[baseReplayId]) !== null && _a !== void 0 ? _a : [];
15
+ resultsForBaseReplayId.push(result);
16
+ groupedResults[baseReplayId] = resultsForBaseReplayId;
17
+ });
18
+ return groupedResults;
19
+ };
20
+ exports.groupScreenshotDiffResults = groupScreenshotDiffResults;
@@ -1,8 +1,7 @@
1
1
  import { TestCase } from "@alwaysmeticulous/api";
2
2
  import { ReplayTarget } from "@alwaysmeticulous/common/dist/types/replay.types";
3
3
  export declare const addTestCase: (testCase: TestCase) => Promise<void>;
4
- export declare const getReplayTargetForTestCase: ({ useAssetsSnapshottedInBaseSimulation, appUrl, testCase, }: {
5
- useAssetsSnapshottedInBaseSimulation: boolean;
4
+ export declare const getReplayTargetForTestCase: ({ appUrl, testCase, }: {
6
5
  appUrl: string | null;
7
6
  testCase: TestCase;
8
7
  }) => ReplayTarget;
@@ -11,7 +11,7 @@ const addTestCase = async (testCase) => {
11
11
  await (0, config_1.saveConfig)(newConfig);
12
12
  };
13
13
  exports.addTestCase = addTestCase;
14
- const getReplayTargetForTestCase = ({ useAssetsSnapshottedInBaseSimulation, appUrl, testCase, }) => {
14
+ const getReplayTargetForTestCase = ({ appUrl, testCase, }) => {
15
15
  var _a, _b, _c;
16
16
  if (((_a = testCase.options) === null || _a === void 0 ? void 0 : _a.simulationIdForAssets) != null) {
17
17
  return {
@@ -19,15 +19,6 @@ const getReplayTargetForTestCase = ({ useAssetsSnapshottedInBaseSimulation, appU
19
19
  simulationIdForAssets: (_b = testCase.options) === null || _b === void 0 ? void 0 : _b.simulationIdForAssets,
20
20
  };
21
21
  }
22
- if (useAssetsSnapshottedInBaseSimulation) {
23
- if (testCase.baseReplayId == null) {
24
- throw new Error(`--useAssetsSnapshottedInBaseSimulation flag set, but test case "${testCase.title}" does not have a baseReplayId.`);
25
- }
26
- return {
27
- type: "snapshotted-assets",
28
- simulationIdForAssets: testCase.baseReplayId,
29
- };
30
- }
31
22
  if ((_c = testCase.options) === null || _c === void 0 ? void 0 : _c.appUrl) {
32
23
  if (appUrl) {
33
24
  throw new Error(`Test cases "${testCase.title}" has an "appUrl" option but --appUrl is also provided.`);
@@ -14,4 +14,3 @@ export interface GetTestsToRunOptions {
14
14
  */
15
15
  baseCommitSha: string | null;
16
16
  }
17
- export declare const getTestsToRun: ({ testCases, client, baseCommitSha, }: GetTestsToRunOptions) => Promise<TestCase[]>;
@@ -1,12 +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.getTestsToRun = exports.sortResults = exports.mergeTestCases = void 0;
7
- const common_1 = require("@alwaysmeticulous/common");
8
- const loglevel_1 = __importDefault(require("loglevel"));
9
- const test_run_api_1 = require("../api/test-run.api");
3
+ exports.sortResults = exports.mergeTestCases = void 0;
10
4
  const mergeTestCases = (...testSuites) => {
11
5
  const seenSessionIds = new Set();
12
6
  return testSuites.flatMap((testSuite) => {
@@ -22,9 +16,9 @@ exports.mergeTestCases = mergeTestCases;
22
16
  const sortResults = ({ results: unsorted_, testCases }) => {
23
17
  const unsorted = [...unsorted_];
24
18
  const results = [];
25
- testCases.forEach(({ title, baseReplayId, sessionId }) => {
19
+ testCases.forEach(({ title, baseTestRunId, sessionId }) => {
26
20
  const idx = unsorted.findIndex((result) => result.title === title &&
27
- result.baseReplayId === baseReplayId &&
21
+ result.baseTestRunId === baseTestRunId &&
28
22
  result.sessionId === sessionId);
29
23
  if (idx == -1) {
30
24
  return;
@@ -36,44 +30,3 @@ const sortResults = ({ results: unsorted_, testCases }) => {
36
30
  return results;
37
31
  };
38
32
  exports.sortResults = sortResults;
39
- const getTestsToRun = async ({ testCases, client, baseCommitSha, }) => {
40
- const testCasesMissingBaseReplayId = testCases.filter((testCase) => testCase.baseReplayId == null);
41
- const testCasesWithBaseReplayId = testCases.flatMap((testCase) => testCase.baseReplayId == null ? [] : [testCase]);
42
- if (testCasesMissingBaseReplayId.length === 0) {
43
- return testCasesWithBaseReplayId;
44
- }
45
- const baseReplayIdBySessionId = await getBaseReplayIdsBySessionId({
46
- client,
47
- baseCommitSha,
48
- });
49
- return testCases.flatMap((test) => {
50
- if (test.baseReplayId != null) {
51
- return [test];
52
- }
53
- const baseReplayId = baseReplayIdBySessionId[test.sessionId];
54
- if (baseReplayId == null) {
55
- const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
56
- logger.warn(`Skipping comparisons for test "${test.title}" since no result to compare against stored for base commit ${baseCommitSha}`);
57
- return [test];
58
- }
59
- return [{ ...test, baseReplayId }];
60
- });
61
- };
62
- exports.getTestsToRun = getTestsToRun;
63
- const getBaseReplayIdsBySessionId = async ({ client, baseCommitSha, }) => {
64
- var _a, _b;
65
- if (!baseCommitSha) {
66
- return {};
67
- }
68
- const baseTestRun = await (0, test_run_api_1.getLatestTestRunResults)({
69
- client,
70
- commitSha: baseCommitSha,
71
- });
72
- const baseReplays = (_b = (_a = baseTestRun === null || baseTestRun === void 0 ? void 0 : baseTestRun.resultData) === null || _a === void 0 ? void 0 : _a.results) !== null && _b !== void 0 ? _b : [];
73
- const baseReplayIdBySessionId = {};
74
- // If there are multiple replays for a given session we take the last in the list
75
- baseReplays.forEach((replay) => {
76
- baseReplayIdBySessionId[replay.sessionId] = replay.headReplayId;
77
- });
78
- return baseReplayIdBySessionId;
79
- };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alwaysmeticulous/cli",
3
- "version": "2.35.0",
3
+ "version": "2.37.0",
4
4
  "description": "The Meticulous CLI",
5
5
  "license": "ISC",
6
6
  "main": "dist/index.js",
@@ -22,10 +22,11 @@
22
22
  "lint:fix": "eslint src --ext=ts,tsx,js --cache --fix",
23
23
  "cli": "node dist/main.js",
24
24
  "cli:dev": "ts-node src/main.ts",
25
+ "cli:dev-localhost": "METICULOUS_API_URL=http://localhost:3001/api/ ts-node src/main.ts",
25
26
  "test": "jest"
26
27
  },
27
28
  "dependencies": {
28
- "@alwaysmeticulous/common": "^2.35.0",
29
+ "@alwaysmeticulous/common": "^2.37.0",
29
30
  "@sentry/node": "^7.36.0",
30
31
  "@sentry/tracing": "^7.36.0",
31
32
  "adm-zip": "^0.5.9",
@@ -34,6 +35,7 @@
34
35
  "chalk": "^4.1.2",
35
36
  "cosmiconfig": "^8.0.0",
36
37
  "express": "^4.18.1",
38
+ "fast-json-stable-stringify": "^2.1.0",
37
39
  "find-free-port": "^2.0.0",
38
40
  "inquirer": "^8.2.4",
39
41
  "luxon": "^3.2.1",
@@ -43,7 +45,7 @@
43
45
  "yargs": "^17.5.1"
44
46
  },
45
47
  "devDependencies": {
46
- "@alwaysmeticulous/api": "^2.34.0",
48
+ "@alwaysmeticulous/api": "^2.37.0",
47
49
  "@types/express": "^4.17.14",
48
50
  "@types/proper-lockfile": "^4.1.2"
49
51
  },
@@ -93,5 +95,5 @@
93
95
  "coverageDirectory": "../coverage",
94
96
  "testEnvironment": "node"
95
97
  },
96
- "gitHead": "2d87c57d10bb368950a1bf7d12d2da85ab701f00"
98
+ "gitHead": "4ad32ef54767510ad29b08254603b774e4001f4b"
97
99
  }