触发浏览器事件进行测试

Triggering browser events for testing

我正在尝试为我在 D3 中创建的一些自定义事件处理编写一些测试,但我想不出合适的方法来触发这些事件,这些事件仍然可以与 D3 一起正常工作内部。

我不会详细介绍自定义事件的具体实施细节,但会给出一个简短的例子来说明我所得到的。因此,首先有一个函数将一系列事件连接到称为 g 的 SVG 元素。为简单起见,我将使用 mousedown 事件来解释我的问题:

var events = function(g) {
    container = g;

    // Register the raw events required
    g.on("mousedown", mousedown)
     .on("mouseenter", mouseenter)
     .on("mouseleave", mouseleave)
     .on("click", clicked)
     .on("contextmenu", contextMenu)
     .on("dblclick", doubleClicked);

    return events;
};

mousedown 事件触发时,这将调用以下函数:

function mousedown(d, i) {

    // Record the initial position of the mouse down
    windowStartPosition = getWindowPosition();
    position = getPosition();

    // Do more stuff

    // Trigger a mouse down event
    dispatch.mousedown.call(this, d, i);
}

function getWindowPosition() {
    var point = {
        x: d3.event.clientX,
        y: d3.event.clientY,
        dx: 0,
        dy: 0
    };

    if (position) {
        point.dx = windowPosition.x -point.x;
        point.dy = windowPosition.y - point.y;
    }       

    return point;
};

所以我想使用 Jasmine 测试此代码,我已经在 SVG circle 上设置了我的自定义事件,现在我想触发它​​们来测试我的代码。请注意 myfuncJasmine 间谍。

it("triggers mouse down", function () {
    events.on("mousedown", myfunc);
    // Trigger mouse down
    expect(myfunc).toHaveBeenCalled();
})

所以我目前尝试了 2 种不同的方法来在圆圈上触发此事件,使用 D3 获取事件并手动触发它:

circle.on("mousedown")();

并使用jQuery到select并触发事件:

$("circle").trigger("mousedown");

不幸的是,我发现我的 getWindowPosition() 调用失败了,这是因为 d3.event 为空。

因此,我试图找到一种方法来触发一个事件,该事件的行为将尽可能像真正的点击,这样 D3 可以正确设置 d3.event 信息。谁能就我如何触发此事件提出任何其他建议?

这可能是一个范围界定问题。尝试在 mousedown 处理程序中调用 d3.event 并查看该值是否仍然为空。如果是这样,只需将 getWindowPosition 函数声明移动到 mousedown 处理程序范围内。

这听起来很像几周前我不得不努力解决的类似问题。你的方法是我当时第一个想到的。在做了一些研究后,我得出结论,由于以下原因,它不会起作用:

jQuery 规范向我们介绍了 jQuery.trigger() 方法:

Although .trigger() simulates an event activation, complete with a synthesized event object, it does not perfectly replicate a naturally-occurring event.

这种行为有两个缺点:

  1. d3.event 不会填充触发事件,如您已经提到的那样 null

  2. 即使您尝试以编程方式将 d3.event 设置为创建的 合成事件 jQuery,您也会无意中进入下一个注意到它几乎是空白的问题。由于事件不是由真正的鼠标单击引起的,因此其中不会有任何坐标(如 .clientX.clientY)。因此,您的代码会因这个缺点而中断。

最终,我创建了自己的 MouseEvent:

// Initialize the event as needed.
var mouseEventInit = {
    "button": 0,
    "clientX": 0,  // maybe something like circle.getBoundingClientRect().left + 1
    "clientY": 0   // maybe something like circle.getBoundingClientRect().top + 1
    // ... fill any properties you need.
};

// create new event and...
var event = new MouseEvent("click", mouseEventInit);

如果您需要支持 Internet Explorer,则必须使用已弃用的方法 MouseEvent.initMouseEvent(),因为 IE 尚不支持使用通过构造函数创建的新事件对象:

var event = document.createEvent("MouseEvents");
event.initMouseEvent( /* params */ );

我使用浏览器检测并切换到设置事件对象的正确方法。

创建新事件对象后,您就可以使用接口 EventTarget 的方法 dispatchEvent() 将其分派到所需的目标,接口 EventTarget 由 DOM 的 [=25] 实现=]:

// dispatch it to the target
circle.dispatchEvent(event);

这样你就得到了一个完全成熟的事件,它也将被 d3 识别并相应地把它放到 d3.event