Как вызвать ng-change в директивном тесте в AngularJS
У меня есть следующая директива AngularJS, которая создает элемент input
. Вход имеет атрибут ng-change
, который выполняет функцию doIt()
. В моей директиве unit test я хочу проверить, вызывается ли функция doIt
, когда пользователи меняют ввод. Но тест не проходит. Хотя он работает в браузере при проверке вручную.
Директива
...
template: "<input ng-model='myModel' ng-change='doIt()' type='text'>"
Тест:
el.find('input').trigger('change') // Dos not trigger ng-change
Live demo (ng-change): http://plnkr.co/edit/0yaUP6IQk7EIneRmphbW?p=preview
Теперь тест проходит, если я вручную привязываю событие change
вместо использования атрибута ng-change
.
template: "<input ng-model='myModel' type='text'>",
link: function(scope, element, attrs) {
element.bind('change', function(event) {
scope.doIt();
});
}
Живая демонстрация (ручная привязка): http://plnkr.co/edit/dizuRaTFn4Ay1t41jL1K?p=preview
Есть ли способ использовать ng-change
и сделать его пригодным для тестирования? Спасибо.
Ответы
Ответ 1
Из вашего объяснительного комментария:
Все, что я хочу сделать в директивном тесте, - проверить, что doIt
вызывается, когда пользователь меняет ввод.
Является ли выражение, указанное ng-change
правильно оцененным или нет, действительно отвечает директиве ngModel
, поэтому я не уверен, что я проверил бы это таким образом; вместо этого я был бы уверен, что директивы ngModel
и ngChange
были правильно реализованы и протестированы для вызова указанной функции и просто проверили, что вызов самой функции влияет на директиву правильным образом. Для обработки полнофункционального сценария можно использовать сквозной или интеграционный тест.
Таким образом, вы можете получить экземпляр ngModelController
, который управляет обратным вызовом изменения ngModel
и сам устанавливает значение представления:
it('trigger doIt', function() {
var ngModelController = el.find('input').controller('ngModel');
ngModelController.$setViewValue('test');
expect($scope.youDidIt).toBe(true);
});
Как я уже сказал, я чувствую, что это слишком далеко продвигает обязанности ngModel
, нарушая черный бокс, который вы получаете с естественно составленными директивами.
Пример: http://plnkr.co/edit/BaWpxLuMh3HvivPUbrsd?p=preview
[Обновление]
Посмотрев вокруг источника AngularJS, я обнаружил, что также работает следующее:
it('trigger doIt', function() {
el.find('input').trigger('input');
expect($scope.youDidIt).toBe(true);
});
Похоже, что в некоторых браузерах событие различно; input
, похоже, работает для Chrome.
Пример: http://plnkr.co/edit/rbZ5OnBtKMzdpmPkmn2B?p=preview
Вот соответствующий код AngularJS, который использует службу $sniffer
, чтобы выяснить, какое событие запускать:
changeInputValueTo = function(value) {
inputElm.val(value);
browserTrigger(inputElm, $sniffer.hasEvent('input') ? 'input' : 'change');
};
Даже имея это, я не уверен, что проверил бы директиву таким образом.
Ответ 2
Я искал эту простую линию в течение долгих часов.
Просто чтобы сохранить это здесь.
Как выбрать значение из html-select, используя Karma, и поэтому работать с функцией ng-change?
HTML:
Контроллер или директива JS:
$scope.itemTypes = [{name: 'Some name 1', value: 'value_1'}, {name: 'Some name 2', value: 'value_2'}]
$scope.itemTypeSelected = function () {
console.log("Yesssa !!!!");
};
Экспериментальный фрагмент кармы:
angular.element(element.find("#selectedItemType")[0]).val('value_1').change();
console.log("selected model.selectedItemType", element.isolateScope().model.selectedItemType);
Консоль:
'Yesssa !!!!'
'selected model.selectedItemType', 'value_1'
Ответ 3
Я googled "angular директива trigger ng-change", и этот вопрос StackOverflow был самым близким к чему-либо полезному, поэтому я отвечу "Как вызвать ng-change в директиве", поскольку другие привязаны к землю на этой странице, и я не знаю, как еще предоставить эту информацию.
Внутри функции ссылки в директиве это вызовет функцию ng-change для вашего элемента:
element.controller('ngModel').$viewChangeListeners[0]();
element.trigger("change")
и element.trigger("input")
не работали для меня, и я ничего не мог найти в Интернете.
В качестве примера, запуская ng-change при размытии:
wpModule.directive('triggerChangeOnBlur', function () {
return {
restrict: 'A',
link: function (scope, element, attrs) {
element.on('blur', function () {
element.controller('ngModel').$viewChangeListeners[0]();
});
}
};
}]);
Мне жаль, что это напрямую не отвечает на вопрос OP. Я буду более чем счастлив принять некоторые обоснованные советы о том, где и как поделиться этой информацией.
Ответ 4
просто и работает
в unit test env:
spyOn(self, 'updateTransactionPrice');
var el = compile('<form name="form" latest novalidate json-schema="main.schema_discount" json-schema-model="main._data"><input type="text" ng-model="main._data.reverse_discount" ng-class="{ \'form-invalid\': form.reverse_discount.$invalid }" ng-change="main.transactionPrice(form);" name="reverse_discount" class="form-control-basic" placeholder="" ng-disabled="!main.selectedProduct.total_price"></form>')(scope);
el.find('input').triggerHandler('change');
expect(self.updateTransactionPrice).toHaveBeenCalled();
Ответ 5
Были попытки заставить это работать, но не удалось при каждой попытке. Наконец пришел к выводу, что проблема с параметрами ng-model с настройкой debounce на onUpdate.
Если у вас есть debounce, убедитесь, что вы запустили службу $timeout. В angular mock эта служба тайм-аута была расширена с помощью операции очистки, которая обрабатывает все невыполненные запросы/действия.
var tobetriggered = angular.element(element[0].querySelector('.js-triggervalue'));
tobetriggered.val('value');
tobetriggered.trigger('change');
$timeout.flush();