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>
137 lines
5.7 KiB
Markdown
137 lines
5.7 KiB
Markdown
# Account Delete Template
|
|
|
|
Tests account deletion flow with confirmation and data warning.
|
|
|
|
## Prerequisites
|
|
- Authenticated session via `{{authStorageStatePath}}`
|
|
- Disposable test account (deletion is irreversible)
|
|
- Settings at `{{baseUrl}}/settings/account`
|
|
|
|
---
|
|
|
|
## TypeScript
|
|
|
|
```typescript
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('Account Delete', () => {
|
|
test.use({ storageState: '{{authStorageStatePath}}' });
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('{{baseUrl}}/settings/account');
|
|
});
|
|
|
|
// Happy path: delete button opens confirmation
|
|
test('clicking delete account shows confirmation dialog', async ({ page }) => {
|
|
await page.getByRole('button', { name: /delete.*account/i }).click();
|
|
const dialog = page.getByRole('dialog', { name: /delete account/i });
|
|
await expect(dialog).toBeVisible();
|
|
await expect(dialog).toContainText(/irreversible|cannot be undone/i);
|
|
await expect(dialog).toContainText(/{{dataWarningText}}/i);
|
|
});
|
|
|
|
// Happy path: cancel preserves account
|
|
test('cancel keeps account intact', async ({ page }) => {
|
|
await page.getByRole('button', { name: /delete.*account/i }).click();
|
|
await page.getByRole('dialog').getByRole('button', { name: /cancel/i }).click();
|
|
await expect(page.getByRole('dialog')).toBeHidden();
|
|
await expect(page).toHaveURL('{{baseUrl}}/settings/account');
|
|
});
|
|
|
|
// Happy path: type-to-confirm gates deletion
|
|
test('confirm button disabled until account email typed', async ({ page }) => {
|
|
await page.getByRole('button', { name: /delete.*account/i }).click();
|
|
const dialog = page.getByRole('dialog');
|
|
const confirmBtn = dialog.getByRole('button', { name: /delete.*account|confirm/i });
|
|
await expect(confirmBtn).toBeDisabled();
|
|
await dialog.getByRole('textbox', { name: /type.*email/i }).fill('{{username}}');
|
|
await expect(confirmBtn).toBeEnabled();
|
|
});
|
|
|
|
// Happy path: successful deletion redirects to login
|
|
test('deletes account and redirects to login', async ({ page }) => {
|
|
await page.getByRole('button', { name: /delete.*account/i }).click();
|
|
const dialog = page.getByRole('dialog');
|
|
await dialog.getByRole('textbox', { name: /type.*email/i }).fill('{{username}}');
|
|
await dialog.getByRole('button', { name: /delete.*account|confirm/i }).click();
|
|
await expect(page).toHaveURL(/\/login/);
|
|
await expect(page.getByText(/account.*deleted|successfully deleted/i)).toBeVisible();
|
|
});
|
|
|
|
// Error case: wrong email in confirmation box
|
|
test('shows error when wrong email typed in confirmation', async ({ page }) => {
|
|
await page.getByRole('button', { name: /delete.*account/i }).click();
|
|
const dialog = page.getByRole('dialog');
|
|
await dialog.getByRole('textbox', { name: /type.*email/i }).fill('wrong@email.com');
|
|
const confirmBtn = dialog.getByRole('button', { name: /delete.*account|confirm/i });
|
|
await expect(confirmBtn).toBeDisabled();
|
|
await expect(dialog.getByText(/does not match/i)).toBeVisible();
|
|
});
|
|
|
|
// Error case: deletion fails server-side
|
|
test('shows error when account deletion fails', async ({ page }) => {
|
|
await page.route('{{baseUrl}}/api/account', route =>
|
|
route.fulfill({ status: 500, body: JSON.stringify({ error: 'Deletion failed' }) })
|
|
);
|
|
await page.getByRole('button', { name: /delete.*account/i }).click();
|
|
const dialog = page.getByRole('dialog');
|
|
await dialog.getByRole('textbox', { name: /type.*email/i }).fill('{{username}}');
|
|
await dialog.getByRole('button', { name: /confirm/i }).click();
|
|
await expect(page.getByRole('alert')).toContainText(/failed|error/i);
|
|
await expect(page).toHaveURL('{{baseUrl}}/settings/account');
|
|
});
|
|
|
|
// Edge case: data export offered before deletion
|
|
test('shows data export option in deletion dialog', async ({ page }) => {
|
|
await page.getByRole('button', { name: /delete.*account/i }).click();
|
|
await expect(page.getByRole('link', { name: /export.*data|download.*data/i })).toBeVisible();
|
|
});
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## JavaScript
|
|
|
|
```javascript
|
|
const { test, expect } = require('@playwright/test');
|
|
|
|
test.describe('Account Delete', () => {
|
|
test.use({ storageState: '{{authStorageStatePath}}' });
|
|
|
|
test('shows confirmation dialog on delete', async ({ page }) => {
|
|
await page.goto('{{baseUrl}}/settings/account');
|
|
await page.getByRole('button', { name: /delete.*account/i }).click();
|
|
await expect(page.getByRole('dialog', { name: /delete account/i })).toBeVisible();
|
|
await expect(page.getByRole('dialog')).toContainText(/irreversible/i);
|
|
});
|
|
|
|
test('confirm button disabled until email typed', async ({ page }) => {
|
|
await page.goto('{{baseUrl}}/settings/account');
|
|
await page.getByRole('button', { name: /delete.*account/i }).click();
|
|
const dialog = page.getByRole('dialog');
|
|
await expect(dialog.getByRole('button', { name: /confirm/i })).toBeDisabled();
|
|
await dialog.getByRole('textbox', { name: /type.*email/i }).fill('{{username}}');
|
|
await expect(dialog.getByRole('button', { name: /confirm/i })).toBeEnabled();
|
|
});
|
|
|
|
test('cancel preserves account', async ({ page }) => {
|
|
await page.goto('{{baseUrl}}/settings/account');
|
|
await page.getByRole('button', { name: /delete.*account/i }).click();
|
|
await page.getByRole('dialog').getByRole('button', { name: /cancel/i }).click();
|
|
await expect(page).toHaveURL('{{baseUrl}}/settings/account');
|
|
});
|
|
});
|
|
```
|
|
|
|
## Variants
|
|
| Variant | Description |
|
|
|---------|-------------|
|
|
| Dialog opens | Delete button → confirmation with warning |
|
|
| Cancel | Dialog closed, account preserved |
|
|
| Type-to-confirm | Button enabled only with correct email |
|
|
| Successful delete | Account deleted → /login |
|
|
| Wrong email | Input mismatch → button stays disabled |
|
|
| Server error | Deletion fails → error alert |
|
|
| Data export | Export link offered in dialog |
|