В тестировании Angular-приложений использование
Protractor предоставляет уникальные возможности для
взаимодействия с внутренним состоянием компонентов через
scope. Понимание того, как получить доступ к Angular
scope, позволяет проверять данные модели напрямую, что
особенно полезно при тестировании динамических интерфейсов.
Scope в Angular — это объект, который связывает
модель и представление. Он хранит все переменные и функции, которые
доступны в шаблоне компонента через выражения Angular
({{variable}}) или директивы (ng-model,
ng-repeat и т.д.). Каждый контроллер и директива создают
свой собственный scope, который может наследоваться от
родительского.
В Protractor можно напрямую обращаться к этим объектам через браузер,
используя executeScript.
Для доступа к scope элемента используется метод
browser.executeScript, который позволяет выполнить
произвольный JavaScript в контексте страницы.
Пример получения scope:
const scope = await browser.executeScript(function(el) {
return angular.element(el).scope();
}, element(by.css('.my-controller')).getWebElement());
Пояснения:
angular.element(el) — обертка Angular вокруг
DOM-элемента..scope() — возвращает scope, связанный с этим
элементом.element(by.css(...)).getWebElement() — стандартный
способ получения нативного DOM-элемента в Protractor.После выполнения этого кода переменная scope содержит
объект, через который можно читать и изменять свойства модели.
После получения scope доступны все переменные контроллера. Например, если в контроллере объявлено:
$scope.username = 'admin';
Можно прочитать значение через Protractor:
const username = await browser.executeScript(function(el) {
return angular.element(el).scope().username;
}, element(by.css('.my-controller')).getWebElement());
console.log(username); // 'admin'
Это позволяет проверять внутреннее состояние приложения без зависимости от DOM-элементов.
Изменение свойств scope через Protractor требует обновления Angular digest цикла для применения изменений в DOM:
await browser.executeScript(function(el) {
const scope = angular.element(el).scope();
scope.username = 'newUser';
scope.$apply();
}, element(by.css('.my-controller')).getWebElement());
scope.$apply() запускает digest цикл, чтобы изменения в
модели сразу отразились в представлении.$apply() изменения останутся только в объекте
JavaScript, а DOM не обновится.Некоторые директивы создают isolate scope, который изолирован от родительского:
const isolateScope = await browser.executeScript(function(el) {
return angular.element(el).isolateScope();
}, element(by.css('.my-directive')).getWebElement());
isolateScope() позволяет получать и изменять свойства,
специфичные для директивы.await с
browser.executeScript, так как Protractor полностью
асинхронен.angular.element(el).scope() перед
использованием, чтобы избежать ошибок на элементах, не связанными с
Angular.executeScript в переменную и работать с ней в последующих
проверках.$apply() влияет
только на эту директиву и ее дочерние scopes, но не на весь родительский
scope.const el = element(by.css('.user-form'));
await browser.executeScript(function(el) {
const scope = angular.element(el).scope();
scope.username = 'testUser';
scope.password = 'securePass';
scope.$apply();
}, el.getWebElement());
const formValues = await browser.executeScript(function(el) {
const scope = angular.element(el).scope();
return {
username: scope.username,
password: scope.password
};
}, el.getWebElement());
console.log(formValues); // { username: 'testUser', password: 'securePass' }
В этом примере одновременно изменяется несколько полей модели и проверяется их значение после digest цикла.
Доступ к Angular scope в Protractor открывает прямое взаимодействие с внутренней моделью приложения, что позволяет создавать более точные и быстрые тесты, особенно для сложных форм и динамического контента. Понимание различий между обычным scope и isolate scope помогает эффективно тестировать как контроллеры, так и кастомные директивы.