agentsclimarketplace

Qa e2e

Skill christopherlouet/claude-base/.claude/skills/qa-e2e

End-to-end tests with Playwright or Cypress. Trigger when the user wants to create user journey tests, UI integration tests, or browser automation.From its SKILL.md

Install
npx -y skills add christopherlouet/claude-base --skill qa-e2e

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

  • 5 stars5 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

8.2 KB, ~1.9k tokens by cl100k_base, as published. Nobody here has run it

E2E Testing Skill

Triggers

This skill activates when the user mentions:

  • "E2E", "end-to-end", "end-to-end test"
  • "Playwright", "Cypress", "Puppeteer"
  • "integration test", "user journey"
  • "browser automation", "UI test"

Recommended framework

FrameworkAdvantagesUse case
PlaywrightMulti-browser, fast, auto-waitModern apps
CypressExcellent DX, easy debuggingPrototyping

Default recommendation: Playwright

Project structure

e2e/
├── fixtures/           # Custom fixtures
├── pages/              # Page Objects
│   ├── login.page.ts
│   └── dashboard.page.ts
├── tests/
│   ├── auth/
│   │   └── login.spec.ts
│   └── checkout/
│       └── purchase.spec.ts
├── utils/              # Helpers
└── playwright.config.ts

Page Object Model

// e2e/pages/login.page.ts
import { Page, Locator } from '@playwright/test';

export class LoginPage {
  readonly page: Page;
  readonly emailInput: Locator;
  readonly passwordInput: Locator;
  readonly submitButton: Locator;

  constructor(page: Page) {
    this.page = page;
    this.emailInput = page.getByLabel('Email');
    this.passwordInput = page.getByLabel('Password');
    this.submitButton = page.getByRole('button', { name: 'Login' });
  }

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

  async login(email: string, password: string) {
    await this.emailInput.fill(email);
    await this.passwordInput.fill(password);
    await this.submitButton.click();
  }
}

Tests

// e2e/tests/auth/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../../pages/login.page';

test.describe('Authentication', () => {
  let loginPage: LoginPage;

  test.beforeEach(async ({ page }) => {
    loginPage = new LoginPage(page);
    await loginPage.goto();
  });

  test('should login with valid credentials', async ({ page }) => {
    await loginPage.login('[email protected]', 'password');
    await expect(page).toHaveURL('/dashboard');
  });

  test('should show error with invalid credentials', async ({ page }) => {
    await loginPage.login('[email protected]', 'wrong');
    await expect(page.getByRole('alert')).toContainText('Invalid');
  });
});

Critical journeys

JourneyTest points
SignupForm validation, email, success
LoginValid/invalid, remember me, forgot
NavigationMenu, breadcrumbs, deep links
SearchQuery, filters, pagination
CheckoutCart, payment, confirmation

Recommended selectors

PrioritySelectorExample
1RolegetByRole('button', { name: 'Submit' })
2LabelgetByLabel('Email')
3TextgetByText('Welcome')
4Test IDgetByTestId('submit-btn')
5CSS.btn-primary (avoid)

Useful commands

# Run the tests
npx playwright test

# Interactive UI mode
npx playwright test --ui

# Headed mode (see the browser)
npx playwright test --headed

# Debug
npx playwright test --debug

# Generate code
npx playwright codegen http://localhost:3000

# Report
npx playwright show-report

Custom fixtures

Playwright fixtures centralize the setup and inject the Page Objects into the tests:

// e2e/fixtures/index.ts
import { test as base } from '@playwright/test';
import { LoginPage } from '../pages/login.page';
import { DashboardPage } from '../pages/dashboard.page';

type Fixtures = {
  loginPage: LoginPage;
  dashboardPage: DashboardPage;
};

export const test = base.extend<Fixtures>({
  loginPage: async ({ page }, use) => {
    await use(new LoginPage(page));
  },
  dashboardPage: async ({ page }, use) => {
    await use(new DashboardPage(page));
  },
});

export { expect } from '@playwright/test';
// e2e/tests/auth/login.spec.ts (with fixtures)
import { test, expect } from '../../fixtures';

test('should login with valid credentials', async ({ loginPage, page }) => {
  await loginPage.goto();
  await loginPage.login('[email protected]', 'password');
  await expect(page).toHaveURL('/dashboard');
});

Reusable authentication fixture

// e2e/fixtures/auth.ts
import { test as base } from '@playwright/test';

export const test = base.extend({
  authenticatedPage: async ({ page }, use) => {
    await page.goto('/login');
    await page.getByLabel('Email').fill('[email protected]');
    await page.getByLabel('Password').fill('password');
    await page.getByRole('button', { name: 'Login' }).click();
    await page.waitForURL('/dashboard');
    await use(page);
  },
});

Playwright best practices

PracticeDescription
Fresh contextEach test starts in an isolated browser context (no shared state)
Auto-waitingDon't add waitForTimeout - Playwright waits automatically
Web-first assertionsUse expect(locator) which retries automatically, not expect(await locator.textContent())
ParallelismfullyParallel: true in the config for parallel execution
Tracestrace: 'on-first-retry' to debug flaky tests

Anti-patterns

Anti-patternAlternative
page.waitForTimeout(3000)await expect(locator).toBeVisible()
page.$('.my-class')page.getByRole('button', { name: '...' })
XPath selectorsrole/label/text selectors
Tests dependent on each otherEach test is independent
page.evaluate() for assertionsWeb-first assertions with expect
Page Objects with business logicPage Objects = actions + locators only

Rules

IMPORTANT: E2E tests are slow - reserve them for critical journeys (10% of the pyramid).

IMPORTANT: Always use accessible selectors (role, label).

IMPORTANT: Use Playwright fixtures to inject the Page Objects - no new Page() in each test.

YOU MUST implement the Page Object Model for maintainability.

NEVER test implementation details - test user behavior.

NEVER use fragile CSS selectors (classes, dynamic IDs).

NEVER use waitForTimeout - use web-first assertions which retry automatically.

See also

The Microsoft Playwright team publishes their own SKILL.md at microsoft/playwright-cli/skills/playwright-cli (9,978★, last commit 2026-05-04). Authoritative on Playwright API patterns, kept in sync with each Playwright release.

When working on a Playwright project, install the vendor skill alongside this one. This skill captures the opinionated workflow patterns the foundation imposes (TDD, anti-fragility rules, don't-do lists); the vendor skill captures the canonical API patterns that evolve with each Playwright release. Both together is the recommended setup for Playwright users.

Vendor-neutrality disclosure: Microsoft owns Playwright. Per the foundation's vendor curation policy, Microsoft tools that predate the company's deepening OpenAI commercial relationship (e.g. VSCode, GitHub, Playwright created in 2020) are evaluated case-by-case rather than auto-rejected. Playwright remains MIT-licensed and the de-facto standard for E2E testing. We point to the vendor skill because no equivalent vendor-neutral source maintains the API canonically and the community alternative lackeyjb/playwright-skill was 5 months stale at audit time. Re-evaluate this pointer if Microsoft's commercial alignment with OpenAI changes the project's roadmap visibly (e.g. direct OpenAI product integration).

For Cypress users (the other framework this skill covers), no vendor-published Cypress skill was identified at the time of the audit; the framework-agnostic guidance in this skill remains the primary reference.

Install command and full list of validated vendor skills: docs/recipes/recommended-vendor-skills.md. Audit pilot trace: specs/marketplace-audit/qa-skills-pilot-2026-05-06.md.

What ships with it: 1 file

5.2 KB alongside SKILL.md

examples/

Keep looking

Skills are one crate of 325,949. 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.