mirror of
https://github.com/callstack/react-native-testing-library.git
synced 2026-09-18 23:09:04 +08:00
feat: add debug to render helpers for easier console debugging (#65)
* feat: add debug to render helpers for easier console debugging * bring back toJSON to docs * revert unnecessary change * update TS * remove cyclic dependency of debug <-> render
This commit is contained in:
committed by
Kacper Wiszczuk
parent
fa198c4037
commit
909916e84c
@@ -147,6 +147,33 @@ Unmount the in-memory tree, triggering the appropriate lifecycle events
|
||||
|
||||
When using React context providers, like Redux Provider, you'll likely want to wrap rendered component with them. In such cases it's convenient to create your custom `render` method. [Follow this great guide on how to set this up](https://github.com/kentcdodds/react-testing-library#custom-render).
|
||||
|
||||
### `debug: (message?: string) => void`
|
||||
|
||||
Prints deeply rendered component passed to `render` with optional message on top. Uses [debug.deep](#debug) under the hood, but it's easier to use.
|
||||
|
||||
```jsx
|
||||
const { debug } = render(<Component />);
|
||||
|
||||
debug('optional message');
|
||||
```
|
||||
|
||||
logs optional message and colored JSX:
|
||||
|
||||
```jsx
|
||||
optional message
|
||||
|
||||
<TouchableOpacity
|
||||
activeOpacity={0.2}
|
||||
onPress={[Function bound fn]}
|
||||
>
|
||||
<Text>Press me</Text>
|
||||
</TouchableOpacity>
|
||||
```
|
||||
|
||||
### `debug.shallow: (message?: string) => void`
|
||||
|
||||
Prints shallowly rendered component passed to `render` with optional message on top. Uses [debug.shallow](#debug) under the hood, but it's easier to use.
|
||||
|
||||
### `toJSON: () => ?ReactTestRendererJSON`
|
||||
|
||||
Get the rendered component JSON representation, e.g. for snapshot testing.
|
||||
|
||||
@@ -1,5 +1,143 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`debug 1`] = `
|
||||
"<View>
|
||||
<Text>
|
||||
Is the banana fresh?
|
||||
</Text>
|
||||
<Text
|
||||
testID=\\"bananaFresh\\"
|
||||
>
|
||||
not fresh
|
||||
</Text>
|
||||
<View
|
||||
accessible={true}
|
||||
isTVSelectable={true}
|
||||
onResponderGrant={[Function bound touchableHandleResponderGrant]}
|
||||
onResponderMove={[Function bound touchableHandleResponderMove]}
|
||||
onResponderRelease={[Function bound touchableHandleResponderRelease]}
|
||||
onResponderTerminate={[Function bound touchableHandleResponderTerminate]}
|
||||
onResponderTerminationRequest={[Function bound touchableHandleResponderTerminationRequest]}
|
||||
onStartShouldSetResponder={[Function bound touchableHandleStartShouldSetResponder]}
|
||||
style={
|
||||
Object {
|
||||
\\"opacity\\": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
Change freshness!
|
||||
</Text>
|
||||
</View>
|
||||
</View>"
|
||||
`;
|
||||
|
||||
exports[`debug changing component: bananaFresh button message should now be "fresh" 1`] = `
|
||||
"<View>
|
||||
<Text>
|
||||
Is the banana fresh?
|
||||
</Text>
|
||||
<Text
|
||||
testID=\\"bananaFresh\\"
|
||||
>
|
||||
fresh
|
||||
</Text>
|
||||
<View
|
||||
accessible={true}
|
||||
isTVSelectable={true}
|
||||
onResponderGrant={[Function bound touchableHandleResponderGrant]}
|
||||
onResponderMove={[Function bound touchableHandleResponderMove]}
|
||||
onResponderRelease={[Function bound touchableHandleResponderRelease]}
|
||||
onResponderTerminate={[Function bound touchableHandleResponderTerminate]}
|
||||
onResponderTerminationRequest={[Function bound touchableHandleResponderTerminationRequest]}
|
||||
onStartShouldSetResponder={[Function bound touchableHandleStartShouldSetResponder]}
|
||||
style={
|
||||
Object {
|
||||
\\"opacity\\": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
Change freshness!
|
||||
</Text>
|
||||
</View>
|
||||
</View>"
|
||||
`;
|
||||
|
||||
exports[`debug: shallow 1`] = `
|
||||
"<View>
|
||||
<Text>
|
||||
Is the banana fresh?
|
||||
</Text>
|
||||
<Text
|
||||
testID=\\"bananaFresh\\"
|
||||
>
|
||||
not fresh
|
||||
</Text>
|
||||
<Button
|
||||
onPress={[Function anonymous]}
|
||||
type=\\"primary\\"
|
||||
>
|
||||
Change freshness!
|
||||
</Button>
|
||||
</View>"
|
||||
`;
|
||||
|
||||
exports[`debug: shallow with message 1`] = `
|
||||
"my other custom message
|
||||
|
||||
<View>
|
||||
<Text>
|
||||
Is the banana fresh?
|
||||
</Text>
|
||||
<Text
|
||||
testID=\\"bananaFresh\\"
|
||||
>
|
||||
not fresh
|
||||
</Text>
|
||||
<Button
|
||||
onPress={[Function anonymous]}
|
||||
type=\\"primary\\"
|
||||
>
|
||||
Change freshness!
|
||||
</Button>
|
||||
</View>"
|
||||
`;
|
||||
|
||||
exports[`debug: with message 1`] = `
|
||||
"my custom message
|
||||
|
||||
<View>
|
||||
<Text>
|
||||
Is the banana fresh?
|
||||
</Text>
|
||||
<Text
|
||||
testID=\\"bananaFresh\\"
|
||||
>
|
||||
not fresh
|
||||
</Text>
|
||||
<View
|
||||
accessible={true}
|
||||
isTVSelectable={true}
|
||||
onResponderGrant={[Function bound touchableHandleResponderGrant]}
|
||||
onResponderMove={[Function bound touchableHandleResponderMove]}
|
||||
onResponderRelease={[Function bound touchableHandleResponderRelease]}
|
||||
onResponderTerminate={[Function bound touchableHandleResponderTerminate]}
|
||||
onResponderTerminationRequest={[Function bound touchableHandleResponderTerminationRequest]}
|
||||
onStartShouldSetResponder={[Function bound touchableHandleStartShouldSetResponder]}
|
||||
style={
|
||||
Object {
|
||||
\\"opacity\\": 1,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text>
|
||||
Change freshness!
|
||||
</Text>
|
||||
</View>
|
||||
</View>"
|
||||
`;
|
||||
|
||||
exports[`toJSON 1`] = `
|
||||
<View
|
||||
accessible={true}
|
||||
|
||||
@@ -4,6 +4,7 @@ import React from 'react';
|
||||
import { TouchableOpacity, Text } from 'react-native';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
import { debug, render, fireEvent, flushMicrotasksQueue } from '..';
|
||||
import debugShallow from '../helpers/debugShallow';
|
||||
|
||||
function TextComponent({ text }) {
|
||||
return <Text>{text}</Text>;
|
||||
@@ -43,11 +44,11 @@ test('debug', () => {
|
||||
|
||||
debug(component, 'test message');
|
||||
|
||||
expect(console.log).toBeCalledWith(output, 'test message');
|
||||
expect(console.log).toBeCalledWith('test message\n\n', output);
|
||||
});
|
||||
|
||||
test('debug.shallow', () => {
|
||||
expect(debug.shallow).toBe(debug);
|
||||
expect(debug.shallow).toBe(debugShallow);
|
||||
});
|
||||
|
||||
test('debug.deep', () => {
|
||||
@@ -65,7 +66,7 @@ test('debug.deep', () => {
|
||||
|
||||
debug.deep(component, 'test message');
|
||||
|
||||
expect(console.log).toBeCalledWith(output, 'test message');
|
||||
expect(console.log).toBeCalledWith('test message\n\n', output);
|
||||
});
|
||||
|
||||
test('debug.deep async test', async () => {
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import React from 'react';
|
||||
import { View, Text, TouchableOpacity } from 'react-native';
|
||||
import { render } from '..';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
import { render, fireEvent } from '..';
|
||||
|
||||
class Button extends React.Component<*> {
|
||||
render() {
|
||||
@@ -186,3 +187,43 @@ test('toJSON', () => {
|
||||
|
||||
expect(toJSON()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('debug', () => {
|
||||
jest.spyOn(console, 'log').mockImplementation(x => x);
|
||||
|
||||
const { debug } = render(<Banana />);
|
||||
|
||||
debug();
|
||||
debug('my custom message');
|
||||
debug.shallow();
|
||||
debug.shallow('my other custom message');
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
const mockCalls = console.log.mock.calls;
|
||||
|
||||
expect(stripAnsi(mockCalls[0][0])).toMatchSnapshot();
|
||||
expect(stripAnsi(mockCalls[1][0] + mockCalls[1][1])).toMatchSnapshot(
|
||||
'with message'
|
||||
);
|
||||
expect(stripAnsi(mockCalls[2][0])).toMatchSnapshot('shallow');
|
||||
expect(stripAnsi(mockCalls[3][0] + mockCalls[3][1])).toMatchSnapshot(
|
||||
'shallow with message'
|
||||
);
|
||||
});
|
||||
|
||||
test('debug changing component', () => {
|
||||
jest.spyOn(console, 'log').mockImplementation(x => x);
|
||||
|
||||
const { getByProps, debug } = render(<Banana />);
|
||||
|
||||
fireEvent.press(getByProps({ type: 'primary' }));
|
||||
|
||||
debug();
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
const mockCalls = console.log.mock.calls;
|
||||
|
||||
expect(stripAnsi(mockCalls[4][0])).toMatchSnapshot(
|
||||
'bananaFresh button message should now be "fresh"'
|
||||
);
|
||||
});
|
||||
|
||||
+15
-25
@@ -1,26 +1,15 @@
|
||||
// @flow
|
||||
/* eslint-disable no-console */
|
||||
import * as React from 'react';
|
||||
import prettyFormat, { plugins } from 'pretty-format';
|
||||
import shallow from './shallow';
|
||||
import render from './render';
|
||||
|
||||
/**
|
||||
* Log pretty-printed shallow test component instance
|
||||
*/
|
||||
function debugShallow(
|
||||
instance: ReactTestInstance | React.Element<*>,
|
||||
message?: any
|
||||
) {
|
||||
const { output } = shallow(instance);
|
||||
|
||||
console.log(format(output), message || '');
|
||||
}
|
||||
import debugShallow from './helpers/debugShallow';
|
||||
import debugDeep from './helpers/debugDeep';
|
||||
import format from './helpers/format';
|
||||
|
||||
/**
|
||||
* Log pretty-printed deep test component instance
|
||||
*/
|
||||
function debugDeep(
|
||||
function debugDeepElementOrInstance(
|
||||
instance: React.Element<*> | ?ReactTestRendererJSON,
|
||||
message?: any = ''
|
||||
) {
|
||||
@@ -29,21 +18,22 @@ function debugDeep(
|
||||
// rendering ?ReactTestRendererJSON
|
||||
// $FlowFixMe
|
||||
const { toJSON } = render(instance);
|
||||
console.log(format(toJSON()), message);
|
||||
if (message) {
|
||||
console.log(`${message}\n\n`, format(toJSON()));
|
||||
} else {
|
||||
console.log(format(toJSON()));
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(format(instance), message);
|
||||
// $FlowFixMe
|
||||
debugDeep(instance);
|
||||
}
|
||||
}
|
||||
|
||||
const format = input =>
|
||||
prettyFormat(input, {
|
||||
plugins: [plugins.ReactTestComponent, plugins.ReactElement],
|
||||
highlight: true,
|
||||
});
|
||||
|
||||
const debug = debugShallow;
|
||||
function debug(instance: ReactTestInstance | React.Element<*>, message?: any) {
|
||||
return debugShallow(instance, message);
|
||||
}
|
||||
|
||||
debug.shallow = debugShallow;
|
||||
debug.deep = debugDeep;
|
||||
debug.deep = debugDeepElementOrInstance;
|
||||
|
||||
export default debug;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
// @flow
|
||||
import format from './format';
|
||||
|
||||
/**
|
||||
* Log pretty-printed deep test component instance
|
||||
*/
|
||||
export default function debugDeep(
|
||||
instance: ?ReactTestRendererJSON,
|
||||
message?: any = ''
|
||||
) {
|
||||
if (message) {
|
||||
console.log(`${message}\n\n`, format(instance));
|
||||
} else {
|
||||
console.log(format(instance));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import shallow from '../shallow';
|
||||
import format from './format';
|
||||
|
||||
/**
|
||||
* Log pretty-printed shallow test component instance
|
||||
*/
|
||||
export default function debugShallow(
|
||||
instance: ReactTestInstance | React.Element<*>,
|
||||
message?: any
|
||||
) {
|
||||
const { output } = shallow(instance);
|
||||
|
||||
if (message) {
|
||||
console.log(`${message}\n\n`, format(output));
|
||||
} else {
|
||||
console.log(format(output));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// @flow
|
||||
import prettyFormat, { plugins } from 'pretty-format';
|
||||
|
||||
const format = (input: ?ReactTestRendererJSON) =>
|
||||
prettyFormat(input, {
|
||||
plugins: [plugins.ReactTestComponent, plugins.ReactElement],
|
||||
highlight: true,
|
||||
});
|
||||
|
||||
export default format;
|
||||
@@ -3,6 +3,8 @@ import * as React from 'react';
|
||||
import TestRenderer from 'react-test-renderer'; // eslint-disable-line import/no-extraneous-dependencies
|
||||
import { getByAPI } from './helpers/getByAPI';
|
||||
import { queryByAPI } from './helpers/queryByAPI';
|
||||
import debugShallow from './helpers/debugShallow';
|
||||
import debugDeep from './helpers/debugDeep';
|
||||
|
||||
/**
|
||||
* Renders test component deeply using react-test-renderer and exposes helpers
|
||||
@@ -21,5 +23,14 @@ export default function render(
|
||||
update: renderer.update,
|
||||
unmount: renderer.unmount,
|
||||
toJSON: renderer.toJSON,
|
||||
debug: debug(instance, renderer),
|
||||
};
|
||||
}
|
||||
|
||||
function debug(instance: ReactTestInstance, renderer) {
|
||||
function debugImpl(message?: string) {
|
||||
return debugDeep(renderer.toJSON(), message);
|
||||
}
|
||||
debugImpl.shallow = message => debugShallow(instance, message);
|
||||
return debugImpl;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,8 @@ const queryAllByTextRegExp: Array<ReactTestInstance> = tree.queryAllByText(
|
||||
const queryAllByProps: Array<ReactTestInstance> = tree.getAllByProps({
|
||||
value: 2,
|
||||
});
|
||||
const debugFn = tree.debug()
|
||||
const debugFnWithMessage = tree.debug('my message')
|
||||
|
||||
tree.update(<View />);
|
||||
tree.unmount();
|
||||
|
||||
Vendored
+1
@@ -35,6 +35,7 @@ export interface RenderAPI extends GetByAPI, QueryByAPI {
|
||||
update(nextElement: React.ReactElement<any>): void;
|
||||
unmount(nextElement?: React.ReactElement<any>): void;
|
||||
toJSON(): ReactTestRendererJSON | null;
|
||||
debug(message?: string): void;
|
||||
}
|
||||
|
||||
export type FireEventFunction = (
|
||||
|
||||
Reference in New Issue
Block a user