diff --git a/visual-js/visual-snapshots/jest.config.cjs b/visual-js/visual-snapshots/jest.config.cjs index 54f691bf..82350928 100644 --- a/visual-js/visual-snapshots/jest.config.cjs +++ b/visual-js/visual-snapshots/jest.config.cjs @@ -1,20 +1,21 @@ /** @type {import('ts-jest').JestConfigWithTsJest} */ module.exports = { // [...] - preset: 'ts-jest/presets/default-esm', // or other ESM presets + preset: "ts-jest/presets/default-esm", // or other ESM presets moduleNameMapper: { - '^(\\.{1,2}/.*)\\.js$': '$1', + "^(\\.{1,2}/.*)\\.js$": "$1", }, - testPathIgnorePatterns: ['/lib/'], + testPathIgnorePatterns: ["/lib/"], + setupFiles: ["./jest.setup.mjs"], transform: { // '^.+\\.[tj]sx?$' to process js/ts with `ts-jest` // '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest` - '^.+\\.tsx?$': [ - 'ts-jest', + "^.+\\.tsx?$": [ + "ts-jest", { useESM: true, - tsconfig: 'tsconfig.test.json' + tsconfig: "tsconfig.test.json", }, ], }, -} +}; diff --git a/visual-js/visual-snapshots/jest.setup.mjs b/visual-js/visual-snapshots/jest.setup.mjs new file mode 100644 index 00000000..664a88f7 --- /dev/null +++ b/visual-js/visual-snapshots/jest.setup.mjs @@ -0,0 +1,2 @@ +import { jest } from "@jest/globals"; +global.jest = jest; diff --git a/visual-js/visual-snapshots/package.json b/visual-js/visual-snapshots/package.json index 25560676..8123e326 100644 --- a/visual-js/visual-snapshots/package.json +++ b/visual-js/visual-snapshots/package.json @@ -24,9 +24,9 @@ "watch": "rimraf *.tsbuildinfo && tsc -b tsconfig.json -w", "lint": "eslint \"{src,test}/**/*.ts\"", "lint-fix": "eslint \"{src,test}/**/*.ts\" --fix", - "test": "jest", - "test-update-snapshots": "jest -u", - "test-with-coverage": "jest --collect-coverage" + "test": "cross-env NODE_NO_WARNINGS=1 NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest", + "test-update-snapshots": "cross-env NODE_NO_WARNINGS=1 NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest -u", + "test-with-coverage": "cross-env NODE_NO_WARNINGS=1 NODE_OPTIONS=\"$NODE_OPTIONS --experimental-vm-modules\" jest --collect-coverage" }, "dependencies": { "@saucelabs/visual": "^0.13.0", diff --git a/visual-js/visual-snapshots/src/api/visual-client.ts b/visual-js/visual-snapshots/src/api/visual-client.ts index f0dcdb4c..c3445e8f 100644 --- a/visual-js/visual-snapshots/src/api/visual-client.ts +++ b/visual-js/visual-snapshots/src/api/visual-client.ts @@ -1,9 +1,9 @@ import { getApi, VisualConfig } from "@saucelabs/visual"; -export const initializeVisualApi = ( - params: VisualConfig, - clientVersion: string -) => - getApi(params, { - userAgent: `visual-snapshots/${clientVersion}`, - }); +export const visualApiInitializer = + (_getApi: typeof getApi) => (params: VisualConfig, clientVersion: string) => + _getApi(params, { + userAgent: `visual-snapshots/${clientVersion}`, + }); + +export const initializeVisualApi = visualApiInitializer(getApi); diff --git a/visual-js/visual-snapshots/src/app/pdf-converter.ts b/visual-js/visual-snapshots/src/app/pdf-converter.ts index 9baee08c..38a57a75 100644 --- a/visual-js/visual-snapshots/src/app/pdf-converter.ts +++ b/visual-js/visual-snapshots/src/app/pdf-converter.ts @@ -2,10 +2,14 @@ import { pdf } from "pdf-to-img"; import { PdfFile } from "./pdf-file.js"; export class PdfConverter { + constructor(private readonly _pdf: typeof pdf = pdf) {} + public async *convertPagesToImages( pdfFilePath: string ): AsyncGenerator { - for await (const pdfPageImage of await pdf(pdfFilePath, { scale: 1 })) { + for await (const pdfPageImage of await this._pdf(pdfFilePath, { + scale: 1, + })) { yield pdfPageImage; } } 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 ff5b60c5..8e1bd216 100644 --- a/visual-js/visual-snapshots/test/api/visual-api.spec.ts +++ b/visual-js/visual-snapshots/test/api/visual-api.spec.ts @@ -28,14 +28,14 @@ describe("VisualSnapshots", () => { const createSnapshotMock = jest.fn(); const finishBuildMock = jest.fn(); const buildStatusMock = jest.fn(); - const visualApiMock: VisualApi = { - ...jest.requireActual("@saucelabs/visual"), + const visualApiMock = { createBuild: createBuildMock, uploadSnapshot: uploadSnapshotMock, createSnapshot: createSnapshotMock, finishBuild: finishBuildMock, buildStatus: buildStatusMock, - }; + } as never as VisualApi; + const visualSnapshots = new VisualSnapshotsApi(visualApiMock); const files = [new TestPdfFile("file1.pdf"), new TestPdfFile("file2.pdf")]; diff --git a/visual-js/visual-snapshots/test/api/visual-client.spec.ts b/visual-js/visual-snapshots/test/api/visual-client.spec.ts index b6ed12bc..f303d9c7 100644 --- a/visual-js/visual-snapshots/test/api/visual-client.spec.ts +++ b/visual-js/visual-snapshots/test/api/visual-client.spec.ts @@ -1,4 +1,4 @@ -import { initializeVisualApi } from "../../src/api/visual-client.js"; +import { visualApiInitializer } from "../../src/api/visual-client.js"; import * as sauceVisual from "@saucelabs/visual"; jest.mock("@saucelabs/visual", () => { @@ -8,8 +8,9 @@ jest.mock("@saucelabs/visual", () => { }); describe("visual api client", () => { - test("initializeVisualApi", async () => { - const getApiSpy = sauceVisual.getApi; + test("visualApiInitializer", async () => { + const getApiSpy = jest.fn(); + const initializeVisualApi = visualApiInitializer(getApiSpy); const pkgVersion = "0.1.0"; const params = { diff --git a/visual-js/visual-snapshots/test/app/pdf-converter.spec.ts b/visual-js/visual-snapshots/test/app/pdf-converter.spec.ts index 075214ba..8818b5f4 100644 --- a/visual-js/visual-snapshots/test/app/pdf-converter.spec.ts +++ b/visual-js/visual-snapshots/test/app/pdf-converter.spec.ts @@ -1,35 +1,42 @@ +import path from "path"; import { PdfConverter } from "../../src/app/pdf-converter.js"; +import { __dirname } from "../helpers.js"; -jest.mock("pdf-to-img", () => { - async function* asyncIterable() { - for (let i = 0; i < 2; i++) { - yield `fake-image-buffer-${i}`; +describe("PdfConverter", () => { + test("convertPagesToImages", async () => { + const pdf = jest.fn().mockResolvedValue([]); + + const pdfFilePath = "./fake-pdf-file-path.pdf"; + const pdfConverter = new PdfConverter(pdf); + for await (const _ of pdfConverter.convertPagesToImages(pdfFilePath)) { } - } - return { - pdf: asyncIterable, - }; -}); + expect(pdf).toHaveBeenCalledWith(pdfFilePath, { scale: 1 }); + }); -describe("PdfConverter", () => { test("convertPagesToImages", async () => { - const pdfFilePath = "./fake-pdf-file-path.pdf"; + const pdfFilePath = path.join(__dirname(import.meta), "../files/test.pdf"); const pdfConverter = new PdfConverter(); - const pdfPageImagesGenerator = await pdfConverter.convertPagesToImages( - pdfFilePath - ); - - for (let i = 0; i < 2; ++i) { - const pdfPageImage = await pdfPageImagesGenerator.next(); - expect(pdfPageImage).toEqual({ - done: false, - value: `fake-image-buffer-${i}`, - }); + + let pages = 0; + for await (const _ of pdfConverter.convertPagesToImages(pdfFilePath)) { + pages++; } - expect(await pdfPageImagesGenerator.next()).toEqual({ - done: true, - value: undefined, - }); + + expect(pages).toEqual(3); + }); + + test("createPdfFile", async () => { + const pdf = jest.fn().mockResolvedValue([]); + const pdfConverter = new PdfConverter(pdf); + + const pdfFilePath = "./fake-pdf-file-path.pdf"; + const pdfFile = pdfConverter.createPdfFile(pdfFilePath); + + for await (const _ of pdfFile.convertPagesToImages()) { + } + + expect(pdfFile.path).toBe(pdfFilePath); + expect(pdf).toHaveBeenCalledWith(pdfFilePath, { scale: 1 }); }); }); diff --git a/visual-js/visual-snapshots/test/files/test.pdf b/visual-js/visual-snapshots/test/files/test.pdf new file mode 100644 index 00000000..580a9a8c Binary files /dev/null and b/visual-js/visual-snapshots/test/files/test.pdf differ diff --git a/visual-js/visual-snapshots/test/helpers.ts b/visual-js/visual-snapshots/test/helpers.ts new file mode 100644 index 00000000..99860f7f --- /dev/null +++ b/visual-js/visual-snapshots/test/helpers.ts @@ -0,0 +1,16 @@ +import { dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +/** + * ESM helper for getting __filename. Pass `import.meta` to this function. + * @param meta `import.meta` + * @returns __filename equivalent + */ +export const __filename = (meta: ImportMeta) => fileURLToPath(meta.url); + +/** + * ESM helper for getting __dirname. Pass `import.meta` to this function. + * @param meta `import.meta` + * @returns __dirname equivalent + */ +export const __dirname = (meta: ImportMeta) => dirname(__filename(meta)); diff --git a/visual-js/visual-snapshots/test/utils/glob.spec.ts b/visual-js/visual-snapshots/test/utils/glob.spec.ts index 8cd209cb..15787e9a 100644 --- a/visual-js/visual-snapshots/test/utils/glob.spec.ts +++ b/visual-js/visual-snapshots/test/utils/glob.spec.ts @@ -1,56 +1,60 @@ import { getFiles } from "../../src/utils/glob.js"; import path from "path"; +import { __dirname, __filename } from "../helpers.js"; describe("getFiles", () => { - function resolvePath(p: string) { - return path.resolve(p); + function normalize(paths: string[]) { + return paths.map((p) => path.resolve(p)).sort((a, b) => a.localeCompare(b)); } it("should return a file", async () => { const input = ["./src/index.ts"]; - const expected = input.map(resolvePath); + const expected = normalize(input); const result = await getFiles(input, "*"); - expect(result.map(resolvePath)).toEqual(expected); + expect(normalize(result)).toEqual(expected); }); it("should return multiple files", async () => { - const input = ["./src/index.ts", __filename]; - const expected = input.map(resolvePath); + const input = ["./src/index.ts", __filename(import.meta)]; + const expected = normalize(input); const actual = await getFiles(input, "*"); - expect(actual.map(resolvePath)).toEqual(expected); + expect(normalize(actual)).toEqual(expected); }); it("should return files matched by glob", async () => { - const input = [path.join(__dirname, "*.spec.ts")]; - const expected = [resolvePath(__filename)]; + const input = [path.join(__dirname(import.meta), "*.spec.ts")]; + const expected = normalize([__filename(import.meta)]); const actual = await getFiles(input, "*"); - expect(actual.map(resolvePath)).toEqual(expect.arrayContaining(expected)); + expect(normalize(actual)).toEqual(expect.arrayContaining(expected)); }); it("should return files in directory matched by dir glob", async () => { - const input = [__dirname]; - const expected = [resolvePath(__filename)]; + const input = [__dirname(import.meta)]; + const expected = normalize([__filename(import.meta)]); const actual = await getFiles(input, "*.spec.ts"); - expect(actual.map(resolvePath)).toEqual(expect.arrayContaining(expected)); + expect(normalize(actual)).toEqual(expect.arrayContaining(expected)); }); it("should not return non-existing files", async () => { - const input = [__filename, __filename + ".not-existing"]; - const expected = [resolvePath(__filename)]; + const input = [ + __filename(import.meta), + __filename(import.meta) + ".not-existing", + ]; + const expected = normalize([__filename(import.meta)]); const result = await getFiles(input, "*"); - expect(result.map(resolvePath)).toEqual(expected); + expect(normalize(result)).toEqual(expected); }); it("should not return files from not existing dirs", async () => { - const input = [__dirname + ".not-existing"]; + const input = [__dirname(import.meta) + ".not-existing"]; const expected: string[] = []; const result = await getFiles(input, "*"); - expect(result.map(resolvePath)).toEqual(expected); + expect(normalize(result)).toEqual(expected); }); });