Added Jest tests for OwnersListContext
Brian Vaughn committed
May 9, 2019 at 14:21 UTC
b6c135c165de5023ce09abb28c35458b41927789
5 files changed
+295
-51
src/__tests__/__snapshots__/ownersListContext-test.js.snap
new
+51
@@ -0,0 +1,51 @@
1
+// Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+exports[`profiling should fetch the owners list for the selected element that includes filtered components: mount 1`] = `
4
+[root]
5
+ ▾ <Grandparent>
6
+ <Child>
7
+ <Child>
8
+`;
9
+
10
+exports[`profiling should fetch the owners list for the selected element that includes filtered components: owners for "Child" 1`] = `
11
+Array [
12
+ Object {
13
+ "displayName": "Parent",
14
+ "id": 9,
15
+ },
16
+ Object {
17
+ "displayName": "Grandparent",
18
+ "id": 7,
19
+ },
20
+]
21
+`;
22
+
23
+exports[`profiling should fetch the owners list for the selected element: mount 1`] = `
24
+[root]
25
+ ▾ <Grandparent>
26
+ ▾ <Parent>
27
+ <Child>
28
+ <Child>
29
+`;
30
+
31
+exports[`profiling should fetch the owners list for the selected element: owners for "Child" 1`] = `
32
+Array [
33
+ Object {
34
+ "displayName": "Parent",
35
+ "id": 3,
36
+ },
37
+ Object {
38
+ "displayName": "Grandparent",
39
+ "id": 2,
40
+ },
41
+]
42
+`;
43
+
44
+exports[`profiling should fetch the owners list for the selected element: owners for "Parent" 1`] = `
45
+Array [
46
+ Object {
47
+ "displayName": "Grandparent",
48
+ "id": 2,
49
+ },
50
+]
51
+`;
src/__tests__/ownersListContext-test.js
new
+166
@@ -0,0 +1,166 @@
1
+// @flow
2
+
3
+import typeof ReactTestRenderer from 'react-test-renderer';
4
+import type { Element } from 'src/devtools/views/Components/types';
5
+import type Bridge from 'src/bridge';
6
+import type Store from 'src/devtools/store';
7
+
8
+describe('profiling', () => {
9
+ let React;
10
+ let ReactDOM;
11
+ let TestRenderer: ReactTestRenderer;
12
+ let bridge: Bridge;
13
+ let store: Store;
14
+ let utils;
15
+
16
+ let BridgeContext;
17
+ let OwnersListContext;
18
+ let OwnersListContextController;
19
+ let StoreContext;
20
+ let TreeContextController;
21
+
22
+ beforeEach(() => {
23
+ utils = require('./utils');
24
+ utils.beforeEachProfiling();
25
+
26
+ bridge = global.bridge;
27
+ store = global.store;
28
+ store.collapseNodesByDefault = false;
29
+
30
+ React = require('react');
31
+ ReactDOM = require('react-dom');
32
+ TestRenderer = utils.requireTestRenderer();
33
+
34
+ BridgeContext = require('src/devtools/views/context').BridgeContext;
35
+ OwnersListContext = require('src/devtools/views/Components/OwnersListContext')
36
+ .OwnersListContext;
37
+ OwnersListContextController = require('src/devtools/views/Components/OwnersListContext')
38
+ .OwnersListContextController;
39
+ StoreContext = require('src/devtools/views/context').StoreContext;
40
+ TreeContextController = require('src/devtools/views/Components/TreeContext')
41
+ .TreeContextController;
42
+ });
43
+
44
+ const Contexts = ({ children, defaultOwnerID = null }) => (
45
+ <BridgeContext.Provider value={bridge}>
46
+ <StoreContext.Provider value={store}>
47
+ <TreeContextController defaultOwnerID={defaultOwnerID}>
48
+ <OwnersListContextController>{children}</OwnersListContextController>
49
+ </TreeContextController>
50
+ </StoreContext.Provider>
51
+ </BridgeContext.Provider>
52
+ );
53
+
54
+ it('should fetch the owners list for the selected element', async done => {
55
+ const Grandparent = () => <Parent />;
56
+ const Parent = ({ count }) => {
57
+ return (
58
+ <React.Fragment>
59
+ <Child duration={1} />
60
+ <Child duration={2} />
61
+ </React.Fragment>
62
+ );
63
+ };
64
+ const Child = ({ duration }) => null;
65
+
66
+ utils.act(() =>
67
+ ReactDOM.render(<Grandparent />, document.createElement('div'))
68
+ );
69
+
70
+ expect(store).toMatchSnapshot('mount');
71
+
72
+ const parent = ((store.getElementAtIndex(1): any): Element);
73
+ const firstChild = ((store.getElementAtIndex(2): any): Element);
74
+
75
+ let didFinish = false;
76
+
77
+ function Suspender({ owner }) {
78
+ const read = React.useContext(OwnersListContext);
79
+ const owners = read(owner.id);
80
+ expect(owners).toMatchSnapshot(
81
+ `owners for "${(owner && owner.displayName) || ''}"`
82
+ );
83
+ didFinish = true;
84
+ return null;
85
+ }
86
+
87
+ await utils.actSuspense(
88
+ () =>
89
+ TestRenderer.create(
90
+ <Contexts defaultOwnerID={parent.id}>
91
+ <React.Suspense fallback={null}>
92
+ <Suspender owner={parent} />
93
+ </React.Suspense>
94
+ </Contexts>
95
+ ),
96
+ 3
97
+ );
98
+ expect(didFinish).toBe(true);
99
+
100
+ didFinish = false;
101
+ await utils.actSuspense(
102
+ () =>
103
+ TestRenderer.create(
104
+ <Contexts defaultOwnerID={firstChild.id}>
105
+ <React.Suspense fallback={null}>
106
+ <Suspender owner={firstChild} />
107
+ </React.Suspense>
108
+ </Contexts>
109
+ ),
110
+ 3
111
+ );
112
+ expect(didFinish).toBe(true);
113
+
114
+ done();
115
+ });
116
+
117
+ it('should fetch the owners list for the selected element that includes filtered components', async done => {
118
+ store.componentFilters = [utils.createDisplayNameFilter('^Parent$')];
119
+
120
+ const Grandparent = () => <Parent />;
121
+ const Parent = ({ count }) => {
122
+ return (
123
+ <React.Fragment>
124
+ <Child duration={1} />
125
+ <Child duration={2} />
126
+ </React.Fragment>
127
+ );
128
+ };
129
+ const Child = ({ duration }) => null;
130
+
131
+ utils.act(() =>
132
+ ReactDOM.render(<Grandparent />, document.createElement('div'))
133
+ );
134
+
135
+ expect(store).toMatchSnapshot('mount');
136
+
137
+ const firstChild = ((store.getElementAtIndex(1): any): Element);
138
+
139
+ let didFinish = false;
140
+
141
+ function Suspender({ owner }) {
142
+ const read = React.useContext(OwnersListContext);
143
+ const owners = read(owner.id);
144
+ expect(owners).toMatchSnapshot(
145
+ `owners for "${(owner && owner.displayName) || ''}"`
146
+ );
147
+ didFinish = true;
148
+ return null;
149
+ }
150
+
151
+ await utils.actSuspense(
152
+ () =>
153
+ TestRenderer.create(
154
+ <Contexts defaultOwnerID={firstChild.id}>
155
+ <React.Suspense fallback={null}>
156
+ <Suspender owner={firstChild} />
157
+ </React.Suspense>
158
+ </Contexts>
159
+ ),
160
+ 3
161
+ );
162
+ expect(didFinish).toBe(true);
163
+
164
+ done();
165
+ });
166
+});
src/__tests__/storeComponentFilters-test.js
+18
-48
@@ -6,42 +6,7 @@ describe('Store component filters', () => {
6
let TestUtils;
7
let Types;
8
let store;
9
-
10
- const createElementTypeFilter = (elementType, isEnabled = true) => ({
11
- type: Types.ComponentFilterElementType,
12
- isEnabled,
13
- value: elementType,
14
- });
15
-
16
- const createDisplayNameFilter = (source, isEnabled = true) => {
17
- let isValid = true;
18
- try {
19
- new RegExp(source);
20
- } catch (error) {
21
- isValid = false;
22
- }
23
- return {
24
- type: Types.ComponentFilterDisplayName,
25
- isEnabled,
26
- isValid,
27
- value: source,
28
- };
29
- };
30
-
31
- const createLocationFilter = (source, isEnabled = true) => {
32
- let isValid = true;
33
- try {
34
- new RegExp(source);
35
- } catch (error) {
36
- isValid = false;
37
- }
38
- return {
39
- type: Types.ComponentFilterLocation,
40
- isEnabled,
41
- isValid,
42
- value: source,
43
- };
44
- };
9
+ let utils;
10
11
const act = (callback: Function) => {
12
TestUtils.act(() => {
@@ -59,6 +24,7 @@ describe('Store component filters', () => {
24
ReactDOM = require('react-dom');
25
TestUtils = require('react-dom/test-utils');
26
Types = require('src/types');
27
+ utils = require('./utils');
28
});
29
30
it('should throw if filters are updated while profiling', () => {
@@ -89,7 +55,7 @@ describe('Store component filters', () => {
55
act(
56
() =>
57
(store.componentFilters = [
92
- createElementTypeFilter(Types.ElementTypeHostComponent),
58
+ utils.createElementTypeFilter(Types.ElementTypeHostComponent),
59
])
60
);
61
@@ -98,7 +64,7 @@ describe('Store component filters', () => {
64
act(
65
() =>
66
(store.componentFilters = [
101
- createElementTypeFilter(Types.ElementTypeClass),
67
+ utils.createElementTypeFilter(Types.ElementTypeClass),
68
])
69
);
70
@@ -107,8 +73,8 @@ describe('Store component filters', () => {
73
act(
74
() =>
75
(store.componentFilters = [
110
- createElementTypeFilter(Types.ElementTypeClass),
111
- createElementTypeFilter(Types.ElementTypeFunction),
76
+ utils.createElementTypeFilter(Types.ElementTypeClass),
77
+ utils.createElementTypeFilter(Types.ElementTypeFunction),
78
])
79
);
80
@@ -117,8 +83,8 @@ describe('Store component filters', () => {
83
act(
84
() =>
85
(store.componentFilters = [
120
- createElementTypeFilter(Types.ElementTypeClass, false),
121
- createElementTypeFilter(Types.ElementTypeFunction, false),
86
+ utils.createElementTypeFilter(Types.ElementTypeClass, false),
87
+ utils.createElementTypeFilter(Types.ElementTypeFunction, false),
88
])
89
);
90
@@ -134,7 +100,7 @@ describe('Store component filters', () => {
100
act(
101
() =>
102
(store.componentFilters = [
137
- createElementTypeFilter(Types.ElementTypeRoot),
103
+ utils.createElementTypeFilter(Types.ElementTypeRoot),
104
])
105
);
106
@@ -159,13 +125,17 @@ describe('Store component filters', () => {
125
);
126
expect(store).toMatchSnapshot('1: mount');
127
162
- act(() => (store.componentFilters = [createDisplayNameFilter('Foo')]));
128
+ act(
129
+ () => (store.componentFilters = [utils.createDisplayNameFilter('Foo')])
130
+ );
131
expect(store).toMatchSnapshot('2: filter "Foo"');
132
165
- act(() => (store.componentFilters = [createDisplayNameFilter('Ba')]));
133
+ act(() => (store.componentFilters = [utils.createDisplayNameFilter('Ba')]));
134
expect(store).toMatchSnapshot('3: filter "Ba"');
135
168
- act(() => (store.componentFilters = [createDisplayNameFilter('B.z')]));
136
+ act(
137
+ () => (store.componentFilters = [utils.createDisplayNameFilter('B.z')])
138
+ );
139
expect(store).toMatchSnapshot('4: filter "B.z"');
140
});
141
@@ -178,7 +148,7 @@ describe('Store component filters', () => {
148
act(
149
() =>
150
(store.componentFilters = [
181
- createLocationFilter(__filename.replace(__dirname, '')),
151
+ utils.createLocationFilter(__filename.replace(__dirname, '')),
152
])
153
);
154
@@ -189,7 +159,7 @@ describe('Store component filters', () => {
159
act(
160
() =>
161
(store.componentFilters = [
192
- createLocationFilter('this:is:a:made:up:path'),
162
+ utils.createLocationFilter('this:is:a:made:up:path'),
163
])
164
);
165
src/__tests__/utils.js
+52
@@ -2,6 +2,8 @@
2
3
import typeof ReactTestRenderer from 'react-test-renderer';
4
5
+import type { ElementType } from 'src/types';
6
+
7
export function act(callback: Function): void {
8
const TestUtils = require('react-dom/test-utils');
9
TestUtils.act(() => {
@@ -54,6 +56,56 @@ export function beforeEachProfiling(): void {
56
);
57
}
58
59
+export function createElementTypeFilter(
60
+ elementType: ElementType,
61
+ isEnabled: boolean = true
62
+) {
63
+ const Types = require('src/types');
64
+ return {
65
+ type: Types.ComponentFilterElementType,
66
+ isEnabled,
67
+ value: elementType,
68
+ };
69
+}
70
+
71
+export function createDisplayNameFilter(
72
+ source: string,
73
+ isEnabled: boolean = true
74
+) {
75
+ const Types = require('src/types');
76
+ let isValid = true;
77
+ try {
78
+ new RegExp(source);
79
+ } catch (error) {
80
+ isValid = false;
81
+ }
82
+ return {
83
+ type: Types.ComponentFilterDisplayName,
84
+ isEnabled,
85
+ isValid,
86
+ value: source,
87
+ };
88
+}
89
+
90
+export function createLocationFilter(
91
+ source: string,
92
+ isEnabled: boolean = true
93
+) {
94
+ const Types = require('src/types');
95
+ let isValid = true;
96
+ try {
97
+ new RegExp(source);
98
+ } catch (error) {
99
+ isValid = false;
100
+ }
101
+ return {
102
+ type: Types.ComponentFilterLocation,
103
+ isEnabled,
104
+ isValid,
105
+ value: source,
106
+ };
107
+}
108
+
109
export function getRendererID(): number {
110
if (global.agent == null) {
111
throw Error('Agent unavailable.');
src/devtools/views/Components/TreeContext.js
+8
-3
@@ -583,10 +583,15 @@ function reduceSuspenseState(
583
return state;
584
}
585
586
-type Props = {| children: React$Node |};
586
+type Props = {|
587
+ children: React$Node,
588
+
589
+ // Used for automated testing
590
+ defaultOwnerID?: ?number,
591
+|};
592
593
// TODO Remove TreeContextController wrapper element once global ConsearchText.write API exists.
589
-function TreeContextController({ children }: Props) {
594
+function TreeContextController({ children, defaultOwnerID }: Props) {
595
const bridge = useContext(BridgeContext);
596
const store = useContext(StoreContext);
597
@@ -650,7 +655,7 @@ function TreeContextController({ children }: Props) {
655
searchText: '',
656
657
// Owners
653
- ownerID: null,
658
+ ownerID: defaultOwnerID == null ? null : defaultOwnerID,
659
ownerFlatTree: null,
660
661
// Inspection element panel