feat: add Playwright E2E and API regression test suite
Production-ready test infrastructure with Page Object Model pattern, reusable fixtures for auth/DB/test-data, and example tests covering login flow, dashboard, accounts CRUD API, and visual regression. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
82
tests/page-objects/AccountsPage.ts
Normal file
82
tests/page-objects/AccountsPage.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Page object for the Accounts page (/accounts).
|
||||
*
|
||||
* Maps to: frontend/src/pages/accounts/AccountsPage.tsx
|
||||
* Data: GET /api/accounts, POST /api/accounts, PUT /api/accounts/:id
|
||||
*/
|
||||
|
||||
import { type Page, expect } from '@playwright/test';
|
||||
import { BasePage } from './BasePage';
|
||||
|
||||
export class AccountsPage extends BasePage {
|
||||
readonly path = '/accounts';
|
||||
|
||||
// ─── Locators ────────────────────────────────────────────────
|
||||
|
||||
get heading() {
|
||||
return this.page.getByRole('heading', { name: /accounts|chart of accounts/i }).first();
|
||||
}
|
||||
|
||||
get addAccountButton() {
|
||||
return this.page.getByRole('button', { name: /add|create|new/i }).first();
|
||||
}
|
||||
|
||||
/** Account name input in the create/edit modal */
|
||||
get nameInput() {
|
||||
return this.page.getByLabel(/name/i).first();
|
||||
}
|
||||
|
||||
/** Account number input */
|
||||
get numberInput() {
|
||||
return this.page.getByLabel(/number/i).first();
|
||||
}
|
||||
|
||||
/** Account type select */
|
||||
get typeSelect() {
|
||||
return this.page.getByLabel(/type/i).first();
|
||||
}
|
||||
|
||||
/** Save/submit button in modal */
|
||||
get saveButton() {
|
||||
return this.page.getByRole('button', { name: /save|create|submit/i }).first();
|
||||
}
|
||||
|
||||
/** Cancel button in modal */
|
||||
get cancelButton() {
|
||||
return this.page.getByRole('button', { name: /cancel/i }).first();
|
||||
}
|
||||
|
||||
// ─── Actions ─────────────────────────────────────────────────
|
||||
|
||||
override async waitForReady(): Promise<void> {
|
||||
await this.page.waitForLoadState('networkidle');
|
||||
// Wait for the accounts list or heading to be visible
|
||||
await expect(this.page.locator('main')).toBeVisible();
|
||||
}
|
||||
|
||||
/** Get the count of visible account rows */
|
||||
async getAccountCount(): Promise<number> {
|
||||
return this.tableRows.count();
|
||||
}
|
||||
|
||||
/** Find an account row by name */
|
||||
accountRow(name: string) {
|
||||
return this.tableBody.locator('tr').filter({ hasText: name });
|
||||
}
|
||||
|
||||
/** Assert an account exists in the table */
|
||||
async assertAccountExists(name: string): Promise<void> {
|
||||
await expect(this.accountRow(name)).toBeVisible();
|
||||
}
|
||||
|
||||
/** Assert an account does NOT exist in the table */
|
||||
async assertAccountNotExists(name: string): Promise<void> {
|
||||
await expect(this.accountRow(name)).not.toBeVisible();
|
||||
}
|
||||
|
||||
/** Open the create account modal */
|
||||
async openCreateModal(): Promise<void> {
|
||||
await this.addAccountButton.click();
|
||||
await expect(this.nameInput).toBeVisible();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user