如何在我的AngularJS指令测试中模拟单击事件?

时间:2023-01-23 21:14:51

I've tried following the format of the ng-directive-testing repo for a directive I've written. The directive basically renders an overlay when the user clicks on an element. Here's the directive (simplified):

我试过按照我编写的指令的ng- direcororepo格式进行测试。当用户单击一个元素时,该指令基本上呈现一个覆盖。这是指令(简体):

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

The test I'm writing looks like this:

我写的测试是这样的:

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

});

You can see in the test that I try several ways to trigger the click() event on the link, with most of them giving an undefined error.

您可以在测试中看到,我尝试了几种方法来触发链接上的click()事件,其中大多数方法都给出一个未定义的错误。

Can anyone tell me what I'm doing wrong here? Reading the examples this sounds like it's the correct syntax but my test runner (Karma via Grunt) doesn't want to play ball.

谁能告诉我我哪里做错了吗?读这些例子听起来这是正确的语法,但是我的测试人员(通过咕哝的因果报应)不想打球。

6 个解决方案

#1


55  

You can use triggerHandler, part of JQLite.

您可以使用triggerHandler, JQLite的一部分。

I used this to trigger a click event on a directive...

我用它来触发一个指令上的点击事件……

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

Full example available on this blog post: http://sravi-kiran.blogspot.co.nz/2013/12/TriggeringEventsInAngularJsDirectiveTests.html

完整的例子可以在这篇博文中找到:http://sravi-kiran.blogspot.co.nz/2013/12/TriggeringEventsInAngularJsDirectiveTests.html

#2


18  

So this turned out to be a problem with PhantomJS: some events that act on elements don't seem to fire when the elements aren't actually on a document anywhere, but just in memory (that's my theory, anyway). To work around this I had to use this function to trigger click events on elements:

因此,这对PhantomJS来说是个问题:当元素不在文档中,而是在内存中(这是我的理论)时,一些作用于元素的事件似乎不会触发。为了解决这个问题,我必须使用这个函数来触发元素上的点击事件:

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

This worked, although other things were harder: I also wanted to write a test that ensures a given form input has focus, but getting this value was almost impossible using PhantomJS since I guess the browser can't make something focused if it has no onscreen representation. Anyone needing this could have a look at CasperJS which offers a simple API for some of these requirements.

这是可行的,尽管其他的事情比较困难:我还想编写一个测试,以确保给定的表单输入具有焦点,但是使用PhantomJS获得这个值几乎是不可能的,因为我想如果浏览器没有屏幕上的表示,那么它就无法实现焦点。任何需要这一点的人都可以看看CasperJS,它为其中的一些需求提供了一个简单的API。

#3


4  

So my solution to this was to actually append the element to the body. Since the root problem is that phantomJs doesn't fire events for elements in memory, it seemed simpler to append each element so that the events work for real.

我的解决方案是把元素添加到主体中。由于根问题是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


3  

I've had trouble with this also. It seems as though click() doesn't seem to work at all for me with PhantomJS for any element I compile. It always returns undefined.

我也遇到过这个问题。似乎在我所编译的任何元素中,单击()似乎对我都不起作用。它总是返回定义。

Though not really as good as an actual click, you could access the directive's function in ng-click to simulate a click through it's isolate scope:

虽然没有实际的点击那么好,但你可以在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 ... */

#5


0  

You can use angular-test-runner library and test will look like:

您可以使用angular-test-runner库,测试如下:

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

});

#6


-1  

How about including angular-scenario and then using browserTrigger(element, 'click')?

如何包含angular场景,然后使用browserTrigger(元素,“单击”)?

#1


55  

You can use triggerHandler, part of JQLite.

您可以使用triggerHandler, JQLite的一部分。

I used this to trigger a click event on a directive...

我用它来触发一个指令上的点击事件……

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

Full example available on this blog post: http://sravi-kiran.blogspot.co.nz/2013/12/TriggeringEventsInAngularJsDirectiveTests.html

完整的例子可以在这篇博文中找到:http://sravi-kiran.blogspot.co.nz/2013/12/TriggeringEventsInAngularJsDirectiveTests.html

#2


18  

So this turned out to be a problem with PhantomJS: some events that act on elements don't seem to fire when the elements aren't actually on a document anywhere, but just in memory (that's my theory, anyway). To work around this I had to use this function to trigger click events on elements:

因此,这对PhantomJS来说是个问题:当元素不在文档中,而是在内存中(这是我的理论)时,一些作用于元素的事件似乎不会触发。为了解决这个问题,我必须使用这个函数来触发元素上的点击事件:

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

This worked, although other things were harder: I also wanted to write a test that ensures a given form input has focus, but getting this value was almost impossible using PhantomJS since I guess the browser can't make something focused if it has no onscreen representation. Anyone needing this could have a look at CasperJS which offers a simple API for some of these requirements.

这是可行的,尽管其他的事情比较困难:我还想编写一个测试,以确保给定的表单输入具有焦点,但是使用PhantomJS获得这个值几乎是不可能的,因为我想如果浏览器没有屏幕上的表示,那么它就无法实现焦点。任何需要这一点的人都可以看看CasperJS,它为其中的一些需求提供了一个简单的API。

#3


4  

So my solution to this was to actually append the element to the body. Since the root problem is that phantomJs doesn't fire events for elements in memory, it seemed simpler to append each element so that the events work for real.

我的解决方案是把元素添加到主体中。由于根问题是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


3  

I've had trouble with this also. It seems as though click() doesn't seem to work at all for me with PhantomJS for any element I compile. It always returns undefined.

我也遇到过这个问题。似乎在我所编译的任何元素中,单击()似乎对我都不起作用。它总是返回定义。

Though not really as good as an actual click, you could access the directive's function in ng-click to simulate a click through it's isolate scope:

虽然没有实际的点击那么好,但你可以在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 ... */

#5


0  

You can use angular-test-runner library and test will look like:

您可以使用angular-test-runner库,测试如下:

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

});

#6


-1  

How about including angular-scenario and then using browserTrigger(element, 'click')?

如何包含angular场景,然后使用browserTrigger(元素,“单击”)?