@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,25 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const get_screenshot_identifier_1 = require("../get-screenshot-identifier");
4
- describe("get-screenshot-identifier", () => {
5
- it("can parse identifiers without version numbers", () => {
6
- expect((0, get_screenshot_identifier_1.getScreenshotIdentifier)("final-state.png")).toEqual({
7
- type: "end-state",
8
- });
9
- expect((0, get_screenshot_identifier_1.getScreenshotIdentifier)("screenshot-after-event-7.png")).toEqual({
10
- type: "after-event",
11
- eventNumber: 7,
12
- });
13
- });
14
- it("can parse identifiers with version numbers", () => {
15
- expect((0, get_screenshot_identifier_1.getScreenshotIdentifier)("final-state-v2.png")).toEqual({
16
- type: "end-state",
17
- logicVersion: 2,
18
- });
19
- expect((0, get_screenshot_identifier_1.getScreenshotIdentifier)("screenshot-after-event-7-v2.png")).toEqual({
20
- type: "after-event",
21
- eventNumber: 7,
22
- logicVersion: 2,
23
- });
24
- });
25
- });
@@ -1,2 +0,0 @@
1
- import { ScreenshotIdentifier } from "@alwaysmeticulous/api";
2
- export declare const getScreenshotFilename: (identifier: ScreenshotIdentifier) => string;
@@ -1,22 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getScreenshotFilename = void 0;
4
- // Note: ideally this should match the filenames produced by the screenshotting code
5
- // in replay-node, and in https://github.com/alwaysmeticulous/meticulous-sdk/blob/395af4394dc51d9b51ba1136fc26b23fcbba5604/packages/replayer/src/screenshot.utils.ts#L42
6
- const getScreenshotFilename = (identifier) => {
7
- if (identifier.type === "end-state") {
8
- return identifier.logicVersion == null
9
- ? "final-state.png"
10
- : `final-state-v${identifier.logicVersion}.png`;
11
- }
12
- else if (identifier.type === "after-event") {
13
- const eventIndexStr = identifier.eventNumber.toString().padStart(5, "0");
14
- return identifier.logicVersion == null
15
- ? `screenshot-after-event-${eventIndexStr}.png`
16
- : `screenshot-after-event-${eventIndexStr}-v${identifier.logicVersion}.png`;
17
- }
18
- else {
19
- throw new Error("Unexpected screenshot identifier: " + JSON.stringify(identifier));
20
- }
21
- };
22
- exports.getScreenshotFilename = getScreenshotFilename;
@@ -1,2 +0,0 @@
1
- import { ScreenshotIdentifier } from "@alwaysmeticulous/api";
2
- export declare const getScreenshotIdentifier: (filename: string) => ScreenshotIdentifier | undefined;
@@ -1,39 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getScreenshotIdentifier = void 0;
4
- const path_1 = require("path");
5
- const getScreenshotIdentifier = (filename) => {
6
- var _a;
7
- const name = (0, path_1.basename)(filename);
8
- if (name === "final-state.png") {
9
- return {
10
- type: "end-state",
11
- };
12
- }
13
- if (name.startsWith("final-state-v")) {
14
- const match = name.match(/^final-state-v(\d+)[.]png$/);
15
- const logicVersionNumber = match ? parseInt(match[1], 10) : undefined;
16
- if (match && logicVersionNumber != null && !isNaN(logicVersionNumber)) {
17
- return {
18
- type: "end-state",
19
- logicVersion: logicVersionNumber,
20
- };
21
- }
22
- }
23
- if (name.startsWith("screenshot-after-event")) {
24
- const match = (_a = name.match(/^(?:.*)-(\d+)-v(\d+)?[.]png$/)) !== null && _a !== void 0 ? _a : name.match(/^(?:.*)-(\d+)[.]png$/);
25
- const eventNumber = match ? parseInt(match[1], 10) : undefined;
26
- const logicVersionNumber = match ? parseInt(match[2], 10) : undefined;
27
- if (match && eventNumber != null && !isNaN(eventNumber)) {
28
- return {
29
- type: "after-event",
30
- eventNumber,
31
- ...(logicVersionNumber != null && !isNaN(logicVersionNumber)
32
- ? { logicVersion: logicVersionNumber }
33
- : {}),
34
- };
35
- }
36
- }
37
- return undefined;
38
- };
39
- exports.getScreenshotIdentifier = getScreenshotIdentifier;
@@ -1,2 +0,0 @@
1
- import { ScreenshotDiffResult } from "@alwaysmeticulous/api";
2
- export declare const hasNotableDifferences: (screenshotDiffResults: ScreenshotDiffResult[]) => boolean;
@@ -1,10 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.hasNotableDifferences = void 0;
4
- const hasNotableDifferences = (screenshotDiffResults) => {
5
- // Note: we ignore flakes & missing-bases here
6
- return screenshotDiffResults.some((diff) => diff.outcome === "diff" ||
7
- diff.outcome === "missing-head" ||
8
- diff.outcome === "different-size");
9
- };
10
- exports.hasNotableDifferences = hasNotableDifferences;
@@ -1,10 +0,0 @@
1
- /// <reference types="yargs" />
2
- export declare const serveCommand: import("yargs").CommandModule<unknown, import("yargs").InferredOptionTypes<{
3
- apiToken: {
4
- string: true;
5
- };
6
- replayId: {
7
- string: true;
8
- demandOption: true;
9
- };
10
- }>>;
@@ -1,31 +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.serveCommand = void 0;
7
- const common_1 = require("@alwaysmeticulous/common");
8
- const loglevel_1 = __importDefault(require("loglevel"));
9
- const client_1 = require("../../api/client");
10
- const command_builder_1 = require("../../command-utils/command-builder");
11
- const serve_assets_from_simulation_1 = require("../../local-data/serve-assets-from-simulation");
12
- const handler = async ({ apiToken, replayId, }) => {
13
- const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
14
- const client = (0, client_1.createClient)({ apiToken });
15
- const { url } = await (0, serve_assets_from_simulation_1.serveAssetsFromSimulation)(client, replayId);
16
- logger.info(`Serving assets at url ${url}`);
17
- };
18
- exports.serveCommand = (0, command_builder_1.buildCommand)("serve")
19
- .details({
20
- describe: "Spin up a localhost server to serve the assets that were snapshotted when running a particular replay",
21
- })
22
- .options({
23
- apiToken: {
24
- string: true,
25
- },
26
- replayId: {
27
- string: true,
28
- demandOption: true,
29
- },
30
- })
31
- .handler(handler);
@@ -1,21 +0,0 @@
1
- /// <reference types="yargs" />
2
- export declare const updateTestsCommand: import("yargs").CommandModule<unknown, import("yargs").InferredOptionTypes<{
3
- apiToken: {
4
- string: true;
5
- };
6
- testRunId: {
7
- string: true;
8
- demandOption: true;
9
- description: string;
10
- };
11
- accept: {
12
- string: true;
13
- array: true;
14
- description: string;
15
- };
16
- acceptAll: {
17
- boolean: true;
18
- description: string;
19
- conflicts: string;
20
- };
21
- }>>;
@@ -1,96 +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.updateTestsCommand = void 0;
7
- const common_1 = require("@alwaysmeticulous/common");
8
- const loglevel_1 = __importDefault(require("loglevel"));
9
- const client_1 = require("../../api/client");
10
- const test_run_api_1 = require("../../api/test-run.api");
11
- const command_builder_1 = require("../../command-utils/command-builder");
12
- const config_1 = require("../../config/config");
13
- const handler = async ({ apiToken, testRunId, accept: accept_, acceptAll: acceptAll_, }) => {
14
- var _a;
15
- const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
16
- const client = (0, client_1.createClient)({ apiToken });
17
- const config = await (0, config_1.readConfig)();
18
- const testCases = config.testCases || [];
19
- if (!testCases.length) {
20
- logger.error("Error! No test case defined");
21
- process.exit(1);
22
- }
23
- const testRun = await (0, test_run_api_1.getTestRun)({ client, testRunId });
24
- if (!testRun) {
25
- logger.error("Error: Could not retrieve test run. Is the API token correct?");
26
- process.exit(1);
27
- }
28
- if (testRun.status === "Success") {
29
- logger.info("Test run successful, no tests to updates");
30
- return;
31
- }
32
- if (testRun.status === "Running") {
33
- logger.error("Error: Tests are still running");
34
- process.exit(1);
35
- }
36
- const testRunResults = ((_a = testRun.resultData) === null || _a === void 0 ? void 0 : _a.results) || [];
37
- const accept = accept_ || [];
38
- const acceptAll = acceptAll_ || false;
39
- if (!acceptAll && !accept.length) {
40
- throw new Error("One of --accept or --acceptAll needs to be specified.");
41
- }
42
- const testRunResultsBySessionId = new Map();
43
- testRunResults.forEach((result) => {
44
- testRunResultsBySessionId.set(result.sessionId, result);
45
- });
46
- const newTestCases = testCases.map((testCase) => {
47
- const { sessionId } = testCase;
48
- const matched = testRunResultsBySessionId.get(sessionId);
49
- if (!matched) {
50
- logger.warn(`WARNING: ${sessionId} not found in test run`);
51
- return testCase;
52
- }
53
- if (matched.result === "pass" ||
54
- (!acceptAll &&
55
- !accept.find((replayId) => replayId === matched.headReplayId))) {
56
- return testCase;
57
- }
58
- const headReplayId = matched.headReplayId || "";
59
- if (!headReplayId) {
60
- logger.warn(`WARNING: ${sessionId} has no new replay id`);
61
- return testCase;
62
- }
63
- const newTestCase = { ...testCase, baseReplayId: headReplayId };
64
- return newTestCase;
65
- });
66
- const newConfig = {
67
- ...config,
68
- testCases: newTestCases,
69
- };
70
- await (0, config_1.saveConfig)(newConfig);
71
- };
72
- exports.updateTestsCommand = (0, command_builder_1.buildCommand)("update-tests")
73
- .details({
74
- describe: "Updates test cases",
75
- })
76
- .options({
77
- apiToken: {
78
- string: true,
79
- },
80
- testRunId: {
81
- string: true,
82
- demandOption: true,
83
- description: "Test run id to fix",
84
- },
85
- accept: {
86
- string: true,
87
- array: true,
88
- description: "Replay ids to accept as valid",
89
- },
90
- acceptAll: {
91
- boolean: true,
92
- description: "Accept all failing tests as valid",
93
- conflicts: "accept",
94
- },
95
- })
96
- .handler(handler);
@@ -1,3 +0,0 @@
1
- import { MeticulousCliConfig } from "./config.types";
2
- export declare const readConfig: (configFilePath?: string) => Promise<MeticulousCliConfig>;
3
- export declare const saveConfig: (config: MeticulousCliConfig) => Promise<void>;
@@ -1,69 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.saveConfig = exports.readConfig = void 0;
4
- const promises_1 = require("fs/promises");
5
- const path_1 = require("path");
6
- const process_1 = require("process");
7
- const cosmiconfig_1 = require("cosmiconfig");
8
- const METICULOUS_CONFIG_FILE = "meticulous.json";
9
- const findConfig = async () => {
10
- const explorer = (0, cosmiconfig_1.cosmiconfig)("meticulous", {
11
- searchPlaces: [METICULOUS_CONFIG_FILE],
12
- });
13
- const loaded = await explorer.search((0, process_1.cwd)());
14
- return (loaded === null || loaded === void 0 ? void 0 : loaded.filepath) || "";
15
- };
16
- let configFilePath = "";
17
- const getConfigFilePath = async () => {
18
- if (configFilePath) {
19
- return configFilePath;
20
- }
21
- configFilePath = (await findConfig()) || (0, path_1.join)((0, process_1.cwd)(), METICULOUS_CONFIG_FILE);
22
- return configFilePath;
23
- };
24
- const validateReplayOptions = (prevOptions) => {
25
- const { appUrl, diffThreshold, diffPixelThreshold, moveBeforeClick, simulationIdForAssets, } = prevOptions;
26
- return {
27
- ...(appUrl != null ? { appUrl } : {}),
28
- ...(diffThreshold != null ? { diffThreshold } : {}),
29
- ...(diffPixelThreshold != null ? { diffPixelThreshold } : {}),
30
- ...(moveBeforeClick != null ? { moveBeforeClick } : {}),
31
- ...(simulationIdForAssets != null ? { simulationIdForAssets } : {}),
32
- };
33
- };
34
- const validateConfig = (prevConfig) => {
35
- const { testCases, ...rest } = prevConfig;
36
- const nextTestCases = (testCases || [])
37
- .map(({ title, sessionId, options }) => ({
38
- title: typeof title === "string" ? title : "",
39
- sessionId: typeof sessionId === "string" ? sessionId : "",
40
- ...(options ? { options: validateReplayOptions(options) } : {}),
41
- }))
42
- .map(({ title, ...rest }) => ({
43
- title: title || `${rest.sessionId}`,
44
- ...rest,
45
- }))
46
- .filter(({ sessionId }) => sessionId);
47
- return { ...rest, testCases: nextTestCases };
48
- };
49
- const readConfig = async (configFilePath) => {
50
- const filePath = configFilePath !== null && configFilePath !== void 0 ? configFilePath : (await getConfigFilePath());
51
- const configStr = await (0, promises_1.readFile)(filePath, "utf-8").catch((error) => {
52
- // Use an empty config object if there is no config file
53
- if (configFilePath == null &&
54
- error instanceof Error &&
55
- error.code === "ENOENT") {
56
- return "{}";
57
- }
58
- throw error;
59
- });
60
- const config = JSON.parse(configStr);
61
- return validateConfig(config);
62
- };
63
- exports.readConfig = readConfig;
64
- const saveConfig = async (config) => {
65
- const filePath = await getConfigFilePath();
66
- const configStr = JSON.stringify(config, null, 2);
67
- await (0, promises_1.writeFile)(filePath, configStr);
68
- };
69
- exports.saveConfig = saveConfig;
@@ -1,16 +0,0 @@
1
- import { ScreenshotDiffResult, TestCase } from "@alwaysmeticulous/api";
2
- export interface MeticulousCliConfig {
3
- testCases?: TestCase[];
4
- }
5
- export interface TestCaseResult extends TestCase {
6
- headReplayId: string;
7
- /**
8
- * A test case is marked as a flake if there were screenshot comparison failures,
9
- * but for every one of those failures regenerating the screenshot on head sometimes gave
10
- * a different screenshot to the original screenshot taken on head.
11
- */
12
- result: "pass" | "fail" | "flake";
13
- }
14
- export interface DetailedTestCaseResult extends TestCaseResult {
15
- screenshotDiffResultsByBaseReplayId: Record<string, ScreenshotDiffResult[]>;
16
- }
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
@@ -1 +0,0 @@
1
- export declare const getSnippetsBaseUrl: () => string;
@@ -1,18 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getSnippetsBaseUrl = void 0;
4
- const common_1 = require("@alwaysmeticulous/common");
5
- const config_1 = require("../errors/config");
6
- const getSnippetsBaseUrl = () => {
7
- const baseUrl = process.env["METICULOUS_SNIPPETS_BASE_URL"] || common_1.BASE_SNIPPETS_URL;
8
- try {
9
- return new URL(baseUrl).href;
10
- }
11
- catch (e) {
12
- if (e instanceof TypeError) {
13
- throw new config_1.ConfigurationError(`Invalid base snippets URL: ${baseUrl}`);
14
- }
15
- throw e;
16
- }
17
- };
18
- exports.getSnippetsBaseUrl = getSnippetsBaseUrl;
@@ -1,3 +0,0 @@
1
- export declare class ConfigurationError extends Error {
2
- constructor(message: string);
3
- }
@@ -1,10 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ConfigurationError = void 0;
4
- class ConfigurationError extends Error {
5
- constructor(message) {
6
- super(message);
7
- this.name = "ConfigurationError";
8
- }
9
- }
10
- exports.ConfigurationError = ConfigurationError;
@@ -1,19 +0,0 @@
1
- import { PNG } from "pngjs";
2
- export interface CompareImageOptions {
3
- base: PNG;
4
- head: PNG;
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;
13
- }
14
- export interface CompareImageResult {
15
- mismatchPixels: number;
16
- mismatchFraction: number;
17
- diff: PNG;
18
- }
19
- export declare const compareImages: (options: CompareImageOptions) => CompareImageResult;
@@ -1,25 +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.compareImages = void 0;
7
- const pixelmatch_1 = __importDefault(require("pixelmatch"));
8
- const pngjs_1 = require("pngjs");
9
- const compareImages = ({ base, head, pixelThreshold }) => {
10
- if (base.width !== head.width || base.height !== head.height) {
11
- throw new Error("Cannot handle different size yet");
12
- }
13
- const { width, height } = base;
14
- const diff = new pngjs_1.PNG({ width, height });
15
- const mismatchPixels = (0, pixelmatch_1.default)(base.data, head.data, diff.data, width, height, {
16
- threshold: pixelThreshold,
17
- });
18
- const mismatchFraction = mismatchPixels / (width * height);
19
- return {
20
- mismatchPixels,
21
- mismatchFraction,
22
- diff,
23
- };
24
- };
25
- exports.compareImages = compareImages;
@@ -1,3 +0,0 @@
1
- import { PNG } from "pngjs";
2
- export declare const readPng: (path: string) => Promise<PNG>;
3
- export declare const writePng: (png: PNG, path: string) => Promise<void>;
@@ -1,25 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.writePng = exports.readPng = void 0;
4
- const fs_1 = require("fs");
5
- const pngjs_1 = require("pngjs");
6
- const readPng = async (path) => {
7
- return new Promise((resolve, reject) => {
8
- const png = new pngjs_1.PNG();
9
- (0, fs_1.createReadStream)(path)
10
- .pipe(png)
11
- .on("parsed", () => {
12
- resolve(png);
13
- })
14
- .on("error", (error) => reject(error));
15
- });
16
- };
17
- exports.readPng = readPng;
18
- const writePng = async (png, path) => {
19
- return new Promise((resolve, reject) => {
20
- const str = (0, fs_1.createWriteStream)(path);
21
- png.pack().pipe(str);
22
- str.on("close", () => resolve()).on("error", (error) => reject(error));
23
- });
24
- };
25
- exports.writePng = writePng;
@@ -1,20 +0,0 @@
1
- export declare const sanitizeFilename: (filename: string) => string;
2
- type ReleaseLock = () => Promise<void>;
3
- export interface LoadOrDownloadJsonFileOptions<T> {
4
- filePath: string;
5
- downloadJson: () => Promise<T | null>;
6
- /**
7
- * For debug messages e.g. 'session' or 'session data'
8
- */
9
- dataDescription: string;
10
- }
11
- /**
12
- * Returns the JSON.parse'd contents of the file at the given path. If the file
13
- * doesn't exist yet then it downloads the object, writes it to the file, and returns it.
14
- *
15
- * Handles concurrent processes trying to download to the same file at the same time.
16
- */
17
- export declare const getOrDownloadJsonFile: <T>({ filePath, downloadJson, dataDescription, }: LoadOrDownloadJsonFileOptions<T>) => Promise<T | null>;
18
- export declare const fileExists: (filePath: string) => Promise<boolean>;
19
- export declare const waitToAcquireLockOnDirectory: (directoryPath: string) => Promise<ReleaseLock>;
20
- export {};
@@ -1,92 +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.waitToAcquireLockOnDirectory = exports.fileExists = exports.getOrDownloadJsonFile = exports.sanitizeFilename = void 0;
7
- const promises_1 = require("fs/promises");
8
- const path_1 = require("path");
9
- const common_1 = require("@alwaysmeticulous/common");
10
- const loglevel_1 = __importDefault(require("loglevel"));
11
- const luxon_1 = require("luxon");
12
- const proper_lockfile_1 = require("proper-lockfile");
13
- const sanitizeFilename = (filename) => {
14
- return filename.replace(/[^a-zA-Z0-9]/g, "_");
15
- };
16
- exports.sanitizeFilename = sanitizeFilename;
17
- /**
18
- * Returns the JSON.parse'd contents of the file at the given path. If the file
19
- * doesn't exist yet then it downloads the object, writes it to the file, and returns it.
20
- *
21
- * Handles concurrent processes trying to download to the same file at the same time.
22
- */
23
- const getOrDownloadJsonFile = async ({ filePath, downloadJson, dataDescription, }) => {
24
- const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
25
- await (0, promises_1.mkdir)((0, path_1.dirname)(filePath), { recursive: true });
26
- // We create a lock file so that if multiple processes try downloading at the same
27
- // time they don't interfere with each other. The second process to run will
28
- // wait for the first process to complete, and then return straight away because
29
- // it'll notice the file already exists.
30
- const releaseLock = await waitToAcquireLockOnFile(filePath);
31
- try {
32
- const existingData = await (0, promises_1.readFile)(filePath)
33
- .then((data) => JSON.parse(data.toString("utf-8")))
34
- .catch(() => null);
35
- if (existingData) {
36
- logger.debug(`Reading ${dataDescription} from local copy in ${filePath}`);
37
- return existingData;
38
- }
39
- const downloadedData = await downloadJson();
40
- if (downloadedData) {
41
- await (0, promises_1.writeFile)(filePath, JSON.stringify(downloadedData, null, 2));
42
- logger.debug(`Wrote ${dataDescription} to ${filePath}`);
43
- }
44
- return downloadedData;
45
- }
46
- finally {
47
- await releaseLock();
48
- }
49
- };
50
- exports.getOrDownloadJsonFile = getOrDownloadJsonFile;
51
- const waitToAcquireLockOnFile = async (filePath) => {
52
- // In many cases the file doesn't exist yet, and can't exist yet (need to download the data, and creating an
53
- // empty file beforehand is risky if the process crashes, and a second process tries reading the empty file).
54
- // However proper-lockfile requires us to pass a file or directory as the first arg. This path is just used
55
- // to detect if the same process tries taking out multiple locks on the same file. It just needs to be calculated
56
- // as something that's unique to the file, and gives the same path for a given file everytime. So we create our
57
- // own lock-target directory for this purpose (directory not file since mkdir is guaranteed to be synchronous).
58
- // The path needs to actually exist, since proper-lockfile resolves symlinks on it.
59
- //
60
- // Note: we don't delete the lock directory afterwards because doing so without creating race-conditions is tricky
61
- const lockDirectory = `${filePath}.lock-target`;
62
- await (0, promises_1.mkdir)(lockDirectory, { recursive: true });
63
- const releaseLock = await (0, proper_lockfile_1.lock)(lockDirectory, {
64
- retries: LOCK_RETRY_OPTIONS,
65
- lockfilePath: `${filePath}.lock`,
66
- });
67
- return async () => {
68
- await releaseLock();
69
- };
70
- };
71
- const fileExists = (filePath) => (0, promises_1.access)(filePath)
72
- .then(() => true)
73
- .catch(() => false);
74
- exports.fileExists = fileExists;
75
- const waitToAcquireLockOnDirectory = (directoryPath) => {
76
- return (0, proper_lockfile_1.lock)(directoryPath, {
77
- retries: LOCK_RETRY_OPTIONS,
78
- lockfilePath: (0, path_1.join)(directoryPath, "dir.lock"),
79
- });
80
- };
81
- exports.waitToAcquireLockOnDirectory = waitToAcquireLockOnDirectory;
82
- const LOCK_RETRY_OPTIONS = {
83
- // We want to keep on retrying till we get the maxRetryTime, so we set retries, which is a maximum, to a high value
84
- retries: 1000,
85
- factor: 1.05,
86
- minTimeout: 500,
87
- maxTimeout: 2000,
88
- // Wait a maximum of 120s for the other process to finish downloading and/or extracting
89
- maxRetryTime: luxon_1.Duration.fromObject({ minutes: 2 }).as("milliseconds"),
90
- // Randomize so processes are less likely to clash on their retries
91
- randomize: true,
92
- };
@@ -1,3 +0,0 @@
1
- import { ReplayEventsDependencies } from "@alwaysmeticulous/common";
2
- export declare const loadReplayEventsDependencies: () => Promise<ReplayEventsDependencies>;
3
- export declare const fetchAsset: (path: string) => Promise<string>;