Переглянути джерело

Merge pull request #35 from tldraw/21-toggle-command-tests

Adds test for the toggle command, renames files to include test
main
Steve Ruiz 4 роки тому
джерело
коміт
8b1072c3c3
Аккаунт користувача з таким Email не знайдено

__tests__/commands/change-page.ts → __tests__/commands/change-page.test.ts Переглянути файл


__tests__/commands/delete-page.ts → __tests__/commands/delete-page.test.ts Переглянути файл


__tests__/commands/distribute.ts → __tests__/commands/distribute.test.ts Переглянути файл


__tests__/commands/draw.ts → __tests__/commands/draw.test.ts Переглянути файл


__tests__/commands/duplicate.ts → __tests__/commands/duplicate.test.ts Переглянути файл


__tests__/commands/edit.ts → __tests__/commands/edit.test.ts Переглянути файл


__tests__/commands/generate.ts → __tests__/commands/generate.test.ts Переглянути файл


__tests__/commands/group.ts → __tests__/commands/group.test.ts Переглянути файл


__tests__/commands/move-to-page.ts → __tests__/commands/move-to-page.test.ts Переглянути файл


+ 237
- 0
__tests__/commands/toggle.test.ts Переглянути файл

@@ -0,0 +1,237 @@
1
+import { ShapeType } from 'types'
2
+import TestState from '../test-utils'
3
+
4
+describe('toggle command', () => {
5
+  const tt = new TestState()
6
+  tt.resetDocumentState()
7
+    .createShape(
8
+      {
9
+        type: ShapeType.Rectangle,
10
+        point: [0, 0],
11
+        size: [100, 100],
12
+        childIndex: 1,
13
+        isLocked: false,
14
+        isHidden: false,
15
+        isAspectRatioLocked: false,
16
+      },
17
+      'rect1'
18
+    )
19
+    .createShape(
20
+      {
21
+        type: ShapeType.Rectangle,
22
+        point: [400, 0],
23
+        size: [100, 100],
24
+        childIndex: 2,
25
+        isHidden: false,
26
+        isLocked: false,
27
+        isAspectRatioLocked: false,
28
+      },
29
+      'rect2'
30
+    )
31
+    .createShape(
32
+      {
33
+        type: ShapeType.Rectangle,
34
+        point: [800, 0],
35
+        size: [100, 100],
36
+        childIndex: 3,
37
+        isHidden: true,
38
+        isLocked: true,
39
+        isAspectRatioLocked: true,
40
+      },
41
+      'rect3'
42
+    )
43
+    .createShape(
44
+      {
45
+        type: ShapeType.Rectangle,
46
+        point: [1000, 0],
47
+        size: [100, 100],
48
+        childIndex: 4,
49
+        isHidden: true,
50
+        isLocked: true,
51
+        isAspectRatioLocked: true,
52
+      },
53
+      'rect4'
54
+    )
55
+    .save()
56
+
57
+  describe('toggles properties on single shape', () => {
58
+    it('does command', () => {
59
+      tt.restore().clickShape('rect1')
60
+
61
+      tt.send('TOGGLED_SHAPE_LOCK')
62
+
63
+      expect(tt.getShape('rect1').isLocked).toBe(true)
64
+
65
+      tt.send('TOGGLED_SHAPE_LOCK')
66
+
67
+      expect(tt.getShape('rect1').isLocked).toBe(false)
68
+    })
69
+
70
+    it('undoes and redoes command', () => {
71
+      // Restore the saved data state, with the initial selection
72
+      tt.restore().clickShape('rect1')
73
+
74
+      tt.send('TOGGLED_SHAPE_LOCK')
75
+
76
+      tt.send('UNDO')
77
+
78
+      expect(tt.getShape('rect1').isLocked).toBe(false)
79
+
80
+      tt.send('REDO')
81
+
82
+      expect(tt.getShape('rect1').isLocked).toBe(true)
83
+    })
84
+  })
85
+
86
+  describe('toggles properties on shapes with matching properties, starting from false', () => {
87
+    it('does command', () => {
88
+      // Restore the saved data state, with the initial selection
89
+      tt.restore().clickShape('rect1').clickShape('rect2', { shiftKey: true })
90
+
91
+      tt.send('TOGGLED_SHAPE_LOCK')
92
+
93
+      expect(tt.getShape('rect1').isLocked).toBe(true)
94
+      expect(tt.getShape('rect2').isLocked).toBe(true)
95
+
96
+      tt.send('TOGGLED_SHAPE_LOCK')
97
+
98
+      expect(tt.getShape('rect1').isLocked).toBe(false)
99
+      expect(tt.getShape('rect2').isLocked).toBe(false)
100
+
101
+      tt.send('TOGGLED_SHAPE_LOCK')
102
+
103
+      expect(tt.getShape('rect1').isLocked).toBe(true)
104
+      expect(tt.getShape('rect2').isLocked).toBe(true)
105
+    })
106
+
107
+    it('undoes and redoes command', () => {
108
+      // Restore the saved data state, with the initial selection
109
+      tt.restore().clickShape('rect1').clickShape('rect2', { shiftKey: true })
110
+
111
+      tt.send('TOGGLED_SHAPE_LOCK').undo()
112
+
113
+      expect(tt.getShape('rect1').isLocked).toBe(false)
114
+      expect(tt.getShape('rect2').isLocked).toBe(false)
115
+
116
+      tt.redo()
117
+
118
+      expect(tt.getShape('rect1').isLocked).toBe(true)
119
+      expect(tt.getShape('rect2').isLocked).toBe(true)
120
+    })
121
+  })
122
+
123
+  describe('toggles properties on shapes with matching properties, starting from true', () => {
124
+    it('does command', () => {
125
+      // Restore the saved data state, with the initial selection
126
+      tt.restore().clickShape('rect3').clickShape('rect4', { shiftKey: true })
127
+
128
+      tt.send('TOGGLED_SHAPE_LOCK')
129
+
130
+      expect(tt.getShape('rect3').isLocked).toBe(false)
131
+      expect(tt.getShape('rect4').isLocked).toBe(false)
132
+
133
+      tt.send('TOGGLED_SHAPE_LOCK')
134
+
135
+      expect(tt.getShape('rect3').isLocked).toBe(true)
136
+      expect(tt.getShape('rect4').isLocked).toBe(true)
137
+    })
138
+
139
+    it('undoes and redoes command', () => {
140
+      // Restore the saved data state, with the initial selection
141
+      tt.restore().clickShape('rect3').clickShape('rect4', { shiftKey: true })
142
+
143
+      tt.send('TOGGLED_SHAPE_LOCK').undo()
144
+
145
+      expect(tt.getShape('rect3').isLocked).toBe(true)
146
+      expect(tt.getShape('rect4').isLocked).toBe(true)
147
+
148
+      tt.redo()
149
+
150
+      expect(tt.getShape('rect3').isLocked).toBe(false)
151
+      expect(tt.getShape('rect4').isLocked).toBe(false)
152
+    })
153
+  })
154
+
155
+  describe('toggles properties on shapes with different properties', () => {
156
+    it('does command', () => {
157
+      // Restore the saved data state, with the initial selection
158
+      tt.restore()
159
+        .clickShape('rect1')
160
+        .clickShape('rect2', { shiftKey: true })
161
+        .clickShape('rect3', { shiftKey: true })
162
+
163
+      tt.send('TOGGLED_SHAPE_LOCK')
164
+
165
+      expect(tt.getShape('rect1').isLocked).toBe(true)
166
+      expect(tt.getShape('rect2').isLocked).toBe(true)
167
+      expect(tt.getShape('rect3').isLocked).toBe(true)
168
+
169
+      tt.send('TOGGLED_SHAPE_LOCK')
170
+
171
+      expect(tt.getShape('rect1').isLocked).toBe(false)
172
+      expect(tt.getShape('rect2').isLocked).toBe(false)
173
+      expect(tt.getShape('rect3').isLocked).toBe(false)
174
+    })
175
+
176
+    it('undoes and redoes command', () => {
177
+      tt.restore()
178
+        .clickShape('rect1')
179
+        .clickShape('rect2', { shiftKey: true })
180
+        .clickShape('rect3', { shiftKey: true })
181
+
182
+      tt.send('TOGGLED_SHAPE_LOCK').undo()
183
+
184
+      expect(tt.getShape('rect1').isLocked).toBe(false)
185
+      expect(tt.getShape('rect2').isLocked).toBe(false)
186
+      expect(tt.getShape('rect3').isLocked).toBe(true)
187
+
188
+      tt.redo()
189
+
190
+      expect(tt.getShape('rect1').isLocked).toBe(true)
191
+      expect(tt.getShape('rect2').isLocked).toBe(true)
192
+      expect(tt.getShape('rect3').isLocked).toBe(true)
193
+    })
194
+  })
195
+
196
+  describe('catch all for other toggle props', () => {
197
+    const eventPropertyPairs = {
198
+      TOGGLED_SHAPE_LOCK: 'isLocked',
199
+      TOGGLED_SHAPE_HIDE: 'isHidden',
200
+      TOGGLED_SHAPE_ASPECT_LOCK: 'isAspectRatioLocked',
201
+    }
202
+
203
+    it('toggles all event property pairs', () => {
204
+      Object.entries(eventPropertyPairs).forEach(([eventName, propName]) => {
205
+        // Restore the saved data state, with the initial selection
206
+        tt.restore()
207
+          .clickShape('rect1')
208
+          .clickShape('rect2', { shiftKey: true })
209
+          .clickShape('rect3', { shiftKey: true })
210
+
211
+        tt.send(eventName)
212
+
213
+        expect(tt.getShape('rect1')[propName]).toBe(true)
214
+        expect(tt.getShape('rect2')[propName]).toBe(true)
215
+        expect(tt.getShape('rect3')[propName]).toBe(true)
216
+
217
+        tt.undo()
218
+
219
+        expect(tt.getShape('rect1')[propName]).toBe(false)
220
+        expect(tt.getShape('rect2')[propName]).toBe(false)
221
+        expect(tt.getShape('rect3')[propName]).toBe(true)
222
+
223
+        tt.redo()
224
+
225
+        expect(tt.getShape('rect1')[propName]).toBe(true)
226
+        expect(tt.getShape('rect2')[propName]).toBe(true)
227
+        expect(tt.getShape('rect3')[propName]).toBe(true)
228
+
229
+        tt.send(eventName)
230
+
231
+        expect(tt.getShape('rect1')[propName]).toBe(false)
232
+        expect(tt.getShape('rect2')[propName]).toBe(false)
233
+        expect(tt.getShape('rect3')[propName]).toBe(false)
234
+      })
235
+    })
236
+  })
237
+})

+ 27
- 12
__tests__/test-utils.ts Переглянути файл

@@ -228,15 +228,18 @@ class TestState {
228 228
    *```
229 229
    */
230 230
   clickShape(id: string, options: PointerOptions = {}): TestState {
231
-    this.state.send(
232
-      'POINTED_SHAPE',
233
-      inputs.pointerDown(TestState.point(options), id)
234
-    )
231
+    const shape = tld.getShape(this.data, id)
232
+    const [x, y] = shape ? vec.add(shape.point, [1, 1]) : [0, 0]
235 233
 
236
-    this.state.send(
237
-      'STOPPED_POINTING',
238
-      inputs.pointerUp(TestState.point(options), id)
239
-    )
234
+    this.state
235
+      .send(
236
+        'POINTED_SHAPE',
237
+        inputs.pointerDown(TestState.point({ x, y, ...options }), id)
238
+      )
239
+      .send(
240
+        'STOPPED_POINTING',
241
+        inputs.pointerUp(TestState.point({ x, y, ...options }), id)
242
+      )
240 243
 
241 244
     return this
242 245
   }
@@ -251,9 +254,12 @@ class TestState {
251 254
    *```
252 255
    */
253 256
   startClick(id: string, options: PointerOptions = {}): TestState {
257
+    const shape = tld.getShape(this.data, id)
258
+    const [x, y] = shape ? vec.add(shape.point, [1, 1]) : [0, 0]
259
+
254 260
     this.state.send(
255 261
       'POINTED_SHAPE',
256
-      inputs.pointerDown(TestState.point(options), id)
262
+      inputs.pointerDown(TestState.point({ x, y, ...options }), id)
257 263
     )
258 264
 
259 265
     return this
@@ -269,9 +275,12 @@ class TestState {
269 275
    *```
270 276
    */
271 277
   stopClick(id: string, options: PointerOptions = {}): TestState {
278
+    const shape = tld.getShape(this.data, id)
279
+    const [x, y] = shape ? vec.add(shape.point, [1, 1]) : [0, 0]
280
+
272 281
     this.state.send(
273 282
       'STOPPED_POINTING',
274
-      inputs.pointerUp(TestState.point(options), id)
283
+      inputs.pointerUp(TestState.point({ x, y, ...options }), id)
275 284
     )
276 285
 
277 286
     return this
@@ -287,12 +296,18 @@ class TestState {
287 296
    *```
288 297
    */
289 298
   doubleClickShape(id: string, options: PointerOptions = {}): TestState {
299
+    const shape = tld.getShape(this.data, id)
300
+    const [x, y] = shape ? vec.add(shape.point, [1, 1]) : [0, 0]
301
+
290 302
     this.state
291 303
       .send(
292 304
         'DOUBLE_POINTED_SHAPE',
293
-        inputs.pointerDown(TestState.point(options), id)
305
+        inputs.pointerDown(TestState.point({ x, y, ...options }), id)
306
+      )
307
+      .send(
308
+        'STOPPED_POINTING',
309
+        inputs.pointerUp(TestState.point({ x, y, ...options }), id)
294 310
       )
295
-      .send('STOPPED_POINTING', inputs.pointerUp(TestState.point(options), id))
296 311
 
297 312
     return this
298 313
   }

+ 15
- 8
state/commands/toggle.ts Переглянути файл

@@ -5,15 +5,22 @@ import tld from 'utils/tld'
5 5
 import { getShapeUtils } from 'state/shape-utils'
6 6
 import { PropsOfType } from 'types'
7 7
 
8
+/**
9
+ * The toggle command toggles a boolean property on all selected shapes.
10
+ * If the value is true for all selected shapes, then the property is
11
+ * set to false; the value is false for one or more of the selected
12
+ * shapes, then the value for all shapes is set to true.
13
+ */
8 14
 export default function toggleCommand(
9 15
   data: Data,
10 16
   prop: PropsOfType<Shape>
11 17
 ): void {
12 18
   const selectedShapes = tld.getSelectedShapes(data)
13 19
   const isAllToggled = selectedShapes.every((shape) => shape[prop])
14
-  const initialShapes = Object.fromEntries(
15
-    selectedShapes.map((shape) => [shape.id, shape[prop]])
16
-  )
20
+  const initialShapes = selectedShapes.map((shape) => ({
21
+    id: shape.id,
22
+    value: shape[prop],
23
+  }))
17 24
 
18 25
   history.execute(
19 26
     data,
@@ -23,22 +30,22 @@ export default function toggleCommand(
23 30
       do(data) {
24 31
         const { shapes } = tld.getPage(data)
25 32
 
26
-        for (const id in initialShapes) {
33
+        initialShapes.forEach(({ id }) => {
27 34
           const shape = shapes[id]
28 35
           getShapeUtils(shape).setProperty(
29 36
             shape,
30 37
             prop,
31 38
             isAllToggled ? false : true
32 39
           )
33
-        }
40
+        })
34 41
       },
35 42
       undo(data) {
36 43
         const { shapes } = tld.getPage(data)
37 44
 
38
-        for (const id in initialShapes) {
45
+        initialShapes.forEach(({ id, value }) => {
39 46
           const shape = shapes[id]
40
-          getShapeUtils(shape).setProperty(shape, prop, initialShapes[id])
41
-        }
47
+          getShapeUtils(shape).setProperty(shape, prop, value)
48
+        })
42 49
       },
43 50
     })
44 51
   )

Завантаження…
Відмінити
Зберегти