Code that I'm trying to test:
$(".toggle_item").on("change", function() {
console.log("change triggered")
item_name = $(this).data("name");
value = $(this).prop("checked");
if (Item.isValid(item_name) && cartModule.isUnique(item_name)) {
cartModule.toggleItem(item_name, value);
}
})
Jasmine spec:
describe("changing toggle item", function() {
beforeEach(function() {
console.log("in spec")
affix(".toggle_item[data-name='ladder']")
spyOn(cartModule, "isUnique")
spyOn(Item, "isValid")
$(".toggle_item").trigger("change")
})
it("should check item for validity & cart for uniqueness", function() {
expect(Item.isValid).toHaveBeenCalledWith("ladder")
expect(cartModule.isUnique).toHaveBeenCalledWith("ladder")
})
})
The console log output indicates that the trigger did not fire. Log output:
> "in spec"
FWIW:
- I have tried using both
affix
andloadFixtures
- I have tried using both
$(".toggle_item").on("change"...
and$(".toggle_item").change...
Gem list:
jasmine (2.7.0)
jasmine-core (2.8.0, 2.7.0)
jasmine-jquery-rails (2.0.3)
jasmine-rails (0.14.1)
** Updated answer: 2018-02-03**
The fundamental issue here is that your code is running too early, before your fixtures have been set up. This is different to loading your code before all of your tests. You need to be able to control when your code runs after it has been loaded.
This is more to do with how you write your code so that it can be tested, and is largely independent of the tools and libraries that are actually being used for testing.
Here is an example:
The explanation in your linked answer isn't quite right.
$('.selector').click(...)
is just an alias or short-hand for$('.selector').on('click', ...)
so changing to that won't make any difference - they are functionally exactly the same.The reason that answer actually works in that case is because the event listener is being added in the
$.ready()
event instead of immediately as soon as the code is loaded.It's the difference between running this:
Result:
And running this:
Result:
Another tool that might help you here is delegated event listeners.
Bascially, you add a single event listener to an element further up the DOM tree which listens for events from particular child selectors.
e.g.:
The benefit of this is that the event listener can be added early, event before
.toggle_item
exists, but the listeners will be fired once the elements have been added.There are some caveats to using event delegates (e.g. when using
event.preventDefault()
orevent.stopPropagation()
) but they are still a very useful tool.Original answer
In your code under test are you able to control/delay when the event listener is added?
I suspect that your code is trying to add the event listener to
.toggle_item
before theaffix(".toggle_item[data-name='ladder']")
statement is being run in your tests.You'll know it's out of order if you get:
Instead of what we want: