Setting up single-player tests before adding spinner

Where am I placing this? const items = Object.values(player);

  function getIframe(player) {
    const items = Object.values(player);

    const iframe = player.d || player.e || player.f || player.g || player.h;
    return iframe;
  }

Next is:

For the second part we can use the array find() method to search those items for anything that has a nodeName of “IFRAME”

Yes, that’s a good place.

Remove the const iframe and return lines from that function. we don’t need those anymore.

Yes that’s right. You have an array of items called items. In that array, one of them is the iframe element, so return from the function items.find(). The find method stops searching when it finds the first valid match, and gives you that matched item.

The find() method uses a testing function, to determine if a valid match has been made. We should give that function parameter name of item. In that function we return the comparison of whether item.nodeName equals “`IFRAME”.

I’m doing this wrong: https://jsfiddle.net/mgyz43hb/1/

    function getIframe(player) {
    const items = Object.values(player);
    const array = [player.d, player.e, player.f, player.g, player.h];
    items.find(item);
    return item.nodeName = "IFRAME"
  }

Maybe I was supposed to do this. https://jsfiddle.net/yc86vu5n/

  function getIframe(player) {
    const items = Object.values(player);
    const array = [player.d, player.e, player.f, player.g, player.h];
    return items.find(item);
  }

I still don’t know how to add this in: item.nodeName = "IFRAME"

Remove the line that has player.d, player.e

Then we’ll deal with the rest of the issues in there.

https://jsfiddle.net/tje28vLf/1/

  function getIframe(player) {
    const items = Object.values(player);
    return items.find(item);
  }

Where you have item, replace that with isIframe.
We will then create an isIframe function that performs the comparison.

After that when it is working, we can then condense things.

1 Like

https://jsfiddle.net/sqp51zmv/

  function isIframe() {
  }

  function getIframe(player) {
    const items = Object.values(player);
    return items.find(isIframe);
  }

This is the comparison:

item.nodeName = "IFRAME"

But I do not know how it is supposed to be written into the code.

Sorry no, that is not a comparison. That is an assignment. A single equals sign is for assignment, and triple equals is for comparison.

That items.find() method takes each item from the array and passes it to the isIframe function. The isIframe function needs a function parameter of item.

When you’ve done that we’ll be in a good place for the comparison.

1 Like

https://jsfiddle.net/z10np52c/

  function isIframe(item) {
  }

  function getIframe(player) {
    const items = Object.values(player);
    return items.find(isIframe);
  }

comparison: item.nodeName === "IFRAME"

In the isIframe function, return the the comparison.

That will get us back to fully passing tests, and with passing tests we can then reorganise the getIframe and isIframe functions to simplify things.

1 Like

Tests now pass.

https://jsfiddle.net/go7fax9L/1/

  function isIframe(item) {
    return item.nodeName === "IFRAME"
  }

  function getIframe(player) {
    const items = Object.values(player);
    return items.find(isIframe);
  }

We can now refactor the isIframe and getIframe functions so that they do things in a better way.

Refactoring the getIframe function, the items variable was only there to help aid the explanation of how to do the code. You should “inline the items variable”.

How you inline the items variable is by replacing items.find to instead be Object.values(player).find

That way you can then remove the const items line.

1 Like

Test code: https://jsfiddle.net/xmjhgenz/

Working video code: https://jsfiddle.net/2w14o79v/

Are both good now.

  function isIframe(item) {
    return item.nodeName === "IFRAME"
  }

  function getIframe(player) {
    return Object.values(player).find(isIframe);
  }

Are we able to get back to continuing the tests?

Where I am up to this:

In the videoPlayer tests there is an init section, and at the end of that init section we need a new test for what we expect to happen when we init with a list of videos for the playlist.

https://jsfiddle.net/jo6td04m/

If the test is supposed to fail first, it’s not failing.

it("init with a list of videos for the playlist", function() {
      //given
      player = undefined;

      //when
      videoPlayer.addPlayer(video);

      //then
      expect(typeof options.events.onReady).toBe("function");
    });
  });

This was my attempt at doing it in the working code: https://jsfiddle.net/gdrhpb3n/

But it has to be first done in the test code, then we can see if I did it wrong or right in my attempt I did.

Maybe some of what I did here can be used in the test code.

const videoPlayer = (function makeVideoPlayer() {
  const config = {};
  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, config.playlist);
  }
  
  function isIframe(item) {
    return item.nodeName === "IFRAME"
  }

  function getIframe(player) {
    return Object.values(player).find(isIframe);
  }
  
  function shufflePlaylist(player) {
    player.setShuffle(true);
    player.playVideoAt(0);
    player.stopVideo();
  }

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

  function addPlayer(video, playlist) {

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

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

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

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

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

  function init(videos) {
    config.playlist = videos.join();
    addEvents();
    loadIframeScript();
    window.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
  }

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

function initCover() {
  manageCover.init(function playVideo() {
    videoPlayer.play();
  });
}
videoPlayer.afterPlayerReady = initCover;
videoPlayer.init([
  "0dgNc5S8cLI",
  "mnfmQe8Mv1g",
  "CHahce95B1g",
  "2VwsvrPFr9w"
]);

There’s one more piece of refactoring to do with the getIframe function, and that is to inline the isIframe function.

1 Like

ok, what do I need to do?

  function isIframe(item) {
    return item.nodeName === "IFRAME"
  }

  function getIframe(player) {
    return Object.values(player).find(isIframe);
  }

This line is changing to something different?

return item.nodeName === "IFRAME"

What am I supposed to do?

No, don’t worry about that. Let’s take a different and easier approach.

Just move the isIframe() function inside of the getIframe() function, above the return statement. That will help to make it clear that the isIframe() function isn’t supposed to be used by anything else.

I’m not supposed to do anything after this?

or I am?

https://jsfiddle.net/sqxngku3/1/

  function getIframe(player) {
    function isIframe(item) {
      return item.nodeName === "IFRAME"
    }
    return Object.values(player).find(isIframe);
  }

Well hopefully this last part will make it less confusing. Convert that isIframe function into an arrow-notation function instead. That might be too difficult for you, so here it is.

  function getIframe(player) {
    const isIframe = (item) => item.nodeName === "IFRAME";
    return Object.values(player).find(isIframe);
  }

Then, if you were to inline the isIframe variable, it would look like this:

  function getIframe(player) {
    return Object.values(player).find(
      (item) => item.nodeName === "IFRAME"
    );
  }

Feel free to pick whichever variation you prefer.

1 Like

https://jsfiddle.net/0cwxymo2/

  function getIframe(player) {
    return Object.values(player).find(
      (item) => item.nodeName === "IFRAME"
    );
  }

Are we able to begin the tests now?

I was able to get a failing test here.

https://jsfiddle.net/m15uk3fr/

Next up is getting the test to pass.

What needs to be fixed in here?

    it("init with a list of videos for the playlist", function() {
      //given
      player = undefined;

      //when
      videoPlayer.addPlayer(video);

      //then
      expect(typeof options.events.onReady).toBe("function");
    });
  });