Skip to content

Commit

Permalink
Merge pull request #2670 from exadel-inc/e2e/size-mismatch
Browse files Browse the repository at this point in the history
test(e2e): handle sanpshot's size mismatch
  • Loading branch information
ala-n authored Oct 21, 2024
2 parents a04ea1e + 700909b commit da3fb97
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 14 deletions.
27 changes: 17 additions & 10 deletions e2e/setup/serializers/image-snapshot.matcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import type {SnapshotData} from './image-snapshot.pocessor';

export type SnapshotMatcherOptions = pixelmatch.PixelmatchOptions;

type ImagesCompareResult = {
reason: 'content';
} | {
reason: 'error';
message: any;
} | undefined;

export class SnapshotMatcher {
protected static readonly MIN_DIFF_THRESHOLD: number = 0.0001;

Expand All @@ -25,7 +32,7 @@ export class SnapshotMatcher {
}

public async match(): Promise<jest.CustomMatcherResult> {
const {current, previous, snapshotPath} = this.received;
const {current, previous, snapshotPath, diffPath} = this.received;
if (!previous.buffer) {
mkDir(path.dirname(snapshotPath));
current.img.toFile(snapshotPath);
Expand All @@ -34,31 +41,31 @@ export class SnapshotMatcher {

const diff = await this.compareImages();
if (!diff) return this.getMatcherResult(true, `Image is the same as the snapshot: ${snapshotPath}`);
if (diff.reason === 'content') return this.getMatcherResult(false, `Image mismatch found: ${diff.path}`);
return this.getMatcherResult(false, `Error comparing snapshot to image ${snapshotPath}`);
if (diff.reason === 'content') return this.getMatcherResult(false, `Image mismatch found: ${diffPath}`);
return this.getMatcherResult(false, `Error comparing snapshot to image ${snapshotPath}\nMessage: ${diff.message}`);
}

protected getMatcherResult(pass: boolean, message: string): jest.CustomMatcherResult {
return {pass, message: () => message};
}

protected async compareImages(): Promise<{reason: 'content' | 'error', path?: string} | undefined> {
protected async compareImages(): Promise<ImagesCompareResult> {
const {current, previous, diffPath} = this.received;
const prevImg = previous.buffer!;
const currImg = current.buffer;
const prevBuffer = previous.buffer!;
const currBuffer = current.buffer;

const {width, height} = prevImg.info;
const {width, height} = prevBuffer.info;
const diffBuffer = Buffer.alloc(width * height * 4);
try {
const numDiffPixel = pixelmatch(prevImg.data, currImg.data, diffBuffer, width, height, this.config);
const numDiffPixel = pixelmatch(prevBuffer.data, currBuffer.data, diffBuffer, width, height, this.config);
if (numDiffPixel > width * height * SnapshotMatcher.MIN_DIFF_THRESHOLD) {
const diffConfig = Object.assign({}, this.received, {diffBuffer});
mkDir(path.dirname(diffPath));
await (await DiffImageComposer.compose(diffConfig)).toFile(diffPath);
return {reason: 'content', path: diffPath};
return {reason: 'content'};
}
} catch (e) {
return {reason: 'error'};
return {reason: 'error', message: e};
}
}
}
14 changes: 10 additions & 4 deletions e2e/setup/serializers/image-snapshot.pocessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,24 @@ export class SnapshotDataProcessor {
const diffPath = path.join(diffDir, buildSnapshotName(currentTestName!, 'diff'));

const shouldUpdate = !fs.existsSync(snapshotPath) || context.snapshotState._updateSnapshot === 'all';
const recievedJPG = SharpService.toJPEG(received);
const [currentBuffer, previousBuffer] = shouldUpdate
? [received, undefined]
: await SharpService.normalizeImages(received, snapshotPath);

const currentJPG = SharpService.toJPEG(currentBuffer);
const currentRAWBuffer = await SharpService.toRawBuffered(await currentJPG.toBuffer());

return {
diffPath,
snapshotPath,

current: {
img: recievedJPG,
buffer: await (SharpService.toRawBuffered(await recievedJPG.toBuffer()))
img: currentJPG,
buffer: currentRAWBuffer
},

previous: {
buffer: shouldUpdate ? undefined : await (SharpService.toRawBuffered(snapshotPath))
buffer: previousBuffer ? await (SharpService.toRawBuffered(previousBuffer)) : undefined
}
};
}
Expand Down
36 changes: 36 additions & 0 deletions e2e/setup/serializers/image-snapshot.sharp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,40 @@ export class SharpService {
public static toJPEGBufferd(input: string | Buffer, options: any = {}): Promise<Buffer> {
return SharpService.toJPEG(input, options).toBuffer();
}

public static async normalizeImages(img1: Buffer | string, img2: Buffer | string): Promise<[Buffer, Buffer]> {
const imgCtx1 = sharp(img1);
const imgCtx2 = sharp(img2);

const metadata1 = await imgCtx1.metadata();
const metadata2 = await imgCtx2.metadata();

const targetWidth = Math.max(metadata1.width!, metadata2.width!);
const targetHeight = Math.max(metadata1.height!, metadata2.height!);

const padImage = async (
image: sharp.Sharp,
width: number,
height: number,
metadata: sharp.Metadata
): Promise<Buffer> => {
if (metadata.width === width && metadata.height === height) return image.toBuffer();

const padX = Math.max(0, (width - metadata.width!) / 2);
const padY = Math.max(0, (height - metadata.height!) / 2);

return image.extend({
top: Math.floor(padY),
bottom: Math.ceil(padY),
left: Math.floor(padX),
right: Math.ceil(padX),
background: {r: 255, g: 255, b: 255, alpha: 1}
})
.toBuffer();
};

const normalizedBuffer1 = await padImage(imgCtx1, targetWidth, targetHeight, metadata1);
const normalizedBuffer2 = await padImage(imgCtx2, targetWidth, targetHeight, metadata2);
return [normalizedBuffer1, normalizedBuffer2];
}
}

0 comments on commit da3fb97

Please sign in to comment.