Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue 3 testing best practices with Vitest, Vue Test Utils, component testing, mocking, and Playwright E2E.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
reference/testing-e2e-playwright-recommended.md
1---2title: Use Playwright for E2E Testing - Cross-Browser Support and Better DX3impact: MEDIUM4impactDescription: Cypress has browser limitations and some features require paid subscriptions5type: best-practice6tags: [vue3, testing, e2e, playwright, cypress, end-to-end]7---89# Use Playwright for E2E Testing - Cross-Browser Support and Better DX1011**Impact: MEDIUM** - Playwright offers superior cross-browser testing (Chromium, WebKit, Firefox), excellent debugging tools, and is fully open source. Cypress has limitations with WebKit support and requires paid subscriptions for some features.1213Use Playwright for new E2E testing setups. Consider Cypress if team already has expertise or for its visual debugging UI.1415## Task Checklist1617- [ ] Install Playwright with browsers for your target platforms18- [ ] Configure for Vue dev server integration19- [ ] Set up projects for different browsers20- [ ] Use locator strategies that match component test patterns21- [ ] Configure CI for parallel test execution22- [ ] Use trace and screenshot features for debugging2324## Quick Setup2526```bash27# Install Playwright28npm init playwright@latest2930# This will create:31# - playwright.config.ts32# - tests/ directory33# - tests-examples/ directory34```3536**playwright.config.ts:**37```typescript38import { defineConfig, devices } from '@playwright/test'3940export default defineConfig({41testDir: './e2e',42fullyParallel: true,43forbidOnly: !!process.env.CI,44retries: process.env.CI ? 2 : 0,45workers: process.env.CI ? 1 : undefined,46reporter: 'html',4748use: {49// Base URL for navigation50baseURL: 'http://localhost:5173',51// Capture trace on first retry52trace: 'on-first-retry',53// Screenshot on failure54screenshot: 'only-on-failure',55},5657projects: [58{59name: 'chromium',60use: { ...devices['Desktop Chrome'] },61},62{63name: 'firefox',64use: { ...devices['Desktop Firefox'] },65},66{67name: 'webkit',68use: { ...devices['Desktop Safari'] },69},70// Mobile viewports71{72name: 'Mobile Chrome',73use: { ...devices['Pixel 5'] },74},75],7677// Run local dev server before tests78webServer: {79command: 'npm run dev',80url: 'http://localhost:5173',81reuseExistingServer: !process.env.CI,82},83})84```8586## E2E Test Example8788```typescript89// e2e/user-flow.spec.ts90import { test, expect } from '@playwright/test'9192test.describe('User Authentication', () => {93test('user can log in and see dashboard', async ({ page }) => {94// Navigate to login95await page.goto('/login')9697// Fill login form98await page.getByLabel('Email').fill('[email protected]')99await page.getByLabel('Password').fill('password123')100await page.getByRole('button', { name: 'Sign In' }).click()101102// Verify redirect to dashboard103await expect(page).toHaveURL('/dashboard')104await expect(page.getByRole('heading', { name: 'Welcome' })).toBeVisible()105})106107test('shows error for invalid credentials', async ({ page }) => {108await page.goto('/login')109110await page.getByLabel('Email').fill('[email protected]')111await page.getByLabel('Password').fill('wrongpassword')112await page.getByRole('button', { name: 'Sign In' }).click()113114await expect(page.getByRole('alert')).toContainText('Invalid credentials')115await expect(page).toHaveURL('/login')116})117})118```119120## Playwright vs Cypress Comparison121122| Feature | Playwright | Cypress |123|---------|------------|---------|124| Browsers | Chromium, Firefox, WebKit | Chromium, Firefox, Electron (WebKit experimental) |125| Cross-browser | Full support | Limited |126| Parallelization | Built-in | Requires Cypress Cloud |127| Open source | Fully | Core only |128| Mobile testing | Device emulation | Limited |129| Debugging | Inspector, trace viewer | Time-travel UI |130| API testing | Built-in | Plugin required |131| Iframes | Full support | Limited |132133## Testing Vue Components with Data-Testid134135```typescript136// e2e/product-list.spec.ts137import { test, expect } from '@playwright/test'138139test('user can add product to cart', async ({ page }) => {140await page.goto('/products')141142// Use data-testid for reliable selectors143await page.getByTestId('product-card').first().click()144145// Verify product detail page146await expect(page.getByTestId('product-title')).toBeVisible()147148// Add to cart149await page.getByTestId('add-to-cart-button').click()150151// Verify cart updated152await expect(page.getByTestId('cart-count')).toHaveText('1')153})154```155156## Page Object Pattern for Vue Apps157158```typescript159// e2e/pages/LoginPage.ts160import { Page, Locator } from '@playwright/test'161162export class LoginPage {163readonly page: Page164readonly emailInput: Locator165readonly passwordInput: Locator166readonly submitButton: Locator167readonly errorMessage: Locator168169constructor(page: Page) {170this.page = page171this.emailInput = page.getByLabel('Email')172this.passwordInput = page.getByLabel('Password')173this.submitButton = page.getByRole('button', { name: 'Sign In' })174this.errorMessage = page.getByRole('alert')175}176177async goto() {178await this.page.goto('/login')179}180181async login(email: string, password: string) {182await this.emailInput.fill(email)183await this.passwordInput.fill(password)184await this.submitButton.click()185}186}187```188189```typescript190// e2e/auth.spec.ts191import { test, expect } from '@playwright/test'192import { LoginPage } from './pages/LoginPage'193194test('successful login', async ({ page }) => {195const loginPage = new LoginPage(page)196await loginPage.goto()197await loginPage.login('[email protected]', 'password123')198199await expect(page).toHaveURL('/dashboard')200})201```202203## Visual Regression Testing204205```typescript206test('homepage visual regression', async ({ page }) => {207await page.goto('/')208209// Full page screenshot comparison210await expect(page).toHaveScreenshot('homepage.png')211212// Element-specific screenshot213await expect(page.getByTestId('hero-section')).toHaveScreenshot('hero.png')214})215```216217## Running Tests218219```bash220# Run all tests221npx playwright test222223# Run in headed mode (see browser)224npx playwright test --headed225226# Run specific file227npx playwright test e2e/auth.spec.ts228229# Run in specific browser230npx playwright test --project=chromium231232# Debug mode233npx playwright test --debug234235# Generate test from actions236npx playwright codegen localhost:5173237```238239## Reference240- [Playwright Documentation](https://playwright.dev/)241- [Vue.js E2E Testing Recommendations](https://vuejs.org/guide/scaling-up/testing#e2e-testing)242- [Playwright Best Practices](https://playwright.dev/docs/best-practices)243