@samitouri / QOS-React-2 / commits / b6c135c165

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