Reputation: 34519
I'm trying to write some tests for some custom event handling that I've created in D3
, but I can't figure out an appropriate way to trigger these events that still works properly with D3's internals.
I won't go into the exact implementation details of the custom eventing, but will give a brief example of what I've got. So firstly there is a function that hooks up a range of events to an SVG element referred to as g
. For simplicity I'll use the mousedown
event to explain my problem:
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;
};
When the mousedown
event fires this will call the following function:
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;
};
So I wish to test this code using Jasmine
, I've setup my custom events up on an SVG circle
and I now want to fire them to test my code. Note that myfunc
is a Jasmine
spy.
it("triggers mouse down", function () {
events.on("mousedown", myfunc);
// Trigger mouse down
expect(myfunc).toHaveBeenCalled();
})
So I've currently tried 2 different ways to trigger this event on a circle, using D3
to get the event and fire it manually:
circle.on("mousedown")();
And using jQuery to select and trigger the event:
$("circle").trigger("mousedown");
Unfortunately what I find is that my getWindowPosition()
call fails, and this is because d3.event
is null.
I'm therefore trying to figure out a way to trigger an event that will behave as much like a real click as possible, such that D3
correctly sets up the d3.event information. Can anyone make any other suggestions on how I might trigger this event?
Upvotes: 0
Views: 1195
Reputation: 21578
This sounds pretty much like a similar problem I had to fight my way through a couple of weeks ago. Your approach was the first which occured to me at that time. After doing some research I concluded it is not going to work for the following reasons:
The jQuery
spec tells us about the jQuery.trigger()
method:
Although
.trigger()
simulates an event activation, complete with a synthesized event object, it does not perfectly replicate a naturally-occurring event.
There are two drawbacks resulting from this behaviour:
The d3.event
will not get populated with the triggered event leaving it null
as you have already mentioned.
Even if you tried to set the d3.event
programmatically to the synthesized event jQuery created you would just stumble into the next problem noticing that it is pretty much blank. Since the event did not result from a real mouse click there won't be any coordinates (like .clientX
, .clientY
) in it. Your code would therefore break on this shortcomming.
Eventually, I ended up creating my own 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);
If you need to support Internet Explorer you have to use the deprecated method MouseEvent.initMouseEvent()
since the use of a new event object which was created via the constructor is not yet supported by IE:
var event = document.createEvent("MouseEvents");
event.initMouseEvent( /* params */ );
I used browser detection and switched to the correct way of setting up the event object.
Having created the new event object you are ready to dispatch it to the desired target by using method dispatchEvent()
of interface EventTarget
which is implemented by DOM's Element
:
// dispatch it to the target
circle.dispatchEvent(event);
This way you end up with a fully fledged event which will also be recognized by d3 putting it to d3.event
accordingly.
Upvotes: 2
Reputation: 118
This might be a scoping issue. Try calling the d3.event within the mousedown handler and see if the value is still null. If so, simply move the getWindowPosition function declaration to within the mousedown handler scope.
Upvotes: 0