Fix jpg export and tests (#3198)
Fix a bug that was preventing JPG and webp exports from working. Also: - Re-enable our export snapshot tests which got commented out again - Fix some react act errors when running tests ### Change Type - [x] `sdk` — Changes the tldraw SDK - [x] `bugfix` — Bug fix
This commit is contained in:
parent
cef70d6a81
commit
16a28bfd90
7 changed files with 127 additions and 61 deletions
|
@ -1,14 +1,14 @@
|
||||||
import test from '@playwright/test'
|
import { Page, expect } from '@playwright/test'
|
||||||
import { TLShapeId, TLShapePartial } from 'tldraw'
|
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'
|
declare const editor: Editor
|
||||||
// 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
|
|
||||||
|
|
||||||
|
// 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', () => {
|
test.describe('Export snapshots', () => {
|
||||||
const snapshots = {
|
const snapshots = {
|
||||||
'Exports geo text with leading line breaks': [
|
'Exports geo text with leading line breaks': [
|
||||||
|
@ -189,50 +189,50 @@ test.describe('Export snapshots', () => {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// const snapshotsToTest = Object.entries(snapshots)
|
const snapshotsToTest = Object.entries(snapshots)
|
||||||
// const filteredSnapshots = snapshotsToTest // maybe we filter these down, there are a lot of them
|
const filteredSnapshots = snapshotsToTest // maybe we filter these down, there are a lot of them
|
||||||
|
|
||||||
// for (const [name, shapes] of filteredSnapshots) {
|
test.beforeEach(setup)
|
||||||
// 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)
|
|
||||||
|
|
||||||
// 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) {
|
await snapshotTest(page, api)
|
||||||
// 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',
|
|
||||||
// `
|
|
||||||
// <!DOCTYPE html>
|
|
||||||
// <meta charset="utf-8" />
|
|
||||||
// <meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
// <img src="${path}.svg" />
|
|
||||||
// `,
|
|
||||||
// 'utf-8'
|
|
||||||
// )
|
|
||||||
|
|
||||||
// await page.goto(`file://${path}.html`)
|
async function snapshotTest(page: Page, api: ApiFixture) {
|
||||||
// const clip = await page.$eval('img', (img) => img.getBoundingClientRect())
|
const downloadAndSnapshot = page.waitForEvent('download').then(async (download) => {
|
||||||
// await expect(page).toHaveScreenshot({
|
const path = (await download.path()) as string
|
||||||
// omitBackground: true,
|
assert(path)
|
||||||
// clip,
|
await rename(path, path + '.svg')
|
||||||
// })
|
await writeFile(
|
||||||
// })
|
path + '.html',
|
||||||
// await page.evaluate(() => (window as any)['tldraw-export']())
|
`
|
||||||
// await downloadAndSnapshot
|
<!DOCTYPE html>
|
||||||
// }
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<img src="${path}.svg" />
|
||||||
|
`,
|
||||||
|
'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
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
37
apps/examples/e2e/tests/fixtures/fixtures.ts
vendored
37
apps/examples/e2e/tests/fixtures/fixtures.ts
vendored
|
@ -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 { ActionsMenu } from './menus/ActionsMenu'
|
||||||
import { HelpMenu } from './menus/HelpMenu'
|
import { HelpMenu } from './menus/HelpMenu'
|
||||||
import { MainMenu } from './menus/MainMenu'
|
import { MainMenu } from './menus/MainMenu'
|
||||||
|
@ -15,6 +16,30 @@ type Fixtures = {
|
||||||
mainMenu: MainMenu
|
mainMenu: MainMenu
|
||||||
pageMenu: PageMenu
|
pageMenu: PageMenu
|
||||||
navigationPanel: NavigationPanel
|
navigationPanel: NavigationPanel
|
||||||
|
api: ReturnType<typeof makeApiFixture>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ApiFixture = {
|
||||||
|
[K in keyof EndToEndApi]: (
|
||||||
|
...args: Parameters<EndToEndApi[K]>
|
||||||
|
) => Promise<ReturnType<EndToEndApi[K]>>
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Fixtures>({
|
const test = base.extend<Fixtures>({
|
||||||
|
@ -46,6 +71,16 @@ const test = base.extend<Fixtures>({
|
||||||
const navigationPanel = new NavigationPanel(page)
|
const navigationPanel = new NavigationPanel(page)
|
||||||
await use(navigationPanel)
|
await use(navigationPanel)
|
||||||
},
|
},
|
||||||
|
api: async ({ page }, use) => {
|
||||||
|
const api = makeApiFixture(
|
||||||
|
{
|
||||||
|
exportAsSvg: true,
|
||||||
|
exportAsFormat: true,
|
||||||
|
},
|
||||||
|
page
|
||||||
|
)
|
||||||
|
await use(api)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export default test
|
export default test
|
||||||
|
|
14
apps/examples/e2e/tests/test-api.spec.ts
Normal file
14
apps/examples/e2e/tests/test-api.spec.ts
Normal file
|
@ -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
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
6
apps/examples/src/misc/EndToEndApi.tsx
Normal file
6
apps/examples/src/misc/EndToEndApi.tsx
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
import { TLExportType } from 'tldraw/src/lib/utils/export/exportAs'
|
||||||
|
|
||||||
|
export interface EndToEndApi {
|
||||||
|
exportAsSvg: () => void
|
||||||
|
exportAsFormat: (format: TLExportType) => void
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { Tldraw, useActions } from 'tldraw'
|
import { Tldraw, exportAs, useActions, useEditor } from 'tldraw'
|
||||||
import 'tldraw/tldraw.css'
|
import 'tldraw/tldraw.css'
|
||||||
|
import { EndToEndApi } from './EndToEndApi'
|
||||||
;(window as any).__tldraw_ui_event = { id: 'NOTHING_YET' }
|
;(window as any).__tldraw_ui_event = { id: 'NOTHING_YET' }
|
||||||
;(window as any).__tldraw_editor_events = []
|
;(window as any).__tldraw_editor_events = []
|
||||||
|
|
||||||
|
@ -27,11 +28,17 @@ export default function EndToEnd() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function SneakyExportButton() {
|
function SneakyExportButton() {
|
||||||
|
const editor = useEditor()
|
||||||
const actions = useActions()
|
const actions = useActions()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
;(window as any)['tldraw-export'] = () => actions['export-as-svg'].onSelect('unknown')
|
const api: EndToEndApi = {
|
||||||
}, [actions])
|
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
|
return null
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ describe('<Tldraw />', () => {
|
||||||
<Tldraw>
|
<Tldraw>
|
||||||
<div data-testid="canvas-1" />
|
<div data-testid="canvas-1" />
|
||||||
</Tldraw>,
|
</Tldraw>,
|
||||||
{ waitForPatterns: false }
|
{ waitForPatterns: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
await screen.findByTestId('canvas-1')
|
await screen.findByTestId('canvas-1')
|
||||||
|
@ -27,7 +27,7 @@ describe('<Tldraw />', () => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
await renderTldrawComponent(<TestComponent />, { waitForPatterns: false })
|
await renderTldrawComponent(<TestComponent />, { waitForPatterns: true })
|
||||||
await screen.findByTestId('canvas-1')
|
await screen.findByTestId('canvas-1')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -86,10 +86,14 @@ export async function getSvgAsImage(
|
||||||
|
|
||||||
if (!blob) return null
|
if (!blob) return null
|
||||||
|
|
||||||
|
if (type === 'png') {
|
||||||
const view = new DataView(await blob.arrayBuffer())
|
const view = new DataView(await blob.arrayBuffer())
|
||||||
return PngHelpers.setPhysChunk(view, effectiveScale, {
|
return PngHelpers.setPhysChunk(view, effectiveScale, {
|
||||||
type: 'image/' + type,
|
type: 'image/' + type,
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
return blob
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
|
|
Loading…
Reference in a new issue