Page Object для меню и навигации

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

Структура Page Object

Page Object для меню обычно включает три ключевых компонента:

  1. Селекторы элементов – хранят локаторы элементов меню и подменю.
  2. Методы взаимодействия – абстрагируют действия, такие как клик по элементу, открытие подменю или проверка видимости.
  3. Методы проверки состояния – позволяют убедиться, что меню отображается корректно и навигация работает.

Пример структуры Page Object для навигационного меню:

class NavigationMenu {
    constructor() {
        // Основные пункты меню
        this.homeLink = element(by.css('nav .home'));
        this.productsLink = element(by.css('nav .products'));
        this.aboutLink = element(by.css('nav .about'));
        this.contactLink = element(by.css('nav .contact'));

        // Подменю "Продукты"
        this.productsDropdown = element(by.css('.products-dropdown'));
        this.productItem = (name) => this.productsDropdown.element(by.xpath(`.//a[text()='${name}']`));
    }

    async goToHome() {
        await this.homeLink.click();
    }

    async openProductsDropdown() {
        await this.productsLink.click();
        await browser.wait(
            protractor.ExpectedConditions.visibilityOf(this.productsDropdown), 
            5000, 
            'Dropdown should be visible'
        );
    }

    async selectProduct(name) {
        await this.openProductsDropdown();
        const item = this.productItem(name);
        await browser.wait(
            protractor.ExpectedConditions.elementToBeClickable(item), 
            5000, 
            `Product ${name} should be clickable`
        );
        await item.click();
    }

    async goToAbout() {
        await this.aboutLink.click();
    }

    async goToContact() {
        await this.contactLink.click();
    }
}

module.exports = new NavigationMenu();

Выделение динамических элементов

Меню часто содержит динамические пункты. Для работы с ними удобно использовать функции-генераторы локаторов, как в примере выше (productItem(name)). Это позволяет писать универсальные методы, не дублируя код для каждого элемента.

Работа с асинхронностью

Protractor полностью интегрирован с async/await, поэтому все действия над элементами меню должны использовать await. Для динамически появляющихся подменю важно использовать Expected Conditions:

  • visibilityOf – проверка появления подменю на странице.
  • elementToBeClickable – проверка возможности клика по элементу.
  • presenceOf – проверка наличия элемента в DOM.

Применение этих условий предотвращает ошибки, связанные с тем, что тест пытается взаимодействовать с элементом, который ещё не загружен.

Логика навигации

Page Object для меню должен содержать методы, отражающие бизнес-логику, а не просто клики по селекторам. Примеры:

  • selectProduct(name) – открывает меню, выбирает продукт и переходит на соответствующую страницу.
  • navigateToSection(sectionName) – универсальный метод для перехода к разделу, используя карту локаторов.

Это позволяет тестам быть максимально читаемыми и независимыми от реализации HTML.

Пример универсального метода для всех пунктов меню

class NavigationMenu {
    constructor() {
        this.menuMap = {
            home: element(by.css('nav .home')),
            products: element(by.css('nav .products')),
            about: element(by.css('nav .about')),
            contact: element(by.css('nav .contact'))
        };
    }

    async navigateTo(section) {
        const menuItem = this.menuMap[section.toLowerCase()];
        if (!menuItem) throw new Error(`Menu section "${section}" not found`);
        await menuItem.click();
    }
}

module.exports = new NavigationMenu();

Такой подход упрощает добавление новых пунктов меню и поддержание тестов при изменении структуры навигации.

Проверка состояния меню

Для проверки правильности отображения и функциональности меню используются методы:

  • Проверка видимости пунктов:
await expect(navigationMenu.homeLink.isDisplayed()).toBe(true);
  • Проверка кликабельности элементов:
await browser.wait(protractor.ExpectedConditions.elementToBeClickable(navigationMenu.aboutLink), 5000);
  • Проверка наличия подменю:
await navigationMenu.openProductsDropdown();
await expect(navigationMenu.productsDropdown.isDisplayed()).toBe(true);

Организация и поддержка Page Object

  • Все методы должны быть атомарными и повторно используемыми, чтобы тесты могли комбинировать действия без дублирования кода.
  • Локаторы лучше держать в одном месте в классе, чтобы при изменении верстки можно было легко исправить селекторы.
  • Для сложных меню с вложенными уровнями можно использовать вложенные Page Object, где каждый подменю реализуется отдельным объектом.

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