Project 32: E2E Workflow Recorder - Test Case Generator
Build a system where Claude observes you interacting with a website, records your actions, understands the intent behind them, and generates reusable test code in Playwright, Cypress, or Selenium format.
Quick Reference
| Attribute | Value |
|---|---|
| Difficulty | Expert |
| Time Estimate | 1 month+ |
| Language | TypeScript (Alternatives: JavaScript, Python) |
| Prerequisites | All previous browser projects, testing frameworks knowledge |
| Key Topics | Test recording, code generation, selector strategies, intent inference |
| Main Book | “xUnit Test Patterns” by Gerard Meszaros |
1. Learning Objectives
By completing this project, you will:
- Record browser interactions: Capture clicks, inputs, navigation, and assertions
- Infer user intent: Understand why actions were taken, not just what was done
- Generate robust selectors: Create stable element selectors that survive refactors
- Synthesize test code: Produce idiomatic code for Playwright, Cypress, or Selenium
- Add intelligent assertions: Determine what to verify based on workflow context
- Handle async patterns: Generate proper waits and timeout handling
2. Theoretical Foundation
2.1 From Actions to Tests
The fundamental challenge: raw actions don’t make good tests.
┌─────────────────────────────────────────────────────────────────────────────┐
│ Action Recording vs Test Generation │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Raw Recording: Intelligent Test: │
│ ────────────── ────────────────── │
│ │
│ click(234, 567) page.getByRole('button', │
│ { name: 'Sign In' }).click() │
│ │
│ type("john@example.com") page.getByLabel('Email') │
│ .fill('john@example.com') │
│ │
│ wait(3000) await expect(page) │
│ .toHaveURL('/dashboard') │
│ │
│ click(456, 789) await expect( │
│ page.getByTestId('welcome') │
│ ).toBeVisible() │
│ │
│ Problems with raw: Benefits of intelligent: │
│ - Coordinates are fragile - Semantic selectors │
│ - No verification of success - Built-in assertions │
│ - Arbitrary waits - Smart waiting │
│ - Not readable/maintainable - Clear intent │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
2.2 Selector Strategy Hierarchy
Different selector strategies have different stability characteristics:
┌─────────────────────────────────────────────────────────────────────────────┐
│ Selector Stability Pyramid │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ MOST STABLE │
│ ▲ │
│ /│\ │
│ / │ \ │
│ / │ \ │
│ / │ \ │
│ / │ \ 1. Test IDs │
│ / │ \ [data-testid="login-btn"] │
│ / │ \ Explicit, stable, purpose-built │
│ / │ \ │
│ / │ \ │
│ / │ \ 2. Semantic/Role │
│ / │ \ getByRole('button', │
│ / │ \ { name: 'Sign In' }) │
│ / │ \ Accessibility-based │
│ / │ \ │
│ / │ \ 3. Text Content │
│ / │ \ getByText('Submit') │
│ / │ \ User-visible, readable │
│ / │ \ │
│ / │ \ 4. Stable Classes │
│ / │ \ .login-button │
│ / │ \ Meaningful names │
│ / │ \ │
│ / │ \ 5. Structural │
│ / │ \ div > button │
│ / │ \ DOM hierarchy │
│ /─────────────────────────│─────────────────────────\ │
│ ▼ │
│ LEAST STABLE │
│ │
│ NEVER USE: │
│ - Coordinates (click at 234, 567) │
│ - Generated IDs (id="react-node-a7f3c2") │
│ - Deep structural paths (div > div > div > span > button) │
│ - Index-based (button:nth-child(3)) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
2.3 Intent Inference Patterns
Claude’s unique capability: understanding why, not just what.
| Observed Actions | Inferred Intent | Generated Test |
|---|---|---|
| Navigate to /login, fill email, fill password, click button | “Login flow” | Test: “should login successfully” |
| Fill search box, click search, click result | “Search and select” | Test: “should find item via search” |
| Click item, click add to cart, navigate to cart | “Add to cart flow” | Test: “should add item to cart” |
| Fill form, submit, see success message | “Form submission” | Test: “should submit form successfully” |
2.4 Action Grouping Logic
┌─────────────────────────────────────────────────────────────────────────────┐
│ Action Grouping Algorithm │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Raw Events Timeline: │
│ ───────────────────── │
│ t=0 navigate(/login) │
│ t=1 click(email_input) │
│ t=2 type(email_input, "user@example.com") │
│ t=3 click(password_input) │
│ t=4 type(password_input, "secret123") │
│ t=5 click(submit_button) │
│ t=6 wait(page_load) │
│ t=7 url_changed(/dashboard) │
│ │
│ Grouping Rules: │
│ ─────────────── │
│ 1. Navigation events → Start new test step │
│ 2. Related inputs → Group together (form filling) │
│ 3. Submit/click → End of action group │
│ 4. URL change → Verification point │
│ 5. Visual check → Assertion opportunity │
│ │
│ Grouped Actions: │
│ ──────────────── │
│ Step 1: Navigate to login page │
│ └── navigate(/login) │
│ │
│ Step 2: Fill login credentials │
│ ├── fill(email, "user@example.com") │
│ └── fill(password, "secret123") │
│ │
│ Step 3: Submit and verify │
│ ├── click(submit) │
│ └── assert(url == /dashboard) │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
2.5 Test Framework Output Patterns
Generated code should be idiomatic for each framework:
Playwright:
import { test, expect } from '@playwright/test';
test.describe('Authentication', () => {
test('should login successfully', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill('user@example.com');
await page.getByLabel('Password').fill('secret123');
await page.getByRole('button', { name: 'Sign In' }).click();
await expect(page).toHaveURL('/dashboard');
await expect(page.getByTestId('welcome-message')).toBeVisible();
});
});
Cypress:
describe('Authentication', () => {
it('should login successfully', () => {
cy.visit('/login');
cy.findByLabelText('Email').type('user@example.com');
cy.findByLabelText('Password').type('secret123');
cy.findByRole('button', { name: 'Sign In' }).click();
cy.url().should('include', '/dashboard');
cy.findByTestId('welcome-message').should('be.visible');
});
});
Selenium (Python):
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
class TestAuthentication:
def test_login_successfully(self, driver):
driver.get('/login')
driver.find_element(By.XPATH, "//input[@aria-label='Email']").send_keys('user@example.com')
driver.find_element(By.XPATH, "//input[@aria-label='Password']").send_keys('secret123')
driver.find_element(By.XPATH, "//button[text()='Sign In']").click()
WebDriverWait(driver, 10).until(EC.url_contains('/dashboard'))
assert driver.find_element(By.CSS_SELECTOR, "[data-testid='welcome-message']").is_displayed()
3. Project Specification
3.1 What You Will Build
An intelligent test recorder that:
- Records user interactions through Chrome MCP
- Infers intent and groups related actions
- Generates stable selectors for each element
- Produces test code in multiple frameworks
- Adds appropriate assertions and waits
- Supports editing and refinement of generated tests
3.2 Functional Requirements
- Recording Mode
- Start/stop recording on demand
- Capture navigation, clicks, inputs, selections
- Record the DOM state at each interaction
- Support GIF recording for visual documentation
- Intent Analysis
- Group related actions into test steps
- Identify workflow patterns (login, search, checkout)
- Determine appropriate test names
- Suggest test boundaries
- Selector Generation
- Prefer test IDs when available
- Fall back through selector hierarchy
- Validate selector uniqueness
- Generate multiple selector options
- Assertion Generation
- Add URL verification after navigation
- Include element visibility checks
- Verify expected text content
- Support custom assertion insertion
- Code Generation
- Output Playwright (default)
- Support Cypress and Selenium
- Generate TypeScript or JavaScript
- Include proper imports and setup
- Workflow Refinement
- Edit generated tests
- Add parameterization
- Generate negative test cases
- Create page objects (stretch)
3.3 Example Workflow
You: Watch me test the login flow and generate Playwright tests
Claude: Recording started. I'll watch your actions and infer test intent.
[Using gif_creator to record the session...]
You perform actions:
1. Navigate to /login
2. Type "user@test.com" in email field
3. Type "password123" in password field
4. Click "Sign In" button
5. Wait for redirect to /dashboard
6. Click on username in header to verify logged in
Claude: Recording stopped. Analyzing your workflow...
================================================================================
WORKFLOW ANALYSIS & TEST GENERATION
================================================================================
RECORDED SESSION
----------------
GIF saved: ./recordings/login-flow-2024-12-22.gif
Duration: 12.4 seconds
Actions captured: 6
Page transitions: 1
ACTION TIMELINE
---------------
00:00 navigate("/login")
Intent: Start of login flow
02:30 fill(input[type="email"], "user@test.com")
Selector options:
[BEST] getByLabel('Email')
[ALT] getByPlaceholder('Enter your email')
[ALT] locator('input[type="email"]')
04:15 fill(input[type="password"], "password123")
Selector options:
[BEST] getByLabel('Password')
[ALT] locator('input[type="password"]')
06:00 click(button[type="submit"])
Selector options:
[BEST] getByRole('button', { name: 'Sign In' })
[ALT] getByTestId('login-submit')
[ALT] locator('button[type="submit"]')
08:30 page transition: /login → /dashboard
Assertion: expect(page).toHaveURL('/dashboard')
10:00 click(header .user-menu)
Intent: Verification of logged-in state
Observation: User dropdown visible
INFERRED INTENT
---------------
Test Name: "should login with valid credentials"
Category: Authentication
Coverage: Happy path - successful login
Assertions inferred:
1. After login: URL should be /dashboard
2. After login: User menu should be visible
3. After login: Welcome message should contain username
GENERATED TEST (Playwright)
---------------------------
```typescript
import { test, expect } from '@playwright/test';
test.describe('Authentication', () => {
test('should login with valid credentials', async ({ page }) => {
// Navigate to login page
await page.goto('/login');
// Fill login credentials
await page.getByLabel('Email').fill('user@test.com');
await page.getByLabel('Password').fill('password123');
// Submit login form
await page.getByRole('button', { name: 'Sign In' }).click();
// Verify successful login
await expect(page).toHaveURL('/dashboard');
await expect(page.getByTestId('user-menu')).toBeVisible();
});
});
RECOMMENDATIONS
-
Security: Replace hardcoded password with environment variable Example: process.env.TEST_PASSWORD
-
Data: Consider using test fixtures for credentials Example: test.use({ storageState: ‘auth.json’ })
-
Edge cases: Consider adding tests for:
- Invalid credentials
- Empty fields
- Account lockout
Would you like me to:
- Generate the additional edge case tests?
- Convert to Cypress or Selenium format?
- Create a Page Object Model structure?
- Add error handling scenarios? ```
4. Solution Architecture
4.1 High-Level Design
┌─────────────────────────────────────────────────────────────────────────────┐
│ E2E Workflow Recorder System │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Recording │──▶│ Action │──▶│ Intent │──▶│ Code │ │
│ │ Engine │ │ Analyzer │ │ Inferrer │ │ Generator │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ gif_creator DOM states Claude Playwright │
│ read_page Grouping reasoning Cypress │
│ computer Deduplication Naming Selenium │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ Selector Generator ││
│ │ ││
│ │ Element ──▶ [Extractor] ──▶ [Ranker] ──▶ [Validator] ──▶ Best Selector││
│ │ ││
│ │ Extraction: Ranking: Validation: ││
│ │ - data-testid - Stability score - Uniqueness check ││
│ │ - aria-label - Readability - Dynamic ID filter ││
│ │ - role + name - Framework support - Fragility analysis ││
│ │ - text content - Maintenance cost ││
│ │ - class names ││
│ │ - structure path ││
│ └─────────────────────────────────────────────────────────────────────────┘│
│ │
└─────────────────────────────────────────────────────────────────────────────┘
4.2 Recording Flow
┌─────────────────────────────────────────────────────────────────────────────┐
│ Recording Flow │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ User: "Start recording" │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 1. Initialize Recording │ │
│ │ gif_creator(action: "start") │ │
│ │ Take initial screenshot │ │
│ └─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 2. User Performs Actions │ │
│ │ (Claude observes via GIF frames) │ │
│ │ │ │
│ │ After each significant action: │ │
│ │ - read_page() to capture DOM │ │
│ │ - Record URL changes │ │
│ │ - Note element interactions │ │
│ └─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ User: "Stop recording" │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 3. Finalize Recording │ │
│ │ gif_creator(action: "stop") │ │
│ │ gif_creator(action: "export") │ │
│ │ Take final screenshot │ │
│ └─────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 4. Analyze Recording │ │
│ │ - Review GIF for actions │ │
│ │ - Identify action sequence │ │
│ │ - Infer user intent │ │
│ │ - Generate test code │ │
│ └─────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
4.3 Selector Generation Algorithm
┌─────────────────────────────────────────────────────────────────────────────┐
│ Selector Generation Algorithm │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Input: Element reference from read_page (e.g., ref_42) │
│ │
│ Step 1: Extract All Possible Selectors │
│ ──────────────────────────────────────── │
│ │
│ candidates = [] │
│ │
│ // Test ID (highest priority) │
│ if element has data-testid: │
│ candidates.push({ │
│ type: 'testid', │
│ selector: `[data-testid="${value}"]`, │
│ playwright: `getByTestId('${value}')`, │
│ stability: 10 │
│ }) │
│ │
│ // Role-based (semantic) │
│ if element has role (button, link, textbox, etc): │
│ if element has accessible name: │
│ candidates.push({ │
│ type: 'role', │
│ selector: `role=${role}[name="${name}"]`, │
│ playwright: `getByRole('${role}', { name: '${name}' })`, │
│ stability: 9 │
│ }) │
│ │
│ // Label-based (forms) │
│ if element is input and has label: │
│ candidates.push({ │
│ type: 'label', │
│ selector: `label=${labelText}`, │
│ playwright: `getByLabel('${labelText}')`, │
│ stability: 8 │
│ }) │
│ │
│ // Text-based │
│ if element has visible text: │
│ candidates.push({ │
│ type: 'text', │
│ selector: `text="${text}"`, │
│ playwright: `getByText('${text}')`, │
│ stability: 6 │
│ }) │
│ │
│ // Class-based (if meaningful) │
│ if element has non-generated class names: │
│ candidates.push({ │
│ type: 'class', │
│ selector: `.${className}`, │
│ playwright: `locator('.${className}')`, │
│ stability: 4 │
│ }) │
│ │
│ Step 2: Validate Uniqueness │
│ ─────────────────────────── │
│ │
│ for each candidate: │
│ matches = querySelectorAll(candidate.selector) │
│ if matches.length === 1: │
│ candidate.unique = true │
│ else: │
│ candidate.unique = false │
│ candidate.matches = matches.length │
│ │
│ Step 3: Filter and Rank │
│ ──────────────────────── │
│ │
│ valid = candidates.filter(c => c.unique) │
│ ranked = valid.sort((a, b) => b.stability - a.stability) │
│ │
│ Output: ranked[0] (best) plus alternatives │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
4.4 Intent Inference Logic
┌─────────────────────────────────────────────────────────────────────────────┐
│ Intent Inference Patterns │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Pattern Matching Rules: │
│ │
│ LOGIN FLOW │
│ ────────── │
│ Indicators: │
│ - URL contains: /login, /signin, /auth │
│ - Fields: email/username + password │
│ - Button: "Sign In", "Log In", "Submit" │
│ - Redirect to: /dashboard, /home, /account │
│ │
│ Generated test name: "should login successfully" │
│ Category: Authentication │
│ │
│ SIGNUP FLOW │
│ ─────────── │
│ Indicators: │
│ - URL contains: /signup, /register, /create-account │
│ - Fields: email + password + confirm password │
│ - Additional fields: name, terms checkbox │
│ - Success: confirmation page or verify email message │
│ │
│ Generated test name: "should create new account" │
│ Category: Registration │
│ │
│ SEARCH FLOW │
│ ─────────── │
│ Indicators: │
│ - Input with: search icon, placeholder "Search" │
│ - Action: type + enter or click search button │
│ - Result: list of items, "X results found" │
│ │
│ Generated test name: "should search and find results" │
│ Category: Search │
│ │
│ CHECKOUT FLOW │
│ ───────────── │
│ Indicators: │
│ - Sequence: add to cart → cart page → checkout │
│ - Fields: shipping address, payment info │
│ - Buttons: "Add to Cart", "Checkout", "Place Order" │
│ - Success: order confirmation, order number │
│ │
│ Generated test name: "should complete purchase" │
│ Category: E-commerce │
│ │
│ FORM SUBMISSION │
│ ─────────────── │
│ Indicators: │
│ - Multiple input fields │
│ - Submit button │
│ - Success message or redirect │
│ │
│ Generated test name: "should submit form successfully" │
│ Category: Forms │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
5. Implementation Guide
5.1 Chrome MCP Tools for Recording
gif_creator - Record Sessions
// Start recording session
mcp__claude-in-chrome__gif_creator({
action: "start_recording",
tabId: 12345
})
// Take initial screenshot immediately after
mcp__claude-in-chrome__computer({
action: "screenshot",
tabId: 12345
})
// ... user performs actions ...
// Stop recording
mcp__claude-in-chrome__gif_creator({
action: "stop_recording",
tabId: 12345
})
// Export GIF with annotations
mcp__claude-in-chrome__gif_creator({
action: "export",
tabId: 12345,
download: true,
options: {
showClickIndicators: true,
showActionLabels: true,
showProgressBar: true,
showWatermark: true
}
})
read_page - Capture DOM State
// After each user action, capture element state
mcp__claude-in-chrome__read_page({
tabId: 12345,
filter: "interactive" // Focus on actionable elements
})
// Response includes elements like:
// button "Sign In" [ref_1]
// textbox "Email" [ref_2]
// checkbox "Remember me" [ref_3]
find - Locate Specific Elements
// Find elements user interacted with
mcp__claude-in-chrome__find({
tabId: 12345,
query: "submit button"
})
// Returns element references for selector extraction
5.2 Implementation Phases
Phase 1: Basic Recording (Week 1)
Goal: Capture user actions through GIF recording.
Tasks:
- Implement start/stop recording commands
- Capture screenshots at key moments
- Track URL changes
- Export GIF with action indicators
Checkpoint: GIF shows user interactions with click indicators.
Phase 2: Action Identification (Week 1-2)
Goal: Identify individual actions from recording.
Tasks:
- Analyze GIF frames for action detection
- Correlate with read_page DOM snapshots
- Build action timeline with timestamps
- Deduplicate redundant actions (hover, scroll)
Checkpoint: Generate list of discrete user actions.
Phase 3: Selector Generation (Week 2)
Goal: Generate stable selectors for each element.
Tasks:
- Implement selector extraction
- Rank selectors by stability
- Validate uniqueness
- Generate framework-specific syntax
Checkpoint: Each action has robust selector attached.
Phase 4: Intent Inference (Week 3)
Goal: Understand workflow purpose using Claude.
Tasks:
- Implement pattern matching rules
- Use Claude to analyze action sequences
- Generate meaningful test names
- Identify assertion opportunities
Checkpoint: Actions grouped into logical test steps.
Phase 5: Code Generation (Week 3-4)
Goal: Output idiomatic test code.
Tasks:
- Build code templates for Playwright
- Add Cypress and Selenium support
- Include proper imports and setup
- Generate assertions and waits
Checkpoint: Generated code runs successfully.
Phase 6: Refinement (Week 4+)
Goal: Polish and extend capabilities.
Tasks:
- Support parameterization
- Generate edge case tests
- Add page object model option
- Implement test editing
Checkpoint: Production-quality test generation.
6. Testing Strategy
6.1 Test Scenarios
| Scenario | Recording | Expected Output |
|---|---|---|
| Simple login | Fill form, submit | Login test with assertions |
| Multi-step wizard | Navigate 3 pages | Test with step comments |
| Search + select | Search, click result | Two-step test |
| Form with validation | Trigger error, fix | Error handling test |
| Dynamic content | Wait for async | Proper wait commands |
6.2 Generated Code Validation
- Syntax check: Run linter on generated code
- Execution test: Run generated test on same page
- Stability test: Run 10 times without flakes
- Readability: Human review of code quality
7. Common Pitfalls & Debugging
7.1 Frequent Issues
| Issue | Symptom | Solution |
|---|---|---|
| Generated ID | Selector breaks on reload | Use role/text selectors instead |
| Dynamic content | Test fails on timing | Add proper waits |
| Iframe content | Elements not found | Handle iframe context |
| Hover menus | Click target hidden | Add hover action before click |
| Animation timing | Screenshot mid-animation | Wait for stability |
7.2 Selector Debugging
// Verify selector uniqueness
mcp__claude-in-chrome__javascript_tool({
action: "javascript_exec",
text: `
const selector = '[data-testid="login-btn"]';
const matches = document.querySelectorAll(selector);
JSON.stringify({
selector: selector,
matchCount: matches.length,
matches: Array.from(matches).map(el => ({
tag: el.tagName,
text: el.textContent?.substring(0, 50)
}))
});
`,
tabId: 12345
})
8. Real World Outcome
When you complete this project, here’s a complete recording and generation session:
You: Record me testing the checkout flow and generate Playwright tests
Claude: I'll start recording your checkout flow test.
[Starting GIF recording...]
[Initial screenshot captured]
Recording in progress... Perform your test actions.
---
[User performs actions over 45 seconds]
---
You: Stop recording
Claude: Recording stopped. Analyzing the workflow...
================================================================================
E2E WORKFLOW RECORDING ANALYSIS
================================================================================
RECORDING SUMMARY
-----------------
Duration: 45 seconds
Actions captured: 12
Page transitions: 3
GIF saved: ./recordings/checkout-2024-12-22-143500.gif
IDENTIFIED WORKFLOW: E-Commerce Checkout
----------------------------------------
Step 1: Browse to Product (0:00 - 0:08)
Actions:
- Navigate to /products
- Click product card "Wireless Headphones"
Element: Product card
[BEST] getByRole('article', { name: 'Wireless Headphones' })
[ALT] getByTestId('product-wh-001')
[ALT] locator('.product-card:has-text("Wireless")')
Step 2: Add to Cart (0:08 - 0:15)
Actions:
- Click "Add to Cart" button
- Wait for cart update notification
Element: Add to Cart button
[BEST] getByRole('button', { name: 'Add to Cart' })
[ALT] getByTestId('add-to-cart')
Assertion opportunity:
- Cart count should increase
- Success notification should appear
Step 3: Navigate to Cart (0:15 - 0:22)
Actions:
- Click cart icon in header
- Verify product in cart
Element: Cart icon
[BEST] getByLabel('Shopping Cart')
[ALT] getByTestId('cart-icon')
Assertion opportunity:
- URL should be /cart
- Product should be visible in cart
Step 4: Proceed to Checkout (0:22 - 0:35)
Actions:
- Click "Checkout" button
- Fill shipping address
- Select shipping method
Elements identified:
- Checkout button: getByRole('button', { name: 'Checkout' })
- Street field: getByLabel('Street Address')
- City field: getByLabel('City')
- Shipping method: getByRole('radio', { name: 'Standard' })
Step 5: Complete Order (0:35 - 0:45)
Actions:
- Click "Place Order" button
- Verify order confirmation
Assertion opportunity:
- URL should contain /order-confirmation
- Order number should be visible
INTENT ANALYSIS
---------------
Primary flow: Complete purchase of single item
Test category: E-commerce / Checkout
Coverage: Happy path with default shipping
Inferred assertions:
1. Product added to cart successfully
2. Cart shows correct item
3. Checkout form accepts valid data
4. Order confirmation displayed
GENERATED TEST (Playwright)
---------------------------
```typescript
import { test, expect } from '@playwright/test';
test.describe('E-commerce Checkout', () => {
test('should complete purchase of single item', async ({ page }) => {
// Step 1: Browse to product
await page.goto('/products');
await page.getByRole('article', { name: 'Wireless Headphones' }).click();
// Step 2: Add to cart
await page.getByRole('button', { name: 'Add to Cart' }).click();
// Verify item added
await expect(page.getByText('Added to cart')).toBeVisible();
// Step 3: Navigate to cart
await page.getByLabel('Shopping Cart').click();
await expect(page).toHaveURL('/cart');
// Verify product in cart
await expect(page.getByText('Wireless Headphones')).toBeVisible();
// Step 4: Proceed to checkout
await page.getByRole('button', { name: 'Checkout' }).click();
// Fill shipping address
await page.getByLabel('Street Address').fill('123 Test Street');
await page.getByLabel('City').fill('Test City');
await page.getByLabel('State').selectOption('California');
await page.getByLabel('ZIP Code').fill('90210');
// Select shipping method
await page.getByRole('radio', { name: 'Standard Shipping' }).check();
// Step 5: Complete order
await page.getByRole('button', { name: 'Place Order' }).click();
// Verify order confirmation
await expect(page).toHaveURL(/order-confirmation/);
await expect(page.getByTestId('order-number')).toBeVisible();
});
});
ADDITIONAL RECOMMENDATIONS
- Data Parameterization:
Consider extracting test data to fixtures:
const testProduct = 'Wireless Headphones'; const shippingAddress = { street: '123 Test Street', city: 'Test City', state: 'California', zip: '90210' }; - Edge Case Tests:
- Empty cart checkout (should be blocked)
- Invalid address (validation error)
- Out of stock item
- Multiple items in cart
- Page Object Model:
For maintainability, consider:
class CheckoutPage { async fillShippingAddress(address) { ... } async selectShippingMethod(method) { ... } async placeOrder() { ... } } - Visual Regression: Add screenshot comparisons at key steps
================================================================================
Would you like me to:
- Generate the edge case tests?
- Convert to Cypress format?
- Create Page Object Model structure?
- Add visual regression checkpoints? ```
9. The Core Question You’re Answering
“How do you go from recorded clicks to maintainable test code?”
Raw action recording produces brittle tests. The transformation requires:
- Semantic Understanding: “Click at (234, 567)” becomes “Click Sign In button”
- Selector Stability: Generated IDs become test IDs or role selectors
- Intent Grouping: 15 low-level events become 5 meaningful steps
- Assertion Inference: “I see the dashboard” becomes
expect(page).toHaveURL('/dashboard') - Wait Handling: “Pause 3 seconds” becomes proper await/expect patterns
Claude’s reasoning capability enables all of these transformations.
10. Concepts You Must Understand First
Before starting this project, ensure you understand:
| Concept | Why It Matters | Where to Learn |
|---|---|---|
| Playwright/Cypress basics | Target output format | Official docs |
| Selector strategies | Core to stable tests | Testing Library docs |
| Async/await patterns | Test timing | MDN JavaScript |
| DOM events | Action recording | MDN - Event reference |
| Page Object Model | Advanced output | Selenium best practices |
| GIF recording | Visual documentation | Project 29 skills |
11. Questions to Guide Your Design
Work through these questions BEFORE implementing:
-
Action Detection: How do you know when a user action occurred? Click? Input? Navigation?
-
Noise Filtering: How do you distinguish intentional actions from incidental mouse movements?
-
Selector Priority: In what order should you try different selector strategies?
-
Assertion Timing: When in the workflow should assertions be added?
-
Multi-Framework: How do you structure code to output different test frameworks?
-
Parameterization: How do you identify values that should be extracted as variables?
12. Thinking Exercise
Before implementing, analyze this action sequence:
1. User clicks dropdown menu
2. Menu opens with animation (300ms)
3. User moves mouse through several options
4. User clicks "Settings"
5. Settings modal opens (fade in 200ms)
6. User toggles a switch to ON
7. User clicks outside modal
8. Modal closes
Questions to answer on paper:
- Which events are “actions” vs “navigation”?
- How do you know the animation completed?
- What’s the meaningful selector for “Settings” option?
- What assertion should verify the toggle worked?
- How would you handle the modal dismissal?
- What’s the generated test code for this sequence?
13. The Interview Questions They’ll Ask
After completing this project, you’ll be ready for:
- “How do you generate stable selectors that survive refactors?”
- Expected: Selector hierarchy, test IDs, role-based selectors
- Bonus: Discuss uniqueness validation, fallback strategies
- “What’s your strategy for handling dynamic IDs or content?”
- Expected: Avoid generated IDs, use semantic selectors
- Bonus: Discuss data-testid patterns, selector composition
- “How do you differentiate between navigation and actions?”
- Expected: URL changes, DOM mutations, explicit user gestures
- Bonus: Discuss action grouping algorithms
- “How would you handle tests that require authenticated state?”
- Expected: Storage state, session fixtures, API authentication
- Bonus: Discuss Playwright’s storageState feature
- “What’s the page object pattern and when would you use it?”
- Expected: Encapsulation, reusability, maintenance
- Bonus: Discuss tradeoffs, when simple tests are better
14. Hints in Layers
If you get stuck, reveal hints progressively:
Hint 1: Recording Strategy
Use GIF recording as the primary capture mechanism:
// Start recording and take initial screenshot
gif_creator(action: "start_recording")
computer(action: "screenshot")
// User performs actions (tracked by GIF frames)
// Stop and export with action labels
gif_creator(action: "stop_recording")
gif_creator(action: "export", options: { showClickIndicators: true })
The GIF provides a visual timeline. Correlate with DOM snapshots from read_page.
Hint 2: Element Reference Tracking
When user interacts with an element, capture its reference:
// After observing a click action
mcp__claude-in-chrome__read_page({ tabId, filter: "interactive" })
// Find the clicked element in the accessibility tree
// Extract selectors from its properties:
// - data-testid attribute
// - role and accessible name
// - label text
// - visible text content
Store these for selector generation.
Hint 3: Selector Validation
Before using a selector, verify it’s unique:
mcp__claude-in-chrome__javascript_tool({
action: "javascript_exec",
text: `
const selector = "button:has-text('Submit')";
const matches = document.querySelectorAll(selector);
({ count: matches.length, unique: matches.length === 1 })
`,
tabId
})
If not unique, try more specific selectors.
Hint 4: Claude's Intent Inference
Show Claude the GIF and ask for analysis: “I recorded this user workflow. Watch the GIF and tell me:
- What is the user trying to accomplish?
- What would be a good test name?
- What assertions should verify success?”
Claude’s visual understanding provides the intent layer.
15. Books That Will Help
| Topic | Book | Chapter/Section |
|---|---|---|
| Test patterns | “xUnit Test Patterns” by Gerard Meszaros | Ch. 11-12 |
| Selector strategies | “Testing JavaScript Applications” by Lucas da Costa | Ch. 6 |
| Code generation | “Domain Specific Languages” by Martin Fowler | Ch. 8 |
| Playwright fundamentals | Playwright Official Docs | Getting Started |
| Page Object Model | “Selenium WebDriver 3 Practical Guide” | Ch. 5 |
| Test architecture | “Growing Object-Oriented Software” by Freeman & Pryce | Ch. 5-6 |
16. Extensions & Challenges
16.1 Beginner Extensions
- Multiple output formats: Add WebDriverIO, TestCafe
- Test organization: Generate describe/context blocks
- Comment generation: Add explanatory comments
16.2 Intermediate Extensions
- Page Object Model: Generate POM structure automatically
- Data-driven tests: Extract data tables from recordings
- Negative tests: Generate error case tests
16.3 Advanced Extensions
- Self-healing selectors: Fallback if primary fails
- Visual assertions: Add screenshot comparisons
- API correlation: Match UI actions to API calls
17. Learning Milestones
Track your progress through these checkpoints:
| Milestone | Description | Verification |
|---|---|---|
| 1. Basic recording | GIF captures user actions | Click indicators visible |
| 2. Action identification | List discrete actions | Timeline matches user flow |
| 3. Selector generation | Stable selectors created | Selectors work on reload |
| 4. Intent inference | Meaningful test names | Names describe workflow |
| 5. Code generation | Runnable test code | Generated test passes |
| 6. Multi-framework | Playwright + Cypress | Both outputs work |
This guide was expanded from CLAUDE_CODE_MASTERY_40_PROJECTS.md. For the complete learning path, see the project index.