Setting up single-player tests before adding spinner

The question is usually the opposite: “Is it worth touching code that has no tests?” for which the answer is a resounding no.

Untested code really needs to have tests, especially before changing that code any further.

How do you want me to add tests to it?

I believe these were the tests that were already done on that set of code.

Adding tests to the video player code

Will the tests I be doing be different from those?

Can that be used as a guide, or as a frame of reference?

Also, how many tests are being done?

Also, what exactly is being tested?

What is already known is that,

In the code we disable clicking the play button, until the player is loaded and ready.

That’s what the code does, and how it works.

What is left is adding in the spinner, which I don’t have the proper instructions on how to implement that.

I believe it was only added to a multi player code, not a single player code. post #102

Yes, those tests were for the manageCover code.

We have recently been updating the tests for manageCover code in the other thread called Adding tests to video player code

Completing those tests are the best way forward.

It’s not the best way forward because I am taking a break from that code.

This code: https://jsfiddle.net/gqjaoykp/

I made following these instructions: post #175

The javascript in this code is completely different from the other code.

In the code here we disable clicking the play button, until the player is loaded and ready.

That is not in the other code I am taking a break from.

Also, all I am doing to this code is adding a spinner to it.

const manageCover = (function makeManageCover() {
  const events = {};

  function show(el) {
    el.classList.remove("hide");
  }

  function hide(el) {
    el.classList.add("hide");
  }

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

  function showVideo(curtain) {
    const thewrap = curtain.parentElement.querySelector(".wrap");
    show(thewrap);
  }

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

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

  return {
    init
  };
}());

const videoPlayer = (function makeVideoPlayer() {
  const events = {};
  const eventHandlers = {};
  let player = null;


  function loadIframeScript() {
    const tag = document.createElement("script");
    tag.src = "https://www.youtube.com/iframe_api";
    const firstScriptTag = document.getElementsByTagName("script")[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
  }

  function onYouTubeIframeAPIReady() {
    const cover = document.querySelector(".play");
    const wrapper = cover.parentElement;
    const frameContainer = wrapper.querySelector(".video");
    videoPlayer.addPlayer(frameContainer);
  }

  function shufflePlaylist(player) {
    player.setShuffle(true);
    player.playVideoAt(0);
    player.stopVideo();
  }

  function onPlayerReady(event) {
    player = event.target;
    player.setVolume(100);
    shufflePlaylist(player);
    const iframe = player.h;
    iframe.dispatchEvent(events.afterPlayerReady);
  }

  function addPlayer(video) {

    const playlist = "0dgNc5S8cLI,mnfmQe8Mv1g,-Xgi_way56U,CHahce95B1g";

    const config = {
      height: 360,
      host: "https://www.youtube-nocookie.com",
      width: 640
    };
    config.playerVars = {
      autoplay: 0,
      cc_load_policy: 0,
      controls: 1,
      disablekb: 1,
      fs: 0,
      iv_load_policy: 3,
      loop: 1,
      playlist,
      rel: 0
    };
    config.events = {
      "onReady": onPlayerReady
    };

    player = new YT.Player(video, config);

    const iframe = player.h;
    const eventHandler = eventHandlers.afterPlayerReady;
    iframe.addEventListener("afterPlayerReady", eventHandler);
  }

  function play() {
    player.playVideo();
  }

  function addEvents(handlers) {
    eventHandlers.afterPlayerReady = handlers.afterPlayerReady;
    events.afterPlayerReady = new Event("afterPlayerReady");
  }

  function init(initEventHandlers) {
    addEvents(initEventHandlers);
    loadIframeScript();
    window.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
  }

  return {
    addPlayer,
    init,
    play
  };
}());

videoPlayer.init({
  afterPlayerReady: function initCover() {
    manageCover.init(function playVideo() {
      videoPlayer.play();
    });
  }
});

We are soon going to have three completely different sets of tests and code for the one project. That way lies madness.

1 Like

This post may need to be moved out into a new topic, maybe called:

Setting up tests to prepare to add spinner to single player code.

How many tests would be needed to be made to prepare to add the spinner in for this single video code?

This code is using only 1 video, not multiple, where the javascript is set up differently, where there is no animation stuff inside the javascript.

I was able to do this: https://jsfiddle.net/edqkb3pg/

To be able to see the tests on the screen I did this:

.container {
  display: none;
}

Do I need to change any of the resources in here?

i.e., Do I need to remove any, add different ones?

jasmine.css
jasmine.min.js
jasmine-html.min.js
boot0.min.js
boot1.min.js

I don’t exactly know what is being tested because, what gets tested to then be able to add a spinner to it?

We know what the code does, and it does what it is supposed to do.

We disable clicking the play button, until the player is loaded and ready.

Instructions for the single player code that were followed: post #175

Where the play button is disabled, that’s where the spinner should be seen.

/* Spinner */
.lds-dual-ring:after {
  content: " ";
  display: block;
  width: 64px;
  height: 64px;
  margin: auto;
  border-radius: 50%;
  border: 6px solid #fff;
  border-color: #fff transparent #fff transparent;
  animation: lds-dual-ring 1.2s linear infinite;
  opacity: 0.5;
}
@keyframes lds-dual-ring {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
 function toggleSpinner(cover) {
    cover.classList.toggle("lds-dual-ring");
  }

I first toggle the spinner when we init the cover:

    const cover = document.querySelector(opts.target);
    toggleSpinner(cover);
    const videoWrapper = cover.nextElementSibling;
    initPlayer(videoWrapper, onPlayerReady);

And toggle the spinner again, thus turning it off, when the player is ready.

    function onPlayerReady() {
        const coverSelector = opts.target;
        manageCover.init(coverSelector);
        const cover = document.querySelector(coverSelector);
        toggleSpinner(cover);
        cover.addEventListener("click", coverClickHandler);
    }

As soon as a video has loaded which only takes a second, the spinner goes away and you can click on the cover to start playing.

Which were part of the instructions here: post #96

But those instructions went with the multi player code, not a single player code.

Also, those may not be the correct instructions:

In post #102

You say this: Where new instructions are given.

With the spinners, it’s not a good idea to have the code calling toggleSpinner embedded directly in the other code that deals with players. There is a Separation of Concerns ideal that should be applied.

Instead of having the toggleSpinner embedded in the code, we want the spinner to start when the player is a added, and to stop spinning when the player is ready.

Still, that was done to the multi player code.

In the single player code it would be written differently.

I think that the spinner idea was not thought of until later on, so that would be why it was not added to the single player code at the time.

Would there be a way to come up with, or modify existing instructions for how to add the spinner to the single player code to see if there are any issues?

I found some code to test the spinner here: post #122

describe("Spinner", function () {
    const div = document.createElement("div");
    it("toggles on when the classname doesn't exist", function () {
        div.classList.remove("lds-dual-ring");
        spinner.toggleDualRing(div);
        expect(div.classList.contains("lds-dual-ring")).to.equal(true);
    });
    it("toggles off when the classname already exists", function () {
        div.classList.add("lds-dual-ring");
        spinner.toggleDualRing(div);
        expect(div.classList.contains("lds-dual-ring")).to.equal(false);
    });
});

Looking at this piece of instruction: post #26

   addPlayer.init({
        afterAddPlayer: function (evt) {
            toggleSpinner(evt);
        },
        afterPlayerReady: function (evt) {
            toggleSpinner(evt);
            const cover = evt.detail.cover;
            manageCover.init(cover, showVideo);
        }
    });

It looks that that would be added to this somehow:

Just because it looks similar, but I have no idea.

videoPlayer.init({
  afterPlayerReady: function initCover() {
    manageCover.init(function playVideo() {
      videoPlayer.play();
    });
  }
});

I didn’t realize it would be this complicated to figure out how to add a spinner to the code. For me it seems difficult to understand how it would be done.

Because the spinner happens on the cover, at minimum the manageCover code needs to be fully tested, as well as anything else that the spinner may affect.

But you’ve announced that you are taking a break from tests on the manageCover code.

The multiplayer code, not the single player code that looks nothing like the multiplayer code.

The spinner is being added to the single video code. https://jsfiddle.net/gqjaoykp/

How do I add tests to this?

How would that be set up?

I started the process of adding tests here: https://jsfiddle.net/edqkb3pg/

What tests will be done to it?

How do I get the process started?

const manageCover = (function makeManageCover() {
  const events = {};

  function show(el) {
    el.classList.remove("hide");
  }

  function hide(el) {
    el.classList.add("hide");
  }

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

  function showVideo(curtain) {
    const thewrap = curtain.parentElement.querySelector(".wrap");
    show(thewrap);
  }

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

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

  return {
    init
  };
}());

const videoPlayer = (function makeVideoPlayer() {
  const events = {};
  const eventHandlers = {};
  let player = null;


  function loadIframeScript() {
    const tag = document.createElement("script");
    tag.src = "https://www.youtube.com/iframe_api";
    const firstScriptTag = document.getElementsByTagName("script")[0];
    firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
  }

  function onYouTubeIframeAPIReady() {
    const cover = document.querySelector(".play");
    const wrapper = cover.parentElement;
    const frameContainer = wrapper.querySelector(".video");
    videoPlayer.addPlayer(frameContainer);
  }

  function shufflePlaylist(player) {
    player.setShuffle(true);
    player.playVideoAt(0);
    player.stopVideo();
  }

  function onPlayerReady(event) {
    player = event.target;
    player.setVolume(100);
    shufflePlaylist(player);
    const iframe = player.h;
    iframe.dispatchEvent(events.afterPlayerReady);
  }

  function addPlayer(video) {

    const playlist = "0dgNc5S8cLI,mnfmQe8Mv1g,-Xgi_way56U,CHahce95B1g";

    const config = {
      height: 360,
      host: "https://www.youtube-nocookie.com",
      width: 640
    };
    config.playerVars = {
      autoplay: 0,
      cc_load_policy: 0,
      controls: 1,
      disablekb: 1,
      fs: 0,
      iv_load_policy: 3,
      loop: 1,
      playlist,
      rel: 0
    };
    config.events = {
      "onReady": onPlayerReady
    };

    player = new YT.Player(video, config);

    const iframe = player.h;
    const eventHandler = eventHandlers.afterPlayerReady;
    iframe.addEventListener("afterPlayerReady", eventHandler);
  }

  function play() {
    player.playVideo();
  }

  function addEvents(handlers) {
    eventHandlers.afterPlayerReady = handlers.afterPlayerReady;
    events.afterPlayerReady = new Event("afterPlayerReady");
  }

  function init(initEventHandlers) {
    addEvents(initEventHandlers);
    loadIframeScript();
    window.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
  }

  return {
    addPlayer,
    init,
    play
  };
}());

videoPlayer.init({
  afterPlayerReady: function initCover() {
    manageCover.init(function playVideo() {
      videoPlayer.play();
    });
  }
});

Wait, I’m also confused, doesn’t the spinner get added to this part of the code?

I think I’m just confused from looking at different codes where it was used in the multi player code.

So, I have no idea how it would be added to the single video code.

videoPlayer.init({
  afterPlayerReady: function initCover() {
    manageCover.init(function playVideo() {
      videoPlayer.play();
    });
  }
});

Are these the right resources I will be using? https://jsfiddle.net/edqkb3pg/

jasmine.css
jasmine.min.js
jasmine-html.min
boot0.min.js
boot1.min.js

How do I properly set this up?

What will be the checklist of items that we will be testing in the code?

We will be testing these?

function show(el) {
function hide(el) {
function openCurtain(cover) {
function showVideo(curtain) {
function coverClickHandler(evt) {
function init(callback) {

What are we looking to confirm when we do the tests?

That each function works?

Only this part is being tested? https://jsfiddle.net/j9b743ke/

Everything else gets removed?

const manageCover = (function makeManageCover() {
  const events = {};

  function show(el) {
    el.classList.remove("hide");
  }

  function hide(el) {
    el.classList.add("hide");
  }

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

  function showVideo(curtain) {
    const thewrap = curtain.parentElement.querySelector(".wrap");
    show(thewrap);
  }

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

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

  return {
    init
  };
}());

This is what I have, can progress be made from here? https://jsfiddle.net/o4vc9w2r/

What will the name of the 1st it section be called?

describe("manageCover", function() {

  describe("init", function() {

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

    it("          ", function() {

    });
  });
});

Continuing, this is what I did next: https://jsfiddle.net/2qupvsjw/1/

describe("manageCover", function() {

  describe("init", function() {

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

    it("needs a function parameter of .play", function() {
      const fnCall = () => manageCover.init();
      expect(fnCall).toThrowError(/Cannot read properties of undefined/);
    });

  });
});

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