Testing ag-Grid in React Applications

We will walk through how you can use testing ag-Grid as part of your React application, using default build tools provided when using the Create React App utility.

Waiting for the Grid to be Initialised

Due to the asynchronous nature of React we cannot simply mount the Grid and assume it'll be ready for testing in the next step - we need to wait for the Grid to be ready before testing it.

We can do this in one of two ways - wait for the gridReady event to be fired, or wait for the Grid API to be set.

The first requires a code change and can be tricky to hook into - the latter is unobtrusive and easier to use.

We can create a utility function that will wait for the Grid API to be set for a set amount of time/attempts:

export const ensureGridApiHasBeenSet = component => { return waitForAsyncCondition(() => { return component.instance().api !== undefined }, 5) }; export const waitForAsyncCondition = (condition, maxAttempts, attempts=0) => new Promise(function (resolve, reject) { (function waitForCondition() { // we need to wait for the gridReady event before we can start interacting with the grid // in this case we're looking at the api property in our App component, but it could be // anything (ie a boolean flag) if (condition()) { // once our condition has been met we can start the tests return resolve(); } attempts++; if(attempts >= maxAttempts) { reject("Max timeout waiting for condition") } // not set - wait a bit longer setTimeout(waitForCondition, 10); })(); });

The first function is what we'll use to wait for the Grid API - the 2nd one is more generic and will be useful for waiting for Grid components to be ready (see later).

We can use ensureGridApiHasBeenSet before our tests are executed, typically in the beforeEach hook:

beforeEach((done) => { component = mount(()); agGridReact = component.find(AgGridReact).instance(); // don't start our tests until the grid is ready ensureGridApiHasBeenSet(component).then(() => done(), () => fail("Grid API not set within expected time limits")); }); it('stateful component returns a valid component instance', () => { expect(agGridReact.api).toBeTruthy(); ..use the Grid API... });

We can now safely test the Grid component safe in the knowledge that it's been fully initialised.

Waiting for Grid Components to be Instantiated

In the same way we need to wait for the Grid to be ready we also need to do something similar for user supplied Grid components.

For example, let us suppose a user provides a custom Editor Component and wants to test this within the context of the Grid.

class EditorComponent extends Component { constructor(props) { super(props); this.state = { value: this.props.value } } render() { return ( <input type="text" value={this.state.value} onChange={this.handleChange} style={{width: "100%"}} /> ) } handleChange = (event) => { this.setState({value: event.target.value}); } getValue() { return this.state.value; } // for testing setValue(newValue) { this.setState({ value: newValue }) } isCancelBeforeStart() { return false; } isCancelAfterEnd() { return false; }; } class GridWithStatefullComponent extends Component { constructor(props) { super(props); this.state = { columnDefs: [{ field: "age", editable: true, cellEditorFramework: EditorComponent }], rowData: [{age: 24}] }; } onGridReady(params) { this.api = params.api; } render() { return ( <div className="ag-theme-balham"> <AgGridReact columnDefs={this.state.columnDefs} onGridReady={this.onGridReady.bind(this)} rowData={this.state.rowData} </div> ); } }

We can now test this Editor Component by using the Grid API to initiate testing, gain access to the created Editor Instance and then invoke methods on it:

it('cell should be editable and editor component usable', async() => { expect(component.render().find('.ag-cell-value').html()).toEqual(`<div>Age: 24</div>`); // we use the API to start and stop editing // in a real e2e test we could actually double click on the cell etc agGridReact.api.startEditingCell({ rowIndex: 0, colKey: 'age' }); await waitForAsyncCondition(() => agGridReact.api.getCellEditorInstances() && agGridReact.api.getCellEditorInstances().length > 0, 5) .then(() => null, () => fail("Editor instance not created within expected time")); const instances = agGridReact.api.getCellEditorInstances(); expect(instances.length).toEqual(1); const editorComponent = instances[0].getFrameworkComponentInstance(); editorComponent.setValue(50); agGridReact.api.stopEditing(); await waitForAsyncCondition(() => agGridReact.api.getCellRendererInstances() && agGridReact.api.getCellRendererInstances().length > 0, 5) .then(() => null, () => fail("Renderer instance not created within expected time")); expect(component.render().find('.ag-cell-value').html()).toEqual(`<div>Age: 50</div>`); });

Note that we make use of the waitForAsyncCondition utility described above to wait for the Editor Component to be instantiated.

We also use the Grid API to initiate and end testing as we're can't readily perform double clicks in a unit testing environment (but could if doing e2e with something like Protractor for example).