@alwaysmeticulous/cli
Advanced tools
Comparing version 1.2.12 to 1.2.13
@@ -34,1 +34,2 @@ import { AxiosInstance } from "axios"; | ||
export declare const postScreenshotDiffStats: (client: AxiosInstance, options: ScreenshotDiffStats) => Promise<void>; | ||
export declare const getDiffUrl: (client: AxiosInstance, baseReplayId: string, headReplayId: string) => Promise<string>; |
@@ -6,4 +6,5 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.postScreenshotDiffStats = exports.getReplayCommandId = exports.putReplayPushedStatus = exports.getReplayDownloadUrl = exports.getReplayPushUrl = exports.createReplay = exports.getReplay = void 0; | ||
exports.getDiffUrl = exports.postScreenshotDiffStats = exports.getReplayCommandId = exports.putReplayPushedStatus = exports.getReplayDownloadUrl = exports.getReplayPushUrl = exports.createReplay = exports.getReplay = void 0; | ||
const axios_1 = __importDefault(require("axios")); | ||
const project_api_1 = require("./project.api"); | ||
const getReplay = async (client, replayId) => { | ||
@@ -83,1 +84,9 @@ const { data } = await client.get(`replays/${replayId}`).catch((error) => { | ||
exports.postScreenshotDiffStats = postScreenshotDiffStats; | ||
const getDiffUrl = async (client, baseReplayId, headReplayId) => { | ||
const project = await (0, project_api_1.getProject)(client); | ||
const organizationName = project.organization.name; | ||
const projectName = project.name; | ||
const diffUrl = `https://app.meticulous.ai/projects/${organizationName}/${projectName}/replays/${headReplayId}/diff/${baseReplayId}`; | ||
return diffUrl; | ||
}; | ||
exports.getDiffUrl = getDiffUrl; |
@@ -10,4 +10,10 @@ import { CommandModule } from "yargs"; | ||
screenshot?: boolean | null | undefined; | ||
baseReplayId?: string | null | undefined; | ||
diffThreshold?: number | null | undefined; | ||
diffPixelThreshold?: number | null | undefined; | ||
save?: boolean | null | undefined; | ||
exitOnMismatch?: boolean | null | undefined; | ||
} | ||
export declare const replayCommandHandler: (options: Options) => Promise<void>; | ||
export declare const replay: CommandModule<unknown, Options>; | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.replay = void 0; | ||
exports.replay = exports.replayCommandHandler = void 0; | ||
const promises_1 = require("fs/promises"); | ||
@@ -11,9 +11,12 @@ const luxon_1 = require("luxon"); | ||
const archive_1 = require("../../archive/archive"); | ||
const config_1 = require("../../config/config"); | ||
const local_data_1 = require("../../local-data/local-data"); | ||
const local_data_utils_1 = require("../../local-data/local-data.utils"); | ||
const replay_assets_1 = require("../../local-data/replay-assets"); | ||
const replays_1 = require("../../local-data/replays"); | ||
const sessions_1 = require("../../local-data/sessions"); | ||
const commit_sha_utils_1 = require("../../utils/commit-sha.utils"); | ||
const version_utils_1 = require("../../utils/version.utils"); | ||
const handler = async ({ apiToken, commitSha: commitSha_, sessionId, appUrl, headless, devTools, screenshot, }) => { | ||
const screenshot_diff_command_1 = require("../screenshot-diff/screenshot-diff.command"); | ||
const replayCommandHandler = async ({ apiToken, commitSha: commitSha_, sessionId, appUrl, headless, devTools, screenshot, baseReplayId: baseReplayId_, diffThreshold, diffPixelThreshold, save, exitOnMismatch, }) => { | ||
const client = (0, client_1.createClient)({ apiToken }); | ||
@@ -123,4 +126,42 @@ // 1. Check session files | ||
console.log(`View replay at: ${replayUrl}`); | ||
// 12. Diff against base replay screenshot if one is provided | ||
const baseReplayId = baseReplayId_ || ""; | ||
if (screenshot && baseReplayId) { | ||
console.log(`Diffing final state screenshot against replay ${baseReplayId}`); | ||
await (0, replays_1.getOrFetchReplay)(client, baseReplayId); | ||
await (0, replays_1.getOrFetchReplayArchive)(client, baseReplayId); | ||
const baseScreenshot = await (0, replays_1.readReplayScreenshot)(baseReplayId); | ||
const headScreenshot = await (0, replays_1.readLocalReplayScreenshot)(tempDir); | ||
await (0, screenshot_diff_command_1.diffScreenshots)({ | ||
client, | ||
baseReplayId, | ||
headReplayId: replay.id, | ||
baseScreenshot, | ||
headScreenshot, | ||
threshold: diffThreshold, | ||
pixelThreshold: diffPixelThreshold, | ||
exitOnMismatch: !!exitOnMismatch, | ||
}); | ||
} | ||
// 13. Add test case to meticulous.json if --save option is passed | ||
if (save) { | ||
if (!screenshot) { | ||
console.error("Warning: saving a new test case without screenshot enabled."); | ||
} | ||
const meticulousConfig = await (0, config_1.readConfig)(); | ||
const newConfig = { | ||
...meticulousConfig, | ||
testCases: [ | ||
...(meticulousConfig.testCases || []), | ||
{ | ||
sessionId, | ||
baseReplayId: replay.id, | ||
}, | ||
], | ||
}; | ||
await (0, config_1.saveConfig)(newConfig); | ||
} | ||
await (0, archive_1.deleteArchive)(archivePath); | ||
}; | ||
exports.replayCommandHandler = replayCommandHandler; | ||
exports.replay = { | ||
@@ -156,4 +197,18 @@ command: "replay", | ||
}, | ||
baseReplayId: { | ||
string: true, | ||
description: "Base replay id to diff the final state screenshot against", | ||
}, | ||
diffThreshold: { | ||
number: true, | ||
}, | ||
diffPixelThreshold: { | ||
number: true, | ||
}, | ||
save: { | ||
boolean: true, | ||
description: "Adds the replay to the list of test cases in meticulous.json", | ||
}, | ||
}, | ||
handler, | ||
handler: (options) => (0, exports.replayCommandHandler)({ ...options, exitOnMismatch: true }), | ||
}; |
@@ -0,2 +1,14 @@ | ||
import { AxiosInstance } from "axios"; | ||
import { PNG } from "pngjs"; | ||
import { CommandModule } from "yargs"; | ||
export declare const diffScreenshots: (options: { | ||
client: AxiosInstance; | ||
baseReplayId: string; | ||
headReplayId: string; | ||
baseScreenshot: PNG; | ||
headScreenshot: PNG; | ||
threshold: number | null | undefined; | ||
pixelThreshold: number | null | undefined; | ||
exitOnMismatch: boolean; | ||
}) => Promise<void>; | ||
interface Options { | ||
@@ -7,4 +19,5 @@ apiToken?: string | null | undefined; | ||
threshold?: number | null | undefined; | ||
pixelThreshold?: number | null | undefined; | ||
} | ||
export declare const screenshotDiff: CommandModule<unknown, Options>; | ||
export {}; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.screenshotDiff = void 0; | ||
exports.screenshotDiff = exports.diffScreenshots = void 0; | ||
const client_1 = require("../../api/client"); | ||
@@ -10,16 +10,15 @@ const replay_api_1 = require("../../api/replay.api"); | ||
const DEFAULT_MISMATCH_THRESHOLD = 0.01; | ||
const handler = async ({ apiToken, baseReplayId, headReplayId, threshold: threshold_, }) => { | ||
const client = (0, client_1.createClient)({ apiToken }); | ||
await (0, replays_1.getOrFetchReplay)(client, baseReplayId); | ||
await (0, replays_1.getOrFetchReplayArchive)(client, baseReplayId); | ||
await (0, replays_1.getOrFetchReplay)(client, headReplayId); | ||
await (0, replays_1.getOrFetchReplayArchive)(client, headReplayId); | ||
const baseScreenshot = await (0, replays_1.readReplayScreenshot)(baseReplayId); | ||
const headScreenshot = await (0, replays_1.readReplayScreenshot)(headReplayId); | ||
const diffScreenshots = async ({ client, baseReplayId, headReplayId, baseScreenshot, headScreenshot, threshold: threshold_, pixelThreshold, exitOnMismatch, }) => { | ||
const threshold = threshold_ || DEFAULT_MISMATCH_THRESHOLD; | ||
const pixelmatchOptions = pixelThreshold ? { threshold: pixelThreshold } : null; | ||
const { mismatchPixels, mismatchFraction, diff } = (0, diff_utils_1.compareImages)({ | ||
base: baseScreenshot, | ||
head: headScreenshot, | ||
...(pixelmatchOptions ? pixelmatchOptions : {}), | ||
}); | ||
console.log({ mismatchPixels, mismatchFraction }); | ||
console.log(`${Math.round(mismatchFraction * 100)}% pixel mismatch (threshold is ${Math.round(threshold * 100)}%) => ${mismatchFraction > threshold ? "FAIL!" : "PASS"}`); | ||
await (0, screenshot_diffs_1.writeScreenshotDiff)({ baseReplayId, headReplayId, diff }); | ||
const diffUrl = await (0, replay_api_1.getDiffUrl)(client, baseReplayId, headReplayId); | ||
console.log(`View screenshot diff at ${diffUrl}`); | ||
await (0, replay_api_1.postScreenshotDiffStats)(client, { | ||
@@ -34,8 +33,30 @@ baseReplayId, | ||
}); | ||
const threshold = threshold_ || DEFAULT_MISMATCH_THRESHOLD; | ||
if (mismatchFraction > threshold) { | ||
console.log("Screenshots do not match!"); | ||
process.exit(1); | ||
if (exitOnMismatch) { | ||
process.exit(1); | ||
} | ||
throw new Error("Screenshots do not match!"); | ||
} | ||
}; | ||
exports.diffScreenshots = diffScreenshots; | ||
const handler = async ({ apiToken, baseReplayId, headReplayId, threshold, pixelThreshold, }) => { | ||
const client = (0, client_1.createClient)({ apiToken }); | ||
await (0, replays_1.getOrFetchReplay)(client, baseReplayId); | ||
await (0, replays_1.getOrFetchReplayArchive)(client, baseReplayId); | ||
await (0, replays_1.getOrFetchReplay)(client, headReplayId); | ||
await (0, replays_1.getOrFetchReplayArchive)(client, headReplayId); | ||
const baseScreenshot = await (0, replays_1.readReplayScreenshot)(baseReplayId); | ||
const headScreenshot = await (0, replays_1.readReplayScreenshot)(headReplayId); | ||
await (0, exports.diffScreenshots)({ | ||
client, | ||
baseReplayId, | ||
headReplayId, | ||
baseScreenshot, | ||
headScreenshot, | ||
threshold, | ||
pixelThreshold, | ||
exitOnMismatch: true, | ||
}); | ||
}; | ||
exports.screenshotDiff = { | ||
@@ -59,4 +80,7 @@ command: "screenshot-diff", | ||
}, | ||
pixelThreshold: { | ||
number: true, | ||
}, | ||
}, | ||
handler, | ||
}; |
@@ -0,5 +1,8 @@ | ||
export { downloadReplay } from "./commands/download-replay/download-replay.command"; | ||
export { downloadSession } from "./commands/download-session/download-session.command"; | ||
export { record } from "./commands/record/record.command"; | ||
export { replay } from "./commands/replay/replay.command"; | ||
export { runAllTests } from "./commands/run-all-tests/run-all-tests.command"; | ||
export { screenshotDiff } from "./commands/screenshot-diff/screenshot-diff.command"; | ||
export { showProject } from "./commands/show-project/show-project.command"; | ||
export { uploadBuild } from "./commands/upload-build/upload-build.command"; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.uploadBuild = exports.showProject = exports.replay = exports.record = exports.downloadSession = void 0; | ||
exports.uploadBuild = exports.showProject = exports.screenshotDiff = exports.runAllTests = exports.replay = exports.record = exports.downloadSession = exports.downloadReplay = void 0; | ||
var download_replay_command_1 = require("./commands/download-replay/download-replay.command"); | ||
Object.defineProperty(exports, "downloadReplay", { enumerable: true, get: function () { return download_replay_command_1.downloadReplay; } }); | ||
var download_session_command_1 = require("./commands/download-session/download-session.command"); | ||
@@ -10,2 +12,6 @@ Object.defineProperty(exports, "downloadSession", { enumerable: true, get: function () { return download_session_command_1.downloadSession; } }); | ||
Object.defineProperty(exports, "replay", { enumerable: true, get: function () { return replay_command_1.replay; } }); | ||
var run_all_tests_command_1 = require("./commands/run-all-tests/run-all-tests.command"); | ||
Object.defineProperty(exports, "runAllTests", { enumerable: true, get: function () { return run_all_tests_command_1.runAllTests; } }); | ||
var screenshot_diff_command_1 = require("./commands/screenshot-diff/screenshot-diff.command"); | ||
Object.defineProperty(exports, "screenshotDiff", { enumerable: true, get: function () { return screenshot_diff_command_1.screenshotDiff; } }); | ||
var show_project_command_1 = require("./commands/show-project/show-project.command"); | ||
@@ -12,0 +18,0 @@ Object.defineProperty(exports, "showProject", { enumerable: true, get: function () { return show_project_command_1.showProject; } }); |
@@ -6,1 +6,2 @@ import { AxiosInstance } from "axios"; | ||
export declare const readReplayScreenshot: (replayId: string) => Promise<PNG>; | ||
export declare const readLocalReplayScreenshot: (tempDir: string) => Promise<PNG>; |
@@ -6,3 +6,3 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.readReplayScreenshot = exports.getOrFetchReplayArchive = exports.getOrFetchReplay = void 0; | ||
exports.readLocalReplayScreenshot = exports.readReplayScreenshot = exports.getOrFetchReplayArchive = exports.getOrFetchReplay = void 0; | ||
const adm_zip_1 = __importDefault(require("adm-zip")); | ||
@@ -69,1 +69,7 @@ const promises_1 = require("fs/promises"); | ||
exports.readReplayScreenshot = readReplayScreenshot; | ||
const readLocalReplayScreenshot = async (tempDir) => { | ||
const screenshotFile = (0, path_1.join)(tempDir, "screenshots", "final-state.png"); | ||
const png = await (0, io_utils_1.readPng)(screenshotFile); | ||
return png; | ||
}; | ||
exports.readLocalReplayScreenshot = readLocalReplayScreenshot; |
@@ -12,2 +12,3 @@ "use strict"; | ||
const replay_command_1 = require("./commands/replay/replay.command"); | ||
const run_all_tests_command_1 = require("./commands/run-all-tests/run-all-tests.command"); | ||
const screenshot_diff_command_1 = require("./commands/screenshot-diff/screenshot-diff.command"); | ||
@@ -30,2 +31,3 @@ const show_project_command_1 = require("./commands/show-project/show-project.command"); | ||
.command(replay_command_1.replay) | ||
.command(run_all_tests_command_1.runAllTests) | ||
.command(screenshot_diff_command_1.screenshotDiff) | ||
@@ -32,0 +34,0 @@ .command(show_project_command_1.showProject) |
{ | ||
"name": "@alwaysmeticulous/cli", | ||
"version": "1.2.12", | ||
"version": "1.2.13", | ||
"description": "The Meticulous CLI", | ||
@@ -30,2 +30,3 @@ "license": "ISC", | ||
"axios": "^0.25.0", | ||
"cosmiconfig": "^7.0.1", | ||
"luxon": "^2.2.0", | ||
@@ -32,0 +33,0 @@ "pixelmatch": "^5.2.1", |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
95892
67
1606
10
14
+ Addedcosmiconfig@^7.0.1
+ Added@babel/code-frame@7.26.2(transitive)
+ Added@babel/helper-validator-identifier@7.25.9(transitive)
+ Added@types/parse-json@4.0.2(transitive)
+ Addedcallsites@3.1.0(transitive)
+ Addedcosmiconfig@7.1.0(transitive)
+ Addederror-ex@1.3.2(transitive)
+ Addedimport-fresh@3.3.0(transitive)
+ Addedis-arrayish@0.2.1(transitive)
+ Addedjs-tokens@4.0.0(transitive)
+ Addedjson-parse-even-better-errors@2.3.1(transitive)
+ Addedlines-and-columns@1.2.4(transitive)
+ Addedparent-module@1.0.1(transitive)
+ Addedparse-json@5.2.0(transitive)
+ Addedpath-type@4.0.0(transitive)
+ Addedpicocolors@1.1.1(transitive)
+ Addedresolve-from@4.0.0(transitive)
+ Addedyaml@1.10.2(transitive)