Lattice Grid

blog

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, so selectRange performs 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, so waitForIdle rests 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 a rowCount expectation 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.

Read next

  • Filter a database grid by picking values, 1.113

    Over a database source, the header filter's Equals picker now lists the values the database holds, capped and searchable, so nobody has to guess a spelling.

  • Line Chart for a Monthly Revenue Trend

    A JavaScript line chart bound to a data grid, with twelve months of revenue per product line and a filter that redraws the lines. Live, with code.

  • Pan the map and the grid follows, 1.112

    The map view can now write what it shows as the grid's own filter, so the rows, the KPI tiles and the charts follow every pan and zoom in 1.112.

All posts RSS