2014-11-26 14 views
7

Ich versuche, eine Direktive zu testen, die ich geschrieben habe, um eine Eingabe zu validieren, und ich habe einige Probleme. Die von der Direktive verwaltete Eingabe sollte einen gültigen hexadezimalen Farbwert enthalten. Wenn der Benutzer diesen Wert mit einem ungültigen Wert ändert, möchte ich diese Änderung abbrechen. Meine Richtlinie ist die folgende und wie erwartet funktioniert:Einheit Test Winkel Direktive Aktualisieren der ngModel

module.directive('colorValidate', function() { 
    return { 
     restrict: 'A', 
     scope: { 
      color: '=ngModel' 
     }, 
     link: function(scope, element) { 
      var previousValue = '#ffffff'; 
      //pattern that accept #ff0000 or #f00 
      var colorPattern = new RegExp('^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$'); 
      element.on('focus', function() { 
       previousValue = scope.color; 
      }); 
      element.on('blur', function() { 
       if (!colorPattern.test(scope.color)) { 
        scope.$apply(function() { 
         scope.color = previousValue; 
        }); 
       } 
       else { 
        scope.$apply(function() { 
         scope.color = scope.color.toLowerCase(); 
        }); 
       } 
      }); 
     } 
    }; 
}); 

und hier ist ein Beispiel für Eingabe mit dieser Direktive:

<input color-validate type="text" ng-model="color.color"/> 

Erste Frage: Ist diese Art und Weise den Zugriff auf und die Änderung der ngModel der Element korrekt?

Dann ist mein Hauptproblem der Testteil. Hier sind zwei einfache Tests, die ich geschrieben habe und die nicht wirklich arbeiten:

describe('colorValidate directive', function() { 
    var scope, 
     elem, 
     compiled, 
     html; 

    beforeEach(function() { 
     html = '<input color-validate type="text" ng-model="color.color"/>'; 

     inject(function($compile, $rootScope) { 
      scope = $rootScope.$new(); 
      scope.color = {color: '#aaaaaa'}; 
      elem = angular.element(html); 
      compiled = $compile(elem); 
      compiled(scope); 
      scope.$digest(); 
     }); 
    }); 

    it('should permit valid 6-chars color value', function() { 
     elem.triggerHandler('focus'); 
     elem.val('#FF0000'); 
     elem.triggerHandler('blur'); 
     scope.$digest(); 
     expect(elem.val()).toBe('#FF0000'); 
    }); 

    it('should reject non valid color values', function() { 
     elem.triggerHandler('focus'); 
     elem.val('#F00F'); 
     scope.$digest(); 
     elem.triggerHandler('blur'); 
     expect(elem.val()).toBe('#aaaaaa'); 
    }); 
}); 

Der erste Test erfolgreich zu sein und die gescheiterten zweiten, weil der getesteten Wert ist ‚# F00F‘ anstelle von ‚#aaaaaa‘. Im Grunde ändert keiner meiner Tests den ngModel-Wert, der von der Direktive gehandhabt wird ...

Antwort

7

Der Aufruf von elem.val() bewirkt nicht, dass die scope.color aktualisiert wird. Mit anderen Worten, wird dieser Test nicht bestanden:

it("should set scope", function() { 
    elem.triggerHandler("focus"); 
    elem.val("#FF0000"); 
    scope.$digest(); 
    elem.triggerHandler("blur"); 

    //Will fail: expected #aaaaaa to be #ff0000 
    expect(scope.color.color).toBe("#ff0000"); 
}); 

Dies liegt daran, ngModel auf wichtige Ereignisse am Eingang bindet und aktualisiert das Modell (scope) an diesem Punkt. Das Aufrufen von val() oder value für das Element löst kein Ereignis aus, bei dem angular etwas als geändert betrachtet (selbst in einer $ digest-Schleife). Daher sollten Sie Ihre Tests ausgeführt werden, indem die Modellwerte ändern und behaupten sie akzeptiert werden oder zurückgesetzt:

it('should permit valid 6-chars color value', function() { 
    elem.triggerHandler('focus'); 
    scope.color.color = '#FF0000'; 
    //need to trigger a digest here for the two-way binding 
    scope.$digest(); 
    elem.triggerHandler('blur'); 
    //Don't need a $digest here because you call scope.$apply() within the blur in both if/else conditions 
    //scope.$digest(); 
    expect(scope.color.color).toBe('#ff0000'); 
}); 

it('should reject non valid color values', function() { 
    elem.triggerHandler('focus'); 
    scope.color.color = '#F00F'; 
    //need to trigger a digest here for the two-way binding 
    scope.$digest(); 
    elem.triggerHandler('blur'); 
    expect(scope.color.color).toBe('#aaaaaa'); 
}); 

Sie nicht testen müssen, dass der Wert aktualisiert, da vermutlich Winkel würde bereits die Tests geschrieben um sicherzustellen, dass die Ansicht aktualisiert wird, wenn der Wert des Bereichs innerhalb der Direktive geändert wird, wenn die Direktive über bidirektionale Bindung (= ngModel) verfügt.

+0

Danke Mann, das hilft sehr! Eigentlich war die Position des scope. $ Digest auch ein Problem, da ich nach der Unschärfezeile anrief. Außerdem hatte ich meine Frage aktualisiert, weil meine Verwendung von elem.blur() und focus() falsch war und elem.triggerHandler ('focus') sein sollte; – ValentinH

+0

Aktualisierte Tests in Antwort. Kommentare hinzugefügt ... Sie müssen noch ändern und erwarten Sie auf den Umfang Werte in den Unit-Tests. Beide obigen Tests bestehen mit Ihren Änderungen mit TriggerHandler. – Patrick

+0

In der Tat, sie gehen beide. Danke noch einmal. – ValentinH

Verwandte Themen