Running the app

The Epic Stack

You have noticed that things have changed in your playground directory. That's because we have an entire Epic Stack application at our disposal now. It includes React components, routes, server handlers, database schemas... That is a lot! But you don't have to know it in-and-out to test it efficiently on an end-to-end level. Those implementation details vanish entirely, and all that's left is a real application that you will be interacting with inside your tests.
But first, we need to tell Playwright to run it.

Configuring webServer

In playwright.config.ts, let's add a property webServer to our Playwright configuration, which will describe how our application should be spawned for testing.
export default defineConfig({
	// ...

	webServer: {
		command: process.env.CI ? 'npm run start:mocks' : 'npm run dev',
		port: Number(PORT),
		reuseExistingServer: true,
		stdout: 'pipe',
		stderr: 'pipe',
		env: {
			PORT,
			NODE_ENV: 'test',
		},
	},
})
Here's what we are doing step-by-step:
  • command lists the exact command Playwright will run to spawn our app. Here, we are making it conditional, using npm run start:mocks on CI and npm run dev for local testing;
  • port is the port number where Playwright will expect the application to occupy. Once it sees something is running at that port, it will consider the application successfully spawned;
  • reuseExistingServer tells Playwright to reuse an application on the given port if it's already running. This is handy when running our end-to-end tests during development so we don't have to spawn another app instance;
  • stdout and stderr are the quality-of-life options that 'pipe' the standard output and errors, respectively, from the application's process to the test runner's process so we could observe them before, during, and after the test run;
  • env, as the name suggests, lists the environment variables we can provide to the application:
    • PORT to spawn the application at the same port that Playwright expects it on;
    • NODE_ENV as a flag that tells our app that it's being run for testing purposes.
You can provide an array of web server configurations in the webServer option for Playwright to spawn them all in parallel before the test run.

Writing tests

From the test's perspective, not much will change. We still want to navigate to the page we want (the homepage), find the heading element, and make sure that it's visible to the user.
import { test, expect } from '@playwright/test'

test('displays the welcome heading', async ({ page }) => {
	await page.goto('/')

	await expect(
		page.getByRole('heading', { name: 'The Epic Stack' }),
	).toBeVisible()
})
Notice that I'm providing a relative path to the homepage in page.goto('/'). How does Playwright know what to resolve it relatively to? It doesn't! I told it in playwright.config.ts:
export default defineConfig({
	// ...

	use: {
		baseURL: `http://localhost:${PORT}/`,
	},
})
Playwright resolves any relative URLs against use.baseURL, if provided.

Running the test

Finally, let's run the test to verify that our application is spawned correctly and all the instructions we have in our test run against that spawned instance.
npm run test:e2e

Please set the playground first

Loading "Running the app"
Loading "Running the app"

No tests here 😢 Sorry.