Page Object — это паттерн проектирования для тестирования, который позволяет создавать абстракцию интерфейса страницы веб-приложения. Основная идея заключается в том, чтобы вынести все элементы страницы и действия над ними в отдельные классы, уменьшая дублирование кода и повышая читаемость тестов.
В Protractor Page Object реализуется через модули или классы, в которых описываются селекторы элементов страницы и методы взаимодействия с ними.
1. Селекторы элементов Все элементы страницы следует хранить в свойствах класса или объекта. Пример:
class LoginPage {
constructor() {
this.usernameInput = element(by.id('username'));
this.passwordInput = element(by.id('password'));
this.loginButton = element(by.css('button[type="submit"]'));
this.errorMessage = element(by.css('.error-message'));
}
}
2. Методы взаимодействия Методы инкапсулируют действия пользователя: ввод текста, клики, проверки состояния элементов. Это позволяет тестам быть более выразительными:
class LoginPage {
constructor() {
this.usernameInput = element(by.id('username'));
this.passwordInput = element(by.id('password'));
this.loginButton = element(by.css('button[type="submit"]'));
this.errorMessage = element(by.css('.error-message'));
}
async enterUsername(username) {
await this.usernameInput.clear();
await this.usernameInput.sendKeys(username);
}
async enterPassword(password) {
await this.passwordInput.clear();
await this.passwordInput.sendKeys(password);
}
async clickLogin() {
await this.loginButton.click();
}
async getErrorMessageText() {
return await this.errorMessage.getText();
}
}
Protractor использует WebDriverJS, поэтому все действия с элементами
являются асинхронными. Для Page Objects это означает использование
async/await при взаимодействии с элементами:
const loginPage = new LoginPage();
describe('Авторизация', () => {
it('должна показывать ошибку при неверных данных', async () => {
await browser.get('https://example.com/login');
await loginPage.enterUsername('wronguser');
await loginPage.enterPassword('wrongpass');
await loginPage.clickLogin();
const message = await loginPage.getErrorMessageText();
expect(message).toEqual('Неверный логин или пароль');
});
});
При масштабировании проекта рекомендуется:
pages для каждой группы страниц.Пример базового класса:
class BasePage {
async navigateTo(url) {
await browser.get(url);
}
async click(element) {
await element.click();
}
async type(element, text) {
await element.clear();
await element.sendKeys(text);
}
async getText(element) {
return await element.getText();
}
}
Page Object может наследовать этот класс:
class LoginPage extends BasePage {
constructor() {
super();
this.usernameInput = element(by.id('username'));
this.passwordInput = element(by.id('password'));
this.loginButton = element(by.css('button[type="submit"]'));
}
async login(username, password) {
await this.type(this.usernameInput, username);
await this.type(this.passwordInput, password);
await this.click(this.loginButton);
}
}
Protractor автоматически синхронизируется с Angular-приложениями, но
при работе с неангуларными элементами или динамическими компонентами
рекомендуется использовать ExpectedConditions:
const EC = protractor.ExpectedConditions;
await browser.wait(EC.visibilityOf(loginPage.errorMessage), 5000);
const message = await loginPage.getErrorMessageText();
Это предотвращает ошибки типа ElementNotVisible или
StaleElementReferenceError.
Page Objects делают тесты e2e максимально приближенными к поведению реального пользователя. Например, сценарий покупки товара можно описать так:
await homePage.searchProduct('laptop');
await searchResultsPage.selectProduct(0);
await productPage.addToCart();
await cartPage.checkout();
Каждое действие скрывает детали DOM и делает тесты компактными, понятными и поддерживаемыми.
id, data-* атрибуты, а не сложные
CSS-селекторы.Page Objects являются фундаментальной частью e2e тестирования в Protractor, обеспечивая структурированность, читаемость и легкость поддержки больших тестовых проектов. Они создают слой абстракции между тестами и интерфейсом приложения, позволяя сосредоточиться на поведении пользователя, а не на деталях DOM.