/* eslint-env qunit */ import Player from '../../src/js/player.js'; import Html5 from '../../src/js/tech/html5.js'; // eslint-disable-line no-unused-vars import TestHelpers from './test-helpers.js'; import sinon from 'sinon'; import window from 'global/window'; import document from 'global/document'; const FullscreenTestHelpers = { makePlayer(prefixed, playerOptions, videoTag) { const player = TestHelpers.makePlayer(playerOptions, videoTag); player.fsApi_ = { prefixed, requestFullscreen: 'vjsRequestFullscreen', exitFullscreen: 'vjsExitFullscreen', fullscreenElement: 'vjsFullscreenElement', fullscreenEnabled: 'vjsFullscreenEnabled', fullscreenchange: 'vjsfullscreenchange', fullscreenerror: 'vjsfullscreenerror', fullscreen: 'vjsfullscreen' }; return player; }, fakeSafariVideoEl() { const testEl = document.createElement('video'); if (!('webkitPresentationMode' in testEl)) { testEl.webkitPresentationMode = 'test'; } if (!('webkitDisplayingFullscreen' in testEl)) { testEl.webkitDisplayingFullscreen = false; } return testEl; } }; QUnit.module('Player Fullscreen', { beforeEach(assert) { this.clock = sinon.useFakeTimers(); // reset players storage for (const playerId in Player.players) { if (Player.players[playerId] !== null) { Player.players[playerId].dispose(); } delete Player.players[playerId]; } window.Element.prototype.vjsRequestFullscreen = function() { assert.ok(false, 'vjsRequestFullscreen should not be called'); }; window.Element.prototype.vjsExitFullscreen = function() { assert.ok(false, 'vjsExitFullscreen should not be called'); }; window.Element.prototype.vjsFullscreenElement = function() { assert.ok(false, 'vjsFullscreenElement should not be called'); }; window.Element.prototype.vjsFullscreenEnabled = function() { assert.ok(false, 'vjsFullscreenEnabled should not be called'); }; window.Element.prototype.vjsfullscreenchange = function() { assert.ok(false, 'vjsfullscreenchange should not be called'); }; window.Element.prototype.vjsfullscreenerror = function() { assert.ok(false, 'vjsfullscreenerror should not be called'); }; }, afterEach() { this.clock.restore(); delete window.Element.prototype.vjsRequestFullscreen; delete window.Element.prototype.vjsExitFullscreen; delete window.Element.prototype.vjsFullscreenElement; delete window.Element.prototype.vjsFullscreenEnabled; delete window.Element.prototype.vjsfullscreenchange; delete window.Element.prototype.vjsfullscreenerror; } }); QUnit.test('fullscreenOptions should not be passed from player options on prefixed api', function(assert) { const fullscreenOptions = { navigationUI: 'test', foo: 'bar' }; const player = FullscreenTestHelpers.makePlayer(true, { fullscreen: { options: fullscreenOptions } }); let requestFullscreenCalled = false; let fsOpts; window.Element.prototype.vjsRequestFullscreen = function(opts) { requestFullscreenCalled = true; fsOpts = opts; }; player.requestFullscreen(); assert.ok(requestFullscreenCalled, 'vjsRequestFullscreen should be called'); assert.strictEqual(fsOpts, undefined, 'fullscreenOptions should not be passed'); player.dispose(); }); QUnit.test('fullscreenOptions should be passed from player options on unprefixed api', function(assert) { const fullscreenOptions = { navigationUI: 'test', foo: 'bar' }; const player = FullscreenTestHelpers.makePlayer(false, { fullscreen: { options: fullscreenOptions } }); let requestFullscreenCalled = false; let fsOpts; window.Element.prototype.vjsRequestFullscreen = function(opts) { requestFullscreenCalled = true; fsOpts = opts; }; player.requestFullscreen(); assert.ok(requestFullscreenCalled, 'vjsRequestFullscreen should be called'); assert.notStrictEqual(fsOpts, undefined, 'fullscreenOptions should be passed'); assert.deepEqual(fsOpts, fullscreenOptions, 'fullscreenOptions should match player options'); player.dispose(); }); QUnit.test('fullscreenOptions should not be passed from function arguments on prefixed api', function(assert) { const fullscreenOptions = { navigationUI: 'test', foo: 'bar' }; const player = FullscreenTestHelpers.makePlayer(true); let requestFullscreenCalled = false; let fsOpts; window.Element.prototype.vjsRequestFullscreen = function(opts) { requestFullscreenCalled = true; fsOpts = opts; }; player.requestFullscreen(fullscreenOptions); assert.ok(requestFullscreenCalled, 'vjsRequestFullscreen should be called'); assert.strictEqual(fsOpts, undefined, 'fullscreenOptions should not be passed'); player.dispose(); }); QUnit.test('fullscreenOptions should be passed from function arguments on unprefixed api', function(assert) { const fullscreenOptions = { navigationUI: 'test', foo: 'bar' }; const player = FullscreenTestHelpers.makePlayer(false); let requestFullscreenCalled = false; let fsOpts; window.Element.prototype.vjsRequestFullscreen = function(opts) { requestFullscreenCalled = true; fsOpts = opts; }; player.requestFullscreen(fullscreenOptions); assert.ok(requestFullscreenCalled, 'vjsRequestFullscreen should be called'); assert.notStrictEqual(fsOpts, undefined, 'fullscreenOptions should be passed'); assert.deepEqual(fsOpts, fullscreenOptions, 'fullscreenOptions should match function args'); player.dispose(); }); QUnit.test('fullscreenOptions from function args should override player options', function(assert) { const fullscreenOptions = { navigationUI: 'args', baz: 'bar' }; const player = FullscreenTestHelpers.makePlayer(false, { fullscreen: { options: { navigationUI: 'playeroptions', foo: 'bar' } } }); let requestFullscreenCalled = false; let fsOpts; window.Element.prototype.vjsRequestFullscreen = function(opts) { requestFullscreenCalled = true; fsOpts = opts; }; player.requestFullscreen(fullscreenOptions); assert.ok(requestFullscreenCalled, 'vjsRequestFullscreen should be called'); assert.notStrictEqual(fsOpts, undefined, 'fullscreenOptions should be passed'); assert.deepEqual(fsOpts, fullscreenOptions, 'fullscreenOptions should match function args'); player.dispose(); }); QUnit.test('full window can be preferred to fullscreen tech', function(assert) { const player = FullscreenTestHelpers.makePlayer(false, { preferFullWindow: true }); player.fsApi_ = {}; player.tech_.supportsFullScreen = () => true; player.requestFullscreen(); assert.strictEqual(player.isFullscreen(), true, 'player considered fullscreen'); assert.strictEqual(player.isFullWindow, true, 'player is full window'); player.exitFullscreen(); assert.strictEqual(player.isFullWindow, false, 'full window is exited'); player.dispose(); }); QUnit.test('fullscreen mode should exit picture-in-picture if it was enabled', function(assert) { const player = FullscreenTestHelpers.makePlayer(false, { preferFullWindow: true }); const fakeExitPictureInPicture = sinon.replace(player, 'exitPictureInPicture', sinon.fake(() => {})); player.fsApi_ = {}; player.tech_.supportsFullScreen = () => true; assert.strictEqual(player.isFullscreen(), false, 'player should not be fullscreen initially'); player.isInPictureInPicture(true); player.trigger('enterpictureinpicture'); assert.strictEqual(player.isInPictureInPicture(), true, 'player is in picture-in-picture'); assert.strictEqual(fakeExitPictureInPicture.called, false, 'should not have called exitPictureInPicture yet'); player.requestFullscreen(); assert.strictEqual(player.isFullscreen(), true, 'player should be fullscreen'); assert.strictEqual(fakeExitPictureInPicture.called, true, 'should have called exitPictureInPicture'); player.dispose(); }); QUnit.test('fullwindow mode should exit when ESC event triggered', function(assert) { const player = TestHelpers.makePlayer(); player.enterFullWindow(); assert.ok(player.isFullWindow, 'enterFullWindow should be called'); const evt = TestHelpers.createEvent('keydown'); evt.key = 'Escape'; evt.keyCode = 27; evt.which = 27; player.boundFullWindowOnEscKey_(evt); // player.fullWindowOnEscKey(evt); assert.equal(player.isFullWindow, false, 'exitFullWindow should be called'); player.dispose(); }); QUnit.test('fullscreenchange event from Html5 should change player.isFullscreen_', function(assert) { const player = FullscreenTestHelpers.makePlayer(false); const html5 = player.tech(true); // simulate html5.proxyWebkitFullscreen_ html5.trigger('fullscreenchange', { isFullscreen: true, nativeIOSFullscreen: true }); assert.ok(player.isFullscreen(), 'player.isFullscreen_ should be true'); html5.trigger('fullscreenchange', { isFullscreen: false }); assert.ok(!player.isFullscreen(), 'player.isFullscreen_ should be false'); player.dispose(); }); QUnit.test('fullscreenchange event from Html5 should guard against Safari showing double controls', function(assert) { const player = FullscreenTestHelpers.makePlayer(undefined, {techOrder: ['html5']}, FullscreenTestHelpers.fakeSafariVideoEl()); const html5 = player.tech(true); html5.trigger('webkitbeginfullscreen'); assert.ok(player.isFullscreen(), 'player.isFullscreen_ should be true'); player.tech_.el_.controls = true; html5.trigger('webkitendfullscreen'); assert.ok(!player.tech_.el_.controls, 'el controls should be false'); player.dispose(); }); QUnit.test('Safari leaving fullscreen should retain controls with nativeControlsForTouch', function(assert) { const player = FullscreenTestHelpers.makePlayer(undefined, {techOrder: ['html5'], nativeControlsForTouch: true}, FullscreenTestHelpers.fakeSafariVideoEl()); const html5 = player.tech(true); html5.trigger('webkitbeginfullscreen'); assert.ok(player.isFullscreen(), 'player.isFullscreen_ should be true'); player.tech_.el_.controls = true; html5.trigger('webkitendfullscreen'); assert.ok(player.tech_.el_.controls, 'el controls should be true'); player.dispose(); }); QUnit.test('fullscreenerror event from Html5 should pass through player', function(assert) { const player = FullscreenTestHelpers.makePlayer(false); const html5 = player.tech(true); const err = new Error('This is test'); let fullscreenerror; player.on('fullscreenerror', function(evt, error) { fullscreenerror = error; }); html5.trigger('fullscreenerror', err); assert.strictEqual(fullscreenerror, err); player.dispose(); }); // only run where we have sinon.promise const skipOrTest = sinon.promise ? 'test' : 'skip'; QUnit[skipOrTest]('requestFullscreen returns a rejected promise if unable to go fullscreen', function(assert) { const player = TestHelpers.makePlayer(); const playerEl = player.el(); const stub = sinon.stub(playerEl, player.fsApi_.requestFullscreen); const promise = sinon.promise(); stub.returns(promise); promise.reject(new Error('Cannot go fullscreen')); assert.rejects( player.requestFullscreen(), new Error('Cannot go fullscreen'), 'our promise was rejected' ); stub.restore(); }); QUnit[skipOrTest]('requestFullscreen returns a resolved promise if we were fullscreen', function(assert) { const player = TestHelpers.makePlayer(); const playerEl = player.el(); const stub = sinon.stub(playerEl, player.fsApi_.requestFullscreen); const promise = sinon.promise(); stub.returns(promise); // pretend we successfully went fullscreen. promise.resolve(); player.requestFullscreen().then(() => assert.ok(true, 'our promise resolved')); stub.restore(); }); QUnit[skipOrTest]('exitFullscreen returns a rejected promise if document is not active', function(assert) { const player = TestHelpers.makePlayer(); const stub = sinon.stub(document, player.fsApi_.exitFullscreen); const promise = sinon.promise(); stub.returns(promise); promise.reject(new Error('Document not active')); assert.rejects( player.exitFullscreen(), new Error('Document not active'), 'our promise was rejected' ); stub.restore(); }); QUnit[skipOrTest]('exitFullscreen returns a resolved promise if we were fullscreen', function(assert) { const player = TestHelpers.makePlayer(); const stub = sinon.stub(document, player.fsApi_.exitFullscreen); const promise = sinon.promise(); stub.returns(promise); // pretend we successfully exited. promise.resolve(); player.exitFullscreen().then(() => assert.ok(true, 'our promise resolved')); stub.restore(); });