agentsclimarketplace

E2e testing

Skill Inteligentsensingsolutions/tdd-dev-workflow/skills/e2e-testing

This skill should be used when the user asks about "e2e testing", "end-to-end tests", "when to write e2e tests", "e2e test strategy", "what should I e2e test", "e2e test structure", "browser testing strategy", "data-testid selectors", "flaky e2e tests", "e2e vs unit tests", "Cypress", or needs guidance on when, what, and how to structure end-to-end tests. For Playwright-specific API patterns and configuration, see playwright-patterns instead. Provides E2E testing strategy and principles.From its SKILL.md

Install
npx -y skills add Inteligentsensingsolutions/tdd-dev-workflow --skill e2e-testing

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

10.5 KB, as published. Nobody here has run it

End-to-End Testing

Purpose

Validate complete user workflows through the actual UI. E2E tests complement unit and integration tests by testing real user journeys end-to-end, verifying that all layers of the application work together correctly from the user's perspective.


When to Write E2E Tests

Always Write E2E Tests For

  • Critical user workflows: Login, registration, data entry, approval flows, payment processing.
  • Acceptance criteria involving UI interaction: When the issue specifies user-facing behavior.
  • Cross-component integration: Workflows that span multiple pages or modules.
  • Regression-prone areas: Features that have broken before.

Timing in the Development Workflow

  1. Complete the TDD unit/integration test cycle first (Red-Green-Refactor).
  2. Write E2E tests to validate the full acceptance criteria from the user's perspective.
  3. Run E2E tests before PR submission.

E2E tests are the final validation layer, not a replacement for unit tests.


Test Structure

Page Object Model

Encapsulate page-specific selectors and actions in Page Object classes. Tests interact with pages through these objects, not directly with selectors.

// pages/login.page.ts
export class LoginPage {
  constructor(private page: Page) {}

  async navigate() {
    await this.page.goto('/login');
  }

  async login(email: string, password: string) {
    await this.page.fill('[data-testid="email"]', email);
    await this.page.fill('[data-testid="password"]', password);
    await this.page.click('[data-testid="submit"]');
  }

  async getErrorMessage() {
    return this.page.textContent('[data-testid="error-message"]');
  }
}
// tests/login.e2e.ts
test('displays error for invalid credentials', async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.navigate();
  await loginPage.login('[email protected]', 'wrongpassword');
  const error = await loginPage.getErrorMessage();
  expect(error).toContain('Invalid credentials');
});

Benefits of Page Object Model

  • Selectors change in one place, not across dozens of tests.
  • Tests read like user stories.
  • Reusable actions reduce test code duplication.

Descriptive Test Names

Name tests to match user stories and acceptance criteria:

// Good: describes the user journey
'user can log in and see the dashboard'
'submitting empty form shows validation errors'
'admin can approve pending requests'

// Bad: describes mechanics
'click login button'
'check form validation'
'test admin page'

Setup and Teardown

Isolate test data to prevent tests from interfering with each other:

  • Create fresh test data before each test or test suite.
  • Clean up test data after each test or test suite.
  • Never rely on data from other tests.
  • Use API calls for setup/teardown instead of UI interactions where possible (faster and more reliable).

Writing E2E Tests

Step 1: Describe the User Journey

Before writing code, outline the journey in plain language:

1. User navigates to the registration page.
2. User fills in name, email, and password.
3. User submits the form.
4. System displays a confirmation message.
5. User receives a verification email.

Step 2: Map to Page Actions

Translate each journey step into page actions:

test('new user can register successfully', async ({ page }) => {
  const registrationPage = new RegistrationPage(page);

  await registrationPage.navigate();
  await registrationPage.fillForm({
    name: 'Test User',
    email: '[email protected]',
    password: 'SecurePass123!',
  });
  await registrationPage.submit();

  await expect(page.locator('[data-testid="confirmation"]'))
    .toBeVisible();
  await expect(page.locator('[data-testid="confirmation"]'))
    .toContainText('Registration successful');
});

Step 3: Handle Async Operations

Web applications involve network requests, animations, and dynamic content. Handle these explicitly:

// Wait for network response
await page.waitForResponse(resp =>
  resp.url().includes('/api/users') && resp.status() === 201
);

// Wait for element to appear after async operation
await expect(page.locator('[data-testid="result"]'))
  .toBeVisible({ timeout: 10000 });

// Wait for navigation
await Promise.all([
  page.waitForNavigation(),
  page.click('[data-testid="submit"]'),
]);

Step 4: Assert Visible Outcomes

Assert what the user sees, not internal state:

// Good: asserts visible outcome
await expect(page.locator('h1')).toContainText('Dashboard');
await expect(page.locator('[data-testid="user-name"]'))
  .toContainText('Test User');

// Bad: asserts internal state
expect(await page.evaluate(() => window.__store.user.isLoggedIn))
  .toBe(true);

Running E2E Tests

Development Mode

Run headed (with visible browser) for debugging and development:

npx playwright test --headed

CI Mode

Run headless for continuous integration:

npx playwright test

Running Specific Tests

# Single test file
npx playwright test tests/login.e2e.ts

# Tests matching a pattern
npx playwright test -g "login"

# Specific project/browser
npx playwright test --project=chromium

Debugging Failures

Use the configured project command or fall back to Playwright's debug tools:

# Debug mode with step-through
npx playwright test --debug

# Generate and view trace
npx playwright test --trace on
npx playwright show-trace trace.zip

# View the HTML report
npx playwright show-report

Screenshot on Failure

Configure automatic screenshots for failed tests in playwright.config.ts:

use: {
  screenshot: 'only-on-failure',
  trace: 'retain-on-failure',
}

Review screenshots to understand the UI state at the moment of failure.


Common Patterns

Login Helper

Create a reusable authentication helper to avoid repeating login steps:

async function loginAs(page: Page, role: 'admin' | 'user') {
  const credentials = TEST_USERS[role];
  await page.goto('/login');
  await page.fill('[data-testid="email"]', credentials.email);
  await page.fill('[data-testid="password"]', credentials.password);
  await page.click('[data-testid="submit"]');
  await page.waitForURL('/dashboard');
}

For better performance, use API-based authentication and inject the session cookie directly.

Data Seeding

Seed test data via API before tests run:

test.beforeEach(async ({ request }) => {
  await request.post('/api/test/seed', {
    data: { scenario: 'basic-user-with-items' },
  });
});

test.afterEach(async ({ request }) => {
  await request.post('/api/test/cleanup');
});

API Mocking for External Services

Mock external service responses to avoid flaky tests and third-party dependencies:

await page.route('**/api/external-service/**', route => {
  route.fulfill({
    status: 200,
    contentType: 'application/json',
    body: JSON.stringify({ result: 'mocked-response' }),
  });
});

Visual Regression Snapshots

Capture and compare visual snapshots for UI consistency:

await expect(page.locator('[data-testid="chart"]'))
  .toHaveScreenshot('dashboard-chart.png');

Update baseline snapshots when intentional UI changes are made:

npx playwright test --update-snapshots

Anti-Patterns

Testing Internal State via UI

Do not inspect JavaScript variables, store state, or DOM properties that the user cannot see. Test what is visible.

Flaky Selectors

Avoid selectors that break with minor UI changes:

// Bad: fragile selectors
page.locator('.btn-primary.mt-3.mb-2')
page.locator('div > div > form > button:nth-child(2)')

// Good: stable selectors
page.locator('[data-testid="submit-button"]')
page.getByRole('button', { name: 'Submit' })
page.getByLabel('Email address')

Use data-testid attributes or ARIA roles as stable selectors.

Hard-Coded Waits

Never use fixed sleep or waitForTimeout calls:

// Bad: arbitrary wait
await page.waitForTimeout(3000);

// Good: wait for specific condition
await expect(page.locator('[data-testid="result"]')).toBeVisible();
await page.waitForResponse(resp => resp.url().includes('/api/data'));

Testing Third-Party UIs

Do not write E2E tests for third-party login screens, payment forms, or external services. Mock these at the API boundary.


Integration with TDD Workflow

E2E tests occupy a specific position in the TDD development flow:

  1. Unit tests (TDD cycle): Test individual functions and components in isolation.
  2. Integration tests (TDD cycle): Test component interactions and API endpoints.
  3. E2E tests (this skill): Test complete user workflows through the UI.

Run E2E tests after the unit/integration TDD cycle is complete. They serve as the final confirmation that all pieces work together from the user's perspective.


Quick Reference

AspectGuideline
When to writeAfter TDD cycle, before PR submission
StructurePage Object Model with descriptive test names
Selectorsdata-testid attributes or ARIA roles
AssertionsVisible outcomes, not internal state
WaitsCondition-based, never hard-coded timeouts
Test dataIsolated, seeded via API, cleaned up after
External servicesMocked at the API boundary
DebuggingHeaded mode, traces, screenshots on failure

Dependencies

SkillReason
tdd-disciplineE2E tests run after the TDD unit/integration cycle is complete

Used By

ComponentReason
playwright-patternsBuilds Playwright-specific API patterns on top of the testing principles defined here
Agent: e2e-testerImplements E2E test writing following the patterns defined here

Keep looking

Skills are one crate of 628,971. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.