mirror of
https://github.com/trezor/trezor-suite.git
synced 2026-03-04 14:35:29 +01:00
291 lines
8.8 KiB
Markdown
291 lines
8.8 KiB
Markdown
# @suite-native/test-utils
|
|
|
|
This package is wrapper around `@testing-library/react-native` that provides some custom utilities for testing React
|
|
Native components in the Suite Native project.
|
|
It also reexports everything from `@testing-library/react-native`, so you should use it as a drop-in replacement.
|
|
|
|
## Testing components
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A{{Do you need redux store?}} -- Yes --> B[Use 'renderWithStoreProvider']
|
|
A -- No --> C{{Do you need any other provider? Such as theme, formatters, etc.}}
|
|
C -- Yes --> D[Use 'renderWithBasicProvider']
|
|
C -- No --> E{{Are you sure?}}
|
|
E -- Yes --> F[Use 'render']
|
|
E -- No --> D
|
|
```
|
|
|
|
### Using render
|
|
|
|
`render` function is just re-exported from `@testing-library/react-native`. For more information, please refer to
|
|
the [official documentation](https://testing-library.com/docs/react-native-testing-library/intro/).
|
|
|
|
You most probably do not want to use this function directly.
|
|
|
|
### Using renderWithBasicProvider
|
|
|
|
`renderWithBasicProvider` is a custom utility that provides basic context providers. Namely:
|
|
|
|
- **IntlProvider**: Simplified variant for tests. Always uses `en-US` locale.
|
|
- **StylesProvider**: With `colorVariant` set to `standard`.
|
|
- **NavigationContainer**
|
|
- **NativeServicesProvider**: With `extraDependenciesNativeMock.services`.
|
|
- **FormatterProvider**: With `locale` set to `en` and `baseCurrency` set to `USD`.
|
|
- **BottomSheetModalProvider**
|
|
|
|
#### Usage example
|
|
|
|
Basic usage
|
|
|
|
```tsx
|
|
import { renderWithBasicProvider, userEvent } from '@suite-native/test-utils';
|
|
|
|
describe('Counter', () => {
|
|
it('should start with 0 value', () => {
|
|
const { getByLabelText } = renderWithBasicProvider(<Counter />);
|
|
|
|
expect(getByLabelText('Counter value')).toHaveTextContent('0');
|
|
});
|
|
|
|
it('should increment value on button press', async () => {
|
|
const { getByLabelText, getByText } = renderWithBasicProvider(<Counter />);
|
|
|
|
await userEvent.press(getByText('+'));
|
|
|
|
expect(getByLabelText('Counter value')).toHaveTextContent('1');
|
|
});
|
|
});
|
|
```
|
|
|
|
### Using renderWithStoreProvider
|
|
|
|
`renderWithStoreProvider` is a custom utility that provides all the context providers from `renderWithBasicProvider`
|
|
plus:
|
|
|
|
- **Redux store provider**
|
|
- Formatters config is now loaded from store instead of being hardcoded.
|
|
- Allows to specify custom store or preloaded state.
|
|
|
|
#### Usage with preloaded state
|
|
|
|
Use when you do not need to access the store directly, but you want to set some initial state for your component.
|
|
For example, you want to test how your component renders with some specific value from the store.
|
|
|
|
```tsx
|
|
import {
|
|
type PreloadedState,
|
|
initStore,
|
|
renderWithStoreProvider,
|
|
userEvent,
|
|
} from '@suite-native/test-utils';
|
|
|
|
describe('Counter', () => {
|
|
const renderCounter = (preloadedState: PreloadedState) =>
|
|
renderWithStoreProvider(<Counter />, { preloadedState });
|
|
|
|
it('should render correct value on 1st render', () => {
|
|
const preloadedState: PreloadedState = {
|
|
counter: { value: 1 },
|
|
};
|
|
const { getByLabelText } = renderCounter(preloadedState);
|
|
|
|
expect(getByLabelText('Counter value')).toHaveTextContent('1');
|
|
});
|
|
|
|
it('should increment value on button press', async () => {
|
|
const preloadedState: PreloadedState = {
|
|
counter: { value: 1 },
|
|
};
|
|
const { getByLabelText, getByText } = renderCounter(preloadedState);
|
|
|
|
await userEvent.press(getByText('+'));
|
|
|
|
expect(getByLabelText('Counter value')).toHaveTextContent('2');
|
|
});
|
|
});
|
|
```
|
|
|
|
#### Usage with custom store
|
|
|
|
Use when you need to access the store directly in your test.
|
|
For example, you want to check if some action was dispatched or if the state was updated correctly.
|
|
You have also possibility to dispatch an action by yourself.
|
|
|
|
```tsx
|
|
import {
|
|
type TestStore,
|
|
act,
|
|
initStore,
|
|
renderWithStoreProvider,
|
|
userEvent,
|
|
} from '@suite-native/test-utils';
|
|
|
|
describe('Counter', () => {
|
|
let store: TestStore;
|
|
|
|
beforeEach(() => {
|
|
({ store } = initStore({ counter: { value: 0 } }));
|
|
});
|
|
|
|
const renderCounter = () => renderWithStoreProvider(<Counter />, { store });
|
|
|
|
it('should react to state changes', () => {
|
|
const { getByLabelText } = renderCounter();
|
|
|
|
act(() => {
|
|
store.dispatch({ type: 'counter/increment' });
|
|
});
|
|
|
|
expect(getByLabelText('Counter value')).toHaveTextContent('1');
|
|
});
|
|
|
|
it('should increment value on button press', async () => {
|
|
const { getByLabelText, getByText } = renderCounter();
|
|
|
|
await userEvent.press(getByText('+'));
|
|
|
|
expect(getByLabelText('Counter value')).toHaveTextContent('1');
|
|
expect(store.getState().counter.value).toBe(1);
|
|
expect(store.getActions()).toContainEqual({ type: 'counter/increment' });
|
|
});
|
|
});
|
|
```
|
|
|
|
#### Injecting custom providers
|
|
|
|
To inject custom provider, you can use `wrapper` option of either `render`, `renderWithBasicProvider` or
|
|
`renderWithStoreProvider`.
|
|
|
|
```tsx
|
|
const renderCounterA = () =>
|
|
renderWithStoreProvider(<Counter />, { store, wrapper: MyCustomProvider });
|
|
|
|
const renderCounterB = () =>
|
|
renderWithBasicProvider(<Counter />, {
|
|
wrapper: ({ children }) => (
|
|
<MyCustomProvider someProp={someValue}>{children}</MyCustomProvider>
|
|
),
|
|
});
|
|
```
|
|
|
|
## Testing hooks
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A{{Do you need redux store?}} -- Yes --> B[Use 'renderHookWithStoreProvider']
|
|
A -- No --> C{{Do you need any other provider? Such as theme, formatters, etc.}}
|
|
C -- Yes --> D[Use 'renderHookWithBasicProvider']
|
|
C -- No --> F[Use 'renderHook']
|
|
```
|
|
|
|
### Using renderHook
|
|
|
|
`renderHook` function is just re-exported from `@testing-library/react-native`. For more information, please refer to
|
|
the [official documentation](https://testing-library.com/docs/react-native-testing-library/intro/).
|
|
|
|
You should use it when your hook does not depend on any context providers.
|
|
|
|
### Using renderHookWithBasicProvider
|
|
|
|
`renderHookWithBasicProvider` is a custom utility that provides the same context providers as `renderWithBasicProvider`.
|
|
For more information, please refer to the section about `renderWithBasicProvider`.
|
|
|
|
#### Usage example
|
|
|
|
```ts
|
|
import { act, renderHookWithBasicProvider } from '@suite-native/test-utils';
|
|
|
|
describe('useCounter', () => {
|
|
const renderUseCounter = () => renderHookWithBasicProvider(() => useCounter());
|
|
|
|
it('should initialize with count 0', () => {
|
|
const { result } = renderUseCounter();
|
|
|
|
expect(result.current.count).toBe(0);
|
|
});
|
|
|
|
describe('increment', () => {
|
|
it('should increment count by 1', () => {
|
|
const { result } = renderUseCounter();
|
|
|
|
act(() => {
|
|
result.current.increment();
|
|
});
|
|
|
|
expect(result.current.count).toBe(1);
|
|
});
|
|
});
|
|
});
|
|
```
|
|
|
|
### Using renderHookWithStoreProvider
|
|
|
|
`renderHookWithStoreProvider` is a custom utility that provides the same context providers as `renderWithStoreProvider`.
|
|
For more information, please refer to the section about `renderWithStoreProvider`.
|
|
|
|
#### Usage with preloaded state
|
|
|
|
```ts
|
|
import {
|
|
type PreloadedState,
|
|
act,
|
|
initStore,
|
|
renderHookWithStoreProvider,
|
|
} from '@suite-native/test-utils';
|
|
|
|
describe('useCounter', () => {
|
|
const renderUseCounter = (store: PreloadedState) =>
|
|
renderHookWithStoreProvider(() => useCounter(), { store });
|
|
|
|
it('should initialize with count from store', () => {
|
|
const { result } = renderUseCounter({
|
|
counter: { value: 5 },
|
|
});
|
|
|
|
expect(result.current.count).toBe(5);
|
|
});
|
|
});
|
|
```
|
|
|
|
#### Usage example with store access
|
|
|
|
```ts
|
|
import { act, initStore, renderHookWithStoreProvider } from '@suite-native/test-utils';
|
|
|
|
describe('useCounter', () => {
|
|
const renderUseCounter = (store: TestStore) =>
|
|
renderHookWithStoreProvider(() => useCounter(), { store });
|
|
|
|
it('should increment count and update store', () => {
|
|
const store = initStore({ counter: { value: 0 } });
|
|
const { result } = renderUseCounter(store);
|
|
|
|
act(() => {
|
|
result.current.increment();
|
|
});
|
|
|
|
expect(result.current.count).toBe(1);
|
|
expect(store.getState().counter.value).toBe(1);
|
|
expect(store.getActions()).toContainEqual({ type: 'counter/increment' });
|
|
});
|
|
});
|
|
```
|
|
|
|
#### Injecting custom providers
|
|
|
|
To inject custom provider, you can use `wrapper` option of either `renderHook`, `renderHookWithBasicProvider` or
|
|
`renderHookWithStoreProvider`.
|
|
|
|
```tsx
|
|
const renderUseCounterA = () =>
|
|
renderHookWithStoreProvider(() => useCounter(), { store, wrapper: MyCustomProvider });
|
|
|
|
const renderUseCounterB = () =>
|
|
renderHookWithBasicProvider(() => useCounter(), {
|
|
wrapper: ({ children }) => (
|
|
<MyCustomProvider someProp={someValue}>{children}</MyCustomProvider>
|
|
),
|
|
});
|
|
```
|