Files
claude-skills-reference/engineering-team/playwright-pro/templates/settings/password-change.md
Alireza Rezvani d33d03da50 feat: add playwright-pro plugin — production-grade Playwright testing toolkit (#254)
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>
2026-03-05 13:50:05 +01:00

6.9 KiB

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

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

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