# Component Tests

**Category:** Guides/Component Tests

## Design

### Overview

`Wix Patterns` provides uni driver testkits, similarly to [`@wix/design-system` testing](https://www.docs.wixdesignsystem.com/?path=/story/getting-started--testing)

#### Requirements

##### WixPatternsProvider / WixPatternsBMProvider

> This is relevant only when testing a component which **is not already** wrapped with either [`WixPatternsProvider`](./?path=/story/base-components-providers--wixpatternsprovider) or [`WixPatternsBMProvider`](./?path=/story/base-components-providers--wixpatternsbmprovider).

If your use case requires testing such component, before rendering it, wrap with one of the providers:

```typescript jsx
import { render } from '@testing-library/react';
import { WixPatternsBMProvider } from '@wix/patterns/bm';

const MyComponent = () => {};

it('test', () => {
  const MyComponentTestWrapper = () => (
    
      
    
  );

  render();
});
```


---

### Recipes

### spec-setup.ts

```tsx
import { configure } from '@testing-library/react';

configure({
  testIdAttribute: 'data-hook',
});

// To make tests run faster and avoid waiting timeouts we use `jest` fake timers
jest.useFakeTimers('legacy');
```

---

### TableTestDriver.ts

This example uses `BM Flow` testkit,  but logic applies to all environments.

```tsx
import { act, render, RenderResult } from '@testing-library/react';
import { BMTestAppContainer, testkit } from '@wix/yoshi-flow-bm/testkit';
import { InMemoryBackend } from '@wix/patterns/testkit/backend';
import { TableTestkit } from '@wix/patterns/testkit/jsdom';
import React from 'react';
import { whenRequest } from '@wix/yoshi-flow-bm/testkit/http-client';
import { WixPatternsBMProvider } from '@wix/patterns/bm';
import { wixPatternsTestProviderPropsOverrides } from '@wix/patterns/testkit/bm';

const Index = () => (
  <WixPatternsBMProvider>
    {/* render a component that uses `<Table />` here*/}
  </WixPatternsBMProvider>
);

export class TableTestDriver {
  _renderResult?: RenderResult;
  _bmTestContext?: BMTestAppContainer;

  /**
   * Here we use a utility which acts as a collection API server, and we integrate it with `HttpClientMocks` below
   * See full API for `InMemoryBackend` - ./?path=/story/testing-utils--inmemorybackend
   */
  backend = new InMemoryBackend<{ id: string; name: string }>({
    createOne: (index) => ({
      id: String(index),
      name: `Item ${index}`,
    }),
    itemKey: (item) => item.id,
    total: 999,
  });

  getTable() {
    if (this._renderResult == null) {
      throw new Error(
        'TableTestDriver was not initialized, call render() first',
      );
    }
    return TableTestkit({
      wrapper: this._renderResult.baseElement,
      dataHook: 'products-infinite-scroll-table',
    });
  }

  render() {
    // Provide `HttpClientMocks` that uses our `InMemoryBackend`
    const { TestComponent, context } = testkit.getBMComponent(
      Index,
      wixPatternsTestProviderPropsOverrides({
        mocks: [
          whenRequest(() => ({
            url: '/api/service',
            method: 'POST',
          }))
            .reply(200, async (data: any, params: any) => {
              // transform the data payload and query params into a query that can be passed to `InMemoryBackend.fetchData`
              const query = {
                ...data,
                ...params,
              };
              return this.backend.fetchData(query);
            })
            .persist(),
        ],
      }),
    );
    this._bmTestContext = context;
    this._renderResult = render(<TestComponent />);
  }

  // A helper function that makes pending timers to execute
  async flush(iterations = 3) {
    for (let i = 0; i < iterations; i++) {
      await act(async () => {
        await new Promise(setImmediate);
        jest.runOnlyPendingTimers();
      });
    }
  }
}
```

---

### index-spec.ts

```tsx
import { testkit } from '@wix/yoshi-flow-bm/testkit';
import { TableTestDriver } from './TableTestDriver';

describe('ProductsTable', () => {
  testkit.beforeAndAfter();

  let driver: TableTestDriver;

  beforeEach(async () => {
    driver = new TableTestDriver();
    driver.render();
    await driver.flush(5);
  });

  it('renders first page', async () => {
    expect(await driver.getTable().exists()).toBe(true);
    expect(await driver.getTable().getRowsCount()).toBe(100);
  });

  it('renders next page', async () => {
    await driver.getTable().scrollDown();
    expect(await driver.getTable().isInfiniteScrollLoaderLoading()).toBe(true);
    await driver.flush();
    expect(await driver.getTable().getRowsCount()).toBe(200);
  });

  it('filters by categories', async () => {
    const fetchSpy = jest.spyOn(driver.backend, 'fetchData');

    const ids = ['1', '2', '3'];

    await driver.getTable().selectFilterOptions('categories', ids);

    expect(fetchSpy).toHaveBeenCalledWith(
      expect.objectContaining({
        filters: expect.objectContaining({
          category: expect.arrayContaining(ids),
        }),
      }),
    );
  });
});
```

