Skip to main content
Arre uses Playwright for comprehensive end-to-end (E2E) testing across multiple browsers and devices. Tests run against Firebase Emulators to ensure a consistent, reproducible testing environment.

Overview

The testing infrastructure provides:
  • Multi-browser coverage: Chromium, Firefox, and WebKit
  • Local Firebase Emulators for Auth and Firestore
  • Interactive UI mode for debugging
  • CI/CD integration with GitHub Actions
  • Parallel test execution for faster feedback

Getting Started

1

Install dependencies

Ensure all project dependencies are installed:
npm install
2

Install Playwright browsers

Playwright requires its own browser binaries:
npx playwright install
This downloads Chromium, Firefox, and WebKit test browsers.
3

Run tests

Execute the full test suite:
npm test

Running Tests

Run the complete test suite in headless mode:
npx playwright test
Or use the npm script:
npm test
This executes all tests across Chromium, Firefox, and WebKit.

Test Configuration

The Playwright configuration is defined in playwright.config.ts:6:
export default defineConfig({
  testDir: "./tests",
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: process.env.CI
    ? [["html"], ["json", { outputFile: "test-results.json" }]]
    : "html",
  
  use: {
    baseURL: "http://localhost:5173",
    trace: "on-first-retry",
  },
  
  projects: [
    { name: "chromium", use: { ...devices["Desktop Chrome"] } },
    { name: "firefox", use: { ...devices["Desktop Firefox"] } },
    { name: "webkit", use: { ...devices["Desktop Safari"] } },
  ],
  
  webServer: {
    command: "npm run dev",
    url: "http://localhost:5173",
    reuseExistingServer: !process.env.CI,
  },
});

Key Configuration Options

OptionDescription
baseURLhttp://localhost:5173 - Vite dev server
fullyParallelRuns test files in parallel for speed
retries2 retries on CI, 0 locally for faster debugging
workers1 worker on CI (stability), unlimited locally
traceCaptures trace on first retry for debugging
webServerAuto-starts dev server before tests
The webServer option automatically starts npm run dev before running tests and stops it afterward.

Browser Coverage

Tests run on three major browser engines:
  • Chromium - Chrome, Edge, Brave, Opera
  • Firefox - Firefox
  • WebKit - Safari
Mobile viewports are currently disabled to focus on the desktop MVP:
// TEMPORARILY DISABLED: Focusing on Web (Desktop) MVP
// { name: 'Mobile Chrome', use: { ...devices['Pixel 5'] } },
// { name: 'Mobile Safari', use: { ...devices['iPhone 12'] } },

Test Structure

Test Files

Tests are organized by feature area:
  • tests/new-task.spec.ts - New Task modal flows
  • tests/task-actions.spec.ts - Task operations (complete, reschedule, delete)
  • tests/project-management.spec.ts - Project CRUD and organization
  • tests/logbook.spec.ts - Logbook view and completed tasks
  • tests/full-flow.spec.ts - End-to-end user journeys
  • tests/utils.ts - Shared test utilities

Writing Tests

Use data-testid attributes for reliable selectors:
<button data-testid="btn-new-task-main">New Task</button>
Avoid CSS class selectors or text-based selectors that break when styling or copy changes. Use data-testid instead.

Test Examples

Manual Task Creation

From tests/new-task.spec.ts:45:
test('should create a manual task', async ({ page }) => {
  await openModal(page);
  await page.getByTestId('tab-manual').click();
  
  const taskTitle = 'E2E Test Task ' + Date.now();
  await page.getByTestId('input-title').fill(taskTitle);
  
  // Select energy level
  const modal = page.getByTestId('new-task-modal');
  await modal.getByText('high', { exact: true }).click();
  
  await page.getByTestId('btn-create-task').click();
  
  // Verify task appears in list
  await expect(page.getByText(taskTitle)).toBeVisible({ timeout: 10000 });
});

Full User Journey

From tests/full-flow.spec.ts:38:
test('should support the full task lifecycle across views', async ({ page }) => {
  // 1. Navigate to Upcoming
  await page.goto('/upcoming');
  await expect(page).toHaveURL('/upcoming');
  
  // 2. Create a task for tomorrow
  await openNewTaskModal(page);
  await page.getByTestId('tab-manual').click();
  
  const tomorrowTask = 'Meeting with Team ' + Date.now();
  await page.getByTestId('input-title').fill(tomorrowTask);
  
  // Set date to tomorrow
  const tomorrow = new Date();
  tomorrow.setDate(tomorrow.getDate() + 1);
  const dateStr = tomorrow.toISOString().split('T')[0];
  await page.locator('input[type="date"]').fill(dateStr);
  
  await page.getByTestId('btn-create-task').click();
  
  // 3. Verify it appears in Upcoming
  await expect(page.getByText(tomorrowTask)).toBeVisible({ timeout: 10000 });
});

Test Utilities

Shared utilities in tests/utils.ts:3:
export async function login(page: Page) {
  await page.goto('/login');
  await page.getByTestId('dev-login-button').click();
  // Wait for redirect to dashboard
  await expect(page).toHaveURL('/');
}

Firebase Emulator Integration

Tests run against Firebase Emulators for:
  • Authentication - Anonymous dev login
  • Firestore - Isolated test database

Emulator Configuration

In CI, tests start emulators automatically:
CI=true npx firebase emulators:exec "npx playwright test" --project demo-test
Environment variables for emulator mode:
VITE_FIREBASE_API_KEY="demo-api-key"
VITE_FIREBASE_AUTH_DOMAIN="demo-test.firebaseapp.com"
VITE_FIREBASE_PROJECT_ID="demo-test"
The emulator provides a clean, isolated environment for each test run. Data is ephemeral and discarded after tests complete.

Debugging Failed Tests

1

Check test output

Playwright displays detailed error messages:
Error: locator.click: Timeout 30000ms exceeded.
=========================== logs ===========================
waiting for getByTestId('btn-create-task')
2

View trace files

Traces are captured on first retry. Open them with:
npx playwright show-trace trace.zip
This shows:
  • Screenshot at each step
  • DOM snapshot
  • Network requests
  • Console logs
3

Use UI mode for live debugging

Run tests interactively:
npm run test:ui
Step through actions, pause execution, and inspect state.

CI Integration

Tests run automatically on every pull request via the AI QA Agent workflow. See the CI/CD documentation for details.
On CI, tests run in Chromium only with 2 retries for flaky test resilience.

Best Practices

  1. Use data-testid for selectors - Avoid brittle CSS or text selectors
  2. Wait for async operations - Use expect().toBeVisible({ timeout: 10000 }) for Firestore sync
  3. Generate unique test data - Use Date.now() to avoid conflicts
  4. Clean up test state - Firebase emulators reset between runs automatically
  5. Test user journeys, not implementation - Focus on what users do, not how code works

Next Steps

CI/CD Pipeline

Learn how tests integrate with GitHub Actions and the AI QA Agent

Deployment

Deploy to Firebase Hosting after tests pass

Build docs developers (and LLMs) love