Setup & First Test
Let's get Playwright running and write your first test against a real e-commerce scenario.
Prerequisites
You need Node.js 18+ installed. Check your version:
node --version
# Should show v18.x.x or higher
Installation
Option 1: New Project (Recommended for Learning)
# Create a new directory and initialize
mkdir ecommerce-tests
cd ecommerce-tests
npm init -y
# Install Playwright
npm init playwright@latest
The init wizard asks a few questions:
✔ Do you want to use TypeScript or JavaScript? › TypeScript
✔ Where to put your end-to-end tests? › tests
✔ Add a GitHub Actions workflow? › true
✔ Install Playwright browsers? › true
Option 2: Existing Project
# Install the package
npm install -D @playwright/test
# Install browsers (Chromium, Firefox, WebKit)
npx playwright install
What Gets Installed?
When you run npx playwright install, Playwright downloads:
| Browser | Engine | Size |
|---|---|---|
| Chromium | Chrome/Edge | ~280 MB |
| Firefox | Mozilla | ~250 MB |
| WebKit | Safari | ~180 MB |
These are not your system browsers. Playwright installs specific versions it's tested against.
Project Structure
After initialization, your project looks like this:
ecommerce-tests/
├── tests/
│ └── example.spec.ts # Example test file
├── tests-examples/
│ └── demo-todo-app.spec.ts
├── playwright.config.ts # Configuration
├── package.json
└── package-lock.json
The Config File
playwright.config.ts controls everything:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
// Test directory
testDir: './tests',
// Run tests in parallel
fullyParallel: true,
// Fail CI if test.only is left in code
forbidOnly: !!process.env.CI,
// Retry failed tests in CI
retries: process.env.CI ? 2 : 0,
// Parallel workers
workers: process.env.CI ? 1 : undefined,
// Reporter
reporter: 'html',
// Shared settings for all tests
use: {
// Base URL for relative navigation
baseURL: 'https://shop.example.com',
// Collect trace on first retry
trace: 'on-first-retry',
// Screenshot on failure
screenshot: 'only-on-failure',
},
// Browser configurations
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
});
Your First E-Commerce Test
Delete the example files and create tests/product-page.spec.ts:
import { test, expect } from '@playwright/test';
test.describe('Product Page', () => {
test('displays product details correctly', async ({ page }) => {
// Navigate to a product page
await page.goto('https://demo.playwright.dev/svgomg');
// Note: Replace with your actual e-commerce URL
// Verify the page loaded
await expect(page).toHaveTitle(/SVGOMG/);
});
});
Let's use a real e-commerce demo site. Create tests/ecommerce.spec.ts:
import { test, expect } from '@playwright/test';
// Using Sauce Demo - a public e-commerce test site
const BASE_URL = 'https://www.saucedemo.com';
test.describe('E-Commerce Product Flow', () => {
test.beforeEach(async ({ page }) => {
// Login before each test
await page.goto(BASE_URL);
await page.getByPlaceholder('Username').fill('standard_user');
await page.getByPlaceholder('Password').fill('secret_sauce');
await page.getByRole('button', { name: 'Login' }).click();
// Verify we're on the products page
await expect(page.getByText('Products')).toBeVisible();
});
test('can view product details', async ({ page }) => {
// Click on a product
await page.getByText('Sauce Labs Backpack').click();
// Verify product page elements
await expect(page.getByText('Sauce Labs Backpack')).toBeVisible();
await expect(page.getByText('$29.99')).toBeVisible();
await expect(page.getByRole('button', { name: 'Add to cart' })).toBeVisible();
});
test('can add product to cart', async ({ page }) => {
// Add first product to cart
await page.getByRole('button', { name: 'Add to cart' }).first().click();
// Verify cart badge shows 1
await expect(page.locator('.shopping_cart_badge')).toHaveText('1');
});
test('can complete checkout flow', async ({ page }) => {
// Add product to cart
await page.getByRole('button', { name: 'Add to cart' }).first().click();
// Go to cart
await page.locator('.shopping_cart_link').click();
await expect(page.getByText('Your Cart')).toBeVisible();
// Proceed to checkout
await page.getByRole('button', { name: 'Checkout' }).click();
// Fill checkout form
await page.getByPlaceholder('First Name').fill('John');
await page.getByPlaceholder('Last Name').fill('Doe');
await page.getByPlaceholder('Zip/Postal Code').fill('12345');
await page.getByRole('button', { name: 'Continue' }).click();
// Verify checkout overview
await expect(page.getByText('Checkout: Overview')).toBeVisible();
// Complete purchase
await page.getByRole('button', { name: 'Finish' }).click();
// Verify success
await expect(page.getByText('Thank you for your order!')).toBeVisible();
});
});
Running Tests
Run All Tests
npx playwright test
Output:
Running 3 tests using 3 workers
✓ [chromium] tests/ecommerce.spec.ts:15:7 › can view product details (2.3s)
✓ [chromium] tests/ecommerce.spec.ts:24:7 › can add product to cart (1.8s)
✓ [chromium] tests/ecommerce.spec.ts:31:7 › can complete checkout flow (3.1s)
3 passed (4.2s)
Run Specific Test File
npx playwright test tests/ecommerce.spec.ts
Run Tests in Headed Mode (See the Browser)
npx playwright test --headed
Run in UI Mode (Interactive)
npx playwright test --ui
This opens a powerful visual interface where you can:
- Watch tests run in real-time
- See DOM snapshots at each step
- Filter and re-run specific tests
- Time-travel through test execution
Run Single Test by Name
npx playwright test -g "can add product to cart"
Run in Specific Browser
npx playwright test --project=firefox
Understanding Test Output
HTML Report
After tests run, open the HTML report:
npx playwright show-report
This shows:
- Pass/fail status for each test
- Test duration
- Screenshots on failure
- Traces (if enabled)
Console Output Explained
Running 3 tests using 3 workers
↑ ↑
Total tests Parallel processes
✓ [chromium] tests/ecommerce.spec.ts:15:7 › can view product details
↑ ↑ ↑
Browser File path Line number
Project Configuration for E-Commerce
Update playwright.config.ts for e-commerce testing:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [
['html'],
['list'],
],
use: {
// Your e-commerce site
baseURL: process.env.BASE_URL || 'https://www.saucedemo.com',
// Capture screenshot on failure
screenshot: 'only-on-failure',
// Record video on failure
video: 'on-first-retry',
// Capture trace for debugging
trace: 'on-first-retry',
// Reasonable timeouts for e-commerce
actionTimeout: 15000, // 15s for actions (add to cart, etc.)
navigationTimeout: 30000, // 30s for page loads
},
// Test timeout
timeout: 60000, // 60s per test (checkout flows can be slow)
projects: [
// Desktop browsers
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
// Mobile browsers (important for e-commerce!)
{
name: 'mobile-chrome',
use: { ...devices['Pixel 5'] },
},
{
name: 'mobile-safari',
use: { ...devices['iPhone 13'] },
},
],
});
Useful Scripts in package.json
Add these to your package.json:
{
"scripts": {
"test": "playwright test",
"test:headed": "playwright test --headed",
"test:ui": "playwright test --ui",
"test:debug": "playwright test --debug",
"test:chrome": "playwright test --project=chromium",
"test:mobile": "playwright test --project=mobile-chrome --project=mobile-safari",
"report": "playwright show-report"
}
}
Now you can run:
npm test # Run all tests
npm run test:headed # Watch tests run
npm run test:ui # Interactive UI mode
npm run test:mobile # Mobile browser tests only
Troubleshooting
"Browser not found" Error
# Reinstall browsers
npx playwright install
"Timeout" Errors
Your site might be slow. Increase timeouts in config:
use: {
actionTimeout: 30000,
navigationTimeout: 60000,
}
Tests Pass Locally, Fail in CI
Usually a timing issue. Enable traces:
use: {
trace: 'on', // Always capture traces
}
Then download and view traces from CI artifacts.
Key Takeaways
- Installation is simple:
npm init playwright@latesthandles everything - Browsers are managed: Playwright installs and updates them for you
- Config is powerful: Control timeouts, browsers, reporters, and more
- Multiple run modes: Headless, headed, UI mode, debug mode
- E-commerce ready: Configure appropriate timeouts for payment flows
- HTML reporter: Built-in beautiful test reports
Next Lesson
You've written tests, but those locators like .shopping_cart_badge are brittle. In the next lesson, we'll learn locator strategies that don't break when developers refactor.