Tldraw/packages/editor/src/lib/test/TldrawEditor.test.tsx

284 wiersze
6.8 KiB
TypeScript

import { act, render, screen } from '@testing-library/react'
import { TLBaseShape, createShapeId } from '@tldraw/tlschema'
import { TldrawEditor } from '../TldrawEditor'
import { Canvas } from '../components/Canvas'
import { HTMLContainer } from '../components/HTMLContainer'
import { createTLStore } from '../config/createTLStore'
import { Editor } from '../editor/Editor'
import { BaseBoxShapeUtil } from '../editor/shapeutils/BaseBoxShapeUtil'
import { BaseBoxShapeTool } from '../editor/tools/BaseBoxShapeTool/BaseBoxShapeTool'
let originalFetch: typeof window.fetch
beforeEach(() => {
window.fetch = jest.fn().mockImplementation((...args: Parameters<typeof fetch>) => {
if (args[0] === '/icons/icon/icon-names.json') {
return Promise.resolve({ json: () => Promise.resolve([]) } as Response)
}
return originalFetch(...args)
})
})
afterEach(() => {
jest.restoreAllMocks()
window.fetch = originalFetch
})
describe('<TldrawEditor />', () => {
it('Renders without crashing', async () => {
await act(async () => (
<TldrawEditor autoFocus>
<div data-testid="canvas-1" />
</TldrawEditor>
))
})
it('Creates its own store', async () => {
let store: any
render(
await act(async () => (
<TldrawEditor onMount={(editor) => (store = editor.store)} autoFocus>
<div data-testid="canvas-1" />
</TldrawEditor>
))
)
await screen.findByTestId('canvas-1')
expect(store).toBeTruthy()
})
it('Renders with an external store', async () => {
const store = createTLStore()
render(
await act(async () => (
<TldrawEditor
store={store}
onMount={(editor) => {
expect(editor.store).toBe(store)
}}
autoFocus
>
<div data-testid="canvas-1" />
</TldrawEditor>
))
)
await screen.findByTestId('canvas-1')
})
it('Accepts fresh versions of store and calls `onMount` for each one', async () => {
const initialStore = createTLStore({})
const onMount = jest.fn()
const rendered = render(
<TldrawEditor store={initialStore} onMount={onMount} autoFocus>
<div data-testid="canvas-1" />
</TldrawEditor>
)
await screen.findByTestId('canvas-1')
const initialEditor = onMount.mock.lastCall[0]
jest.spyOn(initialEditor, 'dispose')
expect(initialEditor.store).toBe(initialStore)
// re-render with the same store:
rendered.rerender(
<TldrawEditor store={initialStore} onMount={onMount} autoFocus>
<div data-testid="canvas-2" />
</TldrawEditor>
)
await screen.findByTestId('canvas-2')
// not called again:
expect(onMount).toHaveBeenCalledTimes(1)
// re-render with a new store:
const newStore = createTLStore({})
rendered.rerender(
<TldrawEditor store={newStore} onMount={onMount} autoFocus>
<div data-testid="canvas-3" />
</TldrawEditor>
)
await screen.findByTestId('canvas-3')
expect(initialEditor.dispose).toHaveBeenCalledTimes(1)
expect(onMount).toHaveBeenCalledTimes(2)
expect(onMount.mock.lastCall[0].store).toBe(newStore)
})
it('Renders the canvas and shapes', async () => {
let editor = {} as Editor
render(
await act(async () => (
<TldrawEditor
autoFocus
onMount={(editorApp) => {
editor = editorApp
}}
>
<Canvas />
<div data-testid="canvas-1" />
</TldrawEditor>
))
)
await screen.findByTestId('canvas-1')
expect(editor).toBeTruthy()
await act(async () => {
editor.updateInstanceState({ screenBounds: { x: 0, y: 0, w: 1080, h: 720 } }, true, true)
})
const id = createShapeId()
await act(async () => {
editor.createShapes([
{
id,
type: 'geo',
props: { w: 100, h: 100 },
},
])
})
// Does the shape exist?
expect(editor.getShapeById(id)).toMatchObject({
id,
type: 'geo',
x: 0,
y: 0,
opacity: 1,
props: { geo: 'rectangle', w: 100, h: 100 },
})
// Is the shape's component rendering?
expect(document.querySelectorAll('.tl-shape')).toHaveLength(1)
expect(document.querySelectorAll('.tl-shape-indicator')).toHaveLength(0)
// Select the shape
await act(async () => editor.select(id))
// Is the shape's component rendering?
expect(document.querySelectorAll('.tl-shape-indicator')).toHaveLength(1)
// Select the eraser tool...
await act(async () => editor.setSelectedTool('eraser'))
// Is the editor's current tool correct?
expect(editor.currentToolId).toBe('eraser')
})
})
describe('Custom shapes', () => {
type CardShape = TLBaseShape<
'card',
{
w: number
h: number
}
>
class CardUtil extends BaseBoxShapeUtil<CardShape> {
static override type = 'card' as const
override isAspectRatioLocked = (_shape: CardShape) => false
override canResize = (_shape: CardShape) => true
override canBind = (_shape: CardShape) => true
override defaultProps(): CardShape['props'] {
return {
w: 300,
h: 300,
}
}
render(shape: CardShape) {
const bounds = this.bounds(shape)
return (
<HTMLContainer
id={shape.id}
data-testid="card-shape"
style={{
border: '1px solid black',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
pointerEvents: 'all',
}}
>
{bounds.w.toFixed()}x{bounds.h.toFixed()}
</HTMLContainer>
)
}
indicator(shape: CardShape) {
return <rect data-testid="card-indicator" width={shape.props.w} height={shape.props.h} />
}
}
class CardTool extends BaseBoxShapeTool {
static override id = 'card'
static override initial = 'idle'
override shapeType = 'card'
}
const tools = [CardTool]
const shapes = { card: { util: CardUtil } }
it('Uses custom shapes', async () => {
let editor = {} as Editor
render(
await act(async () => (
<TldrawEditor
shapes={shapes}
tools={tools}
autoFocus
onMount={(editorApp) => {
editor = editorApp
}}
>
<Canvas />
<div data-testid="canvas-1" />
</TldrawEditor>
))
)
await screen.findByTestId('canvas-1')
expect(editor).toBeTruthy()
await act(async () => {
editor.updateInstanceState({ screenBounds: { x: 0, y: 0, w: 1080, h: 720 } }, true, true)
})
expect(editor.shapeUtils.card).toBeTruthy()
const id = createShapeId()
await act(async () => {
editor.createShapes([
{
id,
type: 'card',
props: { w: 100, h: 100 },
},
])
})
// Does the shape exist?
expect(editor.getShapeById(id)).toMatchObject({
id,
type: 'card',
x: 0,
y: 0,
opacity: 1,
props: { w: 100, h: 100 },
})
// Is the shape's component rendering?
expect(await screen.findByTestId('card-shape')).toBeTruthy()
// Select the shape
await act(async () => editor.select(id))
// Is the shape's component rendering?
expect(await screen.findByTestId('card-indicator')).toBeTruthy()
// Select the tool...
await act(async () => editor.setSelectedTool('card'))
// Is the editor's current tool correct?
expect(editor.currentToolId).toBe('card')
})
})