Кастомные компоненты в веб-приложениях часто представляют собой элементы с уникальной структурой DOM, которые не соответствуют стандартным HTML-элементам (например, сложные виджеты, кастомные селекты, переключатели, модальные окна). Работа с ними в Protractor требует глубокого понимания как селекторов, так и асинхронной природы взаимодействия с Angular-приложениями.
Основные сложности при тестировании кастомных компонентов:
.click() или .sendKeys() могут не всегда
корректно инициировать Angular-события.Для кастомных компонентов ключевое значение имеет правильный выбор селекторов. Наиболее надёжные подходы:
const customButton = element(by.css('.my-custom-button[data-role="submit"]'));
const modalWindow = element(by.id('customModal'));
const componentInput = element(by.model('user.name'));
const dropdownOption = element(by.css('.dropdown .option[data-value="1"]'));
Использование CSS-селекторов предпочтительнее XPath, так как они быстрее и стабильнее при рендеринге Angular.
Кастомные компоненты часто создают асинхронные ситуации, поэтому важно использовать ожидания. Protractor интегрирован с механизмом ожиданий Angular, но для нестандартных компонентов необходимо применять Explicit Waits:
const EC = protractor.ExpectedConditions;
const button = element(by.css('.custom-button'));
browser.wait(EC.elementToBeClickable(button), 5000);
button.click();
Важные методы ExpectedConditions:
elementToBeClickable(element) — ожидание кликабельного
состояния.visibilityOf(element) — ожидание появления элемента в
DOM.presenceOf(element) — ожидание наличия элемента в DOM,
даже если он невидим.Кастомные селекты часто реализуются как список скрытых элементов и визуальный контейнер. Для выбора значения необходимо сначала открыть селект, затем кликнуть по нужной опции:
const selectContainer = element(by.css('.custom-select'));
const option = element(by.cssContainingText('.custom-select .option', 'Выбор 1'));
selectContainer.click();
browser.wait(EC.visibilityOf(option), 3000);
option.click();
Для кастомных чекбоксов стандартный .click() может не
сработать. Рекомендуется проверять текущее состояние и использовать
JavaScript-клик при необходимости:
const checkbox = element(by.css('.custom-checkbox input'));
checkbox.isSelected().then(selected => {
if (!selected) {
browser.executeScript("arguments[0].click();", checkbox.getWebElement());
}
});
Модальные окна могут появляться динамически. Для надёжного теста необходимо:
const modal = element(by.css('.custom-modal'));
browser.wait(EC.visibilityOf(modal), 5000);
const confirmButton = modal.element(by.css('.confirm'));
confirmButton.click();
browser.wait(EC.invisibilityOf(modal), 5000);
Некоторые кастомные компоненты используют Shadow DOM. Protractor не
имеет прямого метода для работы с shadowRoot, поэтому используется
executeScript:
const shadowHost = element(by.css('custom-component'));
browser.executeScript('return arguments[0].shadowRoot.querySelector("button")', shadowHost.getWebElement())
.then(button => button.click());
sleep():
вместо этого применять ExpectedConditions.class CustomSelect {
constructor(rootSelector) {
this.root = element(by.css(rootSelector));
}
open() {
return this.root.click();
}
selectOption(optionText) {
const option = this.root.element(by.cssContainingText('.option', optionText));
browser.wait(protractor.ExpectedConditions.visibilityOf(option), 3000);
return option.click();
}
getSelectedText() {
return this.root.element(by.css('.selected')).getText();
}
}
// Использование
const countrySelect = new CustomSelect('.country-select');
countrySelect.open();
countrySelect.selectOption('Россия');
countrySelect.getSelectedText().then(text => console.log(text));
Этот подход повышает читаемость тестов и облегчает поддержку, особенно при работе с динамическими кастомными компонентами.