diff --git a/visual-js/package-lock.json b/visual-js/package-lock.json index 29c16263..f7b899d0 100644 --- a/visual-js/package-lock.json +++ b/visual-js/package-lock.json @@ -5152,6 +5152,15 @@ "node": ">= 4.0.0" } }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/auto-bind": { "version": "4.0.0", "dev": true, @@ -6612,6 +6621,15 @@ "url": "https://opencollective.com/date-fns" } }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/dayjs": { "version": "1.11.13", "license": "MIT" @@ -8416,6 +8434,12 @@ ], "license": "MIT" }, + "node_modules/fast-copy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", + "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==", + "license": "MIT" + }, "node_modules/fast-decode-uri-component": { "version": "1.0.1", "dev": true, @@ -8467,6 +8491,21 @@ "fast-decode-uri-component": "^1.0.1" } }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, "node_modules/fast-url-parser": { "version": "1.1.3", "dev": true, @@ -9539,6 +9578,12 @@ "tslib": "^2.0.3" } }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "license": "BSD-3-Clause", @@ -11292,7 +11337,6 @@ }, "node_modules/joycon": { "version": "3.1.1", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -12880,6 +12924,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/on-finished": { "version": "2.4.1", "license": "MIT", @@ -13411,6 +13464,67 @@ "node": ">=6" } }, + "node_modules/pino": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.6.0.tgz", + "integrity": "sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^4.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.0.0.tgz", + "integrity": "sha512-cQBBIVG3YajgoUjo1FdKVRX6t9XPxwB9lcNJVD5GCnNM4Y6T12YYx8c6zEejxQsU0wrg9TwmDulcE9LR7qcJqA==", + "license": "MIT", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^3.0.2", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pump": "^3.0.0", + "secure-json-parse": "^2.4.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^3.1.1" + }, + "bin": { + "pino-pretty": "bin.js" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "license": "MIT" + }, "node_modules/pirates": { "version": "4.0.6", "license": "MIT", @@ -13695,6 +13809,22 @@ "node": ">=8" } }, + "node_modules/process-warning": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz", + "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/progress": { "version": "2.0.3", "license": "MIT", @@ -14129,6 +14259,12 @@ "license": "MIT", "optional": true }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, "node_modules/quick-lru": { "version": "5.1.1", "license": "MIT", @@ -14365,6 +14501,15 @@ "node": ">=8.10.0" } }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/recast": { "version": "0.23.9", "license": "MIT", @@ -14688,6 +14833,15 @@ ], "license": "MIT" }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "license": "MIT" @@ -14707,6 +14861,12 @@ "dev": true, "license": "MIT" }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "license": "BSD-3-Clause" + }, "node_modules/selenium-webdriver": { "version": "4.25.0", "dev": true, @@ -15019,6 +15179,15 @@ "node": ">= 14" } }, + "node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "license": "BSD-3-Clause", @@ -15155,7 +15324,6 @@ "node_modules/split2": { "version": "4.2.0", "license": "ISC", - "optional": true, "engines": { "node": ">= 10.x" } @@ -15646,6 +15814,15 @@ "node": ">=0.8" } }, + "node_modules/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, "node_modules/throttleit": { "version": "1.0.1", "license": "MIT", @@ -17695,6 +17872,8 @@ "commander": "^12.0.0", "glob": "^11.0.1", "pdf-to-img": "~4.4.0", + "pino": "^9.6.0", + "pino-pretty": "^13.0.0", "workerpool": "^9.2.0" }, "bin": { diff --git a/visual-js/visual-snapshots/package.json b/visual-js/visual-snapshots/package.json index 7f3018d0..a6ef1c56 100644 --- a/visual-js/visual-snapshots/package.json +++ b/visual-js/visual-snapshots/package.json @@ -34,6 +34,8 @@ "commander": "^12.0.0", "glob": "^11.0.1", "pdf-to-img": "~4.4.0", + "pino": "^9.6.0", + "pino-pretty": "^13.0.0", "workerpool": "^9.2.0" }, "devDependencies": { diff --git a/visual-js/visual-snapshots/src/api/visual-snapshots-api.ts b/visual-js/visual-snapshots/src/api/visual-snapshots-api.ts index e241b084..50ac5934 100644 --- a/visual-js/visual-snapshots/src/api/visual-snapshots-api.ts +++ b/visual-js/visual-snapshots/src/api/visual-snapshots-api.ts @@ -1,5 +1,7 @@ import { BuildStatus, DiffingMethod, VisualApi } from "@saucelabs/visual"; import { __dirname } from "../utils/helpers.js"; +import { Logger } from "pino"; +import { logger as defaultLogger } from "../logger.js"; export interface CreateVisualSnapshotsParams { branch?: string; @@ -19,6 +21,12 @@ export interface CreateBuildParams { readonly defaultBranch?: string; readonly project?: string; readonly customId?: string; + readonly logger?: Logger; +} + +export interface FinishBuildParams { + readonly buildId: string; + readonly logger?: Logger; } export interface UploadSnapshotParams { @@ -27,12 +35,15 @@ export interface UploadSnapshotParams { readonly snapshotName: string; readonly suiteName?: string; readonly testName?: string; + readonly logger?: Logger; } export class VisualSnapshotsApi { constructor(private readonly api: VisualApi) {} public async createBuild(params: CreateBuildParams): Promise { + const logger = params.logger ?? defaultLogger; + const build = await this.api.createBuild({ name: params.buildName, branch: params.branch, @@ -40,37 +51,64 @@ export class VisualSnapshotsApi { project: params.project, customId: params.customId, }); - console.info(`Build ${build.id} created: ${build.url}`); + + logger.info( + { + buildId: build.id, + url: build.url, + }, + `Build created.` + ); + return build.id; } - public async finishBuild(buildId: string) { + public async finishBuild(params: FinishBuildParams) { + const buildId = params.buildId; + const logger = params.logger ?? defaultLogger; + const { status: buildStatus } = await this.api.finishBuild({ uuid: buildId, }); if ([BuildStatus.Running, BuildStatus.Queued].includes(buildStatus)) { - console.info( - `Build ${buildId} finished but snapshots haven't been compared yet. Check the build status in a few moments.` + logger.info( + { + buildId, + buildStatus, + }, + `Build finished but snapshots haven't been compared yet. Check the build status in a few moments.` ); } else { const { unapprovedCount, errorCount } = (await this.api.buildStatus( buildId ))!; - console.info( - `Build ${buildId} finished (status=${buildStatus}, unapprovedCount=${unapprovedCount}, errorCount=${errorCount}).` + logger.info( + { + buildId, + buildStatus, + unapprovedCount, + errorCount, + }, + `Build finished.` ); } } public async uploadImageAndCreateSnapshot(params: UploadSnapshotParams) { + const logger = params.logger ?? defaultLogger; + const uploadId = await this.api.uploadSnapshot({ buildId: params.buildId, image: { data: params.snapshot }, }); - console.info( - `Uploaded image to build ${params.buildId}: upload id=${uploadId}.` + logger.info( + { + buildId: params.buildId, + uploadId, + }, + `Uploaded image to build.` ); await this.api.createSnapshot({ @@ -82,8 +120,15 @@ export class VisualSnapshotsApi { suiteName: params.suiteName, }); - console.info( - `Created a snapshot ${params.snapshotName} for build ${params.buildId}.` + logger.info( + { + buildId: params.buildId, + uploadId, + snapshotName: params.snapshotName, + testName: params.testName, + suiteName: params.suiteName, + }, + `Created a snapshot for build.` ); return uploadId; diff --git a/visual-js/visual-snapshots/src/app/pdf-handler.ts b/visual-js/visual-snapshots/src/app/pdf-handler.ts index 67a4002f..407bc225 100644 --- a/visual-js/visual-snapshots/src/app/pdf-handler.ts +++ b/visual-js/visual-snapshots/src/app/pdf-handler.ts @@ -37,7 +37,7 @@ export class PdfCommandHandler { }); } finally { if (!params.buildId) { - await this.visualSnapshotsApi.finishBuild(buildId); + await this.visualSnapshotsApi.finishBuild({ buildId }); } } } diff --git a/visual-js/visual-snapshots/src/app/worker/pdf-page-snapshot-uploader.ts b/visual-js/visual-snapshots/src/app/worker/pdf-page-snapshot-uploader.ts index f13794b7..a817367a 100644 --- a/visual-js/visual-snapshots/src/app/worker/pdf-page-snapshot-uploader.ts +++ b/visual-js/visual-snapshots/src/app/worker/pdf-page-snapshot-uploader.ts @@ -2,6 +2,7 @@ import path from "path"; import { formatString } from "../../utils/format.js"; import { PdfFileLoader } from "../pdf-file-loader.js"; import { VisualSnapshotsApi } from "../../api/visual-snapshots-api.js"; +import { logger as defaultLogger } from "../../logger.js"; export class PdfPageSnapshotUploader { constructor( @@ -31,17 +32,22 @@ export class PdfPageSnapshotUploader { page: pageNumber, }); - const uploadId = await this.visualSnapshotsApi.uploadImageAndCreateSnapshot( - { - buildId, - snapshot: page, - snapshotName, - suiteName, - testName, - } - ); - - return uploadId; + const logger = defaultLogger.child({ + filePath: pdfFilePath, + pageNumber, + snapshotName, + suiteName, + testName, + }); + + return await this.visualSnapshotsApi.uploadImageAndCreateSnapshot({ + buildId, + snapshot: page, + snapshotName, + suiteName, + testName, + logger, + }); } private getSnapshotFormat(format: string | undefined) { diff --git a/visual-js/visual-snapshots/src/app/worker/worker.ts b/visual-js/visual-snapshots/src/app/worker/worker.ts index 78c45d6d..95ec60e1 100644 --- a/visual-js/visual-snapshots/src/app/worker/worker.ts +++ b/visual-js/visual-snapshots/src/app/worker/worker.ts @@ -4,6 +4,7 @@ import { usernameOption, accessKeyOption, regionOption, + loggerLevel, } from "../../commands/options.js"; import { initializeVisualApi } from "../../api/visual-client.js"; import { LibPdfFileLoader } from "../pdf-file-loader.js"; @@ -12,14 +13,21 @@ import { PdfPageSnapshotUploader } from "./pdf-page-snapshot-uploader.js"; import type { WorkerMethod } from "../../utils/pool.js"; import { clientVersion } from "../../version.js"; import { VisualSnapshotsApi } from "../../api/visual-snapshots-api.js"; +import pino from "pino"; +import { logger } from "../../logger.js"; program .addOption(usernameOption) .addOption(accessKeyOption) .addOption(regionOption) + .addOption(loggerLevel) .allowUnknownOption(true) .allowExcessArguments(true); +program.on("option:log", (level: pino.Level) => { + logger.level = level; +}); + program.parse(); const { user, key, region } = program.opts(); diff --git a/visual-js/visual-snapshots/src/commands/options.ts b/visual-js/visual-snapshots/src/commands/options.ts index db6d0986..84e13076 100644 --- a/visual-js/visual-snapshots/src/commands/options.ts +++ b/visual-js/visual-snapshots/src/commands/options.ts @@ -86,3 +86,8 @@ export const concurrencyOption = new Option( ) .default(cpus().length) .argParser(parseInteger); + +export const loggerLevel = new Option( + "--log ", + "Logging level to use." +).choices(["trace", "debug", "info", "warn", "error", "fatal", "silent"]); diff --git a/visual-js/visual-snapshots/src/commands/pdf.ts b/visual-js/visual-snapshots/src/commands/pdf.ts index 5c85506b..453e0d48 100644 --- a/visual-js/visual-snapshots/src/commands/pdf.ts +++ b/visual-js/visual-snapshots/src/commands/pdf.ts @@ -18,6 +18,7 @@ import { VisualSnapshotsApi } from "../api/visual-snapshots-api.js"; import { initializeVisualApi } from "../api/visual-client.js"; import { WorkerPoolPdfSnapshotUploader } from "../app/worker/worker-pool-pdf-snapshot-uploader.js"; import { LibPdfFileLoader } from "../app/pdf-file-loader.js"; +import { logger } from "../logger.js"; export const testNameOption = new Option( "--test-name ", @@ -68,10 +69,10 @@ export const pdfCommand = (clientVersion: string) => { new PdfCommandHandler(visualSnapshotsApi, pdfSnapshotUploader) .handle(globsOrDirs, params) .then(() => { - console.info("Successfully created PDF snapshots."); + logger.info("Successfully created PDF snapshots."); }) .catch((err) => { - console.error(`At least one PDF snapshot creation failed: ${err}.`); + logger.error(err, "At least one PDF snapshot creation failed."); }); }); }; diff --git a/visual-js/visual-snapshots/src/index.ts b/visual-js/visual-snapshots/src/index.ts index 2a998d48..686d4ccc 100644 --- a/visual-js/visual-snapshots/src/index.ts +++ b/visual-js/visual-snapshots/src/index.ts @@ -3,14 +3,22 @@ import { Command } from "commander"; import { pdfCommand } from "./commands/pdf.js"; import { clientVersion } from "./version.js"; +import { loggerLevel } from "./commands/options.js"; +import pino from "pino"; +import { logger } from "./logger.js"; const program = new Command(); program .name("visual-snapshots") .description("Create visual snapshots of a document.") - .version(clientVersion); + .version(clientVersion) + .addOption(loggerLevel); program.addCommand(pdfCommand(clientVersion)); +program.on("option:log", (level: pino.Level) => { + logger.level = level; +}); + program.parse(); diff --git a/visual-js/visual-snapshots/src/logger.ts b/visual-js/visual-snapshots/src/logger.ts new file mode 100644 index 00000000..b9817137 --- /dev/null +++ b/visual-js/visual-snapshots/src/logger.ts @@ -0,0 +1,7 @@ +import { pino } from "pino"; + +export const logger = pino({ + transport: { + target: "pino-pretty", + }, +}); diff --git a/visual-js/visual-snapshots/test/api/__snapshots__/visual-api.spec.ts.snap b/visual-js/visual-snapshots/test/api/__snapshots__/visual-api.spec.ts.snap index 03a4b8d9..63f88dd1 100644 --- a/visual-js/visual-snapshots/test/api/__snapshots__/visual-api.spec.ts.snap +++ b/visual-js/visual-snapshots/test/api/__snapshots__/visual-api.spec.ts.snap @@ -1,88 +1,66 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`VisualSnapshots createBuild log output 1`] = ` -[MockFunction] { - "calls": [ - [ - "Build foo created: undefined", - ], - ], - "results": [ - { - "type": "return", - "value": undefined, - }, - ], -} +[ + { + "buildId": "foo", + "level": 30, + "msg": "Build created.", + }, +] `; exports[`VisualSnapshots finishBuild log output when build status resolves to Equal 1`] = ` -[MockFunction] { - "calls": [ - [ - "Build buildId finished (status=EQUAL, unapprovedCount=2, errorCount=1).", - ], - ], - "results": [ - { - "type": "return", - "value": undefined, - }, - ], -} +[ + { + "buildId": "buildId", + "buildStatus": "EQUAL", + "errorCount": 1, + "level": 30, + "msg": "Build finished.", + "unapprovedCount": 2, + }, +] `; exports[`VisualSnapshots finishBuild log output when build status resolves to Queued 1`] = ` -[MockFunction] { - "calls": [ - [ - "Build buildId finished but snapshots haven't been compared yet. Check the build status in a few moments.", - ], - ], - "results": [ - { - "type": "return", - "value": undefined, - }, - ], -} +[ + { + "buildId": "buildId", + "buildStatus": "QUEUED", + "level": 30, + "msg": "Build finished but snapshots haven't been compared yet. Check the build status in a few moments.", + }, +] `; exports[`VisualSnapshots finishBuild log output when build status resolves to Running 1`] = ` -[MockFunction] { - "calls": [ - [ - "Build buildId finished but snapshots haven't been compared yet. Check the build status in a few moments.", - ], - ], - "results": [ - { - "type": "return", - "value": undefined, - }, - ], -} +[ + { + "buildId": "buildId", + "buildStatus": "RUNNING", + "level": 30, + "msg": "Build finished but snapshots haven't been compared yet. Check the build status in a few moments.", + }, +] `; exports[`VisualSnapshots uploadImageAndCreateSnapshot log output 1`] = ` -[MockFunction] { - "calls": [ - [ - "Uploaded image to build testBuildId: upload id=uploadId.", - ], - [ - "Created a snapshot testSnapshotName for build testBuildId.", - ], - ], - "results": [ - { - "type": "return", - "value": undefined, - }, - { - "type": "return", - "value": undefined, - }, - ], -} +[ + { + "buildId": "testBuildId", + "level": 30, + "msg": "Uploaded image to build.", + "uploadId": "uploadId", + }, + { + "buildId": "testBuildId", + "level": 30, + "msg": "Created a snapshot for build.", + "snapshotName": "testSnapshotName", + "suiteName": "testSuiteName", + "testName": "testTestName", + "uploadId": "uploadId", + }, +] `; diff --git a/visual-js/visual-snapshots/test/api/visual-api.spec.ts b/visual-js/visual-snapshots/test/api/visual-api.spec.ts index f59b740c..26bb4661 100644 --- a/visual-js/visual-snapshots/test/api/visual-api.spec.ts +++ b/visual-js/visual-snapshots/test/api/visual-api.spec.ts @@ -4,11 +4,10 @@ import { UploadSnapshotParams, VisualSnapshotsApi, } from "../../src/api/visual-snapshots-api.js"; +import { mockLogger } from "../helpers.js"; describe("VisualSnapshots", () => { - const consoleInfoSpy = jest - .spyOn(console, "info") - .mockImplementation(() => undefined); + const { logger, logged, reset: resetLogger } = mockLogger(); const createBuildMock = jest.fn< ReturnType, @@ -42,7 +41,7 @@ describe("VisualSnapshots", () => { createBuildMock.mockReset(); finishBuildMock.mockReset(); buildStatusMock.mockReset(); - consoleInfoSpy.mockReset(); + resetLogger(); }); describe("createBuild", () => { @@ -61,6 +60,7 @@ describe("VisualSnapshots", () => { defaultBranch: "testDefaultBranch", project: "testProject", customId: "testCustomId", + logger, }; await api.createBuild(params); @@ -83,7 +83,7 @@ describe("VisualSnapshots", () => { const api = new VisualSnapshotsApi(visualApi); - const actual = await api.createBuild({}); + const actual = await api.createBuild({ logger }); expect(actual).toEqual(buildId); }); @@ -97,9 +97,9 @@ describe("VisualSnapshots", () => { const api = new VisualSnapshotsApi(visualApi); - await api.createBuild({}); + await api.createBuild({ logger }); - expect(consoleInfoSpy).toMatchSnapshot(); + expect(logged).toMatchSnapshot(); }); }); @@ -119,7 +119,7 @@ describe("VisualSnapshots", () => { const api = new VisualSnapshotsApi(visualApi); - await api.finishBuild(buildId); + await api.finishBuild({ buildId, logger }); expect(finishBuildMock).toHaveBeenCalledWith({ uuid: buildId, @@ -146,7 +146,7 @@ describe("VisualSnapshots", () => { const api = new VisualSnapshotsApi(visualApi); - await api.finishBuild(buildId); + await api.finishBuild({ buildId, logger }); expect(buildStatusMock).toHaveBeenCalledWith(buildId); }); @@ -166,7 +166,7 @@ describe("VisualSnapshots", () => { const api = new VisualSnapshotsApi(visualApi); - await api.finishBuild(buildId); + await api.finishBuild({ buildId, logger }); expect(buildStatusMock).not.toHaveBeenCalled(); }); @@ -185,7 +185,7 @@ describe("VisualSnapshots", () => { }); const api = new VisualSnapshotsApi(visualApi); - await api.finishBuild(buildId); + await api.finishBuild({ buildId, logger }); expect(buildStatusMock).not.toHaveBeenCalled(); }); @@ -205,9 +205,9 @@ describe("VisualSnapshots", () => { const api = new VisualSnapshotsApi(visualApi); - await api.finishBuild(buildId); + await api.finishBuild({ buildId, logger }); - expect(consoleInfoSpy).toMatchSnapshot(); + expect(logged).toMatchSnapshot(); }); test("log output when build status resolves to Running", async () => { @@ -225,9 +225,9 @@ describe("VisualSnapshots", () => { const api = new VisualSnapshotsApi(visualApi); - await api.finishBuild(buildId); + await api.finishBuild({ buildId, logger }); - expect(consoleInfoSpy).toMatchSnapshot(); + expect(logged).toMatchSnapshot(); }); test("log output when build status resolves to Queued", async () => { @@ -245,9 +245,9 @@ describe("VisualSnapshots", () => { const api = new VisualSnapshotsApi(visualApi); - await api.finishBuild(buildId); + await api.finishBuild({ buildId, logger }); - expect(consoleInfoSpy).toMatchSnapshot(); + expect(logged).toMatchSnapshot(); }); }); @@ -264,6 +264,7 @@ describe("VisualSnapshots", () => { snapshotName: "testSnapshotName", suiteName: "testSuiteName", testName: "testTestName", + logger, }; await api.uploadImageAndCreateSnapshot(params); @@ -286,6 +287,7 @@ describe("VisualSnapshots", () => { snapshotName: "testSnapshotName", suiteName: "testSuiteName", testName: "testTestName", + logger, }; await api.uploadImageAndCreateSnapshot(params); @@ -312,6 +314,7 @@ describe("VisualSnapshots", () => { snapshotName: "testSnapshotName", suiteName: "testSuiteName", testName: "testTestName", + logger, }; const actual = await api.uploadImageAndCreateSnapshot(params); @@ -330,11 +333,12 @@ describe("VisualSnapshots", () => { snapshotName: "testSnapshotName", suiteName: "testSuiteName", testName: "testTestName", + logger, }; await api.uploadImageAndCreateSnapshot(params); - expect(consoleInfoSpy).toMatchSnapshot(); + expect(logged).toMatchSnapshot(); }); }); }); diff --git a/visual-js/visual-snapshots/test/api/worker/pdf-page-snapshot-uploader.spec.ts b/visual-js/visual-snapshots/test/api/worker/pdf-page-snapshot-uploader.spec.ts index addc650f..d774fd8e 100644 --- a/visual-js/visual-snapshots/test/api/worker/pdf-page-snapshot-uploader.spec.ts +++ b/visual-js/visual-snapshots/test/api/worker/pdf-page-snapshot-uploader.spec.ts @@ -73,13 +73,15 @@ describe("PdfPageSnapshotUploader", () => { "snapshotName-{filename}-{page}" ); - expect(uploadImageAndCreateSnapshot).toHaveBeenCalledWith({ - buildId: "build-id", - snapshot: await files[0].getPage(1), - snapshotName: `snapshotName-${files[0].path}-1`, - testName: `testName-${files[0].path}`, - suiteName: "suiteName", - }); + expect(uploadImageAndCreateSnapshot).toHaveBeenCalledWith( + expect.objectContaining({ + buildId: "build-id", + snapshot: await files[0].getPage(1), + snapshotName: `snapshotName-${files[0].path}-1`, + testName: `testName-${files[0].path}`, + suiteName: "suiteName", + }) + ); }); }); }); diff --git a/visual-js/visual-snapshots/test/app/pdf-handler.spec.ts b/visual-js/visual-snapshots/test/app/pdf-handler.spec.ts index 51ae9a96..be0738d9 100644 --- a/visual-js/visual-snapshots/test/app/pdf-handler.spec.ts +++ b/visual-js/visual-snapshots/test/app/pdf-handler.spec.ts @@ -146,7 +146,7 @@ describe("pdf-handler", () => { params ); - expect(finishBuildMock).toHaveBeenCalledWith(buildId); + expect(finishBuildMock).toHaveBeenCalledWith({ buildId }); }); it("should not finish build when buildId is passed", async () => { diff --git a/visual-js/visual-snapshots/test/helpers.ts b/visual-js/visual-snapshots/test/helpers.ts index 99860f7f..7a1569d8 100644 --- a/visual-js/visual-snapshots/test/helpers.ts +++ b/visual-js/visual-snapshots/test/helpers.ts @@ -1,5 +1,7 @@ import { dirname } from "node:path"; +import { Writable } from "node:stream"; import { fileURLToPath } from "node:url"; +import { pino } from "pino"; /** * ESM helper for getting __filename. Pass `import.meta` to this function. @@ -14,3 +16,25 @@ export const __filename = (meta: ImportMeta) => fileURLToPath(meta.url); * @returns __dirname equivalent */ export const __dirname = (meta: ImportMeta) => dirname(__filename(meta)); + +export function mockLogger() { + const logged: object[] = []; + + const stream = new Writable({ + write(chunk, _encoding, callback) { + const message = JSON.parse(chunk.toString("utf-8")); + delete message.time; + delete message.pid; + delete message.hostname; + logged.push(message); + callback(); + }, + }); + + function reset() { + logged.length = 0; + } + + const logger = pino(stream); + return { logger, logged, reset }; +}