web-dev-qa-db-fra.com

Comment puis-je simuler un événement de clic dans mon test de directive AngularJS?

J'ai essayé de suivre le format de ng-directive-testing repo pour une directive que j'ai écrite. La directive rend essentiellement une superposition lorsque l'utilisateur clique sur un élément. Voici la directive (simplifiée):

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');
            });
        }
    };
});

Le test que j'écris ressemble à ceci:

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
    });

});

Vous pouvez voir dans le test que j'essaie de plusieurs manières de déclencher l'événement click() sur le lien, la plupart d'entre elles donnant une erreur undefined

Quelqu'un peut-il me dire ce que je fais mal ici? En lisant les exemples, cela semble être la syntaxe correcte, mais mon coureur de test (Karma via Grunt) ne veut pas jouer au ballon.

48
Matt Andrews

Donc, cela se est avéré être un problème avec PhantomJS: certains événements qui agissent sur les éléments ne semblent pas tirer lorsque les éléments ne sont pas réellement sur un document nulle part, mais juste en mémoire (qui est ma théorie, de toute façon). Pour contourner cela, je devais utiliser cette fonction pour déclencher des événements sur click éléments:

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);
        }
    };
});

Cela a fonctionné, bien que d'autres choses étaient plus difficiles: Je voulais aussi écrire un test qui assure une input forme donnée a le focus, mais obtenir cette valeur était presque impossible avec PhantomJS puisque je suppose que le navigateur ne peut pas faire quelque chose de concentré si elle n'a pas à l'écran représentation. Toute personne ayant besoin cela pourrait jeter un oeil à CasperJS qui offre une API simple pour certaines de ces exigences.

18
Matt Andrews

Vous pouvez utiliser triggerHandler, une partie de JQLite.

J'ai utilisé cela pour déclencher un événement de clic sur une directive ...

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

. Exemple complet disponible sur ce blog: .__ http://sravi-kiran.blogspot.co.nz/2013/12/TriggeringEventsInAngularJsDirectiveTests.html

56
Kildareflare

Donc, ma solution à c'était à ajouter en fait l'élément au corps. Étant donné que la racine du problème est que PhantomJS ne se déclenche pas d'événements pour les éléments en mémoire, il semblait plus simple d'ajouter chaque élément afin que les événements travaillent pour de vrai.

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();
});
4
frodo2975

J'ai eu du mal avec cela aussi. Il semble que click() ne semble pas fonctionner du tout pour moi avec PhantomJS pour tout élément que je compile. Il revient toujours pas défini.

Bien que pas vraiment aussi bon que d'un clic réel, vous pouvez accéder à la fonction de la directive cliquez sur ng pour simuler un clic à travers elle est d'isoler la portée:

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

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

Vous pouvez utiliser test-runner angulaire bibliothèque et un test ressemblera:

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')
        );
    });

});
0
wprzechodzen