Shadow DOM селекторы

Shadow DOM — это технология, которая позволяет инкапсулировать часть DOM-дерева внутри элемента. Это позволяет создать изолированную область, которая может содержать собственный стиль и структуру, не влияя на внешний DOM. Каждый элемент с Shadow DOM имеет своё локальное дерево, которое не пересекается с основным документом, что делает его мощным инструментом для разработки компонентных библиотек и интерфейсов.

При работе с WebdriverIO важно уметь правильно находить элементы внутри таких Shadow DOM, поскольку стандартные методы поиска элементов не всегда работают в этом контексте. В этой статье подробно рассмотрим, как взаимодействовать с Shadow DOM элементами в WebdriverIO.

Структура и доступ к элементам внутри Shadow DOM

Элементы внутри Shadow DOM находятся в контексте своего родительского компонента. Это означает, что стандартные методы поиска элементов, такие как $$ или $("#element"), не будут работать для поиска внутри Shadow DOM. Для того чтобы взаимодействовать с элементами, расположенными внутри такого DOM, необходимо использовать специальные методы для получения доступа к внутреннему дереву компонента.

Основная проблема при тестировании с использованием Shadow DOM заключается в том, что мы не можем просто получить доступ к элементу через стандартный селектор. Нужно сначала получить сам элемент, который содержит Shadow DOM, а затем найти нужный элемент внутри этого инкапсулированного контекста.

Поиск элементов внутри Shadow DOM

Для работы с элементами внутри Shadow DOM можно использовать метод shadowRoot. Этот метод позволяет получить доступ к корню Shadow DOM компонента и затем выполнять поиск внутри него.

Пример кода, который демонстрирует, как найти элемент внутри Shadow DOM:

const shadowHost = await $('#shadow-host'); // Находим элемент, который содержит Shadow DOM
const shadowRoot = await shadowHost.shadowRoot; // Получаем доступ к Shadow DOM
const shadowElement = await shadowRoot.$('.element-inside-shadow'); // Ищем элемент внутри Shadow DOM

В данном примере:

  • Сначала мы находим элемент, который является “хозяином” Shadow DOM (например, это может быть веб-компонент).
  • Затем мы получаем доступ к его shadowRoot, который является корнем этого компонента.
  • После этого, работая с полученным shadowRoot, мы можем использовать стандартные методы поиска, такие как $, чтобы находить элементы внутри Shadow DOM.

Важные моменты при работе с Shadow DOM в WebdriverIO

  1. Множественные Shadow DOM: В случае, если элемент содержит несколько уровней Shadow DOM, необходимо выполнить цепочку вызовов shadowRoot. Для этого нужно будет несколько раз обратиться к shadowRoot, чтобы перейти на нужный уровень.

Пример с вложенным Shadow DOM:

const firstShadowHost = await $('#first-shadow-host');
const firstShadowRoot = await firstShadowHost.shadowRoot;
const secondShadowHost = await firstShadowRoot.$('#second-shadow-host');
const secondShadowRoot = await secondShadowHost.shadowRoot;
const elementInsideSecondShadow = await secondShadowRoot.$('.final-element');
  1. Сложности с динамическим контентом: Важно учитывать, что элементы внутри Shadow DOM могут быть динамическими и изменяться после загрузки страницы. В таком случае стоит использовать ожидания, чтобы удостовериться, что элементы в нужном состоянии для взаимодействия.

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

const shadowHost = await $('#shadow-host');
const shadowRoot = await shadowHost.shadowRoot;
await shadowRoot.$('.element-inside-shadow').waitForDisplayed({ timeout: 5000 });
  1. Тестирование и взаимодействие с элементами: После того как элемент найден внутри Shadow DOM, взаимодействие с ним не отличается от работы с обычными элементами на странице. Можно использовать методы click(), setValue(), getText() и другие.
const shadowHost = await $('#shadow-host');
const shadowRoot = await shadowHost.shadowRoot;
const shadowButton = await shadowRoot.$('button');
await shadowButton.click();

Использование кастомных селекторов для Shadow DOM

WebdriverIO позволяет расширять возможности поиска с помощью пользовательских селекторов. Это может быть полезно, если требуется часто работать с элементами внутри Shadow DOM и хочется упростить код.

Пример кастомного селектора для поиска внутри Shadow DOM:

browser.addCommand('shadow', function(selector) {
  return this.$(selector).shadowRoot;
});

// Применение кастомного селектора
const shadowRoot = await browser.shadow('#shadow-host');
const shadowElement = await shadowRoot.$('.element-inside-shadow');

Таким образом, с помощью кастомных команд можно упростить процесс работы с элементами внутри Shadow DOM, улучшая читаемость и поддержку тестов.

Работа с элементами, находящимися за пределами Shadow DOM

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

Пример:

const shadowHost = await $('#shadow-host');
const shadowRoot = await shadowHost.shadowRoot;
const outsideElement = await $('#outside-element'); // Элемент за пределами Shadow DOM
await shadowRoot.$('.element-inside-shadow').click();
await outsideElement.click(); // Взаимодействие с элементом за пределами Shadow DOM

Заключение

Работа с Shadow DOM в WebdriverIO требует особого подхода и использования специфических методов для доступа к элементам внутри инкапсулированных деревьев DOM. Основной задачей является получение доступа к shadowRoot и дальнейшее взаимодействие с элементами внутри этого контекста. Важно учитывать возможные проблемы с динамическим контентом и многократным вложением Shadow DOM. Умение работать с этими особенностями значительно улучшает возможности тестирования современных веб-приложений, использующих веб-компоненты и Shadow DOM.