Testing your grid
@freegridjs/testing reads cells, edits, sorts and presses keys like a user in jsdom, happy-dom or a real browser; mockLayout makes jsdom draw rows; selectors serve Playwright and Cypress.
Tests of a screen with a grid tend to break on the grid's inner DOM, or show no rows at all in jsdom. @freegridjs/testing drives the grid the way a user does — double-click, type, Enter, click a header — and reads back what is on screen.
npm i -D @freegridjs/testingUnit tests (Vitest, Jest)
import { beforeAll, expect, it } from 'vitest';
import { createGrid } from '@freegridjs/core';
import { gridHarness, mockLayout } from '@freegridjs/testing';
// jsdom has no layout: without this every element is 0 × 0 and no rows are drawn
beforeAll(() => { mockLayout(); });
it('refuses a quantity over 99 and saves a good one', async () => {
const host = document.createElement('div');
document.body.append(host);
const api = createGrid(host, { columns, data });
const grid = gridHarness(host);
await grid.waitForRows();
expect(grid.headers()).toEqual(['Name', 'Qty', 'City']);
expect(grid.edit(0, 'qty', '500').ok).toBe(false); // the editor stays open with the message
grid.press('Escape');
expect(grid.edit(0, 'qty', '42')).toEqual({ ok: true });
await grid.settle();
expect(grid.cellText(0, 'qty')).toBe('42');
expect(api.getModifiedRows().updated).toHaveLength(1);
grid.clickHeader('qty'); // sort
await grid.settle();
expect(grid.column('qty')).toEqual(['1', '2', '42']);
});| Name | Type | Description |
|---|---|---|
mockLayout({ width?, height? }) | () => void | Give elements a size in jsdom / happy-dom (default 1024 × 600, about 20 rows) and silence the missing canvas. Returns a function that undoes it. |
gridHarness(container) | GridHarness | Helpers for the grid inside an element: the one given to createGrid, a component's host, or the grid root. |
cell(r, colId) / cellText(r, colId) | HTMLElement | null / string | A drawn cell and its text; null / empty when it is not on screen. |
rows(colIds?) / column(colId) | string[][] / string[] | Texts of the drawn rows in display order. |
headers() / columnIds() / rowCount() | string[] / string[] / number | Header labels and ids of the shown columns; number of drawn rows. |
click / dblclick / clickHeader / press | void | Pointer and key events like a user; press goes to the grid as if it had the focus. |
edit(r, colId, text, finish?) | { ok, message? } | Double-click, replace the text, then Enter (or 'Tab', 'Escape'). ok is true only when the value was saved: false after 'Escape', and false with the message when a rule refuses the value. For text, number, date and mask editors. |
waitForRows(count?, timeout?) / settle() | Promise<void> | Wait for rows to be drawn, or for the next paint. |
With React, Vue, Svelte or Angular
Render the component as usual and give gridHarness the element around it.
import { render } from '@testing-library/react';
import { FreeGrid } from '@freegridjs/react';
import { gridHarness, mockLayout } from '@freegridjs/testing';
beforeAll(() => { mockLayout(); });
it('shows the orders', async () => {
const { container } = render(<FreeGrid columns={columns} data={orders} />);
const grid = gridHarness(container);
await grid.waitForRows(3);
expect(grid.column('customer.name')).toEqual(['Kim', 'Lee', 'Park']);
});Browser tests (Playwright, Cypress)
In a real browser use the selectors, which follow the grid if its inner structure changes:
import { selectors } from '@freegridjs/testing';
await page.locator(selectors.cell(0, 'qty')).dblclick();
await page.keyboard.type('42');
await page.keyboard.press('Enter');
await expect(page.locator(selectors.cell(0, 'qty'))).toHaveText('42');
await page.locator(selectors.header('qty')).click(); // sort
// Cypress
cy.get(selectors.cell(0, 'name')).should('have.text', 'Kim');| selectors. | Matches |
|---|---|
| root | The grid root |
| cell(rowIndex, colId) / row(rowIndex) | A body cell / row; rowIndex is the display position |
| header(colId) / headerLabel | A header cell / the label inside it |
| editor / editorMessage | The open editor box / its message |
| filterInput(colId) / overlay | A filter row box / the "no rows" and loading layer |
Only rows on screen are drawn, also in tests. To check data further down, read it from the API (getDisplayedRows, getCellValue) or scroll with scrollToRow first. Picker editors (select, combo, calendar) are easiest to set with api.setCellValue in unit tests; in a browser, click their options.
Questions about this page
Why does my grid show no rows in a Jest or Vitest test?
jsdom has no layout, so every element is 0 pixels high and the grid draws no rows. Call mockLayout() from @freegridjs/testing before the test (or give elements a size yourself), then wait for the rows with gridHarness(el).waitForRows().
How do I find a cell in Playwright or Cypress?
Use selectors.cell(rowIndex, colId) from @freegridjs/testing, for example page.locator(selectors.cell(0, 'name')). Row indexes are display positions, after sorting and filtering.