#angularjs #jasmine #angular-directive
#angularjs #жасмин #angular-директива #jasmine
Вопрос:
Я украшаю формы следующим образом:
angular.module('Validation').directive('form', function() {
return {
restrict: 'E',
link: function(scope, element) {
var inputs = element[0].querySelectorAll('[name]');
element.on('submit', function() {
for (var i = 0; i < inputs.length; i ) {
angular.element(inputs[i]).triggerHandler('blur');
}
});
}
};
});
Теперь я пытаюсь протестировать эту директиву:
describe('Directive: form', function() {
beforeEach(module('Validation'));
var $rootScope, $compile, scope, form, input, textarea;
function compileElement(elementHtml) {
scope = $rootScope.$new();
form = angular.element(elementHtml);
input = form.find('input');
textarea = form.find('textarea');
$compile(form)(scope);
scope.$digest();
}
beforeEach(inject(function(_$rootScope_, _$compile_) {
$rootScope = _$rootScope_;
$compile = _$compile_;
compileElement('<form><input type="text" name="email"><textarea name="message"></textarea></form>');
}));
it('should trigger "blur" on all inputs when submitted', function() {
spyOn(input, 'trigger');
form.triggerHandler('submit');
expect(input.trigger).toHaveBeenCalled(); // Expected spy trigger to have been called.
});
});
Но тест завершается неудачей.
Каков правильный угловой способ проверки этой директивы?
Комментарии:
1. Какое сообщение об ошибке вы получаете?
2. Вы тестируете в PhantomJS или другом безголовом браузере?
Ответ №1:
У вас есть некоторые проблемы:
1) input = form.find('input');
и angular.element(inputs[i]);
являются ли 2 разных объекта-оболочки одним и тем же базовым объектом DOM.
2) Вместо этого вы должны создать spy on triggerHandler
.
3) Вы работаете напрямую с DOM, который сложно протестировать.
Примером этого является: angular.element(inputs[i])
не вводится, поэтому нам трудно подделать его в наших модульных тестах.
Чтобы убедиться, что точка 1) возвращает тот же объект. Мы можем подделать angular.element
, чтобы вернуть предварительно подготовленное значение, которое является input = form.find('input');
//Jasmine 1.3: andCallFake
//Jasmine 2.0: and.callFake
angular.element = jasmine.createSpy("angular.element").and.callFake(function(){
return input; //return the same object created by form.find('input');
});
Примечание: поскольку form
это уже директива AngularJS, чтобы избежать конфликта с уже определенной директивой, вы должны создать другую директиву и применить ее к form
. Что-то вроде этого:
<form mycustomdirective></form>
Я не уверен, нужно ли это. Поскольку мы отслеживаем глобальную функцию (angular.element), которая может использоваться во многих местах, нам может потребоваться сохранить предыдущую функцию и восстановить ее в конце теста. Ваш полный исходный код выглядит следующим образом:
it('should trigger "blur" on all inputs when submitted', function() {
var angularElement = angular.element; //save previous function
angular.element = jasmine.createSpy("angular.element").and.callFake(function(){
return input;
});
spyOn(input, 'triggerHandler');
form.triggerHandler('submit');
angular.element = angularElement; //restore
expect(input.triggerHandler).toHaveBeenCalled(); // Expected spy trigger to have been called.
});
Ответ №2:
Вероятно, это как-то связано с вызовом события «отправить» во время теста.
Команда angular создала довольно забавный класс, чтобы помочь им в этом, похоже, он охватывает множество крайних случаев — см. https://github.com/angular/angular.js/blob/master/src/ngScenario/browserTrigger.js
Хотя этот помощник от ngScenario, я использую его в своих модульных тестах для преодоления проблем, вызывающих некоторые события в безголовых браузерах, таких как PhantomJS.
Мне пришлось использовать это для тестирования очень похожей директивы, которая выполняет действие при отправке формы см. Тест здесь https://github.com/jonsamwell/angular-auto-validate/blob/master/tests/config/ngSubmitDecorator.spec.js (см. строку 38).
Мне пришлось использовать это, поскольку я использую безголовый браузер для целей тестирования разработки. Похоже, что для запуска события в браузере этого типа элемент, который запускает событие, также должен быть присоединен к dom.
Кроме того, поскольку директива form уже есть в angular, вам следует либо украсить эту директиву, либо присвоить этой директиве новое имя. На самом деле я бы посоветовал вам украсить директиву ngSubmit вместо директивы form, поскольку это больше подходит для отправки формы. На самом деле у меня есть очень хороший пример этого, поскольку я сделал это в модуле проверки, который у меня есть с открытым исходным кодом. Это должно дать вам очень хорошее начало.
Источник директивы здесь
Директивные тесты здесь
Комментарии:
1. «Кроме того, поскольку директива form уже есть в angular, вам следует либо украсить эту директиву, либо присвоить этой директиве новое имя». Почему это так?
2. @ExpertSystem Я считаю, что это противоречило бы директиве angular form, и это выглядело бы менее похоже на код вуду, если бы вместо этого использовалась директива с четким значением, т.е. blur-contorls-on-submit=»» вместо того, чтобы это просто волшебным образом происходило в каждой форме. Приложение должно состоять из значимых частей, и только потому, что мы находимся на земле html, не означает, что мы должны следовать принципу единой ответственности с директивами (в любом случае, мои 10 центов!!!! 🙂 )
3. Я бы не стал конфликтовать. Может быть столько директив с одинаковым именем, сколько вы хотите (и все они применяются к элементу). Это действительно зависит от того, чего хочет пользователь. Если вы хотите, чтобы что-то применялось ко всем формам, тогда имеет смысл объявить другую
form
директиву вместо того, чтобы добавлять дополнительную директиву в каждую форму.4. Я не согласен с применением его к директиве form . Работая в командах, нельзя ожидать, что люди будут знать каждый бит кода, и если что-то странное начнет происходить с формой при отправке, это будет стоить им времени и усилий, чтобы выяснить причину, и может даже привести к взломам кода, чтобы заставить его прекратить это делать. Я думаю, что директивы должны быть короткими и четкими и определять, что они делают с элементом, чтобы избежать путаницы.
5. Здесь вы упускаете главное. Опять же: «Если вы хотите, чтобы что-то применялось ко всем формам, тогда имеет смысл объявить другую директиву формы» , это решение на уровне проекта. Если вам нужно что-то применить ко всем формам, то создание директивы с другим именем не будет автоматически применять ее ко всем формам. Вам все равно нужно сообщить всем остальным участникам.
Ответ №3:
Попробуйте подключиться к событию размытия:
it('should trigger "blur" on all inputs when submitted', function() {
var blurCalled = false;
input.on('blur', function() { blurCalled = true; });
form.triggerHandler('submit');
expect(blurCalled).toBe(true);
});