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:
parent
bdf6e53289
commit
4ac1841d92
@ -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
31
src/tests/fixtures/diagramFixture.ts
vendored
Normal 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
37
src/tests/fixtures/elementFixture.ts
vendored
Normal 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",
|
||||
};
|
80
src/tests/packages/__snapshots__/utils.test.ts.snap
Normal file
80
src/tests/packages/__snapshots__/utils.test.ts.snap
Normal 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,
|
||||
},
|
||||
}
|
||||
`;
|
121
src/tests/packages/utils.test.ts
Normal file
121
src/tests/packages/utils.test.ts
Normal 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 }),
|
||||
);
|
||||
});
|
||||
});
|
70
src/tests/scene/__snapshots__/export.test.ts.snap
Normal file
70
src/tests/scene/__snapshots__/export.test.ts.snap
Normal file
File diff suppressed because one or more lines are too long
96
src/tests/scene/export.test.ts
Normal file
96
src/tests/scene/export.test.ts
Normal 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(),
|
||||
);
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user