diff --git a/packages/tldraw/src/state/command/align/align.command.spec.ts b/packages/tldraw/src/state/command/align/align.command.spec.ts index 2a07d6fd6..be4a8baeb 100644 --- a/packages/tldraw/src/state/command/align/align.command.spec.ts +++ b/packages/tldraw/src/state/command/align/align.command.spec.ts @@ -5,69 +5,73 @@ import { AlignType } from '~types' describe('Align command', () => { const tlstate = new TLDrawState() - it('does, undoes and redoes command', () => { - tlstate.loadDocument(mockDocument) - tlstate.selectAll() - tlstate.align(AlignType.Top) + describe('when less than two shapes are selected', () => { + it('does nothing', () => { + tlstate.loadDocument(mockDocument).select('rect2') + const initialState = tlstate.state + tlstate.align(AlignType.Top) + const currentState = tlstate.state - expect(tlstate.getShape('rect2').point).toEqual([100, 0]) - - tlstate.undo() - - expect(tlstate.getShape('rect2').point).toEqual([100, 100]) - - tlstate.redo() - - expect(tlstate.getShape('rect2').point).toEqual([100, 0]) + expect(currentState).toEqual(initialState) + }) }) - it('aligns top', () => { - tlstate.loadDocument(mockDocument) - tlstate.selectAll() - tlstate.align(AlignType.Top) + describe('when multiple shapes are selected', () => { + beforeEach(() => { + tlstate.loadDocument(mockDocument) + tlstate.selectAll() + }) - expect(tlstate.getShape('rect2').point).toEqual([100, 0]) - }) + it('does, undoes and redoes command', () => { + tlstate.align(AlignType.Top) - it('aligns right', () => { - tlstate.loadDocument(mockDocument) - tlstate.selectAll() - tlstate.align(AlignType.Right) + expect(tlstate.getShape('rect2').point).toEqual([100, 0]) - expect(tlstate.getShape('rect1').point).toEqual([100, 0]) - }) + tlstate.undo() - it('aligns bottom', () => { - tlstate.loadDocument(mockDocument) - tlstate.selectAll() - tlstate.align(AlignType.Bottom) + expect(tlstate.getShape('rect2').point).toEqual([100, 100]) - expect(tlstate.getShape('rect1').point).toEqual([0, 100]) - }) + tlstate.redo() - it('aligns left', () => { - tlstate.loadDocument(mockDocument) - tlstate.selectAll() - tlstate.align(AlignType.Left) + expect(tlstate.getShape('rect2').point).toEqual([100, 0]) + }) - expect(tlstate.getShape('rect2').point).toEqual([0, 100]) - }) + it('aligns top', () => { + tlstate.align(AlignType.Top) - it('aligns center horizontal', () => { - tlstate.loadDocument(mockDocument) - tlstate.selectAll() - tlstate.align(AlignType.CenterHorizontal) + expect(tlstate.getShape('rect2').point).toEqual([100, 0]) + }) - expect(tlstate.getShape('rect1').point).toEqual([50, 0]) - expect(tlstate.getShape('rect2').point).toEqual([50, 100]) - }) + it('aligns right', () => { + tlstate.align(AlignType.Right) - it('aligns center vertical', () => { - tlstate.loadDocument(mockDocument) - tlstate.selectAll() - tlstate.align(AlignType.CenterVertical) + expect(tlstate.getShape('rect1').point).toEqual([100, 0]) + }) - expect(tlstate.getShape('rect1').point).toEqual([0, 50]) - expect(tlstate.getShape('rect2').point).toEqual([100, 50]) + it('aligns bottom', () => { + 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]) + }) }) }) diff --git a/packages/tldraw/src/state/command/create/create.command.spec.ts b/packages/tldraw/src/state/command/create/create.command.spec.ts index 39adf2b77..3dde97ce0 100644 --- a/packages/tldraw/src/state/command/create/create.command.spec.ts +++ b/packages/tldraw/src/state/command/create/create.command.spec.ts @@ -4,8 +4,22 @@ import { mockDocument } from '~test' describe('Create command', () => { const tlstate = new TLDrawState() - it('does, undoes and redoes command', () => { + beforeEach(() => { 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' } tlstate.create(shape) diff --git a/packages/tldraw/src/state/command/delete-page/delete-page.command.spec.ts b/packages/tldraw/src/state/command/delete-page/delete-page.command.spec.ts index 193d85c34..1d358bccb 100644 --- a/packages/tldraw/src/state/command/delete-page/delete-page.command.spec.ts +++ b/packages/tldraw/src/state/command/delete-page/delete-page.command.spec.ts @@ -4,9 +4,22 @@ import { mockDocument } from '~test' describe('Delete page', () => { const tlstate = new TLDrawState() - it('does, undoes and redoes command', () => { + beforeEach(() => { 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 tlstate.createPage() diff --git a/packages/tldraw/src/state/command/delete/delete.command.spec.ts b/packages/tldraw/src/state/command/delete/delete.command.spec.ts index 0051d0393..78832b1ee 100644 --- a/packages/tldraw/src/state/command/delete/delete.command.spec.ts +++ b/packages/tldraw/src/state/command/delete/delete.command.spec.ts @@ -6,8 +6,21 @@ import type { TLDrawShape } from '~types' describe('Delete command', () => { const tlstate = new TLDrawState() - it('does, undoes and redoes command', () => { + beforeEach(() => { 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.delete() @@ -26,7 +39,6 @@ describe('Delete command', () => { }) it('deletes two shapes', () => { - tlstate.loadDocument(mockDocument) tlstate.selectAll() tlstate.delete() @@ -45,8 +57,6 @@ describe('Delete command', () => { }) it('deletes bound shapes', () => { - tlstate.loadDocument(mockDocument) - expect(Object.values(tlstate.page.bindings)[0]).toBe(undefined) tlstate @@ -86,26 +96,18 @@ describe('Delete command', () => { 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', () => { - tlstate - .loadDocument(mockDocument) - .group(['rect1', 'rect2', 'rect3'], 'newGroup') - .select('rect1') - .delete() + tlstate.group(['rect1', 'rect2', 'rect3'], 'newGroup').select('rect1').delete() expect(tlstate.getShape('rect1')).toBeUndefined() expect(tlstate.getShape('newGroup').children).toStrictEqual(['rect2', 'rect3']) }) }) - describe('when deleting shapes with children', () => { - it('also deletes the children', () => { - tlstate - .loadDocument(mockDocument) - .group(['rect1', 'rect2'], 'newGroup') - .select('newGroup') - .delete() + describe('when deleting a group', () => { + it('deletes all grouped shapes', () => { + tlstate.group(['rect1', 'rect2'], 'newGroup').select('newGroup').delete() expect(tlstate.getShape('rect1')).toBeUndefined() expect(tlstate.getShape('rect2')).toBeUndefined() diff --git a/packages/tldraw/src/state/command/distribute/distribute.command.spec.ts b/packages/tldraw/src/state/command/distribute/distribute.command.spec.ts index 15adad42c..1049a1235 100644 --- a/packages/tldraw/src/state/command/distribute/distribute.command.spec.ts +++ b/packages/tldraw/src/state/command/distribute/distribute.command.spec.ts @@ -4,11 +4,26 @@ import { DistributeType } from '~types' describe('Distribute command', () => { 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', () => { + tlstate.selectAll() tlstate.distribute(DistributeType.Horizontal) + expect(tlstate.getShape('rect3').point).toEqual([50, 20]) tlstate.undo() expect(tlstate.getShape('rect3').point).toEqual([20, 20]) @@ -17,9 +32,9 @@ describe('Distribute command', () => { }) it('distributes vertically', () => { - tlstate.loadDocument(mockDocument) tlstate.selectAll() tlstate.distribute(DistributeType.Vertical) + expect(tlstate.getShape('rect3').point).toEqual([20, 50]) }) }) diff --git a/packages/tldraw/src/state/command/duplicate/duplicate.command.spec.ts b/packages/tldraw/src/state/command/duplicate/duplicate.command.spec.ts index 12f2b1a1c..1f2d1b071 100644 --- a/packages/tldraw/src/state/command/duplicate/duplicate.command.spec.ts +++ b/packages/tldraw/src/state/command/duplicate/duplicate.command.spec.ts @@ -1,14 +1,28 @@ /* eslint-disable @typescript-eslint/no-non-null-assertion */ import { TLDrawState } from '~state' import { mockDocument } from '~test' -import { ArrowShape, GroupShape, TLDrawShapeType } from '~types' +import { ArrowShape, TLDrawShapeType } from '~types' describe('Duplicate command', () => { 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', () => { + tlstate.select('rect1') + expect(Object.keys(tlstate.getPage().shapes).length).toBe(3) tlstate.duplicate() @@ -121,7 +135,6 @@ describe('Duplicate command', () => { }) it('duplicates groups', () => { - tlstate.loadDocument(mockDocument) tlstate.group(['rect1', 'rect2'], 'newGroup').select('newGroup') const beforeShapeIds = Object.keys(tlstate.page.shapes) @@ -140,7 +153,6 @@ describe('Duplicate command', () => { }) it('duplicates grouped shapes', () => { - tlstate.loadDocument(mockDocument) tlstate.group(['rect1', 'rect2'], 'newGroup').select('rect1') const beforeShapeIds = Object.keys(tlstate.page.shapes) diff --git a/packages/tldraw/src/state/command/flip/flip.command.spec.ts b/packages/tldraw/src/state/command/flip/flip.command.spec.ts index 0ceba4b9c..248dca5af 100644 --- a/packages/tldraw/src/state/command/flip/flip.command.spec.ts +++ b/packages/tldraw/src/state/command/flip/flip.command.spec.ts @@ -5,8 +5,21 @@ import type { RectangleShape } from '~types' describe('Flip command', () => { const tlstate = new TLDrawState() - it('does, undoes and redoes command', () => { + beforeEach(() => { 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.flipHorizontal() @@ -22,7 +35,6 @@ describe('Flip command', () => { }) it('flips horizontally', () => { - tlstate.loadDocument(mockDocument) tlstate.select('rect1', 'rect2') tlstate.flipHorizontal() @@ -30,7 +42,6 @@ describe('Flip command', () => { }) it('flips vertically', () => { - tlstate.loadDocument(mockDocument) tlstate.select('rect1', 'rect2') tlstate.flipVertical() diff --git a/packages/tldraw/src/state/command/group/group.command.spec.ts b/packages/tldraw/src/state/command/group/group.command.spec.ts index 36be3f23a..f3a246390 100644 --- a/packages/tldraw/src/state/command/group/group.command.spec.ts +++ b/packages/tldraw/src/state/command/group/group.command.spec.ts @@ -6,8 +6,11 @@ import { GroupShape, TLDrawShape, TLDrawShapeType } from '~types' describe('Group command', () => { const tlstate = new TLDrawState() - it('does, undoes and redoes command', () => { + beforeEach(() => { tlstate.loadDocument(mockDocument) + }) + + it('does, undoes and redoes command', () => { tlstate.group(['rect1', 'rect2'], 'newGroup') expect(tlstate.getShape('newGroup')).toBeTruthy() @@ -23,7 +26,6 @@ describe('Group command', () => { describe('when less than two shapes are selected', () => { it('does nothing', () => { - tlstate.loadDocument(mockDocument) tlstate.deselectAll() // @ts-ignore @@ -49,8 +51,6 @@ describe('Group command', () => { */ it('creates a group with the correct props', () => { - tlstate.loadDocument(mockDocument) - tlstate.updateShapes( { id: 'rect1', @@ -74,8 +74,6 @@ describe('Group command', () => { }) it('reparents the grouped shapes', () => { - tlstate.loadDocument(mockDocument) - tlstate.updateShapes( { 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. 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 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( @@ -206,7 +205,7 @@ describe('Group command', () => { expect(tlstate.getShape('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 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 groups, then the selected groups should be destroyed and a new diff --git a/packages/tldraw/src/state/command/move-to-page/move-to-page.command.spec.ts b/packages/tldraw/src/state/command/move-to-page/move-to-page.command.spec.ts index 05dd9d995..2d90a3720 100644 --- a/packages/tldraw/src/state/command/move-to-page/move-to-page.command.spec.ts +++ b/packages/tldraw/src/state/command/move-to-page/move-to-page.command.spec.ts @@ -5,9 +5,23 @@ import { ArrowShape, TLDrawShapeType } from '~types' describe('Move to page command', () => { 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 - 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 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 @@ -15,12 +29,7 @@ describe('Move to page command', () => { */ it('does, undoes and redoes command', () => { - tlstate - .loadDocument(mockDocument) - .createPage('page2') - .changePage('page1') - .select('rect1', 'rect2') - .moveToPage('page2') + tlstate.select('rect1', 'rect2').moveToPage('page2') expect(tlstate.currentPageId).toBe('page2') expect(tlstate.getShape('rect1', 'page1')).toBeUndefined() @@ -51,7 +60,6 @@ describe('Move to page command', () => { describe('when moving shapes with bindings', () => { it('deletes bindings when only the bound-to shape is moved', () => { tlstate - .loadDocument(mockDocument) .selectAll() .delete() .createShapes( @@ -66,7 +74,7 @@ describe('Move to page command', () => { const bindingId = tlstate.bindings[0].id expect(tlstate.getShape('arrow1').handles.start.bindingId).toBe(bindingId) - tlstate.createPage('page2').changePage('page1').select('target1').moveToPage('page2') + tlstate.select('target1').moveToPage('page2') expect( tlstate.getShape('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', () => { tlstate - .loadDocument(mockDocument) .selectAll() .delete() .createShapes( @@ -108,7 +115,7 @@ describe('Move to page command', () => { const bindingId = tlstate.bindings[0].id expect(tlstate.getShape('arrow1').handles.start.bindingId).toBe(bindingId) - tlstate.createPage('page2').changePage('page1').select('arrow1').moveToPage('page2') + tlstate.select('arrow1').moveToPage('page2') expect( tlstate.getShape('arrow1', 'page2').handles.start.bindingId @@ -135,7 +142,6 @@ describe('Move to page command', () => { it('moves bindings when both shapes are moved', () => { tlstate - .loadDocument(mockDocument) .selectAll() .delete() .createShapes( @@ -150,11 +156,7 @@ describe('Move to page command', () => { const bindingId = tlstate.bindings[0].id expect(tlstate.getShape('arrow1').handles.start.bindingId).toBe(bindingId) - tlstate - .createPage('page2') - .changePage('page1') - .select('arrow1', 'target1') - .moveToPage('page2') + tlstate.select('arrow1', 'target1').moveToPage('page2') expect(tlstate.getShape('arrow1', 'page2').handles.start.bindingId).toBe( bindingId @@ -182,12 +184,7 @@ describe('Move to page command', () => { describe('when moving grouped shapes', () => { it('moves groups and their children', () => { - tlstate - .loadDocument(mockDocument) - .createPage('page2') - .changePage('page1') - .group(['rect1', 'rect2'], 'groupA') - .moveToPage('page2') + tlstate.group(['rect1', 'rect2'], 'groupA').moveToPage('page2') expect(tlstate.getShape('rect1', 'page1')).toBeUndefined() expect(tlstate.getShape('rect2', 'page1')).toBeUndefined() @@ -218,18 +215,10 @@ describe('Move to page command', () => { 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', () => { - tlstate - .loadDocument(mockDocument) - .createPage('page2') - .changePage('page1') - .group(['rect1', 'rect2', 'rect3'], 'groupA') - .select('rect1') - .moveToPage('page2') + tlstate.group(['rect1', 'rect2', 'rect3'], 'groupA').select('rect1').moveToPage('page2') expect(tlstate.getShape('rect1', 'page1')).toBeUndefined() expect(tlstate.getShape('rect1', 'page2')).toBeDefined() diff --git a/packages/tldraw/src/state/command/move/move.command.spec.ts b/packages/tldraw/src/state/command/move/move.command.spec.ts index 9afff6b97..5ce57d841 100644 --- a/packages/tldraw/src/state/command/move/move.command.spec.ts +++ b/packages/tldraw/src/state/command/move/move.command.spec.ts @@ -42,8 +42,22 @@ function getSortedShapeIds(data: Data) { describe('Move command', () => { const tlstate = new TLDrawState() - it('does, undoes and redoes command', () => { + beforeEach(() => { 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.moveToBack() expect(getSortedShapeIds(tlstate.state)).toBe('bacd') @@ -55,21 +69,18 @@ describe('Move command', () => { describe('to back', () => { it('moves a shape to back', () => { - tlstate.loadDocument(doc) tlstate.select('b') tlstate.moveToBack() expect(getSortedShapeIds(tlstate.state)).toBe('bacd') }) it('moves two adjacent siblings to back', () => { - tlstate.loadDocument(doc) tlstate.select('b', 'c') tlstate.moveToBack() expect(getSortedShapeIds(tlstate.state)).toBe('bcad') }) it('moves two non-adjacent siblings to back', () => { - tlstate.loadDocument(doc) tlstate.select('b', 'd') tlstate.moveToBack() expect(getSortedShapeIds(tlstate.state)).toBe('bdac') @@ -78,35 +89,30 @@ describe('Move command', () => { describe('backward', () => { it('moves a shape backward', () => { - tlstate.loadDocument(doc) tlstate.select('c') tlstate.moveBackward() expect(getSortedShapeIds(tlstate.state)).toBe('acbd') }) it('moves a shape at first index backward', () => { - tlstate.loadDocument(doc) tlstate.select('a') tlstate.moveBackward() expect(getSortedShapeIds(tlstate.state)).toBe('abcd') }) it('moves two adjacent siblings backward', () => { - tlstate.loadDocument(doc) tlstate.select('c', 'd') tlstate.moveBackward() expect(getSortedShapeIds(tlstate.state)).toBe('acdb') }) it('moves two non-adjacent siblings backward', () => { - tlstate.loadDocument(doc) tlstate.select('b', 'd') tlstate.moveBackward() expect(getSortedShapeIds(tlstate.state)).toBe('badc') }) it('moves two adjacent siblings backward at zero index', () => { - tlstate.loadDocument(doc) tlstate.select('a', 'b') tlstate.moveBackward() expect(getSortedShapeIds(tlstate.state)).toBe('abcd') @@ -115,14 +121,12 @@ describe('Move command', () => { describe('forward', () => { it('moves a shape forward', () => { - tlstate.loadDocument(doc) tlstate.select('c') tlstate.moveForward() expect(getSortedShapeIds(tlstate.state)).toBe('abdc') }) it('moves a shape forward at the top index', () => { - tlstate.loadDocument(doc) tlstate.select('b') tlstate.moveForward() tlstate.moveForward() @@ -131,21 +135,18 @@ describe('Move command', () => { }) it('moves two adjacent siblings forward', () => { - tlstate.loadDocument(doc) tlstate.select('a', 'b') tlstate.moveForward() expect(getSortedShapeIds(tlstate.state)).toBe('cabd') }) it('moves two non-adjacent siblings forward', () => { - tlstate.loadDocument(doc) tlstate.select('a', 'c') tlstate.moveForward() expect(getSortedShapeIds(tlstate.state)).toBe('badc') }) it('moves two adjacent siblings forward at top index', () => { - tlstate.loadDocument(doc) tlstate.select('c', 'd') tlstate.moveForward() expect(getSortedShapeIds(tlstate.state)).toBe('abcd') @@ -154,28 +155,24 @@ describe('Move command', () => { describe('to front', () => { it('moves a shape to front', () => { - tlstate.loadDocument(doc) tlstate.select('b') tlstate.moveToFront() expect(getSortedShapeIds(tlstate.state)).toBe('acdb') }) it('moves two adjacent siblings to front', () => { - tlstate.loadDocument(doc) tlstate.select('a', 'b') tlstate.moveToFront() expect(getSortedShapeIds(tlstate.state)).toBe('cdab') }) it('moves two non-adjacent siblings to front', () => { - tlstate.loadDocument(doc) tlstate.select('a', 'c') tlstate.moveToFront() expect(getSortedShapeIds(tlstate.state)).toBe('bdac') }) it('moves siblings already at front to front', () => { - tlstate.loadDocument(doc) tlstate.select('c', 'd') tlstate.moveToFront() expect(getSortedShapeIds(tlstate.state)).toBe('abcd') diff --git a/packages/tldraw/src/state/command/rotate/rotate.command.spec.ts b/packages/tldraw/src/state/command/rotate/rotate.command.spec.ts index 911cabd87..39295f8ac 100644 --- a/packages/tldraw/src/state/command/rotate/rotate.command.spec.ts +++ b/packages/tldraw/src/state/command/rotate/rotate.command.spec.ts @@ -3,10 +3,24 @@ import { mockDocument } from '~test' describe('Rotate command', () => { 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', () => { + tlstate.select('rect1') + expect(tlstate.getShape('rect1').rotation).toBe(undefined) tlstate.rotate() diff --git a/packages/tldraw/src/state/command/stretch/stretch.command.spec.ts b/packages/tldraw/src/state/command/stretch/stretch.command.spec.ts index 899b58a19..bb055b655 100644 --- a/packages/tldraw/src/state/command/stretch/stretch.command.spec.ts +++ b/packages/tldraw/src/state/command/stretch/stretch.command.spec.ts @@ -5,8 +5,22 @@ import { mockDocument } from '~test' describe('Stretch command', () => { const tlstate = new TLDrawState() - it('does, undoes and redoes command', () => { + beforeEach(() => { 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.stretch(StretchType.Horizontal) @@ -31,7 +45,6 @@ describe('Stretch command', () => { }) it('stretches horizontally', () => { - tlstate.loadDocument(mockDocument) tlstate.select('rect1', 'rect2') tlstate.stretch(StretchType.Horizontal) @@ -42,7 +55,6 @@ describe('Stretch command', () => { }) it('stretches vertically', () => { - tlstate.loadDocument(mockDocument) tlstate.select('rect1', 'rect2') tlstate.stretch(StretchType.Vertical) diff --git a/packages/tldraw/src/state/command/toggle-decoration/toggle-decoration.command.spec.ts b/packages/tldraw/src/state/command/toggle-decoration/toggle-decoration.command.spec.ts index 0f2301595..693b568e7 100644 --- a/packages/tldraw/src/state/command/toggle-decoration/toggle-decoration.command.spec.ts +++ b/packages/tldraw/src/state/command/toggle-decoration/toggle-decoration.command.spec.ts @@ -6,9 +6,32 @@ import { ArrowShape, Decoration, TLDrawShape } from '~types' describe('Toggle decoration command', () => { 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', () => { tlstate - .loadDocument(mockDocument) .create( TLDR.getShapeUtils({ type: 'arrow' } as TLDrawShape).create({ id: 'arrow1', diff --git a/packages/tldraw/src/state/command/toggle/toggle.command.spec.ts b/packages/tldraw/src/state/command/toggle/toggle.command.spec.ts index c545bbd84..1a914631d 100644 --- a/packages/tldraw/src/state/command/toggle/toggle.command.spec.ts +++ b/packages/tldraw/src/state/command/toggle/toggle.command.spec.ts @@ -5,38 +5,57 @@ import { mockDocument } from '~test' describe('Toggle command', () => { const tlstate = new TLDrawState() - it('does, undoes and redoes command', () => { + beforeEach(() => { 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() - 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() - expect(tlstate.getShape('rect2').isAspectRatioLocked).toBe(undefined) + expect(tlstate.getShape('rect2').isLocked).toBe(undefined) tlstate.redo() - expect(tlstate.getShape('rect2').isAspectRatioLocked).toBe(true) + expect(tlstate.getShape('rect2').isLocked).toBe(true) }) it('toggles on before off when mixed values', () => { - tlstate.loadDocument(mockDocument) tlstate.select('rect2') + expect(tlstate.getShape('rect1').isAspectRatioLocked).toBe(undefined) expect(tlstate.getShape('rect2').isAspectRatioLocked).toBe(undefined) + tlstate.toggleAspectRatioLocked() + expect(tlstate.getShape('rect1').isAspectRatioLocked).toBe(undefined) expect(tlstate.getShape('rect2').isAspectRatioLocked).toBe(true) + tlstate.selectAll() tlstate.toggleAspectRatioLocked() + expect(tlstate.getShape('rect1').isAspectRatioLocked).toBe(true) expect(tlstate.getShape('rect1').isAspectRatioLocked).toBe(true) + tlstate.toggleAspectRatioLocked() + expect(tlstate.getShape('rect1').isAspectRatioLocked).toBe(false) expect(tlstate.getShape('rect1').isAspectRatioLocked).toBe(false) }) diff --git a/packages/tldraw/src/state/command/translate/translate.command.spec.ts b/packages/tldraw/src/state/command/translate/translate.command.spec.ts index 4cac8f46d..43b2e4500 100644 --- a/packages/tldraw/src/state/command/translate/translate.command.spec.ts +++ b/packages/tldraw/src/state/command/translate/translate.command.spec.ts @@ -6,8 +6,21 @@ import { ArrowShape, TLDrawShapeType } from '~types' describe('Translate command', () => { const tlstate = new TLDrawState() - it('does, undoes and redoes command', () => { + beforeEach(() => { 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.nudge([1, 2]) @@ -23,7 +36,6 @@ describe('Translate command', () => { }) it('major nudges', () => { - tlstate.loadDocument(mockDocument) tlstate.selectAll() tlstate.nudge([1, 2], true) expect(tlstate.getShape('rect2').point).toEqual([110, 120]) diff --git a/packages/tldraw/src/state/command/update/update.command.spec.ts b/packages/tldraw/src/state/command/update/update.command.spec.ts index 3a8efb687..ef52dda3b 100644 --- a/packages/tldraw/src/state/command/update/update.command.spec.ts +++ b/packages/tldraw/src/state/command/update/update.command.spec.ts @@ -1,19 +1,34 @@ import { TLDrawState } from '~state' import { mockDocument } from '~test' -import { Utils } from '@tldraw/core' - -const doc = Utils.deepClone(mockDocument) describe('Update command', () => { 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', () => { - tlstate.loadDocument(doc) tlstate.updateShapes({ id: 'rect1', point: [100, 100] }) + expect(tlstate.getShape('rect1').point).toStrictEqual([100, 100]) + tlstate.undo() + expect(tlstate.getShape('rect1').point).toStrictEqual([0, 0]) + tlstate.redo() + expect(tlstate.getShape('rect1').point).toStrictEqual([100, 100]) }) })