# How to Use the Custom Test Renderer

This guide shows you how to use the custom render utilities provided by Ledger Wallet to test components and hooks with all necessary providers pre-configured.

> **Note:** Always prefer the custom render utilities over raw `@testing-library` render functions.

---

## How to render a component (Mobile)

Import from `@tests/test-renderer`:

```tsx
import { render, screen } from "@tests/test-renderer";
import { State } from "~/reducers/types";

const { user, store } = render(<MyComponent />, {
  overrideInitialState: (state: State) => ({
    ...state,
    accounts: {
      ...state.accounts,
      active: [mockAccount],
    },
  }),
});

// Use screen queries
expect(screen.getByText("Hello")).toBeTruthy();

// Simulate user interactions
await user.press(screen.getByText("Submit"));
```

## How to render a component (Desktop)

Import from `tests/testSetup`:

```tsx
import { render, screen } from "tests/testSetup";

const { user, store, i18n } = render(<MyComponent />, {
  initialState: {
    accounts: { active: [mockAccount] },
  },
  initialRoute: "/accounts", // Optional: set initial route
});

// Use screen queries
expect(screen.getByText("Hello")).toBeInTheDocument();

// Simulate user interactions
await user.click(screen.getByRole("button", { name: "Submit" }));
```

---

## How to render a hook

### Mobile

```tsx
import { renderHook } from "@tests/test-renderer";

const { result, store } = renderHook(() => useMyCustomHook(), {
  overrideInitialState: (state) => ({
    ...state,
    settings: { ...state.settings, theme: "dark" },
  }),
});

expect(result.current.value).toBe(expectedValue);
```

### Desktop

```tsx
import { renderHook } from "tests/testSetup";

const { result, store } = renderHook(() => useMyCustomHook(), {
  initialState: {
    settings: { theme: "dark" },
  },
});

expect(result.current.value).toBe(expectedValue);
```

---

## How to customize initial state

Use `overrideInitialState` (Mobile) or `initialState` (Desktop) to set up Redux state for your tests:

- **Mobile**: Pass a function that receives the full default state and returns the modified state. You must spread the existing state to avoid replacing reducer defaults.
- **Desktop**: Pass a partial state object that is merged into the default state.

```tsx
// Mobile - function-based override
render(<MyComponent />, {
  overrideInitialState: (state: State) => ({
    ...state,
    settings: { ...state.settings, theme: "dark" },
  }),
});

// Desktop - object-based override
render(<MyComponent />, {
  initialState: {
    settings: { theme: "dark" },
  },
});
```

---

## Before vs After: why use the custom renderer

### Before (manual provider setup)

> **Caution:** This approach requires manually wrapping components with many providers:

```tsx
import { render } from "@testing-library/react-native";
import { Provider } from "react-redux";
import { ThemeProvider } from "styled-components";
import { NavigationContainer } from "@react-navigation/native";
import { FirebaseFeatureFlagsProvider } from "~/components/FirebaseFeatureFlags";
// ... many more imports

const mockStore = createMockStore({ accounts: [mockAccount] });

render(
  <Provider store={mockStore}>
    <FirebaseFeatureFlagsProvider getFeature={getFeature}>
      <NavigationContainer>
        <ThemeProvider theme={theme}>
          <I18nextProvider i18n={i18n}>
            <MyComponent />
          </I18nextProvider>
        </ThemeProvider>
      </NavigationContainer>
    </FirebaseFeatureFlagsProvider>
  </Provider>
);
```

### After (using custom renderer)

> **Note:** The custom renderer handles all provider setup automatically:

```tsx
import { render, screen } from "@tests/test-renderer";

const { user, store } = render(<MyComponent />, {
  overrideInitialState: (state) => ({
    ...state,
    accounts: { ...state.accounts, active: [mockAccount] },
  }),
});
```

---

## Mocks you no longer need

When using the custom render utilities, remove these commonly unnecessary mocks:

```tsx
// ❌ No need to mock react-i18next - I18nextProvider is included
jest.mock("react-i18next", () => ({
  useTranslation: () => ({
    t: (key) => key,
  }),
}));

// ❌ No need to mock react-redux - Provider with real store is included
jest.mock("react-redux", () => ({
  useSelector: jest.fn(),
  useStore: jest.fn(() => ({})),
}));

// ❌ No need to mock theme-related hooks
jest.mock("~/hooks/useTheme", () => ({
  useTheme: () => ({ colors: { /* ... */ } }),
}));
```

---

## See also

- [How to Write Tests](./write-tests) — Integration tests, component tests, debugging tips
- [Testing reference](../reference/testing) — Full render function API, providers included, coverage targets
- [Testing Strategy](../explanation/testing-strategy) — Why we built the custom renderer
