153 lines
3.6 KiB
Markdown
153 lines
3.6 KiB
Markdown
# Page Object Model
|
|
|
|
## Basic Page Object
|
|
|
|
```typescript
|
|
// pages/LoginPage.ts
|
|
import { Page, Locator } from '@playwright/test';
|
|
|
|
export class LoginPage {
|
|
readonly page: Page;
|
|
readonly emailInput: Locator;
|
|
readonly passwordInput: Locator;
|
|
readonly submitButton: Locator;
|
|
readonly errorMessage: Locator;
|
|
|
|
constructor(page: Page) {
|
|
this.page = page;
|
|
this.emailInput = page.getByLabel('Email');
|
|
this.passwordInput = page.getByLabel('Password');
|
|
this.submitButton = page.getByRole('button', { name: 'Log in' });
|
|
this.errorMessage = page.getByRole('alert');
|
|
}
|
|
|
|
async goto() {
|
|
await this.page.goto('/login');
|
|
}
|
|
|
|
async login(email: string, password: string) {
|
|
await this.emailInput.fill(email);
|
|
await this.passwordInput.fill(password);
|
|
await this.submitButton.click();
|
|
}
|
|
|
|
async getErrorMessage() {
|
|
return this.errorMessage.textContent();
|
|
}
|
|
}
|
|
```
|
|
|
|
## Using Page Objects in Tests
|
|
|
|
```typescript
|
|
// tests/login.spec.ts
|
|
import { test, expect } from '@playwright/test';
|
|
import { LoginPage } from '../pages/LoginPage';
|
|
|
|
test('successful login redirects to dashboard', async ({ page }) => {
|
|
const loginPage = new LoginPage(page);
|
|
|
|
await loginPage.goto();
|
|
await loginPage.login('user@test.com', 'password123');
|
|
|
|
await expect(page).toHaveURL(/dashboard/);
|
|
});
|
|
|
|
test('invalid credentials show error', async ({ page }) => {
|
|
const loginPage = new LoginPage(page);
|
|
|
|
await loginPage.goto();
|
|
await loginPage.login('user@test.com', 'wrongpassword');
|
|
|
|
await expect(loginPage.errorMessage).toBeVisible();
|
|
});
|
|
```
|
|
|
|
## Custom Fixtures
|
|
|
|
```typescript
|
|
// fixtures.ts
|
|
import { test as base } from '@playwright/test';
|
|
import { LoginPage } from './pages/LoginPage';
|
|
import { DashboardPage } from './pages/DashboardPage';
|
|
|
|
type Fixtures = {
|
|
loginPage: LoginPage;
|
|
dashboardPage: DashboardPage;
|
|
authenticatedPage: Page;
|
|
};
|
|
|
|
export const test = base.extend<Fixtures>({
|
|
loginPage: async ({ page }, use) => {
|
|
await use(new LoginPage(page));
|
|
},
|
|
|
|
dashboardPage: async ({ page }, use) => {
|
|
await use(new DashboardPage(page));
|
|
},
|
|
|
|
authenticatedPage: async ({ page }, use) => {
|
|
const loginPage = new LoginPage(page);
|
|
await loginPage.goto();
|
|
await loginPage.login('user@test.com', 'password123');
|
|
await page.waitForURL(/dashboard/);
|
|
await use(page);
|
|
},
|
|
});
|
|
|
|
export { expect } from '@playwright/test';
|
|
```
|
|
|
|
## Using Fixtures
|
|
|
|
```typescript
|
|
// tests/dashboard.spec.ts
|
|
import { test, expect } from '../fixtures';
|
|
|
|
test('shows user profile', async ({ authenticatedPage, dashboardPage }) => {
|
|
await expect(dashboardPage.userProfile).toBeVisible();
|
|
});
|
|
```
|
|
|
|
## Component Page Objects
|
|
|
|
```typescript
|
|
// components/NavBar.ts
|
|
export class NavBar {
|
|
constructor(private page: Page) {}
|
|
|
|
readonly homeLink = () => this.page.getByRole('link', { name: 'Home' });
|
|
readonly profileLink = () => this.page.getByRole('link', { name: 'Profile' });
|
|
readonly logoutButton = () => this.page.getByRole('button', { name: 'Logout' });
|
|
|
|
async logout() {
|
|
await this.logoutButton().click();
|
|
}
|
|
}
|
|
|
|
// pages/DashboardPage.ts
|
|
export class DashboardPage {
|
|
readonly navBar: NavBar;
|
|
|
|
constructor(private page: Page) {
|
|
this.navBar = new NavBar(page);
|
|
}
|
|
}
|
|
```
|
|
|
|
## Quick Reference
|
|
|
|
| Pattern | Purpose |
|
|
|---------|---------|
|
|
| Page Object | Encapsulate page interactions |
|
|
| Fixture | Share setup across tests |
|
|
| Component PO | Reusable UI components |
|
|
| Locator methods | Lazy evaluation |
|
|
|
|
| Best Practice | Reason |
|
|
|---------------|--------|
|
|
| Methods for actions | Readable tests |
|
|
| Locators as getters | Lazy evaluation |
|
|
| No assertions in PO | Flexibility |
|
|
| Fixtures for setup | DRY, maintainable |
|