DEV Community

Cover image for Playwright for React Devs: A Complete Guide with API Testing
Debajit Mallick
Debajit Mallick

Posted on

3

Playwright for React Devs: A Complete Guide with API Testing

Introduction

Let's face it – testing isn't the most glamorous part of web development, but it's definitely one of the most important. Without solid tests, we're basically crossing our fingers and hoping our apps don't break when users start clicking around.

That's where Playwright comes in. It's a game-changer for end-to-end testing, making the process faster and more reliable than you might expect. In this guide, I'll walk you through setting up Playwright for a React app built with Vite and TypeScript, with special attention to handling API integration testing.

Setting Up Your React Playground

First things first, let's get a React app up and running. We'll use Vite because, honestly, who doesn't love lightning-fast build times?

npm create vite@latest my-react-app --template react-ts
cd my-react-app
npm install
Enter fullscreen mode Exit fullscreen mode

Fire up the dev server with:

npm run dev
Enter fullscreen mode Exit fullscreen mode

Getting Friendly with Playwright

Now for the fun part – adding Playwright to the mix:

npm init playwright@latest
Enter fullscreen mode Exit fullscreen mode

It will create a tests folder and inside there will be an example.spec.ts file already created.There let's write our first test.

import { test, expect } from '@playwright/test';

test.describe('Basic tests', () => {
  test.beforeAll(async () => {
    console.log('Setting up test environment');
  });

  test('basic test', async ({ page }) => {
    // Navigate to the application
    await page.goto('http://localhost:5173');

    // Check if the page title contains the expected text
    await expect(page).toHaveTitle(/Vite \+ React/);
  });
});
Enter fullscreen mode Exit fullscreen mode

What's happening here?

  • We're grouping our tests with test.describe for better organization
  • Using test.beforeAll to set up anything we need before testing
  • Navigating to our app and checking if the page title is what we expect

Run the test with:

npx playwright test
Enter fullscreen mode Exit fullscreen mode

Building Something Worth Testing

Let's create a simple component that actually does something – like fetching user data from an API an display that to an UI:

import { useEffect, useState } from 'react';

type User = {
  id: number;
  name: string;
};

const UserList = () => {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch('https://jsonplaceholder.typicode.com/users')
      .then((response) => response.json())
      .then((data) => {
        setUsers(data);
        setLoading(false);
      });
  }, []);

  if (loading) return <p>Loading...</p>;

  return (
    <ul>
      {users.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
};

export default UserList;
Enter fullscreen mode Exit fullscreen mode

And let's add this component to our App:

import UserList from './components/UserList';

function App() {
  return (
    <div>
      <h1>User List</h1>
      <UserList />
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Let's test the App

Now comes the really cool part – testing how our app handles API responses. Create a new test file tests/userlist.spec.ts:

import { test, expect } from '@playwright/test';

test.describe('User List API Integration', () => {
  test.beforeAll(async () => {
    console.log('Starting API tests');
  });

  test('User list loads and displays correctly', async ({ page }) => {
    // Intercept API request and provide mock data
    await page.route('https://jsonplaceholder.typicode.com/users', async (route) => {
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify([
          { id: 1, name: 'John Doe' },
          { id: 2, name: 'Jane Doe' }
        ]),
      });
    });

    // Navigate to the app
    await page.goto('http://localhost:5173');

    // Wait for API call and UI update
    await expect(page.getByText('John Doe')).toBeVisible();
    await expect(page.getByText('Jane Doe')).toBeVisible();
  });
});
Enter fullscreen mode Exit fullscreen mode

This is where the Playwright really shines. We're:

  1. Intercepting the API call and sending back our own mock data
  2. Loading up our app
  3. Making sure the UI actually shows the data we expect

No need to worry about flaky tests or timing issues – Playwright intelligently waits for elements to appear.

Run Those Tests!

Time to see if everything works:

npx playwright test
Enter fullscreen mode Exit fullscreen mode

Want to actually see the tests running? Try UI mode:

npx playwright test --ui
Enter fullscreen mode Exit fullscreen mode

Conclusion

And that's it! We've built a React app and set up Playwright for testing, and even tackled API integration testing. The best part? Playwright's API interception means we can test our UI components without worrying about backend dependencies. These techniques should give you confidence that your React components will handle API data correctly – even when things go wrong. If you like this blog and want to learn more about Frontend Development and Software Engineering, you can follow me on Dev.to.

Top comments (2)

Collapse
 
dheeraj_tp_4fb9f37e3fb0d9 profile image
Dheeraj TP

We can't always expect the api response to be same all time right ? So how can we manage that ?

Collapse
 
debajit13 profile image
Debajit Mallick • Edited

In those cases, we can check the API response status code and based on that we can run different tests. For example in the following code to add a new task will only run the final test if the status code is 201.


test("should add a new todo (waiting for API)", async ({ page }) => {
  await page.goto("http://localhost:3000");

  await page.fill("input[type='text']", "New Task");

  // Intercept the request and wait for response
  const responsePromise = page.waitForResponse((response) =>
    response.url().includes("/todos") && response.status() === 201
  );

  await page.click("button:has-text('Add')");
  await responsePromise; // Wait for the API response

  await expect(page.locator("ul li")).toContainText("New Task");
});

Enter fullscreen mode Exit fullscreen mode

Playwright CLI Flags Tutorial

5 Playwright CLI Flags That Will Transform Your Testing Workflow

  • 0:56 --last-failed
  • 2:34 --only-changed
  • 4:27 --repeat-each
  • 5:15 --forbid-only
  • 5:51 --ui --headed --workers 1

Learn how these powerful command-line options can save you time, strengthen your test suite, and streamline your Playwright testing experience. Click on any timestamp above to jump directly to that section in the tutorial!

Watch Full Video 📹️

👋 Kindness is contagious

If you found this article helpful, please give a ❤️ or share a friendly comment!

Got it