diff --git a/apps/examples/e2e/tests/export-snapshots.spec.ts b/apps/examples/e2e/tests/export-snapshots.spec.ts index e883f60ff..237cead9f 100644 --- a/apps/examples/e2e/tests/export-snapshots.spec.ts +++ b/apps/examples/e2e/tests/export-snapshots.spec.ts @@ -1,14 +1,14 @@ -import test from '@playwright/test' -import { TLShapeId, TLShapePartial } from 'tldraw' +import { Page, expect } from '@playwright/test' +import assert from 'assert' +import { rename, writeFile } from 'fs/promises' +import { Editor, TLShapeId, TLShapePartial } from 'tldraw' +import { setup } from '../shared-e2e' +import test, { ApiFixture } from './fixtures/fixtures' -// import test, { Page, expect } from '@playwright/test' -// import assert from 'assert' -// import { rename, writeFile } from 'fs/promises' -// import { setupPage } from '../shared-e2e' -// import { Editor, TLShapeId, TLShapePartial } from 'tldraw' - -// declare const editor: Editor +declare const editor: Editor +// hi steve. please don't comment these out. they stop us getting bugs. u can just ask if they're +// holding u up <3 test.describe('Export snapshots', () => { const snapshots = { 'Exports geo text with leading line breaks': [ @@ -189,50 +189,50 @@ test.describe('Export snapshots', () => { ] } - // const snapshotsToTest = Object.entries(snapshots) - // const filteredSnapshots = snapshotsToTest // maybe we filter these down, there are a lot of them + const snapshotsToTest = Object.entries(snapshots) + const filteredSnapshots = snapshotsToTest // maybe we filter these down, there are a lot of them - // for (const [name, shapes] of filteredSnapshots) { - // test(`Exports with ${name} in dark mode`, async ({ browser }) => { - // const page = await browser.newPage() - // await setupPage(page) - // await page.evaluate((shapes) => { - // editor.user.updateUserPreferences({ isDarkMode: true }) - // editor - // .updateInstanceState({ exportBackground: false }) - // .selectAll() - // .deleteShapes(editor.getSelectedShapeIds()) - // .createShapes(shapes) - // }, shapes as any) + test.beforeEach(setup) - // await snapshotTest(page) - // }) - // } + for (const [name, shapes] of filteredSnapshots) { + test(`Exports with ${name} in dark mode`, async ({ page, api }) => { + await page.evaluate((shapes) => { + editor.user.updateUserPreferences({ isDarkMode: true }) + editor + .updateInstanceState({ exportBackground: false }) + .selectAll() + .deleteShapes(editor.getSelectedShapeIds()) + .createShapes(shapes) + }, shapes as any) - // async function snapshotTest(page: Page) { - // const downloadAndSnapshot = page.waitForEvent('download').then(async (download) => { - // const path = (await download.path()) as string - // assert(path) - // await rename(path, path + '.svg') - // await writeFile( - // path + '.html', - // ` - // - // - // - // - // `, - // 'utf-8' - // ) + await snapshotTest(page, api) + }) + } - // await page.goto(`file://${path}.html`) - // const clip = await page.$eval('img', (img) => img.getBoundingClientRect()) - // await expect(page).toHaveScreenshot({ - // omitBackground: true, - // clip, - // }) - // }) - // await page.evaluate(() => (window as any)['tldraw-export']()) - // await downloadAndSnapshot - // } + async function snapshotTest(page: Page, api: ApiFixture) { + const downloadAndSnapshot = page.waitForEvent('download').then(async (download) => { + const path = (await download.path()) as string + assert(path) + await rename(path, path + '.svg') + await writeFile( + path + '.html', + ` + + + + + `, + 'utf-8' + ) + + await page.goto(`file://${path}.html`) + const clip = await page.$eval('img', (img) => img.getBoundingClientRect()) + await expect(page).toHaveScreenshot({ + omitBackground: true, + clip, + }) + }) + await api.exportAsSvg() + await downloadAndSnapshot + } }) diff --git a/apps/examples/e2e/tests/fixtures/fixtures.ts b/apps/examples/e2e/tests/fixtures/fixtures.ts index 9df95f90d..35c226338 100644 --- a/apps/examples/e2e/tests/fixtures/fixtures.ts +++ b/apps/examples/e2e/tests/fixtures/fixtures.ts @@ -1,4 +1,5 @@ -import { test as base } from '@playwright/test' +import { Page, test as base } from '@playwright/test' +import { EndToEndApi } from '../../../src/misc/EndToEndApi' import { ActionsMenu } from './menus/ActionsMenu' import { HelpMenu } from './menus/HelpMenu' import { MainMenu } from './menus/MainMenu' @@ -15,6 +16,30 @@ type Fixtures = { mainMenu: MainMenu pageMenu: PageMenu navigationPanel: NavigationPanel + api: ReturnType +} + +export type ApiFixture = { + [K in keyof EndToEndApi]: ( + ...args: Parameters + ) => Promise> +} + +function makeApiFixture(keys: { [K in keyof EndToEndApi]: true }, page: Page): ApiFixture { + const result = {} as any + + for (const key of Object.keys(keys)) { + result[key] = (...args: any[]) => { + return page.evaluate( + ([key, ...args]) => { + return (window as any).tldrawApi[key](...args) + }, + [key, ...args] + ) + } + } + + return result } const test = base.extend({ @@ -46,6 +71,16 @@ const test = base.extend({ const navigationPanel = new NavigationPanel(page) await use(navigationPanel) }, + api: async ({ page }, use) => { + const api = makeApiFixture( + { + exportAsSvg: true, + exportAsFormat: true, + }, + page + ) + await use(api) + }, }) export default test diff --git a/apps/examples/e2e/tests/test-api.spec.ts b/apps/examples/e2e/tests/test-api.spec.ts new file mode 100644 index 000000000..8a6a0b6a7 --- /dev/null +++ b/apps/examples/e2e/tests/test-api.spec.ts @@ -0,0 +1,14 @@ +import { setupWithShapes } from '../shared-e2e' +import test from './fixtures/fixtures' + +test.beforeEach(setupWithShapes) + +test.describe('api', () => { + for (const format of ['svg', 'png', 'jpeg', 'webp', 'json'] as const) { + test(`export as ${format}`, async ({ page, api }) => { + const downloadEvent = page.waitForEvent('download') + await api.exportAsFormat(format) + await downloadEvent + }) + } +}) diff --git a/apps/examples/src/misc/EndToEndApi.tsx b/apps/examples/src/misc/EndToEndApi.tsx new file mode 100644 index 000000000..42f2eabab --- /dev/null +++ b/apps/examples/src/misc/EndToEndApi.tsx @@ -0,0 +1,6 @@ +import { TLExportType } from 'tldraw/src/lib/utils/export/exportAs' + +export interface EndToEndApi { + exportAsSvg: () => void + exportAsFormat: (format: TLExportType) => void +} diff --git a/apps/examples/src/misc/end-to-end.tsx b/apps/examples/src/misc/end-to-end.tsx index e92fa4bfe..06ad6c556 100644 --- a/apps/examples/src/misc/end-to-end.tsx +++ b/apps/examples/src/misc/end-to-end.tsx @@ -1,6 +1,7 @@ import { useEffect } from 'react' -import { Tldraw, useActions } from 'tldraw' +import { Tldraw, exportAs, useActions, useEditor } from 'tldraw' import 'tldraw/tldraw.css' +import { EndToEndApi } from './EndToEndApi' ;(window as any).__tldraw_ui_event = { id: 'NOTHING_YET' } ;(window as any).__tldraw_editor_events = [] @@ -27,11 +28,17 @@ export default function EndToEnd() { } function SneakyExportButton() { + const editor = useEditor() const actions = useActions() useEffect(() => { - ;(window as any)['tldraw-export'] = () => actions['export-as-svg'].onSelect('unknown') - }, [actions]) + const api: EndToEndApi = { + exportAsSvg: () => actions['export-as-svg'].onSelect('unknown'), + exportAsFormat: (format) => + exportAs(editor, editor.selectAll().getSelectedShapeIds(), format, 'test'), + } + ;(window as any).tldrawApi = api + }, [actions, editor]) return null } diff --git a/packages/tldraw/src/lib/Tldraw.test.tsx b/packages/tldraw/src/lib/Tldraw.test.tsx index 367b37de2..2fe99f7dc 100644 --- a/packages/tldraw/src/lib/Tldraw.test.tsx +++ b/packages/tldraw/src/lib/Tldraw.test.tsx @@ -10,7 +10,7 @@ describe('', () => {
, - { waitForPatterns: false } + { waitForPatterns: true } ) await screen.findByTestId('canvas-1') @@ -27,7 +27,7 @@ describe('', () => { ) } - await renderTldrawComponent(, { waitForPatterns: false }) + await renderTldrawComponent(, { waitForPatterns: true }) await screen.findByTestId('canvas-1') }) diff --git a/packages/tldraw/src/lib/utils/export/export.ts b/packages/tldraw/src/lib/utils/export/export.ts index 263f478d0..26cf8cf14 100644 --- a/packages/tldraw/src/lib/utils/export/export.ts +++ b/packages/tldraw/src/lib/utils/export/export.ts @@ -86,10 +86,14 @@ export async function getSvgAsImage( if (!blob) return null - const view = new DataView(await blob.arrayBuffer()) - return PngHelpers.setPhysChunk(view, effectiveScale, { - type: 'image/' + type, - }) + if (type === 'png') { + const view = new DataView(await blob.arrayBuffer()) + return PngHelpers.setPhysChunk(view, effectiveScale, { + type: 'image/' + type, + }) + } else { + return blob + } } /** @public */