@alwaysmeticulous/cli 2.39.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.
- package/dist/api/test-run.api.d.ts +3 -31
- package/dist/api/test-run.api.js +3 -96
- package/dist/commands/download-replay/download-replay.command.js +4 -4
- package/dist/commands/download-session/download-session.command.js +4 -4
- package/dist/commands/record/record.command.js +8 -11
- package/dist/commands/replay/replay.command.d.ts +4 -20
- package/dist/commands/replay/replay.command.js +4 -239
- package/dist/commands/run-all-tests/run-all-tests.command.js +5 -6
- package/dist/commands/show-project/show-project.command.js +2 -3
- package/dist/index.d.ts +0 -7
- package/dist/index.js +1 -13
- package/dist/main.js +5 -11
- package/dist/utils/sentry.utils.d.ts +0 -4
- package/dist/utils/sentry.utils.js +3 -23
- package/package.json +14 -32
- package/dist/api/client.d.ts +0 -5
- package/dist/api/client.js +0 -32
- package/dist/api/download.d.ts +0 -1
- package/dist/api/download.js +0 -21
- package/dist/api/project.api.d.ts +0 -3
- package/dist/api/project.api.js +0 -22
- package/dist/api/replay-diff.api.d.ts +0 -13
- package/dist/api/replay-diff.api.js +0 -32
- package/dist/api/replay.api.d.ts +0 -36
- package/dist/api/replay.api.js +0 -89
- package/dist/api/session.api.d.ts +0 -6
- package/dist/api/session.api.js +0 -45
- package/dist/api/types.d.ts +0 -39
- package/dist/api/types.js +0 -2
- package/dist/api/upload.d.ts +0 -1
- package/dist/api/upload.js +0 -18
- package/dist/archive/archive.d.ts +0 -4
- package/dist/archive/archive.js +0 -64
- package/dist/command-utils/common-types.d.ts +0 -20
- package/dist/command-utils/common-types.js +0 -2
- package/dist/commands/bootstrap/bootstrap.command.d.ts +0 -2
- package/dist/commands/bootstrap/bootstrap.command.js +0 -33
- package/dist/commands/create-test/create-test.command.d.ts +0 -94
- package/dist/commands/create-test/create-test.command.js +0 -170
- package/dist/commands/replay/utils/compute-diff.d.ts +0 -13
- package/dist/commands/replay/utils/compute-diff.js +0 -95
- package/dist/commands/replay/utils/exit-early-if-skip-upload-env-var-set.d.ts +0 -1
- package/dist/commands/replay/utils/exit-early-if-skip-upload-env-var-set.js +0 -27
- package/dist/commands/screenshot-diff/screenshot-diff.command.d.ts +0 -53
- package/dist/commands/screenshot-diff/screenshot-diff.command.js +0 -182
- package/dist/commands/screenshot-diff/utils/get-screenshot-filename.d.ts +0 -2
- package/dist/commands/screenshot-diff/utils/get-screenshot-filename.js +0 -18
- package/dist/commands/screenshot-diff/utils/get-screenshot-identifier.d.ts +0 -2
- package/dist/commands/screenshot-diff/utils/get-screenshot-identifier.js +0 -24
- package/dist/commands/screenshot-diff/utils/has-notable-differences.d.ts +0 -2
- package/dist/commands/screenshot-diff/utils/has-notable-differences.js +0 -10
- package/dist/commands/serve/serve.command.d.ts +0 -10
- package/dist/commands/serve/serve.command.js +0 -31
- package/dist/commands/update-tests/update-tests.command.d.ts +0 -21
- package/dist/commands/update-tests/update-tests.command.js +0 -96
- package/dist/config/config.d.ts +0 -3
- package/dist/config/config.js +0 -70
- package/dist/config/config.types.d.ts +0 -16
- package/dist/config/config.types.js +0 -2
- package/dist/config/snippets.d.ts +0 -1
- package/dist/config/snippets.js +0 -18
- package/dist/errors/config.d.ts +0 -3
- package/dist/errors/config.js +0 -10
- package/dist/image/diff.utils.d.ts +0 -19
- package/dist/image/diff.utils.js +0 -25
- package/dist/image/io.utils.d.ts +0 -3
- package/dist/image/io.utils.js +0 -25
- package/dist/local-data/local-data.utils.d.ts +0 -20
- package/dist/local-data/local-data.utils.js +0 -92
- package/dist/local-data/replay-assets.d.ts +0 -3
- package/dist/local-data/replay-assets.js +0 -94
- package/dist/local-data/replays.d.ts +0 -18
- package/dist/local-data/replays.js +0 -87
- package/dist/local-data/screenshot-diffs.d.ts +0 -7
- package/dist/local-data/screenshot-diffs.js +0 -20
- package/dist/local-data/serve-assets-from-simulation.d.ts +0 -5
- package/dist/local-data/serve-assets-from-simulation.js +0 -52
- package/dist/local-data/sessions.d.ts +0 -10
- package/dist/local-data/sessions.js +0 -41
- package/dist/parallel-tests/__tests__/merge-test-results.spec.d.ts +0 -1
- package/dist/parallel-tests/__tests__/merge-test-results.spec.js +0 -104
- package/dist/parallel-tests/__tests__/mock-test-results.d.ts +0 -17
- package/dist/parallel-tests/__tests__/mock-test-results.js +0 -106
- package/dist/parallel-tests/__tests__/parrallel-tests.handler.spec.d.ts +0 -1
- package/dist/parallel-tests/__tests__/parrallel-tests.handler.spec.js +0 -205
- package/dist/parallel-tests/execute-test-in-child-process.d.ts +0 -3
- package/dist/parallel-tests/execute-test-in-child-process.js +0 -44
- package/dist/parallel-tests/merge-test-results.d.ts +0 -12
- package/dist/parallel-tests/merge-test-results.js +0 -122
- package/dist/parallel-tests/messages.types.d.ts +0 -17
- package/dist/parallel-tests/messages.types.js +0 -2
- package/dist/parallel-tests/parallel-replay.handler.d.ts +0 -3
- package/dist/parallel-tests/parallel-replay.handler.js +0 -57
- package/dist/parallel-tests/parallel-replay.types.d.ts +0 -16
- package/dist/parallel-tests/parallel-replay.types.js +0 -2
- package/dist/parallel-tests/parallel-tests.handler.d.ts +0 -14
- package/dist/parallel-tests/parallel-tests.handler.js +0 -194
- package/dist/parallel-tests/run-all-tests.d.ts +0 -73
- package/dist/parallel-tests/run-all-tests.js +0 -237
- package/dist/parallel-tests/run-all-tests.types.d.ts +0 -6
- package/dist/parallel-tests/run-all-tests.types.js +0 -2
- package/dist/parallel-tests/screenshot-diff-results.utils.d.ts +0 -7
- package/dist/parallel-tests/screenshot-diff-results.utils.js +0 -20
- package/dist/parallel-tests/task.handler.d.ts +0 -1
- package/dist/parallel-tests/task.handler.js +0 -86
- package/dist/utils/api-token.utils.d.ts +0 -1
- package/dist/utils/api-token.utils.js +0 -31
- package/dist/utils/commit-sha.utils.d.ts +0 -1
- package/dist/utils/commit-sha.utils.js +0 -42
- package/dist/utils/config.utils.d.ts +0 -7
- package/dist/utils/config.utils.js +0 -33
- package/dist/utils/github-summary.utils.d.ts +0 -5
- package/dist/utils/github-summary.utils.js +0 -38
- package/dist/utils/run-all-tests.utils.d.ts +0 -16
- package/dist/utils/run-all-tests.utils.js +0 -32
- package/dist/utils/test-run-environment.utils.d.ts +0 -2
- package/dist/utils/test-run-environment.utils.js +0 -25
- package/dist/utils/version.utils.d.ts +0 -1
- package/dist/utils/version.utils.js +0 -17
|
@@ -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,94 +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.fetchAsset = exports.loadReplayEventsDependencies = void 0;
|
|
7
|
-
const promises_1 = require("fs/promises");
|
|
8
|
-
const path_1 = require("path");
|
|
9
|
-
const common_1 = require("@alwaysmeticulous/common");
|
|
10
|
-
const axios_1 = __importDefault(require("axios"));
|
|
11
|
-
const loglevel_1 = __importDefault(require("loglevel"));
|
|
12
|
-
const snippets_1 = require("../config/snippets");
|
|
13
|
-
const ASSETS_FOLDER_NAME = "assets";
|
|
14
|
-
const ASSET_METADATA_FILE_NAME = "assets.json";
|
|
15
|
-
const loadReplayEventsDependencies = async () => {
|
|
16
|
-
const browserUserInteractions = await (0, exports.fetchAsset)("replay/v2/snippet-user-interactions.bundle.js");
|
|
17
|
-
const browserPlayback = await (0, exports.fetchAsset)("replay/v2/snippet-playback.bundle.js");
|
|
18
|
-
const browserUrlObserver = await (0, exports.fetchAsset)("replay/v2/snippet-url-observer.bundle.js");
|
|
19
|
-
const nodeBrowserContext = await (0, exports.fetchAsset)("replay/v2/node-browser-context.bundle.js");
|
|
20
|
-
const nodeNetworkStubbing = await (0, exports.fetchAsset)("replay/v2/node-network-stubbing.bundle.js");
|
|
21
|
-
const nodeUserInteractions = await (0, exports.fetchAsset)("replay/v2/node-user-interactions.bundle.js");
|
|
22
|
-
return {
|
|
23
|
-
browserUserInteractions: {
|
|
24
|
-
key: "browserUserInteractions",
|
|
25
|
-
location: browserUserInteractions,
|
|
26
|
-
},
|
|
27
|
-
browserPlayback: {
|
|
28
|
-
key: "browserPlayback",
|
|
29
|
-
location: browserPlayback,
|
|
30
|
-
},
|
|
31
|
-
browserUrlObserver: {
|
|
32
|
-
key: "browserUrlObserver",
|
|
33
|
-
location: browserUrlObserver,
|
|
34
|
-
},
|
|
35
|
-
nodeBrowserContext: {
|
|
36
|
-
key: "nodeBrowserContext",
|
|
37
|
-
location: nodeBrowserContext,
|
|
38
|
-
},
|
|
39
|
-
nodeNetworkStubbing: {
|
|
40
|
-
key: "nodeNetworkStubbing",
|
|
41
|
-
location: nodeNetworkStubbing,
|
|
42
|
-
},
|
|
43
|
-
nodeUserInteractions: {
|
|
44
|
-
key: "nodeUserInteractions",
|
|
45
|
-
location: nodeUserInteractions,
|
|
46
|
-
},
|
|
47
|
-
};
|
|
48
|
-
};
|
|
49
|
-
exports.loadReplayEventsDependencies = loadReplayEventsDependencies;
|
|
50
|
-
const fetchAsset = async (path) => {
|
|
51
|
-
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
52
|
-
const fetchUrl = new URL(path, (0, snippets_1.getSnippetsBaseUrl)()).href;
|
|
53
|
-
const assetFileName = `${(0, path_1.basename)(new URL(fetchUrl).pathname, ".js")}.cjs`;
|
|
54
|
-
const assetMetadata = await loadAssetMetadata();
|
|
55
|
-
const etag = (await axios_1.default.head(fetchUrl)).headers["etag"] || "";
|
|
56
|
-
const entry = assetMetadata.assets.find((item) => item.fileName === assetFileName);
|
|
57
|
-
const filePath = (0, path_1.join)(await getOrCreateAssetsDir(), assetFileName);
|
|
58
|
-
if (entry && etag !== "" && etag === entry.etag) {
|
|
59
|
-
logger.debug(`${fetchUrl} already present`);
|
|
60
|
-
return filePath;
|
|
61
|
-
}
|
|
62
|
-
const contents = (await axios_1.default.get(fetchUrl)).data;
|
|
63
|
-
await (0, promises_1.writeFile)(filePath, contents);
|
|
64
|
-
if (entry) {
|
|
65
|
-
logger.debug(`${fetchUrl} updated`);
|
|
66
|
-
entry.etag = etag;
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
logger.debug(`${fetchUrl} downloaded`);
|
|
70
|
-
assetMetadata.assets.push({ fileName: assetFileName, etag, fetchUrl });
|
|
71
|
-
}
|
|
72
|
-
await saveAssetMetadata(assetMetadata);
|
|
73
|
-
return filePath;
|
|
74
|
-
};
|
|
75
|
-
exports.fetchAsset = fetchAsset;
|
|
76
|
-
const getOrCreateAssetsDir = async () => {
|
|
77
|
-
const assetsDir = (0, path_1.join)((0, common_1.getMeticulousLocalDataDir)(), ASSETS_FOLDER_NAME);
|
|
78
|
-
await (0, promises_1.mkdir)(assetsDir, { recursive: true });
|
|
79
|
-
return assetsDir;
|
|
80
|
-
};
|
|
81
|
-
const loadAssetMetadata = async () => {
|
|
82
|
-
const assetsFile = (0, path_1.join)(await getOrCreateAssetsDir(), ASSET_METADATA_FILE_NAME);
|
|
83
|
-
const existingMetadata = await (0, promises_1.readFile)(assetsFile)
|
|
84
|
-
.then((data) => JSON.parse(data.toString("utf-8")))
|
|
85
|
-
.catch(() => null);
|
|
86
|
-
if (existingMetadata) {
|
|
87
|
-
return existingMetadata;
|
|
88
|
-
}
|
|
89
|
-
return { assets: [] };
|
|
90
|
-
};
|
|
91
|
-
const saveAssetMetadata = async (assetMetadata) => {
|
|
92
|
-
const assetsFile = (0, path_1.join)(await getOrCreateAssetsDir(), ASSET_METADATA_FILE_NAME);
|
|
93
|
-
await (0, promises_1.writeFile)(assetsFile, JSON.stringify(assetMetadata, null, 2));
|
|
94
|
-
};
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { ScreenshotIdentifier } from "@alwaysmeticulous/api";
|
|
2
|
-
import { Replay } from "@alwaysmeticulous/common";
|
|
3
|
-
import { AxiosInstance } from "axios";
|
|
4
|
-
export declare const getOrFetchReplay: (client: AxiosInstance, replayId: string) => Promise<{
|
|
5
|
-
fileName: string;
|
|
6
|
-
data: Replay;
|
|
7
|
-
}>;
|
|
8
|
-
export declare const getOrFetchReplayArchive: (client: AxiosInstance, replayId: string) => Promise<{
|
|
9
|
-
fileName: string;
|
|
10
|
-
}>;
|
|
11
|
-
export interface IdentifiedScreenshotFile {
|
|
12
|
-
identifier: ScreenshotIdentifier;
|
|
13
|
-
fileName: string;
|
|
14
|
-
}
|
|
15
|
-
export declare const getScreenshotFiles: (screenshotsDirPath: string) => Promise<IdentifiedScreenshotFile[]>;
|
|
16
|
-
export declare const getSnapshottedAssetsDir: (replayId: string) => string;
|
|
17
|
-
export declare const getScreenshotsDir: (replayDir: string) => string;
|
|
18
|
-
export declare const getReplayDir: (replayId: string) => string;
|
|
@@ -1,87 +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.getReplayDir = exports.getScreenshotsDir = exports.getSnapshottedAssetsDir = exports.getScreenshotFiles = exports.getOrFetchReplayArchive = exports.getOrFetchReplay = void 0;
|
|
7
|
-
const promises_1 = require("fs/promises");
|
|
8
|
-
const path_1 = require("path");
|
|
9
|
-
const common_1 = require("@alwaysmeticulous/common");
|
|
10
|
-
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
11
|
-
const loglevel_1 = __importDefault(require("loglevel"));
|
|
12
|
-
const download_1 = require("../api/download");
|
|
13
|
-
const replay_api_1 = require("../api/replay.api");
|
|
14
|
-
const get_screenshot_identifier_1 = require("../commands/screenshot-diff/utils/get-screenshot-identifier");
|
|
15
|
-
const local_data_utils_1 = require("./local-data.utils");
|
|
16
|
-
const getOrFetchReplay = async (client, replayId) => {
|
|
17
|
-
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
18
|
-
const replayFile = (0, path_1.join)((0, exports.getReplayDir)(replayId), `${replayId}.json`);
|
|
19
|
-
const replay = await (0, local_data_utils_1.getOrDownloadJsonFile)({
|
|
20
|
-
filePath: replayFile,
|
|
21
|
-
dataDescription: "replay",
|
|
22
|
-
downloadJson: () => (0, replay_api_1.getReplay)(client, replayId),
|
|
23
|
-
});
|
|
24
|
-
if (!replay) {
|
|
25
|
-
logger.error(`Error: Could not retrieve replay with id "${replayId}". Is the API token correct?`);
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
return { fileName: replayFile, data: replay };
|
|
29
|
-
};
|
|
30
|
-
exports.getOrFetchReplay = getOrFetchReplay;
|
|
31
|
-
const getOrFetchReplayArchive = async (client, replayId) => {
|
|
32
|
-
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
33
|
-
const replayDir = (0, exports.getReplayDir)(replayId);
|
|
34
|
-
await (0, promises_1.mkdir)(replayDir, { recursive: true });
|
|
35
|
-
const releaseLock = await (0, local_data_utils_1.waitToAcquireLockOnDirectory)(replayDir);
|
|
36
|
-
try {
|
|
37
|
-
const replayArchiveFile = (0, path_1.join)(replayDir, `${replayId}.zip`);
|
|
38
|
-
const paramsFile = (0, path_1.join)(replayDir, "replayEventsParams.json");
|
|
39
|
-
// Check if "replayEventsParams.json" exists. If yes, we assume the replay
|
|
40
|
-
// zip archive has been downloaded and extracted.
|
|
41
|
-
if (await (0, local_data_utils_1.fileExists)(paramsFile)) {
|
|
42
|
-
logger.debug(`Replay archive already downloaded at ${replayDir}`);
|
|
43
|
-
return { fileName: replayDir };
|
|
44
|
-
}
|
|
45
|
-
const downloadUrlData = await (0, replay_api_1.getReplayDownloadUrl)(client, replayId);
|
|
46
|
-
if (!downloadUrlData) {
|
|
47
|
-
logger.error("Error: Could not retrieve replay archive URL. This may be an invalid replay");
|
|
48
|
-
await releaseLock();
|
|
49
|
-
process.exit(1);
|
|
50
|
-
}
|
|
51
|
-
await (0, download_1.downloadFile)(downloadUrlData.dowloadUrl, replayArchiveFile);
|
|
52
|
-
const zipFile = new adm_zip_1.default(replayArchiveFile);
|
|
53
|
-
zipFile.extractAllTo(replayDir, /*overwrite=*/ true);
|
|
54
|
-
await (0, promises_1.rm)(replayArchiveFile);
|
|
55
|
-
logger.debug(`Extracted replay archive in ${replayDir}`);
|
|
56
|
-
return { fileName: replayDir };
|
|
57
|
-
}
|
|
58
|
-
finally {
|
|
59
|
-
await releaseLock();
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
exports.getOrFetchReplayArchive = getOrFetchReplayArchive;
|
|
63
|
-
const getScreenshotFiles = async (screenshotsDirPath) => {
|
|
64
|
-
const screenshotFiles = [];
|
|
65
|
-
const screenshotsDir = await (0, promises_1.opendir)(screenshotsDirPath);
|
|
66
|
-
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
67
|
-
for await (const dirEntry of screenshotsDir) {
|
|
68
|
-
if (!dirEntry.isFile() || !dirEntry.name.endsWith(".png")) {
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
const identifier = (0, get_screenshot_identifier_1.getScreenshotIdentifier)(dirEntry.name);
|
|
72
|
-
if (identifier == null) {
|
|
73
|
-
logger.error(`Ignoring screenshot file with unrecognized name pattern: ${dirEntry.name}`);
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
screenshotFiles.push({ identifier, fileName: dirEntry.name });
|
|
77
|
-
}
|
|
78
|
-
// Sort files alphabetically to help when reading results.
|
|
79
|
-
return screenshotFiles.sort((a, b) => a.fileName.localeCompare(b.fileName));
|
|
80
|
-
};
|
|
81
|
-
exports.getScreenshotFiles = getScreenshotFiles;
|
|
82
|
-
const getSnapshottedAssetsDir = (replayId) => (0, path_1.join)((0, exports.getReplayDir)(replayId), "snapshotted-assets");
|
|
83
|
-
exports.getSnapshottedAssetsDir = getSnapshottedAssetsDir;
|
|
84
|
-
const getScreenshotsDir = (replayDir) => (0, path_1.join)(replayDir, "screenshots");
|
|
85
|
-
exports.getScreenshotsDir = getScreenshotsDir;
|
|
86
|
-
const getReplayDir = (replayId) => (0, path_1.join)((0, common_1.getMeticulousLocalDataDir)(), "replays", replayId);
|
|
87
|
-
exports.getReplayDir = getReplayDir;
|
|
@@ -1,20 +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.writeScreenshotDiff = 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 io_utils_1 = require("../image/io.utils");
|
|
12
|
-
const writeScreenshotDiff = async ({ baseReplayId, headReplayId, screenshotFileName, diff, }) => {
|
|
13
|
-
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
14
|
-
const diffDir = (0, path_1.join)((0, common_1.getMeticulousLocalDataDir)(), "screenshot-diffs");
|
|
15
|
-
await (0, promises_1.mkdir)(diffDir, { recursive: true });
|
|
16
|
-
const diffFile = (0, path_1.join)(diffDir, `${baseReplayId}-vs-${headReplayId}-${screenshotFileName}.png`);
|
|
17
|
-
await (0, io_utils_1.writePng)(diff, diffFile);
|
|
18
|
-
logger.debug(`Screenshot diff written to ${diffFile}`);
|
|
19
|
-
};
|
|
20
|
-
exports.writeScreenshotDiff = writeScreenshotDiff;
|
|
@@ -1,52 +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.serveAssetsFromSimulation = void 0;
|
|
7
|
-
const fs_1 = require("fs");
|
|
8
|
-
const common_1 = require("@alwaysmeticulous/common");
|
|
9
|
-
const express_1 = __importDefault(require("express"));
|
|
10
|
-
const find_free_port_1 = __importDefault(require("find-free-port"));
|
|
11
|
-
const loglevel_1 = __importDefault(require("loglevel"));
|
|
12
|
-
const replays_1 = require("./replays");
|
|
13
|
-
// We must avoid ports blocked by Chrome: https://superuser.com/questions/188058/which-ports-are-considered-unsafe-by-chrome
|
|
14
|
-
const STARTING_PORT = 9100;
|
|
15
|
-
const ENDING_PORT = 10000;
|
|
16
|
-
const MAX_RETRY_ATTEMPTS = 3;
|
|
17
|
-
async function serveAssetsFromSimulation(client, simulationId) {
|
|
18
|
-
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
19
|
-
await (0, replays_1.getOrFetchReplayArchive)(client, simulationId);
|
|
20
|
-
const snapshottedAssetsDir = (0, replays_1.getSnapshottedAssetsDir)(simulationId);
|
|
21
|
-
if (!(0, fs_1.existsSync)(snapshottedAssetsDir)) {
|
|
22
|
-
logger.error(`No snapshotted assets found for simulation '${simulationId}'.` +
|
|
23
|
-
" Please re-run without the --simulationIdForAssets flag." +
|
|
24
|
-
" You can optionally specify an --appUrl to run the simulation against.");
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
const app = (0, express_1.default)();
|
|
28
|
-
// eslint-disable-next-line import/no-named-as-default-member
|
|
29
|
-
app.use(express_1.default.static(snapshottedAssetsDir));
|
|
30
|
-
const serverStartupPromise = (0, common_1.defer)();
|
|
31
|
-
const { server, port } = await retryUntilFreePortFound((portToTry) => app.listen(portToTry, () => serverStartupPromise.resolve()));
|
|
32
|
-
return serverStartupPromise.promise.then(() => ({
|
|
33
|
-
url: `http://localhost:${port}`,
|
|
34
|
-
closeServer: server.close.bind(server),
|
|
35
|
-
}));
|
|
36
|
-
}
|
|
37
|
-
exports.serveAssetsFromSimulation = serveAssetsFromSimulation;
|
|
38
|
-
// We retry a few times in case of race conditions (two servers trying to grab same port at same time)
|
|
39
|
-
const retryUntilFreePortFound = async (startServer, attempt = 0) => {
|
|
40
|
-
// We randomize the port to try to minimize chance of race conditions
|
|
41
|
-
const [port] = await (0, find_free_port_1.default)(randomNumberBetween(STARTING_PORT, ENDING_PORT));
|
|
42
|
-
try {
|
|
43
|
-
return { server: startServer(port), port };
|
|
44
|
-
}
|
|
45
|
-
catch (err) {
|
|
46
|
-
if (attempt < MAX_RETRY_ATTEMPTS) {
|
|
47
|
-
return retryUntilFreePortFound(startServer, attempt + 1);
|
|
48
|
-
}
|
|
49
|
-
throw err;
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
const randomNumberBetween = (inclusiveStart, exclusiveEnd) => Math.floor(Math.random() * (exclusiveEnd - inclusiveStart) + inclusiveStart);
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { SessionData } from "@alwaysmeticulous/api";
|
|
2
|
-
import { AxiosInstance } from "axios";
|
|
3
|
-
export declare const getOrFetchRecordedSession: (client: AxiosInstance, sessionId: string) => Promise<{
|
|
4
|
-
fileName: string;
|
|
5
|
-
data: any;
|
|
6
|
-
}>;
|
|
7
|
-
export declare const getOrFetchRecordedSessionData: (client: AxiosInstance, sessionId: string) => Promise<{
|
|
8
|
-
fileName: string;
|
|
9
|
-
data: SessionData;
|
|
10
|
-
}>;
|
|
@@ -1,41 +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.getOrFetchRecordedSessionData = exports.getOrFetchRecordedSession = void 0;
|
|
7
|
-
const path_1 = require("path");
|
|
8
|
-
const common_1 = require("@alwaysmeticulous/common");
|
|
9
|
-
const loglevel_1 = __importDefault(require("loglevel"));
|
|
10
|
-
const session_api_1 = require("../api/session.api");
|
|
11
|
-
const local_data_utils_1 = require("./local-data.utils");
|
|
12
|
-
const getOrFetchRecordedSession = async (client, sessionId) => {
|
|
13
|
-
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
14
|
-
const sessionFile = (0, path_1.join)((0, common_1.getMeticulousLocalDataDir)(), "sessions", `${(0, local_data_utils_1.sanitizeFilename)(sessionId)}.json`);
|
|
15
|
-
const session = await (0, local_data_utils_1.getOrDownloadJsonFile)({
|
|
16
|
-
filePath: sessionFile,
|
|
17
|
-
dataDescription: "session",
|
|
18
|
-
downloadJson: () => (0, session_api_1.getRecordedSession)(client, sessionId),
|
|
19
|
-
});
|
|
20
|
-
if (!session) {
|
|
21
|
-
logger.error("Error: Could not retrieve session. Is the API token correct?");
|
|
22
|
-
process.exit(1);
|
|
23
|
-
}
|
|
24
|
-
return { fileName: sessionFile, data: session };
|
|
25
|
-
};
|
|
26
|
-
exports.getOrFetchRecordedSession = getOrFetchRecordedSession;
|
|
27
|
-
const getOrFetchRecordedSessionData = async (client, sessionId) => {
|
|
28
|
-
const logger = loglevel_1.default.getLogger(common_1.METICULOUS_LOGGER_NAME);
|
|
29
|
-
const sessionFile = (0, path_1.join)((0, common_1.getMeticulousLocalDataDir)(), "sessions", `${(0, local_data_utils_1.sanitizeFilename)(sessionId)}_data.json`);
|
|
30
|
-
const sessionData = await (0, local_data_utils_1.getOrDownloadJsonFile)({
|
|
31
|
-
filePath: sessionFile,
|
|
32
|
-
dataDescription: "session data",
|
|
33
|
-
downloadJson: () => (0, session_api_1.getRecordedSessionData)(client, sessionId),
|
|
34
|
-
});
|
|
35
|
-
if (!sessionData) {
|
|
36
|
-
logger.error("Error: Could not retrieve session data. This may be an invalid session");
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
return { fileName: sessionFile, data: sessionData };
|
|
40
|
-
};
|
|
41
|
-
exports.getOrFetchRecordedSessionData = getOrFetchRecordedSessionData;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const merge_test_results_1 = require("../merge-test-results");
|
|
4
|
-
const mock_test_results_1 = require("./mock-test-results");
|
|
5
|
-
describe("mergeResults", () => {
|
|
6
|
-
it("keeps the result as a failure when all retried screenshots are 'identical'", () => {
|
|
7
|
-
const currentResult = (0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.diff)(0), (0, mock_test_results_1.noDiff)(1)]);
|
|
8
|
-
const comparisonToHeadReplay = (0, mock_test_results_1.testResult)("pass", [(0, mock_test_results_1.noDiff)(0), (0, mock_test_results_1.noDiff)(1)]);
|
|
9
|
-
const mergedResult = (0, merge_test_results_1.mergeResults)({
|
|
10
|
-
currentResult,
|
|
11
|
-
comparisonToHeadReplay,
|
|
12
|
-
});
|
|
13
|
-
expect(mergedResult).toEqual((0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.diff)(0), (0, mock_test_results_1.noDiff)(1)]));
|
|
14
|
-
});
|
|
15
|
-
it("doesn't ignore diffs to screenshots which originally passed", () => {
|
|
16
|
-
const currentResult = (0, mock_test_results_1.testResult)("pass", [(0, mock_test_results_1.noDiff)(0), (0, mock_test_results_1.noDiff)(1)]);
|
|
17
|
-
const comparisonToHeadReplay = (0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.noDiff)(0), (0, mock_test_results_1.diff)(1)]);
|
|
18
|
-
const mergedResult = (0, merge_test_results_1.mergeResults)({
|
|
19
|
-
currentResult,
|
|
20
|
-
comparisonToHeadReplay,
|
|
21
|
-
});
|
|
22
|
-
expect(mergedResult).toEqual(
|
|
23
|
-
// All screenshots are either "no-diff" or "flake" thus the overall result is "flake"
|
|
24
|
-
(0, mock_test_results_1.testResult)("flake", [(0, mock_test_results_1.noDiff)(0), (0, mock_test_results_1.flake)(1, (0, mock_test_results_1.noDiff)(), [(0, mock_test_results_1.diff)()])]));
|
|
25
|
-
});
|
|
26
|
-
it("marks screenshots as flakes if the screenshot comparison originally failed, but the second retry gives a different screenshot again", () => {
|
|
27
|
-
const currentResult = (0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.noDiff)(0), (0, mock_test_results_1.diff)(1), (0, mock_test_results_1.diff)(2)]);
|
|
28
|
-
const comparisonToHeadReplay = (0, mock_test_results_1.testResult)("fail", [
|
|
29
|
-
(0, mock_test_results_1.noDiff)(0),
|
|
30
|
-
(0, mock_test_results_1.diff)(1),
|
|
31
|
-
(0, mock_test_results_1.noDiff)(2),
|
|
32
|
-
]);
|
|
33
|
-
const mergedResult = (0, merge_test_results_1.mergeResults)({
|
|
34
|
-
currentResult,
|
|
35
|
-
comparisonToHeadReplay,
|
|
36
|
-
});
|
|
37
|
-
expect(mergedResult).toEqual((0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.noDiff)(0), (0, mock_test_results_1.flake)(1, (0, mock_test_results_1.diff)(), [(0, mock_test_results_1.diff)()]), (0, mock_test_results_1.diff)(2)]));
|
|
38
|
-
});
|
|
39
|
-
it("marks overall test as a flake if there are only flakey screenshots and no failed screenshots", () => {
|
|
40
|
-
const currentResult = (0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.noDiff)(0), (0, mock_test_results_1.diff)(1), (0, mock_test_results_1.diff)(2)]);
|
|
41
|
-
const comparisonToHeadReplay = (0, mock_test_results_1.testResult)("fail", [
|
|
42
|
-
(0, mock_test_results_1.noDiff)(0),
|
|
43
|
-
(0, mock_test_results_1.diff)(1),
|
|
44
|
-
(0, mock_test_results_1.diff)(2),
|
|
45
|
-
]);
|
|
46
|
-
const mergedResult = (0, merge_test_results_1.mergeResults)({
|
|
47
|
-
currentResult,
|
|
48
|
-
comparisonToHeadReplay,
|
|
49
|
-
});
|
|
50
|
-
expect(mergedResult).toEqual((0, mock_test_results_1.testResult)("flake", [
|
|
51
|
-
(0, mock_test_results_1.noDiff)(0),
|
|
52
|
-
(0, mock_test_results_1.flake)(1, (0, mock_test_results_1.diff)(), [(0, mock_test_results_1.diff)()]),
|
|
53
|
-
(0, mock_test_results_1.flake)(2, (0, mock_test_results_1.diff)(), [(0, mock_test_results_1.diff)()]),
|
|
54
|
-
]));
|
|
55
|
-
});
|
|
56
|
-
it("adds to diffsToHeadScreenshotOnRetries for existing flakes", () => {
|
|
57
|
-
const currentResult = (0, mock_test_results_1.testResult)("fail", [
|
|
58
|
-
(0, mock_test_results_1.diff)(0),
|
|
59
|
-
(0, mock_test_results_1.flake)(1, (0, mock_test_results_1.diff)(), [(0, mock_test_results_1.missingHead)()]),
|
|
60
|
-
(0, mock_test_results_1.flake)(2, (0, mock_test_results_1.differentSize)(), [(0, mock_test_results_1.diff)()]),
|
|
61
|
-
(0, mock_test_results_1.flake)(3, (0, mock_test_results_1.missingBase)(), [(0, mock_test_results_1.diff)(), (0, mock_test_results_1.diff)()]),
|
|
62
|
-
]);
|
|
63
|
-
const comparisonToHeadReplay = (0, mock_test_results_1.testResult)("fail", [
|
|
64
|
-
(0, mock_test_results_1.noDiff)(0),
|
|
65
|
-
(0, mock_test_results_1.diff)(1),
|
|
66
|
-
(0, mock_test_results_1.differentSize)(2),
|
|
67
|
-
(0, mock_test_results_1.missingHead)(3),
|
|
68
|
-
]);
|
|
69
|
-
const mergedResult = (0, merge_test_results_1.mergeResults)({
|
|
70
|
-
currentResult,
|
|
71
|
-
comparisonToHeadReplay,
|
|
72
|
-
});
|
|
73
|
-
expect(mergedResult).toEqual((0, mock_test_results_1.testResult)("fail", [
|
|
74
|
-
(0, mock_test_results_1.diff)(0),
|
|
75
|
-
(0, mock_test_results_1.flake)(1, (0, mock_test_results_1.diff)(), [(0, mock_test_results_1.missingHead)(), (0, mock_test_results_1.diff)()]),
|
|
76
|
-
(0, mock_test_results_1.flake)(2, (0, mock_test_results_1.differentSize)(), [(0, mock_test_results_1.diff)(), (0, mock_test_results_1.differentSize)()]),
|
|
77
|
-
(0, mock_test_results_1.flake)(3, (0, mock_test_results_1.missingBase)(), [(0, mock_test_results_1.diff)(), (0, mock_test_results_1.diff)(), (0, mock_test_results_1.missingHead)()]),
|
|
78
|
-
]));
|
|
79
|
-
});
|
|
80
|
-
it("keeps a missing-head as is, if there is no corresponding retry screenshot", () => {
|
|
81
|
-
const currentResult = (0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.missingHead)(0)]);
|
|
82
|
-
const comparisonToHeadReplay = (0, mock_test_results_1.testResult)("pass", []);
|
|
83
|
-
const mergedResult = (0, merge_test_results_1.mergeResults)({
|
|
84
|
-
currentResult,
|
|
85
|
-
comparisonToHeadReplay,
|
|
86
|
-
});
|
|
87
|
-
expect(mergedResult).toEqual((0, mock_test_results_1.testResult)("fail", [(0, mock_test_results_1.missingHead)(0)]));
|
|
88
|
-
});
|
|
89
|
-
it("adds to diffsToHeadScreenshotOnRetries for a flakey missing-head, if there is no corresponding retry screenshot", () => {
|
|
90
|
-
const currentResult = (0, mock_test_results_1.testResult)("fail", [
|
|
91
|
-
(0, mock_test_results_1.flake)(0, (0, mock_test_results_1.missingHead)(), [(0, mock_test_results_1.missingBase)()]),
|
|
92
|
-
(0, mock_test_results_1.diff)(1),
|
|
93
|
-
]);
|
|
94
|
-
const comparisonToHeadReplay = (0, mock_test_results_1.testResult)("pass", [(0, mock_test_results_1.noDiff)(1)]);
|
|
95
|
-
const mergedResult = (0, merge_test_results_1.mergeResults)({
|
|
96
|
-
currentResult,
|
|
97
|
-
comparisonToHeadReplay,
|
|
98
|
-
});
|
|
99
|
-
expect(mergedResult).toEqual((0, mock_test_results_1.testResult)("fail", [
|
|
100
|
-
(0, mock_test_results_1.flake)(0, (0, mock_test_results_1.missingHead)(), [(0, mock_test_results_1.missingBase)(), (0, mock_test_results_1.missingBaseAndHead)()]),
|
|
101
|
-
(0, mock_test_results_1.diff)(1),
|
|
102
|
-
]));
|
|
103
|
-
});
|
|
104
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { ScreenshotDiffResult, ScreenshotIdentifier, TestCase } from "@alwaysmeticulous/api";
|
|
2
|
-
import { DetailedTestCaseResult } from "../../config/config.types";
|
|
3
|
-
export declare const id: (eventNumber?: number) => ScreenshotIdentifier;
|
|
4
|
-
export declare const testResult: (result: "pass" | "fail" | "flake", screenshotDiffResults: ScreenshotDiffResult[], testCase?: TestCase) => DetailedTestCaseResult;
|
|
5
|
-
export declare const diff: (eventNumber?: number) => ScreenshotDiffResult;
|
|
6
|
-
export declare const noDiff: (eventNumber?: number) => ScreenshotDiffResult;
|
|
7
|
-
export declare const flake: (eventNumber: number, diffToBaseScreenshot: ScreenshotDiffResult, diffsToHeadScreenshotOnRetries: Array<ScreenshotDiffResult | {
|
|
8
|
-
identifier: ScreenshotIdentifier;
|
|
9
|
-
outcome: "missing-base-and-head";
|
|
10
|
-
}>) => ScreenshotDiffResult;
|
|
11
|
-
export declare const missingBase: (eventNumber?: number) => ScreenshotDiffResult;
|
|
12
|
-
export declare const missingHead: (eventNumber?: number) => ScreenshotDiffResult;
|
|
13
|
-
export declare const missingBaseAndHead: (eventNumber?: number) => {
|
|
14
|
-
readonly identifier: ScreenshotIdentifier;
|
|
15
|
-
readonly outcome: "missing-base-and-head";
|
|
16
|
-
};
|
|
17
|
-
export declare const differentSize: (eventNumber?: number) => ScreenshotDiffResult;
|