---
model: sonnet
---

# /tas-e2e-web $ARGUMENTS

Role: SE / QA
Generate Playwright E2E test scripts from an E2E Scenario markdown file.

## IMPORTANT — Layer 3: Web E2E Scripts
- Create Playwright test scripts from E2E-Scenario (markdown)
- Scripts located in `apps/web/e2e/flows/`
- REUSE helpers from Layer 2 functional tests (`features/`)
- Cross-browser testing (Chromium, Firefox, WebKit)
- Chain functional test steps into end-to-end flows

## Actions

### Step 1: Identify Scenario
1. `$ARGUMENTS` is path to `E2E-Scenario-*.md`
2. If not provided: scan `docs/e2e-scenarios/E2E-Scenario-*.md`
3. List scenarios and ask user to pick

### Step 2: Check Prerequisites
1. Check `apps/web/` exists. If not → report:
   > "Web app doesn't exist. If project only has mobile, use `/tas-e2e-mobile`."
   > STOP, DO NOT create file.
2. Read scenario file to get steps, FT references, FT Reuse Map, test data, scenario type.
3. Read `apps/web/playwright.config.ts` (if exists)

### Step 3: Find Functional Test Helpers
From FT Reuse Map:
1. Find source files in `apps/web/e2e/features/`
2. Verify helper functions and page objects exist
3. If helper missing → create stub with `// TODO: implement helper`
4. Build import map: FT ID → file path → function name

### Step 4: Generate E2E Script

**File output**: `apps/web/e2e/flows/{scenario-slug}.spec.ts`

**Structure**:
```typescript
/**
 * E2E Flow: {Scenario Name}
 * Scenario: {Scenario_ID}
 * Features: {Feature_IDs comma-separated}
 * Type: {single-stack | cross-stack}
 *
 * Generated by /tas-e2e-web
 * Spec: {path to E2E-Scenario-*.md}
 */

import { test, expect, Page } from '@playwright/test';
import { loadTestData, getCredentials } from '../helpers/data-loader';

// Layer 2 helpers (reuse from functional tests)
import { fillLoginForm, verifyLoginSuccess } from '../features/{feature-slug}/helpers';
import { viewList } from '../features/{feature-slug-2}/helpers';

const testData = loadTestData();

test.describe('E2E Flow: {Scenario Name}', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto(testData.baseUrl);
  });

  // Main Flow
  test.describe('{PROJECT}_E2E_001_H: {Flow Title}', () => {
    test('Complete flow', async ({ page }) => {
      // Step 1: Login (reuses AL_F002_AC1_FT_001_H)
      const creds = getCredentials();
      await fillLoginForm(page, creds.email, creds.password);
      await verifyLoginSuccess(page);

      // Step 2: View list (reuses AL_F003_AC1_FT_001_H)
      await viewList(page);

      // Step 3: New logic (no FT reference)
      await page.getByTestId('scan-button').click();
      await expect(page.getByTestId('scan-result')).toBeVisible();
    });
  });

  // Cross-browser validation
  test.describe('{PROJECT}_E2E_001_H - Cross-browser', () => {
    test('should complete flow on all browsers', async ({ page, browserName }) => {
      test.info().annotations.push({ type: 'browser', description: browserName });
    });
  });

  // Alternate / Error flows
  test.describe('{PROJECT}_E2E_002_N: {Error Flow}', () => {
    test('should handle {error condition}', async ({ page }) => {});
  });
});
```

### Step 5: Update package.json Script
```json
"e2e:flow:{scenario-slug}": "npx playwright test e2e/flows/{scenario-slug}"
```

### Step 6: Generate Execution Report (Optional)
From `.tas/templates/E2E-Execution-Report.md`:
- Browser-specific results
- Viewport testing results
- Performance metrics (LCP, INP, CLS)

## File Structure Output
```
apps/web/e2e/flows/
├── {scenario-1-slug}.spec.ts
├── {scenario-2-slug}.spec.ts
└── ...
```

## Reuse Strategy
```
Layer 2 (features/)          Layer 3 (flows/)
┌───────────────────┐       ┌──────────────────────────┐
│ helpers.ts        │──────>│ {scenario}.spec.ts        │
│  - fillLogin(page)│       │  import { fillLogin }     │
│  - verify(page)   │       │  import { viewList }      │
└───────────────────┘       │                          │
┌───────────────────┐       │  Step 1: fillLogin(page) │
│ helpers.ts        │──────>│  Step 2: viewList(page)  │
│  - viewList(page) │       │  Step 3: new logic       │
└───────────────────┘       └──────────────────────────┘
```

Note: Web helpers receive `page: Page` parameter (unlike mobile using global Detox API).

## Run Tests
```bash
yarn e2e:flow:{scenario-slug}
npx playwright test e2e/flows/
npx playwright test --project=firefox e2e/flows/{scenario}
npx playwright test --ui e2e/flows/{scenario}
npx playwright test e2e/flows/{scenario} --reporter=html
```

## Principles
- MUST reuse helpers from Layer 2 when possible
- Web helpers MUST receive `page: Page` parameter (dependency injection)
- Scripts MUST be runnable from CLI
- If `apps/web/` doesn't exist → graceful error
- Cross-browser: Chromium, Firefox, WebKit
- describe block uses full E2E ID
- Import paths: relative from `flows/` up to `features/`
