skills/development/webapp-testing/SKILL.md
Use when the user says 'write browser tests', 'test this page', 'playwright test', 'e2e test', 'end to end test', 'browser test', 'test the UI', or needs Playwright-based browser testing for a web application. Do NOT use for unit tests, API tests, or non-browser testing.
npx skillsauth add cwinvestments/memstack memstack-development-webapp-testingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Produces Playwright end-to-end tests that verify real user flows in a browser.
| Trigger | Status | |---------|--------| | User says "write browser tests" or "playwright test" | ACTIVE | | User says "test this page" or "e2e test" | ACTIVE | | User says "test the UI" or "browser test" | ACTIVE | | User wants unit tests or API tests | NOT this skill — use standard test patterns | | User wants load testing | NOT this skill |
Determine what to test:
| Parameter | How to find | Example |
|-----------|-------------|---------|
| App URL | Check package.json scripts, .env, or ask | http://localhost:3000 |
| Framework | Check package.json dependencies | Next.js, React, SvelteKit |
| Auth required? | Check for login pages, auth middleware | Yes/No |
| Key user flows | Ask or infer from routes | Sign up, checkout, search |
# Check if Playwright is already installed
cat package.json | grep -i playwright
# Check existing test structure
find . -name "*.spec.ts" -o -name "*.test.ts" | head -20
npm init playwright@latest
# or
pnpm add -D @playwright/test
npx playwright install
Confirm playwright.config.ts exists. If not, create with sensible defaults:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
Follow this structure per flow:
import { test, expect } from '@playwright/test';
test.describe('Feature Name', () => {
test('should [expected behavior] when [action]', async ({ page }) => {
// Arrange — navigate to the page
await page.goto('/path');
// Act — perform user actions
await page.getByRole('button', { name: 'Submit' }).click();
// Assert — verify the result
await expect(page.getByText('Success')).toBeVisible();
});
});
Test writing rules:
getByRole, getByLabel, getByText over CSS selectors — they survive refactorstest.describe to group related flowstest.beforeEach for shared navigation/auth setupexpect with auto-waiting or waitForSelectorIf the app requires login:
// tests/e2e/auth.setup.ts
import { test as setup, expect } from '@playwright/test';
setup('authenticate', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill('[email protected]');
await page.getByLabel('Password').fill('testpassword');
await page.getByRole('button', { name: 'Sign in' }).click();
await expect(page).toHaveURL('/dashboard');
// Save signed-in state
await page.context().storageState({ path: '.auth/user.json' });
});
Add to playwright.config.ts:
projects: [
{ name: 'setup', testMatch: /.*\.setup\.ts/ },
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
storageState: '.auth/user.json',
},
dependencies: ['setup'],
},
],
Form validation:
test('should show validation errors for empty required fields', async ({ page }) => {
await page.goto('/form');
await page.getByRole('button', { name: 'Submit' }).click();
await expect(page.getByText('Email is required')).toBeVisible();
});
Navigation flow:
test('should navigate from landing to pricing', async ({ page }) => {
await page.goto('/');
await page.getByRole('link', { name: 'Pricing' }).click();
await expect(page).toHaveURL('/pricing');
await expect(page.getByRole('heading', { name: 'Pricing' })).toBeVisible();
});
API response verification:
test('should load and display data', async ({ page }) => {
await page.goto('/dashboard');
await expect(page.getByRole('table')).toBeVisible();
await expect(page.locator('tbody tr')).toHaveCount(10);
});
# Run all tests
npx playwright test
# Run specific test file
npx playwright test tests/e2e/auth.spec.ts
# Run with UI mode for debugging
npx playwright test --ui
# Show HTML report
npx playwright show-report
Present results:
Playwright tests written:
- [N] test files covering [N] user flows
- Auth setup: [yes/no]
- Flows tested: [list]
Run with: npx playwright test
tools
Use when the user says 'save diary', 'log session', 'wrapping up', or at end of a productive session.
tools
Use when the user says 'submit to marketplace', 'publish my skill', 'share this skill', 'list on marketplace', 'submit plugin', 'publish to community', or needs to submit a skill or plugin to a community marketplace via PR. Do NOT use for building skills or writing plugin code.
development
Use when the user says 'teach me', 'explain as you go', 'mentor mode', 'walk me through', 'help me learn', 'explain why', 'learning mode', or wants real-time plain language narration of decisions and tradeoffs while building. Do NOT use for code review or debugging.
development
Use when the user says 'generate changelog', 'update changelog', 'what changed', 'release notes', 'write changelog', or needs a formatted CHANGELOG.md from git commit history. Do NOT use for diary entries, git log viewing, or commit message writing.