import { fireEvent, render, waitFor } from "./test-utils"; import ExcalidrawApp from "../excalidraw-app"; import { API } from "./helpers/api"; import { MIME_TYPES } from "../constants"; import { LibraryItem, LibraryItems } from "../types"; import { UI } from "./helpers/ui"; import { serializeLibraryAsJSON } from "../data/json"; import { distributeLibraryItemsOnSquareGrid } from "../data/library"; import { ExcalidrawGenericElement } from "../element/types"; import { getCommonBoundingBox } from "../element/bounds"; import { parseLibraryJSON } from "../data/blob"; const { h } = window; const libraryJSONPromise = API.readFile( "./fixtures/fixture_library.excalidrawlib", "utf8", ); const mockLibraryFilePromise = new Promise(async (resolve, reject) => { try { resolve( new Blob([await libraryJSONPromise], { type: MIME_TYPES.excalidrawlib }), ); } catch (error) { reject(error); } }); jest.mock("../data/filesystem.ts", () => ({ __esmodule: true, ...jest.requireActual("../data/filesystem.ts"), fileOpen: jest.fn(() => mockLibraryFilePromise), })); describe("library", () => { beforeEach(async () => { await render(); h.app.library.resetLibrary(); }); it("import library via drag&drop", async () => { expect(await h.app.library.getLatestLibrary()).toEqual([]); await API.drop( await API.loadFile("./fixtures/fixture_library.excalidrawlib"), ); await waitFor(async () => { expect(await h.app.library.getLatestLibrary()).toEqual([ { status: "unpublished", elements: [expect.objectContaining({ id: "A" })], id: "id0", created: expect.any(Number), }, ]); }); }); // NOTE: mocked to test logic, not actual drag&drop via UI it("drop library item onto canvas", async () => { expect(h.elements).toEqual([]); const libraryItems = parseLibraryJSON(await libraryJSONPromise); await API.drop( new Blob([serializeLibraryAsJSON(libraryItems)], { type: MIME_TYPES.excalidrawlib, }), ); await waitFor(() => { expect(h.elements).toEqual([expect.objectContaining({ id: "A_copy" })]); }); }); it("inserting library item should revert to selection tool", async () => { UI.clickTool("rectangle"); expect(h.elements).toEqual([]); const libraryItems = parseLibraryJSON(await libraryJSONPromise); await API.drop( new Blob([serializeLibraryAsJSON(libraryItems)], { type: MIME_TYPES.excalidrawlib, }), ); await waitFor(() => { expect(h.elements).toEqual([expect.objectContaining({ id: "A_copy" })]); }); expect(h.state.activeTool.type).toBe("selection"); }); }); describe("library menu", () => { it("should load library from file picker", async () => { const { container } = await render(); const latestLibrary = await h.app.library.getLatestLibrary(); expect(latestLibrary.length).toBe(0); const libraryButton = container.querySelector(".ToolIcon__library"); fireEvent.click(libraryButton!); const loadLibraryButton = container.querySelector( ".library-actions .library-actions--load", ); fireEvent.click(loadLibraryButton!); const libraryItems = parseLibraryJSON(await libraryJSONPromise); await waitFor(async () => { const latestLibrary = await h.app.library.getLatestLibrary(); expect(latestLibrary.length).toBeGreaterThan(0); expect(latestLibrary.length).toBe(libraryItems.length); expect(latestLibrary[0].elements).toEqual(libraryItems[0].elements); }); expect(true).toBe(true); }); }); describe("distributeLibraryItemsOnSquareGrid()", () => { it("should distribute items on a grid", async () => { const createLibraryItem = ( elements: ExcalidrawGenericElement[], ): LibraryItem => { return { id: `id-${Date.now()}`, elements, status: "unpublished", created: Date.now(), }; }; const PADDING = 50; const el1 = API.createElement({ id: "id1", width: 100, height: 100, x: 0, y: 0, }); const el2 = API.createElement({ id: "id2", width: 100, height: 80, x: -100, y: -50, }); const el3 = API.createElement({ id: "id3", width: 40, height: 50, x: -100, y: -50, }); const el4 = API.createElement({ id: "id4", width: 50, height: 50, x: 0, y: 0, }); const el5 = API.createElement({ id: "id5", width: 70, height: 100, x: 40, y: 0, }); const libraryItems: LibraryItems = [ createLibraryItem([el1]), createLibraryItem([el2]), createLibraryItem([el3]), createLibraryItem([el4, el5]), ]; const distributed = distributeLibraryItemsOnSquareGrid(libraryItems); // assert the returned library items are flattened to elements expect(distributed.length).toEqual( libraryItems.map((x) => x.elements).flat().length, ); expect(distributed).toEqual( expect.arrayContaining([ expect.objectContaining({ id: el1.id, x: 0, y: 0, }), expect.objectContaining({ id: el2.id, x: el1.width + PADDING + (getCommonBoundingBox([el4, el5]).width - el2.width) / 2, y: Math.abs(el1.height - el2.height) / 2, }), expect.objectContaining({ id: el3.id, x: Math.abs(el1.width - el3.width) / 2, y: Math.max(el1.height, el2.height) + PADDING + Math.abs(el3.height - Math.max(el4.height, el5.height)) / 2, }), expect.objectContaining({ id: el4.id, x: Math.max(el1.width, el2.width) + PADDING, y: Math.max(el1.height, el2.height) + PADDING, }), expect.objectContaining({ id: el5.id, x: Math.max(el1.width, el2.width) + PADDING + Math.abs(el5.x - el4.x), y: Math.max(el1.height, el2.height) + PADDING + Math.abs(el5.y - el4.y), }), ]), ); }); });