1
0
mirror of https://github.com/videojs/video.js.git synced 2024-12-04 10:34:51 +02:00
video.js/test/unit/autoplay.test.js
Roman Pougatchev f9fb1d3f51
fix: throw error on muted resolution rejection during autoplay (#7293)
Previously, when autoplay was set to any or muted, we would accidentally swallow the autoplay rejection when we reset the muted state back to what it was. Instead, we want to re-throw the error.
To get it working, we also had to update our tests to try/catch in our fake promise.
2021-06-30 12:28:41 -04:00

481 lines
17 KiB
JavaScript

/* eslint-env qunit */
import Player from '../../src/js/player.js';
import videojs from '../../src/js/video.js';
import TestHelpers from './test-helpers.js';
import document from 'global/document';
import window from 'global/window';
import sinon from 'sinon';
QUnit.module('autoplay', {
beforeEach() {
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];
}
const videoTag = TestHelpers.makeTag();
const fixture = document.getElementById('qunit-fixture');
this.counts = {
play: 0,
muted: 0,
success: 0,
failure: 0
};
fixture.appendChild(videoTag);
// These mock promises immediately execute,
// effectively synchronising promise chains for testing
// This will only act on catch calls
this.rejectPromise = {
then(fn) {
return this;
},
catch(fn) {
try {
fn();
} catch (err) {
return this;
}
return this;
}
};
// This will only act on then calls
this.resolvePromise = {
then(fn) {
fn();
return this;
},
catch(fn) {
return this;
}
};
this.createPlayer = (options = {}, attributes = {}, playRetval = null) => {
Object.keys(attributes).forEach((a) => {
videoTag.setAttribute(a, attributes[a]);
});
this.player = videojs(videoTag.id, videojs.mergeOptions({techOrder: ['techFaker']}, options));
const oldMuted = this.player.muted;
this.player.play = () => {
this.counts.play++;
if (playRetval || this.playRetval) {
return playRetval || this.playRetval;
}
};
this.mutedValue = this.player.muted();
this.player.muted = (v) => {
if (typeof v !== 'undefined') {
this.counts.muted++;
this.mutedValue = v;
}
return oldMuted.call(this.player, v);
};
this.player.on('autoplay-success', () => this.counts.success++);
this.player.on('autoplay-failure', () => this.counts.failure++);
// we have to trigger ready so that we
// are waiting for loadstart
this.player.tech_.triggerReady();
return this.player;
};
},
afterEach() {
this.clock.restore();
this.player.dispose();
}
});
QUnit.test('option = false no play/muted', function(assert) {
this.createPlayer({autoplay: false});
assert.equal(this.player.autoplay(), false, 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), false, 'tech.autoplay getter');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 0, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 0, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
});
QUnit.test('option = true no play/muted', function(assert) {
this.createPlayer({autoplay: true});
assert.equal(this.player.autoplay(), true, 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), true, 'tech.autoplay getter');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 0, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 0, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
});
QUnit.test('option = "random" no play/muted', function(assert) {
this.createPlayer({autoplay: 'random'});
assert.equal(this.player.autoplay(), true, 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), true, 'tech.autoplay getter');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 0, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 0, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
});
QUnit.test('option = null, should be set to false no play/muted', function(assert) {
this.createPlayer({autoplay: null});
assert.equal(this.player.autoplay(), false, 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), false, 'tech.autoplay getter');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 0, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 0, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
});
QUnit.test('option = "play" play, no muted', function(assert) {
this.createPlayer({autoplay: 'play'}, {}, this.resolvePromise);
assert.equal(this.player.autoplay(), 'play', 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), false, 'tech.autoplay getter');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 1, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 1, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 2, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 2, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
});
QUnit.test('option = true w/ normalizeAutoplay = true play, no muted', function(assert) {
this.createPlayer({
autoplay: true,
normalizeAutoplay: true
}, {}, this.resolvePromise);
assert.equal(this.player.autoplay(), true, 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), false, 'tech.autoplay getter');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 1, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 1, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 2, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 2, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
});
QUnit.test('option = "any" play, no muted', function(assert) {
this.createPlayer({autoplay: 'any'}, {}, this.resolvePromise);
assert.equal(this.player.autoplay(), 'any', 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), false, 'tech.autoplay getter');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 1, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 1, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 2, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 2, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
});
QUnit.test('option = "muted" play and muted', function(assert) {
this.createPlayer({autoplay: 'muted'}, {}, this.resolvePromise);
assert.equal(this.player.autoplay(), 'muted', 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), false, 'tech.autoplay getter');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 1, 'play count');
assert.equal(this.counts.muted, 1, 'muted count');
assert.equal(this.counts.success, 1, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 2, 'play count');
assert.equal(this.counts.muted, 2, 'muted count');
assert.equal(this.counts.success, 2, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
});
QUnit.test('option = "play" play, no muted, rejection ignored', function(assert) {
this.createPlayer({autoplay: 'play'}, {}, this.rejectPromise);
assert.equal(this.player.autoplay(), 'play', 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), false, 'tech.autoplay getter');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 1, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 1, 'failure count');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 2, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 2, 'failure count');
});
QUnit.test('option = "any" play, no muted, rejection leads to muted then play', function(assert) {
this.createPlayer({autoplay: 'any'}, {}, this.rejectPromise);
assert.equal(this.player.autoplay(), 'any', 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), false, 'tech.autoplay getter');
// The workflow described here:
// Call play() -> on rejection, attempt to set mute to true ->
// call play() again -> on rejection, set original mute value ->
// catch failure at the end of promise chain
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 2, 'play count');
assert.equal(this.counts.muted, 2, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 1, 'failure count');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 4, 'play count');
assert.equal(this.counts.muted, 4, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 2, 'failure count');
});
QUnit.test('option = "muted" play and muted, rejection ignored', function(assert) {
this.createPlayer({autoplay: 'muted'}, {}, this.rejectPromise);
assert.equal(this.player.autoplay(), 'muted', 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), false, 'tech.autoplay getter');
// muted called twice here, as muted is value is restored on failure.
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 1, 'play count');
assert.equal(this.counts.muted, 2, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 1, 'failure count');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 2, 'play count');
assert.equal(this.counts.muted, 4, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 2, 'failure count');
});
QUnit.test('option = "muted", attr = true, play and muted', function(assert) {
this.createPlayer({autoplay: 'muted'}, {autoplay: true});
assert.equal(this.player.autoplay(), true, 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), true, 'tech.autoplay getter');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 0, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 0, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
});
QUnit.test('option = "play", attr = true, play only', function(assert) {
this.createPlayer({autoplay: 'play'}, {autoplay: true});
assert.equal(this.player.autoplay(), true, 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), true, 'tech.autoplay getter');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 0, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 0, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
});
QUnit.test('option = "any", attr = true, play only', function(assert) {
this.createPlayer({autoplay: 'any'}, {autoplay: true});
assert.equal(this.player.autoplay(), true, 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), true, 'tech.autoplay getter');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 0, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 0, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
});
QUnit.test('option = "any", play terminated restores muted', function(assert) {
this.createPlayer({autoplay: 'any'});
this.playRetval = {
then(fn) {
fn();
return this;
},
catch: (fn) => {
assert.equal(this.counts.play, 1, 'play count');
assert.equal(this.counts.muted, 0, 'muted count');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
this.playRetval = {
then(_fn) {
window.setTimeout(_fn, 1);
return this;
},
catch(_fn) {
return this;
}
};
const retval = fn();
assert.equal(this.counts.play, 2, 'play count');
assert.equal(this.counts.muted, 1, 'muted count');
assert.equal(this.mutedValue, true, 'is muted');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
return retval;
}
};
assert.equal(this.player.autoplay(), 'any', 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), false, 'tech.autoplay getter');
assert.equal(this.mutedValue, false, 'is not muted');
this.player.tech_.trigger('loadstart');
this.player.runPlayTerminatedQueue_();
assert.equal(this.counts.play, 2, 'play count');
assert.equal(this.counts.muted, 2, 'muted count');
assert.equal(this.mutedValue, false, 'is not muted');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
this.player.runPlayTerminatedQueue_();
assert.equal(this.counts.play, 2, 'play count');
assert.equal(this.counts.muted, 2, 'muted count');
assert.equal(this.mutedValue, false, 'is not muted');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
// verify autoplay success
this.clock.tick(1);
assert.equal(this.counts.play, 2, 'play count');
assert.equal(this.counts.muted, 2, 'muted count');
assert.equal(this.counts.success, 1, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
});
QUnit.test('option = "muted", play terminated restores muted', function(assert) {
this.createPlayer({autoplay: 'muted'}, {}, {
then(fn) {
window.setTimeout(() => {
fn();
}, 1);
return this;
},
catch(fn) {
return this;
}
});
assert.equal(this.player.autoplay(), 'muted', 'player.autoplay getter');
assert.equal(this.player.tech_.autoplay(), false, 'tech.autoplay getter');
this.player.tech_.trigger('loadstart');
assert.equal(this.counts.play, 1, 'play count');
assert.equal(this.counts.muted, 1, 'muted count');
assert.equal(this.mutedValue, true, 'is muted');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
this.player.runPlayTerminatedQueue_();
assert.equal(this.counts.play, 1, 'play count');
assert.equal(this.counts.muted, 2, 'muted count');
assert.equal(this.mutedValue, false, 'no longer muted');
assert.equal(this.counts.success, 0, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
// verify autoplay success
this.clock.tick(1);
assert.equal(this.counts.play, 1, 'play count');
assert.equal(this.counts.muted, 2, 'muted count');
assert.equal(this.counts.success, 1, 'success count');
assert.equal(this.counts.failure, 0, 'failure count');
});