2013-06-20 2 views
48

Ich habe versucht, das Format der ng-directive-testing Repo für eine Richtlinie, die ich geschrieben habe. Die Anweisung rendert grundsätzlich eine Überlagerung, wenn der Benutzer auf ein Element klickt. Hier ist die Richtlinie (vereinfacht):Wie kann ich ein Click-Ereignis in meinem AngularJS-Direktive-Test simulieren?

mod.directive('uiCopyLinkDialog', function(){ 
    return { 
     restrict: 'A', 
     link: function(scope, element, attrs) { 
      var $elm = angular.element(element); 
      element.bind('click', function(event) { 
       $elm.addClass('test'); 
      }); 
     } 
    }; 
}); 

Der Test Ich schreibe wie folgt aussieht:

describe('pre-compiled link', function() { 

    beforeEach(mocks.inject(function($compile, $rootScope) { 
     scope = $rootScope; 
     element = angular.element('<span class="foo" ui-copy-link-dialog="url"></span>'); 
     $compile(element)(scope); 
     scope.$digest(); 
    })); 

    it("should change the class when clicked", function() { 
     element.click(); // this returns "'undefined' is not a function" 
     element[0].click(); // so does this 
     $(elm).click(); // this uses jquery and doesn't *seem* to fail 
     waits(500); // hack to see if it was a race condition 
     expect(elm.className).toContain('test'); // always fails 
    }); 

}); 

Sie können in dem Test sehen, dass ich mehr Möglichkeiten versuchen, das click() Ereignis auf dem Link zu triggern , wobei die meisten einen undefined Fehler geben.

Kann mir jemand sagen, was ich hier falsch mache? Wenn ich die Beispiele lese, klingt das nach korrekter Syntax, aber mein Testläufer (Karma via Grunt) möchte nicht Ball spielen.

+0

Möglicherweise möchten Sie versuchen, eine ID zu geben, und wählen Sie auf der Id basiert: angular.element ('Spanne [id = "theSpan"] '). click(); Zusätzlich, da ich sehe, dass Sie den Dialog erwähnen ... ist Ihr Overlay ein jQuery-Dialog?wenn ja; Sie werden daran gehindert, ein Klick-Ereignis auf etwas außerhalb dieses Dialogs auszulösen, während das Dialogfeld geöffnet wird. Eine Lösung besteht darin, nach dem Schließen des Dialogfelds ein Ereignis auszulösen und anschließend ein Klickereignis auszuführen. –

+1

Danke: Es sieht so aus, als wäre das ein Fehler beim Ausführen dieser Tests in PhantomJS - wenn ich Tests in Chrome starte, werden die 'click()' Ereignisse korrekt ausgelöst. Seltsam! –

Antwort

18

Das war also ein Problem mit PhantomJS: Einige Ereignisse, die auf Elemente wirken, scheinen nicht zu brennen, wenn die Elemente nicht irgendwo auf einem Dokument sind, sondern nur im Speicher (das ist sowieso meine Theorie) . Um dies zu umgehen musste ich diese Funktion nutzen click Ereignisse auf Elemente auszulösen:

define(function() { 
    return { 
     click: function (el) { 
      var ev = document.createEvent("MouseEvent"); 
      ev.initMouseEvent(
       "click", 
       true /* bubble */, true /* cancelable */, 
       window, null, 
       0, 0, 0, 0, /* coordinates */ 
       false, false, false, false, /* modifier keys */ 
       0 /*left*/, null 
      ); 
      el.dispatchEvent(ev); 
     } 
    }; 
}); 

Das funktionierte, obwohl andere Dinge waren härter: Ich wollte auch einen Test schreiben, die eine bestimmte Form gewährleistet input Fokus hat, aber diesen Wert zu bekommen war mit PhantomJS fast unmöglich, da ich denke, dass der Browser nichts fokussieren kann, wenn er keine Bildschirmdarstellung hat. Wer dies benötigt, kann sich CasperJS ansehen, das für einige dieser Anforderungen eine einfache API anbietet.

+0

Wie definieren Sie diese Funktion? Ich kann nicht scheinen, dass das funktioniert. Ich habe eine solche Funktion in einem beforeEach-Block definiert, aber es wird nichts ausgelöst. –

+0

In einer separaten Datei, die ich dann mit 'require' eingebunden und wie' mouse.click (element) 'aufgerufen habe. –

+0

Obwohl bubble in meinen Tests auf true gesetzt ist, scheint es nicht zu blasen. Haben Sie das gleiche Problem erlebt? –

-1

Wie wäre es mit angular-scenario und dann mit browserTrigger(element, 'click')?

55

Sie können TriggerHandler, Teil von JQLite verwenden.

ich dies ein Click-Ereignisses auf einer Richtlinie zur Auslösung verwendet ...

element = angular.element("<div myDirective-on='click'></div>"); 
compiled = $compile(element)($rootScope); 
compiled.triggerHandler('click'); 

Voll Beispiel auf diesem Blog-Eintrag: http://sravi-kiran.blogspot.co.nz/2013/12/TriggeringEventsInAngularJsDirectiveTests.html

+0

Interessant, Ihr Plünderer funktioniert unbestreitbar. Ich habe ein ähnliches Problem [hier] (http://stackoverflow.com/questions/26675196/trigger-click-event-on-an-angularjs-directive-in-mocha-test-suite), aber diese Lösung löst nicht es ziemlich. Vielleicht können Sie das Problem erkennen? – miphe

+0

Großartig! Aber ich brauchte den Schritt '$ compile' nicht. –

+2

Liebe diese Antwort, weil es JQlite verwendet !!!! –

3

Ich habe auch Probleme mit diesem hat. Es scheint, als ob click() überhaupt nicht mit PhantomJS für irgendein Element funktioniert, das ich kompiliere. Es gibt immer undefined zurück.

Obwohl es nicht wirklich so gut wie ein tatsächliches klicken, können Sie die Richtlinie der Funktion in zugreifen konnte ng Sie auf einen Klick simulieren durch Isolat Umfang ist:

var element = $compile('<a ng-click="myfunc()">Click me</a>')(scope); 
var isolateScope = element.isolateScope(); 
isolateScope.myfunc(); 
scope.$digest(); 

/* check that things changed ... */ 
4

Also das ist meine Lösung war, die tatsächlich anhängen Element zum Körper. Da das Grundproblem darin besteht, dass phantomJs keine Ereignisse für Elemente im Speicher auslöst, schien es einfacher zu sein, jedes Element so anzuhängen, dass die Ereignisse real funktionieren.

afterEach(function(){ 
    $('body').empty(); 
}); 

it('should do something when clicked', function(){ 
    element = $compile('<div my-directive></div>')($scope); 
    $('body').append(element); 

    // fire all the watches, so the scope expressions will be evaluated 
    $rootScope.$digest(); 

    $(element).find('.my-input').click(); 
}); 
+0

Ich versuchte diesen Ansatz, aber ich kann es nicht zum Laufen bringen. Würde so nett sein und einen Blick auf diese Bin werfen: http://jsbin.com/womored/edit?js,output –

+0

Sie können auch 'eckle.element ('body')' anstelle von '$ ('body' verwenden) '. Wenn Sie jQuery nicht verwenden, injizieren Sie '$ document' und verwenden Sie stattdessen' $ document.find ('body') ''. –

+0

Hmm, ich habe nachgeschaut, aber das Jsbin sieht für mich ok aus, außer dass es auf einem verpassten Komma zu fehlen scheint. Haben Sie versucht, jquery einzubinden und stattdessen die Click-Methode zu verwenden? Es könnte einen Unterschied geben. Wenn Sie es nicht zum Laufen bringen können, können Sie auch die TriggerHandler-Methode von angular verwenden, um die Listener für ein Element manuell auszulösen. – frodo2975

0

können Sie verwenden angular-test-runner Bibliothek und Test wird wie folgt aussehen:

const testRunner = require('angular-test-runner'); 

describe('directive',() => { 
    let app; 
    const {expectElement, click} = testRunner.actions; 

    beforeEach(() => { 
     app = testRunner.app(['mod']); 
    }); 

    it("should add class when clicked", function() { 
     const html = app.runHtml('<span class="foo" ui-copy-link-dialog="url"></span>'); 

     html.perform(
      click.in('.foo') 
     ); 

     html.verify(
      expectElement('.foo').toHaveClass('test') 
     ); 
    }); 

});