Skip to content

Commit daa5404

Browse files
committed
Upgrade pixelmatch to v7 and refactor imports
Upgraded the 'pixelmatch' dependency to version 7.1.0 and removed '@types/pixelmatch' as types are now included. Refactored PixelmatchService to use dynamic import for pixelmatch, updated related tests to mock the new import pattern, and adjusted Jest configs to handle ESM modules. Also updated TypeScript configs to use 'NodeNext' module resolution and added 'isolatedModules'. Minor import type-only refactors were made for improved type safety.
1 parent a5da984 commit daa5404

File tree

14 files changed

+56
-49
lines changed

14 files changed

+56
-49
lines changed

package-lock.json

Lines changed: 6 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
"passport-local": "^1.0.0",
5858
"pg": "^8.16.3",
5959
"pg-hstore": "^2.3.4",
60-
"pixelmatch": "^5.3.0",
60+
"pixelmatch": "^7.1.0",
6161
"pngjs": "^7.0.0",
6262
"reflect-metadata": "^0.1.13",
6363
"rimraf": "^5.0.1",
@@ -79,7 +79,6 @@
7979
"@types/node": "^20.14.10",
8080
"@types/passport-jwt": "^3.0.9",
8181
"@types/passport-local": "^1.0.38",
82-
"@types/pixelmatch": "^5.2.6",
8382
"@types/pngjs": "^6.0.5",
8483
"@types/supertest": "^2.0.12",
8584
"@typescript-eslint/eslint-plugin": "^8.46.3",

prisma/schema.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
generator client {
22
provider = "prisma-client-js"
3+
output = "../node_modules/.prisma/client"
34
binaryTargets = ["native", "debian-openssl-3.0.x", "linux-arm64-openssl-3.0.x"]
45
}
56

prisma/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"compilerOptions": {
3-
"module": "commonjs",
3+
"module": "NodeNext",
44
"declaration": true,
55
"removeComments": true,
66
"emitDecoratorMetadata": true,
@@ -11,6 +11,7 @@
1111
"incremental": true,
1212
"skipLibCheck": true,
1313
"esModuleInterop": true,
14+
"isolatedModules": true,
1415
// See: https://github.com/prisma/prisma/issues/10203
1516
"strictNullChecks": true
1617
},

src/builds/builds.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { Build, Role } from '@prisma/client';
2323
import { BuildDto } from './dto/build.dto';
2424
import { MixedGuard } from '../auth/guards/mixed.guard';
2525
import { PaginatedBuildDto } from './dto/build-paginated.dto';
26-
import { ModifyBuildDto } from './dto/build-modify.dto';
26+
import type { ModifyBuildDto } from './dto/build-modify.dto';
2727
import { ProjectsService } from '../projects/projects.service';
2828
import { RoleGuard } from '../auth/guards/role.guard';
2929
import { Roles } from '../shared/roles.decorator';

src/builds/builds.service.spec.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ import { BuildsService } from './builds.service';
33
import { PrismaService } from '../prisma/prisma.service';
44
import { TestRunsService } from '../test-runs/test-runs.service';
55
import { EventsGateway } from '../shared/events/events.gateway';
6-
import { Build, TestRun, TestStatus } from '@prisma/client';
6+
import { Build, Prisma, TestRun, TestStatus } from '@prisma/client';
77
import { mocked, MockedObject } from 'jest-mock';
88
import { BuildDto } from './dto/build.dto';
99
import { ProjectsService } from '../projects/projects.service';
1010
import { generateTestRun } from '../_data_';
11-
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
1211

1312
jest.mock('./dto/build.dto');
1413

@@ -387,7 +386,7 @@ describe('BuildsService', () => {
387386
it('create with retry', async () => {
388387
const buildUpsertMock = jest
389388
.fn()
390-
.mockRejectedValueOnce(new PrismaClientKnownRequestError('mock error', { code: 'P2002', clientVersion: '5' }));
389+
.mockRejectedValueOnce(new Prisma.PrismaClientKnownRequestError('mock error', { code: 'P2002', clientVersion: '5' }));
391390
const buildUpdateMock = jest.fn().mockResolvedValueOnce(build);
392391
service = await initService({ buildUpsertMock, buildUpdateMock });
393392
service.incrementBuildNumber = jest.fn().mockResolvedValueOnce(build);

src/compare/libs/pixelmatch/pixelmatch.service.spec.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import { TestingModule, Test } from '@nestjs/testing';
22
import { TestStatus } from '@prisma/client';
3-
import Pixelmatch from 'pixelmatch';
43
import { PNG } from 'pngjs';
5-
import { mocked } from 'jest-mock';
64
import { StaticService } from '../../../static/static.service';
75
import { DIFF_DIMENSION_RESULT, EQUAL_RESULT, NO_BASELINE_RESULT } from '../consts';
86
import { DEFAULT_CONFIG, PixelmatchService } from './pixelmatch.service';
97
import { PixelmatchConfig } from './pixelmatch.types';
108

11-
jest.mock('pixelmatch');
9+
const mockPixelmatch = jest.fn();
1210

1311
const initService = async ({ getImageMock = jest.fn(), saveImageMock = jest.fn(), deleteImageMock = jest.fn() }) => {
1412
const module: TestingModule = await Test.createTestingModule({
@@ -25,7 +23,12 @@ const initService = async ({ getImageMock = jest.fn(), saveImageMock = jest.fn()
2523
],
2624
}).compile();
2725

28-
return module.get<PixelmatchService>(PixelmatchService);
26+
const service = module.get<PixelmatchService>(PixelmatchService);
27+
28+
// Spy on loadPixelmatch to return our mock instead of dynamic import
29+
jest.spyOn(service as any, 'loadPixelmatch').mockResolvedValue(mockPixelmatch);
30+
31+
return service;
2932
};
3033

3134
let service: PixelmatchService;
@@ -123,7 +126,7 @@ describe('getDiff', () => {
123126
const getImageMock = jest.fn().mockReturnValueOnce(image).mockReturnValueOnce(baseline);
124127
const diffName = 'diff name';
125128
const saveImageMock = jest.fn().mockReturnValueOnce(diffName);
126-
mocked(Pixelmatch).mockReturnValueOnce(5);
129+
mockPixelmatch.mockReturnValueOnce(5);
127130
service = await initService({ saveImageMock, getImageMock });
128131

129132
const result = await service.getDiff(
@@ -141,7 +144,7 @@ describe('getDiff', () => {
141144
}
142145
);
143146

144-
expect(mocked(Pixelmatch)).toHaveBeenCalledWith(
147+
expect(mockPixelmatch).toHaveBeenCalledWith(
145148
new PNG({
146149
width: 2,
147150
height: 5,
@@ -185,7 +188,7 @@ describe('getDiff', () => {
185188
const saveImageMock = jest.fn();
186189
service = await initService({ saveImageMock, getImageMock });
187190
const pixelMisMatchCount = 150;
188-
mocked(Pixelmatch).mockReturnValueOnce(pixelMisMatchCount);
191+
mockPixelmatch.mockReturnValueOnce(pixelMisMatchCount);
189192

190193
const result = await service.getDiff(
191194
{
@@ -220,7 +223,7 @@ describe('getDiff', () => {
220223
});
221224
const getImageMock = jest.fn().mockReturnValueOnce(image).mockReturnValueOnce(baseline);
222225
const pixelMisMatchCount = 200;
223-
mocked(Pixelmatch).mockReturnValueOnce(pixelMisMatchCount);
226+
mockPixelmatch.mockReturnValueOnce(pixelMisMatchCount);
224227
const diffName = 'diff name';
225228
const saveImageMock = jest.fn().mockReturnValueOnce(diffName);
226229
service = await initService({

src/compare/libs/pixelmatch/pixelmatch.service.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Injectable, Logger } from '@nestjs/common';
22
import { TestStatus } from '@prisma/client';
3-
import Pixelmatch from 'pixelmatch';
43
import { PNG } from 'pngjs';
54
import { StaticService } from '../../../static/static.service';
65
import { DiffResult } from '../../../test-runs/diffResult';
@@ -18,11 +17,18 @@ export class PixelmatchService implements ImageComparator {
1817

1918
constructor(private readonly staticService: StaticService) {}
2019

20+
protected async loadPixelmatch() {
21+
const { default: pixelmatch } = await import('pixelmatch');
22+
return pixelmatch;
23+
}
24+
2125
parseConfig(configJson: string): PixelmatchConfig {
2226
return parseConfig(configJson, DEFAULT_CONFIG, this.logger);
2327
}
2428

2529
async getDiff(data: ImageCompareInput, config: PixelmatchConfig): Promise<DiffResult> {
30+
const pixelmatch = await this.loadPixelmatch();
31+
2632
const result: DiffResult = {
2733
...NO_BASELINE_RESULT,
2834
};
@@ -58,10 +64,17 @@ export class PixelmatchService implements ImageComparator {
5864
width: maxWidth,
5965
height: maxHeight,
6066
});
61-
result.pixelMisMatchCount = Pixelmatch(baselineIgnored.data, imageIgnored.data, diff.data, maxWidth, maxHeight, {
62-
includeAA: config.ignoreAntialiasing,
63-
threshold: config.threshold,
64-
});
67+
result.pixelMisMatchCount = pixelmatch(
68+
baselineIgnored.data as Uint8Array,
69+
imageIgnored.data as Uint8Array,
70+
diff.data as Uint8Array,
71+
maxWidth,
72+
maxHeight,
73+
{
74+
includeAA: config.ignoreAntialiasing,
75+
threshold: config.threshold,
76+
}
77+
);
6578
result.diffPercent = (result.pixelMisMatchCount * 100) / (scaledImage.width * scaledImage.height);
6679

6780
// process result

src/jest.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ module.exports = async () => {
77
transform: {
88
'^.+\\.(t|j)s$': 'ts-jest',
99
},
10+
transformIgnorePatterns: [
11+
'node_modules/(?!(pixelmatch)/)',
12+
],
1013
coverageDirectory: '../coverage',
1114
testEnvironment: 'node',
1215
};

src/static/static.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Controller, Get, Logger, Param, Res } from '@nestjs/common';
2-
import { Response } from 'express';
2+
import type { Response } from 'express';
33
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
44
import { StaticService } from './static.service';
55

0 commit comments

Comments
 (0)