2013-07-13 8 views
19

auslösen Ich habe die folgende AngularJS Direktive, die ein input Element erstellt. Eingabe hat ng-change Attribut, das doIt() Funktion ausgeführt wird. Im Einheitentest meiner Anweisung möchte ich prüfen, ob die Funktion doIt aufgerufen wird, wenn Benutzer die Eingabe ändert. Aber der Test besteht nicht. Obwohl es im Browser beim manuellen Testen funktioniert.Wie man ng-change in Directional Test in AngularJS

Richtlinie:

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

Test:

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

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


Nun hat der Test bestanden, wenn ich manuell change Ereignis statt binden Verwendung von ng-change Attribut.

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 nutzen und prüfbar machen? Vielen Dank.

+1

ng-change wird bei Modelländerungen ausgelöst, nicht bei Ereignissen. Warum nicht einfach den Controller-Code für Ihr "DoIt" Fn testen? – marko

+0

@marko, danke für die gute Frage. Alles, was ich im Direktiven-Test tun will, ist zu überprüfen, dass doIt aufgerufen wird, wenn Benutzer die Eingabe ändert. doIt selbst wird im Direktiven-Test verspottet, weil es bereits im Controller-Test getestet wurde, wie Sie vorgeschlagen haben. – Evgenii

Antwort

30

Von Ihrem erläuternden Kommentar:

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

Ob die von ng-change angegebenen Ausdruck korrekt ausgewertet wird oder nicht, ist wirklich in der Verantwortung der ngModel Richtlinie, also bin ich nicht sicher, ob ich es auf diese Weise testen würde; stattdessen würde ich darauf vertrauen, dass die ngModel und ngChange Direktiven korrekt implementiert und getestet wurden, um die angegebene Funktion aufzurufen, und nur testen, 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 vollständige Szenario zu behandeln.

Das heißt, Sie halten die ngModelController Instanz bekommen, die die ngModel Änderung Rückruf-Laufwerke und stellen Sie die Ansicht Wert selbst:

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

Wie gesagt, aber ich fühle mich wie dies auch erreicht weit in ngModel 's Verantwortung, bricht das Black-Boxing Sie mit natürlich zusammensetzbaren Direktiven.

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


[Update]

Nach etwas an der AngularJS Quelle, fand ich, dass auch Folgendes funktioniert:

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

Es ist wie das aussieht Ereignis ist in einigen Browsern anders; input scheint für Chrome zu funktionieren.

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

Hier ist die relevant AngularJS code, die den $sniffer Dienst verwendet, welches Ereignis, um herauszufinden, auszulösen:

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

Auch dies ist, ich bin nicht sicher, ob ich einen Test würde Richtlinie auf diese Weise.

+4

In Bezug auf die Ereignisse, denke ich, "Eingabe" ist für Texteingaben und dergleichen und "ändern" ist für Auswahlfelder und Optionsfelder. Zumindest funktioniert das für mich bei Tests mit PhantomJS. –

+1

Es scheint, dass browserTrigger nur in angular-scenario.js vorhanden ist. Dies bedeutet, dass es nur für e2e Test gedacht ist, oder? Ich bin nicht in der Lage, diese Funktion zu einem Komponententest zu verwenden, was meiner Meinung nach absichtlich ist. – unludo

+0

@unludo Vielleicht; Mein Punkt war nur, dass die AngularJS-Quelle geschrieben wurde, um in bestimmten Browsern ein anderes Ereignis auszulösen. –

0

Ich suchte diese einfache Linie für lange Stunden. Nur um das hier zu speichern.

Wie wählt man den Wert von HTML-Select, mit Karma, und damit ng-Change-Funktion funktioniert?

HTML:

-Controller oder Richtlinie 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); 

Console:

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

Ich googelte "angular directory trigger ng-change" und diese StackOverflow-Frage kam mir am nächsten und ich beantworte "Wie man ng-change in einer Direktive auslöst", da andere auf dieser Seite landen werden und ich weiß nicht, wie ich diese Informationen sonst noch bereitstellen soll.

Innerhalb der Link-Funktion über die Richtlinie, dies wird die ng-Änderungsfunktion auf Ihrem Elemente auslösen:

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

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

Als Beispiel die ng-Änderung auf Blur Auslösung:

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 nicht direkt OP Frage beantwortet. Ich werde mehr als glücklich sein, einige fundierte Ratschläge zu geben, wo und wie diese Informationen weitergegeben werden.

+1

Im Idealfall würden Sie eine separate Frage stellen und dann selbst beantworten (klingt komisch, aber es wird empfohlen). Ich bin auch auf der Suche nach etwas, das nicht ganz das OP ist, oder Ihr Problem, und würde die Antwort schneller finden, wenn es eine Frage speziell dazu geben würde. –

0

einfach und es funktioniert in Ihrem 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(); 
0

haben versucht, diese Arbeit zu bekommen, aber bei jedem Versuch schlug fehl. Abschließend stellte sich heraus, dass mein ng-Modell-Optionen mit einer Debounce-Einstellung auf dem onUpdate, das Problem war.

Wenn Sie eine Entprellung haben, vergewissern Sie sich, dass Sie den $ Timeout-Dienst leeren. Im eckigen Schein wurde dieser Timeout-Dienst um eine Flush-Operation erweitert, die alle unerfüllten Anforderungen/Aktionen behandelt.

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