Классы в Playwright (Page Object)

Урок: Классы в Playwright (Page Object)

Введение

На предыдущих уроках мы разобрали:

  • что такие классы — как способ объединять данные;
  • что такое методы — как способ описывать поведение.

Теперь пришло время применить это к реальной задаче — автоматизации тестов.

Представь, что ты пишешь тесты для сайта:

  • нужно открыть страницу логина;
  • заполнить поля;
  • нажать кнопку;
  • проверить результат.

Можно писать всё прямо в тесте. В Playwright это асинхронные вызовы; в песочнице теории используем мок page:

const page = {
  fill: async (selector, value) => console.log('fill', selector, value),
  click: async (selector) => console.log('click', selector),
};

(async () => {
  await page.fill('#login', 'user');
  await page.fill('#password', '123');
  await page.click('#submit');
})();

Сначала это выглядит нормально. Но:

  • тесты начинают повторяться;
  • селекторы разбросаны по коду;
  • при изменении страницы нужно править десятки мест.

И вот здесь появляется подход, который используется в реальных проектах — Page Object (Page Object Model).


Что такое Page Object

Page Object — это способ представить страницу сайта как класс.

То есть:

  • страница = класс;
  • элементы страницы = свойства;
  • действия пользователя = методы.

Мы буквально говорим:

«Страница логина — это объект, который умеет вводить логин, пароль и нажимать кнопку».


Первый Page Object класс

Создадим класс для страницы логина:

class LoginPage {
  constructor(page) {
    this.page = page;
  }
}

const page = { label: 'mock' };
const loginPage = new LoginPage(page);

console.log('page внутри объекта:', loginPage.page.label);

Объяснение:

  • page — в реальном тесте это объект Playwright;
  • мы сохраняем его в this.page, чтобы использовать внутри методов;
  • теперь класс знает, с какой страницей он работает.

Добавление элементов страницы

Обычно в Page Object мы описываем элементы:

class LoginPage {
  constructor(page) {
    this.page = page;

    this.loginInput = '#login';
    this.passwordInput = '#password';
    this.submitButton = '#submit';
  }
}

const page = {};
const lp = new LoginPage(page);

console.log(lp.loginInput, lp.passwordInput, lp.submitButton);

Что происходит:

  • мы сохраняем селекторы в свойства;
  • теперь они не разбросаны по тесту;
  • если селектор изменится — меняем в одном месте.

Добавление методов (действий)

Теперь добавим поведение — методы (с моком page, как в прошлом уроке):

const page = {
  fill: async (selector, value) => console.log('fill', selector, value),
  click: async (selector) => console.log('click', selector),
};

class LoginPage {
  constructor(page) {
    this.page = page;

    this.loginInput = '#login';
    this.passwordInput = '#password';
    this.submitButton = '#submit';
  }

  fillLogin(login) {
    return this.page.fill(this.loginInput, login);
  }

  fillPassword(password) {
    return this.page.fill(this.passwordInput, password);
  }

  clickSubmit() {
    return this.page.click(this.submitButton);
  }
}

(async () => {
  const loginPage = new LoginPage(page);
  await loginPage.fillLogin('user');
  await loginPage.fillPassword('123');
  await loginPage.clickSubmit();
})();

Разбор:

  • fillLogin() — вводит логин;
  • fillPassword() — вводит пароль;
  • clickSubmit() — нажимает кнопку;
  • внутри используется this.page и сохранённые селекторы.

Мы спрятали всю работу с «страницей» внутрь класса.


Как это выглядит в тесте

Тест читается как сценарий. Ниже — тот же класс и вызовы в одном фрагменте (в реальном проекте класс лежит в отдельном файле):

const page = {
  fill: async (selector, value) => console.log('fill', selector, value),
  click: async (selector) => console.log('click', selector),
};

class LoginPage {
  constructor(page) {
    this.page = page;
    this.loginInput = '#login';
    this.passwordInput = '#password';
    this.submitButton = '#submit';
  }

  fillLogin(login) {
    return this.page.fill(this.loginInput, login);
  }

  fillPassword(password) {
    return this.page.fill(this.passwordInput, password);
  }

  clickSubmit() {
    return this.page.click(this.submitButton);
  }
}

(async () => {
  const loginPage = new LoginPage(page);
  await loginPage.fillLogin('user');
  await loginPage.fillPassword('123');
  await loginPage.clickSubmit();
})();

Было бы низкоуровнево: await page.fill('#login', 'user').

Стало по смыслу: await loginPage.fillLogin('user') — ровно так в большом блоке выше после const loginPage = new LoginPage(page).


Почему это важно

Page Object решает сразу несколько проблем: переиспользование, централизация селекторов, читаемость.

Улучшение: объединение действий

Можно сделать метод выше уровнем — один сценарий «логин»:

const page = {
  fill: async (selector, value) => console.log('fill', selector, value),
  click: async (selector) => console.log('click', selector),
};

class LoginPage {
  constructor(page) {
    this.page = page;
    this.loginInput = '#login';
    this.passwordInput = '#password';
    this.submitButton = '#submit';
  }

  async login(login, password) {
    await this.page.fill(this.loginInput, login);
    await this.page.fill(this.passwordInput, password);
    await this.page.click(this.submitButton);
  }
}

(async () => {
  const loginPage = new LoginPage(page);
  await loginPage.login('user', '123');
})();

Важно:

Здесь появился async / await внутри метода — подробнее в следующем уроке.


Связь с предыдущими уроками

  • Урок 1: мы создали класс → теперь это страница

  • Урок 2: мы добавили методы → теперь это действия пользователя

Сейчас мы объединили всё:

класс + методы = Page Object


Связь с следующими уроками

Дальше мы усилим эту модель:

  • Урок 4: разберём async/await, потому что все действия в Playwright асинхронные

  • Урок 5: научимся правильно использовать Page Object в тестах и строить структуру проекта


Итоговое понимание

Page Object — это способ думать о тестах через объекты.

Главная идея урока:

Мы превращаем страницу в класс, а действия пользователя — в методы этого класса.

Это даёт:

  • чистый и понятный код;
  • переиспользуемость;
  • простую поддержку;
  • структуру, которая масштабируется.

Если раньше ты писал низкоуровневые вызовы к page, теперь ты думаешь так:

«Страница умеет кликать, вводить данные и выполнять действия».

И это мышление — основа профессиональной автоматизации тестирования.