Skip to content
TEA docs in plain text: llms-full.txt

How to Run Automate with TEA

Use the Automate skill’s expand mode to add tests for implemented features. It also supports red mode for acceptance scaffolds before implementation. Your prompt selects the mode; the automate command and TA menu code default to expand.

Create runs execute the generated tests and repair test issues by default, for up to three rounds. The summary records the repairs and any remaining failures. Real product defects keep their failing assertions and appear as findings.

For unattended generation and JSON outcomes, use the tea-automate CLI. It supports red and expand, saved-run continuation, validation, and edits.

Choose red mode before implementation or expand mode for existing code. Both modes support Create, Resume, Validate, and Edit. Each keeps its customization files and saved progress.

  1. Request. Invoke the skill and say what to cover, for example Expand coverage for story-profile-management.md: API tests for P0 and P1, one E2E test for the P0 journey.
  2. Let it run. TEA generates the tests, runs them, and repairs test issues for up to three rounds.
  3. Read the summary. {test_artifacts}/automate/automation-summary-{run_key}.md lists the files added, the repairs, and any remaining failures; a product defect keeps its failing assertion.
  4. Verify. Run the suite with your project’s test command and review the diff before you commit.

The sections below cover each step’s options, with examples.

  • Feature already exists and works
  • Want to add test coverage to existing code
  • Expanding existing test suite
  • Adding tests to legacy code

For acceptance tests before implementation, use red mode.

  • Test framework setup complete (run framework if needed)
  • Feature implemented and working

This guide uses Playwright examples. If using Cypress, commands and syntax will differ.

  • Claude Code / Cursor / Windsurf: /bmad-tea-automate
  • Codex: $bmad-tea-automate
  • Inside a /bmad-tea chat: TA

Acceptance scaffolds before implementation select red; coverage for existing code selects expand. With no task mode signal, Automate and TA default to expand. Conflicting task mode signals prompt one mode question in an interactive run; an unattended run uses expand. Every default fallback states the selected mode in the final summary. Existing customization files and interrupted progress remain usable.

Full invocation rules: Invoking a TEA Skill.

TEA will ask for context about what you’re testing.

Section titled “Option A: BMad-Integrated Mode (Recommended)”

If you have BMad artifacts (stories, test designs, PRDs):

What are you testing?

I'm testing the user profile feature we just implemented.
Story: story-profile-management.md
Test Design: test-design/test-design-epic-1.md

Reference documents:

  • Story file with acceptance criteria
  • Test design document (if available)
  • PRD sections relevant to this feature
  • Tech spec (if available)

Existing tests:

We have basic tests in tests/e2e/profile-view.spec.ts
Avoid duplicating that coverage

TEA will analyze your artifacts and generate tests that:

  • Cover acceptance criteria from the story
  • Follow priorities from test design (P0 → P1 → P2)
  • Avoid duplicating existing tests
  • Include edge cases and error scenarios

If you’re using TEA Solo or don’t have BMad artifacts:

What are you testing?

TodoMVC React application at https://todomvc.com/examples/react/dist/
Features: Create todos, mark as complete, filter by status, delete todos

Specific scenarios to cover:

- Creating todos (happy path)
- Marking todos as complete/incomplete
- Filtering (All, Active, Completed)
- Deleting todos
- Edge cases (empty input, long text)

TEA will analyze the application and generate tests based on your description.

TEA will ask which test levels to generate:

Options:

  • E2E tests: Full browser-based user workflows
  • API tests: Backend endpoint testing (faster, more reliable)
  • Component tests: UI component testing in isolation (framework-dependent)
  • Mix: Combination of levels (recommended)

Example response:

Generate:
- API tests for all CRUD operations
- E2E tests for critical user workflows (P0)
- Focus on P0 and P1 scenarios
- Skip P3 (low priority edge cases)

TEA generates a test suite with multiple test levels.

Vanilla Playwright:

import { test, expect } from '@playwright/test';
test.describe('Profile API', () => {
let authToken: string;
test.beforeAll(async ({ request }) => {
// Manual auth token fetch
const response = await request.post('/api/auth/login', {
data: { email: 'test@example.com', password: 'password123' },
});
const { token } = await response.json();
authToken = token;
});
test('should fetch user profile', async ({ request }) => {
const response = await request.get('/api/profile', {
headers: { Authorization: `Bearer ${authToken}` },
});
expect(response.ok()).toBeTruthy();
const profile = await response.json();
expect(profile).toMatchObject({
id: expect.any(String),
name: expect.any(String),
email: expect.any(String),
});
});
test('should update profile successfully', async ({ request }) => {
const response = await request.patch('/api/profile', {
headers: { Authorization: `Bearer ${authToken}` },
data: {
name: 'Updated Name',
bio: 'Test bio',
},
});
expect(response.ok()).toBeTruthy();
const updated = await response.json();
expect(updated.name).toBe('Updated Name');
expect(updated.bio).toBe('Test bio');
});
test('should validate email format', async ({ request }) => {
const response = await request.patch('/api/profile', {
headers: { Authorization: `Bearer ${authToken}` },
data: { email: 'invalid-email' },
});
expect(response.status()).toBe(400);
const error = await response.json();
expect(error.message).toContain('Invalid email');
});
test('should require authentication', async ({ request }) => {
const response = await request.get('/api/profile');
expect(response.status()).toBe(401);
});
});

With Playwright Utils: the spec imports the shared module, which merges the API and auth fixtures (setup).

import { test, expect } from '../support/merged-fixtures';
import { z } from 'zod';
const ProfileSchema = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
});
test.describe('Profile API', () => {
test('should fetch user profile', async ({ apiRequest, authToken }) => {
const { status, body } = await apiRequest({
method: 'GET',
path: '/api/profile',
headers: { Authorization: `Bearer ${authToken}` },
}).validateSchema(ProfileSchema); // Chained validation
expect(status).toBe(200);
// Schema already validated, type-safe access
expect(body.name).toBeDefined();
});
test('should update profile successfully', async ({ apiRequest, authToken }) => {
const { status, body } = await apiRequest({
method: 'PATCH',
path: '/api/profile',
body: { name: 'Updated Name', bio: 'Test bio' },
headers: { Authorization: `Bearer ${authToken}` },
}).validateSchema(ProfileSchema); // Chained validation
expect(status).toBe(200);
expect(body.name).toBe('Updated Name');
});
test('should validate email format', async ({ apiRequest, authToken }) => {
const { status, body } = await apiRequest({
method: 'PATCH',
path: '/api/profile',
body: { email: 'invalid-email' },
headers: { Authorization: `Bearer ${authToken}` },
});
expect(status).toBe(400);
expect(body.message).toContain('Invalid email');
});
});

apiRequest returns { status, body }, supports chained Zod validation, and retries 5xx responses. Use retryConfig: { maxRetries: 0 } when testing a server-error response.

import { test, expect } from '@playwright/test';
test('should edit profile', async ({ page }) => {
// Login
await page.goto('/login');
await page.getByLabel('Email').fill('test@example.com');
await page.getByLabel('Password').fill('password123');
await page.getByRole('button', { name: 'Sign in' }).click();
// Edit profile
await page.goto('/profile');
await page.getByRole('button', { name: 'Edit Profile' }).click();
await page.getByLabel('Name').fill('Updated Name');
await page.getByRole('button', { name: 'Save' }).click();
// Verify success
await expect(page.getByText('Profile updated')).toBeVisible();
await page.reload();
await page.getByRole('button', { name: 'Edit Profile' }).click();
await expect(page.getByLabel('Name')).toHaveValue('Updated Name');
});

TEA generates validation and edge-case tests according to the priorities you supplied.

Fixtures (tests/support/fixtures/profile.ts):
Section titled “Fixtures (tests/support/fixtures/profile.ts):”

Vanilla Playwright:

import { test as base, Page } from '@playwright/test';
type ProfileFixtures = {
authenticatedPage: Page;
testProfile: {
name: string;
email: string;
bio: string;
};
};
export const test = base.extend<ProfileFixtures>({
authenticatedPage: async ({ page }, use) => {
// Manual login flow
await page.goto('/login');
await page.getByLabel('Email').fill('test@example.com');
await page.getByLabel('Password').fill('password123');
await page.getByRole('button', { name: 'Sign in' }).click();
await page.waitForURL(/\/dashboard/);
await use(page);
},
testProfile: async ({ request }, use) => {
// Static test data
const profile = {
name: 'Test User',
email: 'test@example.com',
bio: 'Test bio',
};
await use(profile);
},
});

With Playwright Utils: the custom fixture lives in its own file and joins the shared module, so specs keep one entry point.

tests/support/fixtures/profile-fixture.ts
import { test as base } from '@playwright/test';
import { faker } from '@faker-js/faker';
type ProfileFixtures = {
testProfile: { name: string; email: string; bio: string };
};
export const test = base.extend<ProfileFixtures>({
testProfile: async ({}, use) => {
// Dynamic test data with faker
await use({
name: faker.person.fullName(),
email: faker.internet.email(),
bio: faker.person.bio(),
});
},
});
// tests/support/merged-fixtures.ts: add the fixture to the existing mergeTests call
import { test as profileFixture } from './fixtures/profile-fixture';
export const test = mergeTests(apiRequestFixture, authFixture, profileFixture /* , ... */);

Usage:

import { test, expect } from '../support/merged-fixtures';
test('should update profile', async ({ apiRequest, authToken, testProfile }) => {
const { status } = await apiRequest({
method: 'PATCH',
path: '/api/profile',
body: testProfile,
headers: { Authorization: `Bearer ${authToken}` },
});
expect(status).toBe(200);
});

authToken is the Bearer token for API requests. The merged auth-session fixtures also open page with the storage state the auth provider saved, so a browser test starts signed in once the provider performs a real sign-in; see auth-session. Add cleanup if the test creates persistent records.

TEA also generates:

# Test Suite
## Running Tests
### All Tests
npm test
### Specific Levels
npm run test:api # API tests only
npm run test:e2e # E2E tests only
npm run test:smoke # Smoke tests (@smoke tag)
### Single File
npx playwright test tests/api/profile.spec.ts
## Test Structure
tests/
├── api/ # API tests (fast, reliable)
├── e2e/ # E2E tests (full workflows)
├── support/ # merged-fixtures.ts, fixtures, helpers
└── README.md
## Writing Tests
Follow the patterns in existing tests:
- Use fixtures for authentication
- Network-first patterns (no hard waits)
- Explicit assertions
- Self-cleaning tests

The checklist is part of the automation summary at {test_artifacts}/automate/automation-summary-{run_key}.md. The run_key names the scope: story-{story_key} or epic-{epic_num} when you name a story or epic, target-{slug} for a feature or path with neither, and system for a run across the whole codebase.

## Test Quality Checklist
✅ All tests pass on first run
✅ No hard waits (waitForTimeout)
✅ No conditionals for flow control
✅ Assertions are explicit
✅ Tests clean up after themselves
✅ Tests can run in parallel
✅ Execution time < 1.5 minutes per test
✅ Test files ≤ 1000 lines

Run the generated tests against the implemented feature:

Terminal window
npm run test:e2e # Playwright via TEA's framework scaffold, which carries the config path
npx cypress run # Cypress

Example output; the counts depend on the scope you requested:

Running 6 tests using 4 workers
✓ tests/api/profile.spec.ts (4 tests) - 2.1s
✓ tests/e2e/profile-workflow.spec.ts (2 tests) - 5.3s
6 passed (7.4s)

Investigate each failure: it can reveal a product defect, an incorrect assertion, or missing test setup.

Check which scenarios are covered:

Terminal window
npx playwright show-report

For code coverage, use whatever your project already has. TEA’s framework skill does not add a coverage script.

Compare against:

  • Acceptance criteria from story
  • Test priorities from test design
  • Edge cases and error scenarios
  • API tests: Fast, reliable backend testing
  • E2E tests: Critical user workflows
  • Component tests: UI component testing (if requested)
  • Fixtures: Shared utilities and setup

TEA supports component testing using framework-appropriate tools:

Your FrameworkComponent Testing ToolTests Location
CypressCypress Component Testingtests/component/
PlaywrightVitest + React Testing Librarytests/component/ or src/**/*.test.tsx

Component tests use separate tooling from E2E tests:

  • Cypress users: TEA generates Cypress Component Tests
  • Playwright users: TEA generates Vitest + React Testing Library tests
  • Network-first patterns: Wait for the response a user action triggers
  • Deterministic tests: Explicit assertions and controlled setup
  • Self-cleaning: Tests don’t leave test data behind
  • Parallel-safe setup: Isolated data for concurrent runs
  • Updated README: How to run tests
  • Test structure explanation: Where tests live
  • Definition of Done: Quality standards

Run test-design before automate for better results:

/bmad-tea-test-design # risk assessment, priorities
/bmad-tea-automate # generate tests based on those priorities

TEA will focus on P0/P1 scenarios and skip low-value tests.

Not everything needs E2E tests:

Good strategy:

- P0 scenarios: API + E2E tests # reserve E2E for critical user journeys
- P1 scenarios: API tests only # API tests run ~10x faster, no browser flakiness
- P2 scenarios: API tests (happy path)
- P3 scenarios: Skip or add later

Tell TEA about existing tests:

We already have tests in:
- tests/e2e/profile-view.spec.ts (viewing profile)
- tests/api/auth.spec.ts (authentication)
Don't duplicate that coverage

TEA will analyze existing tests and only generate new scenarios.

Create runs execute the generated tests, classify failures, apply the matching test fixes, and rerun up to three rounds. Selector failures use resilient locators; timing and hard-wait failures use observable readiness; data and network failures use controlled fixtures and request handling. A real product defect remains a finding with the assertion intact. The run reports blocked execution when its environment cannot run the tests.

Validate checks the selected outputs and reports execution evidence. Edit checks the requested changes. These operations never run test repairs or require the full suite to pass.

Run and heal is enabled by default. Customize auto_validate, auto_heal_failures, max_healing_iterations, and use_mcp_healing in the automation settings.

If browser automation is configured (tea_browser_automation: "auto" or "cli" or "mcp"), TEA can use browser tools during automate for:

  • Healing mode: Fix broken selectors using CLI snapshots or MCP DOM analysis
  • Recording mode: Verify selectors with CLI snapshots or MCP browser, capture network requests
  • Evidence capture: CLI traces and screenshots for test validation

TEA uses browser tools automatically when available and appropriate; it does not prompt.

See Configure Browser Automation for setup.

Don’t generate all tests at once:

Run 1: P0 tests only (critical path)
Run 2: P1 tests (high value scenarios); avoid duplicating P0 coverage
Run 3: P2 tests (if time permits)

Run and review each batch before generating the next one.

Tests pass but don’t cover all scenarios.

TEA wasn’t given complete context.

Provide more details:

Generate tests for:
- All acceptance criteria in story-profile.md
- Error scenarios (validation, authorization)
- Edge cases (empty fields, long inputs)

TEA generated 50 tests for a simple feature.

Didn’t specify priorities or scope.

Be specific:

Generate ONLY:
- P0 and P1 scenarios
- API tests for all scenarios
- E2E tests only for critical workflows
- Skip P2/P3 for now

New tests cover the same scenarios as existing tests.

Didn’t tell TEA about existing tests.

Specify existing coverage:

We already have these tests:
- tests/api/profile.spec.ts (GET /api/profile)
- tests/e2e/profile-view.spec.ts (viewing profile)
Generate tests for scenarios NOT covered by those files

Use the Automate skill’s red mode to generate acceptance test scaffolds before implementation. Ask /bmad-tea-automate, $bmad-tea-automate, or TA for red mode. TEA emits these scaffolds with test.skip() so they can be reviewed, linked into the story, and activated task-by-task during implementation.

  • You are about to implement a feature
  • You want to follow TDD workflow (red → green → refactor)
  • You want tests to guide your implementation
  • You’re practicing acceptance test-driven development

For tests of implemented features, use Automate.

  • Test framework setup complete (run framework if needed)
  • Story or feature defined with acceptance criteria

The examples use Playwright. Adapt the commands and selectors for Cypress.

  • Claude Code / Cursor / Windsurf: /bmad-tea-automate
  • Codex: $bmad-tea-automate
  • Inside a /bmad-tea chat: TA

Request red mode explicitly:

Generate red acceptance scaffolds for this story before implementation.

Acceptance scaffolds before implementation select red; coverage for existing code selects expand. With no task mode signal, Automate and TA default to expand. Conflicting task mode signals prompt one mode question in an interactive run; an unattended run uses expand. Every default fallback states the selected mode in the final summary. Existing customization files and interrupted ATDD progress remain usable.

Create runs verify the generated tests in a disposable copy with their scaffold skips activated. TEA uses tea-atdd-red-check for compatible browserless loopback Playwright tests when the command is installed, and tea-unit-check for Vitest and Jest scaffolds. Browser tests and projects needing their own environment or services use the installed project test runner with the original configuration and environment. Execution respects the project’s existing test budgets, and the summary names the runner and any fallback reason. Tests must fail for the acceptance behavior that implementation will add. TEA repairs syntax, imports, selectors, data, timing, network, hard waits, and setup that cause the wrong failure, for up to three rounds. The implementation handoff retains its deliberate skipped scaffolds. The summary records repairs, intended red failures, and unresolved problems. Validate reports findings and Edit checks the changes you requested; neither operation repairs tests.

Full invocation rules: Invoking a TEA Skill.

TEA will ask for:

Story/Feature Details:

We're adding a user profile page where users can:
- View their profile information
- Edit their name and email
- Upload a profile picture
- Save changes with validation

Acceptance Criteria:

Given I'm logged in
When I navigate to /profile
Then I see my current name and email
Given I'm on the profile page
When I click "Edit Profile"
Then I can modify my name and email
Given I've edited my profile
When I click "Save"
Then my changes are persisted
And I see a success message
Given I enter an invalid email address
When I try to save
Then the update returns status 400
And the error message contains "Invalid email format"

TEA builds a criterion registry before it generates tests. Existing AC-<n> ids remain unchanged. For criteria without ids, TEA reserves the supplied ids, visits unnamed criteria in source order, and assigns each the lowest unused AC-<n> id. Every executable test title carries exactly one id from that registry.

TEA emits exactly one red-phase leaf scaffold per declared criterion. Secondary branches and journeys remain implementation-checklist work until green-phase automation. Each scaffold puts one direct criterion assertion first. That assertion isolates the exact newly promised status, scalar, or property before broad object, schema, or secondary checks. API setup calls to unimplemented endpoints keep their responses opaque until this assertion runs. E2E scaffolds place the complete browser journey inside the first potentially failing assertion boundary.

Reference Documents (optional):

  • Point to your story file
  • Reference PRD or tech spec
  • Link to test design (if you ran test-design first)

TEA will ask what test levels to generate:

Options:

  • E2E tests (browser-based, full user journey)
  • API tests (backend only, faster)
  • Component tests (UI components in isolation)
  • Mix of levels (see API Tests First, E2E Later tip)

TEA generates component tests using framework-appropriate tools:

Your FrameworkComponent Testing Tool
CypressCypress Component Testing (*.cy.tsx)
PlaywrightVitest + React Testing Library (*.test.tsx)

Example response:

Generate:
- API tests for profile CRUD operations
- E2E tests for the complete profile editing flow
- Component tests for ProfileForm validation (if using Cypress or Vitest)
- Focus on P0 and P1 scenarios

TEA generates red-phase test scaffolds in appropriate directories:

Both API variants import the project’s existing fixture entry point, which exports test with an authToken fixture for the seeded test@example.com user: tests/support/fixtures.ts in vanilla Playwright, tests/support/merged-fixtures.ts with Playwright Utils. The fixture must supply a valid Bearer token through the working authentication flow before profile scaffolds are activated. Verify authentication independently and report fixture setup failures as prerequisite failures; criterion red evidence comes from the profile response’s first status assertion. For Playwright Utils, build that auth fixture with a registered provider and createAuthFixtures() and merge it into the module, as auth-session shows. The examples reuse that fixture and send its token on every profile request.

Vanilla Playwright:

import { test } from '../support/fixtures';
import { expect } from '@playwright/test';
test.describe('Profile API', () => {
test.skip('[P0] AC-1 should fetch user profile', async ({ request, authToken }) => {
const response = await request.get('/api/profile', {
headers: { Authorization: `Bearer ${authToken}` },
});
expect(response.status()).toBe(200);
const profile = await response.json();
expect(profile).toHaveProperty('name');
expect(profile).toHaveProperty('email');
expect(profile).toHaveProperty('avatarUrl');
});
test.skip('[P0] AC-2 should update user profile', async ({ request, authToken }) => {
const response = await request.patch('/api/profile', {
headers: { Authorization: `Bearer ${authToken}` },
data: {
name: 'Updated Name',
email: 'updated@example.com',
},
});
expect(response.status()).toBe(200);
const updated = await response.json();
expect(updated.name).toBe('Updated Name');
expect(updated.email).toBe('updated@example.com');
});
test.skip('[P1] AC-4 should validate email format', async ({ request, authToken }) => {
const response = await request.patch('/api/profile', {
headers: { Authorization: `Bearer ${authToken}` },
data: {
email: 'invalid-email',
},
});
expect(response.status()).toBe(400);
const error = await response.json();
expect(error.message).toContain('Invalid email format');
});
});

With Playwright Utils:

import { test, expect } from '../support/merged-fixtures';
import { z } from 'zod';
const ProfileSchema = z.object({
name: z.string(),
email: z.string().email(),
avatarUrl: z.string().url(),
});
test.describe('Profile API', () => {
test.skip('[P0] AC-1 should fetch user profile', async ({ apiRequest, authToken }) => {
const { status, body } = await apiRequest({
method: 'GET',
path: '/api/profile',
headers: { Authorization: `Bearer ${authToken}` },
});
expect(status).toBe(200);
const profile = ProfileSchema.parse(body);
expect(profile.name).toBeDefined();
expect(profile.email).toContain('@');
});
test.skip('[P0] AC-2 should update user profile', async ({ apiRequest, authToken }) => {
const { status, body } = await apiRequest({
method: 'PATCH',
path: '/api/profile',
headers: { Authorization: `Bearer ${authToken}` },
body: {
name: 'Updated Name',
email: 'updated@example.com',
},
});
expect(status).toBe(200);
const updated = ProfileSchema.parse(body);
expect(updated.name).toBe('Updated Name');
expect(updated.email).toBe('updated@example.com');
});
test.skip('[P1] AC-4 should validate email format', async ({ apiRequest, authToken }) => {
const { status, body } = await apiRequest({
method: 'PATCH',
path: '/api/profile',
headers: { Authorization: `Bearer ${authToken}` },
body: { email: 'invalid-email' },
});
expect(status).toBe(400);
expect(body.message).toContain('Invalid email format');
});
});

apiRequest returns { status, body } and supports chained Zod validation. Disable retries with retryConfig: { maxRetries: 0 } for tests that assert a 5xx response.

import { test, expect } from '@playwright/test';
test.skip('[P0] AC-3 should edit and save profile', async ({ page }) => {
// This assertion is the first potentially failing operation. Every browser
// failure in the journey retains AC-3 provenance.
await expect(
(async () => {
await page.goto('/login');
await page.getByLabel('Email').fill('test@example.com');
await page.getByLabel('Password').fill('password123');
await page.getByRole('button', { name: 'Sign in' }).click();
await page.goto('/profile');
await page.getByRole('button', { name: 'Edit Profile' }).click();
await page.getByLabel('Name').fill('Updated Name');
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.getByText('Profile updated')).toBeVisible();
await page.reload();
await page.getByRole('button', { name: 'Edit Profile' }).click();
await expect(page.getByLabel('Name')).toHaveValue('Updated Name');
})(),
).resolves.toBeUndefined();
});

TEA records additional display, validation-error, and other secondary E2E journeys in the implementation checklist for green-phase automation.

TEA also provides an implementation checklist, saved as {test_artifacts}/atdd/atdd-checklist-{story_key}.md (for example atdd-checklist-1-2-user-authentication.md):

## Implementation Checklist
### Backend
- [ ] Create `GET /api/profile` endpoint
- [ ] Create `PATCH /api/profile` endpoint
- [ ] Add email validation middleware
- [ ] Add profile picture upload handling
- [ ] Write API unit tests
### Frontend
- [ ] Create ProfilePage component
- [ ] Implement profile form with validation
- [ ] Add file upload for avatar
- [ ] Handle API errors gracefully
- [ ] Add loading states
### Tests
- [x] API test scaffolds generated (`test.skip()`)
- [x] E2E test scaffolds generated (`test.skip()`)
- [ ] Activate and run tests during implementation (should fail before code changes, then pass)

TEA verifies the TDD red phase using an isolated, un-skipped copy. The saved scaffolds keep test.skip() until you’re ready to work on a task. Review the generated files, then remove test.skip() for the current task and confirm that the newly activated test fails before you implement the feature.

Terminal window
npm run test:e2e # Playwright via TEA's framework scaffold, which carries the config path
npx cypress run # Cypress

Example initial output with scaffolds still skipped:

Running 4 tests using 1 worker
- tests/api/profile.spec.ts:3:3 › [P0] AC-1 should fetch user profile
- tests/api/profile.spec.ts:15:3 › [P0] AC-2 should update user profile
- tests/api/profile.spec.ts:30:3 › [P1] AC-4 should validate email format
- tests/e2e/profile.spec.ts:18:3 › [P0] AC-3 should edit and save profile
4 skipped

After you remove test.skip(), confirm the test fails at the assertion for the promised behavior. A setup failure needs repair before implementation starts.

Now implement the feature following the test guidance:

  1. Start with API tests (backend first)
  2. Remove test.skip() from the first API test and confirm RED
  3. Implement until that test passes
  4. Move to the next API or E2E test and repeat
  5. Refactor with confidence (tests protect you)

After implementation, run your test suite with the same command. Example output:

Running 4 tests using 1 worker
✓ tests/api/profile.spec.ts:3:3 › [P0] AC-1 should fetch user profile (850ms)
✓ tests/api/profile.spec.ts:15:3 › [P0] AC-2 should update user profile (1.2s)
✓ tests/api/profile.spec.ts:30:3 › [P1] AC-4 should validate email format (650ms)
✓ tests/e2e/profile.spec.ts:18:3 › [P0] AC-3 should edit and save profile (3.2s)
4 passed (5.9s)

That completes the red → green → refactor cycle for the generated scaffolds.

  • API tests for backend endpoints
  • E2E tests for user workflows
  • Component tests (if requested)
  • Generated with test.skip() until you activate them task-by-task

  • Clear checklist of what to build
  • Acceptance criteria translated to assertions
  • Edge cases and error scenarios identified

  • Activated tests guide implementation
  • Confidence to refactor
  • Living documentation of features

Run test-design before Automate red mode for better results:

/bmad-tea-test-design # risk assessment and priorities
/bmad-tea-automate # request red mode to generate tests based on that design

If browser automation is configured (tea_browser_automation: "auto" or "cli" or "mcp"), TEA can verify selectors against live browsers during Automate red mode.

  • CLI mode: Takes snapshots to verify element names and roles before generating selectors
  • MCP mode: Full browser automation for complex UI interactions
  • Auto mode: Uses CLI for simple verification, MCP for complex flows

Red mode is for features that don’t exist yet, so browser verification only applies if you have skeleton/mockup UI already implemented. For red mode with no UI yet, TEA infers selectors from best practices.

See Configure Browser Automation for setup.

Don’t generate tests for everything at once:

Generate tests for:
- P0: Critical path (happy path)
- P1: High value (validation, errors)
Skip P2/P3 for now; add them later with Automate expand mode.

Recommended order:

  1. Generate API tests with Automate red mode
  2. Implement backend (make API tests pass)
  3. Generate E2E tests with Automate red mode for missing behavior or expand mode for implemented behavior
  4. Implement frontend (make E2E tests pass)

TEA generates deterministic tests by default:

  • No hard waits (waitForTimeout)
  • Network-first patterns (wait for responses)
  • Explicit assertions (no conditionals)

Preserve response waits and explicit assertions when editing the generated tests.