Bug 1122032 Part 2 - Show the Loop screenshare video in place of the remote video for now. r=mikedeboer

This commit is contained in:
Mark Banner 2015-02-02 21:53:19 +00:00
parent bd23ef68d0
commit b729195eb0
18 changed files with 297 additions and 40 deletions

View File

@ -898,7 +898,7 @@ loop.conversationViews = (function(mozL10n) {
React.createElement("div", {className: "conversation"}, React.createElement("div", {className: "conversation"},
React.createElement("div", {className: "media nested"}, React.createElement("div", {className: "media nested"},
React.createElement("div", {className: "video_wrapper remote_wrapper"}, React.createElement("div", {className: "video_wrapper remote_wrapper"},
React.createElement("div", {className: "video_inner remote"}) React.createElement("div", {className: "video_inner remote remote-stream"})
), ),
React.createElement("div", {className: localStreamClasses}) React.createElement("div", {className: localStreamClasses})
), ),

View File

@ -898,7 +898,7 @@ loop.conversationViews = (function(mozL10n) {
<div className="conversation"> <div className="conversation">
<div className="media nested"> <div className="media nested">
<div className="video_wrapper remote_wrapper"> <div className="video_wrapper remote_wrapper">
<div className="video_inner remote"></div> <div className="video_inner remote remote-stream"></div>
</div> </div>
<div className={localStreamClasses}></div> <div className={localStreamClasses}></div>
</div> </div>

View File

@ -274,7 +274,7 @@ loop.roomViews = (function(mozL10n) {
React.createElement("div", {className: "conversation room-conversation"}, React.createElement("div", {className: "conversation room-conversation"},
React.createElement("div", {className: "media nested"}, React.createElement("div", {className: "media nested"},
React.createElement("div", {className: "video_wrapper remote_wrapper"}, React.createElement("div", {className: "video_wrapper remote_wrapper"},
React.createElement("div", {className: "video_inner remote"}) React.createElement("div", {className: "video_inner remote remote-stream"})
), ),
React.createElement("div", {className: localStreamClasses}), React.createElement("div", {className: localStreamClasses}),
React.createElement("div", {className: "screen hide"}) React.createElement("div", {className: "screen hide"})

View File

@ -274,7 +274,7 @@ loop.roomViews = (function(mozL10n) {
<div className="conversation room-conversation"> <div className="conversation room-conversation">
<div className="media nested"> <div className="media nested">
<div className="video_wrapper remote_wrapper"> <div className="video_wrapper remote_wrapper">
<div className="video_inner remote"></div> <div className="video_inner remote remote-stream"></div>
</div> </div>
<div className={localStreamClasses}></div> <div className={localStreamClasses}></div>
<div className="screen hide"></div> <div className="screen hide"></div>

View File

@ -231,7 +231,7 @@
/* Side by side video elements */ /* Side by side video elements */
.conversation .media.side-by-side .remote { .conversation .media.side-by-side .remote-stream {
width: 50%; width: 50%;
float: left; float: left;
} }
@ -509,7 +509,7 @@
max-height: none; max-height: none;
} }
.conversation .media.nested .remote { .conversation .media.nested .remote-stream {
display: inline-block; display: inline-block;
position: absolute; /* workaround for lack of object-fit; see bug 1020445 */ position: absolute; /* workaround for lack of object-fit; see bug 1020445 */
width: 100%; width: 100%;

View File

@ -218,6 +218,13 @@ loop.shared.actions = (function() {
state: String state: String
}), }),
/**
* Used to notify that a shared screen is being received (or not).
*/
ReceivingScreenShare: Action.define("receivingScreenShare", {
receiving: Boolean
}),
/** /**
* Creates a new room. * Creates a new room.
* XXX: should move to some roomActions module - refs bug 1079284 * XXX: should move to some roomActions module - refs bug 1079284

View File

@ -73,7 +73,8 @@ loop.store.ActiveRoomStore = (function() {
used: false, used: false,
localVideoDimensions: {}, localVideoDimensions: {},
remoteVideoDimensions: {}, remoteVideoDimensions: {},
screenSharingState: SCREEN_SHARE_STATES.INACTIVE screenSharingState: SCREEN_SHARE_STATES.INACTIVE,
receivingScreenShare: false
}; };
}, },
@ -121,6 +122,7 @@ loop.store.ActiveRoomStore = (function() {
"connectionFailure", "connectionFailure",
"setMute", "setMute",
"screenSharingState", "screenSharingState",
"receivingScreenShare",
"remotePeerDisconnected", "remotePeerDisconnected",
"remotePeerConnected", "remotePeerConnected",
"windowUnload", "windowUnload",
@ -380,6 +382,13 @@ loop.store.ActiveRoomStore = (function() {
this.setStoreState({screenSharingState: actionData.state}); this.setStoreState({screenSharingState: actionData.state});
}, },
/**
* Used to note the current state of receiving screenshare data.
*/
receivingScreenShare: function(actionData) {
this.setStoreState({receivingScreenShare: actionData.receiving});
},
/** /**
* Handles recording when a remote peer has connected to the servers. * Handles recording when a remote peer has connected to the servers.
*/ */

View File

@ -259,7 +259,8 @@ loop.shared.mixins = (function() {
}, },
/** /**
* Retrieve the dimensions of the remote video stream. * Retrieve the dimensions of the active remote video stream. This assumes
* that if screens are being shared, the remote camera stream is hidden.
* Example output: * Example output:
* { * {
* width: 680, * width: 680,
@ -270,6 +271,8 @@ loop.shared.mixins = (function() {
* offsetY: 0 * offsetY: 0
* } * }
* *
* Note: This expects a class on the element that has the name "remote" or the
* same name as the possible video types (currently only "screen").
* Note: Once we support multiple remote video streams, this function will * Note: Once we support multiple remote video streams, this function will
* need to be updated. * need to be updated.
* @return {Object} contains the remote stream dimension properties of its * @return {Object} contains the remote stream dimension properties of its
@ -320,7 +323,7 @@ loop.shared.mixins = (function() {
// Calculate the size of each individual letter- or pillarbox for convenience. // Calculate the size of each individual letter- or pillarbox for convenience.
remoteVideoDimensions.offsetX = remoteVideoDimensions.width - remoteVideoDimensions.offsetX = remoteVideoDimensions.width -
remoteVideoDimensions.streamWidth remoteVideoDimensions.streamWidth;
if (remoteVideoDimensions.offsetX > 0) { if (remoteVideoDimensions.offsetX > 0) {
remoteVideoDimensions.offsetX /= 2; remoteVideoDimensions.offsetX /= 2;
} }
@ -351,18 +354,22 @@ loop.shared.mixins = (function() {
this._bufferedUpdateVideo = null; this._bufferedUpdateVideo = null;
var localStreamParent = this._getElement(".local .OT_publisher"); var localStreamParent = this._getElement(".local .OT_publisher");
var remoteStreamParent = this._getElement(".remote .OT_subscriber"); var remoteStreamParent = this._getElement(".remote .OT_subscriber");
var screenShareStreamParent = this._getElement('.screen .OT_subscriber');
if (localStreamParent) { if (localStreamParent) {
localStreamParent.style.width = "100%"; localStreamParent.style.width = "100%";
} }
if (remoteStreamParent) { if (remoteStreamParent) {
remoteStreamParent.style.height = "100%"; remoteStreamParent.style.height = "100%";
} }
if (screenShareStreamParent) {
screenShareStreamParent.style.height = "100%";
}
// Update the position and dimensions of the containers of local video // Update the position and dimensions of the containers of local video
// streams, if necessary. The consumer of this mixin should implement the // streams, if necessary. The consumer of this mixin should implement the
// actual updating mechanism. // actual updating mechanism.
Object.keys(this._videoDimensionsCache.local).forEach(function(videoType) { Object.keys(this._videoDimensionsCache.local).forEach(function(videoType) {
var ratio = this._videoDimensionsCache.local[videoType].aspectRatio var ratio = this._videoDimensionsCache.local[videoType].aspectRatio;
if (videoType == "camera" && this.updateLocalCameraPosition) { if (videoType == "camera" && this.updateLocalCameraPosition) {
this.updateLocalCameraPosition(ratio); this.updateLocalCameraPosition(ratio);
} }

View File

@ -136,6 +136,7 @@ loop.OTSdkDriver = (function() {
this.session.on("connectionCreated", this._onConnectionCreated.bind(this)); this.session.on("connectionCreated", this._onConnectionCreated.bind(this));
this.session.on("streamCreated", this._onRemoteStreamCreated.bind(this)); this.session.on("streamCreated", this._onRemoteStreamCreated.bind(this));
this.session.on("streamDestroyed", this._onRemoteStreamDestroyed.bind(this));
this.session.on("connectionDestroyed", this.session.on("connectionDestroyed",
this._onConnectionDestroyed.bind(this)); this._onConnectionDestroyed.bind(this));
this.session.on("sessionDisconnected", this.session.on("sessionDisconnected",
@ -280,6 +281,30 @@ loop.OTSdkDriver = (function() {
this.dispatcher.dispatch(new sharedActions.RemotePeerConnected()); this.dispatcher.dispatch(new sharedActions.RemotePeerConnected());
}, },
/**
* Handles when a remote screen share is created, subscribing to
* the stream, and notifying the stores that a share is being
* received.
*
* @param {Stream} stream The SDK Stream:
* https://tokbox.com/opentok/libraries/client/js/reference/Stream.html
*/
_handleRemoteScreenShareCreated: function(stream) {
if (!this.getScreenShareElementFunc) {
return;
}
// Let the stores know first so they can update the display.
this.dispatcher.dispatch(new sharedActions.ReceivingScreenShare({
receiving: true
}));
var remoteElement = this.getScreenShareElementFunc();
this.session.subscribe(stream,
remoteElement, this._getCopyPublisherConfig());
},
/** /**
* Handles the event when the remote stream is created. * Handles the event when the remote stream is created.
* *
@ -295,14 +320,13 @@ loop.OTSdkDriver = (function() {
})); }));
} }
var remoteElement;
if (event.stream.videoType === "screen") { if (event.stream.videoType === "screen") {
// XXX Implement in part 2. this._handleRemoteScreenShareCreated(event.stream);
remoteElement = "null"; return;
} else {
remoteElement = this.getRemoteElement();
} }
var remoteElement = this.getRemoteElement();
this.session.subscribe(event.stream, this.session.subscribe(event.stream,
remoteElement, this._getCopyPublisherConfig()); remoteElement, this._getCopyPublisherConfig());
@ -328,6 +352,25 @@ loop.OTSdkDriver = (function() {
} }
}, },
/**
* Handles the event when the remote stream is destroyed.
*
* @param {StreamEvent} event The event details:
* https://tokbox.com/opentok/libraries/client/js/reference/StreamEvent.html
*/
_onRemoteStreamDestroyed: function(event) {
if (event.stream.videoType !== "screen") {
return;
}
// All we need to do is notify the store we're no longer receiving,
// the sdk should do the rest.
this.dispatcher.dispatch(new sharedActions.ReceivingScreenShare({
receiving: false
}));
},
/** /**
* Called from the sdk when the media access dialog is opened. * Called from the sdk when the media access dialog is opened.
* Prevents the default action, to prevent the SDK's "allow access" * Prevents the default action, to prevent the SDK's "allow access"

View File

@ -358,7 +358,7 @@ loop.shared.views = (function(_, l10n) {
React.createElement("div", {className: "conversation"}, React.createElement("div", {className: "conversation"},
React.createElement("div", {className: "media nested"}, React.createElement("div", {className: "media nested"},
React.createElement("div", {className: "video_wrapper remote_wrapper"}, React.createElement("div", {className: "video_wrapper remote_wrapper"},
React.createElement("div", {className: "video_inner remote"}) React.createElement("div", {className: "video_inner remote remote-stream"})
), ),
React.createElement("div", {className: localStreamClasses}) React.createElement("div", {className: localStreamClasses})
), ),

View File

@ -358,7 +358,7 @@ loop.shared.views = (function(_, l10n) {
<div className="conversation"> <div className="conversation">
<div className="media nested"> <div className="media nested">
<div className="video_wrapper remote_wrapper"> <div className="video_wrapper remote_wrapper">
<div className="video_inner remote"></div> <div className="video_inner remote remote-stream"></div>
</div> </div>
<div className={localStreamClasses}></div> <div className={localStreamClasses}></div>
</div> </div>

View File

@ -251,7 +251,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({ this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({
publisherConfig: this.getDefaultPublisherConfig({publishVideo: true}), publisherConfig: this.getDefaultPublisherConfig({publishVideo: true}),
getLocalElementFunc: this._getElement.bind(this, ".local"), getLocalElementFunc: this._getElement.bind(this, ".local"),
getRemoteElementFunc: this._getElement.bind(this, ".remote") getRemoteElementFunc: this._getElement.bind(this, ".remote"),
getScreenShareElementFunc: this._getElement.bind(this, ".screen")
})); }));
} }
@ -339,6 +340,19 @@ loop.standaloneRoomViews = (function(mozL10n) {
"local-stream-audio": this.state.videoMuted "local-stream-audio": this.state.videoMuted
}); });
var remoteStreamClasses = React.addons.classSet({
"video_inner": true,
"remote": true,
"remote-stream": true,
hide: this.state.receivingScreenShare
});
var screenShareStreamClasses = React.addons.classSet({
"screen": true,
"remote-stream": true,
hide: !this.state.receivingScreenShare
});
return ( return (
React.createElement("div", {className: "room-conversation-wrapper"}, React.createElement("div", {className: "room-conversation-wrapper"},
React.createElement("div", {className: "beta-logo"}), React.createElement("div", {className: "beta-logo"}),
@ -357,11 +371,13 @@ loop.standaloneRoomViews = (function(mozL10n) {
mozL10n.get("self_view_hidden_message") mozL10n.get("self_view_hidden_message")
), ),
React.createElement("div", {className: "video_wrapper remote_wrapper"}, React.createElement("div", {className: "video_wrapper remote_wrapper"},
React.createElement("div", {className: "video_inner remote"}) React.createElement("div", {className: remoteStreamClasses}),
React.createElement("div", {className: screenShareStreamClasses})
), ),
React.createElement("div", {className: localStreamClasses}) React.createElement("div", {className: localStreamClasses})
), ),
React.createElement(sharedViews.ConversationToolbar, { React.createElement(sharedViews.ConversationToolbar, {
dispatcher: this.props.dispatcher,
video: {enabled: !this.state.videoMuted, video: {enabled: !this.state.videoMuted,
visible: this._roomIsActive()}, visible: this._roomIsActive()},
audio: {enabled: !this.state.audioMuted, audio: {enabled: !this.state.audioMuted,

View File

@ -251,7 +251,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({ this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({
publisherConfig: this.getDefaultPublisherConfig({publishVideo: true}), publisherConfig: this.getDefaultPublisherConfig({publishVideo: true}),
getLocalElementFunc: this._getElement.bind(this, ".local"), getLocalElementFunc: this._getElement.bind(this, ".local"),
getRemoteElementFunc: this._getElement.bind(this, ".remote") getRemoteElementFunc: this._getElement.bind(this, ".remote"),
getScreenShareElementFunc: this._getElement.bind(this, ".screen")
})); }));
} }
@ -339,6 +340,19 @@ loop.standaloneRoomViews = (function(mozL10n) {
"local-stream-audio": this.state.videoMuted "local-stream-audio": this.state.videoMuted
}); });
var remoteStreamClasses = React.addons.classSet({
"video_inner": true,
"remote": true,
"remote-stream": true,
hide: this.state.receivingScreenShare
});
var screenShareStreamClasses = React.addons.classSet({
"screen": true,
"remote-stream": true,
hide: !this.state.receivingScreenShare
});
return ( return (
<div className="room-conversation-wrapper"> <div className="room-conversation-wrapper">
<div className="beta-logo" /> <div className="beta-logo" />
@ -357,11 +371,13 @@ loop.standaloneRoomViews = (function(mozL10n) {
{mozL10n.get("self_view_hidden_message")} {mozL10n.get("self_view_hidden_message")}
</span> </span>
<div className="video_wrapper remote_wrapper"> <div className="video_wrapper remote_wrapper">
<div className="video_inner remote"></div> <div className={remoteStreamClasses}></div>
<div className={screenShareStreamClasses}></div>
</div> </div>
<div className={localStreamClasses}></div> <div className={localStreamClasses}></div>
</div> </div>
<sharedViews.ConversationToolbar <sharedViews.ConversationToolbar
dispatcher={this.props.dispatcher}
video={{enabled: !this.state.videoMuted, video={{enabled: !this.state.videoMuted,
visible: this._roomIsActive()}} visible: this._roomIsActive()}}
audio={{enabled: !this.state.audioMuted, audio={{enabled: !this.state.audioMuted,

View File

@ -14,6 +14,7 @@ FIREFOX_PREFERENCES = {
"media.volume_scale": "0", "media.volume_scale": "0",
"loop.gettingStarted.seen": True, "loop.gettingStarted.seen": True,
"loop.seenToS": "seen", "loop.seenToS": "seen",
"loop.screenshare.enabled": True,
# this dialog is fragile, and likely to introduce intermittent failures # this dialog is fragile, and likely to introduce intermittent failures
"media.navigator.permission.disabled": True "media.navigator.permission.disabled": True

View File

@ -4,7 +4,6 @@ import urlparse
from errors import NoSuchElementException, StaleElementException from errors import NoSuchElementException, StaleElementException
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
from wait import Wait from wait import Wait
from time import sleep
import os import os
import sys import sys
@ -126,23 +125,32 @@ class Test1BrowserCall(MarionetteTestCase):
join_button.click() join_button.click()
# Assumes the standlone or the conversation window is selected first. # Assumes the standlone or the conversation window is selected first.
def check_remote_video(self): def check_video(self, selector):
video_wrapper = self.wait_for_element_displayed( video_wrapper = self.wait_for_element_displayed(By.CSS_SELECTOR,
By.CSS_SELECTOR, selector, 20)
".media .OT_subscriber .OT_widget-container", 20) video = self.wait_for_subelement_displayed(video_wrapper,
video = self.wait_for_subelement_displayed( By.TAG_NAME, "video")
video_wrapper, By.TAG_NAME, "video")
self.wait_for_element_attribute_to_be_false(video, "paused") self.wait_for_element_attribute_to_be_false(video, "paused")
self.assertEqual(video.get_attribute("ended"), "false") self.assertEqual(video.get_attribute("ended"), "false")
def standalone_check_remote_video(self): def standalone_check_remote_video(self):
self.switch_to_standalone() self.switch_to_standalone()
self.check_remote_video() self.check_video(".remote .OT_subscriber .OT_widget-container")
def local_check_remote_video(self): def local_check_remote_video(self):
self.switch_to_chatbox() self.switch_to_chatbox()
self.check_remote_video() self.check_video(".remote .OT_subscriber .OT_widget-container")
def local_enable_screenshare(self):
self.switch_to_chatbox()
button = self.marionette.find_element(By.CLASS_NAME, "btn-screen-share")
button.click()
def standalone_check_remote_screenshare(self):
self.switch_to_standalone()
self.check_video(".media .screen .OT_subscriber .OT_widget-container")
def local_leave_room_and_verify_feedback(self): def local_leave_room_and_verify_feedback(self):
button = self.marionette.find_element(By.CLASS_NAME, "btn-hangup") button = self.marionette.find_element(By.CLASS_NAME, "btn-hangup")
@ -170,6 +178,11 @@ class Test1BrowserCall(MarionetteTestCase):
self.standalone_check_remote_video() self.standalone_check_remote_video()
self.local_check_remote_video() self.local_check_remote_video()
# XXX To enable this, we either need to navigate the permissions prompt
# or have a route where we don't need the permissions prompt.
# self.local_enable_screenshare()
# self.standalone_check_remote_screenshare()
# hangup the call # hangup the call
self.local_leave_room_and_verify_feedback() self.local_leave_room_and_verify_feedback()

View File

@ -657,6 +657,16 @@ describe("loop.store.ActiveRoomStore", function () {
}); });
}); });
describe("#receivingScreenShare", function() {
it("should save the state", function() {
store.receivingScreenShare(new sharedActions.ReceivingScreenShare({
receiving: true
}));
expect(store.getStoreState().receivingScreenShare).eql(true);
});
});
describe("#remotePeerConnected", function() { describe("#remotePeerConnected", function() {
it("should set the state to `HAS_PARTICIPANTS`", function() { it("should set the state to `HAS_PARTICIPANTS`", function() {
store.remotePeerConnected(); store.remotePeerConnected();

View File

@ -236,13 +236,15 @@ describe("loop.shared.mixins", function() {
}); });
describe("Events", function() { describe("Events", function() {
var localElement, remoteElement; var localElement, remoteElement, screenShareElement;
beforeEach(function() { beforeEach(function() {
sandbox.stub(view, "getDOMNode").returns({ sandbox.stub(view, "getDOMNode").returns({
querySelector: function(classSelector) { querySelector: function(classSelector) {
if (classSelector.contains("local")) { if (classSelector.contains("local")) {
return localElement; return localElement;
} else if (classSelector.contains("screen")) {
return screenShareElement;
} }
return remoteElement; return remoteElement;
} }
@ -275,6 +277,19 @@ describe("loop.shared.mixins", function() {
expect(remoteElement.style.height).eql("100%"); expect(remoteElement.style.height).eql("100%");
}); });
it("should update the height on the screen share stream element", function() {
screenShareElement = {
offsetWidth: 100,
offsetHeight: 100,
style: { height: "0%" }
};
rootObject.events.resize();
sandbox.clock.tick(10);
expect(screenShareElement.style.height).eql("100%");
});
}); });
describe("orientationchange", function() { describe("orientationchange", function() {
@ -303,6 +318,19 @@ describe("loop.shared.mixins", function() {
expect(remoteElement.style.height).eql("100%"); expect(remoteElement.style.height).eql("100%");
}); });
it("should update the height on the screen share stream element", function() {
screenShareElement = {
offsetWidth: 100,
offsetHeight: 100,
style: { height: "0%" }
};
rootObject.events.orientationchange();
sandbox.clock.tick(10);
expect(screenShareElement.style.height).eql("100%");
});
}); });

View File

@ -12,13 +12,15 @@ describe("loop.OTSdkDriver", function () {
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES; var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
var sandbox; var sandbox;
var dispatcher, driver, publisher, sdk, session, sessionData; var dispatcher, driver, publisher, sdk, session, sessionData;
var fakeLocalElement, fakeRemoteElement, publisherConfig, fakeEvent; var fakeLocalElement, fakeRemoteElement, fakeScreenElement;
var publisherConfig, fakeEvent;
beforeEach(function() { beforeEach(function() {
sandbox = sinon.sandbox.create(); sandbox = sinon.sandbox.create();
fakeLocalElement = {fake: 1}; fakeLocalElement = {fake: 1};
fakeRemoteElement = {fake: 2}; fakeRemoteElement = {fake: 2};
fakeScreenElement = {fake: 3};
fakeEvent = { fakeEvent = {
preventDefault: sinon.stub() preventDefault: sinon.stub()
}; };
@ -290,6 +292,7 @@ describe("loop.OTSdkDriver", function () {
dispatcher.dispatch(new sharedActions.SetupStreamElements({ dispatcher.dispatch(new sharedActions.SetupStreamElements({
getLocalElementFunc: function() {return fakeLocalElement;}, getLocalElementFunc: function() {return fakeLocalElement;},
getScreenShareElementFunc: function() {return fakeScreenElement;},
getRemoteElementFunc: function() {return fakeRemoteElement;}, getRemoteElementFunc: function() {return fakeRemoteElement;},
publisherConfig: publisherConfig publisherConfig: publisherConfig
})); }));
@ -353,16 +356,50 @@ describe("loop.OTSdkDriver", function () {
}); });
}); });
describe("streamCreated", function() { describe("streamCreated (publisher/local)", function() {
it("should dispatch a VideoDimensionsChanged action", function() {
var fakeStream = {
hasVideo: true,
videoType: "camera",
videoDimensions: {width: 1, height: 2}
};
publisher.trigger("streamCreated", {stream: fakeStream});
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.VideoDimensionsChanged({
isLocal: true,
videoType: "camera",
dimensions: {width: 1, height: 2}
}));
});
});
describe("streamCreated (session/remote)", function() {
var fakeStream; var fakeStream;
beforeEach(function() { beforeEach(function() {
fakeStream = { fakeStream = {
fakeStream: 3 hasVideo: true,
videoType: "camera",
videoDimensions: {width: 1, height: 2}
}; };
}); });
it("should subscribe to the stream", function() { it("should dispatch a VideoDimensionsChanged action", function() {
session.trigger("streamCreated", {stream: fakeStream});
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.VideoDimensionsChanged({
isLocal: false,
videoType: "camera",
dimensions: {width: 1, height: 2}
}));
});
it("should subscribe to a camera stream", function() {
session.trigger("streamCreated", {stream: fakeStream}); session.trigger("streamCreated", {stream: fakeStream});
sinon.assert.calledOnce(session.subscribe); sinon.assert.calledOnce(session.subscribe);
@ -370,15 +407,85 @@ describe("loop.OTSdkDriver", function () {
fakeStream, fakeRemoteElement, publisherConfig); fakeStream, fakeRemoteElement, publisherConfig);
}); });
it("should subscribe to a screen sharing stream", function() {
fakeStream.videoType = "screen";
session.trigger("streamCreated", {stream: fakeStream});
sinon.assert.calledOnce(session.subscribe);
sinon.assert.calledWithExactly(session.subscribe,
fakeStream, fakeScreenElement, publisherConfig);
});
it("should dispach a mediaConnected action if both streams are up", function() { it("should dispach a mediaConnected action if both streams are up", function() {
driver._publishedLocalStream = true; driver._publishedLocalStream = true;
session.trigger("streamCreated", {stream: fakeStream}); session.trigger("streamCreated", {stream: fakeStream});
sinon.assert.calledOnce(dispatcher.dispatch); // Called twice due to the VideoDimensionsChanged above.
sinon.assert.calledTwice(dispatcher.dispatch);
sinon.assert.calledWithMatch(dispatcher.dispatch, sinon.assert.calledWithMatch(dispatcher.dispatch,
sinon.match.hasOwn("name", "mediaConnected")); sinon.match.hasOwn("name", "mediaConnected"));
}); });
it("should not dispatch a mediaConnected action for screen sharing streams",
function() {
driver._publishedLocalStream = true;
fakeStream.videoType = "screen";
session.trigger("streamCreated", {stream: fakeStream});
sinon.assert.neverCalledWithMatch(dispatcher.dispatch,
sinon.match.hasOwn("name", "mediaConnected"));
});
it("should not dispatch a ReceivingScreenShare action for camera streams",
function() {
session.trigger("streamCreated", {stream: fakeStream});
sinon.assert.neverCalledWithMatch(dispatcher.dispatch,
new sharedActions.ReceivingScreenShare({receiving: true}));
});
it("should dispatch a ReceivingScreenShare action for screen sharing streams",
function() {
fakeStream.videoType = "screen";
session.trigger("streamCreated", {stream: fakeStream});
// Called twice due to the VideoDimensionsChanged above.
sinon.assert.calledTwice(dispatcher.dispatch);
sinon.assert.calledWithMatch(dispatcher.dispatch,
new sharedActions.ReceivingScreenShare({receiving: true}));
});
});
describe("streamDestroyed", function() {
var fakeStream;
beforeEach(function() {
fakeStream = {
videoType: "screen"
};
});
it("should dispatch a ReceivingScreenShare action", function() {
session.trigger("streamDestroyed", {stream: fakeStream});
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.ReceivingScreenShare({
receiving: false
}));
});
it("should not dispatch an action if the videoType is camera", function() {
fakeStream.videoType = "camera";
session.trigger("streamDestroyed", {stream: fakeStream});
sinon.assert.notCalled(dispatcher.dispatch);
});
}); });
describe("streamPropertyChanged", function() { describe("streamPropertyChanged", function() {
@ -415,8 +522,8 @@ describe("loop.OTSdkDriver", function () {
sinon.assert.calledOnce(dispatcher.dispatch); sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithMatch(dispatcher.dispatch, sinon.assert.calledWithMatch(dispatcher.dispatch,
sinon.match.hasOwn("name", "videoDimensionsChanged")) sinon.match.hasOwn("name", "videoDimensionsChanged"));
}) });
}); });
describe("connectionCreated", function() { describe("connectionCreated", function() {