← Volver al listado de tecnologías

Capítulo 7: Page Object Model

Por: Siempre Listo
playwrightpompage-objectpatrones

Page Object Model

El patrón POM encapsula la lógica de interacción con páginas en clases reutilizables.

¿Por qué POM?

Estructura Básica

// pages/login.page.ts
import { Page, Locator } from '@playwright/test';

export class LoginPage {
  readonly page: Page;
  readonly emailInput: Locator;
  readonly passwordInput: Locator;
  readonly submitButton: Locator;

  constructor(page: Page) {
    this.page = page;
    this.emailInput = page.getByLabel('Email');
    this.passwordInput = page.getByLabel('Contraseña');
    this.submitButton = page.getByRole('button', { name: 'Iniciar' });
  }

  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();
  }
}

Uso en Tests

// tests/login.spec.ts
import { test, expect } from '@playwright/test';
import { LoginPage } from '../pages/login.page';

test('login exitoso', async ({ page }) => {
  const loginPage = new LoginPage(page);

  await loginPage.goto();
  await loginPage.login('[email protected]', 'password123');

  await expect(page).toHaveURL('/dashboard');
});

Ejemplo: TodoMVC

// pages/todo.page.ts
import { Page, Locator } from '@playwright/test';

export class TodoPage {
  readonly page: Page;
  readonly newTodoInput: Locator;
  readonly todoItems: Locator;

  constructor(page: Page) {
    this.page = page;
    this.newTodoInput = page.getByPlaceholder('What needs to be done?');
    this.todoItems = page.locator('.todo-list li');
  }

  async goto() {
    await this.page.goto('https://demo.playwright.dev/todomvc');
  }

  async addTodo(text: string) {
    await this.newTodoInput.fill(text);
    await this.newTodoInput.press('Enter');
  }

  async getTodoCount() {
    return this.todoItems.count();
  }
}

Ejercicio Práctico

Objetivo

Crear un Page Object para TodoMVC con métodos para completar y eliminar tareas.

Pasos

  1. Crea pages/todo.page.ts
  2. Agrega método completeTodo(index)
  3. Agrega método deleteTodo(index)
  4. Crea un test que use estos métodos
Ver solución
// pages/todo.page.ts
export class TodoPage {
  // ... constructor anterior ...

  async completeTodo(index: number) {
    await this.todoItems.nth(index)
      .getByRole('checkbox').check();
  }

  async deleteTodo(index: number) {
    await this.todoItems.nth(index).hover();
    await this.todoItems.nth(index)
      .getByRole('button', { name: 'Delete' }).click();
  }
}

// tests/todo.spec.ts
test('completar y eliminar', async ({ page }) => {
  const todoPage = new TodoPage(page);
  await todoPage.goto();
  await todoPage.addTodo('Tarea 1');
  await todoPage.completeTodo(0);
  await todoPage.deleteTodo(0);
  expect(await todoPage.getTodoCount()).toBe(0);
});

Criterios de Éxito