Обход ограничений WebDriver через JS

Protractor строится поверх Selenium WebDriver и обеспечивает синхронизацию с Angular-приложениями. Однако, несмотря на высокоуровневую абстракцию, стандартные методы WebDriver накладывают ограничения на доступ к DOM, обработку асинхронных операций и выполнение действий, недоступных через публичный API. Для обхода этих ограничений активно используется JavaScript-интерфейс, предоставляемый методом browser.executeScript().


Использование browser.executeScript()

Метод browser.executeScript() позволяет выполнять произвольный JavaScript-код непосредственно в контексте страницы. Основные моменты:

  • Возвращает промис, результат выполнения скрипта доступен через .then().
  • Позволяет работать с DOM напрямую, минуя ограничения WebDriver.
  • Можно использовать для получения данных, недоступных через стандартные локаторы.

Пример: получение текста скрытого элемента

const hiddenText = await browser.executeScript(
    "return document.querySelector('#hidden-element').textContent;"
);
console.log(hiddenText);

В этом примере #hidden-element может быть скрыт стандартными CSS-правилами (display: none или visibility: hidden), что делает его недоступным для element(by.css(...)).getText().


Манипуляции с DOM и событиями

Прямое выполнение JavaScript позволяет инициировать события и изменять свойства элементов:

await browser.executeScript(() => {
    const btn = document.querySelector('#submit');
    btn.click();
});

Такой подход эффективен, если WebDriver не может корректно инициировать клик из-за перекрытия элементов или сложной анимации.

Для создания кастомных событий:

await browser.executeScript(() => {
    const input = document.querySelector('#input-field');
    const event = new Event('input', { bubbles: true });
    input.value = 'Тестовое значение';
    input.dispatchEvent(event);
});

Это позволяет обойти ограничения стандартного метода sendKeys(), особенно для сложных Angular-форм с двумя-way binding.


Асинхронные операции внутри скриптов

executeAsyncScript() используется для выполнения кода, который завершает работу через колбек:

const result = await browser.executeAsyncScript((done) => {
    setTimeout(() => {
        done('Асинхронный результат');
    }, 1000);
});
console.log(result); // 'Асинхронный результат'
  • Метод передаёт в скрипт специальную функцию done, которую необходимо вызвать для завершения.
  • Полезно для имитации таймеров, ожидания завершения сложных операций на странице или работы с Promise в браузере.

Получение и модификация Angular-контекста

Protractor предоставляет возможности работы с Angular через browser.executeScript():

const angularScope = await browser.executeScript(() => {
    const el = document.querySelector('[ng-controller="MyController"]');
    return angular.element(el).scope().myModel;
});
console.log(angularScope);
  • Позволяет читать и изменять данные внутри scope без необходимости изменять шаблонные локаторы.
  • Эффективно для проверки состояния моделей и запуска функций контроллеров.

Доступ к свойствам и атрибутам элементов

Некоторые атрибуты или свойства, например hidden, readonly, value, часто недоступны через стандартный API:

const value = await browser.executeScript(
    "return document.querySelector('#my-input').value;"
);
console.log(value);

Можно устанавливать свойства напрямую:

await browser.executeScript(
    "document.querySelector('#my-input').value = 'Новое значение';"
);

Такой подход позволяет эмулировать действия пользователя, когда WebDriver ограничен политикой браузера или Angular-движком.


Работа с окнами и фреймами

Обычные методы WebDriver для переключения между окнами или фреймами иногда оказываются недостаточны, особенно при сложной структуре DOM. Через JavaScript можно:

  • Определять размеры и позиции элементов для корректного скролла.
  • Получать доступ к документу внутри iframe без необходимости переключения контекста:
const iframeDoc = await browser.executeScript(
    "return document.querySelector('#my-iframe').contentDocument;"
);
  • Манипулировать стилями и прокруткой для элементов, перекрытых другими слоями:
await browser.executeScript(
    "document.querySelector('#overlay').style.display = 'none';"
);

Ограничения и осторожность

  • Скрипты выполняются в контексте страницы, поэтому недоступны серверные данные и элементы вне DOM.
  • Некорректное использование executeScript() может нарушить синхронизацию Angular в Protractor.
  • Излишняя зависимость от прямого JS-обхода может снизить стабильность тестов при изменениях DOM.

Практические советы

  • Использовать executeScript() только для операций, недоступных через стандартные методы Protractor.
  • Разделять обычные локаторы и обходные скрипты для читаемости и поддержки тестов.
  • Всегда обрабатывать промисы и асинхронность корректно, используя async/await.
  • Для сложных Angular-приложений комбинировать Angular-specific локаторы (by.model, by.binding) с JS-обходами при необходимости.

Обход ограничений WebDriver через JavaScript открывает полный доступ к DOM и Angular-контексту, позволяя реализовать тесты, которые невозможно написать стандартными средствами Protractor. Это ключевой инструмент для обеспечения надёжности и гибкости тестового покрытия сложных приложений.