Skip to content

Commit 17eeb08

Browse files
chore: get rid of extra base64 png parsing
1 parent 03a5228 commit 17eeb08

3 files changed

Lines changed: 63 additions & 4 deletions

File tree

src/image.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@ import looksSame from "looks-same";
33
import { loadEsm } from "./utils/preload-utils";
44
import { DiffOptions, ImageSize } from "./types";
55
import { convertRgbaToPng } from "./utils/eight-bit-rgba-to-png";
6-
import { BITS_IN_BYTE, PNG_HEIGHT_OFFSET, PNG_WIDTH_OFFSET, RGBA_CHANNELS } from "./constants/png";
6+
import {
7+
BITS_IN_BYTE,
8+
PNG_HEIGHT_OFFSET,
9+
PNG_MIN_ASSIST_BYTES,
10+
PNG_SIGNATURE,
11+
PNG_WIDTH_OFFSET,
12+
RGBA_CHANNELS,
13+
} from "./constants/png";
714

815
interface PngImageData {
916
data: Buffer;
@@ -49,6 +56,28 @@ const jsquashDecode = (buffer: ArrayBuffer): Promise<ImageData> => {
4956
]).then(([mod]) => mod.decode(buffer, { bitDepth: BITS_IN_BYTE }));
5057
};
5158

59+
export const extractBase64PngSize = (base64EncodedString: string): ImageSize => {
60+
// Each 6 bits sequence encoded with 1 base64 char
61+
const bytesToBase64CharsRatio = 8 / 6;
62+
63+
if (base64EncodedString.length <= PNG_MIN_ASSIST_BYTES * bytesToBase64CharsRatio) {
64+
throw new Error("Invalid base64 encoded png: too short");
65+
}
66+
67+
const headerBytesToRead = Math.max(PNG_WIDTH_OFFSET, PNG_HEIGHT_OFFSET) + 4;
68+
const headerCharsToRead = headerBytesToRead * bytesToBase64CharsRatio;
69+
const pngHeader = Buffer.from(base64EncodedString.slice(0, headerCharsToRead), "base64");
70+
71+
if (!pngHeader.subarray(0, PNG_SIGNATURE.byteLength).equals(PNG_SIGNATURE)) {
72+
throw new Error("Invalid base64 encoded png: signature missmatch");
73+
}
74+
75+
return {
76+
width: pngHeader.readUInt32BE(PNG_WIDTH_OFFSET),
77+
height: pngHeader.readUInt32BE(PNG_HEIGHT_OFFSET),
78+
};
79+
};
80+
5281
export class Image {
5382
private _imgDataPromise: Promise<Buffer>;
5483
private _imgData: Buffer | null = null;

src/worker/runner/test-runner/one-time-screenshooter.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use strict";
22

3-
const { Image } = require("../../../image");
3+
const { extractBase64PngSize } = require("../../../image");
44
const ScreenShooter = require("../../../browser/screen-shooter");
55
const logger = require("../../../utils/logger");
66
const { promiseTimeout } = require("../../../utils/promise");
@@ -106,8 +106,7 @@ module.exports = class OneTimeScreenshooter {
106106

107107
async _makeViewportScreenshot() {
108108
const base64 = await this._browser.publicAPI.takeScreenshot();
109-
const image = Image.fromBase64(base64);
110-
const size = await image.getSize();
109+
const size = extractBase64PngSize(base64);
111110

112111
return { base64, size };
113112
}

test/src/image.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use strict";
22

33
const proxyquire = require("proxyquire");
4+
const { extractBase64PngSize } = require("src/image");
45

56
describe("Image", () => {
67
const sandbox = sinon.createSandbox();
@@ -53,6 +54,36 @@ describe("Image", () => {
5354

5455
afterEach(() => sandbox.restore());
5556

57+
describe("extractBase64PngSize", () => {
58+
it("should throw error on invalid small strings", () => {
59+
const fn = () => extractBase64PngSize("foobar");
60+
61+
assert.throw(fn, "Invalid base64 encoded png: too short");
62+
});
63+
64+
it("should throw error on non-base64 png strings", () => {
65+
const fn = () => extractBase64PngSize("foobar".repeat(20));
66+
67+
assert.throw(fn, "Invalid base64 encoded png: signature missmatch");
68+
});
69+
70+
it("should work with minimal png", () => {
71+
const minimalPng =
72+
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQImWNgYGAAAAAEAAGjChXjAAAAAElFTkSuQmCC";
73+
const result = extractBase64PngSize(minimalPng);
74+
75+
assert.deepEqual(result, { width: 1, height: 1 });
76+
});
77+
78+
it("should extract size", () => {
79+
const tenPxSquarePng =
80+
"iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mP8z8BQz0AEYBxVSF+FABJADveWkH6oAAAAAElFTkSuQmCC";
81+
const result = extractBase64PngSize(tenPxSquarePng);
82+
83+
assert.deepEqual(result, { width: 10, height: 10 });
84+
});
85+
});
86+
5687
describe("constructor", () => {
5788
it("should read width and height from PNG buffer", () => {
5889
const buffer = createMockPngBuffer(200, 150);

0 commit comments

Comments
 (0)