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 { 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',
|
||||
// `
|
||||
// <!DOCTYPE html>
|
||||
// <meta charset="utf-8" />
|
||||
// <meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
// <img src="${path}.svg" />
|
||||
// `,
|
||||
// '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',
|
||||
`
|
||||
<!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 { HelpMenu } from './menus/HelpMenu'
|
||||
import { MainMenu } from './menus/MainMenu'
|
||||
|
@ -15,6 +16,30 @@ type Fixtures = {
|
|||
mainMenu: MainMenu
|
||||
pageMenu: PageMenu
|
||||
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>({
|
||||
|
@ -46,6 +71,16 @@ const test = base.extend<Fixtures>({
|
|||
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
|
||||
|
|
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 { 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
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ describe('<Tldraw />', () => {
|
|||
<Tldraw>
|
||||
<div data-testid="canvas-1" />
|
||||
</Tldraw>,
|
||||
{ waitForPatterns: false }
|
||||
{ waitForPatterns: true }
|
||||
)
|
||||
|
||||
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')
|
||||
})
|
||||
|
||||
|
|
|
@ -86,10 +86,14 @@ export async function getSvgAsImage(
|
|||
|
||||
if (!blob) return null
|
||||
|
||||
if (type === 'png') {
|
||||
const view = new DataView(await blob.arrayBuffer())
|
||||
return PngHelpers.setPhysChunk(view, effectiveScale, {
|
||||
type: 'image/' + type,
|
||||
})
|
||||
} else {
|
||||
return blob
|
||||
}
|
||||
}
|
||||
|
||||
/** @public */
|
||||
|
|
Loading…
Reference in a new issue