Skip to content

Commit 9d14406

Browse files
thymikeeEsemesek
authored andcommitted
BREAKING: make getAllBy* methods throwable (#12)
* BREAKING: make getAllBy* methods throwable * fix wrong fn args
1 parent 08c5725 commit 9d14406

File tree

8 files changed

+190
-132
lines changed

8 files changed

+190
-132
lines changed

src/__tests__/render.test.js

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,7 @@ test('getByTestId', () => {
7272
const component = getByTestId('bananaFresh');
7373

7474
expect(component.props.children).toBe('not fresh');
75-
expect(() => {
76-
getByTestId('InExistent');
77-
}).toThrow();
75+
expect(() => getByTestId('InExistent')).toThrow();
7876
});
7977

8078
test('getByName', () => {
@@ -91,9 +89,7 @@ test('getByName', () => {
9189

9290
expect(bananaFresh.props.children).toBe('not fresh');
9391

94-
expect(() => {
95-
getByName('InExistent');
96-
}).toThrow();
92+
expect(() => getByName('InExistent')).toThrow();
9793
});
9894

9995
test('getAllByName', () => {
@@ -103,7 +99,7 @@ test('getAllByName', () => {
10399
expect(text.props.children).toBe('Is the banana fresh?');
104100
expect(status.props.children).toBe('not fresh');
105101
expect(button.props.children).toBe('Change freshness!');
106-
expect(getAllByName('InExistent')).toHaveLength(0);
102+
expect(() => getAllByName('InExistent')).toThrow();
107103
});
108104

109105
test('getByText', () => {
@@ -115,35 +111,31 @@ test('getByText', () => {
115111
const sameButton = getByText('not fresh');
116112

117113
expect(sameButton.props.children).toBe('not fresh');
118-
expect(() => {
119-
getByText('InExistent');
120-
}).toThrow();
114+
expect(() => getByText('InExistent')).toThrow();
121115
});
122116

123117
test('getAllByText', () => {
124118
const { getAllByText } = render(<Banana />);
125119
const button = getAllByText(/fresh/i);
126120

127121
expect(button).toHaveLength(3);
128-
expect(getAllByText('InExistent')).toHaveLength(0);
122+
expect(() => getAllByText('InExistent')).toThrow();
129123
});
130124

131125
test('getByProps', () => {
132126
const { getByProps } = render(<Banana />);
133127
const primaryType = getByProps({ type: 'primary' });
134128

135129
expect(primaryType.props.children).toBe('Change freshness!');
136-
expect(() => {
137-
getByProps({ type: 'inexistent' });
138-
}).toThrow();
130+
expect(() => getByProps({ type: 'inexistent' })).toThrow();
139131
});
140132

141133
test('getAllByProps', () => {
142134
const { getAllByProps } = render(<Banana />);
143135
const primaryTypes = getAllByProps({ type: 'primary' });
144136

145137
expect(primaryTypes).toHaveLength(1);
146-
expect(getAllByProps({ type: 'inexistent' })).toHaveLength(0);
138+
expect(() => getAllByProps({ type: 'inexistent' })).toThrow();
147139
});
148140

149141
test('update', () => {

src/debug.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// @flow
2+
import * as React from 'react';
3+
import prettyFormat, { plugins } from 'pretty-format'; // eslint-disable-line import/no-extraneous-dependencies
4+
import shallow from './shallow';
5+
6+
/**
7+
* Log pretty-printed shallow test component instance
8+
*/
9+
export default function debug(
10+
instance: ReactTestInstance | React.Element<*>,
11+
message?: any
12+
) {
13+
const { output } = shallow(instance);
14+
// eslint-disable-next-line no-console
15+
console.log(format(output), message || '');
16+
}
17+
18+
const format = input =>
19+
prettyFormat(input, {
20+
plugins: [plugins.ReactTestComponent, plugins.ReactElement],
21+
});

src/flushMicrotasksQueue.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @flow
2+
/**
3+
* Wait for microtasks queue to flush
4+
*/
5+
export default function flushMicrotasksQueue(): Promise<any> {
6+
return new Promise(resolve => setImmediate(resolve));
7+
}

src/helpers/errorWithStack.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// @flow
2+
export default class ErrorWithStack extends Error {
3+
constructor(message: ?string, callsite: Function) {
4+
super(message);
5+
if (Error.captureStackTrace) {
6+
Error.captureStackTrace(this, callsite);
7+
}
8+
}
9+
}

src/helpers/getBy.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// @flow
2+
import * as React from 'react';
3+
import ErrorWithStack from './errorWithStack';
4+
5+
const getNodeByName = (node, name) =>
6+
node.type.name === name ||
7+
node.type.displayName === name ||
8+
node.type === name;
9+
10+
const getNodeByText = (node, text) =>
11+
(getNodeByName(node, 'Text') || getNodeByName(node, 'TextInput')) &&
12+
(typeof text === 'string'
13+
? text === node.props.children
14+
: text.test(node.props.children));
15+
16+
export const getByName = (instance: ReactTestInstance) => (
17+
name: string | React.Element<*>
18+
) => {
19+
try {
20+
return instance.find(node => getNodeByName(node, name));
21+
} catch (error) {
22+
throw new ErrorWithStack(`Error: Component not found.`, getByName);
23+
}
24+
};
25+
26+
export const getByText = (instance: ReactTestInstance) => (
27+
text: string | RegExp
28+
) => {
29+
try {
30+
return instance.find(node => getNodeByText(node, text));
31+
} catch (error) {
32+
throw new ErrorWithStack(`Error: Component not found.`, getByText);
33+
}
34+
};
35+
36+
export const getByProps = (instance: ReactTestInstance) => (props: {
37+
[propName: string]: any,
38+
}) => {
39+
try {
40+
return instance.findByProps(props);
41+
} catch (error) {
42+
throw new ErrorWithStack(`Error: Component not found.`, getByProps);
43+
}
44+
};
45+
46+
export const getByTestId = (instance: ReactTestInstance) => (testID: string) =>
47+
getByProps(instance)({ testID });
48+
49+
export const getAllByName = (instance: ReactTestInstance) => (
50+
name: string | React.Element<*>
51+
) => {
52+
const results = instance.findAll(node => getNodeByName(node, name));
53+
if (results.length === 0) {
54+
throw new ErrorWithStack(`Error: Components not found.`, getAllByName);
55+
}
56+
return results;
57+
};
58+
59+
export const getAllByText = (instance: ReactTestInstance) => (
60+
text: string | RegExp
61+
) => {
62+
const results = instance.findAll(node => getNodeByText(node, text));
63+
if (results.length === 0) {
64+
throw new ErrorWithStack(`Error: Components not found.`, getAllByText);
65+
}
66+
return results;
67+
};
68+
69+
export const getAllByProps = (instance: ReactTestInstance) => (props: {
70+
[propName: string]: any,
71+
}) => {
72+
const results = instance.findAllByProps(props);
73+
if (results.length === 0) {
74+
throw new ErrorWithStack(`Error: Components not found.`, getAllByProps);
75+
}
76+
return results;
77+
};

src/index.js

Lines changed: 9 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,10 @@
11
// @flow
2-
import * as React from 'react';
3-
import { isValidElementType } from 'react-is';
4-
import TestRenderer from 'react-test-renderer'; // eslint-disable-line import/no-extraneous-dependencies
5-
import ShallowRenderer from 'react-test-renderer/shallow'; // eslint-disable-line import/no-extraneous-dependencies
6-
import prettyFormat, { plugins } from 'pretty-format'; // eslint-disable-line import/no-extraneous-dependencies
7-
8-
const getNodeByName = (node, name) =>
9-
node.type.name === name ||
10-
node.type.displayName === name ||
11-
node.type === name;
12-
13-
const getNodeByText = (node, text) =>
14-
(getNodeByName(node, 'Text') || getNodeByName(node, 'TextInput')) &&
15-
(typeof text === 'string'
16-
? text === node.props.children
17-
: text.test(node.props.children));
18-
19-
/**
20-
* Wait for microtasks queue to flush
21-
*/
22-
export const flushMicrotasksQueue = (): Promise<any> =>
23-
new Promise(resolve => setImmediate(resolve));
24-
25-
/**
26-
* Renders test component deeply using react-test-renderer and exposes helpers
27-
* to assert on the output.
28-
*/
29-
export const render = (
30-
component: React.Element<*>,
31-
options?: { createNodeMock: (element: React.Element<*>) => any }
32-
) => {
33-
const renderer = TestRenderer.create(component, options);
34-
const instance = renderer.root;
35-
36-
const getByName = (name: string | React.Element<*>) => {
37-
try {
38-
return instance.find(node => getNodeByName(node, name));
39-
} catch (error) {
40-
throw new ErrorWithStack(`Error: Component not found.`, getByName);
41-
}
42-
};
43-
44-
const getByText = (text: string | RegExp) => {
45-
try {
46-
return instance.find(node => getNodeByText(node, text));
47-
} catch (error) {
48-
throw new ErrorWithStack(`Error: Component not found.`, getByText);
49-
}
50-
};
51-
52-
const getByProps = (props: { [propName: string]: any }) => {
53-
try {
54-
return instance.findByProps(props);
55-
} catch (error) {
56-
throw new ErrorWithStack(`Error: Component not found.`, getByProps);
57-
}
58-
};
59-
60-
return {
61-
getByTestId: (testID: string) => instance.findByProps({ testID }),
62-
getByName,
63-
getAllByName: (name: string | React.Element<*>) =>
64-
instance.findAll(node => getNodeByName(node, name)),
65-
getByText,
66-
getAllByText: (text: string | RegExp) =>
67-
instance.findAll(node => getNodeByText(node, text)),
68-
getByProps,
69-
getAllByProps: (props: { [propName: string]: any }) =>
70-
instance.findAllByProps(props),
71-
update: renderer.update,
72-
unmount: renderer.unmount,
73-
};
74-
};
75-
76-
/**
77-
* Renders test component shallowly using react-test-renderer/shallow
78-
*/
79-
export const shallow = (instance: ReactTestInstance | React.Element<*>) => {
80-
const renderer = new ShallowRenderer();
81-
if (isValidElementType(instance)) {
82-
// $FlowFixMe - instance is React.Element<*> in this branch
83-
renderer.render(instance);
84-
} else {
85-
renderer.render(React.createElement(instance.type, instance.props));
86-
}
87-
const output = renderer.getRenderOutput();
88-
89-
return {
90-
output,
91-
};
92-
};
93-
94-
/**
95-
* Log pretty-printed shallow test component instance
96-
*/
97-
export const debug = (
98-
instance: ReactTestInstance | React.Element<*>,
99-
message?: any
100-
) => {
101-
const { output } = shallow(instance);
102-
// eslint-disable-next-line no-console
103-
console.log(format(output), message || '');
104-
};
105-
106-
const format = input =>
107-
prettyFormat(input, {
108-
plugins: [plugins.ReactTestComponent, plugins.ReactElement],
109-
});
110-
111-
class ErrorWithStack extends Error {
112-
constructor(message: ?string, callsite: Function) {
113-
super(message);
114-
if (Error.captureStackTrace) {
115-
Error.captureStackTrace(this, callsite);
116-
}
117-
}
118-
}
2+
import render from './render';
3+
import shallow from './shallow';
4+
import flushMicrotasksQueue from './flushMicrotasksQueue';
5+
import debug from './debug';
6+
7+
export { render };
8+
export { shallow };
9+
export { flushMicrotasksQueue };
10+
export { debug };

src/render.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// @flow
2+
import * as React from 'react';
3+
import TestRenderer from 'react-test-renderer'; // eslint-disable-line import/no-extraneous-dependencies
4+
import {
5+
getByTestId,
6+
getByName,
7+
getByText,
8+
getByProps,
9+
getAllByName,
10+
getAllByText,
11+
getAllByProps,
12+
} from './helpers/getBy';
13+
14+
/**
15+
* Renders test component deeply using react-test-renderer and exposes helpers
16+
* to assert on the output.
17+
*/
18+
export default function render(
19+
component: React.Element<*>,
20+
options?: { createNodeMock: (element: React.Element<*>) => any }
21+
) {
22+
const renderer = TestRenderer.create(component, options);
23+
const instance = renderer.root;
24+
25+
return {
26+
getByTestId: getByTestId(instance),
27+
getByName: getByName(instance),
28+
getByText: getByText(instance),
29+
getByProps: getByProps(instance),
30+
getAllByName: getAllByName(instance),
31+
getAllByText: getAllByText(instance),
32+
getAllByProps: getAllByProps(instance),
33+
update: renderer.update,
34+
unmount: renderer.unmount,
35+
};
36+
}

src/shallow.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// @flow
2+
import * as React from 'react';
3+
import { isValidElementType } from 'react-is';
4+
import ShallowRenderer from 'react-test-renderer/shallow'; // eslint-disable-line import/no-extraneous-dependencies
5+
6+
/**
7+
* Renders test component shallowly using react-test-renderer/shallow
8+
*/
9+
export default function shallow(
10+
instance: ReactTestInstance | React.Element<*>
11+
) {
12+
const renderer = new ShallowRenderer();
13+
if (isValidElementType(instance)) {
14+
// $FlowFixMe - instance is React.Element<*> in this branch
15+
renderer.render(instance);
16+
} else {
17+
renderer.render(React.createElement(instance.type, instance.props));
18+
}
19+
const output = renderer.getRenderOutput();
20+
21+
return {
22+
output,
23+
};
24+
}

0 commit comments

Comments
 (0)