End-to-End Testing for AG Grid in React with Cypress

  |   Testing

This tutorial demonstrates how to write e2e (end-to-end) tests for your AG Grid React Table components with Cypress.

You can find the complete code for this tutorial in the following GitHub repo.

Contents

Project setup

Create a new React project by running the following command:

npm create vite@latest testing-ag-grid -- --template react-ts

Change the directory to your newly created app and install the necessary packages with the following command:

cd testing-ag-grid
npm install
npm run dev

Create a new AG Grid component

Next, go ahead and create a new AG Grid component in your React application. Run the following command to install AG Grid npm package.

npm install ag-grid-react

Next, open the index.css file and delete all the styles and replace all the code in your App.tsx file with the following code:

import { useState, useMemo } from "react";
import { AllCommunityModule, ModuleRegistry, ColDef } from "ag-grid-community";
import { AgGridReact } from "ag-grid-react";

export type ICar = {
  make: string;
  model: string;
  price: number;
  electric: boolean;
};

ModuleRegistry.registerModules([AllCommunityModule]);

function App() {
  // Row Data: The data to be displayed.
  const [rowData, setRowData] = useState<ICar[]>([
    { make: "Tesla", model: "Model Y", price: 64950, electric: true },
    { make: "Ford", model: "F-Series", price: 33850, electric: false },
    { make: "Toyota", model: "Corolla", price: 29600, electric: false },
    { make: "Mercedes", model: "EQA", price: 48890, electric: true },
    { make: "Fiat", model: "500", price: 15774, electric: false },
    { make: "Nissan", model: "Juke", price: 20675, electric: false },
  ]);

  // Column Definitions: Defines & controls grid columns.
  const [colDefs, setColDefs] = useState<ColDef<ICar>[]>([
    { field: "make", editable: true, filter: true },
    { field: "model" },
    { field: "price", editable: true },
    { field: "electric" },
  ]);

  const defaultColDef = useMemo(() => { 
    return {
      flex: 1
    };
  }, []);

  return (
    <div style={{ width: "100%", height: '100vh' }}>
      <AgGridReact
        rowData={rowData}
        columnDefs={colDefs}
        defaultColDef={defaultColDef}
      />
    </div>
  );
}

export default App

Configure Cypress

First install the necessary dependencies by running the following command:

npm install cypress --save-dev

Next, run the following command to configure Cypress in you project:

npx cypress open

When you run the command above, a new browser window opens. You'll be presented with a page similar to the one below. This page gives you the option to setup different types of tests.

Cypress tests setup

Select the E2E Testing option as that is what we will be focusing on in this tutorial.

Next, Cypress will give you a list of configuration files that it generated. These files provide project-specific configurations. You can learn more about them here. Select continue to proceed.

Cypress config for AG Grid

At this point, you may get an error as follows. If you do get this error, change the cypress.config.ts file to cypress.config.js

Picture of error in config

After you change the file extension of cypress.config from .ts to .js select "Try again".

Next, choose either a web browser (Firefox or Chrome) or an Electron app. This tells Cypress whether you're setting up tests for a web or Electron application. Select the web browser option and then select "start e2e testing".

choose a browser in config

Next, you will get an option to scaffold spec files or create a new spec file from scratch. Select create new spec file.

Create your first spec file

It will try to create a new test. Name your test cypress/e2e/App.cy.ts. Accept the default sample code.

Click on the newly created test to run it. The test will pass as we haven't implemented anything yet.

showing the test in Cypress
Tests passing in cypress

You can add other tests directly from the browser in this manner. If you go back to your code and go into the Cypress folder you will see an e2e/App.cy.ts was created as well. You can add your tests here, which is what we will be doing going forward. 

Update the script section in the package.json file. Add a new test script to run the tests.

"scripts": {
  "test": "cypress open"
},

The final step before we begin testing is to ensure you have your React server running in one terminal and Cypress in another. Otherwise Cypress will not be able to detect your application. 

Testing Rendering

Let's add a test that verifies that the grid renders correctly. Update the App.cy.ts file with the following code:

describe('AG Grid Rendering', () => {
  beforeEach(() => {
    cy.visit('http://localhost:5173/');
  });

  it('should render the AG Grid component', () => {
    cy.get('.ag-root-wrapper').should('be.visible');

    // Verify column headers
    cy.get('.ag-header-cell-text').eq(0).should('have.text', 'Make');
    cy.get('.ag-header-cell-text').eq(1).should('have.text', 'Model');

    // Wait for grid to load and verify first row data
    cy.get('.ag-cell').should('have.length.greaterThan', 0);
    cy.get('.ag-cell').eq(0).should('have.text', 'Tesla');
  });
});

The test suite first navigates to a local development server at port 5173 before each test using beforeEach.

The main test checks if the grid renders correctly by verifying several elements. It confirms the grid wrapper is visible, validates that the first two column headers are "Make" and "Model" respectively. It also ensures the grid contains data by checking for the presence of cells, and verifies that the first cell contains "Tesla".

Run the test with the following command:

npm run test
Cypress test passing

Asynchronous data loading

Most uses of AG Grid will involve fetching data from an API, so let's simulate this by asynchronously loading our rowData with a setTimeout:

const [rowData, setRowData] = useState<ICar[] | null>(null);

useEffect(() => {
  // Simulate an asynchronous data fetch (e.g., from an API)
  setTimeout(() => {
    setRowData([
      { make: "Tesla", model: "Model Y", price: 64950, electric: true },
      { make: "Ford", model: "F-Series", price: 33850, electric: false },
      { make: "Toyota", model: "Corolla", price: 29600, electric: false },
      { make: "Mercedes", model: "EQA", price: 48890, electric: true },
      { make: "Fiat", model: "500", price: 15774, electric: false },
      { make: "Nissan", model: "Juke", price: 20675, electric: false },
    ]);
  }, 500);
}, []);

Next, let's write a test to verify the table is rendered correctly when async data is involved:

it("should display the loading overlay and then show the data", () => {
  // 1. Check that the AG Grid loading overlay is visible
  cy.get(".ag-overlay-loading-center").should("be.visible");

  // 2. Wait for the first cell to contain "Tesla" (data is loaded)
  cy.get(".ag-cell").first().should("have.text", "Tesla");

  // 3. Ensure the loading overlay disappears after data loads
  cy.get(".ag-overlay-loading-center").should("not.exist");
});

This test code verifies the loading behavior of an AG Grid component when asynchronous data is involved. It checks that a loading overlay is visible while data is being fetched, then waits for actual data to appear and checks the data. Finally, it confirms that the loading overlay has disappeared after the data loads.

User interaction testing

Cypress can be used to simulate user interactions accurately. In this section we will thoroughly test our grid's behavior based on simulated user interactions.

Sorting

Let’s create a test to verify the grid’s sorting functionality. Specifically, this test will check that clicking the price column header correctly sorts the data in both ascending and descending order.

Let's add the following code to our App.cy.ts file:

// ... Rest of the code

describe('AG Grid sorting', () => {
  beforeEach(() => {
    cy.visit('http://localhost:5173/');
  });

  it('sorts "Price" column ascending and then descending', () => {
    // 1. Locate the "Price" column header and click
    cy.contains(".ag-header-cell-text", "Price").click();

    // 2. Verify the first row in the grid is now 'Fiat'
    cy.get('.ag-row[row-index="0"] [col-id="make"]')
      .should("have.text", "Fiat");

    // 3. Click again to sort in descending order
    cy.contains(".ag-header-cell-text", "Price").click();

    // 4. Verify the first row in the grid is now 'Tesla'
    cy.get('.ag-row[row-index="0"] [col-id="make"]')
      .should("have.text", "Tesla");
  });

});

In the above code, we use the rowIndex (which reflects the row's current position in the grid's display) to verify the sort order. In this example, we've hardcoded the values we expect to see.

A more robust test may loop through the values of each row, to validate sorting functionality. The following is a test that dynamically validates the sorting functionality by looping through the grid values instead of hardcoding expectations.

💡
It may also be useful to consider using sourceRowIndex to compare values before and after sorting as this represents the position of a row in the original rowData array and remains constant regardless of sorting, filtering, grouping, or other UI operations.

Edit cell value

In your column definition, make sure you have the make and price columns set as editable.

const [colDefs, _setColDefs] = useState<ColDef<ICar>[]>([
  { field: "make", editable: true, filter: true },
  { field: "model" },
  { field: "price", editable: true },
  { field: "electric" },
]);

Add the following test to your test suite to verify the cell edit behavior of your grid.

describe("AG Grid Editable Cell", () => {
  beforeEach(() => {
    cy.visit("http://localhost:5173/");
  });

  it('should allow editing of cells and update the data', () => {
    // 1. Locate the editable cell for "Price" in the first row
    const priceCellSelector = '.ag-row[row-index="0"] [col-id="price"]';

    // 2. Double-click the cell to activate edit mode
    cy.get(priceCellSelector).dblclick();

    // 3. Wait for the input field to appear
    cy.get(`${priceCellSelector} input`)
      .should("be.visible")
      .as("priceInputField");

    // 4. Enter a new value into the input field and simulate pressing Enter
    const newPrice = "70000";
    cy.get("@priceInputField")
      .clear()
      .type(`${newPrice}{enter}`);

    // 5. Verify the cell displays the updated value
    cy.get(priceCellSelector).should("have.text", newPrice);
  });
});

The above test simulates the following:

  • It locates the price cell in the first row (row-index="0") by its col-id="price".
  • It double-clicks on this cell to activate the cell’s edit mode.
  • It then waits until the input field (rendered by AG Grid for editing) is visible.
  • It clears the existing value, types in a new price, and presses Enter to commit the change.
  • It verifies that the cell now displays the updated price value.

Filtering

Next, let's test the filtering functionality. In your column definition turn the make column to be filterable.

const [colDefs, _setColDefs] = useState<ColDef<ICar>[]>([
  { field: "make", editable: true, filter: true },
  // ... Rest of the code
]);

Add the following test to verify filter functionality

describe('AG Grid Make Column Filter', () => {
  beforeEach(() => {
    cy.visit('http://localhost:5173/');
  });

  it('should filter data by "Make" using the column filter menu', () => {
    // 1. Wait for the AG Grid root element to be visible
    cy.get('.ag-root').should('be.visible');

    // 2. Locate and click the filter menu icon for the "Make" column
    cy.get('.ag-header-cell[col-id="make"] .ag-header-icon')
      .should('be.visible')
      .click();

    // 3. Fill in the filter input with "Tesla"
    cy.get('.ag-filter-body input[placeholder="Filter..."]').type('Tesla');

    // 4. Verify only one row is visible
    cy.get('.ag-center-cols-viewport .ag-row').should('have.length', 1);

    // 5. Check that the "Make" cell in the first (and only) row contains "Tesla"
    cy.get('.ag-center-cols-viewport .ag-row')
      .first()
      .find('.ag-cell[col-id="make"]')
      .should('have.text', 'Tesla');
  });
});

The test above clicks the filter icon in the "Make" column header, enters "Tesla" as the filter criteria, and then confirms that the filtering works correctly by checking that only one row remains visible and that this row contains "Tesla" in the Make column.

Accessibility testing

AG Grid provides amongst the best support for accessibility compared to other grids available on the market today. This section demonstrates how to test this by validating things like a11y standards, ARIA labels etc.

Accessibility (a11y) checks

Install the cypress-axe package in your project with the following command

npm install --save-dev cypress-axe

Navigate to your cypress/support/e2e.ts file and add the following code to import and initialize cypress-axe:

import 'cypress-axe';

Cypress.Commands.add('injectAxe', () => {
  cy.window({ log: false }).then((win) => {
    // Inject axe-core runtime into the browser
    // This makes the `axe` global available
    const script = win.document.createElement('script');
    script.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.6.3/axe.min.js';
    win.document.head.appendChild(script);
  });
});

Cypress.Commands.add('checkA11y', (context, options = {}, violationCallback) => {
  // By default, run on the entire document if 'context' is not provided
  cy.window({ log: false }).then((win) => {
    return new Promise((resolve) => {
      win.axe.run(context || win.document, options, (error, results) => {
        if (error) throw error;
        if (results.violations.length) {
          if (violationCallback) {
            violationCallback(results.violations);
          } else {
            cy.log('Accessibility violations found:', results.violations);
          }
        }
        resolve(results);
      });
    });
  });
});

Next add the following test to your test suite.

describe('Accessibility Tests', () => {
  it('should have no a11y violations on load', () => {
    // 1) Visit your app
    cy.visit('http://localhost:5173');

    // 2) Inject axe into the page
    cy.injectAxe();

    // 3) Optionally wait for content to finish loading
    cy.get('.ag-root').should('be.visible');
    cy.get('.ag-overlay-loading-center').should('not.exist');

    // 4) Run accessibility checks
    cy.checkA11y();
  });
});

This example Cypress test visits your AG Grid page, waits for the grid to load, and runs the axe-core audit.

This automated test ensures your AG Grid implementation follows accessibility standards by automatically catching common issues like missing ARIA labels, contrast problems, or improper HTML structure.

Conclusion

In this tutorial, we covered essential testing scenarios for testing AG Grid components using Cypress. You can build upon these examples to create more specialized tests for their specific use cases, whether that involves complex data manipulations, custom cell renderers, or advanced filtering scenarios.

If you’re an enterprise user and need further assistance with testing strategies, please contact us through Zendesk.

Read more posts about...