You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

378 lines
10 KiB

import Flash from '../src/plugin';
import {createTimeRange} from 'video.js';
import document from 'global/document';
import window from 'global/window';
import sinon from 'sinon';
import QUnit from 'qunit';
// fake out the <object> interaction but leave all the other logic intact
class MockFlash extends Flash {
constructor() {
super({});
}
}
QUnit.module('Flash');
QUnit.test('Flash.canPlaySource', function(assert) {
const canPlaySource = Flash.canPlaySource;
// Supported
assert.ok(
canPlaySource({type: 'video/mp4; codecs=avc1.42E01E,mp4a.40.2' }, {}),
'codecs supported'
);
assert.ok(canPlaySource({type: 'video/mp4' }, {}), 'video/mp4 supported');
assert.ok(canPlaySource({type: 'video/x-flv' }, {}), 'video/x-flv supported');
assert.ok(canPlaySource({type: 'video/flv' }, {}), 'video/flv supported');
assert.ok(canPlaySource({type: 'video/m4v' }, {}), 'video/m4v supported');
assert.ok(canPlaySource({type: 'VIDEO/FLV' }, {}), 'capitalized mime type');
// Not supported
assert.ok(!canPlaySource({ type: 'video/webm; codecs="vp8, vorbis"' }, {}));
assert.ok(!canPlaySource({ type: 'video/webm' }, {}));
});
QUnit.test('currentTime', function(assert) {
const getCurrentTime = Flash.prototype.currentTime;
const setCurrentTime = Flash.prototype.setCurrentTime;
let seekingCount = 0;
let seeking = false;
let setPropVal;
let getPropVal;
let result;
// Mock out a Flash instance to avoid creating the swf object
const mockFlash = {
el_: {
/* eslint-disable camelcase */
vjs_setProperty(prop, val) {
setPropVal = val;
},
vjs_getProperty() {
return getPropVal;
}
/* eslint-enable camelcase */
},
seekable() {
return createTimeRange(5, 1000);
},
trigger(event) {
if (event === 'seeking') {
seekingCount++;
}
},
seeking() {
return seeking;
}
};
// Test the currentTime getter
getPropVal = 3;
result = getCurrentTime.call(mockFlash);
assert.equal(result, 3, 'currentTime is retreived from the swf element');
// Test the currentTime setter
setCurrentTime.call(mockFlash, 10);
assert.equal(setPropVal, 10, 'currentTime is set on the swf element');
assert.equal(seekingCount, 1, 'triggered seeking');
// Test current time while seeking
setCurrentTime.call(mockFlash, 20);
seeking = true;
result = getCurrentTime.call(mockFlash);
assert.equal(
result,
20,
'currentTime is retrieved from the lastSeekTarget while seeking'
);
assert.notEqual(
result,
getPropVal,
'currentTime is not retrieved from the element while seeking'
);
assert.equal(seekingCount, 2, 'triggered seeking');
// clamp seeks to seekable
setCurrentTime.call(mockFlash, 1001);
result = getCurrentTime.call(mockFlash);
assert.equal(result, mockFlash.seekable().end(0), 'clamped to the seekable end');
assert.equal(seekingCount, 3, 'triggered seeking');
setCurrentTime.call(mockFlash, 1);
result = getCurrentTime.call(mockFlash);
assert.equal(result, mockFlash.seekable().start(0), 'clamped to the seekable start');
assert.equal(seekingCount, 4, 'triggered seeking');
});
QUnit.test('dispose removes the object element even before ready fires', function(assert) {
// This test appears to test bad functionaly that was fixed
// so it's debateable whether or not it's useful
const dispose = Flash.prototype.dispose;
const mockFlash = new MockFlash();
const noop = function() {};
// Mock required functions for dispose
mockFlash.off = noop;
mockFlash.trigger = noop;
mockFlash.el_ = {};
dispose.call(mockFlash);
assert.strictEqual(mockFlash.el_, null, 'swf el is nulled');
});
QUnit.test('ready triggering before and after disposing the tech', function(assert) {
const checkReady = sinon.stub(Flash, 'checkReady');
const fixtureDiv = document.getElementById('qunit-fixture');
const playerDiv = document.createElement('div');
const techEl = document.createElement('div');
techEl.id = 'foo1234';
playerDiv.appendChild(techEl);
fixtureDiv.appendChild(playerDiv);
// Mock the swf element
techEl.tech = {
el() {
return techEl;
}
};
playerDiv.player = {
tech: techEl.tech
};
Flash.onReady(techEl.id);
assert.ok(checkReady.called, 'checkReady should be called before the tech is disposed');
// remove the tech el from the player div to simulate being disposed
playerDiv.removeChild(techEl);
Flash.onReady(techEl.id);
assert.ok(
!checkReady.calledTwice,
'checkReady should not be called after the tech is disposed'
);
Flash.checkReady.restore();
});
QUnit.test('should have the source handler interface', function(assert) {
assert.ok(Flash.registerSourceHandler, 'has the registerSourceHandler function');
});
QUnit.test('canPlayType should select the correct types to play', function(assert) {
const canPlayType = Flash.nativeSourceHandler.canPlayType;
assert.equal(canPlayType('video/flv'), 'maybe', 'should be able to play FLV files');
assert.equal(canPlayType('video/x-flv'), 'maybe', 'should be able to play x-FLV files');
assert.equal(canPlayType('video/mp4'), 'maybe', 'should be able to play MP4 files');
assert.equal(canPlayType('video/m4v'), 'maybe', 'should be able to play M4V files');
assert.equal(
canPlayType('video/ogg'),
'',
'should return empty string if it can not play the video'
);
});
QUnit.test('canHandleSource should be able to work with src objects without a type', function(assert) {
const canHandleSource = Flash.nativeSourceHandler.canHandleSource;
assert.equal(
'maybe',
canHandleSource({ src: 'test.video.mp4' }, {}),
'should guess that it is a mp4 video'
);
assert.equal(
'maybe',
canHandleSource({ src: 'test.video.m4v' }, {}),
'should guess that it is a m4v video'
);
assert.equal(
'maybe',
canHandleSource({ src: 'test.video.flv' }, {}),
'should guess that it is a flash video'
);
assert.equal(
'',
canHandleSource({ src: 'test.video.wgg' }, {}),
'should return empty string if it can not play the video'
);
});
QUnit.test('seekable', function(assert) {
const seekable = Flash.prototype.seekable;
let result;
const mockFlash = {
duration() {
return this.duration_;
}
};
// Test a normal duration
mockFlash.duration_ = 23;
result = seekable.call(mockFlash);
assert.equal(result.length, 1, 'seekable is non-empty');
assert.equal(result.start(0), 0, 'starts at zero');
assert.equal(result.end(0), mockFlash.duration_, 'ends at the duration');
// Test a zero duration
mockFlash.duration_ = 0;
result = seekable.call(mockFlash);
assert.equal(
result.length, mockFlash.duration_,
'seekable is empty with a zero duration'
);
});
QUnit.test('play after ended seeks to the beginning', function(assert) {
let plays = 0;
const seeks = [];
Flash.prototype.play.call({
el_: {
/* eslint-disable camelcase */
vjs_play() {
plays++;
}
/* eslint-enable camelcase */
},
ended() {
return true;
},
setCurrentTime(time) {
seeks.push(time);
}
});
assert.equal(plays, 1, 'called play on the SWF');
assert.equal(seeks.length, 1, 'seeked on play');
assert.equal(seeks[0], 0, 'seeked to the beginning');
});
QUnit.test('duration returns NaN, Infinity or duration according to the HTML standard', function(assert) {
const duration = Flash.prototype.duration;
let mockedDuration = -1;
let mockedReadyState = 0;
let result;
const mockFlash = {
el_: {
/* eslint-disable camelcase */
vjs_getProperty() {
return mockedDuration;
}
/* eslint-enable camelcase */
},
readyState() {
return mockedReadyState;
}
};
result = duration.call(mockFlash);
assert.ok(Number.isNaN(result), 'duration returns NaN when readyState equals 0');
mockedReadyState = 1;
result = duration.call(mockFlash);
assert.ok(
!Number.isFinite(result),
'duration returns Infinity when duration property is less then 0'
);
mockedDuration = 1;
result = duration.call(mockFlash);
assert.equal(
result,
1,
'duration returns duration property when readyState' +
' and duration property are both higher than 0'
);
});
QUnit.test('getVideoPlaybackQuality API exists', function(assert) {
const propertyCalls = [];
const videoPlaybackQuality = { test: 'test' };
const mockFlash = {
el_: {
/* eslint-disable camelcase */
vjs_getProperty(attr) {
propertyCalls.push(attr);
return videoPlaybackQuality;
}
/* eslint-enable camelcase */
}
};
assert.deepEqual(
Flash.prototype.getVideoPlaybackQuality.call(mockFlash),
videoPlaybackQuality,
'called to get property from flash'
);
assert.equal(propertyCalls.length, 1, 'only one property call');
assert.equal(
propertyCalls[0],
'getVideoPlaybackQuality',
'called for getVideoPlaybackQuality'
);
});
QUnit.test('getVideoPlaybackQuality uses best available creationTime', function(assert) {
const origPerformance = window.performance;
const origDate = window.Date;
const videoPlaybackQuality = {};
const mockFlash = {
el_: {
/* eslint-disable camelcase */
vjs_getProperty(attr) {
return videoPlaybackQuality;
}
/* eslint-enable camelcase */
}
};
window.performance = void 0;
assert.notOk(
Flash.prototype.getVideoPlaybackQuality.call(mockFlash).creationTime,
'no creationTime when no performance API available'
);
window.performance = {
timing: {}
};
assert.notOk(
Flash.prototype.getVideoPlaybackQuality.call(mockFlash).creationTime,
'no creationTime when performance API insufficient'
);
window.performance = {
now: () => 4
};
assert.equal(
Flash.prototype.getVideoPlaybackQuality.call(mockFlash).creationTime,
4,
'creationTime is performance.now when available'
);
window.Date = {
now: () => 10
};
window.performance = {
timing: {
navigationStart: 3
}
};
assert.equal(
Flash.prototype.getVideoPlaybackQuality.call(mockFlash).creationTime,
7,
'creationTime uses Date.now() - navigationStart when available'
);
window.performance.now = () => 4;
assert.equal(
Flash.prototype.getVideoPlaybackQuality.call(mockFlash).creationTime,
4,
'creationTime prioritizes performance.now when available'
);
window.Date = origDate;
window.performance = origPerformance;
});