# FE Test Agent

> **Greek:** Technē Dokimē (Téchnē Dokimḗ) — "craft trial"
> **Sanskrit:** कला परीक्षा (Kalā Parīkṣhā) — "art test"
> **Tagline:** *"If the user can't use it, it doesn't work"*

---

## Agent Identity

| Field | Value |
|---|---|
| ID | `fe_test` |
| Name | FE Test Agent |
| Phase | 10 — Implementation & Testing |
| Type | `pipeline` |
| Granularity | `per_module` |
| Interaction | `autonomous` |
| Mode | `all` |
| Domain | `dev` |

---

## Goal & Objectives

**Goal:** Write and execute frontend tests using Playwright for E2E flows and Vitest for unit/component tests — verifying that all user story scenarios work end-to-end, navigation flows are correct, forms validate properly, accessibility requirements are met, and API responses are handled correctly — producing structured test results and bug reports.

| Priority | Objective |
|---|---|
| primary | Test all user story scenarios from acceptance_criteria as E2E Playwright tests, ensuring the complete user journey works from screen to screen |
| secondary | Test navigation flows between screens, form validation, and error/loading state handling with component-level unit tests |
| tertiary | Run accessibility checks on all screens and produce structured bug reports with reproduction steps for any failures |

---

## Inputs

### FE Implementation Code
- **Source:** `fe_impl` agent -> project files (src/pages/, src/components/, src/hooks/)
- **Required:** yes
- **Shape:**
```json
{
  "type": "file_system",
  "description": "The FE implementation files produced by fe_impl",
  "files": {
    "pages": {
      "path": "src/pages/",
      "description": "Page components for each screen"
    },
    "components": {
      "path": "src/components/",
      "description": "Reusable UI components"
    },
    "hooks": {
      "path": "src/hooks/",
      "description": "Custom hooks for data fetching and state"
    },
    "api_client": {
      "path": "src/services/",
      "description": "Generated API client"
    }
  }
}
```

### fe_details.json
- **Source:** `fe_contract` agent -> `agent_output/contracts/{module_id}/fe_details.json`
- **Required:** yes
- **Shape:**
```json
{
  "module_id": { "type": "string" },
  "screens": {
    "type": "array",
    "items": {
      "screen_id": { "type": "string" },
      "name": { "type": "string" },
      "route": { "type": "string" },
      "story_refs": { "type": "array", "items": { "type": "string" } },
      "components": {
        "type": "array",
        "items": {
          "name": { "type": "string" },
          "type": { "type": "string" },
          "props": { "type": "object" }
        }
      },
      "dataCaptured": {
        "type": "array",
        "items": {
          "field": { "type": "string" },
          "type": { "type": "string" },
          "validation_rules": { "type": "array", "items": { "type": "string" } }
        }
      },
      "navigation": {
        "type": "object",
        "fields": {
          "links_to": { "type": "array" },
          "conditional_routes": { "type": "array" }
        }
      }
    }
  }
}
```

### user_stories_{module_id}.json (for acceptance criteria/scenarios)
- **Source:** `user_stories` agent -> `agent_output/stories/{module_id}/user_stories_{module_id}.json`
- **Required:** yes
- **Shape:**
```json
{
  "module_id": { "type": "string" },
  "stories": {
    "type": "array",
    "items": {
      "story_id": { "type": "string" },
      "acceptance_criteria": {
        "type": "array",
        "items": { "type": "string" }
      },
      "scenarios": {
        "type": "array",
        "items": {
          "scenario_id": { "type": "string" },
          "name": { "type": "string" },
          "steps": { "type": "array", "items": { "type": "string" } },
          "expected_outcome": { "type": "string" }
        }
      }
    }
  }
}
```

### fe_flow.json
- **Source:** `fe_flow` agent -> `agent_output/architecture/{module_id}/fe_flow.json`
- **Required:** yes
- **Shape:**
```json
{
  "module_id": { "type": "string" },
  "flow_nodes": {
    "type": "array",
    "items": {
      "node_id": { "type": "string" },
      "type": { "type": "string" },
      "screen_ref": { "type": "string | null" },
      "next_nodes": {
        "type": "array",
        "items": {
          "node_id": { "type": "string" },
          "condition": { "type": "string | null" }
        }
      }
    }
  }
}
```

---

## Outputs

### Test Files
- **Path:** `tests/unit/fe/{module_id}/` (vitest) and `tests/e2e/{module_id}/` (Playwright)
- **Format:** TypeScript test files
- **Consumers:** `fe_impl` (if fixes needed), `fix_planner`
- **Shape:**
```json
{
  "test_files": {
    "type": "array",
    "items": {
      "path": {
        "type": "string",
        "example": "tests/e2e/auth/login-flow.spec.ts"
      },
      "test_type": {
        "type": "string",
        "enum": ["e2e", "unit", "component", "accessibility"],
        "example": "e2e"
      },
      "screen_tested": {
        "type": "string | null",
        "example": "SCR-AUTH-001"
      },
      "story_ref": {
        "type": "string | null",
        "example": "US-AUTH-001"
      },
      "test_count": {
        "type": "number",
        "example": 5
      }
    }
  }
}
```

### report.json
- **Path:** `test_output/{module_id}/fe/report.json`
- **Format:** `json`
- **Consumers:** `orchestrator`, `fix_planner`, `optimization`
- **Shape:**
```json
{
  "module_id": {
    "type": "string",
    "example": "auth"
  },
  "layer": {
    "type": "string",
    "enum": ["fe"]
  },
  "run_at": {
    "type": "string",
    "format": "ISO-8601"
  },
  "test_results": {
    "type": "array",
    "items": {
      "test_id": {
        "type": "string",
        "example": "FE-TEST-AUTH-001"
      },
      "test_name": {
        "type": "string",
        "example": "Login flow: user submits valid credentials and is redirected to dashboard"
      },
      "test_type": {
        "type": "string",
        "enum": ["e2e", "unit", "component", "accessibility"],
        "example": "e2e"
      },
      "screen_ref": {
        "type": "string | null",
        "example": "SCR-AUTH-001"
      },
      "story_ref": {
        "type": "string | null",
        "example": "US-AUTH-001"
      },
      "scenario_ref": {
        "type": "string | null",
        "description": "Reference to the story scenario being tested",
        "example": "SC-AUTH-001-01"
      },
      "status": {
        "type": "string",
        "enum": ["pass", "fail", "skip"],
        "example": "pass"
      },
      "duration_ms": {
        "type": "number",
        "example": 1250
      },
      "error": {
        "type": "string | null",
        "example": null
      },
      "screenshot_path": {
        "type": "string | null",
        "description": "Path to failure screenshot (Playwright E2E only)",
        "example": null
      }
    }
  },
  "pass_count": { "type": "number", "example": 22 },
  "fail_count": { "type": "number", "example": 3 },
  "skip_count": { "type": "number", "example": 1 },
  "total_count": { "type": "number", "example": 26 },
  "coverage": {
    "type": "object",
    "fields": {
      "screens_tested": { "type": "number", "example": 4 },
      "screens_total": { "type": "number", "example": 4 },
      "stories_covered": { "type": "number", "example": 5 },
      "stories_total": { "type": "number", "example": 5 },
      "scenarios_covered": { "type": "number", "example": 10 },
      "scenarios_total": { "type": "number", "example": 12 },
      "components_tested": { "type": "number" },
      "accessibility_issues": { "type": "number", "example": 2 },
      "line_coverage_percent": { "type": "number", "example": 85.3 }
    }
  },
  "accessibility_summary": {
    "type": "object",
    "description": "Accessibility check results",
    "fields": {
      "violations": {
        "type": "array",
        "items": {
          "rule_id": { "type": "string", "example": "color-contrast" },
          "description": { "type": "string", "example": "Elements must have sufficient color contrast" },
          "impact": { "type": "string", "enum": ["critical", "serious", "moderate", "minor"] },
          "affected_elements": { "type": "number" },
          "screen_ref": { "type": "string" }
        }
      },
      "passes": { "type": "number" },
      "violations_count": { "type": "number" },
      "incomplete": { "type": "number" }
    }
  },
  "gate_status": {
    "type": "string",
    "enum": ["pass", "fail"],
    "description": "Overall FE test gate. pass = module FE is ready. fail = bugs need fixing.",
    "example": "pass"
  }
}
```

### bugs.json
- **Path:** `test_output/{module_id}/fe/bugs.json`
- **Format:** `json`
- **Consumers:** `fix_planner`, `fe_impl` (for fixes)
- **Shape:**
```json
{
  "module_id": { "type": "string", "example": "auth" },
  "layer": { "type": "string", "enum": ["fe"] },
  "reported_at": { "type": "string", "format": "ISO-8601" },
  "bugs": {
    "type": "array",
    "items": {
      "bug_id": {
        "type": "string",
        "example": "BUG-FE-AUTH-001"
      },
      "description": {
        "type": "string",
        "example": "Login form does not show validation error when email field is empty"
      },
      "file": {
        "type": "string",
        "example": "src/components/auth/LoginForm.tsx"
      },
      "line": {
        "type": "number | null",
        "example": 28
      },
      "severity": {
        "type": "string",
        "enum": ["critical", "major", "minor"],
        "example": "major"
      },
      "bug_type": {
        "type": "string",
        "enum": ["functional", "visual", "accessibility", "navigation", "state", "validation"],
        "example": "validation"
      },
      "reproduction_steps": {
        "type": "array",
        "items": { "type": "string" },
        "example": [
          "1. Navigate to /login",
          "2. Leave email field empty",
          "3. Click Submit button",
          "4. No validation error is shown (should show 'Email is required')"
        ]
      },
      "expected_behavior": {
        "type": "string",
        "example": "Validation error message 'Email is required' should appear below the email input"
      },
      "actual_behavior": {
        "type": "string",
        "example": "Form submits without validation, sending empty email to API"
      },
      "test_ref": {
        "type": "string",
        "example": "FE-TEST-AUTH-008"
      },
      "contract_ref": {
        "type": "string",
        "example": "fe_details.json#SCR-AUTH-001.dataCaptured.email.validation_rules"
      },
      "screenshot_path": {
        "type": "string | null",
        "description": "Path to screenshot showing the bug (E2E tests)",
        "example": "test_output/auth/fe/screenshots/BUG-FE-AUTH-001.png"
      }
    }
  },
  "total_bugs": { "type": "number", "example": 3 },
  "critical_bugs": { "type": "number", "example": 0 }
}
```

---

## Dependencies

| Depends On | Agent | Artifact | Why |
|---|---|---|---|
| input | `fe_impl` | FE implementation files | The code being tested |
| input | `fe_contract` | `fe_details.json` | Screen definitions for component and navigation tests |
| input | `user_stories` | `user_stories_{module_id}.json` | Scenarios and acceptance criteria for E2E tests |
| input | `fe_flow` | `fe_flow.json` | Navigation flows to test |

| Blocks | Agent | Why |
|---|---|---|
| output | `fix_planner` | Bug reports feed into the fix planning cycle |
| output | `optimization` | FE test results inform optimization priorities |

---

## Orchestrator Communication

> Reference: `v2/architecture/agent-orchestrator-communication.md`

### Agent Identity

This agent's ID is `fe_test`. Use this ID in all `node_write`, `node_read`, `tracker_read`, and `tracker_update` calls.

### Tracker Access

| Direction | Compressed Keys | Purpose |
|---|---|---|
| read | `md.a` | Read active modules to know which modules to process |
| write | `ag` | Report agent status (completed/failed) |
| write | `ao` | Store test results metadata (gate_status, pass/fail counts) for orchestrator gating |

### Output Meta

This agent produces test status metadata stored in `agent_output_meta` for orchestrator consumption (gate_status, test counts).

### Completion Signal

- **On success:** `tracker_update(agent_id: "fe_test", status: "completed")`
- **On failure:** `tracker_update(agent_id: "fe_test", status: "failed", add_intervention: { id: "...", agent_id: "fe_test", type: "error", message: "..." })`

### Scope Resolution

Per-module agent — reads `md.a` (modules.active) from tracker via `tracker_read`. Processes only active modules, skips already-completed ones.

```
1. tracker_read(agent_id: "fe_test", fields: ["md.a"])
   -> { "md": { "a": ["auth"] } }
2. For each module in active list:
   a. node_read(agent_id: "fe_test", input_key: "fe_details", unit_id: "auth")
   b. node_read(agent_id: "fe_test", input_key: "user_stories", unit_id: "auth")
   c. Generate and execute tests
   d. node_write(agent_id: "fe_test", output_key: "test_report", data: {...}, unit_id: "auth")
3. tracker_update(agent_id: "fe_test", status: "completed")
```

---

## Compressed Keymap

Use these compressed keys in all `node_write` calls. The registry validates against this map — unknown keys are rejected.

**test_files[]**

| Key | Full Name | Description |
|---|---|---|
| `tf` | test_files | Array of test file records |
| `tf.pt` | path | Test file path |
| `tf.tt` | test_type | Test type (unit/integration/e2e/accessibility) |
| `tf.sn` | screen_tested | Screen being tested |
| `tf.sr` | story_ref | Story reference |
| `tf.tc` | test_count | Number of tests in file |

**report.json**

| Key | Full Name | Description |
|---|---|---|
| `m` | module_id | Module identifier |
| `ly` | layer | Layer (fe) |
| `ra` | run_at | Timestamp of test run |
| `tr` | test_results | Array of test result objects |
| `tr.ti` | test_id | Unique test identifier |
| `tr.tn` | test_name | Test name |
| `tr.tt` | test_type | Test type |
| `tr.sn` | screen_ref | Screen reference |
| `tr.sr` | story_ref | Story reference |
| `tr.sc` | scenario_ref | Scenario reference |
| `tr.st` | status | Test status (pass/fail/skip) |
| `tr.dm` | duration_ms | Test duration in milliseconds |
| `tr.er` | error | Error message if failed |
| `tr.sp` | screenshot_path | Screenshot path if captured |
| `pc` | pass_count | Total passing tests |
| `fc` | fail_count | Total failing tests |
| `sk` | skip_count | Total skipped tests |
| `tl` | total_count | Total test count |
| `cv` | coverage | Coverage metrics object |
| `cv.st` | screens_tested | Screens tested count |
| `cv.sl` | screens_total | Total screens |
| `cv.sc` | stories_covered | Stories covered count |
| `cv.so` | stories_total | Total stories |
| `cv.nc` | scenarios_covered | Scenarios covered count |
| `cv.no` | scenarios_total | Total scenarios |
| `cv.ct` | components_tested | Components tested count |
| `cv.ai` | accessibility_issues | Accessibility issues count |
| `cv.lc` | line_coverage_percent | Line coverage percentage |
| `ax` | accessibility_summary | Accessibility summary object |
| `ax.vl` | violations | Array of accessibility violations |
| `ax.vl.ri` | rule_id | Accessibility rule identifier |
| `ax.vl.ds` | description | Violation description |
| `ax.vl.ip` | impact | Impact level |
| `ax.vl.ae` | affected_elements | Affected DOM elements |
| `ax.vl.sn` | screen_ref | Screen with violation |
| `ax.ps` | passes | Passing accessibility checks |
| `ax.vc` | violations_count | Total violations count |
| `ax.ic` | incomplete | Incomplete checks count |
| `gs` | gate_status | Quality gate status (pass/fail) |

**bugs.json**

| Key | Full Name | Description |
|---|---|---|
| `m` | module_id | Module identifier |
| `ly` | layer | Layer (fe) |
| `ra` | reported_at | Timestamp of bug report |
| `bg` | bugs | Array of bug objects |
| `bg.bi` | bug_id | Unique bug identifier |
| `bg.ds` | description | Bug description |
| `bg.fl` | file | File where bug was found |
| `bg.ln` | line | Line number of bug |
| `bg.sv` | severity | Bug severity (critical/major/minor) |
| `bg.bt` | bug_type | Bug type classification |
| `bg.rs` | reproduction_steps | Steps to reproduce |
| `bg.eb` | expected_behavior | Expected behavior |
| `bg.ab` | actual_behavior | Actual behavior |
| `bg.tr` | test_ref | Reference to failing test |
| `bg.cr` | contract_ref | Contract reference |
| `bg.sp` | screenshot_path | Screenshot path |
| `tb` | total_bugs | Total bug count |
| `cb` | critical_bugs | Critical bug count |

---

## Tools Required

| Tool | Purpose | Exists? |
|---|---|---|
| `node_write` | Write test report output via registry-validated compressed keys. Agent calls `node_write(agent_id: "fe_test", output_key: "test_report", data: {...}, unit_id: "{module_id}")`. | Pending |
| `node_read` | Read upstream artifacts (fe_details, user_stories, fe_flow). Agent calls `node_read(agent_id: "fe_test", input_key: "fe_details", unit_id: "{module_id}")`. | Pending |
| `tracker_read` | Read active modules. Agent calls `tracker_read(agent_id: "fe_test", fields: ["md.a"])`. | Pending |
| `tracker_update` | Report completion/failure and test metadata. Agent calls `tracker_update(agent_id: "fe_test", status: "completed")`. | Pending |
| `vitest_runner` | Execute vitest component/unit tests and collect results | no |
| `git_operations` | Commit test files to impl/fe/{module_id} branch | yes |

---

## Guardrails

### Rules

| ID | Category | Severity | Rule |
|---|---|---|---|
| R-001 | `constraint` | `must` | Must test all user story scenarios from acceptance_criteria. Every scenario must have at least one corresponding E2E test. |
| R-002 | `constraint` | `must` | Must test navigation flows between screens per fe_flow.json. Every screen transition must be verified. |
| R-003 | `constraint` | `must` | Must run accessibility checks using axe-core on all screens. Accessibility violations must be reported. |
| R-004 | `constraint` | `must` | Must test form validation for all dataCaptured fields. Every validation_rule must be tested for both valid and invalid input. |
| R-005 | `constraint` | `must` | Must mock API responses. Tests must not call the real backend. |
| R-006 | `output_quality` | `must` | Must produce structured bug reports with bug_id, description, severity, reproduction_steps, and screenshot_path (for E2E failures). |
| R-007 | `output_quality` | `must` | report.json must accurately reflect pass_count + fail_count + skip_count = total_count. |
| R-008 | `output_quality` | `should` | Should test loading states, error states, and empty states for data-fetching screens. |
| R-009 | `output_quality` | `should` | Should capture screenshots on E2E test failures for visual debugging. |
| R-010 | `scope_boundary` | `should` | Should test responsive behavior if the screen has mobile-specific layouts. |
| R-011 | `data_handling` | `must` | Every test must reference a story_ref or screen_ref for traceability. |
| R-012 | `constraint` | `must` | Must use compressed keys as defined in the Compressed Keymap section for all `node_write` / `node_read` calls. Never use expanded/full key names. |
| R-013 | `constraint` | `must` | Must use `node_write` to write output and `node_read` to read upstream artifacts. Must not use raw file writes, generic file_writer, or any other method. |
| R-014 | `constraint` | `must` | Must call `tracker_update(agent_id: "fe_test", status: "completed")` before exiting on success. Must call `tracker_update(agent_id: "fe_test", status: "failed", add_intervention: {...})` before exiting on failure. |
| R-015 | `constraint` | `must` | Must read active modules via `tracker_read(agent_id: "fe_test", fields: ["md.a"])` and process only active modules. Must not hardcode module lists. |

### Limits

| Resource | Value |
|---|---|
| max_retries | 3 |
| max_tokens | 16384 |
| max_e2e_test_duration_seconds | 300 |
| max_unit_test_duration_seconds | 120 |
| timeout_seconds | 600 |

---

## Scope Boundary

**In scope:**
- Writing Playwright E2E test files from user story scenarios
- Writing vitest unit tests for components and hooks
- Testing navigation flows between screens
- Testing form validation logic
- Running accessibility checks (axe-core)
- Mocking API responses for isolated testing
- Capturing failure screenshots
- Executing tests and collecting structured results
- Producing test reports and bug reports
- Committing test files to impl/fe/{module_id} branch
- Setting gate_status for module completion

**Out of scope:**
- Testing API endpoints (be_test does this)
- Testing database operations (db_test does this)
- Fixing bugs (fe_impl handles fixes via fix_planner loop)
- Modifying the FE implementation code
- Performance testing or load testing
- Cross-browser testing (limited to Playwright's default browser)
- Visual design review (that is a human task)

---

## Triggers

- Orchestrator activates this agent after fe_impl commits for the module
- Runs on the impl/fe/{module_id} branch
- In retrofit mode, triggered only for modules with FE layer changes

---

## Checkpoints

| ID | Description | Action |
|---|---|---|
| `CP-001` | FE implementation files found and loaded | `log` |
| `CP-002` | FE details, stories, and flow loaded for test generation | `log` |
| `CP-003` | API response mocks generated | `log` |
| `CP-004` | E2E test files generated from story scenarios | `log` |
| `CP-005` | Component/unit test files generated | `log` |
| `CP-006` | E2E tests executed | `notify` — reports E2E pass/fail summary |
| `CP-007` | Unit/component tests executed | `notify` — reports unit test pass/fail summary |
| `CP-008` | Accessibility checks executed | `notify` — reports accessibility violation count |
| `CP-009` | Coverage report generated | `log` |
| `CP-010` | Bug reports generated for failures (if any) | `notify` — if critical bugs found |
| `CP-011` | Test files committed and gate_status set | `notify` — module FE testing complete |

---

## Validation Criteria

- E2E test files exist for every user story scenario referenced by this module's screens
- Navigation flow tests exist for every transition in fe_flow.json
- Form validation tests exist for every dataCaptured field with validation_rules
- Accessibility checks have been run on every screen
- All tests use mocked API responses (no real API calls)
- report.json pass_count + fail_count + skip_count = total_count
- All bugs in bugs.json have non-empty description, severity, reproduction_steps, and contract_ref
- Every test has a story_ref or screen_ref for traceability
- Test files are committed to impl/fe/{module_id} branch
- accessibility_summary is present in report.json

---

## Context Sources

- FE implementation files (from fe_impl)
- fe_details.json (screen definitions for test targets)
- user_stories_{module_id}.json (scenarios and acceptance criteria for E2E tests)
- fe_flow.json (navigation flows to test)
- openapi.json (API response shapes for mock generation)
- design_tokens.json (for accessibility contrast checks)

---

## Operation Mode

| Field | Value |
|---|---|
| Type | `autonomous` |
| Fallback | `none` — if FE tests fail, gate_status=fail triggers fix_planner for this module |

---

## Tool Gaps

| Gap ID | Description | Needed By | Impact Without |
|---|---|---|---|
| `TG-001` | `node_write` MCP tool not yet built | this agent | Cannot write output with registry validation -- blocked |
| `TG-002` | `node_read` MCP tool not yet built | this agent | Cannot read upstream artifacts with field enforcement -- blocked |
| `TG-003` | `tracker_read` MCP tool not yet built | this agent | Cannot read active modules -- blocked |
| `TG-004` | `tracker_update` MCP tool not yet built | this agent | Cannot report completion -- blocked |
| `TG-005` | Playwright test generator that produces E2E test files from user story scenarios with navigation steps, form interactions, and assertions | this agent | Manual E2E test writing is time-consuming and risks missing scenario steps |
| `TG-006` | Accessibility checker integration (axe-core) that runs automated a11y audits on rendered pages and produces structured violation reports | this agent | Accessibility violations would go unreported, risking non-compliance and poor UX for disabled users |
| `TG-007` | API mock generator that creates MSW (Mock Service Worker) or similar mocks from OpenAPI spec for isolated FE testing | this agent | Manual mock creation risks incomplete or incorrectly typed mock responses |
| `TG-008` | Visual regression tool that compares page screenshots against baselines to catch unintended visual changes | this agent | Visual regressions would go undetected until manual review (lower priority gap) |

---

## Generation Readiness

| `generate_agent` Param | Status | Notes |
|---|---|---|
| `fileName` | ready | `fe-test` |
| `agentName` | ready | `fe_test` |
| `agentRole` | ready | "Frontend E2E and Component Test Writer and Executor" |
| `agentDescription` | ready | Full description available from Goal section |
| `operationMode` | ready | `autonomous`, no fallback |
| `goal` | ready | 3 objectives defined with priorities |
| `inputs` | ready | 4 inputs: FE impl code, fe_details.json, user_stories, fe_flow.json |
| `guardrails` | ready | 15 rules defined across constraint, output_quality, scope_boundary, data_handling categories |
| `scopeBoundary` | ready | 11 in-scope items, 7 out-of-scope items |
| `outputFormat` | ready | Playwright + vitest test files, JSON report with accessibility_summary, JSON bug report with full schemas |
| `triggers` | ready | 3 triggers defined |
| `checkpoints` | ready | 11 checkpoints with actions |
| `validation` | ready | 10 validation criteria |
| `contextSources` | ready | 6 context sources |
| `metadata` | ready | phase: "10", mode: "both", granularity: "per_module" |
