mirror of
https://github.com/callstack/react-native-testing-library.git
synced 2026-09-18 23:09:04 +08:00
feat: add waitForElement (#20)
* feat: add waitForElement * add docs * update docs
This commit is contained in:
committed by
Kacper Wiszczuk
parent
254127781c
commit
d5219d2e93
@@ -254,6 +254,32 @@ const { getByTestId } = render(
|
||||
fireEvent.scroll(getByTestId('scroll-view'), eventData);
|
||||
```
|
||||
|
||||
## `waitForElement`
|
||||
|
||||
Defined as:
|
||||
|
||||
```jsx
|
||||
function waitForExpect<T: ReactTestInstance>(
|
||||
expectation: () => T,
|
||||
timeout: number = 4500,
|
||||
interval: number = 50
|
||||
): Promise<T> {}
|
||||
```
|
||||
|
||||
Wait for non-deterministic periods of time until your element appears or times out. `waitForExpect` periodically calls `expectation` every `interval` milliseconds to determine whether the element appeared or not.
|
||||
|
||||
```jsx
|
||||
import { render, waitForElement } from 'react-testing-library';
|
||||
|
||||
test('waiting for an Banana to be ready', async () => {
|
||||
const { getByText } = render(<Banana />);
|
||||
|
||||
await waitForElement(() => getByText('Banana ready'));
|
||||
});
|
||||
```
|
||||
|
||||
If you're using Jest's [Timer Mocks](https://jestjs.io/docs/en/timer-mocks#docsNav), remember not to use `async/await` syntax as it will stall your tests.
|
||||
|
||||
## `debug`
|
||||
|
||||
Log prettified shallowly rendered component or test instance (just like snapshot) to stdout.
|
||||
|
||||
@@ -7,8 +7,7 @@ import {
|
||||
ScrollView,
|
||||
TextInput,
|
||||
} from '../__mocks__/reactNativeMock';
|
||||
import fireEvent from '../fireEvent';
|
||||
import { render } from '..';
|
||||
import { render, fireEvent } from '..';
|
||||
|
||||
const OnPressComponent = ({ onPress }) => (
|
||||
<View>
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
// @flow
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
import React from 'react';
|
||||
import { View, Text, TouchableOpacity } from '../__mocks__/reactNativeMock';
|
||||
import { render, fireEvent, waitForElement } from '..';
|
||||
|
||||
class Banana extends React.Component<*, *> {
|
||||
changeFresh = () => {
|
||||
this.props.onChangeFresh();
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View>
|
||||
{this.props.fresh && <Text testID="fresh">Fresh</Text>}
|
||||
<TouchableOpacity onPress={this.changeFresh} type="primary">
|
||||
Change freshness!
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BananaContainer extends React.Component<*, *> {
|
||||
state = { fresh: false };
|
||||
|
||||
onChangeFresh = async () => {
|
||||
await new Promise(resolve => setTimeout(resolve, 300));
|
||||
this.setState({ fresh: true });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Banana onChangeFresh={this.onChangeFresh} fresh={this.state.fresh} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
test('waits for element until it stops throwing', async () => {
|
||||
const { getByTestId, getByName, queryByTestId } = render(<BananaContainer />);
|
||||
|
||||
fireEvent.press(getByName('TouchableOpacity'));
|
||||
|
||||
expect(queryByTestId('fresh')).toBeNull();
|
||||
|
||||
const freshBananaText = await waitForElement(() => getByTestId('fresh'));
|
||||
|
||||
expect(freshBananaText.props.children).toBe('Fresh');
|
||||
});
|
||||
|
||||
test('waits for element until timeout is met', async () => {
|
||||
const { getByTestId, getByName } = render(<BananaContainer />);
|
||||
|
||||
fireEvent.press(getByName('TouchableOpacity'));
|
||||
|
||||
await expect(
|
||||
waitForElement(() => getByTestId('fresh'), 100)
|
||||
).rejects.toThrow();
|
||||
});
|
||||
|
||||
test('waits for element with custom interval', async () => {
|
||||
const mockFn = jest.fn(() => {
|
||||
throw Error('test');
|
||||
});
|
||||
|
||||
try {
|
||||
await waitForElement(() => mockFn(), 400, 200);
|
||||
} catch (e) {
|
||||
// suppress
|
||||
}
|
||||
|
||||
expect(mockFn).toBeCalledTimes(3);
|
||||
});
|
||||
|
||||
test('works with fake timers', async () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
const mockFn = jest.fn(() => {
|
||||
throw Error('test');
|
||||
});
|
||||
|
||||
try {
|
||||
waitForElement(() => mockFn(), 400, 200);
|
||||
} catch (e) {
|
||||
// suppress
|
||||
}
|
||||
jest.runTimersToTime(400);
|
||||
|
||||
expect(mockFn).toBeCalledTimes(3);
|
||||
|
||||
jest.useRealTimers();
|
||||
});
|
||||
@@ -4,9 +4,11 @@ import shallow from './shallow';
|
||||
import flushMicrotasksQueue from './flushMicrotasksQueue';
|
||||
import debug from './debug';
|
||||
import fireEvent from './fireEvent';
|
||||
import waitForElement from './waitForElement';
|
||||
|
||||
export { render };
|
||||
export { shallow };
|
||||
export { flushMicrotasksQueue };
|
||||
export { debug };
|
||||
export { fireEvent };
|
||||
export { waitForElement };
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
// @flow
|
||||
export default function waitForExpect<T: ReactTestInstance>(
|
||||
expectation: () => T,
|
||||
timeout: number = 4500,
|
||||
interval: number = 50
|
||||
) {
|
||||
const startTime = Date.now();
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
const rejectOrRerun = error => {
|
||||
if (Date.now() - startTime >= timeout) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
setTimeout(runExpectation, interval);
|
||||
};
|
||||
function runExpectation() {
|
||||
try {
|
||||
const result = expectation();
|
||||
resolve(result);
|
||||
} catch (error) {
|
||||
rejectOrRerun(error);
|
||||
}
|
||||
}
|
||||
setTimeout(runExpectation, 0);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user