Armino Popp
Armino Popp

Reputation: 358

How to properly make a jasmine test for a button click

I recently started to use Jasmine for javascript testing, and I'm stuck with a problem. I'm sure someone here can help me.

I have the following simple code, posted on CodePen:

The HTML:

<div class="container">
  <button id="btnEraseDiv">Erase Div</button>
</div>

The CSS:

.container{
  display:flex;
  flex-direction: column;
  padding:20px;
  background-color:#eef;
}

button{
  padding:10px 0;
  font-size: 1rem;
  background-color:#fdd;
}

#toBeErased{
  padding:10px;
  border:1px solid #ffdddd;
  background-color:#ffccdd;
  margin-bottom:20px;
}

And the JavaScript:

const eraseDiv = () => {
  const myDiv = $('#toBeErased');
  myDiv.children('p').fadeOut(300, () => {
    myDiv.fadeOut(600, () => {
      myDiv.remove();
    });
  })  
}

const addMyDiv = () => {
  const myDiv = $(`<div id="toBeErased">
    <p>This div and all its content will be erased. <br />Click the button bellow to erase it.</p>    
  </div>`);
  if (!document.querySelectorAll('#toBeErased').length) {
    $('div.container').append(myDiv);
  }
};


$(document).ready(() => {
  $('button#btnEraseDiv').on('click', () => {
    eraseDiv();
  });

});

//here starts the test

describe("Erase button functionality", () => {
  beforeEach( () => {
    addMyDiv();
  });

  it('should erase the div when btnEraseDiv clicked - using async', async () => {
    //this test here is the mistery
    const btn = $('button#btnEraseDiv');
    await btn.click();
    expect($('div#toBeErased').length).toBeLessThan(1);
  });

  it('should erase the div when btnEraseDiv clicked - using done and timeout', (done) => {
    //this is working, but i'm sure it is not the right usage
    const btn = $('button#btnEraseDiv');
    btn.click();    
    setTimeout( () => {
      expect($('div#toBeErased').length).toBeLessThan(1);
      done();
    }, 2000);
  });



});

Reading the documentation I have come out with this test which fails:

it('should erase the div when btnEraseDiv clicked - using async', async () => {
    //this test here is the mistery
    const btn = $('button#btnEraseDiv');
    await btn.click();
    expect($('div#toBeErased').length).toBeLessThan(1);
  });

Then I make the other one with a timeout bigger than the fadeout durations and it works, but it feels improper.

it('should erase the div when btnEraseDiv clicked - using done and timeout', (done) => {
    //this is working, but i'm sure it is not the right usage
    const btn = $('button#btnEraseDiv');
    btn.click();    
    setTimeout( () => {
      expect($('div#toBeErased').length).toBeLessThan(1);
      done();
    }, 2000);
  });

Can someone point me in the right direction or give me an example for this situation?

Thank you for your time!

Upvotes: 2

Views: 1842

Answers (1)

Tom Faltesek
Tom Faltesek

Reputation: 2818

I think your "improper" solution with the done param is actually a pretty good way to go. Your first solution with async/await doesn't quite work out because the jQuery click() method doesn't return an awaitable type (a Promise or "thenable").

If you'd rather have an async/await solution, here's something you could try:

// Add a reusable utility method for your tests.
const wait = async ms => new Promise(res => setTimeout(res, ms));

it('should erase the div when btnEraseDiv clicked - using async', async () => {
  $('button#btnEraseDiv').click();

  await wait(2000); // Wait for animations to complete.

  expect($('div#toBeErased').length).toBeLessThan(1);
});

Side Note

Another thing you may want to consider, especially if you're going to be writing and maintaining a lot of UI tests, is using an E2E/UI testing library like Nightwatch, Puppeteer, Selenium, Cypress, or Protractor (if you're using Angular).

These libraries will allow you to write tests that exist separate from your implementation code. You can run them in a normal browser or headless browser instance. They all have built in "driver" API functions that allow you to "wait" for certain DOM conditions. For instance waitForElementPresent() from Nightwatch or waitForSelector() from Puppeteer. There are many more API methods available to help you easily write maintainable E2E/UI tests.

Upvotes: 1

Related Questions