Работа с веб-компонентами

Protractor — инструмент для энд-ту-энд тестирования Angular-приложений, однако он поддерживает и не-Angular сайты через browser.waitForAngularEnabled(false). Для работы с современными веб-компонентами важно убедиться, что окружение правильно настроено: Node.js, npm и WebDriver Manager.

Настройка проекта:

npm install -g protractor
webdriver-manager update

Создание конфигурационного файла protractor.conf.js:

exports.config = {
  framework: 'jasmine',
  seleniumAddress: 'http://localhost:4444/wd/hub',
  specs: ['specs/*.spec.js'],
  capabilities: {
    browserName: 'chrome'
  },
  onPrepare: () => {
    browser.waitForAngularEnabled(false);
  }
};

Флаг waitForAngularEnabled(false) отключает ожидание Angular, что необходимо для тестирования веб-компонентов на чистом JavaScript.


Локаторы для веб-компонентов

Веб-компоненты создаются через Custom Elements (<my-component>). Для работы с ними Protractor предоставляет стандартные локаторы, однако важно учитывать теневое дерево (Shadow DOM).

Примеры локаторов:

  • По тегу: element(by.tagName('my-component'))
  • По атрибуту: element(by.css('[data-test="submit-button"]'))
  • По тексту: element(by.xpath('//my-component[text()="Click me"]'))

Для элементов внутри Shadow DOM необходимо использовать JavaScript-выражения, так как стандартные локаторы не проникают в теневой корень:

const shadowRoot = element(by.tagName('my-component')).getWebElement()
  .then(el => el.shadowRoot);

const buttonInShadow = shadowRoot.then(root => root.querySelector('button'));

Методы взаимодействия

Protractor наследует все методы WebDriver для кликов, ввода текста и ожиданий. Основные подходы при работе с веб-компонентами:

  • Клик по элементу:
await element(by.css('my-component button')).click();
  • Ввод текста:
await element(by.css('my-component input')).sendKeys('Тестовый текст');
  • Получение текста:
const text = await element(by.css('my-component .label')).getText();

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

const EC = protractor.ExpectedConditions;
const myButton = element(by.css('my-component button'));
await browser.wait(EC.elementToBeClickable(myButton), 5000);
await myButton.click();

Работа с асинхронными операциями и Shadow DOM

В веб-компонентах часто используются асинхронные обновления. Для корректного взаимодействия рекомендуется комбинировать await с ExpectedConditions:

const input = element(by.css('my-component input'));
await browser.wait(EC.visibilityOf(input), 5000);
await input.sendKeys('Тест');

Для элементов внутри Shadow DOM:

const shadowHost = element(by.tagName('my-component')).getWebElement();
const shadowRoot = await browser.executeScript('return arguments[0].shadowRoot', shadowHost);
const shadowButton = await shadowRoot.findElement(By.css('button'));
await shadowButton.click();

executeScript позволяет выполнять произвольный JavaScript, что открывает доступ к теневым элементам.


Организация тестов

Protractor поддерживает Jasmine и Mocha. Для больших веб-компонентов лучше использовать Page Object Model (POM).

Пример POM для веб-компонента:

class MyComponent {
  constructor() {
    this.shadowHost = element(by.tagName('my-component'));
  }

  async getShadowRoot() {
    return await browser.executeScript('return arguments[0].shadowRoot', await this.shadowHost.getWebElement());
  }

  async clickButton() {
    const shadowRoot = await this.getShadowRoot();
    const button = await shadowRoot.findElement(By.css('button'));
    await button.click();
  }

  async enterText(text) {
    const shadowRoot = await this.getShadowRoot();
    const input = await shadowRoot.findElement(By.css('input'));
    await input.sendKeys(text);
  }

  async getLabelText() {
    const shadowRoot = await this.getShadowRoot();
    const label = await shadowRoot.findElement(By.css('.label'));
    return await label.getText();
  }
}

module.exports = MyComponent;

Использование POM позволяет абстрагировать детали Shadow DOM и упрощает поддержку тестов.


Отладка и логирование

При работе с веб-компонентами часто возникают ошибки, связанные с недоступностью элементов. Для отладки полезно:

  • Скриншоты:
await browser.takeScreenshot().then((png) => {
  const fs = require('fs');
  fs.writeFileSync('screenshot.png', png, { encoding: 'base64' });
});
  • Логи браузера:
const logs = await browser.manage().logs().get('browser');
console.log(logs);
  • Увеличение таймаутов:
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;

Эти инструменты помогают выявить проблемы с асинхронными компонентами и Shadow DOM.


Интеграция с CI/CD

Для автоматизации тестов веб-компонентов Protractor можно интегрировать с Jenkins, GitHub Actions или GitLab CI:

jobs:
  e2e:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Install Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 20
      - run: npm install
      - run: webdriver-manager update
      - run: protractor protractor.conf.js

Тесты на веб-компонентах требуют стабильного браузера и корректной настройки Shadow DOM. Параллельный запуск тестов можно настроить через multiCapabilities в конфигурации Protractor.


Работа с веб-компонентами в Protractor требует внимания к Shadow DOM, асинхронности и правильному использованию локаторов. Комбинация Page Object Model, executeScript и ExpectedConditions позволяет создавать надежные и поддерживаемые тесты, пригодные для сложных фронтенд-приложений.