diff options
Diffstat (limited to 'javascript/videojs/test/unit/live-tracker.test.js')
| -rw-r--r-- | javascript/videojs/test/unit/live-tracker.test.js | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/javascript/videojs/test/unit/live-tracker.test.js b/javascript/videojs/test/unit/live-tracker.test.js new file mode 100644 index 0000000..54cb393 --- /dev/null +++ b/javascript/videojs/test/unit/live-tracker.test.js @@ -0,0 +1,354 @@ +/* eslint-env qunit */ +import TestHelpers from './test-helpers.js'; +import {createTimeRanges} from '../../src/js/utils/time.js'; +import sinon from 'sinon'; + +QUnit.module('LiveTracker', () => { + QUnit.module('options', { + beforeEach() { + this.clock = sinon.useFakeTimers(); + }, + afterEach() { + this.player.dispose(); + this.clock.restore(); + } + }); + + QUnit.test('liveui true, trackingThreshold is met', function(assert) { + this.player = TestHelpers.makePlayer({liveui: true}); + this.player.seekable = () => createTimeRanges(0, 30); + + this.player.duration(Infinity); + + assert.ok(this.player.hasClass('vjs-liveui'), 'has vjs-liveui'); + assert.ok(this.player.liveTracker.isTracking(), 'is tracking'); + }); + + QUnit.test('liveui true, trackingThreshold is not met', function(assert) { + this.player = TestHelpers.makePlayer({liveui: true, liveTracker: {trackingThreshold: 31}}); + this.player.seekable = () => createTimeRanges(0, 30); + + this.player.duration(Infinity); + + assert.notOk(this.player.hasClass('vjs-liveui'), 'does not have vjs-iveui'); + assert.notOk(this.player.liveTracker.isTracking(), 'is not tracking'); + }); + + QUnit.test('liveui false, trackingThreshold is met', function(assert) { + this.player = TestHelpers.makePlayer({liveui: false}); + this.player.seekable = () => createTimeRanges(0, 30); + + this.player.duration(Infinity); + + assert.notOk(this.player.hasClass('vjs-liveui'), 'does not have vjs-liveui'); + assert.ok(this.player.liveTracker.isTracking(), 'is tracking'); + }); + + QUnit.test('liveui false, trackingThreshold is not met', function(assert) { + this.player = TestHelpers.makePlayer({liveui: false, liveTracker: {trackingThreshold: 31}}); + this.player.seekable = () => createTimeRanges(0, 30); + + this.player.duration(Infinity); + + assert.notOk(this.player.hasClass('vjs-liveui'), 'does not have vjs-liveui'); + assert.notOk(this.player.liveTracker.isTracking(), 'is not tracking'); + }); + + QUnit.module('start/stop', { + beforeEach() { + this.clock = sinon.useFakeTimers(); + + this.player = TestHelpers.makePlayer({liveui: true}); + this.player.seekable = () => createTimeRanges(0, 30); + + this.liveTracker = this.player.liveTracker; + }, + afterEach() { + this.player.dispose(); + this.clock.restore(); + } + }); + + QUnit.test('with durationchange and triggers liveedgechange', function(assert) { + let liveEdgeChange = 0; + + this.liveTracker.on('liveedgechange', () => { + liveEdgeChange++; + }); + assert.notOk(this.liveTracker.isTracking(), 'not started'); + + this.player.duration(Infinity); + assert.ok(this.liveTracker.isTracking(), 'started'); + assert.equal(liveEdgeChange, 1, 'liveedgechange fired'); + + this.player.duration(5); + assert.notOk(this.liveTracker.isTracking(), 'not started'); + assert.equal(liveEdgeChange, 2, 'liveedgechange fired when we stop tracking'); + + this.player.duration(Infinity); + assert.ok(this.liveTracker.isTracking(), 'started'); + assert.equal(liveEdgeChange, 3, 'liveedgechange fired again'); + }); + + QUnit.test('with canplay', function(assert) { + let duration = Infinity; + + this.player.seekable = () => createTimeRanges(0, 30); + this.player.duration = () => duration; + + assert.notOk(this.liveTracker.isTracking(), 'not started'); + + this.player.trigger('canplay'); + assert.ok(this.liveTracker.isTracking(), 'started'); + + // end the video by triggering a duration change so we toggle off the liveui + duration = 5; + this.player.trigger('durationchange'); + assert.notOk(this.liveTracker.isTracking(), 'not started'); + + // pretend we loaded a new source and we got a canplay + duration = Infinity; + this.player.trigger('canplay'); + assert.ok(this.liveTracker.isTracking(), 'started'); + }); + + QUnit.module('tracking', { + beforeEach() { + this.clock = sinon.useFakeTimers(); + + this.player = TestHelpers.makePlayer(); + + this.liveTracker = this.player.liveTracker; + this.player.seekable = () => createTimeRanges(0, 30); + this.player.paused = () => false; + this.player.duration(Infinity); + + this.liveEdgeChanges = 0; + + this.liveTracker.on('liveedgechange', () => { + this.liveEdgeChanges++; + }); + }, + afterEach() { + this.player.dispose(); + this.clock.restore(); + } + }); + + QUnit.test('Triggers liveedgechange when we fall behind and catch up', function(assert) { + this.liveTracker.options_.liveTolerance = 6; + this.player.seekable = () => createTimeRanges(0, 20); + this.player.trigger('timeupdate'); + this.player.currentTime = () => 14; + this.clock.tick(6000); + this.player.seekable = () => createTimeRanges(0, 26); + this.clock.tick(1000); + + assert.equal(this.liveEdgeChanges, 1, 'should have one live edge change'); + assert.ok(this.liveTracker.behindLiveEdge(), 'behind live edge'); + + this.player.currentTime = () => this.liveTracker.liveCurrentTime(); + this.clock.tick(30); + + assert.equal(this.liveEdgeChanges, 2, 'should have two live edge change'); + assert.ok(this.liveTracker.atLiveEdge(), 'at live edge'); + }); + + QUnit.test('is behindLiveEdge when paused', function(assert) { + this.liveTracker.options_.liveTolerance = 6; + this.player.seekable = () => createTimeRanges(0, 20); + this.player.trigger('timeupdate'); + this.player.currentTime = () => 20; + this.clock.tick(1000); + + assert.ok(this.liveTracker.atLiveEdge(), 'at live edge'); + + this.player.paused = () => true; + this.player.trigger('pause'); + + assert.equal(this.liveEdgeChanges, 1, 'should have one live edge change'); + assert.ok(this.liveTracker.behindLiveEdge(), 'behindLiveEdge live edge'); + }); + + QUnit.test('is behindLiveEdge when seeking behind liveTolerance with API', function(assert) { + this.liveTracker.options_.liveTolerance = 6; + this.player.seekable = () => createTimeRanges(0, 20); + this.player.trigger('timeupdate'); + this.player.currentTime = () => 20; + this.clock.tick(1000); + + assert.ok(this.liveTracker.atLiveEdge(), 'at live edge'); + + this.player.currentTime = () => 14; + this.player.trigger('seeked'); + + assert.equal(this.liveEdgeChanges, 1, 'should have one live edge change'); + assert.ok(this.liveTracker.behindLiveEdge(), 'behindLiveEdge live edge'); + }); + + QUnit.test('is behindLiveEdge when seeking >2s behind with ui', function(assert) { + this.liveTracker.options_.liveTolerance = 6; + this.player.seekable = () => createTimeRanges(0, 20); + this.player.trigger('timeupdate'); + this.player.currentTime = () => 20; + this.clock.tick(1000); + + assert.ok(this.liveTracker.atLiveEdge(), 'at live edge'); + + this.liveTracker.nextSeekedFromUser(); + this.player.currentTime = () => 17; + this.player.trigger('seeked'); + + assert.equal(this.liveEdgeChanges, 1, 'should have one live edge change'); + assert.ok(this.liveTracker.behindLiveEdge(), 'behindLiveEdge live edge'); + }); + + QUnit.test('pastSeekEnd should update when seekable changes', function(assert) { + assert.strictEqual(this.liveTracker.liveCurrentTime(), 30, 'liveCurrentTime is now 30'); + this.clock.tick(2010); + + assert.ok(this.liveTracker.pastSeekEnd() > 2, 'pastSeekEnd should be over 2s'); + + this.player.seekable = () => createTimeRanges(0, 2); + + this.clock.tick(30); + assert.strictEqual(this.liveTracker.pastSeekEnd(), 0.03, 'pastSeekEnd start at 0.03 again'); + assert.strictEqual(this.liveTracker.liveCurrentTime(), 2.03, 'liveCurrentTime is now 2.03'); + }); + + QUnit.test('can seek to live edge', function(assert) { + this.player.trigger('timeupdate'); + + this.player.seekable = () => createTimeRanges(0, 6); + this.liveTracker.options_.liveTolerance = 2; + let currentTime = 0; + + this.player.currentTime = (ct) => { + if (typeof ct !== 'undefined') { + currentTime = ct; + } + return 0; + }; + + this.clock.tick(6000); + + assert.ok(this.liveTracker.pastSeekEnd() > 2, 'pastSeekEnd should be over 2s'); + + this.liveTracker.seekToLiveEdge(); + + assert.equal(currentTime, this.liveTracker.liveCurrentTime(), 'should have seeked to liveCurrentTime'); + }); + + QUnit.test('does not seek to live edge if at live edge', function(assert) { + let pauseCalls = 0; + let playCalls = 0; + let currentTime = 0; + + this.player.currentTime = (ct) => { + if (typeof ct !== 'undefined') { + currentTime = ct; + } + return 0; + }; + + this.player.play = () => { + playCalls++; + }; + + this.player.pause = () => { + pauseCalls++; + }; + + this.liveTracker.seekToLiveEdge(); + assert.notOk(this.player.hasClass('vjs-waiting'), 'player should not be waiting'); + assert.equal(pauseCalls, 0, 'should not have called pause'); + + this.clock.tick(2010); + + assert.ok(this.liveTracker.pastSeekEnd() > 2, 'pastSeekEnd should be over 2s'); + this.player.seekable = () => createTimeRanges(0, 2); + + this.clock.tick(30); + assert.equal(currentTime, 0, 'should not have seeked to seekableEnd'); + assert.equal(playCalls, 0, 'should not have called play'); + }); + + QUnit.test('seeks to live edge on the first timeupdate after playback is requested', function(assert) { + sinon.spy(this.liveTracker, 'seekToLiveEdge'); + + // Begin live tracking. + this.player.duration(Infinity); + assert.ok(this.liveTracker.seekToLiveEdge.notCalled, 'seekToLiveEdge was not called yet'); + + this.player.trigger('play'); + assert.ok(this.liveTracker.seekToLiveEdge.notCalled, 'seekToLiveEdge was not called yet'); + + this.player.trigger('timeupdate'); + assert.ok(this.liveTracker.seekToLiveEdge.calledOnce, 'seekToLiveEdge was called'); + + this.player.trigger('timeupdate'); + assert.ok(this.liveTracker.seekToLiveEdge.calledOnce, 'seekToLiveEdge was not called on subsequent timeupdate'); + }); + + QUnit.test('does not seek to live edge on the first timeupdate after playback is requested if playback already began', function(assert) { + sinon.spy(this.liveTracker, 'seekToLiveEdge'); + + // Begin live tracking. + this.liveTracker.stopTracking(); + this.player.hasStarted = () => true; + this.liveTracker.startTracking(); + assert.ok(this.liveTracker.seekToLiveEdge.notCalled, 'seekToLiveEdge was not called'); + + this.player.trigger('play'); + this.player.trigger('playing'); + assert.ok(this.liveTracker.seekToLiveEdge.notCalled, 'seekToLiveEdge was not called'); + + this.player.trigger('timeupdate'); + assert.ok(this.liveTracker.seekToLiveEdge.notCalled, 'seekToLiveEdge was not called'); + + this.player.trigger('timeupdate'); + assert.ok(this.liveTracker.seekToLiveEdge.notCalled, 'seekToLiveEdge was not called'); + }); + + QUnit.test('single seekable, helpers should be correct', function(assert) { + // simple + this.player.seekable = () => createTimeRanges(10, 50); + assert.strictEqual(Math.round(this.liveTracker.liveWindow()), 40, 'liveWindow is ~40s'); + assert.strictEqual(this.liveTracker.seekableStart(), 10, 'seekableStart is 10s'); + assert.strictEqual(this.liveTracker.seekableEnd(), 50, 'seekableEnd is 50s'); + }); + + QUnit.test('multiple seekables, helpers should be correct', function(assert) { + // multiple + this.player.seekable = () => createTimeRanges([[0, 1], [2, 3], [4, 5]]); + assert.strictEqual(Math.round(this.liveTracker.liveWindow()), 5, 'liveWindow is ~5s'); + assert.strictEqual(this.liveTracker.seekableStart(), 0, 'seekableStart is 0s'); + assert.strictEqual(this.liveTracker.seekableEnd(), 5, 'seekableEnd is 5s'); + }); + + QUnit.test('single seekable with Infinity, helpers should be correct', function(assert) { + // single with Infinity + this.player.seekable = () => createTimeRanges(0, Infinity); + assert.strictEqual(this.liveTracker.liveWindow(), 0, 'liveWindow is Infinity'); + assert.strictEqual(this.liveTracker.seekableStart(), 0, 'seekableStart is 0s'); + assert.strictEqual(this.liveTracker.seekableEnd(), Infinity, 'seekableEnd is Infinity'); + }); + + QUnit.test('multiple seekables with Infinity, helpers should be correct', function(assert) { + // multiple with Infinity + this.player.seekable = () => createTimeRanges([[0, Infinity], [1, Infinity]]); + assert.strictEqual(this.liveTracker.liveWindow(), 0, 'liveWindow is Infinity'); + assert.strictEqual(this.liveTracker.seekableStart(), 0, 'seekableStart is 0s'); + assert.strictEqual(this.liveTracker.seekableEnd(), Infinity, 'seekableEnd is Infinity'); + }); + + QUnit.test('No seekables, helpers should be defaults', function(assert) { + // defaults + this.player.seekable = () => createTimeRanges(); + + assert.strictEqual(this.liveTracker.liveWindow(), 0, 'liveWindow is Infinity'); + assert.strictEqual(this.liveTracker.seekableStart(), 0, 'seekableStart is 0s'); + assert.strictEqual(this.liveTracker.seekableEnd(), Infinity, 'seekableEnd is Infinity'); + }); + +}); |
