0

After a loooot of testing... I stumbled upon some interesting behavior that I don't understand.

Code:

function someFunction() {
  ... // whatever this is stubbed
}

// below is called ("mounted") by finalDOM.mountUserActions()     
$("#button").click(function() {
  savedElement = $("#saved_element")
  console.log("BEFPRE THEN in event listener ==========")
  console.log("found saved_element? = " + savedElement.length)
  console.log("found unsaved_element? = " + $("#unsaved_element").length)

  someFunction().then(function(result){
    console.log("INSIDE THEN in event listener ==========")
    savedElement.val(result)
    $("#unsaved_element").val(result)
    console.log("ensure result is found = " + result)
    console.log("found saved_element? = " + savedElement.length)
    console.log("found unsaved_element?  = " + $("#unsaved_element").length)
  })
})

Test code:

fdescribe("on click of button", function() {
  beforeEach(function(){
    response = Promise.resolve("test")
    spyOn(window, "someFunction").and.returnValue(response)
    savedElement = affix("#saved_element")
    affix("#unsaved_element")
    affix("#button")
    finalDOM.mountUserActions();
    $("#button").click()
  })
  it("should change value of saved_element", function() {
    response.then(function() {
        console.log("INSIDE SPEC EXPECTATION ==========")
        console.log("value of saved_element = " + savedElement.val())
        console.log("value of unsaved_element = " + $("#unsaved_element").val())
      expect(savedElement.val()).toEqual("test") // PASS
      expect($("#unsaved_element").val()).toEqual("test") // FAIL
    })
  })
})

Console output:

BEFPRE THEN in event listener ==========
found saved_element? = 1
found unsaved_element? = 1
INSIDE THEN in event listener ==========
ensure result is found = test
found saved_element? = 1
found unsaved_element?  = 0
INSIDE SPEC EXPECTATION ==========
value of saved_element = test
value of unsaved_element = undefined

The fact that the length of both elements are 1 in BEFORE THEN in event listener proves that the affix worked, both elements are on the page, so then why is only the saved element found once INSIDE THEN in event listener?

I don't think I've ever encountered this... is this a Javascript gotcha or a Jasmine one? Would love to learn more about it to avoid this in the future... have spent a lot of time scratching my head on it.

13
  • What happens if you comment out $("#unsaved_element").val(result)? Are you even returning a value from someFunction() that can be passed to the callback? it's probably setting it to null.
    – womp
    Commented Nov 30, 2018 at 8:50
  • 1
    1: Your #saved_element isn't really found inside the then callback, you're storing a reference to it in a variable. When you have this reference you'll have access to it even if it has been removed from the DOM. 2: Your test relies on a promise being resolved, but you haven't written the test accommodate for the fact that it's async. This means that some "post-test" code might run before your then callback runs. Maybe this is what happens and this post-test code removes the #unsaved_element?
    – Lennholm
    Commented Nov 30, 2018 at 10:09
  • @Lennholm 1: sure yes I get this fact, but it's still really weird that #unsaved_element isn't found. The test has a response.then to accommodate the fact that it's async. And if you look at the console output, you'll see that the order is correct, i.e., the entire event listener is called BEFORE the expectations are checked. If the response.then were not there, then the first expectation would also fail and the console log would show in this order: 1) BEFORE THEN , 2) INSIDE SPEC, 3) INSIDE THEN
    – james
    Commented Nov 30, 2018 at 20:23
  • @womp if I comment it out, the test will definitely fail because then nothing is getting set... but to your point, I put in a console.log to check the value of result and it in fact is test, the spy.returnValue is working appropriately
    – james
    Commented Nov 30, 2018 at 20:30
  • 1
    @james I'm not talking about the order of which your test code runs. I'm talking about the fact that Jasmine doesn't know that your test has a promise in it, thus, Jasmine is unaware that it needs to wait before proceeding from your test to the next or with any kind of cleanup. This means that Jasmine might execute some kind of cleanup before your then block is executed. In other words, your then block is still executed, but Jasmine has already moved on. You tell Jasmine that your test is async by using the done function and then tell Jasmine the test is done by invoking it.
    – Lennholm
    Commented Dec 1, 2018 at 19:17

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.