0

Having this HTML ->

<!DOCTYPE html>
<html lang="en">
  <head>...</head>
  <body>
    <section class="players hidden"></section>
    <button class="btn">Play</button>
  </body>
</html>

Other function calls this one, the event is assigned and works well ->

const players = document.querySelector('.players');
const playBtn = document.querySelector('.btn');

const eventListener = () => {
  playBtn.addEventListener('click', () => {
    if (players.classList.contains('hidden')) {
      players.classList.remove('hidden');
    }
  });
};

export default eventListener;

Now, this test always fails because it only uses the HTML I provided there ->

test('click event starts the game', () => {
  document.body.innerHTML = `
    <section class="players hidden"></section>
    <button class="btn">Play</button>
  `;

  const players = document.querySelector('.players');
  const playBtn = document.querySelector('.btn');
  playBtn.click();
  const myClass = players.classList.contains('hidden');
  expect(myClass).toBe(false);
});

myClass will always be true because it's using that HTML I provided there. My question is, how can I test the functionality of eventListener.js? I want to simulate that button click, remove class hidden and test if it was indeed removed as intended?

Somehow I have to call the eventListener.js but even if I do it will still test that interal HTML in the test, correct? If I go to index.html and remove all classes will not affect the test which makes no sense, The idea of this test is to know if in the future I change or remove classes the test would fail letting me know where is the error.

Is this still considered unit testing? Or would be E2E?

Nicolas
  • 63
  • 4

1 Answers1

0

The test isn't working because eventListener.js (the script, not the function) runs when the function's imported. This happens before you add the elements to the document in the test. So evenListener() will throw an error because the playBtn in its script file is undefined and thus doesn't have the addEventListener() method.

To see what I mean, add a console log under the query selectors in eventListener.js and add a different one in the test above when you set the document body's inner HTML in the test. Run the test and see which comes first.

You can fix this by moving the query selectors inside eventListener(), causing them to run after the elements are created, and allowing the event listener to be added.

I tested the code below, and it works:

// eventListener.js
const eventListener = () => {
  const players = document.querySelector('.players');
  const playBtn = document.querySelector('.btn');
  playBtn.addEventListener('click', () => {
    if (players.classList.contains('hidden')) {
      players.classList.remove('hidden');
    }
  });
};

export default eventListener;
// eventListener.test.js
import eventListener from './eventListener'

test('click event starts the game', () => {
  document.body.innerHTML = `
    <section class="players hidden"></section>
    <button class="btn">Play</button>
  `;

  const players = document.querySelector('.players');
  const playBtn = document.querySelector('.btn');
  eventListener() 
  playBtn.click();
  const myClass = players.classList.contains('hidden');
  expect(myClass).toBe(false);
});

Also keep in mind that jest won't reset the document object between tests in the same test file. source

Brother58697
  • 2,290
  • 2
  • 4
  • 12
  • @I see but what the test looks for is the html inside the `document.body.innerHTML` it doesn't test my original html. I can add a new class to my original html and test if it contains that -> results in false because that class is not inside the `document.body.innerHTML`. Isn't there a way to inject the original html instead of adding it manually creating a new fake copy like in the example of my original post? – Nicolas Aug 28 '22 at 17:58