test: Add unit tests for package/utils (#3357)

* Update return type to reflect actual signature

* add tests

* Set getDimensions as optional

* add newlines between specs

* remove redundant assertion

* move fixtures to separate files

* Add spacing

* Move tests, add cases

* Add unit tests for package/utils exportToSvg

* extract default object in test

* Move test suite to new file
This commit is contained in:
Hampus Lavin 2021-03-31 14:28:25 +02:00 committed by GitHub
parent bdf6e53289
commit 4ac1841d92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 437 additions and 2 deletions

View File

@ -11,7 +11,7 @@ import { restore } from "../data/restore";
type ExportOpts = {
elements: readonly ExcalidrawElement[];
appState?: Partial<Omit<AppState, "offsetTop" | "offsetLeft">>;
getDimensions: (
getDimensions?: (
width: number,
height: number,
) => { width: number; height: number; scale: number };
@ -83,7 +83,7 @@ export const exportToSvg = ({
appState = getDefaultAppState(),
exportPadding,
metadata,
}: ExportOpts & {
}: Omit<ExportOpts, "getDimensions"> & {
exportPadding?: number;
metadata?: string;
}): SVGSVGElement => {

31
src/tests/fixtures/diagramFixture.ts vendored Normal file
View File

@ -0,0 +1,31 @@
import {
diamondFixture,
ellipseFixture,
rectangleFixture,
} from "./elementFixture";
export const diagramFixture = {
type: "excalidraw",
version: 2,
source: "https://excalidraw.com",
elements: [diamondFixture, ellipseFixture, rectangleFixture],
appState: {
viewBackgroundColor: "#ffffff",
gridSize: null,
},
};
export const diagramFactory = ({
overrides = {},
elementOverrides = {},
} = {}) => ({
...diagramFixture,
elements: [
{ ...diamondFixture, ...elementOverrides },
{ ...ellipseFixture, ...elementOverrides },
{ ...rectangleFixture, ...elementOverrides },
],
...overrides,
});
export default diagramFixture;

37
src/tests/fixtures/elementFixture.ts vendored Normal file
View File

@ -0,0 +1,37 @@
import { ExcalidrawElement } from "../../element/types";
const elementBase: Omit<ExcalidrawElement, "type"> = {
id: "vWrqOAfkind2qcm7LDAGZ",
x: 414,
y: 237,
width: 214,
height: 214,
angle: 0,
strokeColor: "#000000",
backgroundColor: "#15aabf",
fillStyle: "hachure",
strokeWidth: 1,
strokeStyle: "solid",
roughness: 1,
opacity: 100,
groupIds: [],
strokeSharpness: "sharp",
seed: 1041657908,
version: 120,
versionNonce: 1188004276,
isDeleted: false,
boundElementIds: null,
};
export const rectangleFixture: ExcalidrawElement = {
...elementBase,
type: "rectangle",
};
export const ellipseFixture: ExcalidrawElement = {
...elementBase,
type: "ellipse",
};
export const diamondFixture: ExcalidrawElement = {
...elementBase,
type: "diamond",
};

View File

@ -0,0 +1,80 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`exportToSvg with default arguments 1`] = `
Object {
"collaborators": Map {},
"currentChartType": "bar",
"currentItemBackgroundColor": "transparent",
"currentItemEndArrowhead": "arrow",
"currentItemFillStyle": "hachure",
"currentItemFontFamily": 1,
"currentItemFontSize": 20,
"currentItemLinearStrokeSharpness": "round",
"currentItemOpacity": 100,
"currentItemRoughness": 1,
"currentItemStartArrowhead": null,
"currentItemStrokeColor": "#000000",
"currentItemStrokeSharpness": "sharp",
"currentItemStrokeStyle": "solid",
"currentItemStrokeWidth": 1,
"currentItemTextAlign": "left",
"cursorButton": "up",
"draggingElement": null,
"editingElement": null,
"editingGroupId": null,
"editingLinearElement": null,
"elementLocked": false,
"elementType": "selection",
"errorMessage": null,
"exportBackground": true,
"exportEmbedScene": false,
"exportPadding": undefined,
"exportWithDarkMode": false,
"fileHandle": null,
"gridSize": null,
"height": 768,
"isBindingEnabled": true,
"isLibraryOpen": false,
"isLoading": false,
"isResizing": false,
"isRotating": false,
"lastPointerDownWith": "mouse",
"metadata": undefined,
"multiElement": null,
"name": "name",
"offsetLeft": 0,
"offsetTop": 0,
"openMenu": null,
"pasteDialog": Object {
"data": null,
"shown": false,
},
"previousSelectedElementIds": Object {},
"resizingElement": null,
"scrollX": 0,
"scrollY": 0,
"scrolledOutside": false,
"selectedElementIds": Object {},
"selectedGroupIds": Object {},
"selectionElement": null,
"shouldAddWatermark": false,
"shouldCacheIgnoreZoom": false,
"showHelpDialog": false,
"showStats": false,
"startBoundElement": null,
"suggestedBindings": Array [],
"theme": "light",
"toastMessage": null,
"viewBackgroundColor": "#ffffff",
"viewModeEnabled": false,
"width": 1024,
"zenModeEnabled": false,
"zoom": Object {
"translation": Object {
"x": 0,
"y": 0,
},
"value": 1,
},
}
`;

View File

@ -0,0 +1,121 @@
import * as utils from "../../packages/utils";
import { diagramFactory } from "../fixtures/diagramFixture";
import * as mockedSceneExportUtils from "../../scene/export";
jest.mock("../../scene/export", () => ({
__esmodule: true,
...jest.requireActual("../../scene/export"),
exportToSvg: jest.fn(),
}));
describe("exportToCanvas", () => {
const EXPORT_PADDING = 10;
it("with default arguments", () => {
const canvas = utils.exportToCanvas({
...diagramFactory({ elementOverrides: { width: 100, height: 100 } }),
});
expect(canvas.width).toBe(100 + 2 * EXPORT_PADDING);
expect(canvas.height).toBe(100 + 2 * EXPORT_PADDING);
});
it("when custom width and height", () => {
const canvas = utils.exportToCanvas({
...diagramFactory({ elementOverrides: { width: 100, height: 100 } }),
getDimensions: () => ({ width: 200, height: 200, scale: 1 }),
});
expect(canvas.width).toBe(200);
expect(canvas.height).toBe(200);
});
});
describe("exportToBlob", () => {
describe("mime type", () => {
afterEach(jest.restoreAllMocks);
it("should change image/jpg to image/jpeg", async () => {
const blob = await utils.exportToBlob({
...diagramFactory(),
getDimensions: (width, height) => ({ width, height, scale: 1 }),
mimeType: "image/jpg",
});
expect(blob?.type).toBe("image/jpeg");
});
it("should default to image/png", async () => {
const blob = await utils.exportToBlob({
...diagramFactory(),
});
expect(blob?.type).toBe("image/png");
});
it("should warn when using quality with image/png", async () => {
const consoleSpy = jest
.spyOn(console, "warn")
.mockImplementationOnce(() => void 0);
await utils.exportToBlob({
...diagramFactory(),
mimeType: "image/png",
quality: 1,
});
expect(consoleSpy).toHaveBeenCalledWith(
'"quality" will be ignored for "image/png" mimeType',
);
});
});
});
describe("exportToSvg", () => {
const mockedExportUtil = mockedSceneExportUtils.exportToSvg as jest.Mock;
const passedElements = () => mockedExportUtil.mock.calls[0][0];
const passedOptions = () => mockedExportUtil.mock.calls[0][1];
afterEach(jest.resetAllMocks);
it("with default arguments", () => {
utils.exportToSvg({
...diagramFactory({
overrides: { appState: void 0 },
}),
});
const passedOptionsWhenDefault = {
...passedOptions(),
// To avoid varying snapshots
name: "name",
};
expect(passedElements().length).toBe(3);
expect(passedOptionsWhenDefault).toMatchSnapshot();
});
it("with deleted elements", () => {
utils.exportToSvg({
...diagramFactory({
overrides: { appState: void 0 },
elementOverrides: { isDeleted: true },
}),
});
expect(passedElements().length).toBe(0);
});
it("with exportPadding and metadata", () => {
const METADATA = "some metada";
utils.exportToSvg({
...diagramFactory({ overrides: { appState: { name: "diagram name" } } }),
exportPadding: 0,
metadata: METADATA,
});
expect(passedElements().length).toBe(3);
expect(passedOptions()).toEqual(
expect.objectContaining({ exportPadding: 0, metadata: METADATA }),
);
});
});

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,96 @@
import { NonDeletedExcalidrawElement } from "../../element/types";
import * as exportUtils from "../../scene/export";
import { diamondFixture, ellipseFixture } from "../fixtures/elementFixture";
describe("exportToSvg", () => {
const ELEMENT_HEIGHT = 100;
const ELEMENT_WIDTH = 100;
const ELEMENTS = [
{ ...diamondFixture, height: ELEMENT_HEIGHT, width: ELEMENT_WIDTH },
{ ...ellipseFixture, height: ELEMENT_HEIGHT, width: ELEMENT_WIDTH },
] as NonDeletedExcalidrawElement[];
const DEFAULT_OPTIONS = {
exportBackground: false,
viewBackgroundColor: "#ffffff",
shouldAddWatermark: false,
};
it("with default arguments", () => {
const svgElement = exportUtils.exportToSvg(ELEMENTS, DEFAULT_OPTIONS);
expect(svgElement).toMatchSnapshot();
});
it("with background color", () => {
const BACKGROUND_COLOR = "#abcdef";
const svgElement = exportUtils.exportToSvg(ELEMENTS, {
...DEFAULT_OPTIONS,
exportBackground: true,
viewBackgroundColor: BACKGROUND_COLOR,
});
expect(svgElement.querySelector("rect")).toHaveAttribute(
"fill",
BACKGROUND_COLOR,
);
});
it("with watermark", () => {
const svgElement = exportUtils.exportToSvg(ELEMENTS, {
...DEFAULT_OPTIONS,
shouldAddWatermark: true,
});
expect(svgElement.querySelector("text")?.textContent).toMatchInlineSnapshot(
`"Made with Excalidraw"`,
);
});
it("with dark mode", () => {
const svgElement = exportUtils.exportToSvg(ELEMENTS, {
...DEFAULT_OPTIONS,
exportWithDarkMode: true,
});
expect(svgElement.getAttribute("filter")).toMatchInlineSnapshot(
`"themeFilter"`,
);
});
it("with exportPadding, metadata", () => {
const svgElement = exportUtils.exportToSvg(ELEMENTS, {
...DEFAULT_OPTIONS,
exportPadding: 0,
metadata: "some metadata",
});
expect(svgElement.innerHTML).toMatch(/some metadata/);
expect(svgElement).toHaveAttribute("height", ELEMENT_HEIGHT.toString());
expect(svgElement).toHaveAttribute("width", ELEMENT_WIDTH.toString());
expect(svgElement).toHaveAttribute(
"viewBox",
`0 0 ${ELEMENT_WIDTH} ${ELEMENT_HEIGHT}`,
);
});
it("with scale", () => {
const SCALE = 2;
const svgElement = exportUtils.exportToSvg(ELEMENTS, {
...DEFAULT_OPTIONS,
exportPadding: 0,
scale: SCALE,
});
expect(svgElement).toHaveAttribute(
"height",
(ELEMENT_HEIGHT * SCALE).toString(),
);
expect(svgElement).toHaveAttribute(
"width",
(ELEMENT_WIDTH * SCALE).toString(),
);
});
});