1
0
mirror of https://github.com/videojs/video.js.git synced 2024-12-04 10:34:51 +02:00
video.js/test/unit/tech/middleware.test.js
Gary Katsevman 29a8ee1d60
fix: cache middleware instances per player (#4939)
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.
2018-02-12 17:30:27 -05:00

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();
});