web-dev-qa-db-ja.com

AngularJSディレクティブテストでクリックイベントをシミュレートするにはどうすればよいですか?

私が書いたディレクティブの ng-directive-testing リポジトリのフォーマットに従うことを試みました。このディレクティブは基本的に、ユーザーが要素をクリックしたときにオーバーレイをレンダリングします。ディレクティブは次のとおりです(簡略化されています)。

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

私が書いているテストは次のようになります。

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

});
_

テストで、リンク上でclick()イベントをトリガーするいくつかの方法を試していることがわかります。ほとんどの場合、undefinedエラーが発生します。

誰も私がここで間違っていることを教えてもらえますか?例を読むと、これは正しい構文のように聞こえますが、私のテストランナー(Grunt経由のKarma)はボールをプレーしたくありません。

48
Matt Andrews

そのため、これはPhantomJSの問題であることが判明しました:要素に作用するイベントの中には、要素が実際にドキュメント上になく、メモリ内にある場合に発生しないように見えるものがあります(とにかく私の理論です)。この問題を回避するには、この関数を使用して要素のclickイベントをトリガーする必要がありました。

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

これはうまくいきましたが、他のことはより困難でした:特定のフォームinputにフォーカスがあることを確認するテストを作成したかったのですが、PhantomJSを使用してこの値を取得することはほとんど不可能でしたオンスクリーン表現がない場合。これが必要な人は、これらの要件のいくつかに簡単なAPIを提供するCasperJSを見ることができます。

18
Matt Andrews

JQLiteの一部であるtriggerHandlerを使用できます。

これを使用して、ディレクティブでクリックイベントをトリガーしました...

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

このブログ投稿で利用可能な完全な例: http://sravi-kiran.blogspot.co.nz/2013/12/TriggeringEventsInAngularJsDirectiveTests.html

56
Kildareflare

したがって、これに対する私の解決策は、実際に要素を本体に追加することでした。根本的な問題は、phantomJsがメモリ内の要素に対してイベントを発生させないことであるため、イベントが実際に機能するように各要素を追加する方が簡単に思えました。

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

これにも問題があります。 click()は、コンパイルした要素のPhantomJSではまったく機能しないようです。常に未定義を返します。

実際のクリックほどではありませんが、ng-clickでディレクティブの機能にアクセスして、その分離スコープを介してクリックをシミュレートできます。

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

angular-test-runner libraryを使用すると、テストは次のようになります。

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