Added more tree context tests
Brian Vaughn committed
May 10, 2019 at 08:07 UTC
ac0022bf4a32f8c464e59f8b71dec12f141619b3
4 files changed
+444
-8
src/__tests__/__snapshots__/treeContext-test.js.snap
+310
@@ -1,5 +1,251 @@
1
// Jest Snapshot v1, https://goo.gl/fbAQLP
2
3
+exports[`TreeListContext owners state should exit the owners list if the current owner is unmounted: 0: mount 1`] = `
4
+[root]
5
+ ▾ <Parent>
6
+ <Child>
7
+`;
8
+
9
+exports[`TreeListContext owners state should exit the owners list if the current owner is unmounted: 1: initial state 1`] = `
10
+Object {
11
+ "inspectedElementID": null,
12
+ "numElements": 2,
13
+ "ownerFlatTree": null,
14
+ "ownerID": null,
15
+ "searchIndex": null,
16
+ "searchResults": Array [],
17
+ "searchText": "",
18
+ "selectedElementID": null,
19
+ "selectedElementIndex": null,
20
+}
21
+`;
22
+
23
+exports[`TreeListContext owners state should exit the owners list if the current owner is unmounted: 2: child owners tree 1`] = `
24
+Object {
25
+ "inspectedElementID": 3,
26
+ "numElements": 1,
27
+ "ownerFlatTree": Array [
28
+ Object {
29
+ "children": Array [],
30
+ "depth": 0,
31
+ "displayName": "Child",
32
+ "id": 3,
33
+ "isCollapsed": false,
34
+ "key": null,
35
+ "ownerID": 0,
36
+ "parentID": 2,
37
+ "type": 5,
38
+ "weight": 1,
39
+ },
40
+ ],
41
+ "ownerID": 3,
42
+ "searchIndex": null,
43
+ "searchResults": Array [],
44
+ "searchText": "",
45
+ "selectedElementID": 3,
46
+ "selectedElementIndex": 0,
47
+}
48
+`;
49
+
50
+exports[`TreeListContext owners state should exit the owners list if the current owner is unmounted: 3: remove child 1`] = `
51
+Object {
52
+ "inspectedElementID": null,
53
+ "numElements": 1,
54
+ "ownerFlatTree": null,
55
+ "ownerID": null,
56
+ "searchIndex": null,
57
+ "searchResults": Array [],
58
+ "searchText": "",
59
+ "selectedElementID": null,
60
+ "selectedElementIndex": 0,
61
+}
62
+`;
63
+
64
+exports[`TreeListContext owners state should exit the owners list if the current owner is unmounted: 4: parent owners tree 1`] = `
65
+Object {
66
+ "inspectedElementID": 2,
67
+ "numElements": 1,
68
+ "ownerFlatTree": Array [
69
+ Object {
70
+ "children": Array [],
71
+ "depth": 0,
72
+ "displayName": "Parent",
73
+ "id": 2,
74
+ "isCollapsed": false,
75
+ "key": null,
76
+ "ownerID": 0,
77
+ "parentID": 1,
78
+ "type": 5,
79
+ "weight": 1,
80
+ },
81
+ ],
82
+ "ownerID": 2,
83
+ "searchIndex": null,
84
+ "searchResults": Array [],
85
+ "searchText": "",
86
+ "selectedElementID": 2,
87
+ "selectedElementIndex": 0,
88
+}
89
+`;
90
+
91
+exports[`TreeListContext owners state should exit the owners list if the current owner is unmounted: 5: unmount root 1`] = `
92
+Object {
93
+ "inspectedElementID": null,
94
+ "numElements": 0,
95
+ "ownerFlatTree": null,
96
+ "ownerID": null,
97
+ "searchIndex": null,
98
+ "searchResults": Array [],
99
+ "searchText": "",
100
+ "selectedElementID": null,
101
+ "selectedElementIndex": 0,
102
+}
103
+`;
104
+
105
+exports[`TreeListContext owners state should remove an element from the owners list if it is unmounted: 0: mount 1`] = `
106
+[root]
107
+ ▾ <Grandparent>
108
+ ▾ <Parent>
109
+ <Child key="0">
110
+ <Child key="1">
111
+`;
112
+
113
+exports[`TreeListContext owners state should remove an element from the owners list if it is unmounted: 1: initial state 1`] = `
114
+Object {
115
+ "inspectedElementID": null,
116
+ "numElements": 4,
117
+ "ownerFlatTree": null,
118
+ "ownerID": null,
119
+ "searchIndex": null,
120
+ "searchResults": Array [],
121
+ "searchText": "",
122
+ "selectedElementID": null,
123
+ "selectedElementIndex": null,
124
+}
125
+`;
126
+
127
+exports[`TreeListContext owners state should remove an element from the owners list if it is unmounted: 2: parent owners tree 1`] = `
128
+Object {
129
+ "inspectedElementID": 3,
130
+ "numElements": 3,
131
+ "ownerFlatTree": Array [
132
+ Object {
133
+ "children": Array [
134
+ 4,
135
+ 5,
136
+ ],
137
+ "depth": 0,
138
+ "displayName": "Parent",
139
+ "id": 3,
140
+ "isCollapsed": false,
141
+ "key": null,
142
+ "ownerID": 2,
143
+ "parentID": 2,
144
+ "type": 5,
145
+ "weight": 3,
146
+ },
147
+ Object {
148
+ "children": Array [],
149
+ "depth": 1,
150
+ "displayName": "Child",
151
+ "id": 4,
152
+ "isCollapsed": false,
153
+ "key": "0",
154
+ "ownerID": 3,
155
+ "parentID": 3,
156
+ "type": 5,
157
+ "weight": 1,
158
+ },
159
+ Object {
160
+ "children": Array [],
161
+ "depth": 1,
162
+ "displayName": "Child",
163
+ "id": 5,
164
+ "isCollapsed": false,
165
+ "key": "1",
166
+ "ownerID": 3,
167
+ "parentID": 3,
168
+ "type": 5,
169
+ "weight": 1,
170
+ },
171
+ ],
172
+ "ownerID": 3,
173
+ "searchIndex": null,
174
+ "searchResults": Array [],
175
+ "searchText": "",
176
+ "selectedElementID": 3,
177
+ "selectedElementIndex": 0,
178
+}
179
+`;
180
+
181
+exports[`TreeListContext owners state should remove an element from the owners list if it is unmounted: 3: remove second child 1`] = `
182
+Object {
183
+ "inspectedElementID": 3,
184
+ "numElements": 2,
185
+ "ownerFlatTree": Array [
186
+ Object {
187
+ "children": Array [
188
+ 4,
189
+ ],
190
+ "depth": 0,
191
+ "displayName": "Parent",
192
+ "id": 3,
193
+ "isCollapsed": false,
194
+ "key": null,
195
+ "ownerID": 2,
196
+ "parentID": 2,
197
+ "type": 5,
198
+ "weight": 2,
199
+ },
200
+ Object {
201
+ "children": Array [],
202
+ "depth": 1,
203
+ "displayName": "Child",
204
+ "id": 4,
205
+ "isCollapsed": false,
206
+ "key": "0",
207
+ "ownerID": 3,
208
+ "parentID": 3,
209
+ "type": 5,
210
+ "weight": 1,
211
+ },
212
+ ],
213
+ "ownerID": 3,
214
+ "searchIndex": null,
215
+ "searchResults": Array [],
216
+ "searchText": "",
217
+ "selectedElementID": 3,
218
+ "selectedElementIndex": 0,
219
+}
220
+`;
221
+
222
+exports[`TreeListContext owners state should remove an element from the owners list if it is unmounted: 4: remove first child 1`] = `
223
+Object {
224
+ "inspectedElementID": 3,
225
+ "numElements": 1,
226
+ "ownerFlatTree": Array [
227
+ Object {
228
+ "children": Array [],
229
+ "depth": 0,
230
+ "displayName": "Parent",
231
+ "id": 3,
232
+ "isCollapsed": false,
233
+ "key": null,
234
+ "ownerID": 2,
235
+ "parentID": 2,
236
+ "type": 5,
237
+ "weight": 1,
238
+ },
239
+ ],
240
+ "ownerID": 3,
241
+ "searchIndex": null,
242
+ "searchResults": Array [],
243
+ "searchText": "",
244
+ "selectedElementID": 3,
245
+ "selectedElementIndex": 0,
246
+}
247
+`;
248
+
249
exports[`TreeListContext owners state should support entering and existing the owners tree view: 0: mount 1`] = `
250
[root]
251
▾ <Grandparent>
@@ -430,6 +676,70 @@ Object {
676
}
677
`;
678
679
+exports[`TreeListContext tree state should clear selection if the selected element is unmounted: 0: mount 1`] = `
680
+[root]
681
+ ▾ <Grandparent>
682
+ ▾ <Parent>
683
+ <Child>
684
+ <Child>
685
+`;
686
+
687
+exports[`TreeListContext tree state should clear selection if the selected element is unmounted: 1: initial state 1`] = `
688
+Object {
689
+ "inspectedElementID": null,
690
+ "numElements": 4,
691
+ "ownerFlatTree": null,
692
+ "ownerID": null,
693
+ "searchIndex": null,
694
+ "searchResults": Array [],
695
+ "searchText": "",
696
+ "selectedElementID": null,
697
+ "selectedElementIndex": null,
698
+}
699
+`;
700
+
701
+exports[`TreeListContext tree state should clear selection if the selected element is unmounted: 2: select second child 1`] = `
702
+Object {
703
+ "inspectedElementID": 5,
704
+ "numElements": 4,
705
+ "ownerFlatTree": null,
706
+ "ownerID": null,
707
+ "searchIndex": null,
708
+ "searchResults": Array [],
709
+ "searchText": "",
710
+ "selectedElementID": 5,
711
+ "selectedElementIndex": 3,
712
+}
713
+`;
714
+
715
+exports[`TreeListContext tree state should clear selection if the selected element is unmounted: 3: remove children (parent should now be selected) 1`] = `
716
+Object {
717
+ "inspectedElementID": 3,
718
+ "numElements": 2,
719
+ "ownerFlatTree": null,
720
+ "ownerID": null,
721
+ "searchIndex": null,
722
+ "searchResults": Array [],
723
+ "searchText": "",
724
+ "selectedElementID": 3,
725
+ "selectedElementIndex": 1,
726
+}
727
+`;
728
+
729
+exports[`TreeListContext tree state should clear selection if the selected element is unmounted: 4: unmount root (nothing should be selected) 1`] = `
730
+Object {
731
+ "inspectedElementID": null,
732
+ "numElements": 0,
733
+ "ownerFlatTree": null,
734
+ "ownerID": null,
735
+ "searchIndex": null,
736
+ "searchResults": Array [],
737
+ "searchText": "",
738
+ "selectedElementID": null,
739
+ "selectedElementIndex": null,
740
+}
741
+`;
742
+
743
exports[`TreeListContext tree state should select child elements: 0: mount 1`] = `
744
[root]
745
▾ <Grandparent>
src/__tests__/ownersListContext-test.js
-2
@@ -205,6 +205,4 @@ describe('OwnersListContext', () => {
205
206
done();
207
});
208
-
209
- // TODO (owners) Verify that if an Element in the list is unmounted the stack is updated.
208
});
src/__tests__/treeContext-test.js
+122
@@ -213,6 +213,56 @@ describe('TreeListContext', () => {
213
utils.act(() => renderer.update(<Contexts />));
214
expect(state).toEqual(previousState);
215
});
216
+
217
+ it('should clear selection if the selected element is unmounted', async done => {
218
+ const Grandparent = props => props.children || null;
219
+ const Parent = props => props.children || null;
220
+ const Child = () => null;
221
+
222
+ const container = document.createElement('div');
223
+ utils.act(() =>
224
+ ReactDOM.render(
225
+ <Grandparent>
226
+ <Parent>
227
+ <Child />
228
+ <Child />
229
+ </Parent>
230
+ </Grandparent>,
231
+ container
232
+ )
233
+ );
234
+
235
+ expect(store).toMatchSnapshot('0: mount');
236
+
237
+ let renderer;
238
+ utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
239
+ expect(state).toMatchSnapshot('1: initial state');
240
+
241
+ utils.act(() =>
242
+ dispatch({ type: 'SELECT_ELEMENT_AT_INDEX', payload: 3 })
243
+ );
244
+ utils.act(() => renderer.update(<Contexts />));
245
+ expect(state).toMatchSnapshot('2: select second child');
246
+
247
+ await utils.actSuspense(() =>
248
+ ReactDOM.render(
249
+ <Grandparent>
250
+ <Parent />
251
+ </Grandparent>,
252
+ container
253
+ )
254
+ );
255
+ expect(state).toMatchSnapshot(
256
+ '3: remove children (parent should now be selected)'
257
+ );
258
+
259
+ await utils.actSuspense(() => ReactDOM.unmountComponentAtNode(container));
260
+ expect(state).toMatchSnapshot(
261
+ '4: unmount root (nothing should be selected)'
262
+ );
263
+
264
+ done();
265
+ });
266
});
267
268
describe('search state', () => {
@@ -424,5 +474,77 @@ describe('TreeListContext', () => {
474
utils.act(() => renderer.update(<Contexts />));
475
expect(state).toMatchSnapshot('3: final state');
476
});
477
+
478
+ it('should remove an element from the owners list if it is unmounted', async done => {
479
+ const Grandparent = ({ count }) => <Parent count={count} />;
480
+ const Parent = ({ count }) =>
481
+ new Array(count).fill(true).map((_, index) => <Child key={index} />);
482
+ const Child = () => null;
483
+
484
+ const container = document.createElement('div');
485
+ utils.act(() => ReactDOM.render(<Grandparent count={2} />, container));
486
+
487
+ expect(store).toMatchSnapshot('0: mount');
488
+
489
+ let renderer;
490
+ utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
491
+ expect(state).toMatchSnapshot('1: initial state');
492
+
493
+ let parentID = ((store.getElementIDAtIndex(1): any): number);
494
+ utils.act(() => dispatch({ type: 'SELECT_OWNER', payload: parentID }));
495
+ utils.act(() => renderer.update(<Contexts />));
496
+ expect(state).toMatchSnapshot('2: parent owners tree');
497
+
498
+ await utils.actSuspense(() =>
499
+ ReactDOM.render(<Grandparent count={1} />, container)
500
+ );
501
+ expect(state).toMatchSnapshot('3: remove second child');
502
+
503
+ await utils.actSuspense(() =>
504
+ ReactDOM.render(<Grandparent count={0} />, container)
505
+ );
506
+ expect(state).toMatchSnapshot('4: remove first child');
507
+
508
+ done();
509
+ });
510
+
511
+ it('should exit the owners list if the current owner is unmounted', async done => {
512
+ const Parent = props => props.children || null;
513
+ const Child = () => null;
514
+
515
+ const container = document.createElement('div');
516
+ utils.act(() =>
517
+ ReactDOM.render(
518
+ <Parent>
519
+ <Child />
520
+ </Parent>,
521
+ container
522
+ )
523
+ );
524
+
525
+ expect(store).toMatchSnapshot('0: mount');
526
+
527
+ let renderer;
528
+ utils.act(() => (renderer = TestRenderer.create(<Contexts />)));
529
+ expect(state).toMatchSnapshot('1: initial state');
530
+
531
+ let childID = ((store.getElementIDAtIndex(1): any): number);
532
+ utils.act(() => dispatch({ type: 'SELECT_OWNER', payload: childID }));
533
+ utils.act(() => renderer.update(<Contexts />));
534
+ expect(state).toMatchSnapshot('2: child owners tree');
535
+
536
+ await utils.actSuspense(() => ReactDOM.render(<Parent />, container));
537
+ expect(state).toMatchSnapshot('3: remove child');
538
+
539
+ let parentID = ((store.getElementIDAtIndex(0): any): number);
540
+ utils.act(() => dispatch({ type: 'SELECT_OWNER', payload: parentID }));
541
+ utils.act(() => renderer.update(<Contexts />));
542
+ expect(state).toMatchSnapshot('4: parent owners tree');
543
+
544
+ await utils.actSuspense(() => ReactDOM.unmountComponentAtNode(container));
545
+ expect(state).toMatchSnapshot('5: unmount root');
546
+
547
+ done();
548
+ });
549
});
550
});
src/devtools/views/Components/TreeContext.js
+12
-6
@@ -447,12 +447,18 @@ function reduceOwnersState(store: Store, state: State, action: Action): State {
447
switch (action.type) {
448
case 'HANDLE_STORE_MUTATION':
449
if (ownerID !== null) {
450
- ownerFlatTree = store.getOwnersListForElement(ownerID);
451
- if (selectedElementID !== null) {
452
- // Mutation might have caused the index of this ID to shift.
453
- selectedElementIndex = ownerFlatTree.findIndex(
454
- element => element.id === selectedElementID
455
- );
450
+ if (!store.containsElement(ownerID)) {
451
+ ownerID = null;
452
+ ownerFlatTree = null;
453
+ selectedElementID = null;
454
+ } else {
455
+ ownerFlatTree = store.getOwnersListForElement(ownerID);
456
+ if (selectedElementID !== null) {
457
+ // Mutation might have caused the index of this ID to shift.
458
+ selectedElementIndex = ownerFlatTree.findIndex(
459
+ element => element.id === selectedElementID
460
+ );
461
+ }
462
}
463
} else {
464
if (selectedElementID !== null) {