Setting up single-player tests before adding spinner

The needs-a-function test isn’t useful. Remove that and start over with an empty test that says: “with no parameters”. That way you can progress forward with what happens when init() is used with no parameters, and as you gain more information about the test you can then update the test description to something else.

1 Like

Like this? https://jsfiddle.net/5rsdoLhu/1/

describe("manageCover", function() {

  describe("init", function() {

    afterEach(function() {
      const container = document.querySelector(".container");
      container.innerHTML = container.innerHTML;
    });

    it("with no parameters", function() {
      const fnCall = () => manageCover.init();
      expect(fnCall).toThrow(/Cannot read properties of undefined/);
    });

  });
});

I did this next, but now I’m confused: https://jsfiddle.net/5cxdq9t8/2/

describe("manageCover", function() {

  describe("init", function() {

    afterEach(function() {
      const container = document.querySelector(".container");
      container.innerHTML = container.innerHTML;
    });

    it("with no parameters", function() {
      const fnCall = () => manageCover.init();
      expect(fnCall).toThrow(/Cannot read properties of undefined/);
    });

    describe("cover", function() {

      it("with a single cover", function() {

        // given
        manageCover.init({
          cover: ".play"
        });

        // when
        const cover = document.querySelector(".play");

        expect(cover).toHaveClass("hide");
      });

    });
  });
});

Are you able to put together a checklist of the it sections so there is a guide to follow?

No - I do not have the homework results already pre-prepared ahead of time.

Sorry no, copy-paste can only get you so far, but no further.

Ther is no need for toThrow or the fnCall const in the no-parameters test. Just do what I said, starting off with calling manageCover.init with no parameters.

1 Like

You want me to do this? https://jsfiddle.net/8jywx3eg/2/

describe("manageCover", function() {

  describe("init", function() {

    afterEach(function() {
      const container = document.querySelector(".container");
      container.innerHTML = container.innerHTML;
    });

    it("with no parameters", function() {
      manageCover.init();
    });

  });
});

Yes that’s right. The test doesn’t immediately fail so we need to figure out what to do from here.

Fail ☐ Pass ☐ Refactor

The test page says:

SPEC HAS NO EXPECTATIONS with no parameters

so we need to figure out a suitable expectation to make. We need to figure out what change manageCover.init() makes to the page, so that we have a suitable expectation.

About the only thing that the init() function does with no parameters, is that it adds a click handler to the .play element on the page.

That means copying over the simulateClick code from the other code, because we’ll want to use that to trigger a click on the “.play” element.

I did this: https://jsfiddle.net/j4L2gv3n/1/

  • “Spec ‘manageCover init with no parameters’ has no expectations.”
describe("manageCover", function() {

  function simulateClick(el) {
    const clickEvent = new MouseEvent('click', {
      currentTarget: 'el'
    });
    el.dispatchEvent(clickEvent);
  }

  describe("init", function() {

    afterEach(function() {
      const container = document.querySelector(".container");
      container.innerHTML = container.innerHTML;
    });

    it("with no parameters", function() {
      manageCover.init();
    });

    const cover = document.querySelector(".play");
    simulateClick(".play");

  });
});

2nd attempt: https://jsfiddle.net/5pnuxo27/

describe("manageCover", function() {

  function simulateClick(el) {
    const clickEvent = new MouseEvent('click', {
      currentTarget: 'el'
    });
    el.dispatchEvent(clickEvent);
  }

  describe("init", function() {

    afterEach(function() {
      const container = document.querySelector(".container");
      container.innerHTML = container.innerHTML;
    });

    it("with no parameters", function() {
      manageCover.init();
    });
    
    const cover = document.querySelector(".play");
    simulateClick(".play");

    expect(cover).toHaveClass("hide");
  });
});

Please ensure that excluding the simulateClick function itself, that the code you add is inside of the “with no parameters” test.

1 Like

I had never placed simulateClick inside there before so I wasn’t sure.

I added an expect because all the other simulateClick’s were followed by an expect.

This is what I have now: https://jsfiddle.net/wp6jaugx/1/

TypeError: el.dispatchEvent is not a function

describe("manageCover", function() {

  function simulateClick(el) {
    const clickEvent = new MouseEvent('click', {
      currentTarget: 'el'
    });
    el.dispatchEvent(clickEvent);
  }

  describe("init", function() {

    afterEach(function() {
      const container = document.querySelector(".container");
      container.innerHTML = container.innerHTML;
    });

    it("with no parameters", function() {
      manageCover.init();
      
    const cover = document.querySelector(".play");
    simulateClick(".play");

    expect(cover).not.toHaveClass("hide");
    });
    
  });
});

Am I supposed to try and fix that error?

This was my attempt at trying to fix it.

    it("with no parameters", function() {
      manageCover.init();

      const coverHandler = jasmine.createSpy("coverHandler");
      manageCover.init(".play", coverHandler);

      const cover = document.querySelector(".play");
      simulateClick(".play");

      expect(cover).not.toHaveClass("hide");
      expect(coverHandler).toHaveBeenCalled();
     
    });

That did not work.

TypeError: Failed to execute ‘addEventListener’ on ‘EventTarget’: parameter 2 is not of type ‘Object’.

I was looking at this to try and figure that out:

 function init(callback) {
    const cover = document.querySelector(".play");
    cover.addEventListener("click", coverClickHandler);
    events.afterClickCover = new Event("afterClickCover");
    cover.addEventListener("afterClickCover", callback);
  }

Maybe that error is supposed to stay there for now?

You have so much that’s bad and inappropriate there. Copy-paste is not your friend when you fail to understand what it’s supposed to do.

Please go back to the “with no parameters” test that you had in post #38 because you need to be taken through this step by step, decision by decision.

This is what I have: https://jsfiddle.net/8jywx3eg/2/

describe("manageCover", function() {

  describe("init", function() {

    afterEach(function() {
      const container = document.querySelector(".container");
      container.innerHTML = container.innerHTML;
    });

    it("with no parameters", function() {
      manageCover.init();
    });

  });
});

I am placing this below what?

Right underneath what?

 function simulateClick(el) {
        const clickEvent = new MouseEvent('click', {
          currentTarget: 'el'
        });
        el.dispatchEvent(clickEvent);
      }

The simulateClick function should go as close as practical to where it’s needed. Right now that means inside of the “with no parameters” test, but because we have a fairly good ability to predict the future use, it can go under the “init” description line instead as it’s going to be needed by a few different tests.

There are a few other things to fix up in that code though, one of them is that there is no need for the separate “init” section because the code we are testing has only the one init method. Removing that means we can describe the test as being “init manageCover” instead.

Also, the afterEach section should be removed until there is a need for it to be there.

1 Like

I got up to this: https://jsfiddle.net/0zqx6ft3/

describe("manageCover", function() {

  describe("init", function() {

    function simulateClick(el) {
      const clickEvent = new MouseEvent('click', {
        currentTarget: 'el'
      });
      el.dispatchEvent(clickEvent);
    }

    it("with no parameters", function() {
      manageCover.init();
    });

  });
});

Next is this:

one of them is that there is no need for the separate “init” section because the code we are testing has only the one init method. Removing that means we can describe the test as being “init manageCover” instead.

I did that here I think correctly: https://jsfiddle.net/0zqx6ft3/2/

describe("init manageCover", function() {

    function simulateClick(el) {
      const clickEvent = new MouseEvent('click', {
        currentTarget: 'el'
      });
      el.dispatchEvent(clickEvent);
    }

    it("with no parameters", function() {
      manageCover.init();
    });
});

Am I supposed to add these in next?

  const cover = document.querySelector(".play");
  simulateClick(".play");

  expect(cover).not.toHaveClass("hide");

Those would be added inside the it section like this? https://jsfiddle.net/bm24xq5p/1/

describe("init manageCover", function() {

  function simulateClick(el) {
    const clickEvent = new MouseEvent('click', {
      currentTarget: 'el'
    });
    el.dispatchEvent(clickEvent);
  }

  it("with no parameters", function() {
    manageCover.init();

    const cover = document.querySelector(".play");
    simulateClick(".play");

    expect(cover).not.toHaveClass("hide");
  });
});

No, that doesn’t get done.

Fail ☐ Pass ☐ Refactor

From the code at https://jsfiddle.net/0zqx6ft3/2/ we are looking for a way to have a failing test, when manageCover.init() is run, to become a passing test.

That means looking for something that the init() code changes, that we can test for.

The easiest kind of test is when the function returns something. That doesn’t happen here.

The next easiest is when the function changes something, which is commonly called a side-effect. Does any of that happen in this init() code?

  function init(callback) {
    const cover = document.querySelector(".play");
    cover.addEventListener("click", coverClickHandler);
    events.afterClickCover = new Event("afterClickCover");
    cover.addEventListener("afterClickCover", callback);
  }

The first two lines of the function make no changes that we can directly test for from outside of the function.

The last two lines of the function involve the callback which we aren’t dealing with yet, so we can ignore those for now.

There are no direct changes that occur, so we must move on to indirect changes. That’s where we use the click event from the first two lines of code.

What does the coverClickHandler do? Here it is:

  function coverClickHandler(evt) {
    const cover = evt.currentTarget;
    const curtain = openCurtain(cover);
    showVideo(curtain);
    cover.dispatchEvent(events.afterClickCover);
  }

The openCurtain() function possibly does things that we can test, as well as the showVideo() function. That showVideo() function is what we call “going for the gold” and needs to be held off for as long as possible. If we most directly test that then we won’t be as interested in testing other things around it that need testing, so delayed gratification really is needed here.

In that openCurtain() function is there anything that we can test for?

  function openCurtain(cover) {
    hide(cover);
    const curtain = document.querySelector(".curtain");
    curtain.classList.add("slide");
    return curtain;
  }

There are two things that we can test for, the hiding of the cover and adding slide to the curtain. Those will be two separate tests.

With hiding the cover, we need to ensure that it is first showing before the test calls manageCover.init(), so that we can expect that it is not showing afterwards.

So in the test, before manageCover.init(), define a cover variable the same as what’s done on line 32, that being the first line of the init() function.

We then use classList to remove “hide” from the cover.

After that the manageCover.init() gets run, and after that we need to expect that the cover doesn’t doesn’t have the class “hide”, by using the toHaveClass() matcher.

And after doing that we should end up with a suitably failing test, from which we can then work on making that test pass.

So in the test, before manageCover.init(), define a cover variable the same as what’s done on line 32, that being the first line of the init() function.

I did that here: https://jsfiddle.net/o1qwh73L/

  it("with no parameters", function() {
    const cover = document.querySelector(".play");
    cover.classList.remove("hide");
    manageCover.init();
  });
});

After that the manageCover.init() gets run, and after that we need to expect that the cover doesn’t doesn’t have the class “hide”, by using the toHaveClass() matcher.

Where exactly is this line being placed?

expect(cover).toHaveClass("hide");

Like this? https://jsfiddle.net/o1qwh73L/1/

it("with no parameters", function() {
    const cover = document.querySelector(".play");
    cover.classList.remove("hide");
    manageCover.init();
    expect(cover).toHaveClass("hide");
  });
});

After that the manageCover.init() gets run, and after that we need to expect

So, that’s why I placed expect after manageCover.init(); If I did that right.

Here is the failing test:

Expected <button class="play" type="button" aria-label="Open"> to have class 'hide'.

🗹 Fail ☐ Pass ☐ Refactor

Good one. The test is now set up so that when we trigger something that manageCover.init() set up, the test will pass.

🗹 Fail Pass ☐ Refactor

In this case, just before the expect line we can call simulateClick() with cover as the function parameter, and the test should pass.

We can then rename the test to something more suitable. A longer-winded description of what is happening is that clicking on the cover causes the cover to be hidden. A shorter description that’s suitable for updating the test description is: “initialized cover hides when clicked”

A reason why that is a good test name is that it naturally leads us to the next test, where “uninitialized cover doesn’t hide when clicked”

1 Like

This is what I have: https://jsfiddle.net/jrL7qazn/1/

  it("initialized cover hides when clicked", function() {
    const cover = document.querySelector(".play");
    cover.classList.remove("hide");
    manageCover.init();
    simulateClick(cover);
    expect(cover).toHaveClass("hide");
  });

  it("uninitialized cover doesn’t hide when clicked", function() {

  });

Are these the 2 lines that are being changed in the next it section?
https://jsfiddle.net/oz8j4rqm/3/

  it("uninitialized cover doesn’t hide when clicked", function() {
    const cover = document.querySelector(".play");
    // cover.classList.remove("hide");
    manageCover.init();
    simulateClick(cover);
    // expect(cover).toHaveClass("hide");
  });
});

We know that:

hide(cover);

Refers to the play button.

//hide(cover);

When that line is removed, the play button stays visible on the screen after it is clicked.

Working Code: https://jsfiddle.net/0tkLw1d5/

Fail ☐ Pass ☐ Refactor

The uninitialized test shouldn’t have manageCover.init(), and should have everything else the same as the previous test, including the simulateClick().

Because manageCover.init() is used to let us to change the hidden cover to being a shown cover, when manageCover.init() is not used, the cover should still remain hidden when we click on it.

Other than having no manageCover.init(), the only other change is that the expectation is reversed, expecting cover not to have that class.

You should then find that the test is unreliable, sometimes passing and sometimes failing, depending on the random order in which the tests is run. It’s good that we found that out now before we had other tests to add confusion to the troubleshooting, and it’s good that we have random test orders that help to expose such issues for us.

The unreliable tests are because the cover is not reliably in the same state when the test runs. Sometimes it has a click handler from a previous init, and sometimes it doesn’t. We will take care of next, but not directly with the afterEach function.

1 Like

This is what I have: https://jsfiddle.net/0zp6emkc/1/

  it("initialized cover hides when clicked", function() {
    const cover = document.querySelector(".play");
    cover.classList.remove("hide");
    manageCover.init();
    simulateClick(cover);
    expect(cover).toHaveClass("hide");
  });

  it("uninitialized cover doesn’t hide when clicked", function() {
    const cover = document.querySelector(".play");
    cover.classList.remove("hide");
    simulateClick(cover);
    expect(cover).not.toHaveClass("hide");
  });
});

What I will be adding next will be placed under the describe section?

describe("init manageCover", function() {