Protractor — это фреймворк для автоматизированного тестирования веб-приложений, построенных на Angular и AngularJS. Основная сила Protractor заключается в его глубокой интеграции с Angular, что позволяет взаимодействовать с элементами страницы через Angular модели и привязки (bindings), обходя сложные манипуляции с DOM.
Angular использует концепцию двусторонней привязки
данных. Каждое поле ввода может быть связано с моделью в
контроллере через ng-model. Protractor предоставляет
удобные методы для работы с такими моделями:
element(by.model('user.name')).sendKeys('Иван');
element(by.model('user.email')).getAttribute('value').then(function(value) {
console.log(value);
});
by.model('user.name') — локатор, который находит
элемент по модели Angular.sendKeys() — ввод текста в поле.getAttribute('value') — получение текущего значения
поля.Использование by.model позволяет не зависеть от
структуры DOM, что делает тесты более устойчивыми к изменениям
разметки.
Angular модели часто используются в формах. Protractor позволяет:
Пример проверки валидности формы:
var usernameInput = element(by.model('user.username'));
usernameInput.sendKeys('testUser');
var form = element(by.css('form[name="userForm"]'));
form.getAttribute('class').then(function(classes) {
if (classes.includes('ng-valid')) {
console.log('Форма валидна');
} else {
console.log('Форма невалидна');
}
});
Здесь важно понимать, что Angular автоматически добавляет CSS-классы
ng-valid, ng-invalid, ng-touched
к элементам и формам, что позволяет легко проверять состояние формы.
Angular позволяет отображать данные на странице через
{{expression}}. Protractor может работать с такими
binding-выражениями через локатор
by.binding.
Пример:
element(by.binding('user.name')).getText().then(function(text) {
console.log('Имя пользователя:', text);
});
by.binding('user.name') находит элемент, который
отображает значение user.name.getText() возвращает текст, отображаемый на
странице.Для повторяющихся элементов используется by.exactBinding
и by.binding, что позволяет точно выбрать нужный
binding.
Angular использует директиву ng-repeat для рендеринга
списков. Protractor предоставляет локатор by.repeater для
работы с такими коллекциями.
Пример работы с таблицей пользователей:
var users = element.all(by.repeater('user in users'));
users.count().then(function(count) {
console.log('Количество пользователей:', count);
});
users.get(0).element(by.binding('user.name')).getText().then(function(name) {
console.log('Первый пользователь:', name);
});
element.all(by.repeater('user in users')) возвращает
массив элементов.count(), get(index),
first(), last() позволяют удобно обращаться к
конкретным элементам списка.Можно комбинировать by.repeater с
row(index) и column(binding) для точного
извлечения данных из таблиц:
var firstUserEmail = element(by.repeater('user in users').row(0).column('user.email')).getText();
Protractor автоматически синхронизируется с Angular, но иногда необходимо использовать явные ожидания. Особенно важно для динамических моделей и асинхронных операций:
var usernameInput = element(by.model('user.username'));
var EC = protractor.ExpectedConditions;
browser.wait(EC.presenceOf(usernameInput), 5000);
usernameInput.sendKeys('testUser');
Использование ExpectedConditions позволяет безопасно
взаимодействовать с элементами, которые могут появляться с
задержкой.
var subscribeCheckbox = element(by.model('user.subscribe'));
subscribeCheckbox.isSelected().then(function(selected) {
if (!selected) subscribeCheckbox.click();
});
var countrySelect = element(by.model('user.country'));
countrySelect.element(by.cssContainingText('option', 'Россия')).click();
var ageInput = element(by.model('user.age'));
ageInput.clear();
ageInput.sendKeys('30');
clear() — необходим для удаления старого значения перед
вводом нового.
by.model, by.binding,
by.repeater) вместо чистого CSS или XPath.sleep(). Вместо этого
применять ExpectedConditions.ng-valid, ng-invalid).element.all() и
методами first(), last(),
get(index).Для сложных форм с вложенными объектами и массивами можно
комбинировать by.model и by.repeater. Пример:
редактирование адресов пользователя:
var addressRows = element.all(by.repeater('address in user.addresses'));
addressRows.get(0).element(by.model('address.city')).sendKeys('Москва');
addressRows.get(1).element(by.model('address.city')).sendKeys('Санкт-Петербург');
Такой подход позволяет работать с глубоко вложенными моделями без прямого обращения к DOM-структуре.