feat: add waitForElement (#20)

* feat: add waitForElement

* add docs

* update docs
This commit is contained in:
Michał Pierzchała
2018-10-10 21:06:06 +02:00
committed by Kacper Wiszczuk
parent 254127781c
commit d5219d2e93
5 changed files with 147 additions and 2 deletions
+26
View File
@@ -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.
+1 -2
View File
@@ -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>
+92
View File
@@ -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();
});
+2
View File
@@ -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 };
+26
View File
@@ -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);
});
}