import {
BaseBoxShapeTool,
BaseBoxShapeUtil,
Canvas,
Editor,
HTMLContainer,
TLBaseShape,
TldrawEditor,
createShapeId,
createTLStore,
noop,
} from '@bigbluebutton/editor'
import { act, render, screen } from '@testing-library/react'
import { defaultTools } from '../lib/defaultTools'
import { GeoShapeUtil } from '../lib/shapes/geo/GeoShapeUtil'
function checkAllShapes(editor: Editor, shapes: string[]) {
expect(Object.keys(editor!.store.schema.types.shape.migrations.subTypeMigrations!)).toStrictEqual(
shapes
)
expect(Object.keys(editor!.shapeUtils)).toStrictEqual(shapes)
}
describe('', () => {
it('Renders without crashing', () => {
render(
)
screen.findByTestId('canvas-1')
})
it('Creates its own store with core shapes', () => {
let editor: Editor
render(
{
editor = e
}}
initialState="select"
tools={defaultTools}
autoFocus
>
)
screen.findByTestId('canvas-1')
checkAllShapes(editor!, ['group'])
})
it('Can be created with default shapes', () => {
let editor: Editor
render(
{
editor = e
}}
autoFocus
>
)
screen.findByTestId('canvas-1')
expect(editor!).toBeTruthy()
checkAllShapes(editor!, ['group'])
})
it('Renders with an external store', () => {
const store = createTLStore({ shapeUtils: [] })
render(
{
expect(editor.store).toBe(store)
}}
autoFocus
>
)
screen.findByTestId('canvas-1')
})
it('throws if the store has different shapes to the ones passed in', () => {
const spy = jest.spyOn(console, 'error').mockImplementation(noop)
// expect(() =>
// render(
// {
// throw error
// },
// }}
// >
//
//
// )
// ).toThrowErrorMatchingInlineSnapshot(
// `"Editor and store have different shapes: \\"draw\\" was passed into the editor but not the schema"`
// )
// expect(() =>
// render(
// {
// throw error
// },
// }}
// >
//
//
// )
// ).toThrowErrorMatchingInlineSnapshot(
// `"Editor and store have different shapes: \\"draw\\" is present in the store schema but not provided to the editor"`
// )
spy.mockRestore()
})
it('Accepts fresh versions of store and calls `onMount` for each one', () => {
const initialStore = createTLStore({ shapeUtils: [] })
const onMount = jest.fn()
const rendered = render(
)
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(
)
screen.findByTestId('canvas-2')
// not called again:
expect(onMount).toHaveBeenCalledTimes(1)
// re-render with a new store:
const newStore = createTLStore({ shapeUtils: [] })
rendered.rerender(
)
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(
{
editor = editorApp
}}
>
)
screen.findByTestId('canvas-1')
expect(editor).toBeTruthy()
await act(async () => {
editor.updateInstanceState(
{ screenBounds: { x: 0, y: 0, w: 1080, h: 720 } },
{ ephemeral: true, squashing: true }
)
})
const id = createShapeId()
await act(async () => {
editor.createShapes([
{
id,
type: 'geo',
props: { w: 100, h: 100 },
},
])
})
// Does the shape exist?
expect(editor.getShape(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))
expect(editor.getSelectedShapeIds().length).toBe(1)
// Is the shape's component rendering?
expect(document.querySelectorAll('.tl-shape-indicator')).toHaveLength(1)
// Select the eraser tool...
await act(async () => editor.setCurrentTool('eraser'))
// Is the editor's current tool correct?
expect(editor.getCurrentToolId()).toBe('eraser')
})
})
describe('Custom shapes', () => {
type CardShape = TLBaseShape<
'card',
{
w: number
h: number
}
>
class CardUtil extends BaseBoxShapeUtil {
static override type = 'card' as const
override isAspectRatioLocked = (_shape: CardShape) => false
override canResize = (_shape: CardShape) => true
override canBind = (_shape: CardShape) => true
override getDefaultProps(): CardShape['props'] {
return {
w: 300,
h: 300,
}
}
component(shape: CardShape) {
return (
{shape.props.w.toFixed()}x{shape.props.h.toFixed()}
)
}
indicator(shape: CardShape) {
return
}
}
class CardTool extends BaseBoxShapeTool {
static override id = 'card'
static override initial = 'idle'
override shapeType = 'card'
}
const tools = [CardTool]
const shapeUtils = [CardUtil]
it('Uses custom shapes', async () => {
let editor = {} as Editor
render(
{
editor = editorApp
}}
>
)
screen.findByTestId('canvas-1')
expect(editor).toBeTruthy()
await act(async () => {
editor.updateInstanceState(
{ screenBounds: { x: 0, y: 0, w: 1080, h: 720 } },
{ ephemeral: true, squashing: true }
)
})
expect(editor.shapeUtils.card).toBeTruthy()
checkAllShapes(editor, ['group', 'card'])
const id = createShapeId()
await act(async () => {
editor.createShapes([
{
id,
type: 'card',
props: { w: 100, h: 100 },
},
])
})
// Does the shape exist?
expect(editor.getShape(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.setCurrentTool('card'))
// Is the editor's current tool correct?
expect(editor.getCurrentToolId()).toBe('card')
})
})