В тестировании веб-приложений на Protractor организация кода через Page Object является одной из лучших практик для повышения читаемости и поддержки тестов. Особое внимание стоит уделять элементам меню и навигации, так как они часто повторяются на страницах и могут быть динамическими.
Page Object для меню обычно включает три ключевых компонента:
Пример структуры 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 для меню и навигации обеспечивает высокую читаемость тестов, снижает дублирование кода и упрощает поддержку автоматизации при изменении интерфейса.