144 lines
6.9 KiB
Markdown
144 lines
6.9 KiB
Markdown
# Password Change Template
|
|
|
|
Tests current password verification, new password validation, and success flow.
|
|
|
|
## Prerequisites
|
|
- Authenticated session via `{{authStorageStatePath}}`
|
|
- Current password: `{{currentPassword}}`
|
|
- New password: `{{newPassword}}`
|
|
|
|
---
|
|
|
|
## TypeScript
|
|
|
|
```typescript
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('Password Change', () => {
|
|
test.use({ storageState: '{{authStorageStatePath}}' });
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.goto('{{baseUrl}}/settings/security');
|
|
});
|
|
|
|
// Happy path: successful password change
|
|
test('changes password with valid inputs', async ({ page }) => {
|
|
await page.getByRole('textbox', { name: /current password/i }).fill('{{currentPassword}}');
|
|
await page.getByRole('textbox', { name: /^new password$/i }).fill('{{newPassword}}');
|
|
await page.getByRole('textbox', { name: /confirm.*password/i }).fill('{{newPassword}}');
|
|
await page.getByRole('button', { name: /change.*password|update password/i }).click();
|
|
await expect(page.getByRole('alert')).toContainText(/password.*changed|updated successfully/i);
|
|
});
|
|
|
|
// Happy path: can log in with new password
|
|
test('new password accepted on next login', async ({ page, context }) => {
|
|
// Change password
|
|
await page.getByRole('textbox', { name: /current password/i }).fill('{{currentPassword}}');
|
|
await page.getByRole('textbox', { name: /^new password$/i }).fill('{{newPassword}}');
|
|
await page.getByRole('textbox', { name: /confirm.*password/i }).fill('{{newPassword}}');
|
|
await page.getByRole('button', { name: /change.*password/i }).click();
|
|
await expect(page.getByRole('alert')).toContainText(/changed/i);
|
|
// Log out and back in
|
|
await page.getByRole('button', { name: /user menu/i }).click();
|
|
await page.getByRole('menuitem', { name: /sign out/i }).click();
|
|
await page.getByRole('textbox', { name: /email/i }).fill('{{username}}');
|
|
await page.getByRole('textbox', { name: /password/i }).fill('{{newPassword}}');
|
|
await page.getByRole('button', { name: /sign in/i }).click();
|
|
await expect(page).toHaveURL('{{baseUrl}}/dashboard');
|
|
});
|
|
|
|
// Error case: wrong current password
|
|
test('shows error when current password is wrong', async ({ page }) => {
|
|
await page.getByRole('textbox', { name: /current password/i }).fill('wrong-password');
|
|
await page.getByRole('textbox', { name: /^new password$/i }).fill('{{newPassword}}');
|
|
await page.getByRole('textbox', { name: /confirm.*password/i }).fill('{{newPassword}}');
|
|
await page.getByRole('button', { name: /change.*password/i }).click();
|
|
await expect(page.getByRole('alert')).toContainText(/current password.*incorrect|wrong password/i);
|
|
});
|
|
|
|
// Error case: new passwords do not match
|
|
test('shows error when confirmation does not match', async ({ page }) => {
|
|
await page.getByRole('textbox', { name: /current password/i }).fill('{{currentPassword}}');
|
|
await page.getByRole('textbox', { name: /^new password$/i }).fill('{{newPassword}}');
|
|
await page.getByRole('textbox', { name: /confirm.*password/i }).fill('mismatch');
|
|
await page.getByRole('button', { name: /change.*password/i }).click();
|
|
await expect(page.getByText(/passwords.*do not match/i)).toBeVisible();
|
|
});
|
|
|
|
// Error case: new password too weak
|
|
test('shows strength error for weak new password', async ({ page }) => {
|
|
await page.getByRole('textbox', { name: /current password/i }).fill('{{currentPassword}}');
|
|
await page.getByRole('textbox', { name: /^new password$/i }).fill('123');
|
|
await page.getByRole('textbox', { name: /^new password$/i }).blur();
|
|
await expect(page.getByText(/too weak|at least \d+ characters/i)).toBeVisible();
|
|
});
|
|
|
|
// Error case: new password same as current
|
|
test('shows error when new password matches current', async ({ page }) => {
|
|
await page.getByRole('textbox', { name: /current password/i }).fill('{{currentPassword}}');
|
|
await page.getByRole('textbox', { name: /^new password$/i }).fill('{{currentPassword}}');
|
|
await page.getByRole('textbox', { name: /confirm.*password/i }).fill('{{currentPassword}}');
|
|
await page.getByRole('button', { name: /change.*password/i }).click();
|
|
await expect(page.getByText(/same as.*current|choose.*different/i)).toBeVisible();
|
|
});
|
|
|
|
// Edge case: password strength meter updates on input
|
|
test('strength meter reacts to new password input', async ({ page }) => {
|
|
await page.getByRole('textbox', { name: /^new password$/i }).fill('weak');
|
|
await expect(page.getByRole('meter', { name: /strength/i })).toHaveAttribute('aria-valuenow', '1');
|
|
await page.getByRole('textbox', { name: /^new password$/i }).fill('Str0ng!Pass#2026');
|
|
await expect(page.getByRole('meter', { name: /strength/i })).toHaveAttribute('aria-valuenow', '4');
|
|
});
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## JavaScript
|
|
|
|
```javascript
|
|
const { test, expect } = require('@playwright/test');
|
|
|
|
test.describe('Password Change', () => {
|
|
test.use({ storageState: '{{authStorageStatePath}}' });
|
|
|
|
test('changes password with valid inputs', async ({ page }) => {
|
|
await page.goto('{{baseUrl}}/settings/security');
|
|
await page.getByRole('textbox', { name: /current password/i }).fill('{{currentPassword}}');
|
|
await page.getByRole('textbox', { name: /^new password$/i }).fill('{{newPassword}}');
|
|
await page.getByRole('textbox', { name: /confirm.*password/i }).fill('{{newPassword}}');
|
|
await page.getByRole('button', { name: /change.*password/i }).click();
|
|
await expect(page.getByRole('alert')).toContainText(/changed|updated/i);
|
|
});
|
|
|
|
test('shows error for wrong current password', async ({ page }) => {
|
|
await page.goto('{{baseUrl}}/settings/security');
|
|
await page.getByRole('textbox', { name: /current password/i }).fill('wrong');
|
|
await page.getByRole('textbox', { name: /^new password$/i }).fill('{{newPassword}}');
|
|
await page.getByRole('textbox', { name: /confirm.*password/i }).fill('{{newPassword}}');
|
|
await page.getByRole('button', { name: /change.*password/i }).click();
|
|
await expect(page.getByRole('alert')).toContainText(/incorrect|wrong/i);
|
|
});
|
|
|
|
test('shows mismatch error', async ({ page }) => {
|
|
await page.goto('{{baseUrl}}/settings/security');
|
|
await page.getByRole('textbox', { name: /current password/i }).fill('{{currentPassword}}');
|
|
await page.getByRole('textbox', { name: /^new password$/i }).fill('{{newPassword}}');
|
|
await page.getByRole('textbox', { name: /confirm.*password/i }).fill('nope');
|
|
await page.getByRole('button', { name: /change.*password/i }).click();
|
|
await expect(page.getByText(/do not match/i)).toBeVisible();
|
|
});
|
|
});
|
|
```
|
|
|
|
## Variants
|
|
| Variant | Description |
|
|
|---------|-------------|
|
|
| Success | All fields valid → success alert |
|
|
| Login with new pw | New password accepted at login |
|
|
| Wrong current | Incorrect current → error alert |
|
|
| Mismatch | Confirm ≠ new → validation error |
|
|
| Weak password | Short password → strength error |
|
|
| Same as current | Reuse blocked with error |
|
|
| Strength meter | Meter aria-valuenow updates on input |
|