-
Notifications
You must be signed in to change notification settings - Fork 7.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(Player#play): Wait for loadstart in play() when changing sources …
…instead of just ready. (#4743) The core goal here is to make sure the following works in light of some middleware process that makes setting the source more async than next tick: ```js player.src('...'); player.ready(() => player.play()); ``` In fact, given this change, we should even be able to do: ```js player.src('...'); player.play(); ``` Unlike #4665, which would have clarified/changed the meaning of "ready", it remains a reflection of the tech's state and we make better use of the ability to queue things on that state and on the middleware `setSource` process.
- Loading branch information
1 parent
6cbe3ed
commit 26b0d2c
Showing
6 changed files
with
198 additions
and
34 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
|
||
/** | ||
* Returns whether an object is `Promise`-like (i.e. has a `then` method). | ||
* | ||
* @param {Object} value | ||
* An object that may or may not be `Promise`-like. | ||
* | ||
* @return {Boolean} | ||
* Whether or not the object is `Promise`-like. | ||
*/ | ||
export function isPromise(value) { | ||
return value !== undefined && typeof value.then === 'function'; | ||
} | ||
|
||
/** | ||
* Silence a Promise-like object. | ||
* | ||
* This is useful for avoiding non-harmful, but potentially confusing "uncaught | ||
* play promise" rejection error messages. | ||
* | ||
* @param {Object} value | ||
* An object that may or may not be `Promise`-like. | ||
*/ | ||
export function silencePromise(value) { | ||
if (isPromise(value)) { | ||
value.then(null, (e) => {}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/* eslint-env qunit */ | ||
import sinon from 'sinon'; | ||
import {silencePromise} from '../../src/js/utils/promise'; | ||
import TestHelpers from './test-helpers'; | ||
|
||
QUnit.module('Player#play', { | ||
|
||
beforeEach() { | ||
this.clock = sinon.useFakeTimers(); | ||
this.player = TestHelpers.makePlayer({}); | ||
this.techPlayCallCount = 0; | ||
this.player.tech_.play = () => { | ||
this.techPlayCallCount++; | ||
}; | ||
}, | ||
|
||
afterEach() { | ||
this.player.dispose(); | ||
this.clock.restore(); | ||
} | ||
}); | ||
|
||
QUnit.test('tech not ready + no source = wait for ready, then loadstart', function(assert) { | ||
|
||
// Mock the player/tech not being ready. | ||
this.player.isReady_ = false; | ||
|
||
// Attempt to play. | ||
this.player.play(); | ||
this.clock.tick(100); | ||
assert.strictEqual(this.techPlayCallCount, 0, 'tech_.play was not called because the tech was not ready'); | ||
|
||
// Ready the player. | ||
this.player.triggerReady(); | ||
this.clock.tick(100); | ||
assert.strictEqual(this.techPlayCallCount, 0, 'tech_.play was not called because there was no source'); | ||
|
||
// Add a source and trigger loadstart. | ||
this.player.src('xyz.mp4'); | ||
this.clock.tick(100); | ||
this.player.trigger('loadstart'); | ||
assert.strictEqual(this.techPlayCallCount, 1, 'tech_.play was called'); | ||
}); | ||
|
||
QUnit.test('tech not ready + has source = wait for ready', function(assert) { | ||
|
||
// Mock the player/tech not being ready, but having a source. | ||
this.player.isReady_ = false; | ||
this.player.src('xyz.mp4'); | ||
this.clock.tick(100); | ||
|
||
// Attempt to play. | ||
this.player.play(); | ||
this.clock.tick(100); | ||
assert.strictEqual(this.techPlayCallCount, 0, 'tech_.play was not called because the tech was not ready'); | ||
|
||
// Ready the player. | ||
this.player.triggerReady(); | ||
this.clock.tick(100); | ||
assert.strictEqual(this.techPlayCallCount, 1, 'tech_.play was called'); | ||
}); | ||
|
||
QUnit.test('tech ready + no source = wait for loadstart', function(assert) { | ||
|
||
// Attempt to play. | ||
this.player.play(); | ||
this.clock.tick(100); | ||
assert.strictEqual(this.techPlayCallCount, 0, 'tech_.play was not called because the tech was not ready'); | ||
|
||
// Add a source and trigger loadstart. | ||
this.player.src('xyz.mp4'); | ||
this.clock.tick(100); | ||
this.player.trigger('loadstart'); | ||
assert.strictEqual(this.techPlayCallCount, 1, 'tech_.play was called'); | ||
}); | ||
|
||
QUnit.test('tech ready + has source = play immediately!', function(assert) { | ||
|
||
// Mock the player having a source. | ||
this.player.src('xyz.mp4'); | ||
this.clock.tick(100); | ||
|
||
// Attempt to play, but silence the promise that might be returned. | ||
silencePromise(this.player.play()); | ||
assert.strictEqual(this.techPlayCallCount, 1, 'tech_.play was called'); | ||
}); | ||
|
||
QUnit.test('tech ready + has source + changing source = wait for loadstart', function(assert) { | ||
|
||
// Mock the player having a source and in the process of changing its source. | ||
this.player.src('xyz.mp4'); | ||
this.clock.tick(100); | ||
this.player.src('abc.mp4'); | ||
this.player.play(); | ||
this.clock.tick(100); | ||
assert.strictEqual(this.techPlayCallCount, 0, 'tech_.play was not called because the source was changing'); | ||
|
||
this.player.trigger('loadstart'); | ||
assert.strictEqual(this.techPlayCallCount, 1, 'tech_.play was called'); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/* eslint-env qunit */ | ||
import window from 'global/window'; | ||
import * as promise from '../../../src/js/utils/promise'; | ||
|
||
QUnit.module('utils/promise'); | ||
|
||
QUnit.test('can correctly identify a native Promise (if supported)', function(assert) { | ||
|
||
// If Promises aren't supported, skip this. | ||
if (!window.Promise) { | ||
return assert.expect(0); | ||
} | ||
|
||
assert.ok(promise.isPromise(new window.Promise((resolve) => resolve())), 'a native Promise was recognized'); | ||
}); | ||
|
||
QUnit.test('can identify a Promise-like object', function(assert) { | ||
assert.notOk(promise.isPromise({}), 'an object without a `then` method is not Promise-like'); | ||
assert.ok(promise.isPromise({then: () => {}}), 'an object with a `then` method is Promise-like'); | ||
}); |