Skip to content

Commit

Permalink
chore: add svg snapshots
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoiver committed Aug 5, 2024
1 parent a488b99 commit 75cf3c0
Show file tree
Hide file tree
Showing 31 changed files with 675 additions and 428 deletions.
22 changes: 11 additions & 11 deletions __tests__/ssr/canvas.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,20 @@ describe('Canvas API', () => {

it('should convert client & viewport coordinates correctly.', async () => {
const $canvas = getCanvas(200, 200);
$canvas.getBoundingClientRect = () => ({
x: 50,
y: 50,
top: 50,
left: 50,
width: 100,
height: 100,
bottom: 150,
right: 150,
toJSON: () => {},
});

const canvas = await new Canvas({
canvas: $canvas,
getBoundingClientRect: () => ({
x: 50,
y: 50,
top: 50,
left: 50,
width: 100,
height: 100,
bottom: 150,
right: 150,
toJSON: () => {},
}),
}).initialized;

expect(canvas.client2Viewport({ x: 50, y: 50 })).toEqual({ x: 0, y: 0 });
Expand Down
56 changes: 56 additions & 0 deletions __tests__/ssr/events.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import _gl from 'gl';
import { createMouseEvent, getCanvas, sleep } from '../utils';
import '../useSnapshotMatchers';
import { Canvas, Circle } from '../../packages/core/src';

describe('Events', () => {
['pointerDown', 'pointerUp', 'pointerMove'].forEach((type) => {
it(`should listen to ${type} correctly.`, async () => {
const $canvas = getCanvas(200, 200);
$canvas.getBoundingClientRect = () => ({
x: 0,
y: 0,
top: 0,
left: 0,
width: 200,
height: 200,
bottom: 200,
right: 200,
toJSON: () => {},
});

const canvas = await new Canvas({
canvas: $canvas,
setCursor: () => {},
}).initialized;
const circle = new Circle({
cx: 100,
cy: 100,
r: 50,
fill: 'red',
});
canvas.appendChild(circle);
canvas.render();

circle.addEventListener(type.toLowerCase(), () => {
circle.fill = 'green';
canvas.render();
});

canvas.pluginContext.hooks[type].call(
createMouseEvent(type.toLowerCase(), { clientX: 100, clientY: 100 }),
);

await sleep(1000);

const dir = `${__dirname}/snapshots`;

expect($canvas.getContext('webgl1')).toMatchWebGLSnapshot(
dir,
`events-${type.toLowerCase()}`,
);

canvas.destroy();
});
});
});
37 changes: 33 additions & 4 deletions __tests__/ssr/exporter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,48 @@ import _gl from 'gl';
import { getCanvas } from '../utils';
import '../useSnapshotMatchers';
import { Canvas, ImageExporter } from '../../packages/core/src';
import { JSDOM } from 'jsdom';
import xmlserializer from 'xmlserializer';
import { CheckboardStyle } from '../../packages/core/src/plugins';

describe('Image Exporter', () => {
it('should export empty canvas correctly.', async () => {
const canvas = await new Canvas({
canvas: getCanvas(200, 200),
}).initialized;

const exporter = new ImageExporter({ canvas, defaultFilename: 'test' });
const exporter = new ImageExporter({
canvas,
document: new JSDOM().window._document,
xmlserializer,
defaultFilename: 'test',
});

canvas.render();
const url = exporter.toSVGDataURL();
expect(url).toBe(
'data:image/svg+xml;charset=utf8,%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22200%22%3E%3Cg%20visibility%3D%22hidden%22%20transform%3D%22matrix(1%2C0%2C0%2C1%2C0%2C0)%22%20transform-origin%3D%220%200%22%2F%3E%3C%2Fsvg%3E',
let dataURL = exporter.toSVGDataURL({ grid: false });
expect(dataURL).toBe(
'data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22200%22%3E%3Cg%20visibility%3D%22hidden%22%20transform%3D%22matrix(1%2C0%2C0%2C1%2C0%2C0)%22%20transform-origin%3D%220%200%22%2F%3E%3C%2Fsvg%3E',
);
exporter.downloadImage({ dataURL });

// Lines grid
dataURL = exporter.toSVGDataURL({ grid: true });
expect(dataURL).toBe(
'data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22200%22%3E%3Cdefs%3E%3Cpattern%20id%3D%22small-grid%22%20width%3D%2210%22%20height%3D%2210%22%20patternUnits%3D%22userSpaceOnUse%22%3E%3Cpath%20d%3D%22M%2010%200%20L%200%200%200%2010%22%20fill%3D%22none%22%20stroke%3D%22rgba(221%2C221%2C221%2C1)%22%20stroke-width%3D%221%22%2F%3E%3C%2Fpattern%3E%3Cpattern%20id%3D%22grid%22%20width%3D%22100%22%20height%3D%22100%22%20patternUnits%3D%22userSpaceOnUse%22%3E%3Crect%20width%3D%22100%22%20height%3D%22100%22%20fill%3D%22url(%23small-grid)%22%2F%3E%3Cpath%20d%3D%22M%20100%200%20L%200%200%200%20100%22%20fill%3D%22none%22%20stroke%3D%22rgba(221%2C221%2C221%2C1)%22%20stroke-width%3D%222%22%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%23grid)%22%2F%3E%3Cg%20visibility%3D%22hidden%22%20transform%3D%22matrix(1%2C0%2C0%2C1%2C0%2C0)%22%20transform-origin%3D%220%200%22%2F%3E%3C%2Fsvg%3E',
);

// Dots grid
canvas.checkboardStyle = CheckboardStyle.DOTS;
dataURL = exporter.toSVGDataURL({ grid: true });
expect(dataURL).toBe(
'data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22200%22%3E%3Cdefs%3E%3Ccircle%20id%3D%22dot-tl%22%20cx%3D%220%22%20cy%3D%220%22%20r%3D%222%22%20fill%3D%22rgba(221%2C221%2C221%2C1)%22%2F%3E%3Ccircle%20id%3D%22dot-tr%22%20cx%3D%2220%22%20cy%3D%220%22%20r%3D%222%22%20fill%3D%22rgba(221%2C221%2C221%2C1)%22%2F%3E%3Ccircle%20id%3D%22dot-bl%22%20cx%3D%220%22%20cy%3D%2220%22%20r%3D%222%22%20fill%3D%22rgba(221%2C221%2C221%2C1)%22%2F%3E%3Ccircle%20id%3D%22dot-br%22%20cx%3D%2220%22%20cy%3D%2220%22%20r%3D%222%22%20fill%3D%22rgba(221%2C221%2C221%2C1)%22%2F%3E%3Cpattern%20id%3D%22dots-grid%22%20width%3D%2220%22%20height%3D%2220%22%20patternUnits%3D%22userSpaceOnUse%22%3E%3Cuse%20xlink%3Ahref%3D%22%23dot-bl%22%2F%3E%3Cuse%20xlink%3Ahref%3D%22%23dot-br%22%2F%3E%3Cuse%20xlink%3Ahref%3D%22%23dot-tl%22%2F%3E%3Cuse%20xlink%3Ahref%3D%22%23dot-tr%22%2F%3E%3C%2Fpattern%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%23dots-grid)%22%2F%3E%3Cg%20visibility%3D%22hidden%22%20transform%3D%22matrix(1%2C0%2C0%2C1%2C0%2C0)%22%20transform-origin%3D%220%200%22%2F%3E%3C%2Fsvg%3E',
);

// None grid
canvas.checkboardStyle = CheckboardStyle.NONE;
dataURL = exporter.toSVGDataURL({ grid: true });
expect(dataURL).toBe(
'data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22200%22%3E%3Cg%20visibility%3D%22hidden%22%20transform%3D%22matrix(1%2C0%2C0%2C1%2C0%2C0)%22%20transform-origin%3D%220%200%22%2F%3E%3C%2Fsvg%3E',
);

canvas.destroy();
Expand Down
Binary file added __tests__/ssr/snapshots/events-pointerdown.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __tests__/ssr/snapshots/events-pointermove.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added __tests__/ssr/snapshots/events-pointerup.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
71 changes: 71 additions & 0 deletions __tests__/toMatchSVGSnapshot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import * as path from 'path';
import * as fs from 'fs';
import xmlserializer from 'xmlserializer';
import { format } from 'prettier';

export type ToMatchSVGSnapshotOptions = {
fileFormat?: string;
};

// @see https://jestjs.io/docs/26.x/expect#expectextendmatchers
export function toMatchSVGSnapshot(
dom: SVGElement | null,
dir: string,
name: string,
options: ToMatchSVGSnapshotOptions = {},
): { message: () => string; pass: boolean } {
const { fileFormat = 'svg' } = options;
const namePath = path.join(dir, name);
const actualPath = path.join(dir, `${name}-actual.${fileFormat}`);
const expectedPath = path.join(dir, `${name}.${fileFormat}`);

let actual: string;
try {
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });

actual = dom
? format(xmlserializer.serializeToString(dom), {
parser: 'babel',
})
: 'null';

// Remove ';' after format by babel.
if (actual !== 'null') actual = actual.slice(0, -2);

if (!fs.existsSync(expectedPath)) {
if (process.env.CI === 'true') {
throw new Error(`Please generate golden image for ${namePath}`);
}
console.warn(`! generate ${namePath}`);
fs.writeFileSync(expectedPath, actual);
return {
message: () => `generate ${namePath}`,
pass: true,
};
} else {
const expected = fs.readFileSync(expectedPath, {
encoding: 'utf8',
flag: 'r',
});
if (actual === expected) {
if (fs.existsSync(actualPath)) fs.unlinkSync(actualPath);
return {
message: () => `match ${namePath}`,
pass: true,
};
}

// Perverse actual file.
if (actual) fs.writeFileSync(actualPath, actual);
return {
message: () => `mismatch ${namePath}`,
pass: false,
};
}
} catch (e) {
return {
message: () => `${e}`,
pass: false,
};
}
}
79 changes: 78 additions & 1 deletion __tests__/unit/serialize.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { serializeNode, Circle } from '../../packages/core/src';
import '../useSnapshotMatchers';
import { JSDOM } from 'jsdom';
import { serializeNode, Circle, toSVGElement } from '../../packages/core/src';

describe('Serialize', () => {
it('should serialize circle correctly.', () => {
Expand Down Expand Up @@ -53,5 +55,80 @@ describe('Serialize', () => {
visible: true,
},
});

const dir = `${__dirname}/snapshots`;

expect(
toSVGElement(serialized, new JSDOM().window._document),
).toMatchSVGSnapshot(dir, 'circle');
});

it('should serialize strokeAlignment correctly.', () => {
const circle = new Circle({
cx: 50,
cy: 50,
r: 50,
fill: '#F67676',
stroke: 'black',
strokeWidth: 20,
strokeOpacity: 0.5,
strokeAlignment: 'inner',
});
let serialized = serializeNode(circle);
expect(serialized).toEqual({
type: 'circle',
children: [],
uid: 1,
attributes: {
batchable: true,
cullable: true,
cx: 50,
cy: 50,
fill: '#F67676',
fillOpacity: 1,
innerShadowBlurRadius: 0,
innerShadowColor: 'black',
innerShadowOffsetX: 0,
innerShadowOffsetY: 0,
opacity: 1,
r: 50,
renderable: true,
stroke: 'black',
strokeAlignment: 'inner',
strokeOpacity: 0.5,
strokeWidth: 20,
transform: {
pivot: {
x: 0,
y: 0,
},
position: {
x: 0,
y: 0,
},
rotation: 0,
scale: {
x: 1,
y: 1,
},
skew: {
x: 0,
y: 0,
},
},
visible: true,
},
});

const dir = `${__dirname}/snapshots`;
expect(
toSVGElement(serialized, new JSDOM().window._document),
).toMatchSVGSnapshot(dir, 'circle-stroke-alignment-inner');

circle.strokeAlignment = 'outer';
serialized = serializeNode(circle);
expect(
toSVGElement(serialized, new JSDOM().window._document),
).toMatchSVGSnapshot(dir, 'circle-stroke-alignment-outer');
});
});
29 changes: 29 additions & 0 deletions __tests__/unit/snapshots/circle-stroke-alignment-inner.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions __tests__/unit/snapshots/circle-stroke-alignment-outer.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions __tests__/unit/snapshots/circle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 75cf3c0

Please sign in to comment.