Test your grid without depending on its markup
Put a data grid in your app and you have to test it end to end. Find a cell. Read a row. Sort a column. Wait until the grid has settled before asserting anything. Until now that meant opening our markup, copying a class name into a selector, and hoping. Any change to the markup broke your tests, even when nothing the user sees had changed.
@toclocoinc/lattice-grid-testing is a small package that does this work for you, and it never reads a class name.
What it reads
The helpers stand on the grid’s public DOM contract: ARIA roles and data-* attributes. The package makes three promises:
- It reads only that contract. No helper references a grid class name, and the package’s own test run fails if one does.
- It sets no grid option. Point it at the element you passed to
createGrid. - It is versioned independently of the grid. Each release states, in its README, the grid versions its contract tests ran against.
A grid update that restyles its markup does not break your tests.
One core, two bindings
There is one framework-neutral core, and thin Playwright and Cypress bindings on top. The same calls work under both runners. Only the import, and each runner’s own idiom for awaiting and asserting, differs.
Playwright:
import { grid } from '@toclocoinc/lattice-grid-testing/playwright';
const g = grid(page.locator('#orders'));
await g.sort('city');
expect(await g.readCell('city', 0)).toBe('Accra');
The same test in Cypress:
import { grid } from '@toclocoinc/lattice-grid-testing/cypress';
const g = grid('#orders');
g.sort('city');
g.readCell('city', 0).should('equal', 'Accra');
Gestures are real input, not synthetic DOM events. The Playwright binding uses Playwright’s mouse and keyboard. The Cypress binding sends mouse and key events through the browser’s DevTools Protocol, so it needs a Chromium-family browser: Chrome, Edge or Electron.
When the grid has settled
Every gesture waits for the grid to settle before it returns, and waitForIdle is the same wait, available to you. In the README’s words, it resolves when, for quietFrames animation frames in a row (default 2), nothing under the root has changed and nothing carries the grid’s public busy signals (aria-busy="true", data-loading="true"), and the component has mounted. It rejects after timeout ms (default 10000) naming what was still pending.
That last part matters. A test that times out tells you what it was waiting for.
Two known gaps
Two parts of the grid’s DOM contract are not there yet, and the helpers say so rather than paper over it:
- No reader for a selected range. The grid does not yet mark range-selected cells with
aria-selected, soselectRangeperforms the gesture but nothing in this package can report which cells ended up selected. Assert on what the selection caused (an edit, a copy, a status line) until the grid carries the attribute. - No busy signal while a source loads. The grid does not yet raise
aria-busy="true"while rows are on their way, sowaitForIdlerests on the quiet-frames rule and the mount check. A slow remote source can look idle between the request and the first row; give such a test arowCountexpectation rather than trusting idleness alone.
Both are tracked on the grid side. A release of this package that can read them will say so in its README.
Install
npm i -D @toclocoinc/lattice-grid-testing
There are no runtime dependencies. @playwright/test and cypress are optional peers, so install whichever runner you use.
The testing guide lists every call, and the code is in the package repository.