Here we give some hints on testing AG Grid as part of your application.
End to End (e2e) Testing Copy Link
We recommend using e2e tests to validate AG Grid as part of your application.
There are a number of tools available to help with this, such as Playwright, Cypress or Selenium.
e2e tests are recommended so that AG Grid is run inside a real browser. Mocked browser environments (such as jsdom
) can be used for simple unit testing cases, but their limitations can lead to confusing test results.
End to End (e2e) Testing Examples Copy Link
There are a number of end-to-end test examples available in the AG Grid repo which demonstrate how to use Test Ids to test AG Grid. While these tests use Playwright, we do not necessarily recommend one e2e framework over another.
Test IDs Copy Link
Enabling Test IDs Copy Link
Many testing libraries allow targeting the data-testid
attribute to locate elements on the page to use for assertions or actions. AG Grid provides support for attaching this attribute to many of the interactive elements in the grid. This is achieved by calling the setupAgTestIds
function. This should only be used in a test or development environment, not in production.
// Application code
import { setupAgTestIds } from 'ag-grid-community'
if(process.env.NODE_ENV !== "production"){
// Setup test IDs for all instances of AG Grid that are created after this call.
setupAgTestIds();
}
By default the test ID attribute will be data-testid
but this can be customised via the testIdAttribute
option. This is useful if you want to use a different attribute name, for example data-customattr
.
// Application code
setupAgTestIds({ testIdAttribute: 'data-customattr' })
Using Test IDs Copy Link
WIth Test IDs enabled on your application's grids it is then possible to query elements via the agTestIdFor
helper methods.
// Test code
import { agTestIdFor } from 'ag-grid-community';
// in your test case
const rowId = 'toyota';
const colId = 'make';
const cellTestId = agTestIdFor.cell(rowId, colId);
// Using Playwright
const checkbox = page.getByTestId(cellTestId);
// Testing Library
const checkbox = findByTestId(cellTestId);
Using Test Id Wrapper Copy Link
When writing tests using Test IDs, the code can become verbose. To reduce boilerplate use the wrapAgTestIdFor
function to create a wrapper around the agTestIdFor
methods for the given test context.
// Test code
import { agTestIdFor, wrapAgTestIdFor } from 'ag-grid-community';
// Before
await expect(page.getByTestId(agTestIdFor.headerCell('toyota'))).toBeVisible();
await expect(page.getByTestId(agTestIdFor.cell('toyota', 'make'))).toBeVisible();
const agIdFor = wrapAgTestIdFor((testId) => page.getByTestId(testId));
// After
await expect(agIdFor.headerCell('toyota')).toBeVisible();
await expect(agIdFor.cell('toyota', 'make')).toBeVisible();
Configuring the Test Module Copy Link
We will walk through how you can use testing AG Grid as part of your Angular application, using default build tools provided when using the Angular CLI.
Before we can test our component we need to configure the TestBed
. In this example we have a TestHostComponent
that wraps AgGridAngular
so we pass that to the TestBed
.
@Component({
selector: 'app-grid-wrapper',
standalone: true,
imports: [AgGridAngular],
template: `<ag-grid-angular
[rowData]="rowData"
[columnDefs]="columnDefs" />`,
})
export class TestHostComponent {
rowData: any[] = [{ name: 'Test Name', number: 42 }];
columnDefs: ColDef[] = [
{ field: 'name' },
{ field: 'number', colId: 'raw', headerName: 'Raw Number', editable: true, cellEditor: EditorComponent },
{ field: 'number', colId: 'renderer', headerName: 'Renderer Value', cellRenderer: PoundRenderer },
];
@ViewChild(AgGridAngular) public agGrid: AgGridAngular;
}
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [TestHostComponent],
}).compileComponents();
fixture = TestBed.createComponent(TestHostComponent);
component = fixture.componentInstance;
});
Testing via the Grid API Copy Link
The grid api will be available after detectChanges()
has run and the fixture is stable. This is true if you store a reference to the api
within onGridReady
or use a ViewChild
to access the AgGridAngular
component.
it('ViewChild not available until `detectChanges`', () => {
expect(component.agGrid).not.toBeTruthy();
});
it('ViewChild is available after `detectChanges`', async () => {
// Detect changes triggers the AgGridAngular lifecycle hooks
fixture.detectChanges();
// Wait for the fixture to stabilise
await fixture.whenStable();
// ViewChild now has a reference to the component
expect(component.agGrid.api).toBeTruthy();
});
Testing Grid Contents Copy Link
One way to check the grid contents is to access the nativeElement
and query DOM elements from there:
it('the grid cells should be as expected', async () => {
fixture.detectChanges();
await fixture.whenStable();
const cellElements = fixture.nativeElement.querySelectorAll('.ag-cell-value');
expect(cellElements.length).toEqual(3);
expect(cellElements[0].textContent).toEqual("Test Name");
expect(cellElements[1].textContent).toEqual("42");
expect(cellElements[2].textContent).toEqual("£42");
});
Testing User Supplied Components Copy Link
To test user supplied components you can access them via the grid API.
For example, given the following code:
@Component({
standalone: true,
template: `£{{params?.value}}`,
})
export class PoundRenderer implements ICellRendererAngularComp {
params: ICellRendererParams | undefined;
agInit(params: ICellRendererParams): void {
this.params = params;
}
refresh(params: ICellRendererParams) {
this.params = params;
return true;
}
}
@Component({
selector: 'editor-cell',
template: `<input #input [(ngModel)]="value" style="width: 100%">`
}
)
export class EditorComponent implements ICellEditorAngularComp {
private params: ICellEditorParams;
public value: number;
@ViewChild('input', {read: ViewContainerRef}) public input;
agInit(params: ICellEditorParams): void {
this.params = params;
this.value = this.params.value;
}
getValue(): any {
return this.value;
}
// for testing
setValue(newValue: any) {
this.value = newValue;
}
isCancelBeforeStart(): boolean {
return false;
}
isCancelAfterEnd(): boolean {
return false;
};
}
@Component({
template:
`<div>
<ag-grid-angular
style="width: 100%; height: 350px;"
[columnDefs]="columnDefs"
[rowData]="rowData"
[stopEditingWhenCellsLoseFocus]="false"
(gridReady)="onGridReady($event)" />
</div>`
})
class TestHostComponent {
rowData: any[] = [{name: 'Test Name', number: 42}];
columnDefs: ColDef[] = [
{field: "name"},
{field: "number", colId: "raw", headerName: "Raw Number", editable: true, cellEditor: EditorComponent},
{field: "number", colId: "renderer", headerName: "Renderer Value", cellRenderer: PoundRenderer}
];
api: GridApi;
public onGridReady(params: GridReadyEvent) {
this.api = params.api;
}
}
We can test that the EditorComponent
works as follows:
it('cell should be editable and editor component usable', async () => {
// Setup template bindings and run ngOInit. This causes the <ag-grid-angular> component to be created.
// As part of the creation the grid apis will be attached to the gridOptions property.
fixture.autoDetectChanges();
await fixture.whenStable();
// we use the API to start and stop editing - in a real e2e test we could actually double click on the cell etc
component.api.startEditingCell({
rowIndex: 0,
colKey: 'raw'
});
const instances = component.api.getCellEditorInstances();
expect(instances.length).toEqual(1);
const editorComponent = instances[0];
editorComponent.setValue(100);
component.api.stopEditing();
await fixture.whenStable();
const cellElements = fixture.nativeElement.querySelectorAll('.ag-cell-value');
expect(cellElements.length).toEqual(3);
expect(cellElements[0].textContent).toEqual("Test Name");
expect(cellElements[1].textContent).toEqual("100");
expect(cellElements[2].textContent).toEqual("£100");
});
Testing with Angular Testing Library Copy Link
It is also possible to use Angular Testing Library to test AG Grid. Here is one example showing how to test a row click handler that displays the last clicked row above the grid.
<div data-testid="rowClicked">Row Clicked: {{ rowClicked?.make }}</div>
<ag-grid-angular [columnDefs]="columnDefs" [rowData]="rowData" (onRowClicked)="onRowClicked($event)" />
import { render, screen } from '@testing-library/angular';
import userEvent from '@testing-library/user-event';
it('Test cell clicked run row handler', async () => {
render(GridWrapperComponent);
const row = await screen.findByText('Ford');
await userEvent.click(row);
const rowClicked = await screen.findByTestId('rowClicked');
expect(rowClicked.textContent).toBe('Row Clicked: Ford');
});
All the tests above and more can be found in the following GitHub Repo.
jsdom
Limitations Copy Link
Test tools such as vitest, Dom Testing Library and Jest often rely on jsdom to mock the browser.
jsdom
is a pure JavaScript implementation of many web standards with the goal to emulate enough of a subset of a web browser to be useful for testing. However, there are some limitations to be aware of when using jsdom.
If you are using jsdom for testing, you may encounter issues that suggest the grid is not rendering correctly. However, this is likely caused by jsdom not supporting all the features of a real browser.
The main limitations that can affect AG Grid are:
- No support for CSS layout - impacts column / row virtualisation
- No support for
innerText
property Issue #1245 - impacts some grid components
If you wish to use jsdom for your tests you may find the following polyfill useful if you encounter issues with missing content due to the use of innerText
:
// Override the innerText setter to use textContent instead within jsdom based tests
Object.defineProperty(Element.prototype, 'innerText', {
set(value) {
this.textContent = value;
},
});
Where you implement this polyfill may vary depending on your testing setup.
Retrieving References to Grid API and Grid Container Element Copy Link
The following two utility functions are made available for convenience during testing:
import { getGridApi, getGridElement } from 'ag-grid-community'
// Retrieve a GridApi instance from a DOM node/selector
const api = getGridApi('#myGrid');
// Retrieve a Grid `Element` instance from a GridApi instance
const element = getGridElement(api);
The getGridApi
function returns a GridApi
instance that is associated with the grid rendered in gridElement
. The gridElement
argument can be one of the following: the grid ID as determined by the gridId
grid option, a DOM node, or a CSS selector string.
It is recommended to use the gridId
as the argument.
The getGridElement
function returns the Element
instance associated with the grid instance referred to by GridApi
.
Next Up Copy Link
Continue to the next section to learn about Testing Async.