# Testing AdapTable

Canonical page: https://www.adaptabletools.com/docs/getting-started-testing

- AdapTable is fully testable and is framework- and runner-agnostic
- Drive tests through the [Adaptable API](https://www.adaptabletools.com/docs/technical-reference-adaptable-api/index.md) after the [Adaptable Ready Event](https://www.adaptabletools.com/docs/getting-started-adaptable-ready/index.md) has fired
- Stub State persistence and isolate each test so runs stay deterministic
- The recommended starting point is the <a href="https://github.com/AdaptableTools/testing-template-vitest-adaptable-react-aggrid" target="_blank">AdapTable React Vitest Template</a>

AdapTable is a standard JavaScript / TypeScript library.

There is no AdapTable-specific test framework — use whatever your application already uses.

The fastest, most reliable tests talk to AdapTable through the API.

Reach for end-to-end browser tests only when you specifically need to exercise the UI.

- Fork the [React Vitest template](https://github.com/AdaptableTools/testing-template-vitest-adaptable-react-aggrid) if you want a runnable suite immediately
- The rest of this page is the practice that template follows

## Choosing a Test Runner

| Runner                                                           | Notes                                                                                                                                                                                           |
| ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Vitest](https://vitest.dev/)                                    | Fast and modern, drop-in compatible with the Jest API; the runner used in the public [React testing template](https://github.com/AdaptableTools/testing-template-vitest-adaptable-react-aggrid) |
| [Jest](https://jestjs.io/)                                       | Widely used; works equally well for unit and integration tests against the Adaptable API                                                                                                        |
| [Testing Library](https://testing-library.com/) | Pairs cleanly with Vitest or Jest when rendering AdapTable inside a component test                                                                                                              |
| [Playwright](https://playwright.dev/)                            | Recommended for end-to-end tests that drive the actual browser UI; this is what we use internally                                                                                               |

- API tests are the default: apply a Layout, run a search, edit a cell, then assert through the API
- Component tests are useful when you render AdapTable inside your own wrapper
- End-to-end tests are for Dashboard clicks, Settings Panel, menus etc — not for proving a Filter or Layout applied

## Wait for Adaptable Ready

AdapTable initialises asynchronously.

`Adaptable.init` (vanilla) and framework wrappers all expose the instance through [Adaptable Ready Event](https://www.adaptabletools.com/docs/getting-started-adaptable-ready/index.md).

Wait for that Event before asserting on Grid state or calling API methods.

Do **not** use AG Grid's `gridReady` event for this — it fires **before** AdapTable has initialised

<!-- framework: vanilla -->
### Vanilla

`Adaptable.init` already returns `adaptableApi`. 

The Ready Event is replayed if you subscribe after `init` has resolved, so this helper is enough:

```ts
async function createAdaptableForTest(
  adaptableOptions: AdaptableOptions,
  agGridConfig: AgGridConfig
): Promise<AdaptableApi> {
  const adaptableApi = await Adaptable.init(adaptableOptions, agGridConfig);

  await new Promise<void>(resolve => {
    adaptableApi.eventApi.on('AdaptableReady', () => resolve());
  });

  return adaptableApi;
}
```

<!-- /framework -->

<!-- framework: react -->
### React

In AdapTable React, wait for `onAdaptableReady` on `Adaptable.Provider` (or read `useAdaptableApi()` from a child once it is no longer `null`).

```tsx
function renderAdaptable(adaptableOptions: AdaptableOptions) {
  let adaptableApi: AdaptableApi | undefined;

  render(
    <Adaptable.Provider
      gridOptions={gridOptions}
      modules={agGridModules}
      adaptableOptions={adaptableOptions}
      onAdaptableReady={({adaptableApi: api}) => {
        adaptableApi = api;
      }}>
      <Adaptable.UI />
      <Adaptable.AgGridReact />
    </Adaptable.Provider>
  );

  return waitFor(() => {
    expect(adaptableApi).toBeDefined();
  }).then(() => adaptableApi!);
}
```

<!-- /framework -->

<!-- framework: angular -->
### Angular

Bind `(adaptableReady)` on `<adaptable-provider>` and resolve the test setup from that handler — the same pattern as [Adaptable Ready](https://www.adaptabletools.com/docs/getting-started-adaptable-ready/index.md).

<!-- /framework -->

<!-- framework: vue -->
### Vue

Listen with `@onAdaptableReady` on `AdaptableProvider` and resolve the test setup from that handler — the same pattern as [Adaptable Ready](https://www.adaptabletools.com/docs/getting-started-adaptable-ready/index.md).

<!-- /framework -->

## Drive the Adaptable API

The [Adaptable API](https://www.adaptabletools.com/docs/technical-reference-adaptable-api/index.md) is the primary test surface.

Anything a user can do in the UI can be done programmatically — applying Filters, switching Layouts, running Exports, editing cells.

This keeps assertions concise and avoids brittle DOM selectors.

```ts
const adaptableApi = await createAdaptableForTest(
  adaptableOptions,
  agGridConfig
);

adaptableApi.layoutApi.setLayout('Cars');
expect(adaptableApi.layoutApi.getCurrentLayoutName()).toBe('Cars');

adaptableApi.quickSearchApi.runQuickSearch('toy');
expect(adaptableApi.quickSearchApi.getQuickSearchValue()).toBe('toy');
```

Each test still needs a valid [Initial Adaptable State](https://www.adaptabletools.com/docs/dev-guide-adaptable-state-initial-state/index.md) — including a `Layout` section with at least one Layout, unless `autoCreateLayout` is set — see [Default Layouts](https://www.adaptabletools.com/docs/handbook-layouts-default/index.md)

## Isolate State

By default AdapTable persists to localStorage under `adaptableStateKey` (which defaults to `adaptableId`). Shared persistence is the usual reason tests leak into each other.

Stub **both** `loadState` and `persistState` (provide them together so localStorage is not used on one side):

```ts
const adaptableOptions: AdaptableOptions = {
  primaryKey: 'id',
  adaptableId: 'layout-test',
  adaptableStateKey: `layout-test-${Date.now()}`,
  initialState,
  stateOptions: {
    loadState: () => Promise.resolve({}),
    persistState: () => Promise.resolve(),
  },
};
```

- Returning `{}` from `loadState` means each test starts from Initial State
- Return a fixture object from `loadState` when the test should start from a known User State
- Give each test its own `adaptableStateKey` (or `adaptableId`) if anything still writes to storage

<!-- framework: vanilla -->
### Vanilla

Call `adaptableApi.destroy()` when the test finishes so the Grid and listeners are torn down.

```ts
afterEach(() => {
  adaptableApi?.destroy();
});
```

<!-- /framework -->

<!-- framework: react -->
### React

Unmount the component (Testing Library `cleanup` / `unmount`) — the React wrapper destroys AdapTable as part of that lifecycle.

<!-- /framework -->

<!-- framework: angular -->
### Angular

Destroy the fixture / unmount the component — the Angular wrapper handles AdapTable cleanup.

<!-- /framework -->

<!-- framework: vue -->
### Vue

Unmount the wrapper — the Vue wrapper handles AdapTable cleanup.

<!-- /framework -->

## Asserting Something Changed

Prefer API getters after you invoked an API method.

When the change is triggered by UI or by another async path, subscribe to the [Adaptable State Changed Event](https://www.adaptabletools.com/docs/dev-guide-adaptable-state-events/index.md) instead of polling:

```ts
const layoutChanged = new Promise<void>(resolve => {
  adaptableApi.eventApi.on(
    'AdaptableStateChanged',
    (info: AdaptableStateChangedInfo) => {
      if (info.actionName === 'LAYOUT_SELECT') {
        resolve();
      }
    }
  );
});

adaptableApi.layoutApi.setLayout('Cars');
await layoutChanged;
```

- For data edits and row add / delete, use the [Cell Changed](https://www.adaptabletools.com/docs/technical-reference-editing/index.md) and [Row Changed](https://www.adaptabletools.com/docs/handbook-managing-grid-data/index.md) Events
- Change data through the [AdapTable Grid API](https://www.adaptabletools.com/docs/handbook-managing-grid-data/index.md) so those Events fire

## End-to-end Tests

Use Playwright (or similar) when the thing under test _is_ the UI: a Dashboard button, a Settings Panel page, a Column Menu item.

Keep assertions on visible outcomes, not on AdapTable internals.

Where you already have `adaptableApi` in the page (for example from `onAdaptableReady`), you can still assert through the API after the click

## Test Template

The [AdapTable React Vitest Template](https://github.com/AdaptableTools/testing-template-vitest-adaptable-react-aggrid) is a complete, runnable repository that shows:

- a minimal React + AG Grid + AdapTable setup
- a Vitest configuration that handles AdapTable's async initialisation
- example tests that wait for Adaptable Ready and then assert against the Adaptable API

Fork it as a starting point for your own test suite.

### FAQ

**Do I need a special AdapTable test package?**
No. Use your existing runner. The Adaptable API is the test surface.

**Should each test use a different `adaptableId`?**
Use a unique `adaptableStateKey` (or stub `loadState` / `persistState` so nothing hits localStorage). A unique `adaptableId` also works, and makes console output easier to read.

**Why do my tests affect each other?**
Almost always leftover localStorage, or a vanilla instance that was not `destroy`ed. Stub persistence and tear down each instance.

**Can I query the DOM instead of using the API?**
You can, but selectors against AdapTable internals are brittle. 
Use API for behaviour; use the DOM only for UI you own (custom toolbars, your wrappers) or true end-to-end coverage.

**Do tests need license keys?**
Yes, the same as a running app. Without an AdapTable `licenseKey` the Grid shows a watermark and runs with reduced functionality — which will fail assertions that depend on those features.
