File
Blob: tests/e2e/specs/24-canvas-image-upload.spec.ts
| 1 | import { test, createTestPage, waitForCanvasSceneCount, waitForPersistedSnapshot } from "../fixtures/bland-test"; |
| 2 | |
| 3 | // Smallest legal PNG (1x1) as base64 — enough to validate the upload |
| 4 | // round-trip without shipping a test asset. |
| 5 | const TINY_PNG_BASE64 = |
| 6 | "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4nGP4z8DwHwAFAAH/iZk9HQAAAABJRU5ErkJggg=="; |
| 7 | |
| 8 | test.describe("canvas page - images", () => { |
| 9 | test("dropping an image uploads + persists across reload via hydration", async ({ |
| 10 | authenticatedPage: { page, accessToken }, |
| 11 | e2eWorkspace, |
| 12 | }) => { |
| 13 | const testPage = await createTestPage(page, accessToken, "Canvas Image Upload", e2eWorkspace, "canvas"); |
| 14 | |
| 15 | await page.goto(`/${testPage.workspaceSlug}/${testPage.pageId}`); |
| 16 | await page.locator(".excalidraw").waitFor({ state: "attached", timeout: 30_000 }); |
| 17 | |
| 18 | // Drop a PNG onto the canvas. Excalidraw listens for drag-drop via |
| 19 | // document-level listeners, so dispatching a `drop` on the excalidraw |
| 20 | // root triggers its image-insertion flow. |
| 21 | const excalidrawRoot = page.locator(".excalidraw").first(); |
| 22 | await excalidrawRoot.evaluate((root, base64) => { |
| 23 | const binary = atob(base64); |
| 24 | const bytes = new Uint8Array(binary.length); |
| 25 | for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i); |
| 26 | const file = new File([bytes as BlobPart], "pixel.png", { type: "image/png" }); |
| 27 | const dt = new DataTransfer(); |
| 28 | dt.items.add(file); |
| 29 | const rect = root.getBoundingClientRect(); |
| 30 | root.dispatchEvent( |
| 31 | new DragEvent("drop", { |
| 32 | bubbles: true, |
| 33 | cancelable: true, |
| 34 | clientX: rect.left + rect.width / 2, |
| 35 | clientY: rect.top + rect.height / 2, |
| 36 | dataTransfer: dt, |
| 37 | }), |
| 38 | ); |
| 39 | }, TINY_PNG_BASE64); |
| 40 | |
| 41 | // Wait for the element to appear in the scene. Drop-to-scene goes through |
| 42 | // Excalidraw's own async readers, so give it a generous window. |
| 43 | await waitForCanvasSceneCount(page, 1); |
| 44 | |
| 45 | await waitForPersistedSnapshot(page, accessToken, testPage); |
| 46 | |
| 47 | await page.reload(); |
| 48 | await page.locator(".excalidraw").waitFor({ state: "attached", timeout: 30_000 }); |
| 49 | |
| 50 | // On reload, the binding reads yFileRefs and hydrates via |
| 51 | // fetchUploadAsDataURL → addFiles. The image element should re-appear |
| 52 | // in the scene. |
| 53 | await waitForCanvasSceneCount(page, 1); |
| 54 | }); |
| 55 | }); |