e2e-tests-studio
Mandatory tool for testing React UI changes by focusing on product workflows and user-centric behavior.
Install
mkdir -p .claude/skills/e2e-tests-studio && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/4429" && unzip -o skill.zip -d .claude/skills/e2e-tests-studio && rm skill.zipInstalls to .claude/skills/e2e-tests-studio
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
REQUIRED when modifying any file in packages/playground-ui or packages/playground. Triggers on: React component creation/modification/refactoring, UI changes, new playground features, bug fixes affecting studio UI. Generates Playwright E2E tests that validate PRODUCT BEHAVIOR, not just UI states.Key capabilities
- →Generate Playwright E2E tests for UI modifications
- →Validate product behavior instead of UI states
- →Enforce BDD structure with precondition groups
- →Verify data persistence after page reloads
- →Intercept API calls to verify payload integrity
How it works
It enforces a strict BDD structure where every test is nested within a 'when' precondition group and asserts a single observable product outcome.
Inputs & outputs
When to use e2e-tests-studio
- →Testing agent provider configuration
- →Validating tool execution workflows
- →Verifying persistent state after UI changes
About this skill
E2E Behavior Validation for Frontend Modifications
Core Principle: Test Product Behavior, Not UI States
CRITICAL: Tests must verify that product features WORK correctly, not just that UI elements render.
What NOT to test (UI States):
- ❌ "Dropdown opens when clicked"
- ❌ "Modal appears after button click"
- ❌ "Loading spinner shows during request"
- ❌ "Form fields are visible"
- ❌ "Sidebar collapses"
What TO test (Product Behavior):
- ✅ "Selecting an LLM provider configures the agent to use that provider"
- ✅ "Creating a new agent persists it and shows in the agents list"
- ✅ "Running a tool with parameters returns the expected output"
- ✅ "Chat messages stream correctly and maintain conversation context"
- ✅ "Workflow execution triggers tools in the correct order"
BDD Structure (REQUIRED)
Every E2E spec MUST follow the same BDD shape as the MSW tests. In packages/playground, e2e-bdd/test-needs-when-describe enforces this shape.
The structure has exactly three levels:
- Outer
test.describe= the unit under test (one page or feature per file). - Inner
test.describe('when …')= exactly ONE precondition. The title MUST start withwhen. - Each
test= exactly ONE observable outcome.
import { test, expect } from '@playwright/test';
import { resetStorage } from '../__utils__/reset-storage';
test.describe('Tools list page', () => {
// the unit
test.afterEach(async () => {
await resetStorage();
});
test.describe('when a registered tool is clicked', () => {
// ONE precondition (starts with "when")
test('navigates to that tool detail page', async ({ page }) => {
// ONE outcome
await page.goto('/tools');
await page.locator('text=Get current weather for a location').click();
await expect(page).toHaveURL(/\/tools\/weatherInfo$/);
});
test('shows the tool name as the page heading', async ({ page }) => {
// ONE outcome
await page.goto('/tools');
await page.locator('text=Get current weather for a location').click();
await expect(page.locator('h2')).toHaveText('weatherInfo');
});
});
});
Rules:
- One outer
test.describeper file naming the unit. - Every leaf
testlives inside atest.describe('when …')precondition group. No top-level flattest(). - Split a multi-assertion
test()only where assertions represent distinct outcomes; keep tightly-coupled assertions that prove a single outcome together. Never drop an assertion. - Place
beforeEach/afterEachin the narrowestdescribescope that needs them.
Prerequisites
Requires Playwright MCP server. If the browser_navigate tool is unavailable, instruct the user to add it:
claude mcp add playwright -- npx @playwright/mcp@latest
Step 1: Understand the Feature Intent
Before writing ANY test, answer these questions:
- What user problem does this feature solve?
- What is the expected outcome when the feature works correctly?
- What data flows through the system? (user input → API → state → UI)
- What should persist after page reload?
- What downstream effects should this action have?
Document these answers as comments in your test file.
Step 2: Build and Start
pnpm build:cli
cd packages/playground/e2e/kitchen-sink && pnpm dev
Verify server at http://localhost:4111
Step 3: Map Feature to Behavior Tests
Feature-to-Test Mapping Guide
| Feature Category | What to Test | Example Assertion |
|---|---|---|
| Agent Configuration | Config changes affect agent behavior | Send message → verify response uses selected model |
| LLM Provider Selection | Selected provider is used in requests | Intercept API call → verify provider in request payload |
| Tool Execution | Tool runs with correct params & returns result | Execute tool → verify output matches expected transformation |
| Workflow Execution | Steps execute in order, data flows between steps | Run workflow → verify each step's output feeds next step |
| Chat/Streaming | Messages persist, context maintained across turns | Multi-turn conversation → verify context awareness |
| MCP Server Tools | Server tools are callable and return data | Call MCP tool → verify response structure and content |
| Memory/Persistence | Data survives page reload | Create item → reload → verify item exists |
| Error Handling | Errors surface correctly to user | Trigger error condition → verify error message + recovery |
Step 4: Write Behavior-Focused Tests
Test Structure Template
import { test, expect, Page } from '@playwright/test';
import { resetStorage } from '../__utils__/reset-storage';
import { selectFixture } from '../__utils__/select-fixture';
import { nanoid } from 'nanoid';
/**
* FEATURE: [Name of feature]
* USER STORY: As a user, I want to [action] so that [outcome]
* BEHAVIOR UNDER TEST: [Specific behavior being validated]
*/
test.describe('[Feature Name] - Behavior Tests', () => {
let page: Page;
test.beforeEach(async ({ browser }) => {
const context = await browser.newContext();
page = await context.newPage();
});
test.afterEach(async () => {
await resetStorage(page);
});
test.describe('when [the single precondition for these outcomes]', () => {
test('[verb describing the single observable outcome]', async () => {
// ARRANGE: Set up preconditions
// - Navigate to the feature
// - Configure any required state
// ACT: Perform the user action that triggers the behavior
// ASSERT: Verify the OUTCOME, not the UI state
// - Check data persistence
// - Verify downstream effects
// - Confirm API calls made correctly
});
});
});
Behavior Test Patterns
Pattern 1: Configuration Affects Behavior
test.describe('when a different LLM provider is selected', () => {
test('uses that provider for agent responses', async () => {
// ARRANGE
await page.goto('/agents/my-agent/chat');
// Intercept API to verify provider
let capturedProvider: string | null = null;
await page.route('**/api/chat', route => {
const body = JSON.parse(route.request().postData() || '{}');
capturedProvider = body.provider;
route.continue();
});
// ACT: Select a different provider
await page.getByTestId('provider-selector').click();
await page.getByRole('option', { name: 'OpenAI' }).click();
// Send a message to trigger the agent
await page.getByTestId('chat-input').fill('Hello');
await page.getByTestId('send-button').click();
// ASSERT: Verify the selected provider was used
await expect.poll(() => capturedProvider).toBe('openai');
});
});
Pattern 2: Data Persistence
test.describe('when a new agent is created', () => {
test('persists after page reload', async () => {
// ARRANGE
await page.goto('/agents');
const agentName = `Test Agent ${nanoid()}`;
// ACT: Create new agent
await page.getByTestId('create-agent-button').click();
await page.getByTestId('agent-name-input').fill(agentName);
await page.getByTestId('save-agent-button').click();
// Wait for creation to complete
await expect(page.getByText(agentName)).toBeVisible();
// ASSERT: Verify persistence
await page.reload();
await expect(page.getByText(agentName)).toBeVisible({ timeout: 10000 });
});
});
Pattern 3: Tool Execution Produces Correct Output
test.describe('when the weather tool is executed with a city', () => {
test('returns formatted weather data for that city', async () => {
// ARRANGE
await selectFixture(page, 'weather-success');
await page.goto('/tools/weather-tool');
// ACT: Execute tool with parameters
await page.getByTestId('param-city').fill('San Francisco');
await page.getByTestId('execute-tool-button').click();
// ASSERT: Verify OUTPUT content, not just that output appears
const output = page.getByTestId('tool-output');
await expect(output).toContainText('temperature');
await expect(output).toContainText('San Francisco');
// Verify structured data if applicable
const outputText = await output.textContent();
const outputData = JSON.parse(outputText || '{}');
expect(outputData).toHaveProperty('temperature');
expect(outputData).toHaveProperty('conditions');
});
});
Pattern 4: Workflow Step Chaining
test.describe('when a multi-step workflow is run', () => {
test('passes data between steps correctly', async () => {
// ARRANGE
await selectFixture(page, 'workflow-multi-step');
const sessionId = nanoid();
await page.goto(`/workflows/data-pipeline?session=${sessionId}`);
// ACT: Trigger workflow execution
await page.getByTestId('workflow-input').fill('test input data');
await page.getByTestId('run-workflow-button').click();
// ASSERT: Verify each step received correct input from previous step
// Wait for completion
await expect(page.getByTestId('workflow-status')).toHaveText('completed', { timeout: 30000 });
// Check step outputs show data transformation chain
const step1Output = await page.getByTestId('step-1-output').textContent();
const step2Output = await page.getByTestId('step-2-output').textContent();
// Verify step 2 received step 1's output as input
expect(step2Output).toContain(step1Output);
});
});
Pattern 5: Streaming Chat with Context
test.describe('when a multi-turn conversation is held', ()
---
*Content truncated.*
When not to use it
- →When testing simple UI visual states
- →When the feature does not involve product behavior
Prerequisites
Limitations
- →Requires Playwright MCP server availability
- →Strict BDD structure is mandatory for all tests
How it compares
It mandates testing product outcomes and data persistence, whereas standard E2E testing often focuses on verifying UI element visibility.
Compared to similar skills
e2e-tests-studio side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| e2e-tests-studio (this skill) | 1 | 6mo | Review | Advanced |
| ui-ux-expert-skill | 91 | 9mo | Review | Advanced |
| frontend-testing | 11 | 3mo | Review | Intermediate |
| feature-flags | 6 | 6mo | Review | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by mastra-ai
View all by mastra-ai →You might also like
ui-ux-expert-skill
fercracix33
Technical workflow for implementing accessible React user interfaces with shadcn/ui, Tailwind CSS, and TanStack Query. Includes 6-phase process with mandatory Style Guide compliance, Context7 best practices consultation, Chrome DevTools validation, and WCAG 2.1 AA accessibility standards. Use after Test Agent, Implementer, and Supabase agents complete their work.
frontend-testing
langgenius
Generate Vitest + React Testing Library tests for Dify frontend components, hooks, and utilities. Triggers on testing, spec files, coverage, Vitest, RTL, unit tests, integration tests, or write/review test requests.
feature-flags
Use when feature flag tests fail, flags need updating, understanding @gate pragmas, debugging channel-specific test failures, or adding new flags to React.
react-hook-form-writer
dust-tt
Write and refactor React forms using react-hook-form with Zod validation. Use when creating new form components, converting existing forms to react-hook-form, or implementing form validation patterns.
ui-testing
alinaqi
Visual testing - catch invisible buttons, broken layouts, contrast
react-skills
llama-farm
React 18 patterns for LlamaFarm Designer. Covers components, hooks, TanStack Query, and testing.