mirror of
https://github.com/videojs/video.js.git
synced 2025-07-13 01:30:17 +02:00
fix: fire sourceset on initial source append (#5038)
In Chrome/Firefox/Safari appending a <source> element when the media element has no source, causes what we think of as a `sourceset`. These changes make our code actual fire that event.
This commit is contained in:
committed by
brandonocasey
parent
fdcae1b469
commit
9eb5de7ec7
@ -161,51 +161,6 @@ QUnit[qunitFn]('sourceset', function(hooks) {
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test('player.src({...}) one source', function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.player = videojs(this.mediaEl, {
|
||||
enableSourceset: true
|
||||
});
|
||||
this.player.one('sourceset', () => {
|
||||
validateSource(assert, this.player, [this.testSrc]);
|
||||
done();
|
||||
});
|
||||
|
||||
this.player.src(this.testSrc);
|
||||
});
|
||||
|
||||
QUnit.test('player.src({...}) preload auto', function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.mediaEl.setAttribute('preload', 'auto');
|
||||
this.player = videojs(this.mediaEl, {
|
||||
enableSourceset: true
|
||||
});
|
||||
|
||||
this.player.one('sourceset', () => {
|
||||
validateSource(assert, this.player, [this.testSrc]);
|
||||
done();
|
||||
});
|
||||
|
||||
this.player.src(this.testSrc);
|
||||
});
|
||||
|
||||
QUnit.test('player.src({...}) two sources', function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.player = videojs(this.mediaEl, {
|
||||
enableSourceset: true
|
||||
});
|
||||
|
||||
this.player.one('sourceset', () => {
|
||||
validateSource(assert, this.player, [this.sourceOne, this.sourceTwo]);
|
||||
done();
|
||||
});
|
||||
|
||||
this.player.src([this.sourceOne, this.sourceTwo]);
|
||||
});
|
||||
|
||||
QUnit.test('mediaEl.src = ...;', function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
@ -292,6 +247,320 @@ QUnit[qunitFn]('sourceset', function(hooks) {
|
||||
});
|
||||
}));
|
||||
|
||||
QUnit.module('source after player', (subhooks) => testTypes.forEach((testName) => {
|
||||
QUnit.module(testName, {
|
||||
beforeEach() {
|
||||
sinon.stub(log, 'error');
|
||||
|
||||
setupEnv(this, testName);
|
||||
},
|
||||
afterEach: setupAfterEach(1)
|
||||
});
|
||||
|
||||
QUnit.test('player.src({...}) one source', function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.player = videojs(this.mediaEl, {
|
||||
enableSourceset: true
|
||||
});
|
||||
this.player.one('sourceset', () => {
|
||||
validateSource(assert, this.player, [this.testSrc]);
|
||||
done();
|
||||
});
|
||||
|
||||
this.player.src(this.testSrc);
|
||||
});
|
||||
|
||||
QUnit.test('player.src({...}) preload auto', function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.mediaEl.setAttribute('preload', 'auto');
|
||||
this.player = videojs(this.mediaEl, {
|
||||
enableSourceset: true
|
||||
});
|
||||
|
||||
this.player.one('sourceset', () => {
|
||||
validateSource(assert, this.player, [this.testSrc]);
|
||||
done();
|
||||
});
|
||||
|
||||
this.player.src(this.testSrc);
|
||||
});
|
||||
|
||||
QUnit.test('player.src({...}) two sources', function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.player = videojs(this.mediaEl, {
|
||||
enableSourceset: true
|
||||
});
|
||||
|
||||
this.player.one('sourceset', () => {
|
||||
validateSource(assert, this.player, [this.sourceOne, this.sourceTwo]);
|
||||
done();
|
||||
});
|
||||
|
||||
this.player.src([this.sourceOne, this.sourceTwo]);
|
||||
});
|
||||
|
||||
QUnit.test('mediaEl.src = ...;', function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.player = videojs(this.mediaEl, {enableSourceset: true});
|
||||
|
||||
this.player.one('sourceset', (e) => {
|
||||
validateSource(assert, this.player, [this.testSrc]);
|
||||
done();
|
||||
});
|
||||
|
||||
this.player.tech_.el_.src = this.testSrc.src;
|
||||
});
|
||||
|
||||
QUnit.test('mediaEl.setAttribute("src", ...)"', function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.player = videojs(this.mediaEl, {enableSourceset: true});
|
||||
|
||||
this.player.one('sourceset', (e) => {
|
||||
validateSource(assert, this.player, [this.testSrc]);
|
||||
done();
|
||||
});
|
||||
|
||||
this.player.tech_.el_.setAttribute('src', this.testSrc.src);
|
||||
});
|
||||
|
||||
const appendTypes = [
|
||||
{name: 'appendChild', fn: (el, obj) => el.appendChild(obj)},
|
||||
{name: 'innerHTML', fn: (el, obj) => {el.innerHTML = obj.outerHTML;}}, // eslint-disable-line
|
||||
];
|
||||
|
||||
// ie does not support this and safari < 10 does not either
|
||||
if (window.Element.prototype.append) {
|
||||
appendTypes.push({name: 'append', fn: (el, obj) => el.append(obj)});
|
||||
}
|
||||
|
||||
if (window.Element.prototype.insertAdjacentHTML) {
|
||||
appendTypes.push({name: 'insertAdjacentHTML', fn: (el, obj) => el.insertAdjacentHTML('afterbegin', obj.outerHTML)});
|
||||
}
|
||||
|
||||
appendTypes.forEach((appendObj) => {
|
||||
|
||||
QUnit.test(`<source> one source through ${appendObj.name}`, function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.source = document.createElement('source');
|
||||
this.source.src = this.testSrc.src;
|
||||
this.source.type = this.testSrc.type;
|
||||
|
||||
this.player = videojs(this.mediaEl, {enableSourceset: true});
|
||||
|
||||
this.player.one('sourceset', (e) => {
|
||||
assert.equal(e.src, this.testSrc.src, 'source is as expected');
|
||||
done();
|
||||
});
|
||||
|
||||
// since the media el has no source, just appending will
|
||||
// change the source without calling load
|
||||
appendObj.fn(this.player.tech_.el_, this.source);
|
||||
});
|
||||
|
||||
QUnit.test(`<source> one source through ${appendObj.name} and load`, function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.totalSourcesets = 2;
|
||||
this.source = document.createElement('source');
|
||||
this.source.src = this.testSrc.src;
|
||||
this.source.type = this.testSrc.type;
|
||||
|
||||
this.player = videojs(this.mediaEl, {enableSourceset: true});
|
||||
|
||||
this.player.one('sourceset', (e1) => {
|
||||
assert.equal(e1.src, this.testSrc.src, 'event has expected source');
|
||||
|
||||
this.player.one('sourceset', (e2) => {
|
||||
assert.equal(e2.src, this.testSrc.src, 'second event has expected source');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
// since the media el has no source, just appending will
|
||||
// change the source without calling load
|
||||
appendObj.fn(this.player.tech_.el_, this.source);
|
||||
|
||||
// should fire an additional sourceset
|
||||
this.player.tech_.el_.load();
|
||||
});
|
||||
|
||||
QUnit.test(`one <source> through ${appendObj.name} and then mediaEl.src`, function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.totalSourcesets = 2;
|
||||
this.source = document.createElement('source');
|
||||
this.source.src = this.testSrc.src;
|
||||
this.source.type = this.testSrc.type;
|
||||
|
||||
this.player = videojs(this.mediaEl, {enableSourceset: true});
|
||||
|
||||
this.player.one('sourceset', (e) => {
|
||||
assert.equal(e.src, this.testSrc.src, 'source is as expected');
|
||||
|
||||
this.player.one('sourceset', (e2) => {
|
||||
validateSource(assert, this.player, [this.sourceOne]);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
// since the media el has no source, just appending will
|
||||
// change the source without calling load
|
||||
appendObj.fn(this.player.tech_.el_, this.source);
|
||||
|
||||
// should fire an additional sourceset
|
||||
this.player.tech_.el_.src = this.sourceOne.src;
|
||||
});
|
||||
|
||||
QUnit.test(`one <source> through ${appendObj.name} and then mediaEl.setAttribute`, function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.totalSourcesets = 2;
|
||||
this.source = document.createElement('source');
|
||||
this.source.src = this.testSrc.src;
|
||||
this.source.type = this.testSrc.type;
|
||||
|
||||
this.player = videojs(this.mediaEl, {enableSourceset: true});
|
||||
|
||||
this.player.one('sourceset', (e) => {
|
||||
assert.equal(e.src, this.testSrc.src, 'source is as expected');
|
||||
|
||||
this.player.one('sourceset', (e2) => {
|
||||
validateSource(assert, this.player, [this.sourceOne]);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
// since the media el has no source, just appending will
|
||||
// change the source without calling load
|
||||
appendObj.fn(this.player.tech_.el_, this.source);
|
||||
|
||||
// should fire an additional sourceset
|
||||
this.player.tech_.el_.setAttribute('src', this.sourceOne.src);
|
||||
});
|
||||
|
||||
QUnit.test(`mediaEl.src and then <source> through ${appendObj.name}`, function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.source = document.createElement('source');
|
||||
this.source.src = this.testSrc.src;
|
||||
this.source.type = this.testSrc.type;
|
||||
|
||||
this.player = videojs(this.mediaEl, {enableSourceset: true});
|
||||
|
||||
this.player.one('sourceset', (e) => {
|
||||
validateSource(assert, this.player, [this.sourceOne]);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
this.player.tech_.el_.src = this.sourceOne.src;
|
||||
|
||||
// should not fire sourceset
|
||||
appendObj.fn(this.player.tech_.el_, this.source);
|
||||
});
|
||||
|
||||
QUnit.test(`mediaEl.setAttribute and then <source> through ${appendObj.name}`, function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.source = document.createElement('source');
|
||||
this.source.src = this.testSrc.src;
|
||||
this.source.type = this.testSrc.type;
|
||||
|
||||
this.player = videojs(this.mediaEl, {enableSourceset: true});
|
||||
|
||||
this.player.one('sourceset', (e) => {
|
||||
validateSource(assert, this.player, [this.sourceOne]);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
this.player.tech_.el_.setAttribute('src', this.sourceOne.src);
|
||||
|
||||
// should not fire sourceset
|
||||
appendObj.fn(this.player.tech_.el_, this.source);
|
||||
});
|
||||
|
||||
QUnit.test(`<source> two sources through ${appendObj.name}`, function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.source = document.createElement('source');
|
||||
this.source.src = this.sourceOne.src;
|
||||
this.source.type = this.sourceOne.type;
|
||||
|
||||
this.source2 = document.createElement('source');
|
||||
this.source2.src = this.sourceTwo.src;
|
||||
this.source2.type = this.sourceTwo.type;
|
||||
|
||||
this.player = videojs(this.mediaEl, {enableSourceset: true});
|
||||
|
||||
this.player.one('sourceset', (e) => {
|
||||
assert.equal(e.src, this.sourceOne.src, 'source is as expected');
|
||||
done();
|
||||
});
|
||||
|
||||
// since the media el has no source, just appending will
|
||||
// change the source without calling load
|
||||
appendObj.fn(this.player.tech_.el_, this.source);
|
||||
|
||||
// this should not be in the source list or fire a sourceset
|
||||
appendObj.fn(this.player.tech_.el_, this.source2);
|
||||
});
|
||||
|
||||
QUnit.test(`set, remove, load, and set again through ${appendObj.name}`, function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.totalSourcesets = 2;
|
||||
this.source = document.createElement('source');
|
||||
this.source.src = this.sourceTwo.src;
|
||||
this.source.type = this.sourceTwo.type;
|
||||
|
||||
this.player = videojs(this.mediaEl, {enableSourceset: true});
|
||||
|
||||
this.player.one('sourceset', (e1) => {
|
||||
validateSource(assert, this.player, [this.sourceOne]);
|
||||
|
||||
this.player.one('sourceset', (e2) => {
|
||||
validateSource(assert, this.player, [this.sourceTwo], false);
|
||||
done();
|
||||
});
|
||||
|
||||
// reset to no source
|
||||
this.player.tech_.el_.removeAttribute('src');
|
||||
this.player.tech_.el_.load();
|
||||
|
||||
// since the media el has no source, just appending will
|
||||
// change the source without calling load
|
||||
appendObj.fn(this.player.tech_.el_, this.source);
|
||||
|
||||
});
|
||||
|
||||
this.player.tech_.el_.setAttribute('src', this.sourceOne.src);
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test('no source and load', function(assert) {
|
||||
const done = assert.async();
|
||||
|
||||
this.player = videojs(this.mediaEl, {enableSourceset: true});
|
||||
this.player.tech_.el_.load();
|
||||
|
||||
this.totalSourcesets = 0;
|
||||
|
||||
window.setTimeout(() => {
|
||||
assert.equal(this.sourcesets, 0, 'no sourceset');
|
||||
done();
|
||||
}, wait);
|
||||
});
|
||||
}));
|
||||
|
||||
QUnit.module('source change', (subhooks) => testTypes.forEach((testName) => {
|
||||
QUnit.module(testName, {
|
||||
beforeEach(assert) {
|
||||
@ -451,10 +720,10 @@ QUnit[qunitFn]('sourceset', function(hooks) {
|
||||
this.mediaEl.removeAttribute('src');
|
||||
|
||||
this.player.one('sourceset', (e1) => {
|
||||
assert.equal(e1.src, '', 'we got a sourceset with an empty src');
|
||||
assert.equal(e1.src, this.testSrc.src, 'we got a sourceset with the expected src');
|
||||
|
||||
this.player.one('sourceset', (e2) => {
|
||||
assert.equal(e2.src, '', 'we got a sourceset with an empty src');
|
||||
assert.equal(e2.src, this.sourceOne.src, 'we got a sourceset with the expected src');
|
||||
});
|
||||
|
||||
source.src = this.sourceOne.src;
|
||||
@ -474,10 +743,10 @@ QUnit[qunitFn]('sourceset', function(hooks) {
|
||||
source.type = this.sourceOne.type;
|
||||
|
||||
this.player.one('sourceset', (e1) => {
|
||||
assert.equal(e1.src, '', 'we got a sourceset with an empty src');
|
||||
assert.equal(e1.src, this.sourceOne.src, 'we got a sourceset with the expected src');
|
||||
|
||||
this.player.one('sourceset', (e2) => {
|
||||
assert.equal(e2.src, '', 'we got a sourceset with an empty src');
|
||||
assert.equal(e2.src, this.sourceTwo.src, 'we got a sourceset with the expected src');
|
||||
});
|
||||
});
|
||||
|
||||
@ -538,7 +807,7 @@ QUnit[qunitFn]('sourceset', function(hooks) {
|
||||
src: 'http://example.com/oceans.flv',
|
||||
type: 'video/flv'
|
||||
};
|
||||
let sourcesets = 0;
|
||||
const sourcesets = [];
|
||||
|
||||
class FakeFlash extends Html5 {
|
||||
static isSupported() {
|
||||
@ -563,38 +832,27 @@ QUnit[qunitFn]('sourceset', function(hooks) {
|
||||
techOrder: ['fakeFlash', 'html5']
|
||||
});
|
||||
|
||||
player.src(flashSrc);
|
||||
|
||||
player.ready(function() {
|
||||
// the first sourceset comes from our FakeFlash because it extends Html5 tech
|
||||
// which calls load() on dispose for various reasons
|
||||
player.one('sourceset', function(e1) {
|
||||
// ignore the first sourceset that gets called when disposing the original tech
|
||||
// the first sourceset ends up being the second source because when the first source is set
|
||||
// the tech isn't ready so we delay it, then the second source comes and the tech is ready
|
||||
// so it ends up being triggered immediately.
|
||||
player.on('sourceset', (e) => {
|
||||
sourcesets.push(e.src);
|
||||
|
||||
// the second sourceset ends up being the second source because when the first source is set
|
||||
// the tech isn't ready so we delay it, then the second source comes and the tech is ready
|
||||
// so it ends up being triggered immediately.
|
||||
player.one('sourceset', function(e2) {
|
||||
assert.equal(e2.src, sourceTwo.src, 'the second sourceset ends up being the second source');
|
||||
sourcesets++;
|
||||
if (sourcesets.length === 3) {
|
||||
assert.deepEqual([flashSrc.src, sourceTwo.src, sourceOne.src], sourcesets, 'sourceset as expected');
|
||||
|
||||
// now that the tech is ready, we will re-trigger the original sourceset event
|
||||
// and get the first source
|
||||
player.one('sourceset', function(e3) {
|
||||
assert.equal(e3.src, sourceOne.src, 'the third sourceset is the first source');
|
||||
sourcesets++;
|
||||
|
||||
assert.equal(sourcesets, 2, 'two sourcesets');
|
||||
player.dispose();
|
||||
delete Tech.techs_.FakeFlash;
|
||||
done();
|
||||
});
|
||||
});
|
||||
player.dispose();
|
||||
delete Tech.techs_.FakeFlash;
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
player.src(sourceOne);
|
||||
player.src(sourceTwo);
|
||||
});
|
||||
|
||||
player.src(flashSrc);
|
||||
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user