Home > front end >  Jasmine testing - persistence of affixed elements and why clicks trigger more than once
Jasmine testing - persistence of affixed elements and why clicks trigger more than once

Time:09-17

Please note this is SPECIFIC to test environment using Jasmine. I am NOT having these issues in dev mode. Here's a bare bones example I created:

Code

function mountTest() {
  $(document).on("click", ".test", function() {
    console.log(">> clicked test")
  })
}

Spec code

fdescribe("test click test", function() {
    beforeEach(function() {
        test_btn = affix(".test")
        mountTest() /******************** KEY LINE ****************/
    })
    describe("1st spec", function() {
        beforeEach(function() {
            console.log(">>1")
            test_btn.click()
        })
        it("shoudl work", function(){

        })
    })
    describe("2nd spec", function() {
        beforeEach(function() {
            console.log(">>2")
            test_btn.click()
        })
        it("shoudl work", function(){

        })
    })
    describe("3rd spec", function() {
        beforeEach(function() {
            console.log(">>3")
            test_btn.click()
        })
        it("shoudl work", function(){

        })
        describe("4th spec", function() {
            beforeEach(function() {
                console.log(">>4")
                test_btn.click()
            })
            it("shoudl work", function(){

            })
        })
    })
})

As it stands, the output in console is this:

>>1
>> clicked test
>>2
>> clicked test
>> clicked test
>>3
>> clicked test
>> clicked test
>> clicked test
>>3
>> clicked test
>> clicked test
>> clicked test
>> clicked test
>>4
>> clicked test
>> clicked test
>> clicked test
>> clicked test

I have discovered that the reason for the redundancy in clicks is because mountTest() is called at the top-level beforeEach. Further experimentation has revealed that if mountTest() is called within one of the describe blocks that is a spec (1, 2, 3, 4), then the redundancy is removed for ALL specs AFTER the first spec on which the mountTest() is placed. FOR EXAMPLE

Modified spec code

fdescribe("test click test", function() {
    beforeEach(function() {
        test_btn = affix(".test")
    })
    describe("1st spec", function() {
        beforeEach(function() {
            console.log(">>1")
            test_btn.click()
        })
        it("shoudl work", function(){

        })
    })
    describe("2nd spec", function() {
        beforeEach(function() {
            console.log(">>2")
            mountTest() /******************** KEY LINE ****************/
            test_btn.click()
        })
        it("shoudl work", function(){

        })
    })
    describe("3rd spec", function() {
        beforeEach(function() {
            console.log(">>3")
            test_btn.click()
        })
        it("shoudl work", function(){

        })
        describe("4th spec", function() {
            beforeEach(function() {
                console.log(">>4")
                test_btn.click()
            })
            it("shoudl work", function(){

            })
        })
    })
})

Console output

>>1
>>2
>> clicked test
>>3
>> clicked test
>>3
>> clicked test
>>4
>> clicked test

The redundancy is gone. The 1st spec doesn't trigger any clicks because the mountTest() is not called until the 2nd spec.

Can someone explain this behavior to me? I don't even begin to know what I'm misunderstanding here... is it the nature of the affix or the beforeEach or...?

Here are some of my questions in looking at this:

  1. In the modified spec example, if mountTest() is only called in the 2nd spec describe block, why are the clicks for the 3rd/4th specs working? How did mountTest() translate over? My only thought would be that somehow the affix(".test") element keeps the eventHandler added by mountTest(), but according to jasmine-fixture docs (https://github.com/searls/jasmine-fixture), affix cleans up its own elements after each spec?

  2. In the original spec example, I think the explanation for why the clicks are so redundant is that mountTest() has mounted N times by the Nth spec, so at Nth spec, each click registers once for each mount, so you get Nth clicks. But overlapping with #1, if the affix(".test") element is new each time, then you're mounting on a new object each time, so it should only trigger once, no?

FWIW, to test the affix, I did manually write a afterEach(function() { $(".test").remove() }) and add it after each it expectation block in the original spec code, and this did NOT fix the issue at all.

Relevant gemfile:

gem 'jasmine', "2.6"
gem 'jasmine-jquery-rails' # resolving to 2.0.3

Then also jasmine-fixture is loading via a file spec > javascripts > helpers > jasmine-fixture.min.js which is requested because in the spec > javascripts > support > jasmine.yml file, there's a line:

src_files:
...
helpers:
  - 'helpers/**/*.js'

CodePudding user response:

@MinusFour answered correctly and I accepted, for process sake I just wanted to clarify the two possible answers that would work, based on explanation commentary.

Option A:

describe("test click test", function() {
    beforeEach(function() {
        test_btn = affix(".test")
    })
    beforeAll(function(){
        mountTest()
    })
    ....

Option B:

describe("test click test", function() {
    beforeEach(function() {
        test_btn = affix(".test")
        mountTest()
    })
    afterEach(function() {
        $(document).off()
    })
    ....

CodePudding user response:

The problem is the listener is being attached to the document not the element that is managed by affix. This is why the listeners are not being removed and why removing the element manually doesn't do anything either. You would need to attach it directly to the element created. You could do:

function mountTest(el) {
  el.on("click", function() {
    console.log(">> clicked test")
  })
}

And call it this way:

beforeEach(function() {
    test_btn = affix(".test")
    mountTest(test_btn);
})

This will attach it directly on the element and not the document.

  • Related