mirror of
https://github.com/videojs/video.js.git
synced 2024-12-04 10:34:51 +02:00
29a8ee1d60
Middleware factories currently get run each time a source is set. Because middleware are assocated with a player, the factories should only run once per player. This PR fixes it so that we associate a middleware instance with a middleware factory per player. Each time a player is disposed, we will clear the cache of the middleware instances for that player. Fixes #4677.
512 lines
13 KiB
JavaScript
512 lines
13 KiB
JavaScript
/* eslint-env qunit */
|
|
import * as middleware from '../../../src/js/tech/middleware.js';
|
|
import sinon from 'sinon';
|
|
import window from 'global/window';
|
|
|
|
QUnit.module('Middleware', {
|
|
beforeEach(assert) {
|
|
this.clock = sinon.useFakeTimers();
|
|
},
|
|
afterEach(assert) {
|
|
this.clock.restore();
|
|
}
|
|
});
|
|
|
|
QUnit.test('middleware can be added with the use method', function(assert) {
|
|
const myMw = {};
|
|
const mwFactory = () => myMw;
|
|
|
|
middleware.use('foo', mwFactory);
|
|
|
|
assert.equal(middleware.getMiddleware('foo').pop(), mwFactory, 'we are able to add middleware');
|
|
});
|
|
|
|
QUnit.test('middleware get iterates through the middleware array in the right order', function(assert) {
|
|
const cts = [];
|
|
const durs = [];
|
|
const foos = [];
|
|
const mws = [
|
|
{
|
|
currentTime(ct) {
|
|
cts.push(ct);
|
|
return ct * 2;
|
|
},
|
|
duration(dur) {
|
|
durs.push(dur);
|
|
return dur + 2;
|
|
},
|
|
foo(f) {
|
|
foos.push(f);
|
|
return f / 2;
|
|
}
|
|
},
|
|
{
|
|
currentTime(ct) {
|
|
cts.push(ct);
|
|
return ct + 2;
|
|
},
|
|
duration(dur) {
|
|
durs.push(dur);
|
|
return dur / 2;
|
|
},
|
|
foo(f) {
|
|
foos.push(f);
|
|
return f + 3;
|
|
}
|
|
}
|
|
];
|
|
const tech = {
|
|
currentTime(ct) {
|
|
return 5;
|
|
},
|
|
duration(dur) {
|
|
return 5;
|
|
},
|
|
foo(f) {
|
|
return 5;
|
|
}
|
|
};
|
|
|
|
const ct = middleware.get(mws, tech, 'currentTime');
|
|
const dur = middleware.get(mws, tech, 'duration');
|
|
const foo = middleware.get(mws, tech, 'foo');
|
|
|
|
const assertion = (actual, expected, actualArr, expectedArr, type) => {
|
|
assert.equal(actual, expected, `our middleware chain return currectly for ${type}`);
|
|
assert.deepEqual(actualArr, expectedArr, `we got called in the correct order for ${type}`);
|
|
};
|
|
|
|
assertion(ct, 14, cts, [5, 7], 'currentTime');
|
|
assertion(dur, 4.5, durs, [5, 2.5], 'duration');
|
|
assertion(foo, 4, foos, [5, 8], 'foo');
|
|
});
|
|
|
|
QUnit.test('middleware set iterates through the middleware array in the right order', function(assert) {
|
|
const cts = [];
|
|
const durs = [];
|
|
const foos = [];
|
|
const mws = [
|
|
{
|
|
currentTime(ct) {
|
|
cts.push(ct);
|
|
return ct * 2;
|
|
},
|
|
duration(dur) {
|
|
durs.push(dur);
|
|
return dur + 2;
|
|
},
|
|
foo(f) {
|
|
foos.push(f);
|
|
return f / 2;
|
|
}
|
|
},
|
|
{
|
|
currentTime(ct) {
|
|
cts.push(ct);
|
|
return ct + 2;
|
|
},
|
|
duration(dur) {
|
|
durs.push(dur);
|
|
return dur / 2;
|
|
},
|
|
foo(f) {
|
|
foos.push(f);
|
|
return f + 3;
|
|
}
|
|
}
|
|
];
|
|
const tech = {
|
|
currentTime(ct) {
|
|
cts.push(ct);
|
|
return ct / 2;
|
|
},
|
|
duration(dur) {
|
|
durs.push(dur);
|
|
return dur;
|
|
},
|
|
foo(f) {
|
|
foos.push(f);
|
|
return f;
|
|
}
|
|
};
|
|
|
|
const ct = middleware.set(mws, tech, 'currentTime', 10);
|
|
const dur = middleware.set(mws, tech, 'duration', 10);
|
|
const foo = middleware.set(mws, tech, 'foo', 10);
|
|
|
|
const assertion = (actual, expected, actualArr, expectedArr, type) => {
|
|
assert.equal(actual, expected, `our middleware chain return currectly for ${type}`);
|
|
assert.deepEqual(actualArr, expectedArr, `we got called in the correct order for ${type}`);
|
|
};
|
|
|
|
assertion(ct, 11, cts, [10, 20, 22], 'currentTime');
|
|
assertion(dur, 6, durs, [10, 12, 6], 'duration');
|
|
assertion(foo, 8, foos, [10, 5, 8], 'foo');
|
|
});
|
|
|
|
QUnit.test('middleware mediate iterates through the middleware array twice', function(assert) {
|
|
let playsToTech = 0;
|
|
let playsToPlayer = 0;
|
|
let techPlays = 0;
|
|
let techPlay;
|
|
let pv1;
|
|
let pv2;
|
|
let pc1;
|
|
let pc2;
|
|
|
|
const mws = [{
|
|
callPlay() {
|
|
playsToTech++;
|
|
},
|
|
play(cancelled, value) {
|
|
playsToPlayer++;
|
|
pv1 = value;
|
|
pc1 = cancelled;
|
|
}
|
|
}, {
|
|
callPlay() {
|
|
playsToTech++;
|
|
},
|
|
play(cancelled, value) {
|
|
playsToPlayer++;
|
|
pv2 = value;
|
|
pc2 = cancelled;
|
|
}
|
|
}];
|
|
const tech = {
|
|
play() {
|
|
techPlays++;
|
|
techPlay = {then: () => {}};
|
|
|
|
return techPlay;
|
|
}
|
|
};
|
|
|
|
const pp = middleware.mediate(mws, tech, 'play');
|
|
|
|
assert.equal(playsToTech, playsToPlayer, 'middleware got called the same number of times');
|
|
assert.equal(playsToTech, 2, 'both middleware got called before the tech');
|
|
assert.equal(techPlays, 1, 'the tech method only gets called once');
|
|
assert.equal(playsToPlayer, 2, 'both middleware got called after the tech');
|
|
|
|
assert.deepEqual(pv1.then, techPlay.then, 'the value returned by the tech is passed through the middleware');
|
|
assert.deepEqual(pv2, techPlay, 'the value returned by the tech is passed through the middleware');
|
|
assert.deepEqual(pp, techPlay, 'the value returned to the player is the value returned from the tech');
|
|
assert.equal(pc1, false, 'the play has not been cancelled in middleware 1');
|
|
assert.equal(pc2, false, 'the play has not been cancelled in middleware 2');
|
|
});
|
|
|
|
QUnit.test('middleware mediate allows and can detect cancellation', function(assert) {
|
|
let playsToTech = 0;
|
|
let playsToPlayer = 0;
|
|
let techPlays = 0;
|
|
let pv1;
|
|
let pv2;
|
|
let pc1;
|
|
let pc2;
|
|
|
|
const mws = [{
|
|
callPlay() {
|
|
playsToTech++;
|
|
},
|
|
play(cancelled, value) {
|
|
playsToPlayer++;
|
|
pv1 = value;
|
|
pc1 = cancelled;
|
|
}
|
|
}, {
|
|
callPlay() {
|
|
playsToTech++;
|
|
return middleware.TERMINATOR;
|
|
},
|
|
play(cancelled, value) {
|
|
playsToPlayer++;
|
|
pv2 = value;
|
|
pc2 = cancelled;
|
|
}
|
|
}];
|
|
const tech = {
|
|
play() {
|
|
techPlays++;
|
|
return {then: () => {}};
|
|
}
|
|
};
|
|
|
|
const pp = middleware.mediate(mws, tech, 'play');
|
|
|
|
assert.equal(playsToTech, 2, 'both middleware run until middleware terminates');
|
|
assert.equal(techPlays, 0, 'the tech should not be called if a middleware terminates');
|
|
assert.equal(playsToPlayer, 2, 'both middleware run after the tech');
|
|
|
|
assert.equal(pv1, null, 'null is returned through the middleware if a middleware terminated previously');
|
|
assert.equal(pv2, null, 'null is returned through the middleware if a middleware terminated previously');
|
|
assert.equal(pp, null, 'null is returned to the player if a middleware terminated previously');
|
|
assert.equal(pc1, true, 'the play has been cancelled in middleware 1');
|
|
assert.equal(pc2, true, 'the play has been cancelled in middleware 2');
|
|
});
|
|
|
|
QUnit.test('setSource is run asynchronously', function(assert) {
|
|
let src;
|
|
let acc;
|
|
|
|
middleware.setSource({
|
|
id() {
|
|
return 'vid1';
|
|
},
|
|
setTimeout: window.setTimeout
|
|
}, { src: 'foo', type: 'video/foo' }, function(_src, _acc) {
|
|
src = _src;
|
|
acc = _acc;
|
|
});
|
|
|
|
assert.equal(src, undefined, 'no src was returned yet');
|
|
assert.equal(acc, undefined, 'no accumulator was returned yet');
|
|
|
|
this.clock.tick(1);
|
|
|
|
assert.deepEqual(src, {src: 'foo', type: 'video/foo'}, 'we got the same source back');
|
|
assert.equal(acc.length, 0, 'we did not accumulate any middleware since there were none');
|
|
});
|
|
|
|
QUnit.test('setSource selects a source based on the middleware given', function(assert) {
|
|
let src;
|
|
let acc;
|
|
const mw = {
|
|
setSource(_src, next) {
|
|
next(null, {
|
|
src: 'http://example.com/video.mp4',
|
|
type: 'video/mp4'
|
|
});
|
|
}
|
|
};
|
|
const fooFactory = () => mw;
|
|
|
|
middleware.use('video/foo', fooFactory);
|
|
|
|
middleware.setSource({
|
|
id() {
|
|
return 'vid1';
|
|
},
|
|
setTimeout: window.setTimeout
|
|
}, {src: 'foo', type: 'video/foo'}, function(_src, _acc) {
|
|
src = _src;
|
|
acc = _acc;
|
|
});
|
|
|
|
this.clock.tick(1);
|
|
|
|
assert.equal(src.type, 'video/mp4', 'we selected a new type of video/mp4');
|
|
assert.equal(src.src, 'http://example.com/video.mp4', 'we selected a new src of video.mp4');
|
|
assert.equal(acc.length, 1, 'we got one middleware');
|
|
assert.equal(acc[0], mw, 'we chose the one middleware');
|
|
|
|
middleware.getMiddleware('video/foo').pop();
|
|
});
|
|
|
|
QUnit.test('setSource can select multiple middleware from multiple types', function(assert) {
|
|
let src;
|
|
let acc;
|
|
const foomw = {
|
|
setSource(_src, next) {
|
|
next(null, {
|
|
src: 'bar',
|
|
type: 'video/bar'
|
|
});
|
|
}
|
|
};
|
|
const barmw = {
|
|
setSource(_src, next) {
|
|
next(null, {
|
|
src: 'http://example.com/video.mp4',
|
|
type: 'video/mp4'
|
|
});
|
|
}
|
|
};
|
|
const fooFactory = () => foomw;
|
|
const barFactory = () => barmw;
|
|
|
|
middleware.use('video/foo', fooFactory);
|
|
middleware.use('video/bar', barFactory);
|
|
|
|
middleware.setSource({
|
|
id() {
|
|
return 'vid1';
|
|
},
|
|
setTimeout: window.setTimeout
|
|
}, {src: 'foo', type: 'video/foo'}, function(_src, _acc) {
|
|
src = _src;
|
|
acc = _acc;
|
|
});
|
|
|
|
this.clock.tick(1);
|
|
|
|
assert.equal(src.type, 'video/mp4', 'we selected a new type of video/mp4');
|
|
assert.equal(src.src, 'http://example.com/video.mp4', 'we selected a new src of video.mp4');
|
|
assert.equal(acc.length, 2, 'we got two middleware');
|
|
assert.equal(acc[0], foomw, 'foomw is the first middleware');
|
|
assert.equal(acc[1], barmw, 'barmw is the first middleware');
|
|
|
|
middleware.getMiddleware('video/foo').pop();
|
|
middleware.getMiddleware('video/bar').pop();
|
|
});
|
|
|
|
QUnit.test('setSource will select all middleware of a given type, until src change', function(assert) {
|
|
let src;
|
|
let acc;
|
|
const foomw1 = {
|
|
setSource(_src, next) {
|
|
next(null, {
|
|
src: 'bar',
|
|
type: 'video/foo'
|
|
});
|
|
}
|
|
};
|
|
const foomw2 = {
|
|
setSource(_src, next) {
|
|
next(null, {
|
|
src: 'http://example.com/video.mp4',
|
|
type: 'video/mp4'
|
|
});
|
|
}
|
|
};
|
|
const foomw3 = {
|
|
setSource(_src, next) {
|
|
next(null, {
|
|
src: 'http://example.com/video.mp4',
|
|
type: 'video/mp4'
|
|
});
|
|
}
|
|
};
|
|
const fooFactory1 = () => foomw1;
|
|
const fooFactory2 = () => foomw2;
|
|
const fooFactory3 = () => foomw3;
|
|
|
|
middleware.use('video/foo', fooFactory1);
|
|
middleware.use('video/foo', fooFactory2);
|
|
middleware.use('video/foo', fooFactory3);
|
|
|
|
middleware.setSource({
|
|
id() {
|
|
return 'vid1';
|
|
},
|
|
setTimeout: window.setTimeout
|
|
}, {src: 'foo', type: 'video/foo'}, function(_src, _acc) {
|
|
src = _src;
|
|
acc = _acc;
|
|
});
|
|
|
|
this.clock.tick(1);
|
|
|
|
assert.equal(src.type, 'video/mp4', 'we selected a new type of video/mp4');
|
|
assert.equal(src.src, 'http://example.com/video.mp4', 'we selected a new src of video.mp4');
|
|
assert.equal(acc.length, 2, 'we got two middleware');
|
|
assert.equal(acc[0], foomw1, 'foomw is the first middleware');
|
|
assert.equal(acc[1], foomw2, 'foomw is the first middleware');
|
|
|
|
middleware.getMiddleware('video/foo').pop();
|
|
middleware.getMiddleware('video/foo').pop();
|
|
});
|
|
|
|
QUnit.test('a middleware without a mediator method will not throw an error', function(assert) {
|
|
let pauseCalled = 0;
|
|
const myMw = {};
|
|
const mwFactory = () => myMw;
|
|
const mwFactory2 = () => ({
|
|
pause() {
|
|
pauseCalled++;
|
|
}
|
|
});
|
|
|
|
middleware.mediate([mwFactory(), mwFactory2()], {pause: () => {}}, 'pause');
|
|
|
|
assert.equal(pauseCalled, 1, 'pauseCalled was called once and no error was thrown');
|
|
});
|
|
|
|
QUnit.test('a middleware factory is not called on source change', function(assert) {
|
|
let mwfactoryCalled = 0;
|
|
const mw = {
|
|
setSource(_src, next) {
|
|
next(null, {
|
|
src: 'http://example.com/video.mp4',
|
|
type: 'video/mp4'
|
|
});
|
|
}
|
|
};
|
|
const fooFactory = () => {
|
|
mwfactoryCalled++;
|
|
return mw;
|
|
};
|
|
|
|
middleware.use('video/foo', fooFactory);
|
|
|
|
// set "initial" source"
|
|
middleware.setSource({
|
|
id() {
|
|
return 'vid1';
|
|
},
|
|
setTimeout: window.setTimeout
|
|
}, {src: 'foo', type: 'video/foo'}, function() {});
|
|
|
|
this.clock.tick(1);
|
|
|
|
assert.equal(mwfactoryCalled, 1, 'the factory was called once');
|
|
|
|
// "change" source
|
|
middleware.setSource({
|
|
id() {
|
|
return 'vid1';
|
|
},
|
|
setTimeout: window.setTimeout
|
|
}, {src: 'bar', type: 'video/foo'}, function() {});
|
|
|
|
this.clock.tick(1);
|
|
|
|
assert.equal(mwfactoryCalled, 1, 'the factory was not called again');
|
|
|
|
middleware.getMiddleware('video/foo').pop();
|
|
});
|
|
|
|
QUnit.test('a middleware factory is called on a new source with a new player', function(assert) {
|
|
let mwfactoryCalled = 0;
|
|
const mw = {
|
|
setSource(_src, next) {
|
|
next(null, {
|
|
src: 'http://example.com/video.mp4',
|
|
type: 'video/mp4'
|
|
});
|
|
}
|
|
};
|
|
const fooFactory = () => {
|
|
mwfactoryCalled++;
|
|
return mw;
|
|
};
|
|
|
|
middleware.use('video/foo', fooFactory);
|
|
|
|
// set "initial" source with player vid1
|
|
middleware.setSource({
|
|
id() {
|
|
return 'vid1';
|
|
},
|
|
setTimeout: window.setTimeout
|
|
}, {src: 'foo', type: 'video/foo'}, function() {});
|
|
|
|
this.clock.tick(1);
|
|
|
|
assert.equal(mwfactoryCalled, 1, 'the factory was called once');
|
|
|
|
// set "initial" source with player vid2
|
|
middleware.setSource({
|
|
id() {
|
|
return 'vid2';
|
|
},
|
|
setTimeout: window.setTimeout
|
|
}, {src: 'bar', type: 'video/foo'}, function() {});
|
|
|
|
this.clock.tick(1);
|
|
|
|
assert.equal(mwfactoryCalled, 2, 'the factory was called again');
|
|
|
|
middleware.getMiddleware('video/foo').pop();
|
|
});
|