├── .claude
├── agents
│ └── stagehand-expert.md
├── output-styles
│ └── pragmatic-test-driven-developer.md
└── commands
│ ├── dev
│ ├── design-app.md
│ └── implement-mvp.md
│ └── agent_prompts
│ └── stagehand_expert_prompt.md
├── my-app
└── tests
│ ├── playwright
│ ├── color-mixer.spec.ts
│ ├── baseline.spec.ts
│ └── interactions.spec.ts
│ ├── stagehand
│ ├── baseline-stagehand.spec.ts
│ ├── color-workflows.spec.ts
│ └── interactions-stagehand.spec.ts
│ └── README.md
├── PRD.md
├── CLAUDE.md
├── README.md
└── LESSON.md
/.claude/agents/stagehand-expert.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: stagehand-expert
3 | description: Use this agent when you need executable Stagehand test files for TDD workflow. ALWAYS checks latest Stagehand documentation first, then creates hybrid AI+data-testid tests that work locally and in cloud. Expert in LOCAL vs BROWSERBASE modes, proper API usage (stagehand.page.act/observe), and fallback strategies for when AI element discovery fails. Context: User needs E2E tests for color picker app. user: 'Create E2E tests for RGB sliders and preset buttons' assistant: 'I'll use the stagehand-expert agent to first check latest Stagehand docs, then create executable test files with hybrid AI+data-testid strategy for reliable TDD workflow' This agent understands real-world Stagehand limitations and creates robust tests that handle AI discovery failures gracefully.
4 | tools: Read, Write
5 | color: cyan
6 | model: sonnet
7 | ---
8 |
9 | Read and Execute: .claude/commands/agent_prompts/stagehand_expert_prompt.md
10 |
--------------------------------------------------------------------------------
/my-app/tests/playwright/color-mixer.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test'
2 |
3 | test.describe('Color Mixer Basic Functionality', () => {
4 | test.beforeEach(async ({ page }) => {
5 | await page.goto('/')
6 | })
7 |
8 | test('should display color mixer with initial state', async ({ page }) => {
9 | // Check title is visible
10 | await expect(page.locator('h1')).toContainText('Color Mixer')
11 |
12 | // Check color preview exists
13 | const preview = page.locator('[data-testid="color-preview"]')
14 | await expect(preview).toBeVisible()
15 |
16 | // Check initial color values
17 | await expect(page.locator('[data-testid="hex-value"]')).toContainText('#FF5733')
18 | await expect(page.locator('[data-testid="rgb-value"]')).toContainText('rgb(255, 87, 51)')
19 |
20 | // Check RGB sliders exist
21 | await expect(page.locator('[data-testid="slider-r"]')).toBeVisible()
22 | await expect(page.locator('[data-testid="slider-g"]')).toBeVisible()
23 | await expect(page.locator('[data-testid="slider-b"]')).toBeVisible()
24 |
25 | // Check presets exist
26 | await expect(page.locator('[data-testid="preset-red"]')).toBeVisible()
27 | await expect(page.locator('[data-testid="preset-green"]')).toBeVisible()
28 | await expect(page.locator('[data-testid="preset-blue"]')).toBeVisible()
29 | await expect(page.locator('[data-testid="preset-yellow"]')).toBeVisible()
30 | await expect(page.locator('[data-testid="preset-black"]')).toBeVisible()
31 | await expect(page.locator('[data-testid="preset-white"]')).toBeVisible()
32 | })
33 |
34 | test('should update color when clicking presets', async ({ page }) => {
35 | // Click red preset
36 | await page.locator('[data-testid="preset-red"]').click()
37 | await expect(page.locator('[data-testid="hex-value"]')).toContainText('#FF0000')
38 | await expect(page.locator('[data-testid="rgb-value"]')).toContainText('rgb(255, 0, 0)')
39 |
40 | // Click blue preset
41 | await page.locator('[data-testid="preset-blue"]').click()
42 | await expect(page.locator('[data-testid="hex-value"]')).toContainText('#0000FF')
43 | await expect(page.locator('[data-testid="rgb-value"]')).toContainText('rgb(0, 0, 255)')
44 | })
45 |
46 | test('should copy color values to clipboard', async ({ page, context }) => {
47 | // Grant clipboard permissions
48 | await context.grantPermissions(['clipboard-read', 'clipboard-write'])
49 |
50 | // Click to copy hex value
51 | await page.locator('[data-testid="hex-value"]').click()
52 |
53 | // Wait for toast to appear
54 | await page.waitForTimeout(500)
55 |
56 | // Check clipboard content
57 | const clipboardText = await page.evaluate(() => navigator.clipboard.readText())
58 | expect(clipboardText).toBe('#FF5733')
59 | })
60 | })
--------------------------------------------------------------------------------
/.claude/output-styles/pragmatic-test-driven-developer.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Pragmatic Test-Driven Developer
3 | description: Simple TDD cycle - write test, implement minimal code, verify with user
4 | ---
5 |
6 | You follow a strict Test-Driven Development (TDD) cycle for all development work.
7 |
8 | ## TDD Cycle: Red → Green → Verify
9 |
10 | ### 1. RED: Write the Test First
11 |
12 | - Write a SMALL number of failing tests for the specific feature/behavior
13 | - Run the tests to confirm it fails
14 | - State: "❌ Test written and failing: [test description]"
15 |
16 | ### 2. GREEN: Implement Minimal Code
17 |
18 | - Write the MINIMUM amount of code needed to make that the tests pass.
19 | - No extra features, no "while we're here" additions
20 | - Focus only on making the test green
21 | - State: "✅ Implemented: [minimal description]"
22 |
23 | ### 3. VERIFY: Check with User
24 |
25 | - Run the test to confirm it passes
26 | - Show the working feature to the user
27 | - Ask: "Test passing ✅ - please verify this works as expected before I continue"
28 | - **IMPORTANT** Wait for user feedback before proceeding on any subsequent task in the Todo list.
29 |
30 | ## Rules
31 |
32 | ### What to Do:
33 |
34 | - Write a SMALL number of tests at a time
35 | - Implement the MINIMUM to pass that tests
36 | - **Always verify** with user before moving to next test
37 | - Keep cycles short (5-10 minutes max)
38 |
39 | ### What NOT to Do:
40 |
41 | - Don't implement multiple features at once
42 | - Don't add "nice to have" features
43 | - Don't write multiple tests before implementing
44 | - Don't assume what the user wants next
45 |
46 | ## Communication Style
47 |
48 | **Starting a cycle:**
49 | "Writing test for: [specific behavior]"
50 |
51 | **After test written:**
52 | "❌ Test failing as expected - implementing minimal solution..."
53 |
54 | **After implementation:**
55 | "✅ Test passing - [feature] is working. Please verify before I continue."
56 |
57 | **Waiting for feedback:**
58 | "Ready for next feature when you confirm this works correctly."
59 |
60 | ## Example Flow
61 |
62 | ```
63 | 1. "Writing test for: RGB slider changes color preview"
64 | 2. ❌ "Test failing - slider not connected to preview"
65 | 3. "Implementing minimal slider-to-preview connection..."
66 | 4. ✅ "Test passing - red slider now updates preview color"
67 | 5. "Please test the red slider and confirm it works before I add green/blue sliders"
68 | 6. [Wait for user verification]
69 | 7. "Writing test for: Green slider changes color preview"
70 | 8. [Repeat cycle]
71 | ```
72 |
73 | ## Key Principles
74 |
75 | - **One test, one feature, one verification**
76 | - **User drives the priorities**
77 | - **No assumptions about next steps**
78 | - **Minimal viable implementation**
79 | - **Always verify before proceeding**
80 |
81 | Remember: TDD means the test drives the development, not the other way around. Let the user guide what to build next based on what they see working.
82 |
--------------------------------------------------------------------------------
/PRD.md:
--------------------------------------------------------------------------------
1 | # Product Requirements: Simple Color Mixer
2 |
3 | ## Overview
4 |
5 | A minimalist color mixing tool designed to demonstrate Test-Driven Development with AI-powered testing using Stagehand and traditional Playwright side-by-side.
6 |
7 | ## Core Features
8 |
9 | ### RGB Sliders
10 |
11 | - Three sliders (Red, Green, Blue: 0-255 range)
12 | - Live color preview updates
13 | - Hex value display (#FF5733)
14 | - RGB value display (rgb(255, 87, 51))
15 |
16 | ### Color Presets
17 |
18 | - 6 preset buttons: Red, Blue, Green, Yellow, Black, White
19 | - Instant color application on click
20 |
21 | ### Copy to Clipboard
22 |
23 | - Copy current hex value
24 | - Visual feedback on success
25 |
26 | ---
27 |
28 | ## Testing Comparison
29 |
30 | ### Traditional Playwright
31 |
32 | ```typescript
33 | // Brittle - depends on specific selectors
34 | const redSlider = page.locator('[data-testid="slider-r"]');
35 | await redSlider.fill("255");
36 | const preview = page.locator('[data-testid="color-preview"]');
37 | await expect(preview).toHaveCSS("background-color", "rgb(255, 0, 0)");
38 | ```
39 |
40 | ### Stagehand AI-Powered
41 |
42 | ```typescript
43 | // Resilient - uses natural language
44 | await stagehand.page.act("Set the red color to maximum");
45 | const result = await stagehand.page.extract({
46 | instruction: "Get current color values",
47 | schema: z.object({ hexValue: z.string() }),
48 | });
49 | expect(result.hexValue).toBe("#FF0000");
50 | ```
51 |
52 | ---
53 |
54 | ## Key Testing Advantages
55 |
56 | ### Less Code
57 |
58 | - Playwright: ~150 lines per test file
59 | - Stagehand: ~50 lines for same coverage
60 |
61 | ### Natural Language vs Selectors
62 |
63 | ```typescript
64 | // Instead of finding elements:
65 | page.locator('[data-testid="preset-red"]').click();
66 |
67 | // Use intent:
68 | stagehand.page.act("Click the red preset button");
69 | ```
70 |
71 | ### Structured Data Extraction
72 |
73 | ```typescript
74 | // Extract with schema validation:
75 | const colorData = await stagehand.page.extract({
76 | instruction: "Get current color values",
77 | schema: z.object({
78 | hexValue: z.string(),
79 | rgbValue: z.string(),
80 | isActuallyRed: z.boolean(),
81 | }),
82 | });
83 | ```
84 |
85 | ---
86 |
87 | ## Tech Stack
88 |
89 | - Next.js with App Router
90 | - shadcn/ui components
91 | - React useState
92 | - Tailwind CSS
93 |
94 | ## TDD Testing Strategy
95 |
96 | ### 1. **Baseline Tests** - Structure validation
97 |
98 | - Element presence
99 | - Accessibility attributes
100 | - Layout structure
101 |
102 | ### 2. **Interaction Tests** - User actions
103 |
104 | - Slider manipulation
105 | - Preset button clicks
106 | - Copy functionality
107 |
108 | ### 3. **Workflow Tests** - Complex scenarios
109 |
110 | - Color mixing sequences
111 | - State persistence
112 | - Edge cases
113 |
114 | ## Success Metrics
115 |
116 | - ✅ **70% code reduction** with Stagehand
117 | - ✅ **Tests survive UI refactors** without changes
118 | - ✅ **Natural language** test descriptions
119 | - ✅ **Faster test development** (3x improvement)
120 |
121 | ---
122 |
123 | ## Cloud Testing with Browserbase
124 |
125 | ### MCP Integration
126 |
127 | ```bash
128 | # Install Browserbase MCP
129 | claude mcp add --scope project --transport stdio \
130 | browserbase npx @browserbasehq/mcp-server-browserbase
131 | ```
132 |
133 | ### Test Execution Modes
134 |
135 | ```typescript
136 | // Local development
137 | const stagehand = new Stagehand({ env: "LOCAL" });
138 |
139 | // Cloud testing
140 | const stagehand = new Stagehand({
141 | env: "BROWSERBASE",
142 | apiKey: process.env.BROWSERBASE_API_KEY,
143 | });
144 | ```
145 |
--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------
1 | # CLAUDE.md
2 |
3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4 |
5 | ## Project Overview
6 |
7 | This is a Test-Driven Development (TDD) educational repository demonstrating Stagehand AI-powered testing vs traditional Playwright testing for a Color Mixer application. The repository showcases a 70% code reduction when using AI-powered natural language tests.
8 |
9 | ## Development Commands
10 |
11 | ### Initial Setup
12 | ```bash
13 | # Navigate to my-app directory
14 | cd my-app
15 |
16 | # Install dependencies
17 | npm install
18 |
19 | # Create .env file with required API keys
20 | echo "OPENAI_API_KEY=your_openai_api_key_here" > .env
21 |
22 | # Optional: Install Browserbase MCP for cloud testing
23 | claude mcp add --scope project --transport stdio browserbase npx @browserbasehq/mcp-server-browserbase
24 | ```
25 |
26 | ### Running the Application
27 | ```bash
28 | # Start development server (required for tests)
29 | npm run dev # App runs on http://localhost:3000
30 | ```
31 |
32 | ### Testing Commands
33 | ```bash
34 | # Run all tests
35 | npm run test
36 |
37 | # Run specific test categories
38 | npm run test tests/playwright # All Playwright tests
39 | npm run test tests/stagehand # All Stagehand AI tests
40 |
41 | # Run individual test files
42 | npm run test tests/playwright/baseline.spec.ts
43 | npm run test tests/playwright/color-mixer.spec.ts
44 | npm run test tests/playwright/interactions.spec.ts
45 | npm run test tests/stagehand/baseline-stagehand.spec.ts
46 | npm run test tests/stagehand/color-workflows.spec.ts
47 | npm run test tests/stagehand/interactions-stagehand.spec.ts
48 |
49 | # Debug and development commands
50 | npm run test:headed # Run with visible browser
51 | npm run test:debug # Step-by-step debugging
52 | npm run test:ui # Playwright UI for interactive debugging
53 |
54 | # Advanced test execution
55 | npm run test -- --project=chromium # Specific browser
56 | npm run test -- --grep "color preview" # Pattern matching
57 | npm run test -- --only-failures # Re-run failures
58 | npm run test -- --workers=4 # Parallel execution
59 | ```
60 |
61 | ## Repository Structure
62 |
63 | ```
64 | browserbase-claude-code-stagehand/
65 | ├── my-app/
66 | │ └── tests/
67 | │ ├── playwright/ # Traditional selector-based tests
68 | │ │ ├── baseline.spec.ts # Element presence validation
69 | │ │ ├── interactions.spec.ts # User interaction tests
70 | │ │ └── color-mixer.spec.ts # Full workflow tests
71 | │ └── stagehand/ # AI-powered natural language tests
72 | │ ├── baseline-stagehand.spec.ts
73 | │ ├── interactions-stagehand.spec.ts
74 | │ └── color-workflows.spec.ts
75 | └── CLAUDE.md
76 | ```
77 |
78 | ## TDD Methodology
79 |
80 | Follow the Red → Green → Refactor cycle:
81 |
82 | 1. **RED Phase**: Write tests that fail (tests exist but app doesn't)
83 | 2. **GREEN Phase**: Implement minimal code to pass tests
84 | - Add required `data-testid` attributes
85 | - Implement color calculation logic
86 | - Add event handlers for sliders and buttons
87 | 3. **REFACTOR Phase**: Optimize while keeping tests green
88 | - Performance (<16ms updates)
89 | - Accessibility improvements
90 |
91 | ## Stagehand vs Playwright Patterns
92 |
93 | ### Playwright (Traditional)
94 | ```typescript
95 | const redSlider = page.locator('[data-testid="slider-r"]')
96 | await redSlider.fill('255')
97 | const preview = page.locator('[data-testid="color-preview"] div')
98 | const style = await preview.getAttribute('style')
99 | expect(style).toContain('rgb(255,')
100 | ```
101 |
102 | ### Stagehand (AI-Powered)
103 | ```typescript
104 | await stagehand.page.act('Set red color to maximum')
105 | const result = await stagehand.page.extract({
106 | instruction: "Get current color values",
107 | schema: z.object({ hexValue: z.string() })
108 | })
109 | expect(result.hexValue).toBe('#FF0000')
110 | ```
111 |
112 | ## Key Stagehand API
113 |
114 | - **stagehand.page.act()** - Natural language actions
115 | - **stagehand.page.observe()** - Query page state
116 | - **stagehand.page.extract()** - Extract structured data with Zod schemas
117 |
118 | ## Application Requirements
119 |
120 | The Color Mixer app being tested includes:
121 | - RGB sliders (0-255 range) with live preview
122 | - Hex and RGB value display
123 | - 6 color presets (Red, Green, Blue, Yellow, Black, White)
124 | - Copy to clipboard functionality
125 | - <16ms response time requirement
126 | - Proper accessibility attributes
127 |
128 | ## Environment Variables
129 |
130 | ```bash
131 | # Required for Stagehand tests
132 | OPENAI_API_KEY=your_key_here
133 |
134 | # Optional for cloud testing
135 | BROWSERBASE_API_KEY=your_key_here
136 | BROWSERBASE_PROJECT_ID=your_project_id
137 | ```
--------------------------------------------------------------------------------
/my-app/tests/playwright/baseline.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test'
2 |
3 | /**
4 | * Baseline Tests - Simple Color Mixer Application
5 | *
6 | * These tests verify basic structure and presence of UI elements.
7 | * Following TDD principles - tests should FAIL initially before implementation.
8 | */
9 |
10 | test.describe('Color Mixer Baseline Tests', () => {
11 | test.beforeEach(async ({ page }) => {
12 | // Navigate to the app
13 | await page.goto('/')
14 | })
15 |
16 | test('app loads and displays title', async ({ page }) => {
17 | // Test that the main heading is visible
18 | await expect(page.locator('h1')).toBeVisible()
19 | await expect(page.locator('h1')).toContainText('Color Mixer')
20 |
21 | // Test that the subtitle/description is present
22 | await expect(page.locator('p')).toContainText('Mix and match colors with RGB sliders')
23 | })
24 |
25 | test('color preview element exists', async ({ page }) => {
26 | // Test that the main color preview card is visible
27 | const colorPreview = page.locator('[data-testid="color-preview"]')
28 | await expect(colorPreview).toBeVisible()
29 |
30 | // Test that it has a colored background (should have style attribute)
31 | await expect(colorPreview.locator('div')).toHaveAttribute('style', /background-color/)
32 |
33 | // Test that it has proper aria label for accessibility
34 | await expect(colorPreview.locator('div')).toHaveAttribute('aria-label', /Color preview/)
35 | })
36 |
37 | test('all three RGB sliders exist', async ({ page }) => {
38 | // Test that RGB controls container exists
39 | const rgbControls = page.locator('[data-testid="rgb-controls"]')
40 | await expect(rgbControls).toBeVisible()
41 |
42 | // Test that each slider exists with proper data-testid
43 | await expect(page.locator('[data-testid="slider-r"]')).toBeVisible()
44 | await expect(page.locator('[data-testid="slider-g"]')).toBeVisible()
45 | await expect(page.locator('[data-testid="slider-b"]')).toBeVisible()
46 |
47 | // Test that slider labels are present
48 | await expect(page.locator('[data-testid="label-r"]')).toBeVisible()
49 | await expect(page.locator('[data-testid="label-g"]')).toBeVisible()
50 | await expect(page.locator('[data-testid="label-b"]')).toBeVisible()
51 | })
52 |
53 | test('hex and RGB values are displayed', async ({ page }) => {
54 | // Test that color values container exists
55 | const colorValues = page.locator('[data-testid="color-values"]')
56 | await expect(colorValues).toBeVisible()
57 |
58 | // Test that HEX value badge exists and shows format
59 | const hexValue = page.locator('[data-testid="hex-value"]')
60 | await expect(hexValue).toBeVisible()
61 | await expect(hexValue).toContainText('HEX: #')
62 |
63 | // Test that RGB value badge exists and shows format
64 | const rgbValue = page.locator('[data-testid="rgb-value"]')
65 | await expect(rgbValue).toBeVisible()
66 | await expect(rgbValue).toContainText('RGB: rgb(')
67 | })
68 |
69 | test('six color preset buttons exist', async ({ page }) => {
70 | // Test that preset grid container exists
71 | const presetGrid = page.locator('[data-testid="preset-grid"]')
72 | await expect(presetGrid).toBeVisible()
73 |
74 | // Test that all 6 preset buttons exist with proper data-testids
75 | await expect(page.locator('[data-testid="preset-red"]')).toBeVisible()
76 | await expect(page.locator('[data-testid="preset-green"]')).toBeVisible()
77 | await expect(page.locator('[data-testid="preset-blue"]')).toBeVisible()
78 | await expect(page.locator('[data-testid="preset-yellow"]')).toBeVisible()
79 | await expect(page.locator('[data-testid="preset-black"]')).toBeVisible()
80 | await expect(page.locator('[data-testid="preset-white"]')).toBeVisible()
81 |
82 | // Test that each preset button has a color indicator dot
83 | await expect(presetGrid.locator('span').first()).toBeVisible()
84 | })
85 |
86 | test('UI elements have proper accessibility attributes', async ({ page }) => {
87 | // Test that main heading has proper hierarchy
88 | await expect(page.locator('h1')).toBeVisible()
89 |
90 | // Test that sliders have labels associated
91 | await expect(page.locator('label[for]').or(page.locator('label')).first()).toBeVisible()
92 |
93 | // Test that clickable elements are properly marked
94 | const hexBadge = page.locator('[data-testid="hex-value"]')
95 | await expect(hexBadge).toHaveClass(/cursor-pointer/)
96 |
97 | const rgbBadge = page.locator('[data-testid="rgb-value"]')
98 | await expect(rgbBadge).toHaveClass(/cursor-pointer/)
99 | })
100 |
101 | test('page has proper responsive layout', async ({ page }) => {
102 | // Test that main container has max width constraint
103 | const mainContainer = page.locator('.max-w-2xl')
104 | await expect(mainContainer).toBeVisible()
105 |
106 | // Test that cards are properly structured
107 | const cards = page.locator('[class*="Card"]').or(page.locator('.card')).or(page.locator('div').filter({ hasText: /Color preview|RGB|Color Presets/ }))
108 | await expect(cards.first()).toBeVisible()
109 |
110 | // Test that grid layout exists for presets
111 | const presetGrid = page.locator('[data-testid="preset-grid"]')
112 | await expect(presetGrid).toHaveClass(/grid/)
113 | })
114 | })
--------------------------------------------------------------------------------
/my-app/tests/stagehand/baseline-stagehand.spec.ts:
--------------------------------------------------------------------------------
1 | import { test, expect } from '@playwright/test'
2 | import { Stagehand } from '@browserbasehq/stagehand'
3 | import { z } from 'zod'
4 |
5 | /**
6 | * Baseline Stagehand Tests - Simple Color Mixer Application
7 | *
8 | * These tests demonstrate AI-powered element discovery vs data-testid selectors.
9 | * Showcases more maintainable and readable test code using natural language.
10 | */
11 |
12 | test.describe('Color Mixer Baseline Tests - Stagehand', () => {
13 | let stagehand: Stagehand
14 |
15 | test.beforeAll(async () => {
16 | if (!process.env.OPENAI_API_KEY) {
17 | console.warn('OPENAI_API_KEY not found. Stagehand tests will fail.')
18 | }
19 | })
20 |
21 | test.beforeEach(async () => {
22 | stagehand = new Stagehand({
23 | env: 'LOCAL',
24 | modelName: 'gpt-4o',
25 | modelClientOptions: {
26 | apiKey: process.env.OPENAI_API_KEY,
27 | },
28 | verbose: 1,
29 | })
30 |
31 | await stagehand.init()
32 | await stagehand.page.goto('http://localhost:3000')
33 | })
34 |
35 | test.afterEach(async () => {
36 | if (stagehand) {
37 | await stagehand.close()
38 | }
39 | })
40 |
41 | test('app loads and displays title using AI element discovery', async () => {
42 | // ✨ Stagehand: Use natural language to find elements
43 | // Instead of: page.locator('h1')
44 | const titleInfo = await stagehand.page.extract({
45 | instruction: "Get the main heading text and subtitle/description from the color mixer page",
46 | schema: z.object({
47 | mainTitle: z.string().describe("The main heading text"),
48 | description: z.string().describe("The subtitle or description text")
49 | })
50 | })
51 |
52 | expect(titleInfo.mainTitle).toContain('Color Mixer')
53 | expect(titleInfo.description).toContain('Mix and match colors with RGB sliders')
54 | })
55 |
56 | test('all three RGB sliders exist using AI discovery', async () => {
57 | const sliderInfo = await stagehand.page.extract({
58 | instruction: "Describe the RGB color sliders, including their labels (Red, Green, Blue), if they are visible and their current values.",
59 | schema: z.object({
60 | redSlider: z.string().describe("Description of the Red slider and its label in the form: Label: