2016-08-04 17:49:32 +02:00
/* eslint-env qunit */
2017-01-18 08:52:23 +02:00
import Plugin from '../../src/js/plugin' ;
2015-03-26 06:43:41 +02:00
import Player from '../../src/js/player.js' ;
2015-05-04 01:12:38 +02:00
import videojs from '../../src/js/video.js' ;
import * as Dom from '../../src/js/utils/dom.js' ;
import * as browser from '../../src/js/utils/browser.js' ;
import log from '../../src/js/utils/log.js' ;
2015-03-26 06:43:41 +02:00
import MediaError from '../../src/js/media-error.js' ;
2015-04-14 22:08:32 +02:00
import Html5 from '../../src/js/tech/html5.js' ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
import Tech from '../../src/js/tech/tech.js' ;
2015-03-26 06:43:41 +02:00
import TestHelpers from './test-helpers.js' ;
import document from 'global/document' ;
2016-08-04 17:49:32 +02:00
import sinon from 'sinon' ;
2015-12-07 23:27:33 +02:00
import window from 'global/window' ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
import * as middleware from '../../src/js/tech/middleware.js' ;
2018-12-26 20:03:36 +02:00
import * as Events from '../../src/js/utils/events.js' ;
2019-08-30 20:56:41 +02:00
import pkg from '../../package.json' ;
2019-09-05 21:20:06 +02:00
import * as Guid from '../../src/js/utils/guid.js' ;
2015-03-26 06:43:41 +02:00
2016-08-04 17:49:32 +02:00
QUnit . module ( 'Player' , {
2016-08-12 19:51:31 +02:00
beforeEach ( ) {
2014-12-03 21:31:39 +02:00
this . clock = sinon . useFakeTimers ( ) ;
2016-11-23 20:52:54 +02:00
// reset players storage
for ( const playerId in Player . players ) {
if ( Player . players [ playerId ] !== null ) {
Player . players [ playerId ] . dispose ( ) ;
}
delete Player . players [ playerId ] ;
}
2014-09-02 22:39:08 +03:00
} ,
2016-08-12 19:51:31 +02:00
afterEach ( ) {
2014-12-03 21:31:39 +02:00
this . clock . restore ( ) ;
2014-09-02 22:39:08 +03:00
}
} ) ;
2013-01-11 00:06:12 +03:00
2019-09-05 21:20:06 +02:00
QUnit . test ( 'the default ID of the first player remains "vjs_video_3"' , function ( assert ) {
Guid . resetGuidInTestsOnly ( ) ;
const tag = document . createElement ( 'video' ) ;
tag . className = 'video-js' ;
const player = TestHelpers . makePlayer ( { } , tag ) ;
assert . strictEqual ( player . id ( ) , 'vjs_video_3' , 'id is correct' ) ;
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should create player instance that inherits from component and dispose it' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( ) ;
2013-01-11 00:06:12 +03:00
2016-08-12 19:51:31 +02:00
assert . ok ( player . el ( ) . nodeName === 'DIV' ) ;
assert . ok ( player . on , 'component function exists' ) ;
2013-01-11 00:06:12 +03:00
player . dispose ( ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . el ( ) === null , 'element disposed' ) ;
2013-01-11 00:06:12 +03:00
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'dispose should not throw if styleEl is missing' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( ) ;
2016-01-14 21:01:30 +02:00
player . styleEl _ . parentNode . removeChild ( player . styleEl _ ) ;
player . dispose ( ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . el ( ) === null , 'element disposed' ) ;
2016-01-14 21:01:30 +02:00
} ) ;
2018-05-16 18:02:41 +02:00
QUnit . test ( 'dispose should not throw if techEl is missing' , function ( assert ) {
const videoTag = TestHelpers . makeTag ( ) ;
const fixture = document . getElementById ( 'qunit-fixture' ) ;
fixture . appendChild ( videoTag ) ;
const player = new Player ( videoTag ) ;
player . tech _ . el _ . parentNode . removeChild ( player . tech _ . el _ ) ;
player . tech _ . el _ = null ;
let error ;
try {
player . dispose ( ) ;
} catch ( e ) {
error = e ;
}
assert . notOk ( error , 'Function did not throw an error on dispose' ) ;
} ) ;
QUnit . test ( 'dispose should not throw if playerEl is missing' , function ( assert ) {
const videoTag = TestHelpers . makeTag ( ) ;
const fixture = document . getElementById ( 'qunit-fixture' ) ;
fixture . appendChild ( videoTag ) ;
const player = new Player ( videoTag ) ;
player . el _ . parentNode . removeChild ( player . el _ ) ;
player . el _ = null ;
let error ;
try {
player . dispose ( ) ;
} catch ( e ) {
error = e ;
}
assert . notOk ( error , 'Function did not throw an error on dispose' ) ;
} ) ;
2022-05-18 16:59:17 +02:00
QUnit . test ( 'dispose should replace playerEl with restoreEl' , function ( assert ) {
const videoTag = TestHelpers . makeTag ( ) ;
const fixture = document . getElementById ( 'qunit-fixture' ) ;
const replacement = document . createElement ( 'div' ) ;
fixture . appendChild ( videoTag ) ;
const player = new Player ( videoTag , { restoreEl : replacement } ) ;
player . dispose ( ) ;
assert . ok ( replacement . parentNode , fixture , 'Replacement node present after dispose' ) ;
} ) ;
2015-08-12 22:51:43 +02:00
// technically, all uses of videojs.options should be replaced with
// Player.prototype.options_ in this file and a equivalent test using
// videojs.options should be made in video.test.js. Keeping this here
// until we make that move.
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should accept options from multiple sources and override in correct order' , function ( assert ) {
2013-01-11 00:06:12 +03:00
// Set a global option
2015-08-12 22:51:43 +02:00
videojs . options . attr = 1 ;
2013-01-11 00:06:12 +03:00
2016-08-04 17:49:32 +02:00
const tag0 = TestHelpers . makeTag ( ) ;
const player0 = new Player ( tag0 , { techOrder : [ 'techFaker' ] } ) ;
2013-01-11 00:06:12 +03:00
2016-08-12 19:51:31 +02:00
assert . equal ( player0 . options _ . attr , 1 , 'global option was set' ) ;
2013-01-11 00:06:12 +03:00
player0 . dispose ( ) ;
// Set a tag level option
2016-08-04 17:49:32 +02:00
const tag2 = TestHelpers . makeTag ( ) ;
2013-01-11 00:06:12 +03:00
2016-08-04 17:49:32 +02:00
// Attributes must be set as strings
tag2 . setAttribute ( 'attr' , 'asdf' ) ;
const player2 = new Player ( tag2 , { techOrder : [ 'techFaker' ] } ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player2 . options _ . attr , 'asdf' , 'Tag options overrode global options' ) ;
2015-08-12 22:51:43 +02:00
player2 . dispose ( ) ;
2013-01-11 00:06:12 +03:00
// Set a tag level option
2016-08-04 17:49:32 +02:00
const tag3 = TestHelpers . makeTag ( ) ;
2015-08-12 22:51:43 +02:00
tag3 . setAttribute ( 'attr' , 'asdf' ) ;
2013-01-11 00:06:12 +03:00
2016-08-04 17:49:32 +02:00
const player3 = new Player ( tag3 , { techOrder : [ 'techFaker' ] , attr : 'fdsa' } ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player3 . options _ . attr , 'fdsa' , 'Init options overrode tag and global options' ) ;
2015-08-12 22:51:43 +02:00
player3 . dispose ( ) ;
2013-01-11 00:06:12 +03:00
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should get tag, source, and track settings' , function ( assert ) {
2017-01-19 23:01:56 +02:00
// Partially tested in lib->getAttributes
2013-01-11 00:06:12 +03:00
2016-08-04 17:49:32 +02:00
const fixture = document . getElementById ( 'qunit-fixture' ) ;
2013-01-11 00:06:12 +03:00
2016-08-04 17:49:32 +02:00
let html = '<video id="example_1" class="video-js" autoplay preload="none">' ;
html += '<source src="http://google.com" type="video/mp4">' ;
html += '<source src="http://google.com" type="video/webm">' ;
html += '<track kind="captions" attrtest>' ;
html += '</video>' ;
2013-01-11 00:06:12 +03:00
fixture . innerHTML += html ;
2016-08-04 17:49:32 +02:00
const tag = document . getElementById ( 'example_1' ) ;
const player = TestHelpers . makePlayer ( { } , tag ) ;
2013-01-11 00:06:12 +03:00
2016-08-12 19:51:31 +02:00
assert . equal ( player . options _ . autoplay , true , 'autoplay is set to true' ) ;
assert . equal ( player . options _ . preload , 'none' , 'preload is set to none' ) ;
assert . equal ( player . options _ . id , 'example_1' , 'id is set to example_1' ) ;
assert . equal ( player . options _ . sources . length , 2 , 'we have two sources' ) ;
assert . equal ( player . options _ . sources [ 0 ] . src , 'http://google.com' , 'first source is google.com' ) ;
feat: Queue playback events when the playback rate is zero and we are seeking (#5024)
SourceHandlers that use MSE have a problem: if they push a segment into a SourceBuffer and then seek close to the end, playback will stall and/or there will be a massive downswitch in quality. The general approach to fixing this that was discussed on slack was by setting the playback rate of the player to zero, buffering all that was required, and then restoring the previous playback rate. In my implementation, I've done this in the source handler (see: videojs/videojs-contrib-hls#1374).
From the video.js perspective, it should ensure that the UI reflects the buffering status and that the player API behaves like you'd expect -- that is to say, that it will fire seeking immediately after a call to currentTime, and it will fire seeked, canplay, canplaythrough, and playing when everything is buffered.
2018-04-17 21:28:05 +02:00
assert . equal ( player . options _ . sources [ 0 ] . type , 'video/mp4' , 'first type is video/mp4' ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player . options _ . sources [ 1 ] . type , 'video/webm' , 'second type is video/webm' ) ;
assert . equal ( player . options _ . tracks . length , 1 , 'we have one text track' ) ;
assert . equal ( player . options _ . tracks [ 0 ] . kind , 'captions' , 'the text track is a captions file' ) ;
assert . equal ( player . options _ . tracks [ 0 ] . attrtest , '' , 'we have an empty attribute called attrtest' ) ;
2016-04-19 23:09:53 +02:00
2016-08-12 19:51:31 +02:00
assert . notEqual ( player . el ( ) . className . indexOf ( 'video-js' ) , - 1 , 'transferred class from tag to player div' ) ;
assert . equal ( player . el ( ) . id , 'example_1' , 'transferred id from tag to player div' ) ;
2016-04-19 23:09:53 +02:00
2016-08-12 19:51:31 +02:00
assert . equal ( Player . players [ player . id ( ) ] , player , 'player referenceable from global list' ) ;
assert . notEqual ( tag . id , player . id , 'tag ID no longer is the same as player ID' ) ;
assert . notEqual ( tag . className , player . el ( ) . className , 'tag classname updated' ) ;
2013-01-11 00:06:12 +03:00
player . dispose ( ) ;
2016-08-12 19:51:31 +02:00
assert . notEqual ( tag . player , player , 'tag player ref killed' ) ;
assert . ok ( ! Player . players . example _1 , 'global player ref killed' ) ;
assert . equal ( player . el ( ) , null , 'player el killed' ) ;
2012-12-11 03:40:12 +03:00
} ) ;
2016-11-03 21:50:55 +02:00
QUnit . test ( 'should get current source from source tag' , function ( assert ) {
const fixture = document . getElementById ( 'qunit-fixture' ) ;
const html = [
'<video id="example_1" class="video-js" preload="none">' ,
'<source src="http://google.com" type="video/mp4">' ,
'<source src="http://hugo.com" type="video/webm">' ,
'</video>'
] . join ( '' ) ;
fixture . innerHTML += html ;
const tag = document . getElementById ( 'example_1' ) ;
const player = TestHelpers . makePlayer ( { } , tag ) ;
assert . ok ( player . currentSource ( ) . src === 'http://google.com' ) ;
assert . ok ( player . currentSource ( ) . type === 'video/mp4' ) ;
2016-12-02 21:17:36 +02:00
player . dispose ( ) ;
2016-11-03 21:50:55 +02:00
} ) ;
QUnit . test ( 'should get current sources from source tag' , function ( assert ) {
const fixture = document . getElementById ( 'qunit-fixture' ) ;
const html = [
'<video id="example_1" class="video-js" preload="none">' ,
'<source src="http://google.com" type="video/mp4">' ,
'<source src="http://hugo.com" type="video/webm">' ,
'</video>'
] . join ( '' ) ;
fixture . innerHTML += html ;
const tag = document . getElementById ( 'example_1' ) ;
const player = TestHelpers . makePlayer ( { } , tag ) ;
assert . ok ( player . currentSources ( ) [ 0 ] . src === 'http://google.com' ) ;
assert . ok ( player . currentSources ( ) [ 0 ] . type === 'video/mp4' ) ;
assert . ok ( player . currentSources ( ) [ 1 ] . src === 'http://hugo.com' ) ;
assert . ok ( player . currentSources ( ) [ 1 ] . type === 'video/webm' ) ;
// when redefining src expect sources to update accordingly
player . src ( 'http://google.com' ) ;
assert . ok ( player . currentSources ( ) [ 0 ] . src === 'http://google.com' ) ;
assert . ok ( player . currentSources ( ) [ 0 ] . type === undefined ) ;
assert . ok ( player . currentSources ( ) [ 1 ] === undefined ) ;
2016-12-02 21:17:36 +02:00
player . dispose ( ) ;
2016-11-03 21:50:55 +02:00
} ) ;
QUnit . test ( 'should get current source from src set' , function ( assert ) {
const fixture = document . getElementById ( 'qunit-fixture' ) ;
const html = '<video id="example_1" class="video-js" preload="none"></video>' ;
fixture . innerHTML += html ;
const tag = document . getElementById ( 'example_1' ) ;
const player = TestHelpers . makePlayer ( { } , tag ) ;
player . loadTech _ ( 'Html5' ) ;
// check for matching undefined src
assert . deepEqual ( player . currentSource ( ) , { } ) ;
2017-11-01 17:12:48 +02:00
assert . equal ( player . src ( ) , '' ) ;
2016-11-03 21:50:55 +02:00
player . src ( 'http://google.com' ) ;
assert . ok ( player . currentSource ( ) . src === 'http://google.com' ) ;
assert . ok ( player . currentSource ( ) . type === undefined ) ;
player . src ( {
src : 'http://google.com'
} ) ;
assert . ok ( player . currentSource ( ) . src === 'http://google.com' ) ;
assert . ok ( player . currentSource ( ) . type === undefined ) ;
player . src ( {
src : 'http://google.com' ,
type : 'video/mp4'
} ) ;
assert . ok ( player . currentSource ( ) . src === 'http://google.com' ) ;
assert . ok ( player . currentSource ( ) . type === 'video/mp4' ) ;
2016-12-02 21:17:36 +02:00
player . dispose ( ) ;
2016-11-03 21:50:55 +02:00
} ) ;
QUnit . test ( 'should get current sources from src set' , function ( assert ) {
const fixture = document . getElementById ( 'qunit-fixture' ) ;
const html = '<video id="example_1" class="video-js" preload="none"></video>' ;
fixture . innerHTML += html ;
const tag = document . getElementById ( 'example_1' ) ;
const player = TestHelpers . makePlayer ( { } , tag ) ;
player . loadTech _ ( 'Html5' ) ;
// check for matching undefined src
assert . ok ( player . currentSources ( ) , [ ] ) ;
player . src ( [ {
src : 'http://google.com'
} , {
src : 'http://hugo.com'
} ] ) ;
assert . ok ( player . currentSources ( ) [ 0 ] . src === 'http://google.com' ) ;
assert . ok ( player . currentSources ( ) [ 0 ] . type === undefined ) ;
assert . ok ( player . currentSources ( ) [ 1 ] . src === 'http://hugo.com' ) ;
assert . ok ( player . currentSources ( ) [ 1 ] . type === undefined ) ;
player . src ( [ {
src : 'http://google.com' ,
type : 'video/mp4'
} , {
src : 'http://hugo.com' ,
type : 'video/webm'
} ] ) ;
assert . ok ( player . currentSources ( ) [ 0 ] . src === 'http://google.com' ) ;
assert . ok ( player . currentSources ( ) [ 0 ] . type === 'video/mp4' ) ;
assert . ok ( player . currentSources ( ) [ 1 ] . src === 'http://hugo.com' ) ;
assert . ok ( player . currentSources ( ) [ 1 ] . type === 'video/webm' ) ;
// when redefining src expect sources to update accordingly
player . src ( 'http://hugo.com' ) ;
assert . ok ( player . currentSources ( ) [ 0 ] . src === 'http://hugo.com' ) ;
assert . ok ( player . currentSources ( ) [ 0 ] . type === undefined ) ;
assert . ok ( player . currentSources ( ) [ 1 ] === undefined ) ;
2016-12-02 21:17:36 +02:00
player . dispose ( ) ;
2016-11-03 21:50:55 +02:00
} ) ;
2021-06-08 17:03:51 +02:00
QUnit . test ( 'should remove autoplay attribute when normalizeAutoplay: true' , function ( assert ) {
const fixture = document . getElementById ( 'qunit-fixture' ) ;
let html = '<video id="example_1" class="video-js" autoplay preload="none">' ;
html += '<source src="http://google.com" type="video/mp4">' ;
html += '<source src="http://google.com" type="video/webm">' ;
html += '<track kind="captions" attrtest>' ;
html += '</video>' ;
fixture . innerHTML += html ;
const tag = document . getElementById ( 'example_1' ) ;
const player = TestHelpers . makePlayer ( { normalizeAutoplay : true } , tag ) ;
player . loadTech _ ( 'Html5' ) ;
assert . equal ( player . autoplay ( ) , true , 'autoplay option is set to true' ) ;
assert . equal ( tag . getAttribute ( 'autoplay' ) , null , 'autoplay attribute removed' ) ;
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should asynchronously fire error events during source selection' , function ( assert ) {
assert . expect ( 2 ) ;
2014-09-02 22:39:08 +03:00
2015-05-04 01:12:38 +02:00
sinon . stub ( log , 'error' ) ;
2014-09-02 22:39:08 +03:00
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( {
techOrder : [ 'foo' ] ,
sources : [
{ src : 'http://vjs.zencdn.net/v/oceans.mp4' , type : 'video/mp4' }
2014-09-02 22:39:08 +03:00
]
} ) ;
2016-08-04 17:49:32 +02:00
2016-08-12 19:51:31 +02:00
assert . ok ( player . options _ . techOrder [ 0 ] === 'foo' , 'Foo listed as the only tech' ) ;
2014-09-02 22:39:08 +03:00
player . on ( 'error' , function ( e ) {
2016-08-12 19:51:31 +02:00
assert . ok ( player . error ( ) . code === 4 , 'Source could not be played error thrown' ) ;
2014-09-02 22:39:08 +03:00
} ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
// The first one is for player initialization
// The second one is the setTimeout for triggering the error
this . clock . tick ( 1 ) ;
2014-12-03 21:31:39 +02:00
this . clock . tick ( 1 ) ;
2014-09-02 22:39:08 +03:00
player . dispose ( ) ;
2015-05-04 01:12:38 +02:00
log . error . restore ( ) ;
2014-09-02 22:39:08 +03:00
} ) ;
2022-08-08 21:17:24 +02:00
QUnit . test ( 'should retry setting source if error occurs' , function ( assert ) {
2021-03-23 23:50:12 +02:00
const player = TestHelpers . makePlayer ( {
techOrder : [ 'html5' ] ,
sources : [
{ src : 'http://vjs.zencdn.net/v/oceans.mp4' , type : 'video/mp4' } ,
{ src : 'http://vjs.zencdn.net/v/oceans2.mp4' , type : 'video/mp4' } ,
{ src : 'http://vjs.zencdn.net/v/oceans3.mp4' , type : 'video/mp4' }
]
} ) ;
assert . deepEqual (
player . currentSource ( ) ,
{ src : 'http://vjs.zencdn.net/v/oceans.mp4' , type : 'video/mp4' } ,
'first source set'
) ;
player . trigger ( 'error' ) ;
assert . deepEqual (
player . currentSource ( ) ,
{ src : 'http://vjs.zencdn.net/v/oceans2.mp4' , type : 'video/mp4' } ,
'second source set'
) ;
player . trigger ( 'error' ) ;
assert . deepEqual (
player . currentSource ( ) ,
{ src : 'http://vjs.zencdn.net/v/oceans3.mp4' , type : 'video/mp4' } ,
'last source set'
) ;
// No more sources to try so the previous source should remain
player . trigger ( 'error' ) ;
assert . deepEqual (
player . currentSource ( ) ,
{ src : 'http://vjs.zencdn.net/v/oceans3.mp4' , type : 'video/mp4' } ,
'last source remains'
) ;
assert . deepEqual (
player . currentSources ( ) ,
[
{ src : 'http://vjs.zencdn.net/v/oceans.mp4' , type : 'video/mp4' } ,
{ src : 'http://vjs.zencdn.net/v/oceans2.mp4' , type : 'video/mp4' } ,
{ src : 'http://vjs.zencdn.net/v/oceans3.mp4' , type : 'video/mp4' }
] ,
'currentSources() correctly returns the full source list'
) ;
player . dispose ( ) ;
} ) ;
2022-08-08 21:17:24 +02:00
QUnit . test ( 'should not retry setting source if error occurs during playback' , function ( assert ) {
2021-03-23 23:50:12 +02:00
const player = TestHelpers . makePlayer ( {
techOrder : [ 'html5' ] ,
sources : [
{ src : 'http://vjs.zencdn.net/v/oceans.mp4' , type : 'video/mp4' } ,
{ src : 'http://vjs.zencdn.net/v/oceans2.mp4' , type : 'video/mp4' } ,
{ src : 'http://vjs.zencdn.net/v/oceans3.mp4' , type : 'video/mp4' }
]
} ) ;
assert . deepEqual (
player . currentSource ( ) ,
{ src : 'http://vjs.zencdn.net/v/oceans.mp4' , type : 'video/mp4' } ,
'first source set'
) ;
player . trigger ( 'error' ) ;
assert . deepEqual (
player . currentSource ( ) ,
{ src : 'http://vjs.zencdn.net/v/oceans2.mp4' , type : 'video/mp4' } ,
'second source set'
) ;
// Playback starts then error occurs
player . trigger ( 'playing' ) ;
player . trigger ( 'error' ) ;
assert . deepEqual (
player . currentSource ( ) ,
{ src : 'http://vjs.zencdn.net/v/oceans2.mp4' , type : 'video/mp4' } ,
'second source remains'
) ;
assert . deepEqual (
player . currentSources ( ) ,
[
{ src : 'http://vjs.zencdn.net/v/oceans.mp4' , type : 'video/mp4' } ,
{ src : 'http://vjs.zencdn.net/v/oceans2.mp4' , type : 'video/mp4' } ,
{ src : 'http://vjs.zencdn.net/v/oceans3.mp4' , type : 'video/mp4' }
] ,
'currentSources() correctly returns the full source list'
) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'aborts and resets retryOnError behavior if new src() call made during a retry' , function ( assert ) {
const player = TestHelpers . makePlayer ( {
techOrder : [ 'html5' ] ,
sources : [
{ src : 'http://vjs.zencdn.net/v/oceans.mp4' , type : 'video/mp4' } ,
{ src : 'http://vjs.zencdn.net/v/oceans2.mp4' , type : 'video/mp4' } ,
{ src : 'http://vjs.zencdn.net/v/oceans3.mp4' , type : 'video/mp4' }
]
} ) ;
assert . deepEqual (
player . currentSource ( ) ,
{ src : 'http://vjs.zencdn.net/v/oceans.mp4' , type : 'video/mp4' } ,
'first source set'
) ;
player . trigger ( 'error' ) ;
assert . deepEqual (
player . currentSource ( ) ,
{ src : 'http://vjs.zencdn.net/v/oceans2.mp4' , type : 'video/mp4' } ,
'second source set'
) ;
// Setting a new source list should reset retry behavior and enable it for the new sources
player . src ( [
{ src : 'http://vjs.zencdn.net/v/newSource.mp4' , type : 'video/mp4' } ,
{ src : 'http://vjs.zencdn.net/v/newSource2.mp4' , type : 'video/mp4' } ,
{ src : 'http://vjs.zencdn.net/v/newSource3.mp4' , type : 'video/mp4' }
] ) ;
assert . deepEqual (
player . currentSource ( ) ,
{ src : 'http://vjs.zencdn.net/v/newSource.mp4' , type : 'video/mp4' } ,
'first new source set'
) ;
player . trigger ( 'error' ) ;
assert . deepEqual (
player . currentSource ( ) ,
{ src : 'http://vjs.zencdn.net/v/newSource2.mp4' , type : 'video/mp4' } ,
'second new source set'
) ;
player . trigger ( 'error' ) ;
assert . deepEqual (
player . currentSource ( ) ,
{ src : 'http://vjs.zencdn.net/v/newSource3.mp4' , type : 'video/mp4' } ,
'third new source set'
) ;
assert . deepEqual (
player . currentSources ( ) ,
[
{ src : 'http://vjs.zencdn.net/v/newSource.mp4' , type : 'video/mp4' } ,
{ src : 'http://vjs.zencdn.net/v/newSource2.mp4' , type : 'video/mp4' } ,
{ src : 'http://vjs.zencdn.net/v/newSource3.mp4' , type : 'video/mp4' }
] ,
'currentSources() correctly returns the full new source list'
) ;
player . dispose ( ) ;
} ) ;
2019-06-20 20:00:12 +02:00
QUnit . test ( 'should suppress source error messages' , function ( assert ) {
2019-08-07 23:36:55 +02:00
sinon . stub ( log , 'error' ) ;
2019-06-20 20:00:12 +02:00
const clock = sinon . useFakeTimers ( ) ;
const player = TestHelpers . makePlayer ( {
techOrder : [ 'foo' ] ,
suppressNotSupportedError : true
} ) ;
let errors = 0 ;
player . on ( 'error' , function ( e ) {
errors ++ ;
} ) ;
player . src ( { src : 'http://example.com' , type : 'video/mp4' } ) ;
clock . tick ( 10 ) ;
assert . strictEqual ( errors , 0 , 'no error on bad source load' ) ;
player . trigger ( 'click' ) ;
clock . tick ( 10 ) ;
assert . strictEqual ( errors , 1 , 'error after click' ) ;
player . dispose ( ) ;
2019-08-07 23:36:55 +02:00
assert . strictEqual ( log . error . callCount , 2 , 'two stubbed errors' ) ;
log . error . restore ( ) ;
2019-06-20 20:00:12 +02:00
} ) ;
QUnit . test ( 'should cancel a suppressed error message on loadstart' , function ( assert ) {
2019-08-07 23:36:55 +02:00
sinon . stub ( log , 'error' ) ;
2019-06-20 20:00:12 +02:00
const clock = sinon . useFakeTimers ( ) ;
const player = TestHelpers . makePlayer ( {
techOrder : [ 'foo' ] ,
suppressNotSupportedError : true
} ) ;
let errors = 0 ;
player . on ( 'error' , function ( e ) {
errors ++ ;
} ) ;
player . src ( { src : 'http://example.com' , type : 'video/mp4' } ) ;
clock . tick ( 10 ) ;
assert . strictEqual ( errors , 0 , 'no error on bad source load' ) ;
assert . strictEqual (
player . options _ . suppressNotSupportedError ,
false ,
'option was unset when error was suppressed'
) ;
player . trigger ( 'loadstart' ) ;
clock . tick ( 10 ) ;
player . trigger ( 'click' ) ;
clock . tick ( 10 ) ;
assert . strictEqual ( errors , 0 , 'no error after click after loadstart' ) ;
2019-08-07 23:36:55 +02:00
assert . strictEqual ( log . error . callCount , 3 , 'one stubbed errors' ) ;
2019-06-20 20:00:12 +02:00
player . dispose ( ) ;
2019-08-07 23:36:55 +02:00
log . error . restore ( ) ;
2019-06-20 20:00:12 +02:00
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should set the width, height, and aspect ratio via a css class' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( ) ;
const getStyleText = function ( styleEl ) {
2015-05-14 07:45:01 +02:00
return ( styleEl . styleSheet && styleEl . styleSheet . cssText ) || styleEl . innerHTML ;
} ;
2013-01-11 00:06:12 +03:00
2015-07-23 21:45:41 +02:00
// NOTE: was using npm/css to parse the actual CSS ast
// but the css module doesn't support ie8
2016-08-04 17:49:32 +02:00
const confirmSetting = function ( prop , val ) {
2015-07-23 21:45:41 +02:00
let styleText = getStyleText ( player . styleEl _ ) ;
2016-08-04 17:49:32 +02:00
const re = new RegExp ( prop + ':\\s?' + val ) ;
2013-01-11 00:06:12 +03:00
2015-07-23 21:45:41 +02:00
// Lowercase string for IE8
styleText = styleText . toLowerCase ( ) ;
2013-01-11 00:06:12 +03:00
2015-07-23 21:45:41 +02:00
return ! ! re . test ( styleText ) ;
} ;
2013-04-09 02:23:41 +03:00
2015-07-23 21:45:41 +02:00
// Initial state
2016-08-12 19:51:31 +02:00
assert . ok ( ! getStyleText ( player . styleEl _ ) , 'style element should be empty when the player is given no dimensions' ) ;
2015-05-14 07:45:01 +02:00
// Set only the width
player . width ( 100 ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( confirmSetting ( 'width' , '100px' ) , 'style width should equal the supplied width in pixels' ) ;
assert . ok ( confirmSetting ( 'height' , '56.25px' ) , 'style height should match the default aspect ratio of the width' ) ;
2015-05-14 07:45:01 +02:00
// Set the height
player . height ( 200 ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( confirmSetting ( 'height' , '200px' ) , 'style height should match the supplied height in pixels' ) ;
2015-05-14 07:45:01 +02:00
// Reset the width and height to defaults
player . width ( '' ) ;
player . height ( '' ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( confirmSetting ( 'width' , '300px' ) , 'supplying an empty string should reset the width' ) ;
assert . ok ( confirmSetting ( 'height' , '168.75px' ) , 'supplying an empty string should reset the height' ) ;
2015-05-14 07:45:01 +02:00
// Switch to fluid mode
player . fluid ( true ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . hasClass ( 'vjs-fluid' ) , 'the vjs-fluid class should be added to the player' ) ;
assert . ok ( confirmSetting ( 'padding-top' , '56.25%' ) , 'fluid aspect ratio should match the default aspect ratio' ) ;
2015-05-14 07:45:01 +02:00
// Change the aspect ratio
player . aspectRatio ( '4:1' ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( confirmSetting ( 'padding-top' , '25%' ) , 'aspect ratio percent should match the newly set aspect ratio' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2013-03-09 11:39:28 +03:00
} ) ;
2016-11-03 21:37:30 +02:00
QUnit . test ( 'should default to 16:9 when fluid' , function ( assert ) {
const player = TestHelpers . makePlayer ( { fluid : true } ) ;
2016-11-04 00:40:14 +02:00
const ratio = player . currentHeight ( ) / player . currentWidth ( ) ;
2016-11-03 21:37:30 +02:00
2018-03-23 19:25:12 +02:00
// account for some rounding of 0.5625 up to 0.563
2016-11-04 00:40:14 +02:00
assert . ok ( ( ( ratio >= 0.562 ) && ( ratio <= 0.563 ) ) , 'fluid player without dimensions defaults to 16:9' ) ;
2016-12-02 21:17:36 +02:00
player . dispose ( ) ;
2016-11-03 21:37:30 +02:00
} ) ;
2021-01-06 19:49:57 +02:00
QUnit . test ( 'should resize fluid player on resize' , function ( assert ) {
const player = TestHelpers . makePlayer ( { fluid : true } ) ;
let ratio = player . currentHeight ( ) / player . currentWidth ( ) ;
// account for some rounding of 0.5625 up to 0.563
assert . ok ( ( ( ratio >= 0.562 ) && ( ratio <= 0.563 ) ) , 'fluid player without dimensions defaults to 16:9' ) ;
player . tech _ . videoWidth = ( ) => 100 ;
player . tech _ . videoHeight = ( ) => 50 ;
player . trigger ( 'resize' ) ;
this . clock . tick ( 1 ) ;
ratio = player . currentHeight ( ) / player . currentWidth ( ) ;
assert . ok ( ratio === 0.5 , 'player aspect ratio changed on resize event' ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'should resize fluid player on resize if fluid enabled post initialisation' , function ( assert ) {
const player = TestHelpers . makePlayer ( { fluid : false } ) ;
player . tech _ . videoWidth = ( ) => 100 ;
player . tech _ . videoHeight = ( ) => 30 ;
player . fluid ( true ) ;
player . trigger ( 'resize' ) ;
this . clock . tick ( 1 ) ;
const ratio = player . currentHeight ( ) / player . currentWidth ( ) ;
assert . ok ( ratio === 0.3 , 'player aspect ratio changed on resize event' ) ;
player . dispose ( ) ;
} ) ;
2016-11-03 21:37:30 +02:00
QUnit . test ( 'should set fluid to true if element has vjs-fluid class' , function ( assert ) {
const tag = TestHelpers . makeTag ( ) ;
tag . className += ' vjs-fluid' ;
const player = TestHelpers . makePlayer ( { } , tag ) ;
assert . ok ( player . fluid ( ) , 'fluid is true with vjs-fluid class' ) ;
2016-12-02 21:17:36 +02:00
player . dispose ( ) ;
2016-11-03 21:37:30 +02:00
} ) ;
2018-10-05 20:28:09 +02:00
QUnit . test ( 'should set fill to true if element has vjs-fill class' , function ( assert ) {
const tag = TestHelpers . makeTag ( ) ;
tag . className += ' vjs-fill' ;
const player = TestHelpers . makePlayer ( { } , tag ) ;
assert . ok ( player . fill ( ) , 'fill is true with vjs-fill class' ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'fill turns off fluid' , function ( assert ) {
const tag = TestHelpers . makeTag ( ) ;
tag . className += ' vjs-fluid' ;
const player = TestHelpers . makePlayer ( { } , tag ) ;
assert . notOk ( player . fill ( ) , 'fill is false' ) ;
assert . ok ( player . fluid ( ) , 'fluid is true' ) ;
player . fill ( true ) ;
assert . ok ( player . fill ( ) , 'fill is true' ) ;
assert . notOk ( player . fluid ( ) , 'fluid is false' ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'fluid turns off fill' , function ( assert ) {
const tag = TestHelpers . makeTag ( ) ;
tag . className += ' vjs-fill' ;
const player = TestHelpers . makePlayer ( { } , tag ) ;
assert . ok ( player . fill ( ) , 'fill is true' ) ;
assert . notOk ( player . fluid ( ) , 'fluid is false' ) ;
player . fluid ( true ) ;
assert . notOk ( player . fill ( ) , 'fill is false' ) ;
assert . ok ( player . fluid ( ) , 'fluid is true' ) ;
player . dispose ( ) ;
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should use an class name that begins with an alpha character' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const alphaPlayer = TestHelpers . makePlayer ( { id : 'alpha1' } ) ;
const numericPlayer = TestHelpers . makePlayer ( { id : '1numeric' } ) ;
2015-11-21 00:38:05 +02:00
2016-08-04 17:49:32 +02:00
const getStyleText = function ( styleEl ) {
2015-11-21 00:38:05 +02:00
return ( styleEl . styleSheet && styleEl . styleSheet . cssText ) || styleEl . innerHTML ;
} ;
alphaPlayer . width ( 100 ) ;
numericPlayer . width ( 100 ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( /\s*\.alpha1-dimensions\s*\{/ . test ( getStyleText ( alphaPlayer . styleEl _ ) ) , 'appends -dimensions to an alpha player ID' ) ;
assert . ok ( /\s*\.dimensions-1numeric\s*\{/ . test ( getStyleText ( numericPlayer . styleEl _ ) ) , 'prepends dimensions- to a numeric player ID' ) ;
2016-08-25 16:58:42 +02:00
alphaPlayer . dispose ( ) ;
numericPlayer . dispose ( ) ;
2015-11-21 00:38:05 +02:00
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should wrap the original tag in the player div' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const tag = TestHelpers . makeTag ( ) ;
const container = document . createElement ( 'div' ) ;
const fixture = document . getElementById ( 'qunit-fixture' ) ;
2013-01-11 00:06:12 +03:00
container . appendChild ( tag ) ;
fixture . appendChild ( container ) ;
2016-08-04 17:49:32 +02:00
const player = new Player ( tag , { techOrder : [ 'techFaker' ] } ) ;
const el = player . el ( ) ;
2013-01-11 00:06:12 +03:00
2016-08-12 19:51:31 +02:00
assert . ok ( el . parentNode === container , 'player placed at same level as tag' ) ;
2013-01-11 00:06:12 +03:00
// Tag may be placed inside the player element or it may be removed from the DOM
2016-08-12 19:51:31 +02:00
assert . ok ( tag . parentNode !== container , 'tag removed from original place' ) ;
2013-01-11 00:06:12 +03:00
player . dispose ( ) ;
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should set and update the poster value' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const poster = '#' ;
const updatedPoster = 'http://example.com/updated-poster.jpg' ;
2013-08-25 02:34:26 +03:00
2016-08-04 17:49:32 +02:00
const tag = TestHelpers . makeTag ( ) ;
2013-08-25 02:34:26 +03:00
2013-11-27 03:53:23 +03:00
tag . setAttribute ( 'poster' , poster ) ;
2013-08-25 02:34:26 +03:00
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( { } , tag ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player . poster ( ) , poster , 'the poster property should equal the tag attribute' ) ;
2013-08-25 02:34:26 +03:00
2016-08-04 17:49:32 +02:00
let pcEmitted = false ;
player . on ( 'posterchange' , function ( ) {
2013-11-27 03:53:23 +03:00
pcEmitted = true ;
} ) ;
2013-08-25 02:34:26 +03:00
player . poster ( updatedPoster ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( pcEmitted , 'posterchange event was emitted' ) ;
assert . equal ( player . poster ( ) , updatedPoster , 'the updated poster is returned' ) ;
2013-08-25 02:34:26 +03:00
player . dispose ( ) ;
} ) ;
2015-02-09 19:11:00 +02:00
// hasStarted() is equivalent to the "show poster flag" in the
// standard, for the purpose of displaying the poster image
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-media-play
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should hide the poster when play is called' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( {
2015-02-09 19:11:00 +02:00
poster : 'https://example.com/poster.jpg'
} ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player . hasStarted ( ) , false , 'the show poster flag is true before play' ) ;
2016-06-28 05:07:00 +02:00
player . tech _ . trigger ( 'play' ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player . hasStarted ( ) , true , 'the show poster flag is false after play' ) ;
2015-02-09 19:11:00 +02:00
2015-09-15 02:45:14 +02:00
player . tech _ . trigger ( 'loadstart' ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player . hasStarted ( ) , false , 'the resource selection algorithm sets the show poster flag to true' ) ;
2015-02-09 19:11:00 +02:00
2016-06-28 05:07:00 +02:00
player . tech _ . trigger ( 'play' ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player . hasStarted ( ) , true , 'the show poster flag is false after play' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2015-02-09 19:11:00 +02:00
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should load a media controller' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( {
2013-01-11 00:06:12 +03:00
preload : 'none' ,
sources : [
2013-02-09 01:29:04 +03:00
{ src : 'http://google.com' , type : 'video/mp4' } ,
{ src : 'http://google.com' , type : 'video/webm' }
2013-01-11 00:06:12 +03:00
]
} ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . el ( ) . children [ 0 ] . className . indexOf ( 'vjs-tech' ) !== - 1 , 'media controller loaded' ) ;
2013-01-11 00:06:12 +03:00
player . dispose ( ) ;
} ) ;
2013-01-18 04:33:53 +03:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should be able to initialize player twice on the same tag using string reference' , function ( assert ) {
2016-08-04 17:49:32 +02:00
let videoTag = TestHelpers . makeTag ( ) ;
const id = videoTag . id ;
const fixture = document . getElementById ( 'qunit-fixture' ) ;
2013-03-06 00:23:01 +03:00
fixture . appendChild ( videoTag ) ;
2016-08-04 17:49:32 +02:00
let player = videojs ( videoTag . id , { techOrder : [ 'techFaker' ] } ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player , 'player is created' ) ;
2013-03-06 00:23:01 +03:00
player . dispose ( ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( ! document . getElementById ( id ) , 'element is removed' ) ;
2015-03-26 06:43:41 +02:00
videoTag = TestHelpers . makeTag ( ) ;
2013-03-06 00:23:01 +03:00
fixture . appendChild ( videoTag ) ;
2016-08-04 17:49:32 +02:00
// here we receive cached version instead of real
2015-09-28 20:23:25 +02:00
player = videojs ( videoTag . id , { techOrder : [ 'techFaker' ] } ) ;
2016-08-04 17:49:32 +02:00
// here it triggers error, because player was destroyed already after first dispose
2013-03-06 00:23:01 +03:00
player . dispose ( ) ;
} ) ;
2013-05-03 03:15:37 +03:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should set controls and trigger events' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( { controls : false } ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . controls ( ) === false , 'controls set through options' ) ;
2016-08-04 17:49:32 +02:00
const hasDisabledClass = player . el ( ) . className . indexOf ( 'vjs-controls-disabled' ) ;
2013-05-03 03:15:37 +03:00
2016-08-12 19:51:31 +02:00
assert . ok ( hasDisabledClass !== - 1 , 'Disabled class added to player' ) ;
2013-08-10 00:29:22 +03:00
2013-05-03 03:15:37 +03:00
player . controls ( true ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . controls ( ) === true , 'controls updated' ) ;
2016-08-04 17:49:32 +02:00
const hasEnabledClass = player . el ( ) . className . indexOf ( 'vjs-controls-enabled' ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( hasEnabledClass !== - 1 , 'Disabled class added to player' ) ;
2013-05-03 03:15:37 +03:00
2016-08-04 17:49:32 +02:00
player . on ( 'controlsenabled' , function ( ) {
2016-08-12 19:51:31 +02:00
assert . ok ( true , 'enabled fired once' ) ;
2013-08-10 00:29:22 +03:00
} ) ;
2016-08-04 17:49:32 +02:00
player . on ( 'controlsdisabled' , function ( ) {
2016-08-12 19:51:31 +02:00
assert . ok ( true , 'disabled fired once' ) ;
2013-05-03 03:15:37 +03:00
} ) ;
player . controls ( false ) ;
player . dispose ( ) ;
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should toggle user the user state between active and inactive' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( { } ) ;
2013-08-10 00:29:22 +03:00
2016-08-12 19:51:31 +02:00
assert . expect ( 9 ) ;
2013-08-10 00:29:22 +03:00
2016-08-12 19:51:31 +02:00
assert . ok ( player . userActive ( ) , 'User should be active at player init' ) ;
2013-08-10 00:29:22 +03:00
2016-08-04 17:49:32 +02:00
player . on ( 'userinactive' , function ( ) {
2016-08-12 19:51:31 +02:00
assert . ok ( true , 'userinactive event triggered' ) ;
2013-08-10 00:29:22 +03:00
} ) ;
2016-08-04 17:49:32 +02:00
player . on ( 'useractive' , function ( ) {
2016-08-12 19:51:31 +02:00
assert . ok ( true , 'useractive event triggered' ) ;
2013-08-10 00:29:22 +03:00
} ) ;
player . userActive ( false ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . userActive ( ) === false , 'Player state changed to inactive' ) ;
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-user-active' ) === - 1 , 'Active class removed' ) ;
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-user-inactive' ) !== - 1 , 'Inactive class added' ) ;
2013-08-10 00:29:22 +03:00
player . userActive ( true ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . userActive ( ) === true , 'Player state changed to active' ) ;
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-user-inactive' ) === - 1 , 'Inactive class removed' ) ;
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-user-active' ) !== - 1 , 'Active class added' ) ;
2013-08-10 00:29:22 +03:00
player . dispose ( ) ;
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should add a touch-enabled classname when touch is supported' , function ( assert ) {
assert . expect ( 1 ) ;
2013-08-10 00:29:22 +03:00
// Fake touch support. Real touch support isn't needed for this test.
2016-08-04 17:49:32 +02:00
const origTouch = browser . TOUCH _ENABLED ;
2013-08-10 00:29:22 +03:00
2019-08-30 20:56:41 +02:00
browser . stub _TOUCH _ENABLED ( true ) ;
2013-08-10 00:29:22 +03:00
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( { } ) ;
2013-08-10 00:29:22 +03:00
2019-01-03 20:50:48 +02:00
assert . notEqual ( player . el ( ) . className . indexOf ( 'vjs-touch-enabled' ) , - 1 , 'touch-enabled classname added' ) ;
2019-08-30 20:56:41 +02:00
browser . stub _TOUCH _ENABLED ( origTouch ) ;
2019-01-03 20:50:48 +02:00
player . dispose ( ) ;
} ) ;
2023-06-12 20:31:06 +02:00
QUnit . test ( 'should add a svg-icons-enabled classname when svg icons are supported' , function ( assert ) {
// Stub a successful parsing of the SVG sprite.
sinon . stub ( window . DOMParser . prototype , 'parseFromString' ) . returns ( {
querySelector : ( ) => false ,
documentElement : document . createElement ( 'span' )
} ) ;
assert . expect ( 1 ) ;
const player = TestHelpers . makePlayer ( { experimentalSvgIcons : true } ) ;
assert . ok ( player . hasClass ( 'vjs-svg-icons-enabled' ) , 'svg-icons-enabled classname added' ) ;
window . DOMParser . prototype . parseFromString . restore ( ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'should revert to font icons if the SVG sprite cannot be loaded' , function ( assert ) {
// Stub an unsuccessful parsing of the SVG sprite.
sinon . stub ( window . DOMParser . prototype , 'parseFromString' ) . returns ( {
querySelector : ( ) => true
} ) ;
assert . expect ( 1 ) ;
const player = TestHelpers . makePlayer ( { experimentalSvgIcons : true } ) ;
assert . ok ( ! player . hasClass ( 'vjs-svg-icons-enabled' ) , 'svg-icons-enabled classname was not added' ) ;
window . DOMParser . prototype . parseFromString . restore ( ) ;
player . dispose ( ) ;
} ) ;
2019-01-03 20:50:48 +02:00
QUnit . test ( 'should not add a touch-enabled classname when touch is not supported' , function ( assert ) {
assert . expect ( 1 ) ;
// Fake not having touch support in case that the browser running the test supports it
const origTouch = browser . TOUCH _ENABLED ;
2019-08-30 20:56:41 +02:00
browser . stub _TOUCH _ENABLED ( false ) ;
2019-01-03 20:50:48 +02:00
const player = TestHelpers . makePlayer ( { } ) ;
assert . equal ( player . el ( ) . className . indexOf ( 'vjs-touch-enabled' ) , - 1 , 'touch-enabled classname not added' ) ;
2013-08-10 00:29:22 +03:00
2019-08-30 20:56:41 +02:00
browser . stub _TOUCH _ENABLED ( origTouch ) ;
2013-08-10 00:29:22 +03:00
player . dispose ( ) ;
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should allow for tracking when native controls are used' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( { } ) ;
2013-08-10 00:29:22 +03:00
2016-08-12 19:51:31 +02:00
assert . expect ( 6 ) ;
2013-08-10 00:29:22 +03:00
// Make sure native controls is false before starting test
player . usingNativeControls ( false ) ;
2016-08-04 17:49:32 +02:00
player . on ( 'usingnativecontrols' , function ( ) {
2016-08-12 19:51:31 +02:00
assert . ok ( true , 'usingnativecontrols event triggered' ) ;
2013-08-10 00:29:22 +03:00
} ) ;
2016-08-04 17:49:32 +02:00
player . on ( 'usingcustomcontrols' , function ( ) {
2016-08-12 19:51:31 +02:00
assert . ok ( true , 'usingcustomcontrols event triggered' ) ;
2013-08-10 00:29:22 +03:00
} ) ;
player . usingNativeControls ( true ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . usingNativeControls ( ) === true , 'Using native controls is true' ) ;
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-using-native-controls' ) !== - 1 , 'Native controls class added' ) ;
2013-08-10 00:29:22 +03:00
player . usingNativeControls ( false ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . usingNativeControls ( ) === false , 'Using native controls is false' ) ;
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-using-native-controls' ) === - 1 , 'Native controls class removed' ) ;
2013-08-10 00:29:22 +03:00
player . dispose ( ) ;
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'make sure that controls listeners do not get added too many times' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( { } ) ;
let listeners = 0 ;
2015-08-25 22:17:35 +02:00
2015-09-15 02:45:14 +02:00
player . addTechControlsListeners _ = function ( ) {
2015-08-25 22:17:35 +02:00
listeners ++ ;
} ;
// Make sure native controls is false before starting test
player . usingNativeControls ( false ) ;
player . usingNativeControls ( true ) ;
player . controls ( true ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( listeners , 0 , 'addTechControlsListeners_ should not have gotten called yet' ) ;
2015-08-25 22:17:35 +02:00
player . usingNativeControls ( false ) ;
player . controls ( false ) ;
player . controls ( true ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( listeners , 1 , 'addTechControlsListeners_ should have gotten called once' ) ;
2015-08-25 22:17:35 +02:00
player . dispose ( ) ;
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should register players with generated ids' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const fixture = document . getElementById ( 'qunit-fixture' ) ;
const video = document . createElement ( 'video' ) ;
2013-12-03 02:29:41 +03:00
video . className = 'vjs-default-skin video-js' ;
fixture . appendChild ( video ) ;
2016-08-04 17:49:32 +02:00
const player = new Player ( video , { techOrder : [ 'techFaker' ] } ) ;
const id = player . el ( ) . id ;
2013-12-03 02:29:41 +03:00
2016-08-12 19:51:31 +02:00
assert . equal ( player . el ( ) . id , player . id ( ) , 'the player and element ids are equal' ) ;
assert . ok ( Player . players [ id ] , 'the generated id is registered' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2013-12-03 02:29:41 +03:00
} ) ;
2014-04-03 20:41:02 +03:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should remove vjs-has-started class' , function ( assert ) {
assert . expect ( 3 ) ;
2014-04-03 20:41:02 +03:00
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( { } ) ;
2014-04-03 20:41:02 +03:00
2015-09-15 02:45:14 +02:00
player . tech _ . trigger ( 'loadstart' ) ;
player . tech _ . trigger ( 'play' ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-has-started' ) !== - 1 , 'vjs-has-started class added' ) ;
2014-04-03 20:41:02 +03:00
2015-09-15 02:45:14 +02:00
player . tech _ . trigger ( 'loadstart' ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-has-started' ) === - 1 , 'vjs-has-started class removed' ) ;
2014-04-03 20:41:02 +03:00
2015-09-15 02:45:14 +02:00
player . tech _ . trigger ( 'play' ) ;
2016-08-25 16:58:42 +02:00
2016-08-12 19:51:31 +02:00
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-has-started' ) !== - 1 , 'vjs-has-started class added again' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2014-04-03 20:41:02 +03:00
} ) ;
2014-05-13 03:08:48 +03:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should add and remove vjs-ended class' , function ( assert ) {
assert . expect ( 4 ) ;
2015-02-13 01:15:44 +02:00
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( { } ) ;
2015-02-13 01:15:44 +02:00
2015-09-15 02:45:14 +02:00
player . tech _ . trigger ( 'loadstart' ) ;
player . tech _ . trigger ( 'play' ) ;
player . tech _ . trigger ( 'ended' ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-ended' ) !== - 1 , 'vjs-ended class added' ) ;
2015-02-13 01:15:44 +02:00
2015-09-15 02:45:14 +02:00
player . tech _ . trigger ( 'play' ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-ended' ) === - 1 , 'vjs-ended class removed' ) ;
2015-02-13 01:15:44 +02:00
2015-09-15 02:45:14 +02:00
player . tech _ . trigger ( 'ended' ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-ended' ) !== - 1 , 'vjs-ended class re-added' ) ;
2015-02-13 01:15:44 +02:00
2015-09-15 02:45:14 +02:00
player . tech _ . trigger ( 'loadstart' ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-ended' ) === - 1 , 'vjs-ended class removed' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2015-02-13 01:15:44 +02:00
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'player should handle different error types' , function ( assert ) {
assert . expect ( 8 ) ;
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( { } ) ;
const testMsg = 'test message' ;
2014-05-13 03:08:48 +03:00
// prevent error log messages in the console
2015-05-04 01:12:38 +02:00
sinon . stub ( log , 'error' ) ;
2014-05-13 03:08:48 +03:00
// error code supplied
2016-08-04 17:49:32 +02:00
function errCode ( ) {
2016-08-12 19:51:31 +02:00
assert . equal ( player . error ( ) . code , 1 , 'error code is correct' ) ;
2014-05-13 03:08:48 +03:00
}
player . on ( 'error' , errCode ) ;
player . error ( 1 ) ;
player . off ( 'error' , errCode ) ;
// error instance supplied
2016-08-04 17:49:32 +02:00
function errInst ( ) {
2016-08-12 19:51:31 +02:00
assert . equal ( player . error ( ) . code , 2 , 'MediaError code is correct' ) ;
assert . equal ( player . error ( ) . message , testMsg , 'MediaError message is correct' ) ;
2014-05-13 03:08:48 +03:00
}
player . on ( 'error' , errInst ) ;
2015-03-26 06:43:41 +02:00
player . error ( new MediaError ( { code : 2 , message : testMsg } ) ) ;
2014-05-13 03:08:48 +03:00
player . off ( 'error' , errInst ) ;
// error message supplied
2016-08-04 17:49:32 +02:00
function errMsg ( ) {
2016-08-12 19:51:31 +02:00
assert . equal ( player . error ( ) . code , 0 , 'error message code is correct' ) ;
assert . equal ( player . error ( ) . message , testMsg , 'error message is correct' ) ;
2014-05-13 03:08:48 +03:00
}
player . on ( 'error' , errMsg ) ;
player . error ( testMsg ) ;
player . off ( 'error' , errMsg ) ;
// error config supplied
2016-08-04 17:49:32 +02:00
function errConfig ( ) {
2016-08-12 19:51:31 +02:00
assert . equal ( player . error ( ) . code , 3 , 'error config code is correct' ) ;
assert . equal ( player . error ( ) . message , testMsg , 'error config message is correct' ) ;
2014-05-13 03:08:48 +03:00
}
player . on ( 'error' , errConfig ) ;
player . error ( { code : 3 , message : testMsg } ) ;
player . off ( 'error' , errConfig ) ;
// check for vjs-error classname
2016-08-12 19:51:31 +02:00
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-error' ) >= 0 , 'player does not have vjs-error classname' ) ;
2014-05-13 03:08:48 +03:00
// restore error logging
2015-05-04 01:12:38 +02:00
log . error . restore ( ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2014-05-13 03:08:48 +03:00
} ) ;
2021-07-28 19:32:38 +02:00
QUnit . test ( 'beforeerror hook allows us to modify errors' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
const beforeerrorHook = function ( p , err ) {
assert . equal ( player , p , 'the players match' ) ;
assert . equal ( err . code , 4 , 'we got code 4 in beforeerror hook' ) ;
return { code : 1 } ;
} ;
const errorHook = function ( p , err ) {
assert . equal ( player , p , 'the players match' ) ;
assert . equal ( err . code , 1 , 'we got code 1 in error hook' ) ;
} ;
videojs . hook ( 'beforeerror' , beforeerrorHook ) ;
videojs . hook ( 'error' , errorHook ) ;
player . error ( { code : 4 } ) ;
player . dispose ( ) ;
videojs . removeHook ( 'beforeerror' , beforeerrorHook ) ;
videojs . removeHook ( 'error' , errorHook ) ;
} ) ;
QUnit . test ( 'beforeerror hook logs a warning if the incorrect type is returned' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
const stub = sinon . stub ( player . log , 'error' ) ;
let errorReturnValue ;
const beforeerrorHook = function ( p , err ) {
return errorReturnValue ;
} ;
videojs . hook ( 'beforeerror' , beforeerrorHook ) ;
stub . reset ( ) ;
errorReturnValue = { code : 4 } ;
player . error ( { code : 4 } ) ;
assert . ok ( stub . notCalled , '{code: 4} is supported' ) ;
stub . reset ( ) ;
errorReturnValue = 1 ;
player . error ( { code : 4 } ) ;
assert . ok ( stub . notCalled , 'number is supported' ) ;
stub . reset ( ) ;
errorReturnValue = null ;
player . error ( { code : 4 } ) ;
assert . ok ( stub . notCalled , 'null is supported' ) ;
stub . reset ( ) ;
errorReturnValue = 'hello' ;
player . error ( { code : 4 } ) ;
assert . ok ( stub . notCalled , 'string is supported' ) ;
stub . reset ( ) ;
errorReturnValue = new Error ( 'hello' ) ;
player . error ( { code : 4 } ) ;
assert . ok ( stub . notCalled , 'Error object is supported' ) ;
stub . reset ( ) ;
errorReturnValue = [ 1 , 2 , 3 ] ;
player . error ( { code : 4 } ) ;
assert . ok ( stub . called , 'array is not supported' ) ;
stub . reset ( ) ;
errorReturnValue = undefined ;
player . error ( { code : 4 } ) ;
assert . ok ( stub . called , 'undefined is not supported' ) ;
stub . reset ( ) ;
errorReturnValue = true ;
player . error ( { code : 4 } ) ;
assert . ok ( stub . called , 'booleans are not supported' ) ;
videojs . removeHook ( 'beforeerror' , beforeerrorHook ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'player should trigger error related hooks' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
const beforeerrorHook = function ( p , err ) {
assert . equal ( player , p , 'the players match' ) ;
assert . equal ( err . code , 4 , 'we got code 4 in beforeerror hook' ) ;
return err ;
} ;
const errorHook = function ( p , err ) {
assert . equal ( player , p , 'the players match' ) ;
assert . equal ( err . code , 4 , 'we got code 4 in error hook' ) ;
} ;
videojs . hook ( 'beforeerror' , beforeerrorHook ) ;
videojs . hook ( 'error' , errorHook ) ;
player . error ( { code : 4 } ) ;
player . dispose ( ) ;
videojs . removeHook ( 'beforeerror' , beforeerrorHook ) ;
videojs . removeHook ( 'error' , errorHook ) ;
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'Data attributes on the video element should persist in the new wrapper element' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const dataId = 123 ;
2014-07-01 22:10:45 +03:00
2016-08-04 17:49:32 +02:00
const tag = TestHelpers . makeTag ( ) ;
2014-07-01 22:10:45 +03:00
tag . setAttribute ( 'data-id' , dataId ) ;
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( { } , tag ) ;
2014-07-01 22:10:45 +03:00
2016-08-12 19:51:31 +02:00
assert . equal ( player . el ( ) . getAttribute ( 'data-id' ) , dataId , 'data-id should be available on the new player element after creation' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2014-07-01 22:10:45 +03:00
} ) ;
2014-08-05 01:12:17 +03:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should restore attributes from the original video tag when creating a new element' , function ( assert ) {
2014-08-05 01:04:39 +03:00
// simulate attributes stored from the original tag
2016-08-04 17:49:32 +02:00
const tag = Dom . createEl ( 'video' ) ;
2015-05-06 20:01:52 +02:00
tag . setAttribute ( 'preload' , 'auto' ) ;
tag . setAttribute ( 'autoplay' , '' ) ;
tag . setAttribute ( 'webkit-playsinline' , '' ) ;
2016-08-04 17:49:32 +02:00
const html5Mock = { options _ : { tag } } ;
2014-08-05 01:12:17 +03:00
2014-08-05 01:04:39 +03:00
// set options that should override tag attributes
2015-05-06 20:01:52 +02:00
html5Mock . options _ . preload = 'none' ;
2014-08-05 01:12:17 +03:00
2014-08-05 01:04:39 +03:00
// create the element
2016-08-04 17:49:32 +02:00
const el = Html5 . prototype . createEl . call ( html5Mock ) ;
2014-08-05 01:12:17 +03:00
2016-08-12 19:51:31 +02:00
assert . equal ( el . getAttribute ( 'preload' ) , 'none' , 'attribute was successful overridden by an option' ) ;
assert . equal ( el . getAttribute ( 'autoplay' ) , '' , 'autoplay attribute was set properly' ) ;
assert . equal ( el . getAttribute ( 'webkit-playsinline' ) , '' , 'webkit-playsinline attribute was set properly' ) ;
2014-08-05 01:12:17 +03:00
} ) ;
2014-08-25 12:28:52 +03:00
2017-05-12 22:39:37 +02:00
if ( Html5 . isSupported ( ) ) {
QUnit . test ( 'player.playsinline() should be able to get/set playsinline attribute' , function ( assert ) {
assert . expect ( 5 ) ;
const video = document . createElement ( 'video' ) ;
const player = TestHelpers . makePlayer ( { techOrder : [ 'html5' ] } , video ) ;
// test setter
assert . ok ( ! player . tech _ . el ( ) . hasAttribute ( 'playsinline' ) , 'playsinline has not yet been added' ) ;
player . playsinline ( true ) ;
assert . ok ( player . tech _ . el ( ) . hasAttribute ( 'playsinline' ) , 'playsinline attribute added' ) ;
player . playsinline ( false ) ;
assert . ok ( ! player . tech _ . el ( ) . hasAttribute ( 'playsinline' ) , 'playsinline attribute removed' ) ;
// test getter
player . tech _ . el ( ) . setAttribute ( 'playsinline' , 'playsinline' ) ;
assert . ok ( player . playsinline ( ) , 'correctly detects playsinline attribute' ) ;
player . tech _ . el ( ) . removeAttribute ( 'playsinline' ) ;
assert . ok ( ! player . playsinline ( ) , 'correctly detects absence of playsinline attribute' ) ;
} ) ;
}
2016-12-19 18:51:42 +02:00
QUnit . test ( 'if tag exists and movingMediaElementInDOM, re-use the tag' , function ( assert ) {
// simulate attributes stored from the original tag
const tag = Dom . createEl ( 'video' ) ;
tag . setAttribute ( 'preload' , 'auto' ) ;
tag . setAttribute ( 'autoplay' , '' ) ;
tag . setAttribute ( 'webkit-playsinline' , '' ) ;
const html5Mock = {
options _ : {
tag ,
playerElIngest : false
} ,
movingMediaElementInDOM : true
} ;
// set options that should override tag attributes
html5Mock . options _ . preload = 'none' ;
// create the element
const el = Html5 . prototype . createEl . call ( html5Mock ) ;
assert . equal ( el . getAttribute ( 'preload' ) , 'none' , 'attribute was successful overridden by an option' ) ;
assert . equal ( el . getAttribute ( 'autoplay' ) , '' , 'autoplay attribute was set properly' ) ;
assert . equal ( el . getAttribute ( 'webkit-playsinline' ) , '' , 'webkit-playsinline attribute was set properly' ) ;
assert . equal ( el , tag , 'we have re-used the tag as expected' ) ;
} ) ;
QUnit . test ( 'if tag exists and *not* movingMediaElementInDOM, create a new tag' , function ( assert ) {
// simulate attributes stored from the original tag
const tag = Dom . createEl ( 'video' ) ;
tag . setAttribute ( 'preload' , 'auto' ) ;
tag . setAttribute ( 'autoplay' , '' ) ;
tag . setAttribute ( 'webkit-playsinline' , '' ) ;
const html5Mock = {
options _ : {
tag ,
playerElIngest : false
} ,
movingMediaElementInDOM : false
} ;
// set options that should override tag attributes
html5Mock . options _ . preload = 'none' ;
// create the element
const el = Html5 . prototype . createEl . call ( html5Mock ) ;
assert . equal ( el . getAttribute ( 'preload' ) , 'none' , 'attribute was successful overridden by an option' ) ;
assert . equal ( el . getAttribute ( 'autoplay' ) , '' , 'autoplay attribute was set properly' ) ;
assert . equal ( el . getAttribute ( 'webkit-playsinline' ) , '' , 'webkit-playsinline attribute was set properly' ) ;
assert . notEqual ( el , tag , 'we have not re-used the tag as expected' ) ;
} ) ;
QUnit . test ( 'if tag exists and *not* movingMediaElementInDOM, but playerElIngest re-use tag' , function ( assert ) {
// simulate attributes stored from the original tag
const tag = Dom . createEl ( 'video' ) ;
tag . setAttribute ( 'preload' , 'auto' ) ;
tag . setAttribute ( 'autoplay' , '' ) ;
tag . setAttribute ( 'webkit-playsinline' , '' ) ;
const html5Mock = {
options _ : {
tag ,
playerElIngest : true
} ,
movingMediaElementInDOM : false
} ;
// set options that should override tag attributes
html5Mock . options _ . preload = 'none' ;
// create the element
const el = Html5 . prototype . createEl . call ( html5Mock ) ;
assert . equal ( el . getAttribute ( 'preload' ) , 'none' , 'attribute was successful overridden by an option' ) ;
assert . equal ( el . getAttribute ( 'autoplay' ) , '' , 'autoplay attribute was set properly' ) ;
assert . equal ( el . getAttribute ( 'webkit-playsinline' ) , '' , 'webkit-playsinline attribute was set properly' ) ;
assert . equal ( el , tag , 'we have re-used the tag as expected' ) ;
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should honor default inactivity timeout' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const clock = sinon . useFakeTimers ( ) ;
2014-08-25 12:35:06 +03:00
2016-08-04 17:49:32 +02:00
// default timeout is 2000ms
const player = TestHelpers . makePlayer ( { } ) ;
2014-08-25 12:35:06 +03:00
2018-04-19 17:57:32 +02:00
player . trigger ( 'play' ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player . userActive ( ) , true , 'User is active on creation' ) ;
2016-08-04 17:49:32 +02:00
clock . tick ( 1800 ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player . userActive ( ) , true , 'User is still active' ) ;
2016-08-04 17:49:32 +02:00
clock . tick ( 500 ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player . userActive ( ) , false , 'User is inactive after timeout expired' ) ;
2014-08-25 12:35:06 +03:00
2016-08-04 17:49:32 +02:00
clock . restore ( ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2014-08-25 12:35:06 +03:00
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should honor configured inactivity timeout' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const clock = sinon . useFakeTimers ( ) ;
2014-08-25 12:28:52 +03:00
2016-08-04 17:49:32 +02:00
// default timeout is 2000ms, set to shorter 200ms
const player = TestHelpers . makePlayer ( {
inactivityTimeout : 200
} ) ;
2014-08-25 12:28:52 +03:00
2018-04-19 17:57:32 +02:00
player . trigger ( 'play' ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player . userActive ( ) , true , 'User is active on creation' ) ;
2016-08-04 17:49:32 +02:00
clock . tick ( 150 ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player . userActive ( ) , true , 'User is still active' ) ;
2016-08-04 17:49:32 +02:00
clock . tick ( 350 ) ;
// make sure user is now inactive after 500ms
2016-08-12 19:51:31 +02:00
assert . equal ( player . userActive ( ) , false , 'User is inactive after timeout expired' ) ;
2014-08-25 17:42:58 +03:00
2016-08-04 17:49:32 +02:00
clock . restore ( ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2014-08-25 12:28:52 +03:00
} ) ;
2014-08-25 12:35:06 +03:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should honor disabled inactivity timeout' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const clock = sinon . useFakeTimers ( ) ;
2014-08-25 12:35:06 +03:00
2016-08-04 17:49:32 +02:00
// default timeout is 2000ms, disable by setting to zero
const player = TestHelpers . makePlayer ( {
inactivityTimeout : 0
} ) ;
2014-08-25 12:35:06 +03:00
2016-08-12 19:51:31 +02:00
assert . equal ( player . userActive ( ) , true , 'User is active on creation' ) ;
2016-08-04 17:49:32 +02:00
clock . tick ( 5000 ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player . userActive ( ) , true , 'User is still active' ) ;
2014-08-25 12:35:06 +03:00
2016-08-04 17:49:32 +02:00
clock . restore ( ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2014-08-25 12:35:06 +03:00
} ) ;
2014-09-04 18:51:05 +03:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should clear pending errors on disposal' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const clock = sinon . useFakeTimers ( ) ;
const player = TestHelpers . makePlayer ( ) ;
2014-09-04 18:51:05 +03:00
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
clock . tick ( 1 ) ;
2014-09-04 18:51:05 +03:00
player . src ( {
src : 'http://example.com/movie.unsupported-format' ,
type : 'video/unsupported-format'
} ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
clock . tick ( 1 ) ;
2014-09-04 18:51:05 +03:00
player . dispose ( ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
2014-09-04 18:51:05 +03:00
try {
clock . tick ( 5000 ) ;
} catch ( e ) {
2016-08-12 19:51:31 +02:00
return assert . ok ( ! e , 'threw an error: ' + e . message ) ;
2014-09-04 18:51:05 +03:00
}
2016-08-12 19:51:31 +02:00
assert . ok ( true , 'did not throw an error after disposal' ) ;
2014-09-04 18:51:05 +03:00
} ) ;
2014-09-16 20:55:55 +03:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'pause is called when player ended event is fired and player is not paused' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const video = document . createElement ( 'video' ) ;
const player = TestHelpers . makePlayer ( { } , video ) ;
let pauses = 0 ;
2014-09-16 20:55:55 +03:00
player . paused = function ( ) {
return false ;
} ;
player . pause = function ( ) {
pauses ++ ;
} ;
2015-09-15 02:45:14 +02:00
player . tech _ . trigger ( 'ended' ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( pauses , 1 , 'pause was called' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2014-09-16 20:55:55 +03:00
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'pause is not called if the player is paused and ended is fired' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const video = document . createElement ( 'video' ) ;
const player = TestHelpers . makePlayer ( { } , video ) ;
let pauses = 0 ;
2014-09-16 20:55:55 +03:00
player . paused = function ( ) {
return true ;
} ;
player . pause = function ( ) {
pauses ++ ;
} ;
2015-09-15 02:45:14 +02:00
player . tech _ . trigger ( 'ended' ) ;
2016-08-25 16:58:42 +02:00
2016-08-12 19:51:31 +02:00
assert . equal ( pauses , 0 , 'pause was not called when ended fired' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2014-09-16 20:55:55 +03:00
} ) ;
2014-10-01 04:34:51 +03:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should add an audio class if an audio el is used' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const audio = document . createElement ( 'audio' ) ;
const player = TestHelpers . makePlayer ( { } , audio ) ;
const audioClass = 'vjs-audio' ;
2014-10-01 04:34:51 +03:00
2016-08-12 19:51:31 +02:00
assert . ok ( player . el ( ) . className . indexOf ( audioClass ) !== - 1 , 'added ' + audioClass + ' css class' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2014-10-01 04:34:51 +03:00
} ) ;
2015-04-28 22:33:20 +02:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should add a video player region if a video el is used' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const video = document . createElement ( 'video' ) ;
const player = TestHelpers . makePlayer ( { } , video ) ;
2016-04-05 19:47:58 +02:00
2016-08-12 19:51:31 +02:00
assert . ok ( player . el ( ) . getAttribute ( 'role' ) === 'region' , 'region role is present' ) ;
2017-02-09 00:29:32 +02:00
assert . ok ( player . el ( ) . getAttribute ( 'aria-label' ) === 'Video Player' , 'Video Player label present' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2016-04-05 19:47:58 +02:00
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should add an audio player region if an audio el is used' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const audio = document . createElement ( 'audio' ) ;
const player = TestHelpers . makePlayer ( { } , audio ) ;
2016-04-05 19:47:58 +02:00
2016-08-12 19:51:31 +02:00
assert . ok ( player . el ( ) . getAttribute ( 'role' ) === 'region' , 'region role is present' ) ;
2017-02-09 00:29:32 +02:00
assert . ok ( player . el ( ) . getAttribute ( 'aria-label' ) === 'Audio Player' , 'Audio Player label present' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2016-04-05 19:47:58 +02:00
} ) ;
2022-03-01 22:50:46 +02:00
QUnit . test ( 'default audioPosterMode value at player creation' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
2022-03-17 23:10:33 +02:00
player . trigger ( 'ready' ) ;
2022-03-01 22:50:46 +02:00
assert . ok ( player . audioPosterMode ( ) === false , 'audioPosterMode is false by default' ) ;
const player2 = TestHelpers . makePlayer ( {
audioPosterMode : true
} ) ;
2022-03-17 23:10:33 +02:00
const done = assert . async ( ) ;
2022-03-01 22:50:46 +02:00
2022-03-17 23:10:33 +02:00
player2 . trigger ( 'ready' ) ;
player2 . one ( 'audiopostermodechange' , ( ) => {
assert . ok ( player2 . audioPosterMode ( ) , 'audioPosterMode can be set to true when the player is created' ) ;
done ( ) ;
} ) ;
2022-03-01 22:50:46 +02:00
} ) ;
QUnit . test ( 'get and set audioPosterMode value' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
2022-03-17 23:10:33 +02:00
return player . audioPosterMode ( true )
. then ( ( ) => {
assert . ok ( player . audioPosterMode ( ) , 'audioPosterMode is set to true' ) ;
} ) ;
2022-03-01 22:50:46 +02:00
} ) ;
QUnit . test ( 'vjs-audio-poster-mode class and video element visibility when audioPosterMode is true' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
2022-03-17 23:10:33 +02:00
player . trigger ( 'ready' ) ;
2022-03-01 22:50:46 +02:00
2022-03-17 23:10:33 +02:00
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-audio-poster-mode' ) === - 1 , 'vjs-audio-poster-mode class is not present initially' ) ;
2022-03-01 22:50:46 +02:00
assert . ok ( player . tech _ . el ( ) . className . indexOf ( 'vjs-hidden' ) === - 1 , 'video element is visible' ) ;
2022-03-17 23:10:33 +02:00
return player . audioPosterMode ( true )
. then ( ( ) => {
assert . ok ( player . el ( ) . className . indexOf ( 'vjs-audio-poster-mode' ) !== - 1 , 'vjs-audio-poster-mode class is present' ) ;
assert . ok ( player . tech _ . el ( ) . className . indexOf ( 'vjs-hidden' ) !== - 1 , 'video element is hidden' ) ;
} ) ;
} ) ;
QUnit . test ( 'setting audioPosterMode() should trigger audiopostermodechange event' , function ( assert ) {
const player = TestHelpers . makePlayer ( {
audioPosterMode : true
} ) ;
const done = assert . async ( ) ;
player . trigger ( 'ready' ) ;
player . one ( 'audiopostermodechange' , ( ) => {
assert . ok ( true , 'audiopostermodechange event was triggered' ) ;
assert . ok ( player . audioPosterMode ( ) , 'audioPosterMode is set to true' ) ;
done ( ) ;
} ) ;
} ) ;
2020-04-22 18:40:26 +02:00
QUnit . test ( 'should setScrubbing when seeking or not seeking' , function ( assert ) {
const player = TestHelpers . makePlayer ( ) ;
let isScrubbing ;
player . tech _ . setScrubbing = ( _isScrubbing ) => {
isScrubbing = _isScrubbing ;
} ;
assert . equal ( player . scrubbing ( ) , false , 'player is not scrubbing' ) ;
player . scrubbing ( true ) ;
assert . ok ( isScrubbing , "tech's setScrubbing was called with true" ) ;
player . scrubbing ( false ) ;
assert . notOk ( isScrubbing , "tech's setScrubbing was called with false" ) ;
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should not be scrubbing while not seeking' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player . scrubbing ( ) , false , 'player is not scrubbing' ) ;
assert . ok ( player . el ( ) . className . indexOf ( 'scrubbing' ) === - 1 , 'scrubbing class is not present' ) ;
2015-04-28 22:33:20 +02:00
player . scrubbing ( false ) ;
2016-08-25 16:58:42 +02:00
2016-08-12 19:51:31 +02:00
assert . equal ( player . scrubbing ( ) , false , 'player is not scrubbing' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2015-04-28 22:33:20 +02:00
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should be scrubbing while seeking' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( ) ;
2015-04-28 22:33:20 +02:00
player . scrubbing ( true ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player . scrubbing ( ) , true , 'player is scrubbing' ) ;
assert . ok ( player . el ( ) . className . indexOf ( 'scrubbing' ) !== - 1 , 'scrubbing class is present' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2015-04-28 22:33:20 +02:00
} ) ;
2015-04-29 23:05:22 +02:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should throw on startup no techs are specified' , function ( assert ) {
2015-08-12 22:51:43 +02:00
const techOrder = videojs . options . techOrder ;
2017-11-17 01:11:55 +02:00
const fixture = document . getElementById ( 'qunit-fixture' ) ;
2015-04-29 23:05:22 +02:00
2015-08-12 22:51:43 +02:00
videojs . options . techOrder = null ;
2016-08-12 19:51:31 +02:00
assert . throws ( function ( ) {
2017-11-17 01:11:55 +02:00
const tag = TestHelpers . makeTag ( ) ;
fixture . appendChild ( tag ) ;
videojs ( tag ) ;
2015-04-29 23:05:22 +02:00
} , 'a falsey techOrder should throw' ) ;
2015-08-12 22:51:43 +02:00
videojs . options . techOrder = techOrder ;
2015-04-29 23:05:22 +02:00
} ) ;
2015-05-01 23:16:19 +02:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should have a sensible toJSON that is equivalent to player.options' , function ( assert ) {
2015-05-01 23:16:19 +02:00
const playerOptions = {
html5 : {
nativeTextTracks : false
}
} ;
const player = TestHelpers . makePlayer ( playerOptions ) ;
2016-08-12 19:51:31 +02:00
assert . deepEqual ( player . toJSON ( ) , player . options _ , 'simple player options toJSON produces output equivalent to player.options_' ) ;
2015-05-01 23:16:19 +02:00
const playerOptions2 = {
tracks : [ {
label : 'English' ,
srclang : 'en' ,
src : '../docs/examples/shared/example-captions.vtt' ,
kind : 'captions'
} ]
} ;
const player2 = TestHelpers . makePlayer ( playerOptions2 ) ;
playerOptions2 . tracks [ 0 ] . player = player2 ;
2015-06-05 02:33:34 +02:00
const popts = player2 . options _ ;
2016-08-04 17:49:32 +02:00
2015-05-01 23:16:19 +02:00
popts . tracks [ 0 ] . player = undefined ;
2016-08-12 19:51:31 +02:00
assert . deepEqual ( player2 . toJSON ( ) , popts , 'no circular references' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
player2 . dispose ( ) ;
2015-05-01 23:16:19 +02:00
} ) ;
2015-05-20 00:33:52 +02:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should ignore case in language codes and try primary code' , function ( assert ) {
assert . expect ( 3 ) ;
2015-05-20 00:33:52 +02:00
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( {
languages : {
2015-05-20 00:33:52 +02:00
'en-gb' : {
2016-08-04 17:49:32 +02:00
Good : 'Brilliant'
2015-05-20 00:33:52 +02:00
} ,
'EN' : {
2016-08-04 17:49:32 +02:00
Good : 'Awesome' ,
Error : 'Problem'
2015-05-20 00:33:52 +02:00
}
}
} ) ;
player . language ( 'en-gb' ) ;
2016-08-12 19:51:31 +02:00
assert . strictEqual ( player . localize ( 'Good' ) , 'Brilliant' , 'Used subcode specific localisation' ) ;
assert . strictEqual ( player . localize ( 'Error' ) , 'Problem' , 'Used primary code localisation' ) ;
2015-05-20 00:33:52 +02:00
player . language ( 'en-GB' ) ;
2016-08-12 19:51:31 +02:00
assert . strictEqual ( player . localize ( 'Good' ) , 'Brilliant' , 'Ignored case' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2015-05-20 00:33:52 +02:00
} ) ;
2015-10-27 19:46:05 +02:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'inherits language from parent element' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const fixture = document . getElementById ( 'qunit-fixture' ) ;
const oldLang = fixture . getAttribute ( 'lang' ) ;
2016-07-18 20:53:31 +02:00
fixture . setAttribute ( 'lang' , 'x-test' ) ;
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( ) ;
2016-07-18 20:53:31 +02:00
2016-08-12 19:51:31 +02:00
assert . equal ( player . language ( ) , 'x-test' , 'player inherits parent element language' ) ;
2016-07-18 20:53:31 +02:00
player . dispose ( ) ;
if ( oldLang ) {
fixture . setAttribute ( 'lang' , oldLang ) ;
} else {
fixture . removeAttribute ( 'lang' ) ;
}
} ) ;
2017-02-15 22:22:10 +02:00
QUnit . test ( 'sets lang attribute on player el' , function ( assert ) {
const fixture = document . getElementById ( 'qunit-fixture' ) ;
const oldLang = fixture . getAttribute ( 'lang' ) ;
fixture . setAttribute ( 'lang' , 'x-attr-test' ) ;
const player = TestHelpers . makePlayer ( ) ;
assert . equal ( player . el ( ) . getAttribute ( 'lang' ) , 'x-attr-test' , 'player sets lang attribute on self' ) ;
player . dispose ( ) ;
if ( oldLang ) {
fixture . setAttribute ( 'lang' , oldLang ) ;
} else {
fixture . removeAttribute ( 'lang' ) ;
}
} ) ;
2020-11-11 01:09:37 +02:00
QUnit . test ( 'language changed should trigger languagechange event' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
assert . expect ( 1 ) ;
player . on ( 'languagechange' , function ( ) {
assert . ok ( true , 'languagechange event triggered' ) ;
} ) ;
player . language ( 'es-MX' ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'language changed should not trigger languagechange event if language is the same' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
assert . expect ( 1 ) ;
let triggered = false ;
player . language ( 'es-MX' ) ;
player . on ( 'languagechange' , function ( ) {
triggered = true ;
} ) ;
player . language ( 'es-MX' ) ;
assert . equal ( triggered , false , 'languagechange event was not triggered' ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'change language multiple times should trigger languagechange event' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
assert . expect ( 3 ) ;
player . on ( 'languagechange' , function ( ) {
assert . ok ( true , 'languagechange event triggered' ) ;
} ) ;
player . language ( 'es-MX' ) ;
player . language ( 'en-EU' ) ;
// set same language should not trigger the event so we expect 3 asserts not 4.
player . language ( 'en-EU' ) ;
player . language ( 'es-ES' ) ;
player . dispose ( ) ;
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'should return correct values for canPlayType' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( ) ;
2015-10-27 19:46:05 +02:00
2016-08-12 19:51:31 +02:00
assert . equal ( player . canPlayType ( 'video/mp4' ) , 'maybe' , 'player can play mp4 files' ) ;
assert . equal ( player . canPlayType ( 'video/unsupported-format' ) , '' , 'player can not play unsupported files' ) ;
2015-10-27 19:46:05 +02:00
player . dispose ( ) ;
} ) ;
2015-10-28 19:28:15 +02:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'createModal()' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( ) ;
const modal = player . createModal ( 'foo' ) ;
const spy = sinon . spy ( ) ;
2015-10-28 19:28:15 +02:00
modal . on ( 'dispose' , spy ) ;
2016-08-12 19:51:31 +02:00
assert . expect ( 5 ) ;
assert . strictEqual ( modal . el ( ) . parentNode , player . el ( ) , 'the modal is injected into the player' ) ;
assert . strictEqual ( modal . content ( ) , 'foo' , 'content is set properly' ) ;
assert . ok ( modal . opened ( ) , 'modal is opened by default' ) ;
2015-10-28 19:28:15 +02:00
modal . close ( ) ;
2016-08-25 16:58:42 +02:00
2016-08-12 19:51:31 +02:00
assert . ok ( spy . called , 'modal was disposed when closed' ) ;
assert . strictEqual ( player . children ( ) . indexOf ( modal ) , - 1 , 'modal was removed from player\'s children' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2015-10-28 19:28:15 +02:00
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'createModal() options object' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( ) ;
const modal = player . createModal ( 'foo' , { content : 'bar' , label : 'boo' } ) ;
2015-10-28 19:28:15 +02:00
2016-08-12 19:51:31 +02:00
assert . expect ( 2 ) ;
assert . strictEqual ( modal . content ( ) , 'foo' , 'content argument takes precedence' ) ;
assert . strictEqual ( modal . options _ . label , 'boo' , 'modal options are set properly' ) ;
2015-10-28 19:28:15 +02:00
modal . close ( ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2015-10-28 19:28:15 +02:00
} ) ;
2015-11-24 22:37:34 +02:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'you can clear error in the error event' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( ) ;
2015-11-24 22:37:34 +02:00
sinon . stub ( log , 'error' ) ;
player . error ( { code : 4 } ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( player . error ( ) , 'we have an error' ) ;
2015-11-24 22:37:34 +02:00
player . error ( null ) ;
player . one ( 'error' , function ( ) {
player . error ( null ) ;
} ) ;
player . error ( { code : 4 } ) ;
2016-08-12 19:51:31 +02:00
assert . ok ( ! player . error ( ) , 'we no longer have an error' ) ;
2015-11-24 22:37:34 +02:00
log . error . restore ( ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2015-11-24 22:37:34 +02:00
} ) ;
2015-12-07 23:27:33 +02:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'Player#tech will return tech given the appropriate input' , function ( assert ) {
2017-01-18 07:40:24 +02:00
const oldLogWarn = log . warn ;
let warning ;
log . warn = function ( _warning ) {
warning = _warning ;
} ;
2016-08-04 17:49:32 +02:00
const tech _ = { } ;
2017-01-18 07:40:24 +02:00
const returnedTech = Player . prototype . tech . call ( { tech _ } , true ) ;
2015-12-07 23:27:33 +02:00
2016-08-12 19:51:31 +02:00
assert . equal ( returnedTech , tech _ , 'We got back the tech we wanted' ) ;
2017-01-18 07:40:24 +02:00
assert . notOk ( warning , 'no warning was logged' ) ;
log . warn = oldLogWarn ;
2015-12-07 23:27:33 +02:00
} ) ;
2017-01-18 07:40:24 +02:00
QUnit . test ( 'Player#tech logs a warning when called without a safety argument' , function ( assert ) {
const oldLogWarn = log . warn ;
const warningRegex = new RegExp ( 'https://github.com/videojs/video.js/issues/2617' ) ;
let warning ;
2016-08-04 17:49:32 +02:00
2017-01-18 07:40:24 +02:00
log . warn = function ( _warning ) {
warning = _warning ;
2016-08-04 17:49:32 +02:00
} ;
const tech _ = { } ;
2015-12-07 23:27:33 +02:00
2017-01-18 07:40:24 +02:00
Player . prototype . tech . call ( { tech _ } ) ;
assert . ok ( warningRegex . test ( warning ) , 'we logged a warning' ) ;
2015-12-07 23:27:33 +02:00
2017-01-18 07:40:24 +02:00
log . warn = oldLogWarn ;
2015-12-07 23:27:33 +02:00
} ) ;
2015-12-08 00:45:50 +02:00
2016-08-12 19:51:31 +02:00
QUnit . test ( 'player#reset loads the Html5 tech and then techCalls reset' , function ( assert ) {
2015-12-08 00:45:50 +02:00
let loadedTech ;
let loadedSource ;
let techCallMethod ;
2016-08-04 17:49:32 +02:00
const testPlayer = {
2015-12-08 00:45:50 +02:00
options _ : {
2022-07-27 23:05:09 +02:00
techOrder : [ 'html5' , 'youtube' ]
2015-12-08 00:45:50 +02:00
} ,
2023-11-29 00:38:41 +02:00
error ( ) { } ,
2019-01-03 22:14:54 +02:00
resetCache _ ( ) { } ,
2015-12-08 00:45:50 +02:00
loadTech _ ( tech , source ) {
loadedTech = tech ;
loadedSource = source ;
} ,
techCall _ ( method ) {
techCallMethod = method ;
2019-01-03 20:49:34 +02:00
} ,
2019-01-08 21:15:51 +02:00
resetControlBarUI _ ( ) { } ,
2019-04-11 21:53:31 +02:00
poster ( ) { } ,
paused ( ) {
return true ;
} ,
doReset _ : Player . prototype . doReset _
2015-12-08 00:45:50 +02:00
} ;
Player . prototype . reset . call ( testPlayer ) ;
2017-02-02 21:34:33 +02:00
assert . equal ( loadedTech , 'html5' , 'we loaded the html5 tech' ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( loadedSource , null , 'with a null source' ) ;
assert . equal ( techCallMethod , 'reset' , 'we then reset the tech' ) ;
2015-12-08 00:45:50 +02:00
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'player#reset loads the first item in the techOrder and then techCalls reset' , function ( assert ) {
2015-12-08 00:45:50 +02:00
let loadedTech ;
let loadedSource ;
let techCallMethod ;
2016-08-04 17:49:32 +02:00
const testPlayer = {
2015-12-08 00:45:50 +02:00
options _ : {
2022-07-27 23:05:09 +02:00
techOrder : [ 'youtube' , 'html5' ]
2015-12-08 00:45:50 +02:00
} ,
2023-11-29 00:38:41 +02:00
error ( ) { } ,
2019-01-03 22:14:54 +02:00
resetCache _ ( ) { } ,
2015-12-08 00:45:50 +02:00
loadTech _ ( tech , source ) {
loadedTech = tech ;
loadedSource = source ;
} ,
techCall _ ( method ) {
techCallMethod = method ;
2019-01-03 20:49:34 +02:00
} ,
2019-01-08 21:15:51 +02:00
resetControlBarUI _ ( ) { } ,
2019-04-11 21:53:31 +02:00
poster ( ) { } ,
paused ( ) {
return true ;
} ,
doReset _ : Player . prototype . doReset _
2015-12-08 00:45:50 +02:00
} ;
Player . prototype . reset . call ( testPlayer ) ;
2022-07-27 23:05:09 +02:00
assert . equal ( loadedTech , 'youtube' , 'we loaded the Youtube tech' ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( loadedSource , null , 'with a null source' ) ;
assert . equal ( techCallMethod , 'reset' , 'we then reset the tech' ) ;
2015-12-08 00:45:50 +02:00
} ) ;
2016-03-07 21:48:15 +02:00
2019-01-03 22:14:54 +02:00
QUnit . test ( 'player#reset clears the player cache' , function ( assert ) {
const player = TestHelpers . makePlayer ( ) ;
const sources = [ {
src : '//vjs.zencdn.net/v/oceans.mp4' ,
type : 'video/mp4'
} , {
src : '//vjs.zencdn.net/v/oceans.webm' ,
type : 'video/webm'
} ] ;
this . clock . tick ( 1 ) ;
player . src ( sources ) ;
player . duration ( 10 ) ;
player . playbackRate ( 0.5 ) ;
2021-06-08 17:01:56 +02:00
player . playbackRates ( [ 1 , 2 , 3 ] ) ;
2019-01-03 22:14:54 +02:00
player . volume ( 0.2 ) ;
assert . strictEqual ( player . currentSrc ( ) , sources [ 0 ] . src , 'currentSrc is correct' ) ;
assert . deepEqual ( player . currentSource ( ) , sources [ 0 ] , 'currentSource is correct' ) ;
assert . deepEqual ( player . currentSources ( ) , sources , 'currentSources is correct' ) ;
assert . strictEqual ( player . duration ( ) , 10 , 'duration is correct' ) ;
assert . strictEqual ( player . playbackRate ( ) , 0.5 , 'playbackRate is correct' ) ;
2021-06-08 17:01:56 +02:00
assert . deepEqual ( player . playbackRates ( ) , [ 1 , 2 , 3 ] , 'playbackRates is correct' ) ;
2019-01-03 22:14:54 +02:00
assert . strictEqual ( player . volume ( ) , 0.2 , 'volume is correct' ) ;
assert . strictEqual ( player . lastVolume _ ( ) , 0.2 , 'lastVolume_ is correct' ) ;
player . reset ( ) ;
assert . strictEqual ( player . currentSrc ( ) , '' , 'currentSrc is correct' ) ;
assert . deepEqual ( player . currentSource ( ) , { } , 'currentSource is correct' ) ;
assert . deepEqual ( player . currentSources ( ) , [ ] , 'currentSources is correct' ) ;
// Right now, the currentTime is not _really_ cached because it is always
// retrieved from the tech. However, for completeness, we set it to zero in
// the `resetCache_` method to ensure that if we do start actually caching it,
// we reset it along with everything else.
assert . strictEqual ( player . getCache ( ) . currentTime , 0 , 'currentTime is correct' ) ;
assert . ok ( isNaN ( player . duration ( ) ) , 'duration is correct' ) ;
assert . strictEqual ( player . playbackRate ( ) , 1 , 'playbackRate is correct' ) ;
2021-06-08 17:01:56 +02:00
assert . deepEqual ( player . playbackRates ( ) , [ ] , 'playbackRates is correct' ) ;
2019-01-03 22:14:54 +02:00
assert . strictEqual ( player . volume ( ) , 1 , 'volume is correct' ) ;
assert . strictEqual ( player . lastVolume _ ( ) , 1 , 'lastVolume_ is correct' ) ;
} ) ;
2019-01-03 20:49:34 +02:00
QUnit . test ( 'player#reset removes the poster' , function ( assert ) {
const player = TestHelpers . makePlayer ( ) ;
this . clock . tick ( 1 ) ;
player . poster ( 'foo.jpg' ) ;
assert . strictEqual ( player . poster ( ) , 'foo.jpg' , 'the poster was set' ) ;
player . reset ( ) ;
assert . strictEqual ( player . poster ( ) , '' , 'the poster was reset' ) ;
} ) ;
QUnit . test ( 'player#reset removes remote text tracks' , function ( assert ) {
const player = TestHelpers . makePlayer ( ) ;
this . clock . tick ( 1 ) ;
player . addRemoteTextTrack ( {
kind : 'captions' ,
src : 'foo.vtt' ,
language : 'en' ,
label : 'English'
} ) ;
assert . strictEqual ( player . remoteTextTracks ( ) . length , 1 , 'there is one RTT' ) ;
player . reset ( ) ;
assert . strictEqual ( player . remoteTextTracks ( ) . length , 0 , 'there are zero RTTs' ) ;
} ) ;
2022-05-04 17:43:52 +02:00
QUnit . test ( 'player#reset progress bar' , function ( assert ) {
let error ;
const player = TestHelpers . makePlayer ( ) ;
player . removeChild ( 'controlBar' ) ;
player . controlBar = null ;
try {
player . resetProgressBar _ ( ) ;
} catch ( e ) {
error = e ;
}
assert . notOk ( error , 'Function did not throw an error on resetProgressBar' ) ;
} ) ;
2018-11-02 22:43:32 +02:00
QUnit . test ( 'Remove waiting class after tech waiting when timeupdate shows a time change' , function ( assert ) {
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( ) ;
2018-11-02 22:43:32 +02:00
player . currentTime = ( ) => 1 ;
2016-03-07 21:48:15 +02:00
player . tech _ . trigger ( 'waiting' ) ;
2018-11-02 22:43:32 +02:00
assert . ok (
/vjs-waiting/ . test ( player . el ( ) . className ) ,
'vjs-waiting is added to the player el on tech waiting'
) ;
2016-03-07 21:48:15 +02:00
player . trigger ( 'timeupdate' ) ;
2018-11-02 22:43:32 +02:00
assert . ok (
/vjs-waiting/ . test ( player . el ( ) . className ) ,
'vjs-waiting still exists on the player el when time hasn\'t changed on timeupdate'
) ;
player . currentTime = ( ) => 2 ;
player . trigger ( 'timeupdate' ) ;
assert . notOk (
( /vjs-waiting/ ) . test ( player . el ( ) . className ) ,
'vjs-waiting removed from the player el when time has changed on timeupdate'
) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2016-03-07 21:48:15 +02:00
} ) ;
2016-03-25 20:06:39 +02:00
feat: Queue playback events when the playback rate is zero and we are seeking (#5024)
SourceHandlers that use MSE have a problem: if they push a segment into a SourceBuffer and then seek close to the end, playback will stall and/or there will be a massive downswitch in quality. The general approach to fixing this that was discussed on slack was by setting the playback rate of the player to zero, buffering all that was required, and then restoring the previous playback rate. In my implementation, I've done this in the source handler (see: videojs/videojs-contrib-hls#1374).
From the video.js perspective, it should ensure that the UI reflects the buffering status and that the player API behaves like you'd expect -- that is to say, that it will fire seeking immediately after a call to currentTime, and it will fire seeked, canplay, canplaythrough, and playing when everything is buffered.
2018-04-17 21:28:05 +02:00
QUnit . test ( 'Queues playing events when playback rate is zero while seeking' , function ( assert ) {
const player = TestHelpers . makePlayer ( { techOrder : [ 'html5' ] } ) ;
let canPlayCount = 0 ;
let canPlayThroughCount = 0 ;
let playingCount = 0 ;
let seekedCount = 0 ;
let seeking = false ;
player . on ( 'canplay' , ( ) => canPlayCount ++ ) ;
player . on ( 'canplaythrough' , ( ) => canPlayThroughCount ++ ) ;
player . on ( 'playing' , ( ) => playingCount ++ ) ;
player . on ( 'seeked' , ( ) => seekedCount ++ ) ;
player . tech _ . seeking = ( ) => {
return seeking ;
} ;
player . tech _ . setPlaybackRate ( 0 ) ;
player . tech _ . trigger ( 'ratechange' ) ;
player . tech _ . trigger ( 'canplay' ) ;
player . tech _ . trigger ( 'canplaythrough' ) ;
player . tech _ . trigger ( 'playing' ) ;
player . tech _ . trigger ( 'seeked' ) ;
assert . equal ( canPlayCount , 1 , 'canplay event dispatched when not seeking' ) ;
assert . equal ( canPlayThroughCount , 1 , 'canplaythrough event dispatched when not seeking' ) ;
assert . equal ( playingCount , 1 , 'playing event dispatched when not seeking' ) ;
assert . equal ( seekedCount , 1 , 'seeked event dispatched when not seeking' ) ;
seeking = true ;
player . tech _ . trigger ( 'canplay' ) ;
player . tech _ . trigger ( 'canplaythrough' ) ;
player . tech _ . trigger ( 'playing' ) ;
player . tech _ . trigger ( 'seeked' ) ;
assert . equal ( canPlayCount , 1 , 'canplay event not dispatched' ) ;
assert . equal ( canPlayThroughCount , 1 , 'canplaythrough event not dispatched' ) ;
assert . equal ( playingCount , 1 , 'playing event not dispatched' ) ;
assert . equal ( seekedCount , 1 , 'seeked event not dispatched' ) ;
seeking = false ;
player . tech _ . setPlaybackRate ( 1 ) ;
player . tech _ . trigger ( 'ratechange' ) ;
assert . equal ( canPlayCount , 2 , 'canplay event dispatched after playback rate restore' ) ;
assert . equal ( canPlayThroughCount , 2 , 'canplaythrough event dispatched after playback rate restore' ) ;
assert . equal ( playingCount , 2 , 'playing event dispatched after playback rate restore' ) ;
assert . equal ( seekedCount , 2 , 'seeked event dispatched after playback rate restore' ) ;
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'Make sure that player\'s style el respects VIDEOJS_NO_DYNAMIC_STYLE option' , function ( assert ) {
2016-03-25 20:06:39 +02:00
// clear the HEAD before running this test
let styles = document . querySelectorAll ( 'style' ) ;
let i = styles . length ;
2016-08-04 17:49:32 +02:00
2016-03-25 20:06:39 +02:00
while ( i -- ) {
2016-08-04 17:49:32 +02:00
const style = styles [ i ] ;
2016-03-25 20:06:39 +02:00
style . parentNode . removeChild ( style ) ;
}
let tag = TestHelpers . makeTag ( ) ;
2016-08-04 17:49:32 +02:00
2016-03-25 20:06:39 +02:00
tag . id = 'vjs-no-base-theme-tag' ;
tag . width = 600 ;
tag . height = 300 ;
window . VIDEOJS _NO _DYNAMIC _STYLE = true ;
2016-08-04 17:49:32 +02:00
TestHelpers . makePlayer ( { } , tag ) ;
2016-03-25 20:06:39 +02:00
styles = document . querySelectorAll ( 'style' ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( styles . length , 0 , 'we should not get any style elements included in the DOM' ) ;
2016-03-25 20:06:39 +02:00
window . VIDEOJS _NO _DYNAMIC _STYLE = false ;
2016-03-28 18:08:00 +02:00
tag = TestHelpers . makeTag ( ) ;
2016-08-04 17:49:32 +02:00
TestHelpers . makePlayer ( { } , tag ) ;
2016-03-25 20:06:39 +02:00
styles = document . querySelectorAll ( 'style' ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( styles . length , 1 , 'we should have one style element in the DOM' ) ;
assert . equal ( styles [ 0 ] . className , 'vjs-styles-dimensions' , 'the class name is the one we expected' ) ;
2016-03-25 20:06:39 +02:00
} ) ;
2016-08-12 19:51:31 +02:00
QUnit . test ( 'When VIDEOJS_NO_DYNAMIC_STYLE is set, apply sizing directly to the tech el' , function ( assert ) {
2016-03-25 20:06:39 +02:00
// clear the HEAD before running this test
2022-03-10 20:13:49 +02:00
const originalVjsNoDynamicStyling = window . VIDEOJS _NO _DYNAMIC _STYLE ;
2016-08-04 17:49:32 +02:00
const styles = document . querySelectorAll ( 'style' ) ;
2016-03-25 20:06:39 +02:00
let i = styles . length ;
2016-08-04 17:49:32 +02:00
2016-03-25 20:06:39 +02:00
while ( i -- ) {
2016-08-04 17:49:32 +02:00
const style = styles [ i ] ;
2016-03-25 20:06:39 +02:00
style . parentNode . removeChild ( style ) ;
}
2016-08-04 17:49:32 +02:00
const tag = TestHelpers . makeTag ( ) ;
2016-03-25 20:06:39 +02:00
tag . id = 'vjs-no-base-theme-tag' ;
tag . width = 600 ;
tag . height = 300 ;
window . VIDEOJS _NO _DYNAMIC _STYLE = true ;
2016-08-04 17:49:32 +02:00
const player = TestHelpers . makePlayer ( { } , tag ) ;
2016-03-25 20:06:39 +02:00
player . width ( 300 ) ;
player . height ( 600 ) ;
2016-08-12 19:51:31 +02:00
assert . equal ( player . tech _ . el ( ) . width , 300 , 'the width is equal to 300' ) ;
assert . equal ( player . tech _ . el ( ) . height , 600 , 'the height is equal 600' ) ;
2016-03-25 20:06:39 +02:00
player . width ( 600 ) ;
player . height ( 300 ) ;
2016-08-25 16:58:42 +02:00
2016-08-12 19:51:31 +02:00
assert . equal ( player . tech _ . el ( ) . width , 600 , 'the width is equal to 600' ) ;
assert . equal ( player . tech _ . el ( ) . height , 300 , 'the height is equal 300' ) ;
2016-08-25 16:58:42 +02:00
player . dispose ( ) ;
2022-03-10 20:13:49 +02:00
window . VIDEOJS _NO _DYNAMIC _STYLE = originalVjsNoDynamicStyling ;
2016-03-25 20:06:39 +02:00
} ) ;
2016-11-23 20:52:54 +02:00
2023-05-12 18:23:27 +02:00
QUnit . test ( 'should return the registered component' , function ( assert ) {
class CustomPlayer extends Player { }
assert . strictEqual ( videojs . registerComponent ( 'CustomPlayer' , CustomPlayer ) , CustomPlayer , 'the component is returned' ) ;
} ) ;
2016-11-23 20:52:54 +02:00
QUnit . test ( 'should allow to register custom player when any player has not been created' , function ( assert ) {
class CustomPlayer extends Player { }
videojs . registerComponent ( 'Player' , CustomPlayer ) ;
const tag = TestHelpers . makeTag ( ) ;
2017-11-17 01:11:55 +02:00
const fixture = document . getElementById ( 'qunit-fixture' ) ;
fixture . appendChild ( tag ) ;
2016-11-23 20:52:54 +02:00
const player = videojs ( tag ) ;
assert . equal ( player instanceof CustomPlayer , true , 'player is custom' ) ;
player . dispose ( ) ;
2016-12-02 21:17:36 +02:00
// reset the Player to the original value;
videojs . registerComponent ( 'Player' , Player ) ;
2016-11-23 20:52:54 +02:00
} ) ;
QUnit . test ( 'should not allow to register custom player when any player has been created' , function ( assert ) {
const tag = TestHelpers . makeTag ( ) ;
2017-11-17 01:11:55 +02:00
const fixture = document . getElementById ( 'qunit-fixture' ) ;
fixture . appendChild ( tag ) ;
2016-11-23 20:52:54 +02:00
const player = videojs ( tag ) ;
class CustomPlayer extends Player { }
2016-12-02 21:17:36 +02:00
assert . throws ( function ( ) {
2016-11-23 20:52:54 +02:00
videojs . registerComponent ( 'Player' , CustomPlayer ) ;
2016-12-02 21:17:36 +02:00
} , 'Can not register Player component after player has been created' ) ;
player . dispose ( ) ;
2016-11-23 20:52:54 +02:00
2016-12-02 21:17:36 +02:00
// reset the Player to the original value;
videojs . registerComponent ( 'Player' , Player ) ;
2016-11-23 20:52:54 +02:00
} ) ;
2017-01-18 08:52:23 +02:00
2023-05-31 17:12:57 +02:00
QUnit . test ( 'setters getters passed to tech' , function ( assert ) {
const tag = TestHelpers . makeTag ( ) ;
const fixture = document . getElementById ( 'qunit-fixture' ) ;
fixture . appendChild ( tag ) ;
const player = videojs ( tag , {
techOrder : [ 'techFaker' ]
} ) ;
const setSpy = sinon . spy ( player . tech _ , 'setDefaultMuted' ) ;
const getSpy = sinon . spy ( player . tech _ , 'defaultMuted' ) ;
player . defaultMuted ( true ) ;
player . defaultMuted ( ) ;
assert . ok ( setSpy . calledWith ( true ) , 'setSpy called' ) ;
assert . ok ( getSpy . called ) ;
setSpy . restore ( ) ;
getSpy . restore ( ) ;
} ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
QUnit . test ( 'techGet runs through middleware if allowedGetter' , function ( assert ) {
let cts = 0 ;
2019-08-29 22:42:42 +02:00
let muts = 0 ;
2019-04-23 19:42:52 +02:00
let vols = 0 ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
let durs = 0 ;
2018-01-30 18:30:42 +02:00
let lps = 0 ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
2017-01-27 22:09:27 +02:00
videojs . use ( 'video/foo' , ( ) => ( {
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
currentTime ( ) {
cts ++ ;
} ,
duration ( ) {
durs ++ ;
} ,
2018-01-30 18:30:42 +02:00
loop ( ) {
lps ++ ;
2019-08-29 22:42:42 +02:00
} ,
muted ( ) {
muts ++ ;
} ,
volume ( ) {
vols ++ ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
}
2017-01-27 22:09:27 +02:00
} ) ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
const tag = TestHelpers . makeTag ( ) ;
2017-11-17 01:11:55 +02:00
const fixture = document . getElementById ( 'qunit-fixture' ) ;
fixture . appendChild ( tag ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
const player = videojs ( tag , {
techOrder : [ 'techFaker' ]
} ) ;
2017-01-27 22:09:27 +02:00
player . middleware _ = [ middleware . getMiddleware ( 'video/foo' ) [ 0 ] ( player ) ] ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
player . techGet _ ( 'currentTime' ) ;
2019-04-23 19:42:52 +02:00
player . techGet _ ( 'volume' ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
player . techGet _ ( 'duration' ) ;
2018-01-30 18:30:42 +02:00
player . techGet _ ( 'loop' ) ;
2019-08-29 22:42:42 +02:00
player . techGet _ ( 'muted' ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
assert . equal ( cts , 1 , 'currentTime is allowed' ) ;
2019-04-23 19:42:52 +02:00
assert . equal ( vols , 1 , 'volume is allowed' ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
assert . equal ( durs , 1 , 'duration is allowed' ) ;
2019-08-29 22:42:42 +02:00
assert . equal ( muts , 1 , 'muted is allowed' ) ;
2018-01-30 18:30:42 +02:00
assert . equal ( lps , 0 , 'loop is not allowed' ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
middleware . getMiddleware ( 'video/foo' ) . pop ( ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'techCall runs through middleware if allowedSetter' , function ( assert ) {
let cts = 0 ;
2019-08-29 22:42:42 +02:00
let muts = false ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
let vols = 0 ;
2019-04-23 19:42:52 +02:00
let prs = 0 ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
2017-01-27 22:09:27 +02:00
videojs . use ( 'video/foo' , ( ) => ( {
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
setCurrentTime ( ct ) {
cts ++ ;
return ct ;
} ,
setVolume ( ) {
vols ++ ;
2019-04-23 19:42:52 +02:00
return vols ;
} ,
2019-08-29 22:42:42 +02:00
setMuted ( ) {
muts = true ;
return muts ;
} ,
2019-04-23 19:42:52 +02:00
setPlaybackRate ( ) {
prs ++ ;
return prs ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
}
2017-01-27 22:09:27 +02:00
} ) ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
const tag = TestHelpers . makeTag ( ) ;
2017-11-17 01:11:55 +02:00
const fixture = document . getElementById ( 'qunit-fixture' ) ;
fixture . appendChild ( tag ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
const player = videojs ( tag , {
techOrder : [ 'techFaker' ]
} ) ;
2017-01-27 22:09:27 +02:00
player . middleware _ = [ middleware . getMiddleware ( 'video/foo' ) [ 0 ] ( player ) ] ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
this . clock . tick ( 1 ) ;
player . techCall _ ( 'setCurrentTime' , 10 ) ;
player . techCall _ ( 'setVolume' , 0.5 ) ;
2019-08-29 22:42:42 +02:00
player . techCall _ ( 'setMuted' , true ) ;
2019-04-23 19:42:52 +02:00
player . techCall _ ( 'setPlaybackRate' , 0.75 ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
this . clock . tick ( 1 ) ;
assert . equal ( cts , 1 , 'setCurrentTime is allowed' ) ;
2019-04-23 19:42:52 +02:00
assert . equal ( vols , 1 , 'setVolume is allowed' ) ;
2019-08-29 22:42:42 +02:00
assert . equal ( muts , true , 'setMuted is allowed' ) ;
2019-04-23 19:42:52 +02:00
assert . equal ( prs , 0 , 'setPlaybackRate is not allowed' ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
middleware . getMiddleware ( 'video/foo' ) . pop ( ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'src selects tech based on middleware' , function ( assert ) {
2017-02-02 21:34:33 +02:00
const oldTechs = Tech . techs _ ;
const oldDefaultTechOrder = Tech . defaultTechOrder _ ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
class FooTech extends Html5 { }
class BarTech extends Html5 { }
FooTech . isSupported = ( ) => true ;
FooTech . canPlayType = ( type ) => type === 'video/mp4' ;
FooTech . canPlaySource = ( src ) => FooTech . canPlayType ( src . type ) ;
BarTech . isSupported = ( ) => true ;
2022-07-27 23:05:09 +02:00
BarTech . canPlayType = ( type ) => type === 'video/youtube' ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
BarTech . canPlaySource = ( src ) => BarTech . canPlayType ( src . type ) ;
videojs . registerTech ( 'FooTech' , FooTech ) ;
videojs . registerTech ( 'BarTech' , BarTech ) ;
2017-01-27 22:09:27 +02:00
videojs . use ( 'video/foo' , ( ) => ( {
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
setSource ( src , next ) {
next ( null , {
src : 'http://example.com/video.mp4' ,
type : 'video/mp4'
} ) ;
}
2017-01-27 22:09:27 +02:00
} ) ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
2017-01-27 22:09:27 +02:00
videojs . use ( 'video/bar' , ( ) => ( {
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
setSource ( src , next ) {
next ( null , {
2022-07-27 23:05:09 +02:00
src : 'https://www.youtube.com/watch?v=C0DPdy98e4c' ,
type : 'video/youtube'
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
} ) ;
}
2017-01-27 22:09:27 +02:00
} ) ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
2017-11-17 01:11:55 +02:00
const fixture = document . getElementById ( 'qunit-fixture' ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
const tag = TestHelpers . makeTag ( ) ;
2017-11-17 01:11:55 +02:00
fixture . appendChild ( tag ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
const player = videojs ( tag , {
techOrder : [ 'fooTech' , 'barTech' ]
} ) ;
player . src ( {
src : 'foo' ,
type : 'video/foo'
} ) ;
this . clock . tick ( 1 ) ;
assert . equal ( player . techName _ , 'FooTech' , 'the FooTech (html5) tech is chosen' ) ;
player . src ( {
src : 'bar' ,
type : 'video/bar'
} ) ;
this . clock . tick ( 1 ) ;
2022-07-27 23:05:09 +02:00
assert . equal ( player . techName _ , 'BarTech' , 'the BarTech (Youtube) tech is chosen' ) ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
middleware . getMiddleware ( 'video/foo' ) . pop ( ) ;
middleware . getMiddleware ( 'video/bar' ) . pop ( ) ;
player . dispose ( ) ;
delete Tech . techs _ . FooTech ;
delete Tech . techs _ . BarTech ;
2017-02-02 21:34:33 +02:00
Tech . defaultTechOrder _ = oldDefaultTechOrder ;
Tech . techs _ = oldTechs ;
feat: middleware (#3788)
Add middleware support. Middleware can function as go-between between the player and the tech. For example, it can modify the duration that the tech returns to the player. In addition, middleware allow for supporting custom video sources and types.
Currently, middleware can only intercept timeline methods like duration, currentTime, and setCurrentTime.
For example,
```js
videojs.use('video/foo', {
setSource(src, next) {
next(null, {
src: 'http://example.com/video.mp4',
type: 'video/mp4'
});
}
});
```
Will allow you to set a source with type `video/foo` which will play back `video.mp4`.
This makes setting the source asynchronous, which aligns it with the spec a bit more. Methods like play can still be called synchronously on the player after setting the source and the player will play once the source has loaded.
`sourceOrder` option was removed as well and it will now always use source ordering.
BREAKING CHANGE: setting the source is now asynchronous. `sourceOrder` option removed and made the default.
2017-01-20 00:29:09 +02:00
} ) ;
2017-04-12 23:17:33 +02:00
QUnit . test ( 'src_ does not call loadTech is name is titleCaseEquals' , function ( assert ) {
let loadTechCalled = 0 ;
const playerProxy = {
selectSource ( ) {
return {
tech : 'html5'
} ;
} ,
techName _ : 'Html5' ,
ready ( ) { } ,
loadTech _ ( ) {
loadTechCalled ++ ;
}
} ;
Player . prototype . src _ . call ( playerProxy ) ;
assert . equal ( loadTechCalled , 0 , 'loadTech was not called' ) ;
} ) ;
2017-01-18 08:52:23 +02:00
QUnit . test ( 'options: plugins' , function ( assert ) {
const optionsSpy = sinon . spy ( ) ;
Plugin . registerPlugin ( 'foo' , ( options ) => {
optionsSpy ( options ) ;
} ) ;
const player = TestHelpers . makePlayer ( {
plugins : {
foo : {
bar : 1
}
}
} ) ;
assert . strictEqual ( optionsSpy . callCount , 1 , 'the plugin was set up' ) ;
assert . deepEqual ( optionsSpy . getCall ( 0 ) . args [ 0 ] , { bar : 1 } , 'the plugin got the expected options' ) ;
assert . throws (
( ) => {
TestHelpers . makePlayer ( {
plugins : {
nope : { }
}
} ) ;
} ,
new Error ( 'plugin "nope" does not exist' ) ,
'plugins that do not exist cause the player to throw'
) ;
player . dispose ( ) ;
Plugin . deregisterPlugin ( 'foo' ) ;
} ) ;
2017-05-11 23:15:12 +02:00
QUnit . test ( 'should add a class with major version' , function ( assert ) {
2019-08-30 20:56:41 +02:00
const majorVersion = pkg . version . split ( '.' ) [ 0 ] ;
2017-05-11 23:15:12 +02:00
const player = TestHelpers . makePlayer ( ) ;
assert . ok ( player . hasClass ( 'vjs-v' + majorVersion ) , 'the version class should be added to the player' ) ;
player . dispose ( ) ;
} ) ;
2017-07-14 20:20:37 +02:00
QUnit . test ( 'player.duration() returns NaN if player.cache_.duration is undefined' , function ( assert ) {
const player = TestHelpers . makePlayer ( ) ;
player . cache _ . duration = undefined ;
2023-03-22 16:00:01 +02:00
assert . ok ( Number . isNaN ( player . duration ( ) ) , 'returned NaN for unknown duration' ) ;
2017-07-14 20:20:37 +02:00
} ) ;
QUnit . test ( 'player.duration() returns player.cache_.duration if it is defined' , function ( assert ) {
const player = TestHelpers . makePlayer ( ) ;
player . cache _ . duration = 200 ;
assert . equal ( player . duration ( ) , 200 , 'returned correct integer duration' ) ;
player . cache _ . duration = 942 ;
assert . equal ( player . duration ( ) , 942 , 'returned correct integer duration' ) ;
} ) ;
QUnit . test ( 'player.duration() sets the value of player.cache_.duration' , function ( assert ) {
const player = TestHelpers . makePlayer ( ) ;
// set an arbitrary initial cached duration value for testing the setter functionality
player . cache _ . duration = 1 ;
player . duration ( NaN ) ;
assert . ok ( Number . isNaN ( player . duration ( ) ) , 'duration() set and get NaN duration value' ) ;
player . duration ( 200 ) ;
assert . equal ( player . duration ( ) , 200 , 'duration() set and get integer duration value' ) ;
} ) ;
2018-03-07 21:31:50 +02:00
QUnit . test ( 'setPoster in tech with `techCanOverridePoster` in player should override poster' , function ( assert ) {
const player = TestHelpers . makePlayer ( {
techCanOverridePoster : true
} ) ;
const posterchangeSpy = sinon . spy ( ) ;
const firstPosterUrl = 'https://wherever.test/test.jpg' ;
const techPosterUrl = 'https://somewhere.text/my/image.png' ;
assert . equal ( player . options _ . techCanOverridePoster , true , 'make sure player option was passed correctly' ) ;
assert . equal ( player . tech _ . options _ . canOverridePoster , true , 'make sure tech option was passed correctly' ) ;
player . on ( 'posterchange' , posterchangeSpy ) ;
player . poster ( '' ) ;
assert . ok ( posterchangeSpy . notCalled , 'posterchangeSpy not called when no change of poster' ) ;
assert . equal ( player . isPosterFromTech _ , false , "ensure tech didn't change poster after empty call from player" ) ;
player . poster ( firstPosterUrl ) ;
assert . ok ( posterchangeSpy . calledOnce , 'posterchangeSpy only called once on update' ) ;
assert . equal ( player . poster ( ) , firstPosterUrl , "ensure tech didn't change poster after setting from player" ) ;
assert . equal ( player . isPosterFromTech _ , false , "ensure player didn't mark poster as changed by the tech" ) ;
2019-04-24 16:22:48 +02:00
posterchangeSpy . resetHistory ( ) ;
2018-03-07 21:31:50 +02:00
player . tech _ . setPoster ( techPosterUrl ) ;
assert . ok ( posterchangeSpy . calledOnce , "posterchangeSpy should've been called" ) ;
assert . equal ( player . isPosterFromTech _ , true , 'ensure player marked poster as set by tech after the fact' ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'setPoster in tech WITHOUT `techCanOverridePoster` in player should NOT override poster' , function ( assert ) {
const player = TestHelpers . makePlayer ( ) ;
const posterchangeSpy = sinon . spy ( ) ;
const firstPosterUrl = 'https://wherever.test/test.jpg' ;
const techPosterUrl = 'https://somewhere.test/my/image.png' ;
assert . equal ( player . options _ . techCanOverridePoster , undefined , "ensure player option wasn't unwittingly set" ) ;
assert . equal ( player . tech _ . options _ . canOverridePoster , false , "ensure tech option wasn't unwittinyly set" ) ;
player . on ( 'posterchange' , posterchangeSpy ) ;
player . poster ( firstPosterUrl ) ;
assert . ok ( posterchangeSpy . calledOnce , 'posterchangeSpy only called once on update' ) ;
assert . equal ( player . poster ( ) , firstPosterUrl , "ensure tech didn't change poster after setting from player" ) ;
assert . equal ( player . isPosterFromTech _ , false , "ensure player didn't mark poster as changed by the tech" ) ;
2019-04-24 16:22:48 +02:00
posterchangeSpy . resetHistory ( ) ;
2018-03-07 21:31:50 +02:00
player . tech _ . setPoster ( techPosterUrl ) ;
assert . ok ( posterchangeSpy . notCalled , "posterchangeSpy shouldn't have been called" ) ;
assert . equal ( player . isPosterFromTech _ , false , "ensure tech didn't change poster because player option was false" ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'disposing a tech that set a poster, should unset the poster' , function ( assert ) {
const player = TestHelpers . makePlayer ( {
techCanOverridePoster : true
} ) ;
const techPosterUrl = 'https://somewhere.test/my/image.png' ;
assert . equal ( player . options _ . techCanOverridePoster , true , 'make sure player option was passed correctly' ) ;
assert . equal ( player . tech _ . options _ . canOverridePoster , true , 'make sure tech option was passed correctly' ) ;
player . tech _ . setPoster ( techPosterUrl ) ;
assert . equal ( player . poster ( ) , techPosterUrl , 'player poster should equal tech poster' ) ;
assert . equal ( player . isPosterFromTech _ , true , 'setting the poster with the tech should be remembered in the player' ) ;
player . unloadTech _ ( ) ;
assert . equal ( player . poster ( ) , '' , 'ensure poster set by poster is unset after tech disposal' ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'disposing a tech that dit NOT set a poster, should keep the poster' , function ( assert ) {
const player = TestHelpers . makePlayer ( {
techCanOverridePoster : true
} ) ;
const posterUrl = 'https://myposter.test/lol.jpg' ;
assert . equal ( player . options _ . techCanOverridePoster , true , 'make sure player option was passed correctly' ) ;
assert . equal ( player . tech _ . options _ . canOverridePoster , true , 'make sure tech option was passed correctly' ) ;
player . poster ( posterUrl ) ;
assert . equal ( player . poster ( ) , posterUrl , 'player poster should NOT have changed' ) ;
assert . equal ( player . isPosterFromTech _ , false , 'player should mark poster as set by itself' ) ;
player . unloadTech _ ( ) ;
assert . equal ( player . poster ( ) , posterUrl , 'player poster should stay the same after unloading / dispoing tech' ) ;
player . dispose ( ) ;
} ) ;
2018-06-11 19:47:36 +02:00
QUnit . test ( 'source options are retained' , function ( assert ) {
const player = TestHelpers . makePlayer ( ) ;
const source = {
src : 'https://some.url' ,
type : 'someType' ,
sourceOption : 'someOption'
} ;
player . src ( source ) ;
assert . equal ( player . currentSource ( ) . sourceOption , 'someOption' , 'source option retained' ) ;
} ) ;
2018-12-11 21:23:13 +02:00
QUnit . test ( 'setting children to false individually, does not cause an assertion' , function ( assert ) {
const defaultChildren = Player . prototype . options _ . children ;
defaultChildren . forEach ( ( childName ) => {
const options = { } ;
options [ childName ] = false ;
const player = TestHelpers . makePlayer ( options ) ;
this . clock . tick ( 1000 ) ;
player . triggerReady ( ) ;
player . dispose ( ) ;
assert . ok ( true , ` ${ childName } : false. did not cause an assertion ` ) ;
} ) ;
} ) ;
QUnit . test ( 'setting all children to false, does not cause an assertion' , function ( assert ) {
const defaultChildren = Player . prototype . options _ . children ;
const options = { } ;
defaultChildren . forEach ( ( childName ) => {
options [ childName ] = false ;
} ) ;
const player = TestHelpers . makePlayer ( options ) ;
this . clock . tick ( 1000 ) ;
player . triggerReady ( ) ;
player . dispose ( ) ;
assert . ok ( true , 'did not cause an assertion' ) ;
} ) ;
2018-12-26 20:03:36 +02:00
QUnit . test ( 'controlBar behaviour with mouseenter and mouseleave events' , function ( assert ) {
const player = TestHelpers . makePlayer ( ) ;
player . listenForUserActivity _ ( ) ;
assert . equal ( player . options _ . inactivityTimeout , 2000 , 'inactivityTimeout default value is 2000' ) ;
const el = player . getChild ( 'controlBar' ) . el ( ) ;
// move mouse to controlBar
Events . trigger ( el , 'mouseenter' ) ;
assert . equal ( player . options _ . inactivityTimeout , 0 , 'mouseenter on control-bar, inactivityTimeout is set to 0' ) ;
// move mouse out of controlBar bounds
Events . trigger ( el , 'mouseleave' ) ;
assert . equal ( player . options _ . inactivityTimeout , player . cache _ . inactivityTimeout , 'mouse leaves control-bar, inactivityTimeout is set to default value (2000)' ) ;
player . dispose ( ) ;
} ) ;
2020-03-25 23:54:51 +02:00
QUnit . test ( 'Should be able to set a currentTime after player initialization as soon the canplay event is fired' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
player . src ( 'xyz.mp4' ) ;
player . currentTime ( 500 ) ;
assert . strictEqual ( player . currentTime ( ) , 0 , 'currentTime value was not changed' ) ;
this . clock . tick ( 100 ) ;
player . trigger ( 'canplay' ) ;
assert . strictEqual ( player . currentTime ( ) , 500 , 'currentTime value is the one passed after initialization' ) ;
} ) ;
QUnit . test ( 'Should accept multiple calls to currentTime after player initialization and apply the last value as soon the canplay event is fired' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
const spyInitTime = sinon . spy ( player , 'applyInitTime_' ) ;
const spyCurrentTime = sinon . spy ( player , 'currentTime' ) ;
player . src ( 'xyz.mp4' ) ;
player . currentTime ( 500 ) ;
player . currentTime ( 600 ) ;
player . currentTime ( 700 ) ;
player . currentTime ( 800 ) ;
this . clock . tick ( 100 ) ;
player . trigger ( 'canplay' ) ;
assert . equal ( spyInitTime . callCount , 1 , 'After multiple calls to currentTime just apply the last one' ) ;
assert . ok ( spyCurrentTime . calledAfter ( spyInitTime ) , 'currentTime was called on canplay event listener' ) ;
assert . equal ( player . currentTime ( ) , 800 , 'The last value passed is stored as the currentTime value' ) ;
} ) ;
2020-06-19 20:36:43 +02:00
2023-06-01 14:50:29 +02:00
QUnit . test ( 'Should be able to set the cache currentTime after player initialization as soon the canplay event is fired' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
player . src ( 'xyz.mp4' ) ;
player . currentTime ( 500 ) ;
assert . strictEqual ( player . getCache ( ) . currentTime , 0 , 'cache currentTime value was not changed' ) ;
this . clock . tick ( 100 ) ;
player . trigger ( 'canplay' ) ;
assert . strictEqual ( player . getCache ( ) . currentTime , 500 , 'cache currentTime value is the one passed after initialization' ) ;
} ) ;
2020-07-10 18:24:58 +02:00
QUnit . test ( 'Should fire debugon event when debug mode is enabled' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
const debugOnSpy = sinon . spy ( ) ;
player . on ( 'debugon' , debugOnSpy ) ;
player . debug ( true ) ;
assert . ok ( debugOnSpy . calledOnce , 'debugon event was fired' ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'Should fire debugoff event when debug mode is disabled' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
const debugOffSpy = sinon . spy ( ) ;
player . on ( 'debugoff' , debugOffSpy ) ;
player . debug ( false ) ;
assert . ok ( debugOffSpy . calledOnce , 'debugoff event was fired' ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'Should enable debug mode and store log level when calling options' , function ( assert ) {
const player = TestHelpers . makePlayer ( { debug : true } ) ;
assert . ok ( player . previousLogLevel _ , 'debug' , 'previous log level is stored when enabling debug' ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'Should restore previous log level when disabling debug mode' , function ( assert ) {
const player = TestHelpers . makePlayer ( ) ;
player . log . level ( 'error' ) ;
player . debug ( true ) ;
assert . ok ( player . log . level ( ) , 'debug' , 'log level is debug when debug is enabled' ) ;
player . debug ( false ) ;
assert . ok ( player . log . level ( ) , 'error' , 'previous log level was restored' ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'Should return if debug is enabled or disabled' , function ( assert ) {
const player = TestHelpers . makePlayer ( ) ;
player . debug ( true ) ;
const enabled = player . debug ( ) ;
assert . ok ( enabled ) ;
player . debug ( false ) ;
const disabled = player . debug ( ) ;
assert . notOk ( disabled ) ;
player . dispose ( ) ;
} ) ;
2020-06-19 21:09:25 +02:00
const testOrSkip = 'pictureInPictureEnabled' in document ? 'test' : 'skip' ;
QUnit [ testOrSkip ] ( 'Should only allow requestPictureInPicture if the tech supports it' , function ( assert ) {
2020-06-19 20:36:43 +02:00
const player = TestHelpers . makePlayer ( { } ) ;
let count = 0 ;
player . tech _ . el _ = {
disablePictureInPicture : false ,
requestPictureInPicture ( ) {
count ++ ;
}
} ;
player . tech _ . requestPictureInPicture = function ( ) {
return player . tech _ . el _ . requestPictureInPicture ( ) ;
} ;
player . tech _ . disablePictureInPicture = function ( ) {
return this . el _ . disablePictureInPicture ;
} ;
player . requestPictureInPicture ( ) ;
assert . equal ( count , 1 , 'requestPictureInPicture passed through to supporting tech' ) ;
player . tech _ . el _ . disablePictureInPicture = true ;
2023-04-04 22:44:16 +02:00
player . requestPictureInPicture ( ) . catch ( _ => { } ) ;
2020-06-19 20:36:43 +02:00
assert . equal ( count , 1 , 'requestPictureInPicture not passed through when disabled on tech' ) ;
delete player . tech _ . el _ . disablePictureInPicture ;
2023-04-04 22:44:16 +02:00
player . requestPictureInPicture ( ) . catch ( _ => { } ) ;
2020-06-19 20:36:43 +02:00
assert . equal ( count , 1 , 'requestPictureInPicture not passed through when tech does not support' ) ;
} ) ;
2021-06-08 17:01:56 +02:00
2023-04-04 22:44:16 +02:00
QUnit . test ( 'document pictureinpicture is opt-in' , function ( assert ) {
const done = assert . async ( ) ;
const player = TestHelpers . makePlayer ( {
disablePictureInPicture : true
} ) ;
const testPiPObj = { } ;
if ( ! window . documentPictureInPicture ) {
window . documentPictureInPicture = testPiPObj ;
}
player . requestPictureInPicture ( ) . catch ( e => {
assert . equal ( e , 'No PiP mode is available' , 'docPiP not used when not enabled' ) ;
2023-07-12 19:43:17 +02:00
} ) . then ( _ => {
2023-04-04 22:44:16 +02:00
if ( window . documentPictureInPicture === testPiPObj ) {
delete window . documentPictureInPicture ;
}
done ( ) ;
} ) ;
} ) ;
QUnit . test ( 'docPiP is used in preference to winPiP' , function ( assert ) {
assert . expect ( 2 ) ;
const done = assert . async ( ) ;
const player = TestHelpers . makePlayer ( {
enableDocumentPictureInPicture : true
} ) ;
let count = 0 ;
player . tech _ . el _ = {
disablePictureInPicture : false ,
requestPictureInPicture ( ) {
count ++ ;
}
} ;
const testPiPObj = {
requestWindow ( ) {
return Promise . resolve ( { } ) ;
}
} ;
if ( ! window . documentPictureInPicture ) {
window . documentPictureInPicture = testPiPObj ;
}
// Test isn't concerned with whether the browser allows the request,
player . requestPictureInPicture ( ) . then ( _ => {
assert . ok ( true , 'docPiP was called' ) ;
} ) . catch ( _ => {
assert . ok ( true , 'docPiP was called' ) ;
2023-07-12 19:43:17 +02:00
} ) . then ( _ => {
2023-04-04 22:44:16 +02:00
assert . equal ( 0 , count , 'requestPictureInPicture not passed to tech' ) ;
if ( window . documentPictureInPicture === testPiPObj ) {
delete window . documentPictureInPicture ;
}
done ( ) ;
} ) ;
} ) ;
QUnit . test ( 'docPiP moves player and triggers events' , function ( assert ) {
const done = assert . async ( ) ;
const player = TestHelpers . makePlayer ( {
enableDocumentPictureInPicture : true
} ) ;
const playerParent = player . el ( ) . parentElement ;
player . videoHeight = ( ) => 9 ;
player . videoWidth = ( ) => 16 ;
const counts = {
enterpictureinpicture : 0 ,
leavepictureinpicture : 0
} ;
player . on ( Object . keys ( counts ) , function ( e ) {
counts [ e . type ] ++ ;
} ) ;
const fakePiPWindow = document . createElement ( 'div' ) ;
fakePiPWindow . document = {
2023-07-07 15:56:54 +02:00
head : document . createElement ( 'div' ) ,
2023-04-04 22:44:16 +02:00
body : document . createElement ( 'div' )
} ;
fakePiPWindow . querySelector = function ( sel ) {
return fakePiPWindow . document . body . querySelector ( sel ) ;
} ;
fakePiPWindow . close = function ( ) {
2023-07-04 13:43:09 +02:00
fakePiPWindow . dispatchEvent ( new Event ( 'pagehide' ) ) ;
2023-04-04 22:44:16 +02:00
delete window . documentPictureInPicture . window ;
} ;
const testPiPObj = {
requestWindow ( ) {
window . documentPictureInPicture . window = fakePiPWindow ;
return Promise . resolve ( fakePiPWindow ) ;
}
} ;
if ( ! window . documentPictureInPicture ) {
window . documentPictureInPicture = testPiPObj ;
}
player . requestPictureInPicture ( ) . then ( win => {
assert . ok ( player . el ( ) . parentElement === win . document . body , 'player el was moved' ) ;
assert . ok ( playerParent . querySelector ( '.vjs-pip-container' ) , 'placeholder el was added' ) ;
assert . ok ( player . isInPictureInPicture ( ) , 'player is in pip state' ) ;
assert . equal ( counts . enterpictureinpicture , 1 , '`enterpictureinpicture` triggered' ) ;
player . exitPictureInPicture ( ) . then ( _ => {
assert . ok ( player . el ( ) . parentElement === playerParent , 'player el was restored' ) ;
assert . notOk ( playerParent . querySelector ( '.vjs-pip-container' ) , 'placeholder el was removed' ) ;
assert . notOk ( player . isInPictureInPicture ( ) , 'player is not in pip state' ) ;
assert . equal ( counts . leavepictureinpicture , 1 , '`leavepictureinpicture` triggered' ) ;
if ( window . documentPictureInPicture === testPiPObj ) {
delete window . documentPictureInPicture ;
}
done ( ) ;
} ) ;
} ) . catch ( e => {
if ( e === 'No PiP mode is available' ) {
assert . ok ( true , 'Test skipped because PiP not available' ) ;
} else if ( e . name && e . name === 'NotAllowedError' ) {
assert . ok ( true , 'Test skipped because PiP not allowed' ) ;
} else {
assert . notOk ( true , 'An unexpected error occurred' ) ;
}
if ( window . documentPictureInPicture === testPiPObj ) {
delete window . documentPictureInPicture ;
}
done ( ) ;
} ) ;
} ) ;
2021-06-08 17:01:56 +02:00
QUnit . test ( 'playbackRates should trigger a playbackrateschange event' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
const rates = [ ] ;
let rateschangeCount = 0 ;
player . on ( 'playbackrateschange' , function ( ) {
rates . push ( player . playbackRates ( ) ) ;
rateschangeCount ++ ;
} ) ;
player . playbackRates ( [ 1 , 2 , 3 ] ) ;
player . playbackRates ( [ ] ) ;
player . playbackRates ( [ 1 , 4 ] ) ;
assert . equal ( rateschangeCount , 3 , 'we got 3 playbackrateschange events' ) ;
assert . deepEqual ( rates [ 0 ] , [ 1 , 2 , 3 ] , 'first rates is 1,2,3' ) ;
assert . deepEqual ( rates [ 1 ] , [ ] , 'second rates is empty' ) ;
assert . deepEqual ( rates [ 2 ] , [ 1 , 4 ] , 'third rates is 1,4' ) ;
player . dispose ( ) ;
} ) ;
QUnit . test ( 'playbackRates only accepts arrays of numbers' , function ( assert ) {
const player = TestHelpers . makePlayer ( ) ;
let rateschangeCount = 0 ;
player . on ( 'playbackrateschange' , function ( ) {
rateschangeCount ++ ;
} ) ;
player . playbackRates ( [ 1 , 2 , 3 ] ) ;
assert . equal ( rateschangeCount , 1 , 'we got a playbackrateschange event' ) ;
player . playbackRates ( 'hello' ) ;
assert . equal ( rateschangeCount , 1 , 'we did not get a playbackrateschange event' ) ;
player . playbackRates ( [ 1 , 4 ] ) ;
assert . equal ( rateschangeCount , 2 , 'we got a playbackrateschange event' ) ;
player . playbackRates ( 5 ) ;
assert . equal ( rateschangeCount , 2 , 'we did not get a playbackrateschange event' ) ;
player . playbackRates ( [ 'hello' , '2' , 'why?' ] ) ;
assert . equal ( rateschangeCount , 2 , 'we did not get a playbackrateschange event' ) ;
player . dispose ( ) ;
} ) ;
2022-03-10 20:13:49 +02:00
QUnit . test ( 'audioOnlyMode can be set by option' , function ( assert ) {
2022-03-17 23:10:33 +02:00
assert . expect ( 3 ) ;
2022-03-10 20:13:49 +02:00
2022-03-11 19:24:50 +02:00
const done = assert . async ( ) ;
2022-03-10 20:13:49 +02:00
const player = TestHelpers . makePlayer ( { audioOnlyMode : true } ) ;
2022-03-17 23:10:33 +02:00
player . trigger ( 'ready' ) ;
2022-03-10 20:13:49 +02:00
player . one ( 'audioonlymodechange' , ( ) => {
assert . equal ( player . audioOnlyMode ( ) , true , 'asynchronously set via option' ) ;
assert . equal ( player . hasClass ( 'vjs-audio-only-mode' ) , true , 'class added asynchronously' ) ;
2022-03-11 19:24:50 +02:00
done ( ) ;
2022-03-10 20:13:49 +02:00
} ) ;
assert . equal ( player . hasClass ( 'vjs-audio-only-mode' ) , false , 'initially does not have class' ) ;
} ) ;
QUnit . test ( 'audioOnlyMode(true) returns Promise when promises are supported' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
2022-03-17 23:10:33 +02:00
player . trigger ( 'ready' ) ;
2022-03-10 20:13:49 +02:00
const returnValTrue = player . audioOnlyMode ( true ) ;
if ( window . Promise ) {
assert . ok ( returnValTrue instanceof window . Promise , 'audioOnlyMode(true) returns Promise when supported' ) ;
}
} ) ;
QUnit . test ( 'audioOnlyMode(false) returns Promise when promises are supported' , function ( assert ) {
2022-03-11 19:24:50 +02:00
const done = assert . async ( ) ;
2022-03-10 20:13:49 +02:00
const player = TestHelpers . makePlayer ( { audioOnlyMode : true } ) ;
2022-03-17 23:10:33 +02:00
player . trigger ( 'ready' ) ;
2022-03-10 20:13:49 +02:00
player . one ( 'audioonlymodechange' , ( ) => {
const returnValFalse = player . audioOnlyMode ( false ) ;
if ( window . Promise ) {
assert . ok ( returnValFalse instanceof window . Promise , 'audioOnlyMode(false) returns Promise when supported' ) ;
2022-03-11 19:24:50 +02:00
done ( ) ;
2022-03-10 20:13:49 +02:00
}
} ) ;
} ) ;
QUnit . test ( 'audioOnlyMode() getter returns Boolean' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
2022-03-17 23:10:33 +02:00
player . trigger ( 'ready' ) ;
2022-03-10 20:13:49 +02:00
assert . ok ( typeof player . audioOnlyMode ( ) === 'boolean' , 'getter correctly returns boolean' ) ;
} ) ;
QUnit . test ( 'audioOnlyMode() gets the correct audioOnlyMode state' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
2022-03-17 23:10:33 +02:00
player . trigger ( 'ready' ) ;
2022-03-10 20:13:49 +02:00
assert . equal ( player . audioOnlyMode ( ) , false , 'defaults to false' ) ;
return player . audioOnlyMode ( true )
. then ( ( ) => assert . equal ( player . audioOnlyMode ( ) , true , 'returns updated state after enabled' ) )
. then ( ( ) => player . audioOnlyMode ( false ) )
. then ( ( ) => assert . equal ( player . audioOnlyMode ( ) , false , 'returns updated state after disabled' ) )
. catch ( ( ) => assert . ok ( false , 'test error' ) ) ;
} ) ;
QUnit . test ( 'audioOnlyMode(true/false) adds or removes vjs-audio-only-mode class to player' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
2022-03-17 23:10:33 +02:00
player . trigger ( 'ready' ) ;
2022-03-10 20:13:49 +02:00
assert . equal ( player . hasClass ( 'vjs-audio-only-mode' ) , false , 'class not initially present' ) ;
return player . audioOnlyMode ( true )
. then ( ( ) => assert . equal ( player . hasClass ( 'vjs-audio-only-mode' ) , true , 'class was added' ) )
. then ( ( ) => player . audioOnlyMode ( false ) )
. then ( ( ) => assert . equal ( player . hasClass ( 'vjs-audio-only-mode' ) , false , 'class was removed' ) )
. catch ( ( ) => assert . ok ( false , 'test error' ) ) ;
} ) ;
QUnit . test ( 'setting audioOnlyMode() triggers audioonlymodechange event' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
let audioOnlyModeState = false ;
let audioOnlyModeChangeEvents = 0 ;
2022-03-17 23:10:33 +02:00
player . trigger ( 'ready' ) ;
2022-03-10 20:13:49 +02:00
player . on ( 'audioonlymodechange' , ( ) => {
audioOnlyModeChangeEvents ++ ;
audioOnlyModeState = player . audioOnlyMode ( ) ;
} ) ;
return player . audioOnlyMode ( true )
. then ( ( ) => {
assert . equal ( audioOnlyModeState , true , 'state is correct' ) ;
assert . equal ( audioOnlyModeChangeEvents , 1 , 'event fired once' ) ;
} )
. then ( ( ) => player . audioOnlyMode ( false ) )
. then ( ( ) => {
assert . equal ( audioOnlyModeState , false , 'state is correct' ) ;
assert . equal ( audioOnlyModeChangeEvents , 2 , 'event fired again' ) ;
} )
. catch ( ( ) => assert . ok ( false , 'test error' ) ) ;
} ) ;
QUnit . test ( 'audioOnlyMode(true/false) changes player height' , function ( assert ) {
const player = TestHelpers . makePlayer ( { controls : true , height : 600 } ) ;
2022-03-17 23:10:33 +02:00
player . trigger ( 'ready' ) ;
2022-03-10 20:13:49 +02:00
player . hasStarted ( true ) ;
const controlBarHeight = player . getChild ( 'ControlBar' ) . currentHeight ( ) ;
const playerHeight = player . currentHeight ( ) ;
assert . notEqual ( playerHeight , controlBarHeight , 'heights are not the same' ) ;
assert . equal ( player . currentHeight ( ) , playerHeight , 'player initial height is correct' ) ;
return player . audioOnlyMode ( true )
. then ( ( ) => assert . equal ( player . currentHeight ( ) , controlBarHeight , 'player height set to height of control bar in audioOnlyMode' ) )
. then ( ( ) => player . audioOnlyMode ( false ) )
. then ( ( ) => assert . equal ( player . currentHeight ( ) , playerHeight , 'player reset to original height when disabling audioOnlyMode' ) )
. catch ( ( ) => assert . ok ( false , 'test error' ) ) ;
} ) ;
QUnit . test ( 'audioOnlyMode(true/false) hides/shows player components except control bar' , function ( assert ) {
const player = TestHelpers . makePlayer ( { controls : true } ) ;
2022-03-17 23:10:33 +02:00
player . trigger ( 'ready' ) ;
2022-03-10 20:13:49 +02:00
player . hasStarted ( true ) ;
assert . equal ( TestHelpers . getComputedStyle ( player . getChild ( 'TextTrackDisplay' ) . el _ , 'display' ) , 'block' , 'TextTrackDisplay is initially visible' ) ;
assert . equal ( TestHelpers . getComputedStyle ( player . tech ( true ) . el _ , 'display' ) , 'block' , 'Tech is initially visible' ) ;
assert . equal ( TestHelpers . getComputedStyle ( player . getChild ( 'ControlBar' ) . el _ , 'display' ) , 'flex' , 'ControlBar is initially visible' ) ;
return player . audioOnlyMode ( true )
. then ( ( ) => {
assert . equal ( TestHelpers . getComputedStyle ( player . getChild ( 'TextTrackDisplay' ) . el _ , 'display' ) , 'none' , 'TextTrackDisplay is hidden' ) ;
assert . equal ( TestHelpers . getComputedStyle ( player . tech ( true ) . el _ , 'display' ) , 'none' , 'Tech is hidden' ) ;
assert . equal ( TestHelpers . getComputedStyle ( player . getChild ( 'ControlBar' ) . el _ , 'display' ) , 'flex' , 'ControlBar is still visible' ) ;
// Sanity check that all non-ControlBar player children are hidden
player . children ( ) . forEach ( child => {
const el = child . el _ ;
if ( el ) {
if ( child . name _ !== 'ControlBar' ) {
assert . equal ( TestHelpers . getComputedStyle ( child . el _ , 'display' ) === 'none' , true , 'non-controlBar component is hidden' ) ;
}
}
} ) ;
} )
. then ( ( ) => player . audioOnlyMode ( false ) )
. then ( ( ) => {
assert . equal ( TestHelpers . getComputedStyle ( player . getChild ( 'TextTrackDisplay' ) . el _ , 'display' ) , 'block' , 'TextTrackDisplay is visible again' ) ;
assert . equal ( TestHelpers . getComputedStyle ( player . tech ( true ) . el _ , 'display' ) , 'block' , 'Tech is visible again' ) ;
assert . equal ( TestHelpers . getComputedStyle ( player . getChild ( 'ControlBar' ) . el _ , 'display' ) , 'flex' , 'ControlBar is still visible' ) ;
} )
. catch ( ( ) => assert . ok ( false , 'test error' ) ) ;
} ) ;
QUnit . test ( 'audioOnlyMode(true/false) hides/shows video-specific control bar components' , function ( assert ) {
const tracks = [ 'captions' , 'subtitles' , 'descriptions' , 'chapters' ] . map ( kind => {
return {
kind ,
label : 'English'
} ;
} ) ;
const player = TestHelpers . makePlayer ( { controls : true , tracks , playbackRates : [ 1 , 2 ] } ) ;
this . clock . tick ( 1000 ) ;
const controlBar = player . getChild ( 'ControlBar' ) ;
const childrenShownInAudioOnlyMode = [
'PlayToggle' ,
'VolumePanel' ,
'ProgressControl' ,
'PlaybackRateMenuButton' ,
'ChaptersButton' ,
'RemainingTimeDisplay'
] ;
const childrenHiddenInAudioOnlyMode = [
'CaptionsButton' ,
'DescriptionsButton' ,
'FullscreenToggle' ,
'PictureInPictureToggle' ,
'SubsCapsButton'
] ;
const allChildren = childrenShownInAudioOnlyMode . concat ( childrenHiddenInAudioOnlyMode ) ;
const chapters = player . textTracks ( ) [ 3 ] ;
chapters . addCue ( {
startTime : 0 ,
endTime : 2 ,
text : 'Chapter 1'
} ) ;
chapters . addCue ( {
startTime : 2 ,
endTime : 4 ,
text : 'Chapter 2'
} ) ;
// ChaptersButton only shows once cues added and update() called
controlBar . getChild ( 'ChaptersButton' ) . update ( ) ;
2022-03-17 23:10:33 +02:00
player . trigger ( 'ready' ) ;
2023-05-31 16:54:51 +02:00
player . trigger ( 'loadedmetadata' ) ;
2022-03-10 20:13:49 +02:00
player . hasStarted ( true ) ;
// Show all control bar children
allChildren . forEach ( child => {
const el = controlBar . getChild ( child ) && controlBar . getChild ( child ) . el _ ;
if ( el ) {
2023-05-31 16:54:51 +02:00
if ( ! document . exitPictureInPicture && child === 'PictureInPictureToggle' ) {
assert . equal ( TestHelpers . getComputedStyle ( el , 'display' ) , 'none' , ` ${ child } is not visible if PiP is not supported ` ) ;
} else {
// Sanity check that component is showing
assert . notEqual ( TestHelpers . getComputedStyle ( el , 'display' ) , 'none' , ` ${ child } is initially visible ` ) ;
}
2022-03-10 20:13:49 +02:00
}
} ) ;
return player . audioOnlyMode ( true )
. then ( ( ) => {
childrenHiddenInAudioOnlyMode . forEach ( child => {
const el = controlBar . getChild ( child ) && controlBar . getChild ( child ) . el _ ;
if ( el ) {
assert . equal ( TestHelpers . getComputedStyle ( el , 'display' ) , 'none' , ` ${ child } is hidden ` ) ;
}
} ) ;
childrenShownInAudioOnlyMode . forEach ( child => {
const el = controlBar . getChild ( child ) && controlBar . getChild ( child ) . el _ ;
if ( el ) {
assert . notEqual ( TestHelpers . getComputedStyle ( el , 'display' ) , 'none' , ` ${ child } is still shown ` ) ;
}
} ) ;
} )
. then ( ( ) => player . audioOnlyMode ( false ) )
. then ( ( ) => {
// Check that all are showing again
allChildren . concat ( childrenHiddenInAudioOnlyMode ) . forEach ( child => {
const el = controlBar . getChild ( child ) && controlBar . getChild ( child ) . el _ ;
if ( el ) {
2023-05-31 16:54:51 +02:00
if ( ! document . exitPictureInPicture && child === 'PictureInPictureToggle' ) {
assert . equal ( TestHelpers . getComputedStyle ( el , 'display' ) , 'none' , ` ${ child } is not visible if PiP is not supported ` ) ;
} else {
assert . notEqual ( TestHelpers . getComputedStyle ( el , 'display' ) , 'none' , ` ${ child } is shown ` ) ;
}
2022-03-10 20:13:49 +02:00
}
} ) ;
} )
. catch ( ( ) => assert . ok ( false , 'test error' ) ) ;
} ) ;
2022-03-17 23:10:33 +02:00
QUnit . test ( 'setting both audioOnlyMode and audioPosterMode options to true will only turn audioOnlyMode' , function ( assert ) {
const player = TestHelpers . makePlayer ( { audioOnlyMode : true , audioPosterMode : true } ) ;
const done = assert . async ( ) ;
player . trigger ( 'ready' ) ;
player . one ( 'audioonlymodechange' , ( ) => {
assert . ok ( player . audioOnlyMode ( ) , 'audioOnlyMode is true' ) ;
assert . notOk ( player . audioPosterMode ( ) , 'audioPosterMode is false' ) ;
done ( ) ;
} ) ;
} ) ;
QUnit . test ( 'turning on audioOnlyMode when audioPosterMode is already on will turn off audioPosterMode' , function ( assert ) {
const player = TestHelpers . makePlayer ( { audioPosterMode : true } ) ;
player . trigger ( 'ready' ) ;
assert . ok ( player . audioPosterMode ( ) , 'audioPosterMode is true' ) ;
return player . audioOnlyMode ( true )
. then ( ( ) => {
assert . notOk ( player . audioPosterMode ( ) , 'audioPosterMode is false' ) ;
assert . ok ( player . audioOnlyMode ( ) , 'audioOnlyMode is true' ) ;
} ) ;
} ) ;
QUnit . test ( 'turning on audioPosterMode when audioOnlyMode is already on will turn off audioOnlyMode' , function ( assert ) {
const player = TestHelpers . makePlayer ( { audioOnlyMode : true } ) ;
player . trigger ( 'ready' ) ;
assert . ok ( player . audioOnlyMode ( ) , 'audioOnlyMode is true' ) ;
return player . audioPosterMode ( true )
. then ( ( ) => {
assert . ok ( player . audioPosterMode ( ) , 'audioPosterMode is true' ) ;
assert . notOk ( player . audioOnlyMode ( ) , 'audioOnlyMode is false' ) ;
} ) ;
} ) ;
2023-05-31 16:25:34 +02:00
2023-05-31 16:41:44 +02:00
QUnit . test ( 'player#load resets the media element to its initial state' , function ( assert ) {
const player = TestHelpers . makePlayer ( { } ) ;
player . src ( { src : 'http://vjs.zencdn.net/v/oceans2.mp4' , type : 'video/mp4' } ) ;
// Declaring spies here avoids spying on previous calls
const techGet _ = sinon . spy ( player , 'techCall_' ) ;
const src = sinon . spy ( player , 'src' ) ;
player . load ( ) ;
// Case when the VHS tech is not used
assert . ok ( techGet _ . calledOnce , 'techCall_ was called once' ) ;
assert . ok ( src . notCalled , 'src was not called' ) ;
// Simulate the VHS tech
player . tech _ . vhs = true ;
player . load ( ) ;
// Case when the VHS tech is used
assert . ok ( techGet _ . calledOnce , 'techCall_ remains the same' ) ;
assert . ok ( src . calledOnce , 'src was called' ) ;
techGet _ . restore ( ) ;
src . restore ( ) ;
player . dispose ( ) ;
} ) ;
2023-05-31 16:25:34 +02:00
QUnit . test ( 'crossOrigin value should be maintained after loadMedia is called' , function ( assert ) {
const fixture = document . getElementById ( 'qunit-fixture' ) ;
const example1 = '<video id="example_1" class="video-js" preload="none"></video>' ;
const example2 = '<video id="example_2" class="video-js" preload="none"></video>' ;
const example3 = '<video id="example_3" class="video-js" crossorigin="anonymous" preload="none"></video>' ;
fixture . innerHTML += example1 ;
fixture . innerHTML += example2 ;
fixture . innerHTML += example3 ;
const tagExample1 = document . getElementById ( 'example_1' ) ;
const tagExample2 = document . getElementById ( 'example_2' ) ;
const tagExample3 = document . getElementById ( 'example_3' ) ;
const playerExample1 = TestHelpers . makePlayer ( { techOrder : [ 'Html5' ] } , tagExample1 ) ;
const playerExample2 = TestHelpers . makePlayer ( { techOrder : [ 'Html5' ] , crossOrigin : 'use-credentials' } , tagExample2 ) ;
const playerExample3 = TestHelpers . makePlayer ( { techOrder : [ 'Html5' ] } , tagExample3 ) ;
this . clock . tick ( 1000 ) ;
playerExample1 . crossOrigin ( 'anonymous' ) ;
playerExample1 . loadMedia ( {
src : 'foo.mp4'
} ) ;
playerExample2 . loadMedia ( {
src : 'foo.mp4'
} ) ;
playerExample3 . loadMedia ( {
src : 'foo.mp4'
} ) ;
assert . strictEqual ( playerExample1 . crossOrigin ( ) , 'anonymous' , 'crossOrigin value remains correct when assigned by the crossOrigin method and loadMedia is called' ) ;
assert . ok ( tagExample1 . crossOrigin === 'anonymous' ) ;
assert . strictEqual ( playerExample2 . crossOrigin ( ) , 'use-credentials' , 'crossOrigin value remains correct when passed through the options and loadMedia is called' ) ;
assert . ok ( tagExample2 . crossOrigin === 'use-credentials' ) ;
assert . strictEqual ( playerExample3 . crossOrigin ( ) , 'anonymous' , 'crossOrigin value remains correct when passed via the html property and loadMedia is called' ) ;
assert . ok ( tagExample3 . crossOrigin === 'anonymous' ) ;
playerExample1 . dispose ( ) ;
playerExample2 . dispose ( ) ;
playerExample3 . dispose ( ) ;
} ) ;
2023-09-27 10:18:58 +02:00
QUnit . test ( 'should not reset the error when the tech triggers an error that is null' , function ( assert ) {
sinon . stub ( log , 'error' ) ;
const player = TestHelpers . makePlayer ( ) ;
player . src ( {
src : 'http://example.com/movie.unsupported-format' ,
type : 'video/unsupported-format'
} ) ;
this . clock . tick ( 60 ) ;
// Simulates Chromium's behavior when the poster is invalid
// is only there for context, but does nothing
player . poster ( 'invalid' ) ;
const spyError = sinon . spy ( player , 'error' ) ;
// Chromium behavior produced by the video element
const errorStub = sinon . stub ( player . tech ( true ) , 'error' ) . callsFake ( ( ) => null ) ;
player . tech ( true ) . trigger ( 'error' ) ;
// End
assert . ok ( player . hasClass ( 'vjs-error' ) , 'player has vjs-error class' ) ;
assert . ok ( spyError . notCalled , 'error was not called' ) ;
assert . ok ( player . error ( ) , 'error is retained' ) ;
player . dispose ( ) ;
spyError . restore ( ) ;
errorStub . restore ( ) ;
log . error . restore ( ) ;
} ) ;