diff --git a/server/src/api.js b/server/src/api.js index 6d2f179..db66c31 100644 --- a/server/src/api.js +++ b/server/src/api.js @@ -1,6 +1,11 @@ import fastify from "fastify"; import { compare } from "./compare.js"; import { render } from "./render.js"; +import pino from "pino"; + +const logger = pino({ + level: process.env.LOG_LEVEL || "info", +}); export function buildFastify(renderFn, compareFn) { const server = fastify({ @@ -25,6 +30,7 @@ export function buildFastify(renderFn, compareFn) { actualHtml: { type: "string" }, fullpage: { type: "boolean" }, viewport: { $ref: "#viewport" }, + style: { type: "string" }, }, required: ["actualHtml", "expected", "viewport"], }); @@ -36,6 +42,7 @@ export function buildFastify(renderFn, compareFn) { actualHtml: { type: "string" }, fullpage: { type: "boolean" }, viewport: { $ref: "#viewport" }, + style: { type: "string" }, }, required: ["actualHtml", "viewport"], }); @@ -51,8 +58,9 @@ export function buildFastify(renderFn, compareFn) { const actualHtml = request.body.actualHtml; const viewport = request.body.viewport; const fullpage = request.body.fullpage ?? false; + const style = request.body.style; - const actual = await renderFn(actualHtml, viewport, fullpage); + const actual = await renderFn(actualHtml, viewport, fullpage, style); const result = await compareFn(expected, actual); return { @@ -74,8 +82,9 @@ export function buildFastify(renderFn, compareFn) { const actualHtml = request.body.actualHtml; const viewport = request.body.viewport; const fullpage = request.body.fullpage ?? false; + const style = request.body.style; - const actual = await renderFn(actualHtml, viewport, fullpage); + const actual = await renderFn(actualHtml, viewport, fullpage, style); return { actual: actual.toString("base64"), @@ -83,6 +92,16 @@ export function buildFastify(renderFn, compareFn) { }, }); + server.setErrorHandler((error, request, reply) => { + logger.error(error.message); + const errorResponse = { + message: error.message, + error: error.error, + statusCode: error.statusCode || 500, + }; + reply.code(errorResponse.statusCode).send(errorResponse); + }); + return server; } diff --git a/server/src/api.test.js b/server/src/api.test.js index 99bd56d..4f048ba 100644 --- a/server/src/api.test.js +++ b/server/src/api.test.js @@ -58,6 +58,7 @@ describe("api", () => { actualHtml: "

Hello World

", expected: expected, viewport: { width: 1920, height: 1024 }, + style: "svg { visibility: hidden; }", }, }); @@ -100,6 +101,7 @@ describe("api", () => { payload: { actualHtml: "

Hello World

", viewport: { width: 1920, height: 1024 }, + style: "svg { visibility: hidden; }", }, }); diff --git a/server/src/compare.js b/server/src/compare.js index 9555215..e816483 100644 --- a/server/src/compare.js +++ b/server/src/compare.js @@ -2,15 +2,21 @@ import pixelmatch from "pixelmatch"; import { PNG } from "pngjs"; export async function compare(expected, actual, options = { threshold: 0.01 }) { - const { width, height, data: expectedPng } = PNG.sync.read(expected); - const { data: actualPng } = PNG.sync.read(actual); - const diffPng = new PNG({ width, height }); + const expectedPng = PNG.sync.read(expected); + const actualPng = PNG.sync.read(actual); + const diffDimensions = { + width: Math.max(expectedPng.width, actualPng.width), + height: Math.max(expectedPng.height, actualPng.height), + }; + const resizedExpectedPng = createResized(expectedPng, diffDimensions); + const resizedActualPng = createResized(actualPng, diffDimensions); + const diffPng = new PNG(diffDimensions); const numDiffPixels = pixelmatch( - expectedPng, - actualPng, + resizedExpectedPng.data, + resizedActualPng.data, diffPng.data, - width, - height, + diffDimensions.width, + diffDimensions.height, options, ); const diff = PNG.sync.write(diffPng); @@ -22,3 +28,20 @@ export async function compare(expected, actual, options = { threshold: 0.01 }) { diff, }; } + +/** Cretes a copy of {@link img}, with the {@link dimensions}. + * @param {PNG} img + * @param {{width: number, height: number}} dimensions + * @returns {PNG} + */ +function createResized(img, dimensions) { + if (img.width > dimensions.width || img.height > dimensions.height) { + throw new Error( + `New dimensions expected to be greater than or equal to the original dimensions!`, + ); + } + const resized = new PNG(dimensions); + PNG.bitblt(img, resized, 0, 0, img.width, img.height); + + return resized; +} diff --git a/server/src/render.js b/server/src/render.js index 97e617f..0d5f4a9 100644 --- a/server/src/render.js +++ b/server/src/render.js @@ -32,13 +32,13 @@ export class BrowserRenderer { logger.debug("Rendering browser started"); } - async screenshot(url, viewport, fullPage) { + async screenshot(url, viewport, fullPage, style) { logger.debug("Creating screenshot", { url }); const page = await this.browser.newPage({ viewport }); logger.debug("Waiting for page to load", { url }); await page.goto(url); logger.debug(`Page loaded`, { url }); - const screenshot = await page.screenshot({ fullPage }); + const screenshot = await page.screenshot({ fullPage, style }); logger.debug(`Screenshot taken`); return screenshot; }