Управление состоянием в Page Objects

В автоматизированном тестировании на Protractor управление состоянием является ключевым аспектом для обеспечения стабильности и предсказуемости тестов. Page Object — это паттерн, позволяющий абстрагировать взаимодействие с элементами страницы, а также контролировать их состояние, уменьшая дублирование кода и повышая читаемость тестов.

Хранение состояния элементов

Page Object обычно содержит свойства, соответствующие элементам страницы. Для управления состоянием рекомендуется использовать геттеры, возвращающие элемент с проверкой его доступности:

class LoginPage {
    constructor() {
        this.usernameField = element(by.id('username'));
        this.passwordField = element(by.id('password'));
        this.loginButton = element(by.id('loginBtn'));
    }

    async getUsernameField() {
        await browser.wait(protractor.ExpectedConditions.visibilityOf(this.usernameField), 5000);
        return this.usernameField;
    }

    async getLoginButton() {
        await browser.wait(protractor.ExpectedConditions.elementToBeClickable(this.loginButton), 5000);
        return this.loginButton;
    }
}

Использование ExpectedConditions позволяет гарантировать, что элемент готов к взаимодействию, прежде чем выполнять действия.

Управление состоянием данных формы

Для форм и интерактивных элементов важно хранить и отслеживать состояние данных. В Page Object можно создать методы для чтения, установки и проверки значений полей:

class LoginPage {
    async setUsername(username) {
        const field = await this.getUsernameField();
        await field.clear();
        await field.sendKeys(username);
    }

    async getUsername() {
        const field = await this.getUsernameField();
        return field.getAttribute('value');
    }

    async setPassword(password) {
        const field = await this.passwordField;
        await field.clear();
        await field.sendKeys(password);
    }
}

Такой подход позволяет изолировать логику работы с полями от самих тестов и облегчает поддержку.

Состояние видимости и доступности элементов

Элементы могут быть динамическими, появляясь на странице после определенных действий. Для корректного управления состоянием необходимо регулярно проверять видимость и доступность элементов через методы Page Object:

async isLoginButtonVisible() {
    try {
        const button = await this.getLoginButton();
        return await button.isDisplayed();
    } catch (e) {
        return false;
    }
}

Метод возвращает булевое значение, что упрощает построение условий в тестах и предотвращает ошибки ElementNotVisible.

Синхронизация состояния с ожиданиями

Protractor работает с Angular и использует автоматическую синхронизацию, но для сложных страниц с динамическими элементами часто требуется явное ожидание состояния. Page Object должен инкапсулировать логику ожиданий:

async waitForPageToLoad() {
    await browser.wait(async () => {
        const ready = await element(by.css('body')).isPresent();
        return ready;
    }, 10000, 'Страница не загрузилась за 10 секунд');
}

Это позволяет тестам оставаться детерминированными, избегая случайных падений из-за незавершенной загрузки.

Управление состоянием сессии и авторизации

В Page Object удобно хранить методы для управления состоянием сессии пользователя, что особенно актуально для многопользовательских тестов:

class SessionPage {
    async login(username, password) {
        await loginPage.setUsername(username);
        await loginPage.setPassword(password);
        const button = await loginPage.getLoginButton();
        await button.click();
        await browser.wait(protractor.ExpectedConditions.urlContains('/dashboard'), 5000);
    }

    async logout() {
        const logoutBtn = element(by.id('logoutBtn'));
        await browser.wait(protractor.ExpectedConditions.elementToBeClickable(logoutBtn), 5000);
        await logoutBtn.click();
    }
}

Инкапсуляция действий, связанных с сессией, в отдельном Page Object уменьшает зависимость тестов от конкретной страницы и позволяет переиспользовать код в разных сценариях.

Управление сложными состояниями компонентов

Для компонентов с динамическим поведением, таких как модальные окна или таблицы с пагинацией, важно создавать методы, отражающие состояние компонента, а не только взаимодействие с элементами:

class ModalComponent {
    constructor(modalSelector) {
        this.modal = element(by.css(modalSelector));
    }

    async isVisible() {
        return await this.modal.isDisplayed();
    }

    async waitForOpen() {
        await browser.wait(protractor.ExpectedConditions.visibilityOf(this.modal), 5000);
    }

    async close() {
        const closeButton = this.modal.element(by.css('.close-btn'));
        await closeButton.click();
        await browser.wait(protractor.ExpectedConditions.stalenessOf(this.modal), 5000);
    }
}

Поддержка таких методов позволяет тестам реагировать на состояние компонентов, а не просто выполнять последовательность кликов.

Рекомендации по организации состояния

  • Разделять геттеры элементов и методы действий, чтобы состояние и взаимодействие не смешивались.
  • Использовать ожидания состояния элементов (visibilityOf, elementToBeClickable) перед выполнением действий.
  • Инкапсулировать состояние динамических компонентов в отдельные Page Objects или вспомогательные классы.
  • Хранить данные формы в Page Object через методы get/set, а не напрямую в тестах.
  • Для сложных сценариев сессии использовать отдельные объекты, управляющие авторизацией и выходом.

Такой подход к управлению состоянием повышает устойчивость и читаемость тестов, а Page Object становится не просто слоем для поиска элементов, а полноценным инструментом контроля состояния страницы и ее компонентов.