Тестирование прямых Angular компонентов

Protractor — это инструмент для end-to-end тестирования веб-приложений на базе Angular и AngularJS. Он построен поверх WebDriverJS, что позволяет управлять браузером и выполнять асинхронные операции. Основной особенностью Protractor является глубокая интеграция с Angular, благодаря чему синхронизация с приложением осуществляется автоматически: тест ожидает завершения всех асинхронных операций, таких как HTTP-запросы и таймеры, прежде чем выполнять следующие действия.

Protractor использует promise-based архитектуру, что позволяет безопасно работать с асинхронными операциями. В современных версиях рекомендуется использование async/await для упрощения чтения и поддержки кода.

Настройка окружения

Для работы с Protractor необходимо установить несколько зависимостей:

npm install --save-dev protractor
npm install --save-dev jasmine

Protractor требует файл конфигурации (protractor.conf.js), который задаёт:

  • seleniumAddress — адрес Selenium Server, если он используется отдельно;
  • specs — путь к файлам тестов;
  • capabilities — настройки браузера (Chrome, Firefox и др.);
  • framework — используемый тестовый фреймворк (Jasmine, Mocha, Cucumber);
  • onPrepare — функции, выполняемые перед стартом тестов, например, настройка browser.waitForAngularEnabled.

Пример минимальной конфигурации:

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

Работа с элементами Angular

Для работы с элементами Angular Protractor использует локаторы, расширяющие стандартные селекторы WebDriver:

  • by.model('modelName') — связывает элемент с Angular-моделью;
  • by.binding('bindingName') — находит элемент по привязке данных;
  • by.repeater('repeaterExpression') — используется для коллекций, созданных с помощью ng-repeat;
  • by.css() и by.id() — стандартные CSS- и ID-селекторы.

Пример поиска элемента по модели:

const usernameInput = element(by.model('user.name'));
await usernameInput.sendKeys('testUser');

Асинхронные операции и ожидания

Angular-приложения активно используют асинхронные операции. Protractor автоматически ждёт завершения этих операций, однако иногда требуется явное ожидание:

  • Explicit Waits: browser.wait с функцией ожидания, возвращающей true при готовности элемента;
  • ExpectedConditions (EC): набор готовых условий для ожидания, например, видимость элемента или его кликабельность.

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

const EC = protractor.ExpectedConditions;
const submitButton = element(by.id('submit'));

await browser.wait(EC.elementToBeClickable(submitButton), 5000);
await submitButton.click();

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

Protractor совместим с Jasmine, поэтому тесты обычно структурируются с помощью блоков describe и it:

describe('Форма входа', () => {
    it('должна вводить логин и пароль', async () => {
        await browser.get('http://localhost:4200/login');
        await element(by.model('user.name')).sendKeys('admin');
        await element(by.model('user.password')).sendKeys('12345');
        await element(by.id('loginButton')).click();

        const successMessage = element(by.binding('message'));
        expect(await successMessage.getText()).toBe('Добро пожаловать!');
    });
});

Блок beforeEach часто используется для настройки состояния перед каждым тестом, а afterEach — для очистки.

Взаимодействие с компонентами Angular

Angular-компоненты часто имеют сложную структуру, состоящую из дочерних компонентов и шаблонов. Protractor позволяет:

  • Работать с вложенными компонентами через локаторы CSS и Angular;
  • Проверять состояние компонентов через их свойства и методы;
  • Взаимодействовать с формами, переключателями, чекбоксами, кнопками и выпадающими списками.

Пример проверки состояния компонента:

const todoList = element.all(by.repeater('todo INTOdos'));
expect(await todoList.count()).toBe(3);

const firstTodoText = await todoList.first().element(by.binding('todo.text')).getText();
expect(firstTodoText).toBe('Купить хлеб');

Работа с навигацией и маршрутами

Protractor позволяет тестировать маршрутизацию Angular-приложений. Метод browser.get() открывает страницу, а browser.waitForAngular() обеспечивает синхронизацию. Для проверки маршрутов можно использовать Angular service location или считывать browser.getCurrentUrl():

await browser.get('/dashboard');
expect(await browser.getCurrentUrl()).toContain('/dashboard');

Использование Page Object Model

Для упрощения и масштабирования тестов применяется паттерн Page Object Model (POM). В POM создаются отдельные классы для страниц и компонентов, инкапсулирующие локаторы и действия:

class LoginPage {
    constructor() {
        this.username = element(by.model('user.name'));
        this.password = element(by.model('user.password'));
        this.loginButton = element(by.id('loginButton'));
    }

    async login(user, pass) {
        await this.username.sendKeys(user);
        await this.password.sendKeys(pass);
        await this.loginButton.click();
    }
}

module.exports = new LoginPage();

Тест с использованием POM становится более чистым:

const loginPage = require('../pages/login.page');

describe('Авторизация', () => {
    it('должна успешно входить', async () => {
        await browser.get('/login');
        await loginPage.login('admin', '12345');
        expect(await element(by.binding('message')).getText()).toBe('Добро пожаловать!');
    });
});

Логирование, скриншоты и отладка

Для отладки и анализа тестов Protractor поддерживает:

  • Скриншоты через browser.takeScreenshot();
  • Логи браузера через browser.manage().logs().get('browser');
  • Настройку уровня логирования WebDriver через конфигурацию;
  • Удалённое подключение к DevTools для инспекции.

Пример снятия скриншота при ошибке:

afterEach(async () => {
    const png = await browser.takeScreenshot();
    require('fs').writeFileSync('screenshot.png', png, {encoding: 'base64'});
});

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

Protractor легко интегрируется с системами CI/CD. В pipeline можно:

  • Устанавливать зависимости через npm install;
  • Запускать Selenium Server или использовать WebDriverManager;
  • Параллельно запускать тесты в нескольких браузерах через multiCapabilities;
  • Собирать отчёты с помощью Jasmine reporters или Allure.

Особенности работы с современным Angular

Несмотря на официальное прекращение поддержки Protractor, в существующих проектах важно учитывать:

  • Необходимо явно отключать ожидания Angular для страниц с не-Angular элементами: browser.waitForAngularEnabled(false);
  • Использование async/await упрощает работу с promise и предотвращает ошибки синхронизации;
  • Для сложных компонентов рекомендуется комбинировать Protractor с прямым доступом к Angular сервисам через browser.executeScript.

Эти практики обеспечивают стабильное выполнение end-to-end тестов даже в крупных приложениях с динамическим контентом и многочисленными асинхронными процессами.