webentwicklung-frage-antwort-db.com.de

Wie löse ich ng-change im Direktive-Test in AngularJS aus?

Ich habe die folgende AngularJS-Direktive, die ein input-Element erstellt. Die Eingabe hat das Attribut ng-change, das die Funktion doIt() ausführt. Im Unit-Test meiner Direktive möchte ich prüfen, ob die Funktion doIt aufgerufen wird, wenn Benutzer die Eingabe ändern. Aber der Test ist nicht bestanden. Obwohl es beim manuellen Testen im Browser funktioniert.

Richtlinie:

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

Prüfung:

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

Live-Demo (ng-change): http://plnkr.co/edit/0yaUP6IQk7EIneRmphbW?p=preview


Nun ist der Test erfolgreich, wenn ich das change -Ereignis manuell binde, anstatt das ng-change-Attribut zu verwenden.

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

Live-Demo (manuelle Bindung): http://plnkr.co/edit/dizuRaTFn4Ay1t41jL1K?p=preview


Gibt es eine Möglichkeit, ng-change zu verwenden und es testbar zu machen? Vielen Dank.

19
Evgenii

Aus Ihrem erläuternden Kommentar:

Alles, was ich im Direktive test tun möchte, ist zu prüfen, ob doIt aufgerufen wird, wenn der Benutzer die Eingabe ändert.

Ob der durch ng-change angegebene Ausdruck richtig ausgewertet wird oder nicht, liegt in der Verantwortung der ngModel-Direktive. Daher bin ich mir nicht sicher, ob ich ihn auf diese Weise testen würde. Ich würde stattdessen vertrauen, dass die Direktiven ngModel und ngChange korrekt implementiert und getestet wurden, um die angegebene Funktion aufzurufen. Testen Sie einfach, dass der Aufruf der Funktion selbst die Direktive in der richtigen Weise beeinflusst. Ein End-to-End- oder Integrationstest könnte verwendet werden, um das Szenario mit vollständiger Nutzung zu handhaben.

Sie können sich jedoch die ngModelController-Instanz holen, die den ngModel change-Callback steuert, und den View-Wert selbst festlegen:

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

Wie gesagt, ich habe das Gefühl, dass dies zu weit in die Verantwortung von ngModel hineinreicht und das Blackboxing, das Sie mit natürlich zusammenstellbaren Anweisungen erhalten, bricht.

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


[Update]

Nachdem ich mich in der AngularJS-Quelle umgesehen hatte, stellte ich fest, dass auch das Folgende funktioniert:

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

Es sieht so aus, als ob das Ereignis in einigen Browsern anders ist. input scheint für Chrome zu funktionieren.

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

Hier ist der relevante AngularJS-Code , der den $sniffer-Dienst verwendet, um herauszufinden, welches Ereignis ausgelöst werden soll:

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

Trotzdem bin ich nicht sicher, ob ich eine Richtlinie auf diese Weise testen würde.

31
Michelle Tilley

einfach und es funktioniertin Ihrer Gerätetestumgebung:

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

Ich habe gegoogelt "Winkelrichtungsauslöser ng-change" und diese StackOverflow-Frage war die, die ich am nützlichsten hatte. Daher werde ich antworten: "Wie löse ich ng-change in einer Direktive aus?" und ich weiß nicht, wie ich diese Informationen weitergeben soll.

Innerhalb der Link-Funktion in der Direktive wird dies die Ng-Change-Funktion in Ihrem Element auslösen:

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

element.trigger("change") und element.trigger("input") funktionierten nicht für mich und auch nichts, was ich online finden konnte.

Als Beispiel das Auslösen der ng-Änderung bei Unschärfe:

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

Es tut mir leid, dass dies die Frage von OP nicht direkt beantwortet. Ich werde mehr als glücklich sein, einen guten Rat zu nehmen, wo und wie diese Informationen weitergegeben werden sollen.

1
Mikal Madsen

Ich habe versucht, dies zum Laufen zu bringen, scheiterte aber bei jedem Versuch. Abschließend kam ich zu dem Schluss, dass meine ng-model-options mit einer debounce-Einstellung auf dem onUpdate das Problem waren. 

Wenn Sie eine Entprellung haben, stellen Sie sicher, dass Sie den $ timeout-Dienst verwenden. Im Winkelmodus wurde dieser Timeout-Dienst um eine Spüloperation erweitert, mit der alle nicht erfüllten Anforderungen/Aktionen behandelt werden.

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

Ich habe lange nach dieser einfachen Linie gesucht ... Um das hier zu retten.

Wie kann man einen Wert aus html-select mit Karma auswählen und so die ng-change-Funktion zum Laufen bringen?

HTML:

Controller oder Direktive JS:

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

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

Karma-Testfragment:

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

Konsole:

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