How to Use Playwright with Remix for End-to-End Testing
To use
Playwright with Remix, install Playwright in your Remix project, write test scripts that launch your Remix app server, and use Playwright's API to automate browser actions. Run your Remix app locally or in test mode, then use Playwright to navigate pages, fill forms, and check outputs.Syntax
Playwright tests for Remix apps typically follow this pattern:
- Import Playwright testing functions.
- Start your Remix app server or use a running instance.
- Launch a browser and open a new page.
- Navigate to your Remix app URL.
- Perform user actions like clicks or typing.
- Assert expected page content or behavior.
- Close the browser after tests.
This structure helps simulate real user interactions with your Remix app.
typescript
import { test, expect } from '@playwright/test'; test('basic user flow', async ({ page }) => { await page.goto('http://localhost:3000'); await page.click('text=Login'); await page.fill('input[name="username"]', 'user1'); await page.fill('input[name="password"]', 'password'); await Promise.all([ page.waitForNavigation(), page.click('button[type="submit"]'), ]); await expect(page).toHaveURL('http://localhost:3000/dashboard'); await expect(page.locator('h1')).toHaveText('Welcome user1'); });
Example
This example shows a simple Playwright test for a Remix app running locally on port 3000. It navigates to the home page, clicks a login link, fills the login form, submits it, and checks the dashboard page for a welcome message.
typescript
import { test, expect } from '@playwright/test'; // Run your Remix app locally before running this test test('login flow works correctly', async ({ page }) => { await page.goto('http://localhost:3000'); await page.click('text=Login'); await page.fill('input[name="username"]', 'testuser'); await page.fill('input[name="password"]', 'testpass'); await Promise.all([ page.waitForNavigation(), page.click('button[type="submit"]'), ]); await expect(page).toHaveURL('http://localhost:3000/dashboard'); await expect(page.locator('h1')).toHaveText('Welcome testuser'); });
Output
Running 1 test using 1 worker
โ login flow works correctly (3s)
1 passed (3s)
Common Pitfalls
Common mistakes when using Playwright with Remix include:
- Not starting the Remix server before running tests, causing connection errors.
- Using incorrect selectors that do not match the rendered HTML.
- Ignoring asynchronous page loads or navigation delays, leading to flaky tests.
- Not cleaning up browser instances, which can cause resource leaks.
Always ensure your Remix app is running and use await properly to wait for page events.
typescript
/* Wrong: Not waiting for navigation after form submit */ await page.click('button[type="submit"]'); await expect(page).toHaveURL('http://localhost:3000/dashboard'); // May fail /* Right: Wait for navigation explicitly */ await Promise.all([ page.waitForNavigation(), page.click('button[type="submit"]'), ]); await expect(page).toHaveURL('http://localhost:3000/dashboard');
Quick Reference
Tips for using Playwright with Remix:
- Run
npm run devor your Remix production build before tests. - Use Playwright's
page.goto()to open your app URL. - Use semantic selectors like
text=orrole=for accessibility. - Wait for navigation or network idle to avoid flaky tests.
- Close browser contexts after tests to free resources.
Key Takeaways
Always start your Remix app server before running Playwright tests.
Use Playwright's async API to simulate user actions and verify UI changes.
Wait for navigation or page events to avoid flaky tests.
Use clear and accessible selectors for reliable element targeting.
Clean up browser instances after tests to prevent resource leaks.