Lab 23: E2E Testing with Playwright
📖 Resources
Section titled “📖 Resources”🚀 Starter Code
Section titled “🚀 Starter Code”step-21-album-wholesale-v21-post-test-vittest
In this lab you will write end-to-end tests for the album wholesale app using Playwright. You will use role-based selectors, fill forms, and verify navigation flows.
📝 Instructions
Section titled “📝 Instructions”Step 1: Install Playwright
Section titled “Step 1: Install Playwright”npm install --save-dev @playwright/testnpx playwright install chromiumThen initialize the project structure:
npx playwright initWhen prompted:
- TypeScript: Yes
- Tests folder:
e2e - Add GitHub Actions: No (for now)
Step 2: Configure Playwright
Section titled “Step 2: Configure Playwright”Update playwright.config.ts:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({ testDir: './e2e', fullyParallel: true, retries: 0, use: { baseURL: 'http://localhost:4200', trace: 'on-first-retry', }, projects: [ { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, { name: 'firefox', use: { ...devices['Desktop Firefox'] } }, ], webServer: { command: 'ng serve', url: 'http://localhost:4200', reuseExistingServer: !process.env['CI'], },});Step 3: Write a Basic Navigation Test
Section titled “Step 3: Write a Basic Navigation Test”Create e2e/navigation.spec.ts:
import { test, expect } from '@playwright/test';
test('homepage loads and shows album list', async ({ page }) => { await page.goto('/'); await expect(page.getByRole('heading', { level: 1 })).toBeVisible(); await expect(page.locator('.album-card').first()).toBeVisible();});
test('can navigate to album detail', async ({ page }) => { await page.goto('/'); await page.locator('.album-card').first().click(); await expect(page.getByRole('heading', { level: 2 })).toBeVisible();});Step 4: Test the Search / Filter Feature
Section titled “Step 4: Test the Search / Filter Feature”Create e2e/search.spec.ts:
import { test, expect } from '@playwright/test';
test('can search for an album', async ({ page }) => { await page.goto('/');
await page.getByRole('searchbox').fill('jazz'); await expect(page.locator('.album-card')).toHaveCount( await page.locator('.album-card').count() );});Step 5: Test the Cart Flow
Section titled “Step 5: Test the Cart Flow”Create e2e/cart.spec.ts:
import { test, expect } from '@playwright/test';
test('can add an album to the cart', async ({ page }) => { await page.goto('/'); await page.locator('.album-card').first() .getByRole('button', { name: /add to cart/i }).click();
await expect(page.getByTestId('cart-count')).toContainText('1');});
test('cart persists across navigation', async ({ page }) => { await page.goto('/'); await page.locator('.album-card').first() .getByRole('button', { name: /add to cart/i }).click();
await page.getByRole('link', { name: /home/i }).click(); await expect(page.getByTestId('cart-count')).toContainText('1');});Step 6: Use Role-Based Selectors
Section titled “Step 6: Use Role-Based Selectors”Prefer accessible selectors over CSS classes:
// By rolepage.getByRole('button', { name: 'Submit' });page.getByRole('link', { name: 'Home' });page.getByRole('textbox', { name: 'Search' });
// By labelpage.getByLabel('Email address');
// By test id (add data-testid attributes in template)page.getByTestId('cart-count');Step 7: Run the Tests
Section titled “Step 7: Run the Tests”# Run all testsnpx playwright test
# Run with UI modenpx playwright test --ui
# Run a specific filenpx playwright test e2e/cart.spec.ts
# Show HTML reportnpx playwright show-reportIf a test fails, Playwright captures a screenshot and trace automatically. Open the report to inspect the failure.

