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

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


Page Object и управление переходами

Page Object Model (POM) — основной паттерн для управления страницами в WebdriverIO. Каждая страница приложения описывается отдельным классом, который содержит:

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

Пример класса страницы:

class LoginPage {
    get usernameInput() { return $('#username'); }
    get passwordInput() { return $('#password'); }
    get loginButton() { return $('#login'); }

    async open() {
        await browser.url('/login');
    }

    async login(username, password) {
        await this.usernameInput.setValue(username);
        await this.passwordInput.setValue(password);
        await this.loginButton.click();
        return new DashboardPage(); // возвращаем следующую страницу
    }
}

Важный момент: методы, которые приводят к переходу на новую страницу, должны возвращать объект следующей страницы. Это упрощает управление зависимостями между страницами и позволяет строить цепочки действий.


Асинхронность и ожидания

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

  • waitForExist — ожидание появления элемента в DOM;
  • waitForDisplayed — ожидание видимости элемента;
  • waitUntil — универсальное ожидание произвольного условия.

Пример использования при переходе между страницами:

class DashboardPage {
    get profileLink() { return $('#profile'); }

    async openProfile() {
        await this.profileLink.waitForDisplayed({ timeout: 5000 });
        await this.profileLink.click();
        return new ProfilePage();
    }
}

Это гарантирует, что следующий шаг теста будет выполняться только после полной готовности страницы.


Организация цепочек действий

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

it('должен корректно пройти авторизацию и открыть профиль', async () => {
    const loginPage = new LoginPage();
    const dashboardPage = await loginPage.open().then(() => loginPage.login('user', 'pass'));
    const profilePage = await dashboardPage.openProfile();
    await expect(profilePage.profileHeader).toBeDisplayed();
});

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


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

Иногда зависимости между страницами связаны не только с переходами, но и с состоянием приложения. Например, после логина могут быть доступны определённые данные или виджеты. В таких случаях используют:

  • проверку состояния страницы через методы isDisplayed, isEnabled, getText;
  • сохранение промежуточных данных в свойствах Page Object;
  • методы сброса состояния перед началом нового сценария (reset, logout).

Пример контроля состояния:

class ProfilePage {
    get profileHeader() { return $('h1.profile-title'); }

    async isLoaded() {
        return this.profileHeader.waitForDisplayed({ timeout: 3000 });
    }
}

Метод isLoaded используется перед любыми действиями на странице, что снижает вероятность ошибок из-за несоответствия состояния.


Разделение зависимостей через вспомогательные классы

Для крупных приложений удобно вынести общие операции в отдельные утилитные классы или сервисы. Это позволяет:

  • уменьшить дублирование кода между Page Object;
  • централизовать логику авторизации, работы с данными и навигацией;
  • упростить управление изменениями интерфейса.

Пример сервиса авторизации:

class AuthService {
    static async loginAsUser(username, password) {
        const loginPage = new LoginPage();
        await loginPage.open();
        return loginPage.login(username, password);
    }
}

Теперь тесты могут вызывать сервис, не заботясь о деталях навигации.


Советы по стабильности тестов

  • Использовать явные ожидания перед переходами между страницами.
  • Возвращать объекты следующей страницы после действий, которые её изменяют.
  • Разделять действия на логические шаги с проверкой состояния.
  • Сохранять чистое состояние приложения перед каждым тестом, чтобы избежать скрытых зависимостей.

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


Управление зависимостями между страницами в WebdriverIO строится на строгом разделении ответственности между объектами страниц, правильной обработке асинхронности и централизованной логике навигации. Соблюдение этих принципов позволяет создавать масштабируемые, надёжные и легко поддерживаемые автоматизированные тесты.