Подтвердить что ты не робот

Как вызвать 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 и сделать его пригодным для тестирования? Спасибо.

4b9b3361

Ответ 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();