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