This article contributed to the AG Grid blog by Cameron Pavey
Playwright is a testing tool created and maintained by Microsoft geared primarily toward end-to-end (E2E) tests. It boasts several features that make it a compelling choice for teams considering different E2E tools, including a cross-language API that lets you write your tests in various languages (JavaScript, TypeScript, Python, .NET, and Java) and cross-platform support for all major browsers.
Similar to Cypress and Selenium, Playwright’s primary use case is E2E testing; however, recently, it has also gained experimental support for component testing. Component testing allows you to validate individual UI components’ functionality in isolation without invoking the whole application as you typically would in an E2E test.
In this tutorial, you’ll learn more about the distinction between E2E testing and component testing before seeing how Playwright facilitates both kinds of tests. You’ll learn how to create a simple React application that utilizes AG Grid and how to validate the functionality of that application at an E2E and component testing level using Playwright.
What Are E2E Tests
E2E tests are one of the three tiers of testing described by the testing pyramid. They are typically regarded as one of the slower and more expensive types of tests because they usually invoke the entire system and simulate user interactions with a fully composed system. This inevitably leads to longer-running tests (compared to unit tests and integration tests, which both reside closer to the base of the pyramid), with often fragile imperative instructions describing how the test runner should interact with the application. While this tutorial will primarily focus on Playwright, Cypress is also a very compelling option, and it recently released the beta of their component testing solution, similar to Playwright.
One of the big differences between Playwright and Cypress is the philosophies that they follow. Playwright endorses the use of the Page Object Model (POM) pattern. However, Cypress suggests that code being reused through custom commands is an effective alternative, although the POM pattern is still achievable in Cypress if you prefer it. AG Grid has a blog post about using a Cypress plug-in for testing AG Grid applications that eloquently demonstrates the virtues of Cypress’s custom command functionality. Either way, both tools are effective for E2E testing, but what about component testing?
Component Testing Challenges
Component testing can be tricky because, depending on your interpretation, there are various places it could fit in the testing pyramid. You can treat it like an E2E test, as it typically deals with the same UI components, but this approach has some drawbacks. Using the full E2E setup for component testing will be slower than potential alternatives because it still needs to invoke the entire application and its dependencies. This also poses a challenge if you are dealing with a component library with no distinct application to invoke. In this case, you need to make a simple application that mounts your components to test this. This increases the amount of setup and the amount of overhead involved in running these tests, and makes them generally less appealing when viewed from a cost/benefit perspective.
Another approach is to treat them more like unit tests. You can achieve this by using tools like Jest as your test runner and Testing Library, which is a testing utility library that exposes some functions to help you test your components in isolation. This is typically a good approach in terms of effort, maintainability, and general developer experience. However, there are still some drawbacks, the most notable of which is that the tests don’t run in a real browser. Instead, the tests use JSDom in most cases. This is where the component testing functionality of Playwright comes in.
With Playwright, you can run your component tests in real browsers, using the same sort of tests that you would write for your E2E tests but without the drawbacks of using the full E2E setup, with additional overhead and unrelated application code being included.
E2E and Component Testing with Playwright
Before getting started, there are a few things you will need:
- A recent version of Node.js (which comes with npm). This tutorial uses v14.19.3.
- A code editor of your choice. Visual Studio Code is a good choice if you don’t already have a code editor you prefer.
If you wish to see the completed code for the tutorial, you can find it in this public GitHub repo. Otherwise, if you’d prefer to build it yourself, or see how it’s done, keep following along.
Once you have your prerequisites, the first thing you need to do is create a new project. You can use Vite for this tutorial, as it is fast, minimal, and easy to get started with, but other app starters, like create-react-app and Next.js, should also work. To create your new project, open a terminal and run the following commands:
npm create vite@latest ag-grid-playwright --template react
cd ag-grid-playwright
npm install
This will create a directory named ag-grid-playwright/
with a minimalist project inside it before navigating into the directory and installing all the current node dependencies. Next, you can install Playwright with the following command:
npm init playwright@latest
The installer will ask you questions like whether you want to use JavaScript or TypeScript, where you want to store the test files, and whether you want to create a GitHub Action. If you are following along, select JavaScript when prompted and then accept the default answers for the other questions, as these will work for this tutorial.
Once Playwright is installed, you can test that it works as expected. By default, it comes with an example test spec that runs twenty-five sample assertions in each of the three major browsers: Chrome, Firefox, and WebKit. To run this spec file, run the following command:
npx playwright test
If all is well so far, you should see an output like this:
To simplify the output later when you add your tests, you can delete the example located at tests/example.spec.js
.
Now that you have Playwright set up, you can install a couple more dependencies that you will need to build the actual application, AG Grid. To do this, use the following command:
npm install ag-grid-react ag-grid-community
Creating the Application
With the dependencies installed, you need to create the components you will use in this application. So that there is a level of contrived complexity in the application, you will create two main components: the DataGrid
and the CustomRenderer
for one of the columns in your grid. You can do this by running the following commands:
mkdir src/components
touch src/components/DataGrid.jsx
touch src/components/CustomRenderer.jsx
This will create the directory and files for the components you need. Next, open the CustomRenderer.jsx
file in your editor and paste in the following content:
export const CustomRenderer = (props) => {
return <span>{`$${props.value.toLocaleString()}`}</span>;
};
This simple component will be responsible for rendering the integer values in your data, formatted as monetary values. After this, open the DataGrid.jsx
file and paste in the following content:
import React, { useState } from "react";
import { AgGridReact } from "ag-grid-react";
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-alpine.css";
import { CustomRenderer } from "./CustomRenderer";
export const DataGrid = () => {
const [rowData] = useState([
{ make: "Toyota", model: "Celica", price: 35000 },
{ make: "Ford", model: "Mondeo", price: 32000 },
{ make: "Porsche", model: "Boxster", price: 72000 },
]);
const [columnDefs] = useState([
{ field: "make" },
{ field: "model" },
{ field: "price", cellRenderer: CustomRenderer },
]);
return (
<div className="ag-theme-alpine" style={{ height: 400, width: 600 }}>
<AgGridReact rowData={rowData} columnDefs={columnDefs}></AgGridReact>
</div>
);
};
This code is a slightly modified version of the example from the AG Grid documentation. The modification to this code is simply to use the CustomRenderer
component for the price
column, which will display the value as a formatted monetary value rather than just a number. You will need to change one more file to ensure the application renders correctly. Open src/App.jsx
and replace its content with the following:
import { DataGrid } from "./components/DataGrid";
function App() {
return <DataGrid />;
}
export default App;
Before proceeding to the E2E test, you should verify that the app is working as expected. To do this, from your terminal, run npm run dev
, which will tell Vite to start a dev server, allowing you to access your application, typically located at http://localhost:3000 if the port is free. If it needs to run on a different port for whatever reason, the terminal output will tell you where it is running when you execute the command. When you visit that URL, you should see something like this:
Although the application is very simple, it gives you a few things that you can test with both E2E and component tests and, as such, serves as an excellent example for this kind of tutorial.
The E2E Test
For your E2E test, you want to ensure that the application works as expected. In the case of this simple application, that essentially amounts to showing the correct data. To do this, make a new file located at tests/app.spec.jsx
and give it the following content:
import { test, expect } from "@playwright/test";
test("Check that all expected data is present", async ({ page }) => {
await page.goto("http://localhost:3000");
// Verify that the title is correct
await expect(page).toHaveTitle("Vite App");
// Specify the data that we expect to be present
const expectedData = [
["Toyota", "Celica", "$35,000"],
["Ford", "Mondeo", "$32,000"],
["Porsche", "Boxster", "$72,000"],
];
// Verify that the data is correct
for (let index = 0; index < expectedData.length; index++) {
const row = await page.locator("role=row").nth(index + 1);
const [make, model, price] = expectedData[index];
await expect(row).toContainText([make, model, price]);
}
});
This test will instruct Playwright to navigate to your application, hosted by Vite’s dev server. Check that the page title is correct and that the three expected rows of data are present and correct. You can add a script to your package.json
file to help run your E2E tests. Open your package.json
file and add the following line to your scripts
object:
…
"test": "playwright test"
…
Now, ensure that your Vite dev server is still running so that your application is available on port 3000, and then in a new terminal window (navigate back to the project directory if you need to), run the following command:
npm run test
You should see an output like this:
Even though you only have one test at the moment, Playwright is configured to run three different projects, one with each of the three major browsers. You can see this configuration in playwright.config.js
.
This E2E test requires your application to be running and will load your entire application for each test. That isn’t a problem for a small application like this one. With larger, more complex real-world applications, however, it isn’t ideal to have that overhead if you aren’t testing the whole application.
Next, you’ll see how you can use Playwright to create some simple component tests for your CustomRenderer
and DataGrid
components.
The Component Tests
Getting started with Playwright component testing is similar to the initial setup for Playwright E2E testing. However, you should note that at the time of writing this, component testing support in Playwright is still regarded as experimental, so it’s possible things will change in the future. If you find that these instructions do not work as expected, please refer to the official documentation for further guidance.
To get started, run the following command from the root of your project directory:
npm init playwright@latest --ct
This will prompt you with similar questions to the initial setup, but if you are following along, answer with “JavaScript” and “React” when prompted for your language and framework, respectively. When this command finishes, you should have a new file called playwright-ct.config.js
. Open this file and edit the testDir
property as follows:
…
testDir: ‘./src’,
…
This change is necessary because the default value of './'
will include your E2E tests, which you don’t necessarily want to run alongside your component tests in the same command. After making this change, you can create your two component tests. For the first one, create a file at src/components/CustomRenderer.spec.jsx
and give it the following content:
import { test, expect } from "@playwright/experimental-ct-react";
import { CustomRenderer } from "./CustomRenderer";
test.use({ viewport: { width: 500, height: 500 } });
test("formats value correctly", async ({ mount }) => {
const component = await mount(<CustomRenderer value={10000} />);
await expect(component).toContainText("$10,000");
});
This test will ensure that the CustomRenderer
parses numeric values into monetary values correctly. Next, create a file at src/components/DataGrid.spec.jsx
and give it the following content:
import { test, expect } from "@playwright/experimental-ct-react";
import { DataGrid } from "./DataGrid";
test.use({ viewport: { width: 500, height: 500 } });
test("contains the expected data", async ({ mount }) => {
const component = await mount(<DataGrid />);
const expectedData = [
["Toyota", "Celica", "$35,000"],
["Ford", "Mondeo", "$32,000"],
["Porsche", "Boxster", "$72,000"],
];
// Verify that the data is correct
for (let index = 0; index < expectedData.length; index++) {
const row = await component.locator("role=row").nth(index + 1);
const [make, model, price] = expectedData[index];
await expect(row).toContainText([make, model, price]);
}
});
You will notice that this test borrows heavily from the E2E test in terms of logic. It is, after all, performing very similar assertions due to the limited scope of the application. The key difference here, however, is that it will not instantiate your whole application, nor does it require your Vite dev server to be running. With both of these tests created, you can run the following command to execute them:
npm run test-ct
From this command, you should see similar output to your E2E test, except it will be 6
now instead of 3
(because you have two tests and three browsers):
Being able to test components in isolation, away from the rest of your application, is important because not only will the tests run faster than heavier E2E tests but, more importantly, the tests will not be affected by external factors and unrelated code. Performing your component tests with the same tools as your E2E tests (as opposed to using Jest and Testing Library) is a huge advantage. It opens the door for productivity boosts, such as sharing test helpers between E2E and component tests, allowing you to share abstractions and utilities between these two testing domains without the drawbacks that traditionally come with fully merging them.
Summary of Github Steps
You can find the code for this tutorial in this public GitHub repo.
First, clone or download and unzip the repo code, then install the dependencies:
npm install
Second, install Playwright and browsers:
npx playwright install
To run the component tests:
npm run test-ct
To run the end to end tests we need to run the dev server from one console with npm run dev
to start the app running on localhost
. Then run the end to end tests with:
npm run test
After each test run you can use npx playwright show-report
to see the execution report.
Wrapping Up
In this tutorial, you learned how to create a simple application with React, Vite, and AG Grid, and then test that application at an E2E and component level using Playwright. You’ve also learned about the differences between E2E and component testing, as well as some tools that can help you create those kinds of tests, like Playwright, Cypress, Jest, and Testing Library.
If you want to learn more about Playwright, the official documentation is a great place to get started.
Top comments (0)