Tldraw/packages/editor/src/lib/test/commands/stretch.test.ts

318 wiersze
6.3 KiB
TypeScript
Czysty Zwykły widok Historia

2023-04-25 11:01:25 +00:00
import { PI } from '@tldraw/primitives'
import { createCustomShapeId, TLShapeId } from '@tldraw/tlschema'
import { TestApp } from '../TestApp'
let app: TestApp
jest.useFakeTimers()
const ids = {
boxA: createCustomShapeId('boxA'),
boxB: createCustomShapeId('boxB'),
boxC: createCustomShapeId('boxC'),
videoA: createCustomShapeId('videoA'),
}
function createVideoShape(id: TLShapeId) {
app.createShapes([
{
id: id,
type: 'video',
x: 0,
y: 0,
props: {
w: 160,
h: 90,
},
},
])
}
beforeEach(() => {
app = new TestApp()
app.selectAll()
app.deleteShapes()
app.createShapes([
{
id: ids.boxA,
type: 'geo',
x: 0,
y: 0,
props: {
w: 100,
h: 100,
},
},
{
id: ids.boxB,
type: 'geo',
x: 100,
y: 100,
props: {
w: 50,
h: 50,
},
},
{
id: ids.boxC,
type: 'geo',
x: 400,
y: 400,
props: {
w: 100,
h: 100,
},
},
])
app.selectAll()
})
describe('when less than two shapes are selected', () => {
it('does nothing', () => {
app.setSelectedIds([ids.boxB])
[feature] ui events (#1326) This PR updates the editor events: - adds types to the events emitted by the app (by `app.emit`) - removes a few events emitted by the app (e.g. `move-to-page`, `change-camera`) - adds `onEvent` prop to the <TldrawUi> / <Tldraw> components - call the `onEvent` when actions occur or tools are selected - does some superficial cleanup on editor app APIs ### Release Note - Fix layout bug in error dialog - (ui) Add `TLEventMap` for types emitted from editor app - (editor) Update `crash` event emitted from editor app to include error - (editor) Update `change-history` event emitted from editor app - (editor) Remove `change-camera` event from editor app - (editor) Remove `move-to-page` event from editor app - (ui) Add `onEvent` prop and events to <Tldraw> / <TldrawUi> - (editor) Replace `app.openMenus` plain Set with computed value - (editor) Add `addOpenMenu` method - (editor) Add `removeOpenMenu` method - (editor) Add `setFocusMode` method - (editor) Add `setToolLocked` method - (editor) Add `setSnapMode` method - (editor) Add `isSnapMode` method - (editor) Update `setGridMode` method return type to editor app - (editor) Update `setReadOnly` method return type to editor app - (editor) Update `setPenMode` method return type to editor app - (editor) Update `selectNone` method return type to editor app - (editor) Rename `backToContent` to `zoomToContent` - (editor) Remove `TLReorderOperation` type --------- Co-authored-by: Orange Mug <orangemug@users.noreply.github.com>
2023-05-11 22:14:58 +00:00
const fn = jest.fn()
app.on('change-history', fn)
2023-04-25 11:01:25 +00:00
app.stretchShapes('horizontal')
jest.advanceTimersByTime(1000)
expect(fn).not.toHaveBeenCalled()
})
})
describe('when multiple shapes are selected', () => {
it('stretches horizontally', () => {
app.selectAll()
app.stretchShapes('horizontal')
jest.advanceTimersByTime(1000)
app.expectShapeToMatch(
{ id: ids.boxA, x: 0, y: 0, props: { w: 500 } },
{ id: ids.boxB, x: 0, y: 100, props: { w: 500 } },
{ id: ids.boxC, x: 0, y: 400, props: { w: 500 } }
)
})
it('stretches horizontally and preserves aspect ratio', () => {
createVideoShape(ids.videoA)
app.selectAll()
expect(app.selectedShapes.length).toBe(4)
app.stretchShapes('horizontal')
jest.advanceTimersByTime(1000)
const newHeight = (500 * 9) / 16
app.expectShapeToMatch(
{ id: ids.boxA, x: 0, y: 0, props: { w: 500 } },
{ id: ids.boxB, x: 0, y: 100, props: { w: 500 } },
{ id: ids.boxC, x: 0, y: 400, props: { w: 500 } },
{ id: ids.videoA, x: 0, y: -95.625, props: { w: 500, h: newHeight } }
)
})
it('stretches vertically', () => {
app.selectAll()
app.stretchShapes('vertical')
jest.advanceTimersByTime(1000)
app.expectShapeToMatch(
{ id: ids.boxA, x: 0, y: 0, props: { h: 500 } },
{ id: ids.boxB, x: 100, y: 0, props: { h: 500 } },
{ id: ids.boxC, x: 400, y: 0, props: { h: 500 } }
)
})
it('stretches vertically and preserves aspect ratio', () => {
createVideoShape(ids.videoA)
app.selectAll()
expect(app.selectedShapes.length).toBe(4)
app.stretchShapes('vertical')
jest.advanceTimersByTime(1000)
const newWidth = (500 * 16) / 9
app.expectShapeToMatch(
{ id: ids.boxA, x: 0, y: 0, props: { h: 500 } },
{ id: ids.boxB, x: 100, y: 0, props: { h: 500 } },
{ id: ids.boxC, x: 400, y: 0, props: { h: 500 } },
{ id: ids.videoA, x: -364.44444444444446, y: 0, props: { w: newWidth, h: 500 } }
)
})
it('does, undoes and redoes command', () => {
app.mark('stretch')
app.stretchShapes('horizontal')
jest.advanceTimersByTime(1000)
app.expectShapeToMatch({ id: ids.boxB, x: 0, props: { w: 500 } })
app.undo()
app.expectShapeToMatch({ id: ids.boxB, x: 100, props: { w: 50 } })
app.redo()
app.expectShapeToMatch({ id: ids.boxB, x: 0, props: { w: 500 } })
})
})
describe('When shapes are the child of another shape.', () => {
it('stretches horizontally', () => {
app.reparentShapesById([ids.boxB], ids.boxA)
app.select(ids.boxB, ids.boxC)
app.stretchShapes('horizontal')
jest.advanceTimersByTime(1000)
app.expectShapeToMatch(
{ id: ids.boxB, x: 100, y: 100, props: { w: 400 } },
{ id: ids.boxC, x: 100, y: 400, props: { w: 400 } }
)
})
it('stretches vertically', () => {
app.reparentShapesById([ids.boxB], ids.boxA)
app.select(ids.boxB, ids.boxC)
app.stretchShapes('vertical')
jest.advanceTimersByTime(1000)
app.expectShapeToMatch(
{ id: ids.boxB, x: 100, y: 100, props: { h: 400 } },
{ id: ids.boxC, x: 400, y: 100, props: { h: 400 } }
)
})
})
describe('When shapes are the child of a rotated shape.', () => {
beforeEach(() => {
app = new TestApp()
app.selectAll()
app.deleteShapes()
app.createShapes([
{
id: ids.boxA,
type: 'geo',
x: 0,
y: 0,
props: {
w: 100,
h: 100,
},
rotation: PI,
},
{
id: ids.boxB,
type: 'geo',
parentId: ids.boxA,
x: 100,
y: 100,
props: {
w: 50,
h: 50,
},
},
{
id: ids.boxC,
type: 'geo',
x: 200,
y: 200,
props: {
w: 100,
h: 100,
},
},
])
app.selectAll()
})
it('does not stretches rotated shapes', () => {
app.select(ids.boxB, ids.boxC)
app.stretchShapes('horizontal')
jest.advanceTimersByTime(1000)
app.expectShapeToMatch(
{
id: ids.boxB,
x: 100,
y: 100,
props: {
w: 50,
h: 50,
},
},
{
id: ids.boxC,
x: -150,
y: 200,
props: {
w: 450,
h: 100,
},
}
)
})
it('does not stretches rotated shapes', () => {
app.select(ids.boxB, ids.boxC)
app.stretchShapes('vertical')
jest.advanceTimersByTime(1000)
app.expectShapeToMatch(
{
id: ids.boxB,
x: 100,
y: 100,
props: {
w: 50,
h: 50,
},
},
{
id: ids.boxC,
x: 200,
y: -150,
props: {
w: 100,
h: 450,
},
}
)
})
})
describe('When shapes have 0-width or 0-height', () => {
it('Does not error with 0-width', () => {
app.selectAll()
app.deleteShapes()
app
.setSelectedTool('arrow')
.keyDown('shift')
.pointerDown(50, 0)
.pointerMove(50, 100)
.pointerUp(50, 100)
.keyUp('shift')
.setSelectedTool('geo')
.pointerDown(0, 0)
.pointerMove(100, 100)
.pointerUp(100, 100)
app.selectAll()
// make sure we don't get any errors:
app.stretchShapes('horizontal')
app.stretchShapes('vertical')
})
it('Does not error with 0-height', () => {
app.selectAll()
app.deleteShapes()
app
// draw a perfectly horiztonal arrow:
.setSelectedTool('arrow')
.keyDown('shift')
.pointerDown(0, 50)
.pointerMove(100, 50)
.pointerUp(100, 50)
.keyUp('shift')
// plus a box:
.setSelectedTool('geo')
.pointerDown(0, 0)
.pointerMove(100, 100)
.pointerUp(100, 100)
app.selectAll()
// make sure we don't get any errors:
app.stretchShapes('horizontal')
app.stretchShapes('vertical')
})
})