shaka-player
Version:
DASH/EME video player library
965 lines (838 loc) • 31.2 kB
JavaScript
/**
* @license
* Copyright 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
describe('CastReceiver', function() {
/** @const */
var CastReceiver = shaka.cast.CastReceiver;
/** @const */
var CastUtils = shaka.cast.CastUtils;
/** @const */
var Util = shaka.test.Util;
/** @const */
var originalCast = window['cast'];
/** @const */
var originalUserAgent = navigator.userAgent;
/** @type {!shaka.test.FakeVideo} */
var mockVideo;
/** @type {!jasmine.Spy} */
var mockAppDataCallback;
var mockPlayer;
var mockReceiverManager;
var mockReceiverApi;
var mockShakaMessageBus;
var mockGenericMessageBus;
/** @type {!jasmine.Spy} */
var mockCanDisplayType;
/** @type {shaka.cast.CastReceiver} */
var receiver;
/** @type {boolean} */
var isChrome;
/** @type {boolean} */
var isChromecast;
function checkChromeOrChromecast() {
if (!isChromecast && !isChrome) {
pending('Skipping CastReceiver tests for non-Chrome and non-Chromecast');
}
}
beforeAll(function() {
// The receiver is only meant to run on the Chromecast, so we have the
// ability to use modern APIs there that may not be available on all of the
// browsers our library supports. Because of this, CastReceiver tests will
// only be run on Chrome and Chromecast.
isChromecast = navigator.userAgent.indexOf('CrKey') >= 0;
var isEdge = navigator.userAgent.indexOf('Edge/') >= 0;
// Edge also has "Chrome/" in its user agent string.
isChrome = navigator.userAgent.indexOf('Chrome/') >= 0 && !isEdge;
// Don't do any more work here if the tests will not end up running.
if (!isChromecast && !isChrome) return;
// In uncompiled mode, there is a UA check for Chromecast in order to make
// manual testing easier. For these automated tests, we want to act as if
// we are running on the Chromecast, even in Chrome.
// Since we can't write to window.navigator or navigator.userAgent, we use
// Object.defineProperty.
Object.defineProperty(window['navigator'],
'userAgent', {value: 'CrKey', configurable: true});
});
beforeEach(function() {
checkChromeOrChromecast();
mockReceiverApi = createMockReceiverApi();
mockCanDisplayType = jasmine.createSpy('canDisplayType');
mockCanDisplayType.and.returnValue(false);
// We're using quotes to access window.cast because the compiler
// knows about lots of Cast-specific APIs we aren't mocking. We
// don't need this mock strictly type-checked.
window['cast'] = {
receiver: mockReceiverApi,
__platform__: { canDisplayType: mockCanDisplayType }
};
mockReceiverManager = createMockReceiverManager();
mockShakaMessageBus = createMockMessageBus();
mockGenericMessageBus = createMockMessageBus();
mockVideo = new shaka.test.FakeVideo();
mockPlayer = createMockPlayer();
mockAppDataCallback = jasmine.createSpy('appDataCallback');
});
afterEach(function(done) {
if (receiver) {
receiver.destroy().catch(fail).then(done);
} else {
done();
}
});
afterAll(function() {
if (originalUserAgent) {
window['cast'] = originalCast;
Object.defineProperty(window['navigator'],
'userAgent', {value: originalUserAgent});
}
});
describe('constructor', function() {
it('starts the receiver manager', function() {
checkChromeOrChromecast();
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
expect(mockReceiverManager.start).toHaveBeenCalled();
});
it('listens for video and player events', function() {
checkChromeOrChromecast();
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
expect(Object.keys(mockVideo.on).length).toBeGreaterThan(0);
expect(Object.keys(mockPlayer.listeners).length).toBeGreaterThan(0);
});
it('limits streams to 1080p on Chromecast v1 and v2', function() {
checkChromeOrChromecast();
// Simulate the canDisplayType reponse of Chromecast v1 or v2
mockCanDisplayType.and.callFake(function(type) {
var matches = /height=(\d+)/.exec(type);
var height = matches[1];
if (height && height > 1080) return false;
return true;
});
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
expect(mockCanDisplayType).toHaveBeenCalled();
expect(mockPlayer.setMaxHardwareResolution).
toHaveBeenCalledWith(1920, 1080);
});
it('limits streams to 4k on Chromecast Ultra', function() {
checkChromeOrChromecast();
// Simulate the canDisplayType reponse of Chromecast Ultra
mockCanDisplayType.and.callFake(function(type) {
var matches = /height=(\d+)/.exec(type);
var height = matches[1];
if (height && height > 2160) return false;
return true;
});
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
expect(mockCanDisplayType).toHaveBeenCalled();
expect(mockPlayer.setMaxHardwareResolution).
toHaveBeenCalledWith(3840, 2160);
});
it('does not start polling', function() {
checkChromeOrChromecast();
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
expect(mockPlayer.getConfiguration).not.toHaveBeenCalled();
expect(mockShakaMessageBus.messages.length).toBe(0);
});
});
describe('isConnected', function() {
beforeEach(function() {
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
});
it('is true when there are senders', function() {
checkChromeOrChromecast();
expect(receiver.isConnected()).toBe(false);
fakeConnectedSenders(1);
expect(receiver.isConnected()).toBe(true);
fakeConnectedSenders(2);
expect(receiver.isConnected()).toBe(true);
fakeConnectedSenders(99);
expect(receiver.isConnected()).toBe(true);
fakeConnectedSenders(0);
expect(receiver.isConnected()).toBe(false);
});
});
describe('"caststatuschanged" event', function() {
beforeEach(function() {
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
});
it('is triggered when senders connect or disconnect', function(done) {
checkChromeOrChromecast();
var listener = jasmine.createSpy('listener');
receiver.addEventListener('caststatuschanged', Util.spyFunc(listener));
shaka.test.Util.delay(0.2).then(function() {
expect(listener).not.toHaveBeenCalled();
fakeConnectedSenders(1);
return shaka.test.Util.delay(0.2);
}).then(function() {
expect(listener).toHaveBeenCalled();
listener.calls.reset();
mockReceiverManager.onSenderDisconnected();
return shaka.test.Util.delay(0.2);
}).then(function() {
expect(listener).toHaveBeenCalled();
}).catch(fail).then(done);
});
it('is triggered when idle state changes', function(done) {
checkChromeOrChromecast();
var listener = jasmine.createSpy('listener');
receiver.addEventListener('caststatuschanged', Util.spyFunc(listener));
var fakeLoadingEvent = {type: 'loading'};
var fakeUnloadingEvent = {type: 'unloading'};
var fakeEndedEvent = {type: 'ended'};
var fakePlayingEvent = {type: 'playing'};
shaka.test.Util.delay(0.2).then(function() {
expect(listener).not.toHaveBeenCalled();
expect(receiver.isIdle()).toBe(true);
mockPlayer.listeners['loading'](fakeLoadingEvent);
return shaka.test.Util.delay(0.2);
}).then(function() {
expect(listener).toHaveBeenCalled();
expect(receiver.isIdle()).toBe(false);
listener.calls.reset();
mockPlayer.listeners['unloading'](fakeUnloadingEvent);
return shaka.test.Util.delay(0.2);
}).then(function() {
expect(listener).toHaveBeenCalled();
expect(receiver.isIdle()).toBe(true);
listener.calls.reset();
mockVideo.ended = true;
mockVideo.on['ended'](fakeEndedEvent);
return shaka.test.Util.delay(5.2); // There is a long delay for 'ended'
}).then(function() {
expect(listener).toHaveBeenCalled();
listener.calls.reset();
expect(receiver.isIdle()).toBe(true);
mockVideo.ended = false;
mockVideo.on['playing'](fakePlayingEvent);
}).then(function() {
expect(listener).toHaveBeenCalled();
expect(receiver.isIdle()).toBe(false);
}).catch(fail).then(done);
});
});
describe('local events', function() {
beforeEach(function() {
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
});
it('trigger "update" and "event" messages', function() {
checkChromeOrChromecast();
fakeConnectedSenders(1);
// No messages yet.
expect(mockShakaMessageBus.messages).toEqual([]);
var fakeEvent = {type: 'timeupdate'};
mockVideo.on['timeupdate'](fakeEvent);
// There are now "update" and "event" messages, in that order.
expect(mockShakaMessageBus.messages).toEqual([
{
type: 'update',
update: jasmine.any(Object)
},
{
type: 'event',
targetName: 'video',
event: jasmine.objectContaining(fakeEvent)
}
]);
});
});
describe('"init" message', function() {
/** @const */
var fakeConfig = {key: 'value'};
/** @const */
var fakeAppData = {myFakeAppData: 1234};
var fakeInitState;
beforeEach(function() {
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
fakeInitState = {
player: {
configure: fakeConfig
},
'playerAfterLoad': {
setTextTrackVisibility: true
},
video: {
loop: true,
playbackRate: 5
}
};
});
it('sets initial state', function(done) {
checkChromeOrChromecast();
expect(mockVideo.loop).toBe(false);
expect(mockVideo.playbackRate).toBe(1);
expect(mockPlayer.configure).not.toHaveBeenCalled();
fakeIncomingMessage({
type: 'init',
initState: fakeInitState,
appData: fakeAppData
}, mockShakaMessageBus);
// Initial Player state first:
expect(mockPlayer.configure).toHaveBeenCalledWith(fakeConfig);
// App data next:
expect(mockAppDataCallback).toHaveBeenCalledWith(fakeAppData);
// Nothing else yet:
expect(mockPlayer.setTextTrackVisibility).not.toHaveBeenCalled();
expect(mockVideo.loop).toBe(false);
expect(mockVideo.playbackRate).toBe(1);
// The rest is done async:
shaka.test.Util.delay(0.1).then(function() {
expect(mockPlayer.setTextTrackVisibility).toHaveBeenCalledWith(
fakeInitState['playerAfterLoad'].setTextTrackVisibility);
expect(mockVideo.loop).toEqual(fakeInitState.video.loop);
expect(mockVideo.playbackRate).toEqual(
fakeInitState.video.playbackRate);
}).catch(fail).then(done);
});
it('starts polling', function() {
checkChromeOrChromecast();
var fakeConfig = {key: 'value'};
mockPlayer.getConfiguration.and.returnValue(fakeConfig);
fakeConnectedSenders(1);
mockPlayer.getConfiguration.calls.reset();
expect(mockShakaMessageBus.messages.length).toBe(0);
fakeIncomingMessage({
type: 'init',
initState: fakeInitState,
appData: fakeAppData
}, mockShakaMessageBus);
expect(mockPlayer.getConfiguration).toHaveBeenCalled();
expect(mockShakaMessageBus.messages).toContain(jasmine.objectContaining({
type: 'update',
update: jasmine.objectContaining({
player: jasmine.objectContaining({
getConfiguration: fakeConfig
})
})
}));
});
it('doesn\'t poll live methods while loading a VOD', function() {
checkChromeOrChromecast();
mockPlayer.getConfiguration.and.returnValue({key: 'value'});
mockPlayer.isLive.and.returnValue(false);
fakeConnectedSenders(1);
expect(mockShakaMessageBus.messages.length).toBe(0);
fakeIncomingMessage({
type: 'init',
initState: fakeInitState,
appData: fakeAppData
}, mockShakaMessageBus);
expect(mockPlayer.getPlayheadTimeAsDate).not.toHaveBeenCalled();
});
it('does poll live methods while loading a livestream', function() {
checkChromeOrChromecast();
mockPlayer.getConfiguration.and.returnValue({key: 'value'});
mockPlayer.isLive.and.returnValue(true);
fakeConnectedSenders(1);
expect(mockShakaMessageBus.messages.length).toBe(0);
fakeIncomingMessage({
type: 'init',
initState: fakeInitState,
appData: fakeAppData
}, mockShakaMessageBus);
expect(mockPlayer.getPlayheadTimeAsDate).toHaveBeenCalled();
});
it('loads the manifest', function() {
checkChromeOrChromecast();
fakeInitState.startTime = 12;
fakeInitState.manifest = 'foo://bar';
expect(mockPlayer.load).not.toHaveBeenCalled();
fakeIncomingMessage({
type: 'init',
initState: fakeInitState,
appData: fakeAppData
}, mockShakaMessageBus);
expect(mockPlayer.load).toHaveBeenCalledWith('foo://bar', 12);
});
it('plays the video after loading', function(done) {
checkChromeOrChromecast();
fakeInitState.manifest = 'foo://bar';
mockVideo.autoplay = true;
fakeIncomingMessage({
type: 'init',
initState: fakeInitState,
appData: fakeAppData
}, mockShakaMessageBus);
// Video autoplay inhibited:
expect(mockVideo.autoplay).toBe(false);
shaka.test.Util.delay(0.1).then(function() {
expect(mockVideo.play).toHaveBeenCalled();
// Video autoplay restored:
expect(mockVideo.autoplay).toBe(true);
}).catch(fail).then(done);
});
it('does not load or play without a manifest URI', function(done) {
checkChromeOrChromecast();
fakeInitState.manifest = null;
fakeIncomingMessage({
type: 'init',
initState: fakeInitState,
appData: fakeAppData
}, mockShakaMessageBus);
shaka.test.Util.delay(0.1).then(function() {
// Nothing loaded or played:
expect(mockPlayer.load).not.toHaveBeenCalled();
expect(mockVideo.play).not.toHaveBeenCalled();
// State was still transferred, though:
expect(mockPlayer.setTextTrackVisibility).toHaveBeenCalledWith(
fakeInitState['playerAfterLoad'].setTextTrackVisibility);
expect(mockVideo.loop).toEqual(fakeInitState.video.loop);
expect(mockVideo.playbackRate).toEqual(
fakeInitState.video.playbackRate);
}).catch(fail).then(done);
});
it('triggers an "error" event if load fails', function(done) {
checkChromeOrChromecast();
fakeInitState.manifest = 'foo://bar';
var fakeError = new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.UNABLE_TO_GUESS_MANIFEST_TYPE);
mockPlayer.load.and.returnValue(Promise.reject(fakeError));
var listener = jasmine.createSpy('listener');
mockPlayer.addEventListener('error', listener);
expect(listener).not.toHaveBeenCalled();
fakeIncomingMessage({
type: 'init',
initState: fakeInitState,
appData: fakeAppData
}, mockShakaMessageBus);
shaka.test.Util.delay(0.1).then(function() {
expect(mockPlayer.load).toHaveBeenCalled();
expect(mockPlayer.dispatchEvent).toHaveBeenCalledWith(
jasmine.objectContaining({ type: 'error', detail: fakeError }));
}).catch(fail).then(done);
});
});
describe('"appData" message', function() {
beforeEach(function() {
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
});
it('triggers the app data callback', function() {
checkChromeOrChromecast();
expect(mockAppDataCallback).not.toHaveBeenCalled();
var fakeAppData = {myFakeAppData: 1234};
fakeIncomingMessage({
type: 'appData',
appData: fakeAppData
}, mockShakaMessageBus);
expect(mockAppDataCallback).toHaveBeenCalledWith(fakeAppData);
});
});
describe('"set" message', function() {
beforeEach(function() {
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
});
it('sets local properties', function() {
checkChromeOrChromecast();
expect(mockVideo.currentTime).toBe(0);
fakeIncomingMessage({
type: 'set',
targetName: 'video',
property: 'currentTime',
value: 12
}, mockShakaMessageBus);
expect(mockVideo.currentTime).toEqual(12);
expect(mockPlayer['arbitraryName']).toBe(undefined);
fakeIncomingMessage({
type: 'set',
targetName: 'player',
property: 'arbitraryName',
value: 'arbitraryValue'
}, mockShakaMessageBus);
expect(mockPlayer['arbitraryName']).toEqual('arbitraryValue');
});
it('routes volume properties to the receiver manager', function() {
checkChromeOrChromecast();
expect(mockVideo.volume).toBe(1);
expect(mockVideo.muted).toBe(false);
expect(mockReceiverManager.setSystemVolumeLevel).not.toHaveBeenCalled();
expect(mockReceiverManager.setSystemVolumeMuted).not.toHaveBeenCalled();
fakeIncomingMessage({
type: 'set',
targetName: 'video',
property: 'volume',
value: 0.5
}, mockShakaMessageBus);
fakeIncomingMessage({
type: 'set',
targetName: 'video',
property: 'muted',
value: true
}, mockShakaMessageBus);
expect(mockVideo.volume).toBe(1);
expect(mockVideo.muted).toBe(false);
expect(mockReceiverManager.setSystemVolumeLevel).
toHaveBeenCalledWith(0.5);
expect(mockReceiverManager.setSystemVolumeMuted).
toHaveBeenCalledWith(true);
});
});
describe('"call" message', function() {
beforeEach(function() {
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
});
it('calls local methods', function() {
checkChromeOrChromecast();
expect(mockVideo.play).not.toHaveBeenCalled();
fakeIncomingMessage({
type: 'call',
targetName: 'video',
methodName: 'play',
args: [1, 2, 3]
}, mockShakaMessageBus);
expect(mockVideo.play).toHaveBeenCalledWith(1, 2, 3);
expect(mockPlayer.configure).not.toHaveBeenCalled();
fakeIncomingMessage({
type: 'call',
targetName: 'player',
methodName: 'configure',
args: [42]
}, mockShakaMessageBus);
expect(mockPlayer.configure).toHaveBeenCalledWith(42);
});
});
describe('"asyncCall" message', function() {
/** @const */
var fakeSenderId = 'senderId';
/** @const */
var fakeCallId = '5';
/** @type {!shaka.util.PublicPromise} */
var p;
beforeEach(function() {
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
fakeConnectedSenders(1);
p = new shaka.util.PublicPromise();
mockPlayer.load.and.returnValue(p);
expect(mockPlayer.load).not.toHaveBeenCalled();
fakeIncomingMessage({
type: 'asyncCall',
id: fakeCallId,
targetName: 'player',
methodName: 'load',
args: ['foo://bar', 12]
}, mockShakaMessageBus, fakeSenderId);
});
it('calls local async methods', function() {
checkChromeOrChromecast();
expect(mockPlayer.load).toHaveBeenCalledWith('foo://bar', 12);
p.resolve();
});
it('sends "asyncComplete" replies when resolved', function(done) {
checkChromeOrChromecast();
// No messages have been sent, either broadcast or privately.
expect(mockShakaMessageBus.broadcast).not.toHaveBeenCalled();
expect(mockShakaMessageBus.getCastChannel).not.toHaveBeenCalled();
p.resolve();
shaka.test.Util.delay(0.1).then(function() {
// No broadcast messages have been sent, but a private message has
// been sent to the sender who started the async call.
expect(mockShakaMessageBus.broadcast).not.toHaveBeenCalled();
expect(mockShakaMessageBus.getCastChannel).toHaveBeenCalledWith(
fakeSenderId);
var senderChannel = mockShakaMessageBus.getCastChannel();
expect(senderChannel.messages).toEqual([{
type: 'asyncComplete',
id: fakeCallId,
error: null
}]);
}).catch(fail).then(done);
});
it('sends "asyncComplete" replies when rejected', function(done) {
checkChromeOrChromecast();
// No messages have been sent, either broadcast or privately.
expect(mockShakaMessageBus.broadcast).not.toHaveBeenCalled();
expect(mockShakaMessageBus.getCastChannel).not.toHaveBeenCalled();
var fakeError = new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.UNABLE_TO_GUESS_MANIFEST_TYPE);
p.reject(fakeError);
shaka.test.Util.delay(0.1).then(function() {
// No broadcast messages have been sent, but a private message has
// been sent to the sender who started the async call.
expect(mockShakaMessageBus.broadcast).not.toHaveBeenCalled();
expect(mockShakaMessageBus.getCastChannel).toHaveBeenCalledWith(
fakeSenderId);
var senderChannel = mockShakaMessageBus.getCastChannel();
expect(senderChannel.messages).toEqual([{
type: 'asyncComplete',
id: fakeCallId,
error: jasmine.any(Object)
}]);
if (senderChannel.messages.length) {
var error = senderChannel.messages[0].error;
shaka.test.Util.expectToEqualError(fakeError, error);
}
}).catch(fail).then(done);
});
});
describe('respects generic control messages', function() {
beforeEach(function() {
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
fakeConnectedSenders(1);
});
it('get status', function() {
checkChromeOrChromecast();
var message = {
// Arbitrary number
'requestId': 0,
'type': 'GET_STATUS'
};
fakeIncomingMessage(message, mockGenericMessageBus);
expect(mockGenericMessageBus.broadcast.calls.count()).toEqual(1);
expect(mockGenericMessageBus.broadcast.calls.argsFor(0)[0].indexOf(
'"requestId":0,"type":"MEDIA_STATUS"') != -1).toBe(true);
});
it('play', function() {
checkChromeOrChromecast();
var message = {
// Arbitrary number
'requestId': 0,
'type': 'PLAY'
};
fakeIncomingMessage(message, mockGenericMessageBus);
expect(mockVideo.play).toHaveBeenCalled();
});
it('pause', function() {
checkChromeOrChromecast();
var message = {
// Arbitrary number
'requestId': 0,
'type': 'PAUSE'
};
fakeIncomingMessage(message, mockGenericMessageBus);
expect(mockVideo.pause).toHaveBeenCalled();
});
it('seek', function() {
checkChromeOrChromecast();
var message = {
// Arbitrary number
'requestId': 0,
'type': 'SEEK',
'resumeState': 'PLAYBACK_START',
'currentTime': 10
};
fakeIncomingMessage(message, mockGenericMessageBus);
expect(mockVideo.play).toHaveBeenCalled();
expect(mockVideo.currentTime).toBe(10);
});
it('stop', function() {
checkChromeOrChromecast();
var message = {
// Arbitrary number
'requestId': 0,
'type': 'STOP'
};
fakeIncomingMessage(message, mockGenericMessageBus);
expect(mockPlayer.unload).toHaveBeenCalled();
});
it('volume', function() {
checkChromeOrChromecast();
var message = {
// Arbitrary number
'requestId': 0,
'type': 'VOLUME',
'volume': {
'level': 0.5,
'muted': true
}
};
fakeIncomingMessage(message, mockGenericMessageBus);
expect(mockVideo.volume).toBe(0.5);
expect(mockVideo.muted).toBe(true);
});
it('load', function() {
checkChromeOrChromecast();
var message = {
// Arbitrary number
'requestId': 0,
'type': 'LOAD',
'autoplay': false,
'currentTime': 10,
'media': {
'contentId': 'manifestUri',
'contentType': 'video/mp4',
'streamType': 'BUFFERED'
}
};
fakeIncomingMessage(message, mockGenericMessageBus);
expect(mockPlayer.load).toHaveBeenCalled();
});
it('dispatches error on unrecognized request type', function() {
checkChromeOrChromecast();
var message = {
// Arbitrary number
'requestId': 0,
'type': 'UNKNOWN_TYPE'
};
fakeIncomingMessage(message, mockGenericMessageBus);
expect(mockGenericMessageBus.broadcast.calls.count()).toEqual(1);
expect(mockGenericMessageBus.broadcast.calls.argsFor(0)[0].indexOf(
'"requestId":0,' +
'"type":"INVALID_REQUEST",' +
'"reason":"INVALID_COMMAND"') != -1)
.toBe(true);
});
});
describe('destroy', function() {
beforeEach(function() {
receiver = new CastReceiver(
mockVideo, mockPlayer, Util.spyFunc(mockAppDataCallback));
});
it('destroys the local player', function(done) {
checkChromeOrChromecast();
expect(mockPlayer.destroy).not.toHaveBeenCalled();
receiver.destroy().then(function() {
expect(mockPlayer.destroy).toHaveBeenCalled();
}).catch(fail).then(done);
});
it('stops polling', function(done) {
checkChromeOrChromecast();
// Start polling:
fakeIncomingMessage({
type: 'init',
initState: {},
appData: {}
}, mockShakaMessageBus);
mockPlayer.getConfiguration.calls.reset();
shaka.test.Util.delay(1).then(function() {
// We have polled at least once, so this getter has been called.
expect(mockPlayer.getConfiguration).toHaveBeenCalled();
mockPlayer.getConfiguration.calls.reset();
// Destroy the receiver.
return receiver.destroy();
}).then(function() {
// Wait another second.
return shaka.test.Util.delay(1);
}).then(function() {
// We have not polled again since destruction.
expect(mockPlayer.getConfiguration).not.toHaveBeenCalled();
}).catch(fail).then(done);
});
it('stops the receiver manager', function(done) {
checkChromeOrChromecast();
expect(mockReceiverManager.stop).not.toHaveBeenCalled();
receiver.destroy().then(function() {
expect(mockReceiverManager.stop).toHaveBeenCalled();
}).catch(fail).then(done);
});
});
function createMockReceiverApi() {
return {
CastReceiverManager: {
getInstance: function() { return mockReceiverManager; }
}
};
}
function createMockReceiverManager() {
return {
start: jasmine.createSpy('CastReceiverManager.start'),
stop: jasmine.createSpy('CastReceiverManager.stop'),
setSystemVolumeLevel:
jasmine.createSpy('CastReceiverManager.setSystemVolumeLevel'),
setSystemVolumeMuted:
jasmine.createSpy('CastReceiverManager.setSystemVolumeMuted'),
getSenders: jasmine.createSpy('CastReceiverManager.getSenders'),
getSystemVolume: function() { return { level: 1, muted: false }; },
getCastMessageBus: function(namespace) {
if (namespace == shaka.cast.CastUtils.SHAKA_MESSAGE_NAMESPACE)
return mockShakaMessageBus;
return mockGenericMessageBus;
}
};
}
function createMockMessageBus() {
var bus = {
messages: [],
broadcast: jasmine.createSpy('CastMessageBus.broadcast'),
getCastChannel: jasmine.createSpy('CastMessageBus.getCastChannel')
};
// For convenience, deserialize and store sent messages.
bus.broadcast.and.callFake(function(message) {
bus.messages.push(CastUtils.deserialize(message));
});
var channel = {
messages: [],
send: function(message) {
channel.messages.push(CastUtils.deserialize(message));
}
};
bus.getCastChannel.and.returnValue(channel);
return bus;
}
function createMockPlayer() {
var player = {
destroy: jasmine.createSpy('destroy').and.returnValue(Promise.resolve()),
setMaxHardwareResolution: jasmine.createSpy('setMaxHardwareResolution'),
addEventListener: function(eventName, listener) {
player.listeners[eventName] = listener;
},
dispatchEvent: jasmine.createSpy('dispatchEvent'),
// For convenience:
listeners: {}
};
CastUtils.PlayerVoidMethods.forEach(function(name) {
player[name] = jasmine.createSpy(name);
});
for (var name in CastUtils.PlayerGetterMethods) {
player[name] = jasmine.createSpy(name);
}
for (var name in CastUtils.PlayerGetterMethodsThatRequireLive) {
player[name] = jasmine.createSpy(name);
}
CastUtils.PlayerPromiseMethods.forEach(function(name) {
player[name] = jasmine.createSpy(name).and.returnValue(Promise.resolve());
});
return player;
}
/**
* @param {number} num
*/
function fakeConnectedSenders(num) {
var senderArray = [];
while (num--) {
senderArray.push('senderId');
}
mockReceiverManager.getSenders.and.returnValue(senderArray);
mockReceiverManager.onSenderConnected();
}
/**
* @param {?} message
* @param {!Object} bus
* @param {string=} opt_senderId
*/
function fakeIncomingMessage(message, bus, opt_senderId) {
var serialized = CastUtils.serialize(message);
var messageEvent = {
senderId: opt_senderId,
data: serialized
};
bus.onMessage(messageEvent);
}
});