web-dev-qa-db-fra.com

Comment déclencher un changement de directive dans le test de directive dans AngularJS

J'ai la directive AngularJS suivante qui crée un élément input. L'entrée a l'attribut ng-change qui exécute la fonction doIt(). Dans le test unitaire de ma directive, je souhaite vérifier si la fonction doIt est appelée lorsque les utilisateurs modifient l'entrée. Mais le test ne passe pas. Bien que cela fonctionne dans le navigateur lorsque vous testez manuellement.

Directif:

...
template: "<input ng-model='myModel' ng-change='doIt()' type='text'>" 

Tester:

el.find('input').trigger('change') // Dos not trigger ng-change

Démo en direct (ng-change): http://plnkr.co/edit/0yaUP6IQk7EIneRmphbW?p=preview


Maintenant, le test réussit si je lie manuellement l'événement change au lieu d'utiliser l'attribut ng-change.

template: "<input ng-model='myModel' type='text'>",
link: function(scope, element, attrs) {
  element.bind('change', function(event) {
    scope.doIt();
  });
}

Démo en direct (liaison manuelle): http://plnkr.co/edit/dizuRaTFn4Ay1t41jL1K?p=preview


Existe-t-il un moyen d'utiliser ng-change et de le rendre testable? Je vous remercie.

19
Evgenii

De votre commentaire explicatif:

Tout ce que je veux faire dans le test de la directive est de vérifier que doIt est appelé lorsque l'utilisateur modifie l'entrée.

Que l'expression indiquée par ng-change soit correctement évaluée ou non relève vraiment de la directive ngModel, je ne suis donc pas sûr de le tester de cette manière; au lieu de cela, je pense que les directives ngModel et ngChange ont été correctement implémentées et testées pour appeler la fonction spécifiée, et que l’appel de la fonction elle-même affecte la directive de manière correcte. Un test d'intégration ou de bout en bout peut être utilisé pour gérer le scénario d'utilisation complète.

Cela dit, vous pouvez obtenir l'instance ngModelController qui pilote le rappel ngModel change et définir vous-même la valeur d'affichage:

it('trigger doIt', function() {
  var ngModelController = el.find('input').controller('ngModel');
  ngModelController.$setViewValue('test');
  expect($scope.youDidIt).toBe(true);
});

Comme je l’ai dit, cependant, j’ai l’impression que cela va trop loin dans les responsabilités de ngModel, rompant avec les directives naturellement composables.

Exemple: http://plnkr.co/edit/BaWpxLuMh3HvivPUbrsd?p=preview


[Mettre à jour]

Après avoir jeté un coup d’œil à la source AngularJS, j’ai trouvé que ce qui suit fonctionnait également:

it('trigger doIt', function() {
  el.find('input').trigger('input');
  expect($scope.youDidIt).toBe(true);
});

Il semble que l'événement est différent dans certains navigateurs. input semble fonctionner pour Chrome.

Exemple: http://plnkr.co/edit/rbZ5OnBtKMzdpmPkmn2B?p=preview

Voici le code AngularJS pertinent }, qui utilise le service $sniffer pour déterminer quel événement déclencher:

changeInputValueTo = function(value) {
  inputElm.val(value);
  browserTrigger(inputElm, $sniffer.hasEvent('input') ? 'input' : 'change');
};

Même avec cela, je ne suis pas sûr que je testerais une directive de cette façon.

31
Michelle Tilley

simple et ça marcheen votre test unitaire 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();
1
miukki

J'ai googlé "directive angulaire déclenchant-changement" et cette question de StackOverflow était le plus proche de tout ce qui m'était utile, je vais donc répondre "Comment déclencher un changement dans une directive", puisque d'autres vont atterrir sur cette page, et je ne sais pas comment fournir cette information.

Dans la fonction link de la directive, cela déclenchera la fonction ng-change sur votre élément:

element.controller('ngModel').$viewChangeListeners[0]();

element.trigger("change") et element.trigger("input") n'ont pas fonctionné pour moi, ni rien d'autre que j'ai pu trouver en ligne.

Par exemple, déclencher le changement de couleur sur le flou:

wpModule.directive('triggerChangeOnBlur', function () {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            element.on('blur', function () {
                element.controller('ngModel').$viewChangeListeners[0]();
            });
        }
    };
}]);

Je suis désolé que cela ne réponde pas directement à la question de OP. Je serai plus qu'heureux de prendre des conseils judicieux sur où et comment partager ces informations.

1
Mikal Madsen

J'ai essayé de faire en sorte que cela fonctionne, mais a échoué à chaque tentative. Finalement, j'ai conclu que le problème était mes options de modèle ng avec un paramètre anti-rebond sur onUpdate. 

Si vous avez un anti-rebond, assurez-vous de choisir le service $ timeout. Dans angular mock, ce service de délai d'attente a été étendu avec une opération de vidage, qui gère toutes les requêtes/actions non satisfaites.

    var tobetriggered = angular.element(element[0].querySelector('.js-triggervalue'));
    tobetriggered.val('value');
    tobetriggered.trigger('change');
    $timeout.flush();
0
EvtK

Je cherchais cette simple ligne pendant de longues heures ... juste pour la sauvegarder ici.

Comment sélectionner une valeur parmi html-select, en utilisant Karma, et obtenir ainsi que la fonction ng-change fonctionne?

HTML:

Contrôleur ou directive JS:

  $scope.itemTypes = [{name: 'Some name 1', value: 'value_1'}, {name: 'Some name 2', value: 'value_2'}]

  $scope.itemTypeSelected = function () {
    console.log("Yesssa !!!!");
  };

Fragment de test de karma:

  angular.element(element.find("#selectedItemType")[0]).val('value_1').change();
  console.log("selected model.selectedItemType", element.isolateScope().model.selectedItemType);

Console:

'Yesssa !!!!'
'selected model.selectedItemType', 'value_1'
0
Dmitri Algazin