MailOnline/videojs-vast-vpaid

View on GitHub
test/plugin/videojs.vast.spec.js

Summary

Maintainability
F
1 wk
Test Coverage
var MediaFile = require('ads/vast/MediaFile');
var VASTClient = require('ads/vast/VASTClient');
var VASTError = require('ads/vast/VASTError');
var VASTIntegrator = require('ads/vast/VASTIntegrator');
var VASTResponse = require('ads/vast/VASTResponse');
var vastUtil = require('ads/vast/vastUtil');

var dom = require('utils/dom');
var playerUtils = require('utils/playerUtils');
var utilities = require('utils/utilityFunctions');
var xml = require('utils/xml');

var testUtils = require('../test-utils');

var videoJsVersion = parseInt(videojs.VERSION.split('.')[0], 10);

if(videoJsVersion === 4) {
  require('videojs_4.vast.vpaid');
}
if(videoJsVersion === 5) {
  require('videojs_5.vast.vpaid');
}

describe("videojs.vast plugin", function () {
  var testDiv, videoEl, player;

  function assertError(callback, msg, code) {
    var error = testUtils.firstArg(callback).error;

    assert.instanceOf(error, VASTError);
    assert.equal(error.message, "VAST Error: " + msg);
    if (code) {
      assert.equal(error.code, code);
    }
  }

  function createMediaFile(url, type) {
    var xmlStr = '<MediaFile delivery="progressive" type="' + type + '" codec="video/mpeg-generic" bitrate="457" width="300" height="225">' +
      '<![CDATA[' + url + ']]>' +
      '</MediaFile>';
    return new MediaFile(xml.toJXONTree(xmlStr));
  }

  function assertVASTTrackRequest(URLs, variables) {
    URLs = utilities.isArray(URLs) ? URLs : [URLs];
    sinon.assert.calledOnce(vastUtil.track);
    sinon.assert.calledWithExactly(vastUtil.track, URLs, variables);
  }

  function assertTriggersTrackError(fn, msg, code, vastResponse) {
    var adsCanceledSpy = sinon.spy();
    var vastAdErrorSpy = sinon.spy();
    player.on('vast.adError', vastAdErrorSpy);
    player.on('vast.adsCancel', adsCanceledSpy);

    fn();

    assertError(vastAdErrorSpy, msg, code);
    if (code && vastResponse) {
      assertVASTTrackRequest(vastResponse.errorURLMacros, {ERRORCODE: code});
    }
    sinon.assert.called(adsCanceledSpy);
  }

  beforeEach(function () {
    window.iPhone = false;
    testDiv = document.createElement("div");
    document.body.appendChild(testDiv);

    videoEl = document.createElement('video');
    videoEl.id = 'testVideoElm';
    testDiv.appendChild(videoEl);
  });

  afterEach(function () {
    dom.remove(testDiv);
  });

  it("must be instantiated as part of the player", function () {
    var player = videojs(videoEl, {});
    assert.isDefined(player.vastClient);
  });

  it("must trigger 'vast.adError' event with an explanatory error if there was a problem initializing the ads", function () {
    var spy = sinon.spy();
    var player = videojs(document.createElement('video'), {});

    player.on('vast.adError', spy);
    player.vastClient();
    sinon.assert.calledOnce(spy);
    assertError(spy, 'on VideoJS VAST plugin, missing adTagUrl on options object');
  });

  it("must not trigger 'vast.adError' if the adTagUrl is passed as part of the options", function () {
    var vastErrorSpy = sinon.spy();
    var player = videojs(document.createElement('video'), {});
    player.on('vast.adError', vastErrorSpy);
    player.vastClient({adTagUrl: 'http://fake.ad.url'});
    sinon.assert.notCalled(vastErrorSpy);
  });

  it("must not trigger 'vast.adError' if the adTagXML is defined as part of the options", function(){
    var vastErrorSpy = sinon.spy();
    var player = videojs(document.createElement('video'), {});
    player.on('vast.adError', vastErrorSpy);
    player.vastClient({adTagXML: utilities.noop});
    sinon.assert.notCalled(vastErrorSpy);
  });

  it("must trigger a 'vast.adError' if the passed adTagXML is not a function", function(){
    var spy = sinon.spy();
    var player = videojs(document.createElement('video'), {});

    player.on('vast.adError', spy);
    player.vastClient({adTagXML: 'NOT A FUNCTION'});
    sinon.assert.calledOnce(spy);
    assertError(spy, 'on VideoJS VAST plugin, the passed adTagXML option does not contain a function');
  });

  it("must cancel the ads on 'vast.reset' evt", function(){
    var spy = sinon.spy();
    var player = videojs(document.createElement('video'), {});
    player.on('vast.adsCancel', spy);
    player.vastClient({adTagUrl: 'http://fake.ad.url'});
    player.trigger('vast.reset');
    sinon.assert.calledOnce(spy);
  });

  describe("playAdAlways option", function () {
    var resetSpy;

    beforeEach(function () {
      this.clock = sinon.useFakeTimers();
      player = videojs(document.createElement('video'), {});
      resetSpy = sinon.spy();
    });

    afterEach(function(done){
      this.clock.restore();
      done();
    });

    it("set to true, must reset plugin 'vast.firstPlay' event", function () {
      player.vastClient({
        adTagUrl: utilities.echoFn('/fake.ad.url'),
        playAdAlways: true
      });
      player.on('vast.reset', resetSpy);
      //We simulate we finish playing the video.
      player.trigger('vast.contentEnd');
      this.clock.tick();
      sinon.assert.calledOnce(resetSpy);
    });

    it("set to false, must try not play a new ad every time the user replays the ad", function () {
      player.vastClient({
        adTagUrl: utilities.echoFn('/fake.ad.url'),
        playAdAlways: false
      });

      player.on('vast.reset', resetSpy);
      //We simulate we finish playing the video.
      player.trigger('vast.contentEnd');
      this.clock.tick();
      sinon.assert.notCalled(resetSpy);
    });
  });

  describe("player.vast", function () {
    var vastAd;

    beforeEach(function () {
      player = videojs(document.createElement('video'), {});
      vastAd = player.vastClient({adTagUrl: 'http://fake.ad.url'});
    });

    it("must be equal to the object returned by the plugin", function(){
      assert.strictEqual(vastAd, player.vast);
    });

    describe("isEnabled", function () {
      it("must return true when the vast plugin is first enabled", function () {
        assert.isTrue(player.vast.isEnabled());
      });
    });

    describe("enable", function () {
      it("must enable the ads", function () {
        player.vast.disable();
        assert.isFalse(vastAd.isEnabled());
        player.vast.enable();
        assert.isTrue(vastAd.isEnabled());
      });
    });

    describe("disable", function () {
      it("must disable the ads", function () {
        player.vast.disable();
        assert.isFalse(player.vast.isEnabled());
      });
    });
  });

  describe("on 'vast.firstPlay' event", function(){
    var clock;

    beforeEach(function () {
      clock = sinon.useFakeTimers();
      player = videojs(document.createElement('video'), {});
      player.vastClient({
        adTagUrl: utilities.echoFn('/fake.ad.url'),
        playAdAlways: true
      });
    });

    afterEach(function(done){
      clock.restore();
      done();
    });

    it("must cancel the ads if the ads are not enabled", function(){
      var adsCanceled = sinon.spy();
      player.on('vast.adsCancel', adsCanceled);
      player.vast.disable();
      player.trigger('vast.firstPlay');
      clock.tick();
      sinon.assert.calledOnce(adsCanceled);
    });

    it("must cancel the ad on replay if the ads are disabled and trigger the content events", function(){
      var adsCanceled = sinon.spy();
      var contentStart = sinon.spy();
      var contentEnd = sinon.spy();
      player.vast.disable();
      player.trigger('vast.firstPlay');
      clock.tick();
      //We simulate we finish playing the video.
      player.trigger('vast.contentEnd');
      clock.tick();
      player.on('vast.adsCancel', adsCanceled);
      player.on('vast.contentStart', contentStart);
      player.on('vast.contentEnd', contentEnd);

      //We simulate the second first play (replay)
      player.trigger('vast.firstPlay');
      clock.tick();

      sinon.assert.calledOnce(adsCanceled);
      sinon.assert.notCalled(contentStart);
      sinon.assert.notCalled(contentEnd);

      player.trigger('playing');

      sinon.assert.calledOnce(adsCanceled);
      sinon.assert.calledOnce(contentStart);
      sinon.assert.notCalled(contentEnd);

      player.trigger('ended');

      sinon.assert.calledOnce(adsCanceled);
      sinon.assert.calledOnce(contentStart);
      sinon.assert.calledOnce(contentEnd);
    });

    it("must remove the native poster to prevent flickering when video content starts", function(){
      var tech = player.el().querySelector('.vjs-tech');
      player.trigger('vast.firstPlay');
      clock.tick();
      assert.isNull(tech.getAttribute('poster'));
    });

    describe("with ads enabled", function(){
      it("must not cancel the ads", function(){
        var adsCanceled = sinon.spy();
        player.on('vast.adsCancel', adsCanceled);
        player.vast.enable();
        player.trigger('vast.firstPlay');
        clock.tick();

        sinon.assert.notCalled(adsCanceled);
      });

      describe("loading spinner", function(){
        beforeEach(function(){
          player = videojs(document.createElement('video'), {});
          player.vastClient({adTagUrl: utilities.echoFn('/fake.ad.url')});
        });

        it("must be added while we retrieve the ad", function(){
          player.trigger('vast.firstPlay');
          clock.tick();
          assert.isTrue(dom.hasClass(player.el(), 'vjs-vast-ad-loading'));
        });

        it("must be removed on vast ad start", function(){
          player.trigger('vast.firstPlay');
          clock.tick();
          assert.isTrue(dom.hasClass(player.el(), 'vjs-vast-ad-loading'));
          player.trigger('vast.adStart');
          clock.tick(100);
          assert.isFalse(dom.hasClass(player.el(), 'vjs-vast-ad-loading'));
        });

        it("must be removed if ads are canceled while trying to play the ad", function(){
          player.trigger('vast.firstPlay');
          clock.tick();
          assert.isTrue(dom.hasClass(player.el(), 'vjs-vast-ad-loading'));
          player.trigger('vast.adsCancel');
          clock.tick(100);
          assert.isFalse(dom.hasClass(player.el(), 'vjs-vast-ad-loading'));
        });
      });

      it("must pause the video if it is not paused", function(){
        player = videojs(document.createElement('video'), {});
        player.vastClient({adTagUrl: utilities.echoFn('/fake.ad.url'), adCancelTimeout:5000});
        sinon.spy(player, 'pause');
        player.trigger('vast.firstPlay');
        clock.tick();
        sinon.assert.calledOnce(player.pause);
      });

      it("must cancel the ads if there it takes too much time (adCancelTimeout) to start the ad", function(){
        player = videojs(document.createElement('video'), {});
        player.vastClient({adTagUrl: utilities.echoFn('/fake.ad.url'), adCancelTimeout: 3000});

        assertTriggersTrackError(function () {
          player.trigger('vast.firstPlay');
          clock.tick(3001);
        }, 'timeout while waiting for the video to start playing', 402);
      });

      it("must not cancel the ad if the ad starts before the timeout", function(){
        var adsCancelSpy = sinon.spy();
        player = videojs(document.createElement('video'), {});
        player.vastClient({adTagUrl: utilities.echoFn('/fake.ad.url'), adCancelTimeout: 3000});
        player.on('vast.adsCancel', adsCancelSpy);
        player.trigger('vast.firstPlay');
        clock.tick();
        player.trigger('vast.adStart');
        clock.tick(3000);
        sinon.assert.notCalled(adsCancelSpy);
      });
    });

    describe("vast.contentStart && vast.contentEnd", function(){
      var contentStartSpy, contentEndedSpy;

      beforeEach(function(){
        contentStartSpy = sinon.spy();
        contentEndedSpy = sinon.spy();
        player.trigger('vast.firstPlay');
        clock.tick();
        player.on('vast.contentStart', contentStartSpy);
        player.on('vast.contentEnd', contentEndedSpy);
        player.trigger('vast.adsCancel');
      });

      it("must be triggered on content playing and content end", function(){
        player.trigger('playing');
        sinon.assert.calledOnce(contentStartSpy);
        sinon.assert.notCalled(contentEndedSpy);
        player.trigger('ended');
        sinon.assert.calledOnce(contentStartSpy);
        sinon.assert.calledOnce(contentEndedSpy);
      });

      it("must not be triggered if there is an vast.reset after restoring the content", function(){
        player.trigger('vast.reset');
        player.trigger('playing');
        player.trigger('ended');
        sinon.assert.notCalled(contentStartSpy);
        sinon.assert.notCalled(contentEndedSpy);
      });

      it("must not be triggered if there is an vast.firstPlay after restoring the content", function(){
        player.trigger('vast.firstPlay');
        player.trigger('playing');
        player.trigger('ended');
        sinon.assert.notCalled(contentStartSpy);
        sinon.assert.notCalled(contentEndedSpy);
      });

      it("must not trigger vast.contentEnd if there is a vast.reset after restoring the content", function(){
        player.trigger('playing');
        player.trigger('vast.reset');
        player.trigger('ended');
        sinon.assert.calledOnce(contentStartSpy);
        sinon.assert.notCalled(contentEndedSpy);
      });

      it("must not trigger vast.contentEnd if there is a vast.firstPlay after restoring the content", function(){
        player.trigger('playing');
        player.trigger('vast.firstPlay');
        player.trigger('ended');
        sinon.assert.calledOnce(contentStartSpy);
        sinon.assert.notCalled(contentEndedSpy);
      });

      it("must not trigger vast.contentEnd if there is a vast.reset while playing the content", function(){
        player.trigger('playing');
        sinon.assert.calledOnce(contentStartSpy);
        sinon.assert.notCalled(contentEndedSpy);
        player.trigger('vast.reset');
        sinon.assert.calledOnce(contentStartSpy);
        sinon.assert.notCalled(contentEndedSpy);
      });

      it("must not trigger vast.contentEnd if there is a vast.firstPlay while playing the content", function(){
        player.trigger('playing');
        sinon.assert.calledOnce(contentStartSpy);
        sinon.assert.notCalled(contentEndedSpy);
        player.trigger('vast.firstPlay');
        sinon.assert.calledOnce(contentStartSpy);
        sinon.assert.notCalled(contentEndedSpy);
      });
    });

    it("must set the player.vast.adUnit to null once we finish playing", function(){
      player.vast.adUnit = {
        type: 'FAKE',
        isPaused: function() {
          return false;
        },
        pauseAd: utilities.noop
      };
      player.vast.disable();
      player.trigger('vast.firstPlay');
      clock.tick();
      assert.isNull(player.vast.adUnit);
    });

    describe(":", function(){
      beforeEach(function(){
        sinon.stub(playerUtils, 'restorePlayerSnapshot');
      });

      afterEach(function(){
        playerUtils.restorePlayerSnapshot.restore();
      });

      it("must remove the adUnit and restore the video content on 'vast.adsCancel' evt", function(){
        player.vast.adUnit = {
          type: 'FAKE',
          pauseAd: utilities.noop,
          resumeAd: utilities.noop,
          isPaused: utilities.noop
        };
        player.trigger('vast.firstPlay');
        clock.tick();
        player.trigger('vast.adsCancel');
        assert.isNull(player.vast.adUnit);
        sinon.assert.calledOnce(playerUtils.restorePlayerSnapshot);
      });

       it("must remove the adUnit but not restore the video content on 'vast.reset' evt", function(){
        player.vast.adUnit = {
          type: 'FAKE',
          pauseAd: utilities.noop,
          resumeAd: utilities.noop,
          isPaused: utilities.noop
        };
        player.trigger('vast.firstPlay');
        clock.tick();
        player.trigger('vast.reset');
        assert.isNull(player.vast.adUnit);
        sinon.assert.notCalled(playerUtils.restorePlayerSnapshot);
      });
    });
  });

  describe("playPrerollAd", function () {
    var getVASTResponse, callback, old_UA;

    beforeEach(function () {
      old_UA = utilities._UA;
      utilities._UA = "iPhone";

      this.clock = sinon.useFakeTimers();
      sinon.stub(vastUtil, 'track').returns(null);
      sinon.spy(VASTIntegrator.prototype, 'playAd');
      player = videojs(document.createElement('video'), {});
      player.vastClient({adTagUrl: utilities.echoFn('/fake.ad.url')});
      getVASTResponse = sinon.spy(VASTClient.prototype, 'getVASTResponse');
      player.trigger('vast.firstPlay');
      this.clock.tick();
      callback = testUtils.secondArg(getVASTResponse);
    });

    afterEach(function (done) {
      this.clock.restore();
      vastUtil.track.restore();
      getVASTResponse.restore();
      VASTIntegrator.prototype.playAd.restore();
      utilities._UA = old_UA;
      done();
    });

    it("must request the vastResponse", function () {
      sinon.assert.calledOnce(getVASTResponse);
      sinon.assert.calledWith(getVASTResponse, '/fake.ad.url');
    });

    it("must request the vastResponse using the adTagXML function if provided", function(){
      player = videojs(document.createElement('video'), {});
      player.vastClient({adTagXML: utilities.noop});
      player.trigger('vast.firstPlay');
      this.clock.tick();
      sinon.assert.calledWith(getVASTResponse, utilities.noop);
    });

    it("must track the vast response if there was an error retrieving the vast response", function () {
      assertTriggersTrackError(function () {
        callback(new VASTError('Foo VAST ERROR', 101));
      }, 'Foo VAST ERROR', 101);
    });

    it("must play the ad with the returned response", function () {
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      callback(null, response);

      this.clock.tick();
      sinon.assert.calledWith(VASTIntegrator.prototype.playAd, response);
    });

    it("must not prevent manual progress if you play the ad on a no IDevice", function(){
      utilities._UA = "android";
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      sinon.stub(player, 'currentTime').returns(1);
      callback(null, response);
      this.clock.tick();
      player.trigger('timeupdate');
      this.clock.tick();
      player.currentTime.returns(10);
      player.trigger('timeupdate');
      this.clock.tick();
      sinon.assert.alwaysCalledWith(player.currentTime);
    });

    it("must prevent manual progress when you play the ad", function(){
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      sinon.stub(player, 'currentTime').returns(1);
      callback(null, response);
      this.clock.tick();
      player.trigger('timeupdate');
      this.clock.tick();
      player.currentTime.returns(10);
      player.trigger('timeupdate');
      this.clock.tick();
      sinon.assert.calledWithExactly(player.currentTime, 1);
    });

    it("must prevent ad skip if 'ended' event is triggered but ad wasn't finished", function(){
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      sinon.stub(player, 'duration').returns(10);
      sinon.stub(player, 'currentTime').returns(1);
      callback(null, response);
      this.clock.tick();
      player.trigger('timeupdate');
      this.clock.tick();
      player.trigger('ended');
      this.clock.tick();
      sinon.assert.calledWithExactly(player.currentTime, 1);
    });

    it("must NOT prevent ad skip if 'ended' event is triggered and Ad is finished", function(){
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      sinon.stub(player, 'duration').returns(10);
      sinon.stub(player, 'currentTime').returns(1);
      callback(null, response);
      this.clock.tick();
      player.trigger('timeupdate');
      this.clock.tick();
      player.currentTime.returns(3);
      player.trigger('timeupdate');
      this.clock.tick();
      player.currentTime.returns(5);
      player.trigger('timeupdate');
      this.clock.tick();
      player.currentTime.returns(7);
      player.trigger('timeupdate');
      this.clock.tick();
      player.currentTime.returns(9);
      player.trigger('timeupdate');
      this.clock.tick();
      player.currentTime.returns(10);
      player.trigger('timeupdate');
      this.clock.tick();
      player.trigger('ended');
      this.clock.tick();
      // we expect player.currentTime to have never been called with params
      sinon.assert.neverCalledWith(player.currentTime, sinon.match.defined);
    });


    it("must pause the play if the user tries to skip the ad manually twice", function(){
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      sinon.stub(player, 'currentTime').returns(1);
      callback(null, response);
      this.clock.tick();
      sinon.spy(player, 'pause');
      player.trigger('timeupdate');
      this.clock.tick();
      player.currentTime.returns(10);
      player.trigger('timeupdate');
      this.clock.tick();
      sinon.assert.notCalled(player.pause);
      player.currentTime.returns(10);
      player.trigger('timeupdate');
      this.clock.tick();
      sinon.assert.called(player.pause);
      player.pause.reset();
      player.trigger('timeupdate');
      this.clock.tick();
      sinon.assert.called(player.pause);
    });

    it("must not prevent the manual progress after the ad has ended", function(){
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      sinon.stub(player, 'currentTime').returns(1);
      var setCurrentTime = player.currentTime.withArgs(1);
      callback(null, response);
      this.clock.tick();
      player.trigger('timeupdate');
      this.clock.tick();
      player.trigger('vast.adEnd');
      this.clock.tick();
      player.currentTime.returns(10);
      player.trigger('timeupdate');
      this.clock.tick();
      sinon.assert.notCalled(setCurrentTime);
    });

    it("must not prevent the manual progress after the ad has been canceled", function(){
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      sinon.stub(player, 'currentTime').returns(1);
      var setCurrentTime = player.currentTime.withArgs(1);
      callback(null, response);
      this.clock.tick();
      player.trigger('timeupdate');
      this.clock.tick();
      player.trigger('vast.adsCancel');
      this.clock.tick();
      player.currentTime.returns(10);
      player.trigger('timeupdate');
      this.clock.tick();
      sinon.assert.notCalled(setCurrentTime);
    });

    it("must add the adsLabel component once we know the ad is going to start. (i.e. vast.adstart)", function () {
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      callback(null, response);
      this.clock.tick();
      player.trigger('vast.adStart');
      assert.isObject(player.controlBar.getChild('AdsLabel'));
    });

    it("must NOT add the adsLabel component if the ad gets canceled. (i.e. vast.adstart)", function () {
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      callback(null, response);
      this.clock.tick();
      player.trigger('vast.adsCancel');
      player.trigger('vast.adStart');
      assert.isUndefined(player.controlBar.getChild('AdsLabel'));
    });

    it("must NOT add the adsLabel component if there is an error in the player. (i.e. vast.adstart)", function () {
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      callback(null, response);
      this.clock.tick();
      player.trigger('error');
      player.trigger('vast.adStart');
      assert.isUndefined(player.controlBar.getChild('AdsLabel'));
    });

    it("must remove the adsLabel component when the ads finish playing", function () {
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      callback(null, response);
      this.clock.tick();
      player.trigger('vast.adStart');
      var playAdCallback = testUtils.secondArg(VASTIntegrator.prototype.playAd);
      playAdCallback(null, response);
      this.clock.tick();
      assert.isNull(player.controlBar.getChild('AdsLabel'));
    });

    it("must remove the adsLabel component on 'vast.adsCancel' event", function () {
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      callback(null, response);
      this.clock.tick();
      player.trigger('vast.adStart');
      player.trigger('vast.adsCancel');

      this.clock.tick();
      assert.isNull(player.controlBar.getChild('AdsLabel'));
    });

    it("must not ad the adsLabel if the ad has finished playing", function(){
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      callback(null, response);
      this.clock.tick();
      player.trigger('vast.adsCancel');
      player.trigger('vast.adStart');
      assert.isUndefined(player.controlBar.getChild('AdsLabel'));
    });

    it("must track the error if there as a problem playing the ad", function () {
      var response = new VASTResponse();
      var clock = this.clock;
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      callback(null, response);
      clock.tick();
      assertTriggersTrackError(function () {
        player.trigger('error');
        clock.tick();
      }, 'on VASTIntegrator, Player is unable to play the Ad');
    });

    it("must not play the ad if the ad was previously canceled due to an adCancelTimeout", function () {
      var adstartSpy = sinon.spy();
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);

      player.trigger('vast.firstPlay');
      //We force the adCancelTimeout
      this.clock.tick(3001);
      player.on('vast.adStart', adstartSpy);
      callback(null, response);
      this.clock.tick();
      sinon.assert.notCalled(adstartSpy);
    });

    it("must remove the adUnit and restore the video content after the ad has finished playing", function(){
      sinon.stub(playerUtils, 'restorePlayerSnapshot');
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      callback(null, response);
      this.clock.tick();
      player.trigger('vast.adStart');
      var playAdCallback = testUtils.secondArg(VASTIntegrator.prototype.playAd);
      assert.isNotNull(player.vast.adUnit);

      playAdCallback(null, response);
      this.clock.tick();
      assert.isNull(player.vast.adUnit);

      sinon.assert.calledOnce(playerUtils.restorePlayerSnapshot);
      playerUtils.restorePlayerSnapshot.restore();
    });

    it("must remove the adUnit and restore the video content on adsCancel", function(){
      sinon.stub(playerUtils, 'restorePlayerSnapshot');
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);

      callback(null, response);
      this.clock.tick();
      player.trigger('vast.adStart');
      assert.isNotNull(player.vast.adUnit);

      player.trigger('vast.adsCancel');
      assert.isNull(player.vast.adUnit);

      sinon.assert.calledOnce(playerUtils.restorePlayerSnapshot);
      playerUtils.restorePlayerSnapshot.restore();
    });

    it("must publish the adUnit on the player.vast obj on 'vast.adStart' evt", function(){
      var response = new VASTResponse();
      response._addMediaFiles([
        createMediaFile('http://fakeVideoFile', 'video/mp4')
      ]);
      callback(null, response);
      this.clock.tick();
      player.trigger('vast.adStart');
      assert.equal(player.vast.adUnit.type, 'VAST');
    });
  });

  describe("on iPhone", function(){
    beforeEach(function(){
      this.clock = sinon.useFakeTimers();
      sinon.stub(utilities, 'isIPhone').returns(true);
    });

    afterEach(function(done){
      utilities.isIPhone.restore();
      this.clock.tick();
      this.clock.restore();
      done();
    });

    it("must not play the ad if the video content has played more than what specified on the iosPrerollCancelTimeout and must track the error", function(){
      var player = videojs(document.createElement('video'), {});
      var errorSpy = sinon.spy();

      sinon.stub(player, 'currentTime').returns(2000);
      player.on('vast.adError', errorSpy);

      player.vastClient({adTagUrl: 'http://fake.ad.url', iosPrerollCancelTimeout: 1000});
      player.trigger('vast.firstPlay');
      this.clock.tick();
      sinon.assert.calledOnce(errorSpy);
      assert.equal(testUtils.firstArg(errorSpy).error.message, 'VAST Error: video content has been playing before preroll ad');
    });

    it("must play the ad if the video content has played less than what specified on the iosPrerollCancelTimeout", function(){
      var player = videojs(document.createElement('video'), {});
      var errorSpy = sinon.spy();
      sinon.stub(player, 'currentTime').returns(500);
      player.on('vast.adError', errorSpy);
      player.vastClient({adTagUrl: 'http://fake.ad.url', iosPrerollCancelTimeout: 1000});
      player.trigger('vast.firstPlay');
      this.clock.tick();
      sinon.assert.notCalled(errorSpy);
    });
  });
});