score:8

Accepted answer

There's a few issues here; we can sort out your syntax, but we also have to sort out the intention.

The error message "Error: expected on to have been called at least once but was never called" is accurate. on isn't called during your test. It's only called in your fixtures, to set up the event handlers. You're also triggering a mouseenter event but your listeners are for mouseover and mouseout. In the real world, you'd get a mouseover very soon after a mouseenter event, but when you fake it with jQuery that won't happen. jQuery is a non-starter anyway; see below.


You could try to fix this by changing them from anonymous functions to something named, like this:

var mouseOverHandler = function() { 
    d3.select(this).style('fill', '#ff0000');
};

And then bind it to your path with path.on('mouseover', mouseOverHandler). You'd think you can now spy on mouseOverHandler, but that won't work either. Your function will be bound when you call on, so swapping it out for a Sinon spy later won't have any effect.

jQuery triggering won't work with D3

Another problem is you can't use jQuery to trigger D3 events because jQuery events aren't DOM events. So you should replace your call to $('path').mouseenter() to something like document.getElementById('path_one').dispatchEvent(new MouseEvent('mouseover')); (Noting that this is changing the code from "trigger mouseenter on all paths" to "trigger mouseover on the element with id path_one").

Testing the wrong thing

You could futz around with refactoring so you can swap out your callback with something you can spy, but fundamentally you're testing the wrong thing. Essentially, you're trying to write a test for D3; "When I add an event listener, make sure that event listener gets called." Instead, you should test your actual code's behaviour: "When someone mouses over the chart, its colour should change".

If you really want to assert that your event handler was bound, you could do that:

expect(path.on('mouseover')).to.be.a('function')

But if you want to make sure your colour is changed, you want your test to be as follows:

describe("Mouseover events", function() {
    it("should update the colours when a user mouses over the paths", function () {
        var oldColor = path.style('fill');
        document.getElementById('path_one').dispatchEvent(new MouseEvent('mouseover'));
        expect(path.style('fill')).to.not.equal(oldColor);
    });
});

Related Query

More Query from same tag