it-swarm.com.de

Wie kann ich ein Klickereignis in meinem AngularJS-Direktive-Test simulieren?

Ich habe versucht, dem Format des Befehls ng-direct-testing repo für eine von mir geschriebene Anweisung zu folgen. Die Direktive rendert grundsätzlich eine Überlagerung, wenn der Benutzer auf ein Element klickt. Hier ist die Direktive (vereinfacht):

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, den ich schreibe, sieht so aus:

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

});

Im Test können Sie sehen, dass ich verschiedene Möglichkeiten ausprobiere, um das click() -Ereignis auf dem Link auszulösen, wobei die meisten von ihnen einen undefined-Fehler anzeigen. 

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

48
Matt Andrews

Dies stellte sich als ein Problem mit PhantomJS heraus: Einige Ereignisse, die auf Elemente einwirken, scheinen nicht auszulösen, wenn die Elemente nicht tatsächlich in einem Dokument enthalten sind, sondern nur im Gedächtnis (das ist meine Theorie, jedenfalls). Um dies zu umgehen, musste ich diese Funktion verwenden, um click -Ereignisse auf Elementen 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);
        }
    };
});

Dies funktionierte, obwohl andere Dinge schwieriger waren: Ich wollte auch einen Test schreiben, der sicherstellt, dass eine bestimmte Form input den Fokus hat, aber diesen Wert zu erhalten, war mit PhantomJS fast unmöglich, da der Browser keinen Fokus erzielen kann, wenn er nicht auf dem Bildschirm angezeigt wird Darstellung. Wer dies benötigt, kann sich CasperJS ansehen, die eine einfache API für einige dieser Anforderungen bietet.

18
Matt Andrews

Sie können triggerHandler als Teil von JQLite verwenden.

Ich habe damit ein Klickereignis auf eine Direktive ausgelöst ...

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

Vollständiges Beispiel in diesem Blogpost verfügbar: http://sravi-kiran.blogspot.co.nz/2013/12/TriggeringEventsInAngularJsDirectiveTests.html

56
Kildareflare

Daher bestand meine Lösung darin, das Element tatsächlich an den Körper anzuhängen. Da das Hauptproblem darin besteht, dass phantomJs keine Ereignisse für Elemente im Speicher auslöst, schien es einfacher, jedes Element anzufügen, damit die Ereignisse wirklich 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();
});
4
frodo2975

Ich hatte auch Probleme damit. Es scheint, als würde click() bei PhantomJS für jedes Element, das ich kompiliere, überhaupt nicht funktionieren. Es wird immer undefiniert zurückgegeben.

Sie ist zwar nicht wirklich so gut wie ein tatsächlicher Klick, aber Sie können auf die Funktion der Direktive in ng-click zugreifen, um einen Klick durch den Isolationsbereich zu simulieren:

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

Sie können angle-test-runner library verwenden 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')
        );
    });

});
0
wprzechodzen