Complete Claude Code plugin with: - 9 skills (/pw:init, generate, review, fix, migrate, coverage, testrail, browserstack, report) - 3 specialized agents (test-architect, test-debugger, migration-planner) - 55 test case templates across 11 categories (auth, CRUD, checkout, search, forms, dashboard, settings, onboarding, notifications, API, accessibility) - TestRail MCP server (TypeScript) — 8 tools for bidirectional sync - BrowserStack MCP server (TypeScript) — 7 tools for cross-browser testing - Smart hooks (auto-validate tests, auto-detect Playwright projects) - 6 curated reference docs (golden rules, locators, assertions, fixtures, pitfalls, flaky tests) - Leverages Claude Code built-ins (/batch, /debug, Explore subagent) - Zero-config for core features; TestRail/BrowserStack via env vars - Both TypeScript and JavaScript support throughout Co-authored-by: Leo <leo@openclaw.ai>
132 lines
5.7 KiB
Markdown
132 lines
5.7 KiB
Markdown
# Registration Template
|
|
|
|
Tests signup form submission, validation, and post-registration flow.
|
|
|
|
## Prerequisites
|
|
- Unique test email for each run: `{{newUserEmail}}`
|
|
- App running at `{{baseUrl}}`
|
|
|
|
---
|
|
|
|
## TypeScript
|
|
|
|
```typescript
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
const uniqueEmail = `test+${Date.now()}@example.com`;
|
|
|
|
test.describe('Registration', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('{{baseUrl}}/register');
|
|
});
|
|
|
|
// Happy path: successful registration
|
|
test('registers new user with valid data', async ({ page }) => {
|
|
await page.getByRole('textbox', { name: /first name/i }).fill('{{firstName}}');
|
|
await page.getByRole('textbox', { name: /last name/i }).fill('{{lastName}}');
|
|
await page.getByRole('textbox', { name: /email/i }).fill(uniqueEmail);
|
|
await page.getByRole('textbox', { name: /^password$/i }).fill('{{newPassword}}');
|
|
await page.getByRole('textbox', { name: /confirm.*password/i }).fill('{{newPassword}}');
|
|
await page.getByRole('checkbox', { name: /terms/i }).check();
|
|
await page.getByRole('button', { name: /sign up|register|create account/i }).click();
|
|
await expect(page).toHaveURL(/\/verify-email|\/dashboard|\/onboarding/);
|
|
});
|
|
|
|
// Happy path: success message or redirect
|
|
test('shows confirmation after registration', async ({ page }) => {
|
|
await page.getByRole('textbox', { name: /email/i }).fill(uniqueEmail);
|
|
await page.getByRole('textbox', { name: /^password$/i }).fill('{{newPassword}}');
|
|
await page.getByRole('checkbox', { name: /terms/i }).check();
|
|
await page.getByRole('button', { name: /sign up|register/i }).click();
|
|
await expect(page.getByText(/check your email|account created|welcome/i)).toBeVisible();
|
|
});
|
|
|
|
// Error case: email already registered
|
|
test('shows error for already registered email', async ({ page }) => {
|
|
await page.getByRole('textbox', { name: /email/i }).fill('{{existingUserEmail}}');
|
|
await page.getByRole('textbox', { name: /^password$/i }).fill('{{newPassword}}');
|
|
await page.getByRole('checkbox', { name: /terms/i }).check();
|
|
await page.getByRole('button', { name: /sign up|register/i }).click();
|
|
await expect(page.getByRole('alert')).toContainText(/already.*registered|email.*taken/i);
|
|
});
|
|
|
|
// Error case: terms not accepted
|
|
test('blocks registration if terms not accepted', async ({ page }) => {
|
|
await page.getByRole('textbox', { name: /email/i }).fill(uniqueEmail);
|
|
await page.getByRole('textbox', { name: /^password$/i }).fill('{{newPassword}}');
|
|
await page.getByRole('button', { name: /sign up|register/i }).click();
|
|
await expect(page.getByText(/accept.*terms|terms.*required/i)).toBeVisible();
|
|
});
|
|
|
|
// Error case: weak password
|
|
test('shows error for weak password', async ({ page }) => {
|
|
await page.getByRole('textbox', { name: /^password$/i }).fill('123');
|
|
await page.getByRole('textbox', { name: /^password$/i }).blur();
|
|
await expect(page.getByText(/at least \d+ characters|too weak/i)).toBeVisible();
|
|
});
|
|
|
|
// Error case: passwords mismatch
|
|
test('shows error when passwords do not match', async ({ page }) => {
|
|
await page.getByRole('textbox', { name: /^password$/i }).fill('{{newPassword}}');
|
|
await page.getByRole('textbox', { name: /confirm.*password/i }).fill('different');
|
|
await page.getByRole('textbox', { name: /confirm.*password/i }).blur();
|
|
await expect(page.getByText(/do not match/i)).toBeVisible();
|
|
});
|
|
|
|
// Edge case: already logged-in user redirected
|
|
test('redirects to dashboard when already authenticated', async ({ page, context }) => {
|
|
await context.addCookies([{ name: '{{sessionCookieName}}', value: '{{validSession}}', domain: '{{cookieDomain}}', path: '/' }]);
|
|
await page.goto('{{baseUrl}}/register');
|
|
await expect(page).toHaveURL('{{baseUrl}}/dashboard');
|
|
});
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## JavaScript
|
|
|
|
```javascript
|
|
const { test, expect } = require('@playwright/test');
|
|
|
|
test.describe('Registration', () => {
|
|
test('registers with valid data', async ({ page }) => {
|
|
const email = `test+${Date.now()}@example.com`;
|
|
await page.goto('{{baseUrl}}/register');
|
|
await page.getByRole('textbox', { name: /email/i }).fill(email);
|
|
await page.getByRole('textbox', { name: /^password$/i }).fill('{{newPassword}}');
|
|
await page.getByRole('checkbox', { name: /terms/i }).check();
|
|
await page.getByRole('button', { name: /sign up|register/i }).click();
|
|
await expect(page).toHaveURL(/\/verify-email|\/dashboard|\/onboarding/);
|
|
});
|
|
|
|
test('shows error for existing email', async ({ page }) => {
|
|
await page.goto('{{baseUrl}}/register');
|
|
await page.getByRole('textbox', { name: /email/i }).fill('{{existingUserEmail}}');
|
|
await page.getByRole('textbox', { name: /^password$/i }).fill('{{newPassword}}');
|
|
await page.getByRole('checkbox', { name: /terms/i }).check();
|
|
await page.getByRole('button', { name: /sign up|register/i }).click();
|
|
await expect(page.getByRole('alert')).toContainText(/already.*registered/i);
|
|
});
|
|
|
|
test('requires terms acceptance', async ({ page }) => {
|
|
await page.goto('{{baseUrl}}/register');
|
|
await page.getByRole('textbox', { name: /email/i }).fill(`t${Date.now()}@example.com`);
|
|
await page.getByRole('textbox', { name: /^password$/i }).fill('{{newPassword}}');
|
|
await page.getByRole('button', { name: /sign up|register/i }).click();
|
|
await expect(page.getByText(/accept.*terms/i)).toBeVisible();
|
|
});
|
|
});
|
|
```
|
|
|
|
## Variants
|
|
| Variant | Description |
|
|
|---------|-------------|
|
|
| Valid registration | All fields → redirect or success message |
|
|
| Confirmation | Email check or welcome shown |
|
|
| Existing email | Error alert |
|
|
| Terms not accepted | Validation error |
|
|
| Weak password | Strength error on blur |
|
|
| Password mismatch | Confirm error |
|
|
| Already authed | Redirected to dashboard |
|