test(commands): add coverage for when commands shouldn't run

This commit is contained in:
Tais Massaro 2021-09-04 22:05:09 +02:00
parent 010a09ff19
commit 367f2c5cd0
16 changed files with 312 additions and 161 deletions

View file

@ -5,69 +5,73 @@ import { AlignType } from '~types'
describe('Align command', () => { describe('Align command', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
it('does, undoes and redoes command', () => { describe('when less than two shapes are selected', () => {
tlstate.loadDocument(mockDocument) it('does nothing', () => {
tlstate.selectAll() tlstate.loadDocument(mockDocument).select('rect2')
tlstate.align(AlignType.Top) const initialState = tlstate.state
tlstate.align(AlignType.Top)
const currentState = tlstate.state
expect(tlstate.getShape('rect2').point).toEqual([100, 0]) expect(currentState).toEqual(initialState)
})
tlstate.undo()
expect(tlstate.getShape('rect2').point).toEqual([100, 100])
tlstate.redo()
expect(tlstate.getShape('rect2').point).toEqual([100, 0])
}) })
it('aligns top', () => { describe('when multiple shapes are selected', () => {
tlstate.loadDocument(mockDocument) beforeEach(() => {
tlstate.selectAll() tlstate.loadDocument(mockDocument)
tlstate.align(AlignType.Top) tlstate.selectAll()
})
expect(tlstate.getShape('rect2').point).toEqual([100, 0]) it('does, undoes and redoes command', () => {
}) tlstate.align(AlignType.Top)
it('aligns right', () => { expect(tlstate.getShape('rect2').point).toEqual([100, 0])
tlstate.loadDocument(mockDocument)
tlstate.selectAll()
tlstate.align(AlignType.Right)
expect(tlstate.getShape('rect1').point).toEqual([100, 0]) tlstate.undo()
})
it('aligns bottom', () => { expect(tlstate.getShape('rect2').point).toEqual([100, 100])
tlstate.loadDocument(mockDocument)
tlstate.selectAll()
tlstate.align(AlignType.Bottom)
expect(tlstate.getShape('rect1').point).toEqual([0, 100]) tlstate.redo()
})
it('aligns left', () => { expect(tlstate.getShape('rect2').point).toEqual([100, 0])
tlstate.loadDocument(mockDocument) })
tlstate.selectAll()
tlstate.align(AlignType.Left)
expect(tlstate.getShape('rect2').point).toEqual([0, 100]) it('aligns top', () => {
}) tlstate.align(AlignType.Top)
it('aligns center horizontal', () => { expect(tlstate.getShape('rect2').point).toEqual([100, 0])
tlstate.loadDocument(mockDocument) })
tlstate.selectAll()
tlstate.align(AlignType.CenterHorizontal)
expect(tlstate.getShape('rect1').point).toEqual([50, 0]) it('aligns right', () => {
expect(tlstate.getShape('rect2').point).toEqual([50, 100]) tlstate.align(AlignType.Right)
})
it('aligns center vertical', () => { expect(tlstate.getShape('rect1').point).toEqual([100, 0])
tlstate.loadDocument(mockDocument) })
tlstate.selectAll()
tlstate.align(AlignType.CenterVertical)
expect(tlstate.getShape('rect1').point).toEqual([0, 50]) it('aligns bottom', () => {
expect(tlstate.getShape('rect2').point).toEqual([100, 50]) tlstate.align(AlignType.Bottom)
expect(tlstate.getShape('rect1').point).toEqual([0, 100])
})
it('aligns left', () => {
tlstate.align(AlignType.Left)
expect(tlstate.getShape('rect2').point).toEqual([0, 100])
})
it('aligns center horizontal', () => {
tlstate.align(AlignType.CenterHorizontal)
expect(tlstate.getShape('rect1').point).toEqual([50, 0])
expect(tlstate.getShape('rect2').point).toEqual([50, 100])
})
it('aligns center vertical', () => {
tlstate.align(AlignType.CenterVertical)
expect(tlstate.getShape('rect1').point).toEqual([0, 50])
expect(tlstate.getShape('rect2').point).toEqual([100, 50])
})
}) })
}) })

View file

@ -4,8 +4,22 @@ import { mockDocument } from '~test'
describe('Create command', () => { describe('Create command', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
it('does, undoes and redoes command', () => { beforeEach(() => {
tlstate.loadDocument(mockDocument) tlstate.loadDocument(mockDocument)
})
describe('when no shape is provided', () => {
it('does nothing', () => {
const initialState = tlstate.state
tlstate.create()
const currentState = tlstate.state
expect(currentState).toEqual(initialState)
})
})
it('does, undoes and redoes command', () => {
const shape = { ...tlstate.getShape('rect1'), id: 'rect4' } const shape = { ...tlstate.getShape('rect1'), id: 'rect4' }
tlstate.create(shape) tlstate.create(shape)

View file

@ -4,9 +4,22 @@ import { mockDocument } from '~test'
describe('Delete page', () => { describe('Delete page', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
it('does, undoes and redoes command', () => { beforeEach(() => {
tlstate.loadDocument(mockDocument) tlstate.loadDocument(mockDocument)
})
describe('when there are no pages in the current document', () => {
it('does nothing', () => {
tlstate.resetDocument()
const initialState = tlstate.state
tlstate.deletePage('page1')
const currentState = tlstate.state
expect(currentState).toEqual(initialState)
})
})
it('does, undoes and redoes command', () => {
const initialId = tlstate.currentPageId const initialId = tlstate.currentPageId
tlstate.createPage() tlstate.createPage()

View file

@ -6,8 +6,21 @@ import type { TLDrawShape } from '~types'
describe('Delete command', () => { describe('Delete command', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
it('does, undoes and redoes command', () => { beforeEach(() => {
tlstate.loadDocument(mockDocument) tlstate.loadDocument(mockDocument)
})
describe('when no shape is selected', () => {
it('does nothing', () => {
const initialState = tlstate.state
tlstate.delete()
const currentState = tlstate.state
expect(currentState).toEqual(initialState)
})
})
it('does, undoes and redoes command', () => {
tlstate.select('rect2') tlstate.select('rect2')
tlstate.delete() tlstate.delete()
@ -26,7 +39,6 @@ describe('Delete command', () => {
}) })
it('deletes two shapes', () => { it('deletes two shapes', () => {
tlstate.loadDocument(mockDocument)
tlstate.selectAll() tlstate.selectAll()
tlstate.delete() tlstate.delete()
@ -45,8 +57,6 @@ describe('Delete command', () => {
}) })
it('deletes bound shapes', () => { it('deletes bound shapes', () => {
tlstate.loadDocument(mockDocument)
expect(Object.values(tlstate.page.bindings)[0]).toBe(undefined) expect(Object.values(tlstate.page.bindings)[0]).toBe(undefined)
tlstate tlstate
@ -86,26 +96,18 @@ describe('Delete command', () => {
expect(tlstate.getShape('arrow1').handles?.start.bindingId).toBe(undefined) expect(tlstate.getShape('arrow1').handles?.start.bindingId).toBe(undefined)
}) })
describe('when deleting grouped shapes', () => { describe('when deleting shapes in a group', () => {
it('updates the group', () => { it('updates the group', () => {
tlstate tlstate.group(['rect1', 'rect2', 'rect3'], 'newGroup').select('rect1').delete()
.loadDocument(mockDocument)
.group(['rect1', 'rect2', 'rect3'], 'newGroup')
.select('rect1')
.delete()
expect(tlstate.getShape('rect1')).toBeUndefined() expect(tlstate.getShape('rect1')).toBeUndefined()
expect(tlstate.getShape('newGroup').children).toStrictEqual(['rect2', 'rect3']) expect(tlstate.getShape('newGroup').children).toStrictEqual(['rect2', 'rect3'])
}) })
}) })
describe('when deleting shapes with children', () => { describe('when deleting a group', () => {
it('also deletes the children', () => { it('deletes all grouped shapes', () => {
tlstate tlstate.group(['rect1', 'rect2'], 'newGroup').select('newGroup').delete()
.loadDocument(mockDocument)
.group(['rect1', 'rect2'], 'newGroup')
.select('newGroup')
.delete()
expect(tlstate.getShape('rect1')).toBeUndefined() expect(tlstate.getShape('rect1')).toBeUndefined()
expect(tlstate.getShape('rect2')).toBeUndefined() expect(tlstate.getShape('rect2')).toBeUndefined()

View file

@ -4,11 +4,26 @@ import { DistributeType } from '~types'
describe('Distribute command', () => { describe('Distribute command', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
tlstate.loadDocument(mockDocument)
tlstate.selectAll() beforeEach(() => {
tlstate.loadDocument(mockDocument)
})
describe('when less than three shapes are selected', () => {
it('does nothing', () => {
tlstate.select('rect1', 'rect2')
const initialState = tlstate.state
tlstate.distribute(DistributeType.Horizontal)
const currentState = tlstate.state
expect(currentState).toEqual(initialState)
})
})
it('does, undoes and redoes command', () => { it('does, undoes and redoes command', () => {
tlstate.selectAll()
tlstate.distribute(DistributeType.Horizontal) tlstate.distribute(DistributeType.Horizontal)
expect(tlstate.getShape('rect3').point).toEqual([50, 20]) expect(tlstate.getShape('rect3').point).toEqual([50, 20])
tlstate.undo() tlstate.undo()
expect(tlstate.getShape('rect3').point).toEqual([20, 20]) expect(tlstate.getShape('rect3').point).toEqual([20, 20])
@ -17,9 +32,9 @@ describe('Distribute command', () => {
}) })
it('distributes vertically', () => { it('distributes vertically', () => {
tlstate.loadDocument(mockDocument)
tlstate.selectAll() tlstate.selectAll()
tlstate.distribute(DistributeType.Vertical) tlstate.distribute(DistributeType.Vertical)
expect(tlstate.getShape('rect3').point).toEqual([20, 50]) expect(tlstate.getShape('rect3').point).toEqual([20, 50])
}) })
}) })

View file

@ -1,14 +1,28 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-non-null-assertion */
import { TLDrawState } from '~state' import { TLDrawState } from '~state'
import { mockDocument } from '~test' import { mockDocument } from '~test'
import { ArrowShape, GroupShape, TLDrawShapeType } from '~types' import { ArrowShape, TLDrawShapeType } from '~types'
describe('Duplicate command', () => { describe('Duplicate command', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
tlstate.loadDocument(mockDocument)
tlstate.select('rect1') beforeEach(() => {
tlstate.loadDocument(mockDocument)
})
describe('when no shape is selected', () => {
it('does nothing', () => {
const initialState = tlstate.state
tlstate.duplicate()
const currentState = tlstate.state
expect(currentState).toEqual(initialState)
})
})
it('does, undoes and redoes command', () => { it('does, undoes and redoes command', () => {
tlstate.select('rect1')
expect(Object.keys(tlstate.getPage().shapes).length).toBe(3) expect(Object.keys(tlstate.getPage().shapes).length).toBe(3)
tlstate.duplicate() tlstate.duplicate()
@ -121,7 +135,6 @@ describe('Duplicate command', () => {
}) })
it('duplicates groups', () => { it('duplicates groups', () => {
tlstate.loadDocument(mockDocument)
tlstate.group(['rect1', 'rect2'], 'newGroup').select('newGroup') tlstate.group(['rect1', 'rect2'], 'newGroup').select('newGroup')
const beforeShapeIds = Object.keys(tlstate.page.shapes) const beforeShapeIds = Object.keys(tlstate.page.shapes)
@ -140,7 +153,6 @@ describe('Duplicate command', () => {
}) })
it('duplicates grouped shapes', () => { it('duplicates grouped shapes', () => {
tlstate.loadDocument(mockDocument)
tlstate.group(['rect1', 'rect2'], 'newGroup').select('rect1') tlstate.group(['rect1', 'rect2'], 'newGroup').select('rect1')
const beforeShapeIds = Object.keys(tlstate.page.shapes) const beforeShapeIds = Object.keys(tlstate.page.shapes)

View file

@ -5,8 +5,21 @@ import type { RectangleShape } from '~types'
describe('Flip command', () => { describe('Flip command', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
it('does, undoes and redoes command', () => { beforeEach(() => {
tlstate.loadDocument(mockDocument) tlstate.loadDocument(mockDocument)
})
describe('when no shape is selected', () => {
it('does nothing', () => {
const initialState = tlstate.state
tlstate.flipHorizontal()
const currentState = tlstate.state
expect(currentState).toEqual(initialState)
})
})
it('does, undoes and redoes command', () => {
tlstate.select('rect1', 'rect2') tlstate.select('rect1', 'rect2')
tlstate.flipHorizontal() tlstate.flipHorizontal()
@ -22,7 +35,6 @@ describe('Flip command', () => {
}) })
it('flips horizontally', () => { it('flips horizontally', () => {
tlstate.loadDocument(mockDocument)
tlstate.select('rect1', 'rect2') tlstate.select('rect1', 'rect2')
tlstate.flipHorizontal() tlstate.flipHorizontal()
@ -30,7 +42,6 @@ describe('Flip command', () => {
}) })
it('flips vertically', () => { it('flips vertically', () => {
tlstate.loadDocument(mockDocument)
tlstate.select('rect1', 'rect2') tlstate.select('rect1', 'rect2')
tlstate.flipVertical() tlstate.flipVertical()

View file

@ -6,8 +6,11 @@ import { GroupShape, TLDrawShape, TLDrawShapeType } from '~types'
describe('Group command', () => { describe('Group command', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
it('does, undoes and redoes command', () => { beforeEach(() => {
tlstate.loadDocument(mockDocument) tlstate.loadDocument(mockDocument)
})
it('does, undoes and redoes command', () => {
tlstate.group(['rect1', 'rect2'], 'newGroup') tlstate.group(['rect1', 'rect2'], 'newGroup')
expect(tlstate.getShape<GroupShape>('newGroup')).toBeTruthy() expect(tlstate.getShape<GroupShape>('newGroup')).toBeTruthy()
@ -23,7 +26,6 @@ describe('Group command', () => {
describe('when less than two shapes are selected', () => { describe('when less than two shapes are selected', () => {
it('does nothing', () => { it('does nothing', () => {
tlstate.loadDocument(mockDocument)
tlstate.deselectAll() tlstate.deselectAll()
// @ts-ignore // @ts-ignore
@ -49,8 +51,6 @@ describe('Group command', () => {
*/ */
it('creates a group with the correct props', () => { it('creates a group with the correct props', () => {
tlstate.loadDocument(mockDocument)
tlstate.updateShapes( tlstate.updateShapes(
{ {
id: 'rect1', id: 'rect1',
@ -74,8 +74,6 @@ describe('Group command', () => {
}) })
it('reparents the grouped shapes', () => { it('reparents the grouped shapes', () => {
tlstate.loadDocument(mockDocument)
tlstate.updateShapes( tlstate.updateShapes(
{ {
id: 'rect1', id: 'rect1',
@ -114,9 +112,9 @@ describe('Group command', () => {
}) })
}) })
describe('when grouping shapes that are the child of another group', () => { describe('when grouping shapes that already belong to a group', () => {
/* /*
Do not allow groups to nest. All groups should be the parent of Do not allow groups to nest. All groups should be children of
the page: a group should never be the child of a different group. the page: a group should never be the child of a different group.
This is a UX decision as much as a technical one. This is a UX decision as much as a technical one.
*/ */
@ -125,7 +123,8 @@ describe('Group command', () => {
/* /*
When the selected shapes are the children of another group, and so When the selected shapes are the children of another group, and so
long as the children do not represent ALL of the group's children, long as the children do not represent ALL of the group's children,
then a new group should be created that is a child of the parent group. then a new group should be created from the selected shapes and the
original group be updated to only contain the remaining ones.
*/ */
tlstate.resetDocument().createShapes( tlstate.resetDocument().createShapes(
@ -206,7 +205,7 @@ describe('Group command', () => {
expect(tlstate.getShape<GroupShape>('newGroupB').children).toStrictEqual(['rect1', 'rect3']) expect(tlstate.getShape<GroupShape>('newGroupB').children).toStrictEqual(['rect1', 'rect3'])
}) })
it('does not group shapes if shapes are all the groups children', () => { it('does nothing if all shapes in the group are selected', () => {
/* /*
If the selected shapes represent ALL of the children of the a If the selected shapes represent ALL of the children of the a
group, then no effect should occur. group, then no effect should occur.
@ -269,7 +268,7 @@ describe('Group command', () => {
]) ])
}) })
it('marges selected groups that no longer have children', () => { it('merges selected groups that no longer have children', () => {
/* /*
If the user is creating a group while having selected other If the user is creating a group while having selected other
groups, then the selected groups should be destroyed and a new groups, then the selected groups should be destroyed and a new

View file

@ -5,9 +5,23 @@ import { ArrowShape, TLDrawShapeType } from '~types'
describe('Move to page command', () => { describe('Move to page command', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
beforeEach(() => {
tlstate.loadDocument(mockDocument).createPage('page2').changePage('page1')
})
describe('when no shape is selected', () => {
it('does nothing', () => {
const initialState = tlstate.state
tlstate.moveToPage('page2')
const currentState = tlstate.state
expect(currentState).toEqual(initialState)
})
})
/* /*
Moving shapes to a new page should remove those shapes from the Moving shapes to a new page should remove those shapes from the
current page and add them to the specifed page. If bindings exist current page and add them to the specified page. If bindings exist
that effect the moved shapes, then the bindings should be destroyed that effect the moved shapes, then the bindings should be destroyed
on the old page and created on the new page only if both the "to" on the old page and created on the new page only if both the "to"
and "from" shapes were moved. The app should then change pages to and "from" shapes were moved. The app should then change pages to
@ -15,12 +29,7 @@ describe('Move to page command', () => {
*/ */
it('does, undoes and redoes command', () => { it('does, undoes and redoes command', () => {
tlstate tlstate.select('rect1', 'rect2').moveToPage('page2')
.loadDocument(mockDocument)
.createPage('page2')
.changePage('page1')
.select('rect1', 'rect2')
.moveToPage('page2')
expect(tlstate.currentPageId).toBe('page2') expect(tlstate.currentPageId).toBe('page2')
expect(tlstate.getShape('rect1', 'page1')).toBeUndefined() expect(tlstate.getShape('rect1', 'page1')).toBeUndefined()
@ -51,7 +60,6 @@ describe('Move to page command', () => {
describe('when moving shapes with bindings', () => { describe('when moving shapes with bindings', () => {
it('deletes bindings when only the bound-to shape is moved', () => { it('deletes bindings when only the bound-to shape is moved', () => {
tlstate tlstate
.loadDocument(mockDocument)
.selectAll() .selectAll()
.delete() .delete()
.createShapes( .createShapes(
@ -66,7 +74,7 @@ describe('Move to page command', () => {
const bindingId = tlstate.bindings[0].id const bindingId = tlstate.bindings[0].id
expect(tlstate.getShape<ArrowShape>('arrow1').handles.start.bindingId).toBe(bindingId) expect(tlstate.getShape<ArrowShape>('arrow1').handles.start.bindingId).toBe(bindingId)
tlstate.createPage('page2').changePage('page1').select('target1').moveToPage('page2') tlstate.select('target1').moveToPage('page2')
expect( expect(
tlstate.getShape<ArrowShape>('arrow1', 'page1').handles.start.bindingId tlstate.getShape<ArrowShape>('arrow1', 'page1').handles.start.bindingId
@ -93,7 +101,6 @@ describe('Move to page command', () => {
it('deletes bindings when only the bound-from shape is moved', () => { it('deletes bindings when only the bound-from shape is moved', () => {
tlstate tlstate
.loadDocument(mockDocument)
.selectAll() .selectAll()
.delete() .delete()
.createShapes( .createShapes(
@ -108,7 +115,7 @@ describe('Move to page command', () => {
const bindingId = tlstate.bindings[0].id const bindingId = tlstate.bindings[0].id
expect(tlstate.getShape<ArrowShape>('arrow1').handles.start.bindingId).toBe(bindingId) expect(tlstate.getShape<ArrowShape>('arrow1').handles.start.bindingId).toBe(bindingId)
tlstate.createPage('page2').changePage('page1').select('arrow1').moveToPage('page2') tlstate.select('arrow1').moveToPage('page2')
expect( expect(
tlstate.getShape<ArrowShape>('arrow1', 'page2').handles.start.bindingId tlstate.getShape<ArrowShape>('arrow1', 'page2').handles.start.bindingId
@ -135,7 +142,6 @@ describe('Move to page command', () => {
it('moves bindings when both shapes are moved', () => { it('moves bindings when both shapes are moved', () => {
tlstate tlstate
.loadDocument(mockDocument)
.selectAll() .selectAll()
.delete() .delete()
.createShapes( .createShapes(
@ -150,11 +156,7 @@ describe('Move to page command', () => {
const bindingId = tlstate.bindings[0].id const bindingId = tlstate.bindings[0].id
expect(tlstate.getShape<ArrowShape>('arrow1').handles.start.bindingId).toBe(bindingId) expect(tlstate.getShape<ArrowShape>('arrow1').handles.start.bindingId).toBe(bindingId)
tlstate tlstate.select('arrow1', 'target1').moveToPage('page2')
.createPage('page2')
.changePage('page1')
.select('arrow1', 'target1')
.moveToPage('page2')
expect(tlstate.getShape<ArrowShape>('arrow1', 'page2').handles.start.bindingId).toBe( expect(tlstate.getShape<ArrowShape>('arrow1', 'page2').handles.start.bindingId).toBe(
bindingId bindingId
@ -182,12 +184,7 @@ describe('Move to page command', () => {
describe('when moving grouped shapes', () => { describe('when moving grouped shapes', () => {
it('moves groups and their children', () => { it('moves groups and their children', () => {
tlstate tlstate.group(['rect1', 'rect2'], 'groupA').moveToPage('page2')
.loadDocument(mockDocument)
.createPage('page2')
.changePage('page1')
.group(['rect1', 'rect2'], 'groupA')
.moveToPage('page2')
expect(tlstate.getShape('rect1', 'page1')).toBeUndefined() expect(tlstate.getShape('rect1', 'page1')).toBeUndefined()
expect(tlstate.getShape('rect2', 'page1')).toBeUndefined() expect(tlstate.getShape('rect2', 'page1')).toBeUndefined()
@ -218,18 +215,10 @@ describe('Move to page command', () => {
expect(tlstate.getShape('groupA', 'page2')).toBeDefined() expect(tlstate.getShape('groupA', 'page2')).toBeDefined()
}) })
it('deletes groups shapes if the groups children were all moved', () => { it.todo('deletes groups shapes if the groups children were all moved')
// ...
})
it('reparents grouped shapes if the group is not moved', () => { it('reparents grouped shapes if the group is not moved', () => {
tlstate tlstate.group(['rect1', 'rect2', 'rect3'], 'groupA').select('rect1').moveToPage('page2')
.loadDocument(mockDocument)
.createPage('page2')
.changePage('page1')
.group(['rect1', 'rect2', 'rect3'], 'groupA')
.select('rect1')
.moveToPage('page2')
expect(tlstate.getShape('rect1', 'page1')).toBeUndefined() expect(tlstate.getShape('rect1', 'page1')).toBeUndefined()
expect(tlstate.getShape('rect1', 'page2')).toBeDefined() expect(tlstate.getShape('rect1', 'page2')).toBeDefined()

View file

@ -42,8 +42,22 @@ function getSortedShapeIds(data: Data) {
describe('Move command', () => { describe('Move command', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
it('does, undoes and redoes command', () => { beforeEach(() => {
tlstate.loadDocument(doc) tlstate.loadDocument(doc)
})
describe('when no shape is selected', () => {
it('does nothing', () => {
const initialState = tlstate.state
tlstate.moveToBack()
const currentState = tlstate.state
expect(currentState).toEqual(initialState)
})
})
it('does, undoes and redoes command', () => {
tlstate.select('b') tlstate.select('b')
tlstate.moveToBack() tlstate.moveToBack()
expect(getSortedShapeIds(tlstate.state)).toBe('bacd') expect(getSortedShapeIds(tlstate.state)).toBe('bacd')
@ -55,21 +69,18 @@ describe('Move command', () => {
describe('to back', () => { describe('to back', () => {
it('moves a shape to back', () => { it('moves a shape to back', () => {
tlstate.loadDocument(doc)
tlstate.select('b') tlstate.select('b')
tlstate.moveToBack() tlstate.moveToBack()
expect(getSortedShapeIds(tlstate.state)).toBe('bacd') expect(getSortedShapeIds(tlstate.state)).toBe('bacd')
}) })
it('moves two adjacent siblings to back', () => { it('moves two adjacent siblings to back', () => {
tlstate.loadDocument(doc)
tlstate.select('b', 'c') tlstate.select('b', 'c')
tlstate.moveToBack() tlstate.moveToBack()
expect(getSortedShapeIds(tlstate.state)).toBe('bcad') expect(getSortedShapeIds(tlstate.state)).toBe('bcad')
}) })
it('moves two non-adjacent siblings to back', () => { it('moves two non-adjacent siblings to back', () => {
tlstate.loadDocument(doc)
tlstate.select('b', 'd') tlstate.select('b', 'd')
tlstate.moveToBack() tlstate.moveToBack()
expect(getSortedShapeIds(tlstate.state)).toBe('bdac') expect(getSortedShapeIds(tlstate.state)).toBe('bdac')
@ -78,35 +89,30 @@ describe('Move command', () => {
describe('backward', () => { describe('backward', () => {
it('moves a shape backward', () => { it('moves a shape backward', () => {
tlstate.loadDocument(doc)
tlstate.select('c') tlstate.select('c')
tlstate.moveBackward() tlstate.moveBackward()
expect(getSortedShapeIds(tlstate.state)).toBe('acbd') expect(getSortedShapeIds(tlstate.state)).toBe('acbd')
}) })
it('moves a shape at first index backward', () => { it('moves a shape at first index backward', () => {
tlstate.loadDocument(doc)
tlstate.select('a') tlstate.select('a')
tlstate.moveBackward() tlstate.moveBackward()
expect(getSortedShapeIds(tlstate.state)).toBe('abcd') expect(getSortedShapeIds(tlstate.state)).toBe('abcd')
}) })
it('moves two adjacent siblings backward', () => { it('moves two adjacent siblings backward', () => {
tlstate.loadDocument(doc)
tlstate.select('c', 'd') tlstate.select('c', 'd')
tlstate.moveBackward() tlstate.moveBackward()
expect(getSortedShapeIds(tlstate.state)).toBe('acdb') expect(getSortedShapeIds(tlstate.state)).toBe('acdb')
}) })
it('moves two non-adjacent siblings backward', () => { it('moves two non-adjacent siblings backward', () => {
tlstate.loadDocument(doc)
tlstate.select('b', 'd') tlstate.select('b', 'd')
tlstate.moveBackward() tlstate.moveBackward()
expect(getSortedShapeIds(tlstate.state)).toBe('badc') expect(getSortedShapeIds(tlstate.state)).toBe('badc')
}) })
it('moves two adjacent siblings backward at zero index', () => { it('moves two adjacent siblings backward at zero index', () => {
tlstate.loadDocument(doc)
tlstate.select('a', 'b') tlstate.select('a', 'b')
tlstate.moveBackward() tlstate.moveBackward()
expect(getSortedShapeIds(tlstate.state)).toBe('abcd') expect(getSortedShapeIds(tlstate.state)).toBe('abcd')
@ -115,14 +121,12 @@ describe('Move command', () => {
describe('forward', () => { describe('forward', () => {
it('moves a shape forward', () => { it('moves a shape forward', () => {
tlstate.loadDocument(doc)
tlstate.select('c') tlstate.select('c')
tlstate.moveForward() tlstate.moveForward()
expect(getSortedShapeIds(tlstate.state)).toBe('abdc') expect(getSortedShapeIds(tlstate.state)).toBe('abdc')
}) })
it('moves a shape forward at the top index', () => { it('moves a shape forward at the top index', () => {
tlstate.loadDocument(doc)
tlstate.select('b') tlstate.select('b')
tlstate.moveForward() tlstate.moveForward()
tlstate.moveForward() tlstate.moveForward()
@ -131,21 +135,18 @@ describe('Move command', () => {
}) })
it('moves two adjacent siblings forward', () => { it('moves two adjacent siblings forward', () => {
tlstate.loadDocument(doc)
tlstate.select('a', 'b') tlstate.select('a', 'b')
tlstate.moveForward() tlstate.moveForward()
expect(getSortedShapeIds(tlstate.state)).toBe('cabd') expect(getSortedShapeIds(tlstate.state)).toBe('cabd')
}) })
it('moves two non-adjacent siblings forward', () => { it('moves two non-adjacent siblings forward', () => {
tlstate.loadDocument(doc)
tlstate.select('a', 'c') tlstate.select('a', 'c')
tlstate.moveForward() tlstate.moveForward()
expect(getSortedShapeIds(tlstate.state)).toBe('badc') expect(getSortedShapeIds(tlstate.state)).toBe('badc')
}) })
it('moves two adjacent siblings forward at top index', () => { it('moves two adjacent siblings forward at top index', () => {
tlstate.loadDocument(doc)
tlstate.select('c', 'd') tlstate.select('c', 'd')
tlstate.moveForward() tlstate.moveForward()
expect(getSortedShapeIds(tlstate.state)).toBe('abcd') expect(getSortedShapeIds(tlstate.state)).toBe('abcd')
@ -154,28 +155,24 @@ describe('Move command', () => {
describe('to front', () => { describe('to front', () => {
it('moves a shape to front', () => { it('moves a shape to front', () => {
tlstate.loadDocument(doc)
tlstate.select('b') tlstate.select('b')
tlstate.moveToFront() tlstate.moveToFront()
expect(getSortedShapeIds(tlstate.state)).toBe('acdb') expect(getSortedShapeIds(tlstate.state)).toBe('acdb')
}) })
it('moves two adjacent siblings to front', () => { it('moves two adjacent siblings to front', () => {
tlstate.loadDocument(doc)
tlstate.select('a', 'b') tlstate.select('a', 'b')
tlstate.moveToFront() tlstate.moveToFront()
expect(getSortedShapeIds(tlstate.state)).toBe('cdab') expect(getSortedShapeIds(tlstate.state)).toBe('cdab')
}) })
it('moves two non-adjacent siblings to front', () => { it('moves two non-adjacent siblings to front', () => {
tlstate.loadDocument(doc)
tlstate.select('a', 'c') tlstate.select('a', 'c')
tlstate.moveToFront() tlstate.moveToFront()
expect(getSortedShapeIds(tlstate.state)).toBe('bdac') expect(getSortedShapeIds(tlstate.state)).toBe('bdac')
}) })
it('moves siblings already at front to front', () => { it('moves siblings already at front to front', () => {
tlstate.loadDocument(doc)
tlstate.select('c', 'd') tlstate.select('c', 'd')
tlstate.moveToFront() tlstate.moveToFront()
expect(getSortedShapeIds(tlstate.state)).toBe('abcd') expect(getSortedShapeIds(tlstate.state)).toBe('abcd')

View file

@ -3,10 +3,24 @@ import { mockDocument } from '~test'
describe('Rotate command', () => { describe('Rotate command', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
tlstate.loadDocument(mockDocument)
tlstate.select('rect1') beforeEach(() => {
tlstate.loadDocument(mockDocument)
})
describe('when no shape is selected', () => {
it('does nothing', () => {
const initialState = tlstate.state
tlstate.rotate()
const currentState = tlstate.state
expect(currentState).toEqual(initialState)
})
})
it('does, undoes and redoes command', () => { it('does, undoes and redoes command', () => {
tlstate.select('rect1')
expect(tlstate.getShape('rect1').rotation).toBe(undefined) expect(tlstate.getShape('rect1').rotation).toBe(undefined)
tlstate.rotate() tlstate.rotate()

View file

@ -5,8 +5,22 @@ import { mockDocument } from '~test'
describe('Stretch command', () => { describe('Stretch command', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
it('does, undoes and redoes command', () => { beforeEach(() => {
tlstate.loadDocument(mockDocument) tlstate.loadDocument(mockDocument)
})
describe('when less than two shapes are selected', () => {
it('does nothing', () => {
tlstate.select('rect2')
const initialState = tlstate.state
tlstate.stretch(StretchType.Horizontal)
const currentState = tlstate.state
expect(currentState).toEqual(initialState)
})
})
it('does, undoes and redoes command', () => {
tlstate.select('rect1', 'rect2') tlstate.select('rect1', 'rect2')
tlstate.stretch(StretchType.Horizontal) tlstate.stretch(StretchType.Horizontal)
@ -31,7 +45,6 @@ describe('Stretch command', () => {
}) })
it('stretches horizontally', () => { it('stretches horizontally', () => {
tlstate.loadDocument(mockDocument)
tlstate.select('rect1', 'rect2') tlstate.select('rect1', 'rect2')
tlstate.stretch(StretchType.Horizontal) tlstate.stretch(StretchType.Horizontal)
@ -42,7 +55,6 @@ describe('Stretch command', () => {
}) })
it('stretches vertically', () => { it('stretches vertically', () => {
tlstate.loadDocument(mockDocument)
tlstate.select('rect1', 'rect2') tlstate.select('rect1', 'rect2')
tlstate.stretch(StretchType.Vertical) tlstate.stretch(StretchType.Vertical)

View file

@ -6,9 +6,32 @@ import { ArrowShape, Decoration, TLDrawShape } from '~types'
describe('Toggle decoration command', () => { describe('Toggle decoration command', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
beforeEach(() => {
tlstate.loadDocument(mockDocument)
})
describe('when no shape is selected', () => {
it('does nothing', () => {
const initialState = tlstate.state
tlstate.toggleDecoration('start')
const currentState = tlstate.state
expect(currentState).toEqual(initialState)
})
})
describe('when handle id is invalid', () => {
it('does nothing', () => {
const initialState = tlstate.state
tlstate.toggleDecoration('invalid')
const currentState = tlstate.state
expect(currentState).toEqual(initialState)
})
})
it('does, undoes and redoes command', () => { it('does, undoes and redoes command', () => {
tlstate tlstate
.loadDocument(mockDocument)
.create( .create(
TLDR.getShapeUtils({ type: 'arrow' } as TLDrawShape).create({ TLDR.getShapeUtils({ type: 'arrow' } as TLDrawShape).create({
id: 'arrow1', id: 'arrow1',

View file

@ -5,38 +5,57 @@ import { mockDocument } from '~test'
describe('Toggle command', () => { describe('Toggle command', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
it('does, undoes and redoes command', () => { beforeEach(() => {
tlstate.loadDocument(mockDocument) tlstate.loadDocument(mockDocument)
})
describe('when no shape is selected', () => {
it('does nothing', () => {
const initialState = tlstate.state
tlstate.toggleHidden()
const currentState = tlstate.state
expect(currentState).toEqual(initialState)
})
})
it('does, undoes and redoes command', () => {
tlstate.selectAll() tlstate.selectAll()
expect(tlstate.getShape('rect2').isAspectRatioLocked).toBe(undefined) expect(tlstate.getShape('rect2').isLocked).toBe(undefined)
tlstate.toggleAspectRatioLocked() tlstate.toggleLocked()
expect(tlstate.getShape('rect2').isAspectRatioLocked).toBe(true) expect(tlstate.getShape('rect2').isLocked).toBe(true)
tlstate.undo() tlstate.undo()
expect(tlstate.getShape('rect2').isAspectRatioLocked).toBe(undefined) expect(tlstate.getShape('rect2').isLocked).toBe(undefined)
tlstate.redo() tlstate.redo()
expect(tlstate.getShape('rect2').isAspectRatioLocked).toBe(true) expect(tlstate.getShape('rect2').isLocked).toBe(true)
}) })
it('toggles on before off when mixed values', () => { it('toggles on before off when mixed values', () => {
tlstate.loadDocument(mockDocument)
tlstate.select('rect2') tlstate.select('rect2')
expect(tlstate.getShape<RectangleShape>('rect1').isAspectRatioLocked).toBe(undefined) expect(tlstate.getShape<RectangleShape>('rect1').isAspectRatioLocked).toBe(undefined)
expect(tlstate.getShape<RectangleShape>('rect2').isAspectRatioLocked).toBe(undefined) expect(tlstate.getShape<RectangleShape>('rect2').isAspectRatioLocked).toBe(undefined)
tlstate.toggleAspectRatioLocked() tlstate.toggleAspectRatioLocked()
expect(tlstate.getShape<RectangleShape>('rect1').isAspectRatioLocked).toBe(undefined) expect(tlstate.getShape<RectangleShape>('rect1').isAspectRatioLocked).toBe(undefined)
expect(tlstate.getShape<RectangleShape>('rect2').isAspectRatioLocked).toBe(true) expect(tlstate.getShape<RectangleShape>('rect2').isAspectRatioLocked).toBe(true)
tlstate.selectAll() tlstate.selectAll()
tlstate.toggleAspectRatioLocked() tlstate.toggleAspectRatioLocked()
expect(tlstate.getShape<RectangleShape>('rect1').isAspectRatioLocked).toBe(true) expect(tlstate.getShape<RectangleShape>('rect1').isAspectRatioLocked).toBe(true)
expect(tlstate.getShape<RectangleShape>('rect1').isAspectRatioLocked).toBe(true) expect(tlstate.getShape<RectangleShape>('rect1').isAspectRatioLocked).toBe(true)
tlstate.toggleAspectRatioLocked() tlstate.toggleAspectRatioLocked()
expect(tlstate.getShape<RectangleShape>('rect1').isAspectRatioLocked).toBe(false) expect(tlstate.getShape<RectangleShape>('rect1').isAspectRatioLocked).toBe(false)
expect(tlstate.getShape<RectangleShape>('rect1').isAspectRatioLocked).toBe(false) expect(tlstate.getShape<RectangleShape>('rect1').isAspectRatioLocked).toBe(false)
}) })

View file

@ -6,8 +6,21 @@ import { ArrowShape, TLDrawShapeType } from '~types'
describe('Translate command', () => { describe('Translate command', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
it('does, undoes and redoes command', () => { beforeEach(() => {
tlstate.loadDocument(mockDocument) tlstate.loadDocument(mockDocument)
})
describe('when no shape is selected', () => {
it('does nothing', () => {
const initialState = tlstate.state
tlstate.nudge([1, 2])
const currentState = tlstate.state
expect(currentState).toEqual(initialState)
})
})
it('does, undoes and redoes command', () => {
tlstate.selectAll() tlstate.selectAll()
tlstate.nudge([1, 2]) tlstate.nudge([1, 2])
@ -23,7 +36,6 @@ describe('Translate command', () => {
}) })
it('major nudges', () => { it('major nudges', () => {
tlstate.loadDocument(mockDocument)
tlstate.selectAll() tlstate.selectAll()
tlstate.nudge([1, 2], true) tlstate.nudge([1, 2], true)
expect(tlstate.getShape('rect2').point).toEqual([110, 120]) expect(tlstate.getShape('rect2').point).toEqual([110, 120])

View file

@ -1,19 +1,34 @@
import { TLDrawState } from '~state' import { TLDrawState } from '~state'
import { mockDocument } from '~test' import { mockDocument } from '~test'
import { Utils } from '@tldraw/core'
const doc = Utils.deepClone(mockDocument)
describe('Update command', () => { describe('Update command', () => {
const tlstate = new TLDrawState() const tlstate = new TLDrawState()
beforeEach(() => {
tlstate.loadDocument(mockDocument)
})
describe('when no shape is selected', () => {
it('does nothing', () => {
const initialState = tlstate.state
tlstate.updateShapes()
const currentState = tlstate.state
expect(currentState).toEqual(initialState)
})
})
it('does, undoes and redoes command', () => { it('does, undoes and redoes command', () => {
tlstate.loadDocument(doc)
tlstate.updateShapes({ id: 'rect1', point: [100, 100] }) tlstate.updateShapes({ id: 'rect1', point: [100, 100] })
expect(tlstate.getShape('rect1').point).toStrictEqual([100, 100]) expect(tlstate.getShape('rect1').point).toStrictEqual([100, 100])
tlstate.undo() tlstate.undo()
expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0])
tlstate.redo() tlstate.redo()
expect(tlstate.getShape('rect1').point).toStrictEqual([100, 100]) expect(tlstate.getShape('rect1').point).toStrictEqual([100, 100])
}) })
}) })