зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1048162 Part 1 - Add an 'Email Link' button to Loop desktop failed call view. r=Standard8
This commit is contained in:
Родитель
86289da8b5
Коммит
9b7f6776df
|
@ -601,15 +601,18 @@ function injectLoopAPI(targetWindow) {
|
|||
/**
|
||||
* Composes an email via the external protocol service.
|
||||
*
|
||||
* @param {String} subject Subject of the email to send
|
||||
* @param {String} body Body message of the email to send
|
||||
* @param {String} subject Subject of the email to send
|
||||
* @param {String} body Body message of the email to send
|
||||
* @param {String} recipient Recipient email address (optional)
|
||||
*/
|
||||
composeEmail: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(subject, body) {
|
||||
let mailtoURL = "mailto:?subject=" + encodeURIComponent(subject) + "&" +
|
||||
"body=" + encodeURIComponent(body);
|
||||
value: function(subject, body, recipient) {
|
||||
recipient = recipient || "";
|
||||
let mailtoURL = "mailto:" + encodeURIComponent(recipient) +
|
||||
"?subject=" + encodeURIComponent(subject) +
|
||||
"&body=" + encodeURIComponent(body);
|
||||
extProtocolSvc.loadURI(CommonUtils.makeURI(mailtoURL));
|
||||
}
|
||||
},
|
||||
|
|
|
@ -12,8 +12,20 @@ loop.conversationViews = (function(mozL10n) {
|
|||
var CALL_STATES = loop.store.CALL_STATES;
|
||||
var CALL_TYPES = loop.shared.utils.CALL_TYPES;
|
||||
var sharedActions = loop.shared.actions;
|
||||
var sharedUtils = loop.shared.utils;
|
||||
var sharedViews = loop.shared.views;
|
||||
|
||||
// This duplicates a similar function in contacts.jsx that isn't used in the
|
||||
// conversation window. If we get too many of these, we might want to consider
|
||||
// finding a logical place for them to be shared.
|
||||
function _getPreferredEmail(contact) {
|
||||
// A contact may not contain email addresses, but only a phone number.
|
||||
if (!contact.email || contact.email.length === 0) {
|
||||
return { value: "" };
|
||||
}
|
||||
return contact.email.find(e => e.pref) || contact.email[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays information about the call
|
||||
* Caller avatar, name & conversation creation date
|
||||
|
@ -93,17 +105,6 @@ loop.conversationViews = (function(mozL10n) {
|
|||
contact: React.PropTypes.object
|
||||
},
|
||||
|
||||
// This duplicates a similar function in contacts.jsx that isn't used in the
|
||||
// conversation window. If we get too many of these, we might want to consider
|
||||
// finding a logical place for them to be shared.
|
||||
_getPreferredEmail: function(contact) {
|
||||
// A contact may not contain email addresses, but only a phone number.
|
||||
if (!contact.email || contact.email.length == 0) {
|
||||
return { value: "" };
|
||||
}
|
||||
return contact.email.find(e => e.pref) || contact.email[0];
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var contactName;
|
||||
|
||||
|
@ -111,7 +112,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||
this.props.contact.name[0]) {
|
||||
contactName = this.props.contact.name[0];
|
||||
} else {
|
||||
contactName = this._getPreferredEmail(this.props.contact).value;
|
||||
contactName = _getPreferredEmail(this.props.contact).value;
|
||||
}
|
||||
|
||||
document.title = contactName;
|
||||
|
@ -187,8 +188,33 @@ loop.conversationViews = (function(mozL10n) {
|
|||
* Call failed view. Displayed when a call fails.
|
||||
*/
|
||||
var CallFailedView = React.createClass({displayName: 'CallFailedView',
|
||||
mixins: [Backbone.Events],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
store: React.PropTypes.instanceOf(
|
||||
loop.store.ConversationStore).isRequired,
|
||||
contact: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {emailLinkButtonDisabled: false};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.listenTo(this.props.store, "change:emailLink",
|
||||
this._onEmailLinkReceived);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this.stopListening(this.props.store);
|
||||
},
|
||||
|
||||
_onEmailLinkReceived: function() {
|
||||
var emailLink = this.props.store.get("emailLink");
|
||||
var contactEmail = _getPreferredEmail(this.props.contact).value;
|
||||
sharedUtils.composeCallUrlEmail(emailLink, contactEmail);
|
||||
window.close();
|
||||
},
|
||||
|
||||
retryCall: function() {
|
||||
|
@ -199,25 +225,33 @@ loop.conversationViews = (function(mozL10n) {
|
|||
this.props.dispatcher.dispatch(new sharedActions.CancelCall());
|
||||
},
|
||||
|
||||
emailLink: function() {
|
||||
this.setState({emailLinkButtonDisabled: true});
|
||||
|
||||
this.props.dispatcher.dispatch(new sharedActions.FetchEmailLink());
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
React.DOM.div({className: "call-window"},
|
||||
React.DOM.h2(null, mozL10n.get("generic_failure_title")),
|
||||
|
||||
React.DOM.p({className: "btn-label"}, mozL10n.get("generic_failure_no_reason2")),
|
||||
React.DOM.p({className: "btn-label"}, mozL10n.get("generic_failure_with_reason2")),
|
||||
|
||||
React.DOM.div({className: "btn-group call-action-group"},
|
||||
React.DOM.div({className: "fx-embedded-call-button-spacer"}),
|
||||
React.DOM.button({className: "btn btn-accept btn-retry",
|
||||
onClick: this.retryCall},
|
||||
mozL10n.get("retry_call_button")
|
||||
),
|
||||
React.DOM.div({className: "fx-embedded-call-button-spacer"}),
|
||||
React.DOM.button({className: "btn btn-cancel",
|
||||
onClick: this.cancelCall},
|
||||
mozL10n.get("cancel_button")
|
||||
),
|
||||
React.DOM.div({className: "fx-embedded-call-button-spacer"})
|
||||
React.DOM.button({className: "btn btn-cancel",
|
||||
onClick: this.cancelCall},
|
||||
mozL10n.get("cancel_button")
|
||||
),
|
||||
React.DOM.button({className: "btn btn-info btn-retry",
|
||||
onClick: this.retryCall},
|
||||
mozL10n.get("retry_call_button")
|
||||
),
|
||||
React.DOM.button({className: "btn btn-info btn-email",
|
||||
onClick: this.emailLink,
|
||||
disabled: this.state.emailLinkButtonDisabled},
|
||||
mozL10n.get("share_button2")
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
@ -425,7 +459,9 @@ loop.conversationViews = (function(mozL10n) {
|
|||
}
|
||||
case CALL_STATES.TERMINATED: {
|
||||
return (CallFailedView({
|
||||
dispatcher: this.props.dispatcher}
|
||||
dispatcher: this.props.dispatcher,
|
||||
store: this.props.store,
|
||||
contact: this.state.contact}
|
||||
));
|
||||
}
|
||||
case CALL_STATES.ONGOING: {
|
||||
|
@ -445,7 +481,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||
callState: this.state.callState,
|
||||
contact: this.state.contact,
|
||||
enableCancelButton: this._isCancellable()}
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -12,8 +12,20 @@ loop.conversationViews = (function(mozL10n) {
|
|||
var CALL_STATES = loop.store.CALL_STATES;
|
||||
var CALL_TYPES = loop.shared.utils.CALL_TYPES;
|
||||
var sharedActions = loop.shared.actions;
|
||||
var sharedUtils = loop.shared.utils;
|
||||
var sharedViews = loop.shared.views;
|
||||
|
||||
// This duplicates a similar function in contacts.jsx that isn't used in the
|
||||
// conversation window. If we get too many of these, we might want to consider
|
||||
// finding a logical place for them to be shared.
|
||||
function _getPreferredEmail(contact) {
|
||||
// A contact may not contain email addresses, but only a phone number.
|
||||
if (!contact.email || contact.email.length === 0) {
|
||||
return { value: "" };
|
||||
}
|
||||
return contact.email.find(e => e.pref) || contact.email[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays information about the call
|
||||
* Caller avatar, name & conversation creation date
|
||||
|
@ -93,17 +105,6 @@ loop.conversationViews = (function(mozL10n) {
|
|||
contact: React.PropTypes.object
|
||||
},
|
||||
|
||||
// This duplicates a similar function in contacts.jsx that isn't used in the
|
||||
// conversation window. If we get too many of these, we might want to consider
|
||||
// finding a logical place for them to be shared.
|
||||
_getPreferredEmail: function(contact) {
|
||||
// A contact may not contain email addresses, but only a phone number.
|
||||
if (!contact.email || contact.email.length == 0) {
|
||||
return { value: "" };
|
||||
}
|
||||
return contact.email.find(e => e.pref) || contact.email[0];
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var contactName;
|
||||
|
||||
|
@ -111,7 +112,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||
this.props.contact.name[0]) {
|
||||
contactName = this.props.contact.name[0];
|
||||
} else {
|
||||
contactName = this._getPreferredEmail(this.props.contact).value;
|
||||
contactName = _getPreferredEmail(this.props.contact).value;
|
||||
}
|
||||
|
||||
document.title = contactName;
|
||||
|
@ -187,8 +188,33 @@ loop.conversationViews = (function(mozL10n) {
|
|||
* Call failed view. Displayed when a call fails.
|
||||
*/
|
||||
var CallFailedView = React.createClass({
|
||||
mixins: [Backbone.Events],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
|
||||
store: React.PropTypes.instanceOf(
|
||||
loop.store.ConversationStore).isRequired,
|
||||
contact: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {emailLinkButtonDisabled: false};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.listenTo(this.props.store, "change:emailLink",
|
||||
this._onEmailLinkReceived);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
this.stopListening(this.props.store);
|
||||
},
|
||||
|
||||
_onEmailLinkReceived: function() {
|
||||
var emailLink = this.props.store.get("emailLink");
|
||||
var contactEmail = _getPreferredEmail(this.props.contact).value;
|
||||
sharedUtils.composeCallUrlEmail(emailLink, contactEmail);
|
||||
window.close();
|
||||
},
|
||||
|
||||
retryCall: function() {
|
||||
|
@ -199,25 +225,33 @@ loop.conversationViews = (function(mozL10n) {
|
|||
this.props.dispatcher.dispatch(new sharedActions.CancelCall());
|
||||
},
|
||||
|
||||
emailLink: function() {
|
||||
this.setState({emailLinkButtonDisabled: true});
|
||||
|
||||
this.props.dispatcher.dispatch(new sharedActions.FetchEmailLink());
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div className="call-window">
|
||||
<h2>{mozL10n.get("generic_failure_title")}</h2>
|
||||
|
||||
<p className="btn-label">{mozL10n.get("generic_failure_no_reason2")}</p>
|
||||
<p className="btn-label">{mozL10n.get("generic_failure_with_reason2")}</p>
|
||||
|
||||
<div className="btn-group call-action-group">
|
||||
<div className="fx-embedded-call-button-spacer"></div>
|
||||
<button className="btn btn-accept btn-retry"
|
||||
onClick={this.retryCall}>
|
||||
{mozL10n.get("retry_call_button")}
|
||||
</button>
|
||||
<div className="fx-embedded-call-button-spacer"></div>
|
||||
<button className="btn btn-cancel"
|
||||
onClick={this.cancelCall}>
|
||||
{mozL10n.get("cancel_button")}
|
||||
</button>
|
||||
<div className="fx-embedded-call-button-spacer"></div>
|
||||
<button className="btn btn-cancel"
|
||||
onClick={this.cancelCall}>
|
||||
{mozL10n.get("cancel_button")}
|
||||
</button>
|
||||
<button className="btn btn-info btn-retry"
|
||||
onClick={this.retryCall}>
|
||||
{mozL10n.get("retry_call_button")}
|
||||
</button>
|
||||
<button className="btn btn-info btn-email"
|
||||
onClick={this.emailLink}
|
||||
disabled={this.state.emailLinkButtonDisabled}>
|
||||
{mozL10n.get("share_button2")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -426,6 +460,8 @@ loop.conversationViews = (function(mozL10n) {
|
|||
case CALL_STATES.TERMINATED: {
|
||||
return (<CallFailedView
|
||||
dispatcher={this.props.dispatcher}
|
||||
store={this.props.store}
|
||||
contact={this.state.contact}
|
||||
/>);
|
||||
}
|
||||
case CALL_STATES.ONGOING: {
|
||||
|
@ -445,7 +481,7 @@ loop.conversationViews = (function(mozL10n) {
|
|||
callState={this.state.callState}
|
||||
contact={this.state.contact}
|
||||
enableCancelButton={this._isCancellable()}
|
||||
/>)
|
||||
/>);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -15,6 +15,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
var sharedModels = loop.shared.models;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var sharedActions = loop.shared.actions;
|
||||
var sharedUtils = loop.shared.utils;
|
||||
var Button = sharedViews.Button;
|
||||
var ButtonGroup = sharedViews.ButtonGroup;
|
||||
var ContactsList = loop.contacts.ContactsList;
|
||||
|
@ -362,11 +363,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
handleEmailButtonClick: function(event) {
|
||||
this.handleLinkExfiltration(event);
|
||||
|
||||
navigator.mozLoop.composeEmail(
|
||||
__("share_email_subject4", { clientShortname: __("clientShortname2")}),
|
||||
__("share_email_body4", { callUrl: this.state.callUrl,
|
||||
clientShortname: __("clientShortname2"),
|
||||
learnMoreUrl: navigator.mozLoop.getLoopCharPref("learnMoreUrl") }));
|
||||
sharedUtils.composeCallUrlEmail(this.state.callUrl);
|
||||
},
|
||||
|
||||
handleCopyButtonClick: function(event) {
|
||||
|
|
|
@ -15,6 +15,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
var sharedModels = loop.shared.models;
|
||||
var sharedMixins = loop.shared.mixins;
|
||||
var sharedActions = loop.shared.actions;
|
||||
var sharedUtils = loop.shared.utils;
|
||||
var Button = sharedViews.Button;
|
||||
var ButtonGroup = sharedViews.ButtonGroup;
|
||||
var ContactsList = loop.contacts.ContactsList;
|
||||
|
@ -362,11 +363,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
handleEmailButtonClick: function(event) {
|
||||
this.handleLinkExfiltration(event);
|
||||
|
||||
navigator.mozLoop.composeEmail(
|
||||
__("share_email_subject4", { clientShortname: __("clientShortname2")}),
|
||||
__("share_email_body4", { callUrl: this.state.callUrl,
|
||||
clientShortname: __("clientShortname2"),
|
||||
learnMoreUrl: navigator.mozLoop.getLoopCharPref("learnMoreUrl") }));
|
||||
sharedUtils.composeCallUrlEmail(this.state.callUrl);
|
||||
},
|
||||
|
||||
handleCopyButtonClick: function(event) {
|
||||
|
|
|
@ -240,16 +240,21 @@
|
|||
min-height: 230px;
|
||||
}
|
||||
|
||||
.call-window > .btn-label {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.call-action-group {
|
||||
display: flex;
|
||||
padding: 2.5em 0 0 0;
|
||||
padding: 2.5em 4px 0 4px;
|
||||
width: 100%;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.call-action-group > .btn {
|
||||
margin-left: .5em;
|
||||
height: 26px;
|
||||
border-radius: 2px;
|
||||
margin: 0 4px;
|
||||
min-width: 64px;
|
||||
}
|
||||
|
||||
.call-action-group .btn-group-chevron,
|
||||
|
|
|
@ -30,6 +30,13 @@ loop.shared.actions = (function() {
|
|||
};
|
||||
|
||||
return {
|
||||
/**
|
||||
* Fetch a new call url from the server, intended to be sent over email when
|
||||
* a contact can't be reached.
|
||||
*/
|
||||
FetchEmailLink: Action.define("fetchEmailLink", {
|
||||
}),
|
||||
|
||||
/**
|
||||
* Used to trigger gathering of initial call data.
|
||||
*/
|
||||
|
|
|
@ -126,7 +126,8 @@ loop.store.ConversationStore = (function() {
|
|||
"cancelCall",
|
||||
"retryCall",
|
||||
"mediaConnected",
|
||||
"setMute"
|
||||
"setMute",
|
||||
"fetchEmailLink"
|
||||
]);
|
||||
},
|
||||
|
||||
|
@ -303,6 +304,23 @@ loop.store.ConversationStore = (function() {
|
|||
this.set(muteType, !actionData.enabled);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetches a new call URL intended to be sent over email when a contact
|
||||
* can't be reached.
|
||||
*/
|
||||
fetchEmailLink: function() {
|
||||
// XXX This is an empty string as a conversation identifier. Bug 1015938 implements
|
||||
// a user-set string.
|
||||
this.client.requestCallUrl("", function(err, callUrlData) {
|
||||
if (err) {
|
||||
// XXX better error reporting in the UI
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
this.set("emailLink", callUrlData.callUrl);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Obtains the outgoing call data from the server and handles the
|
||||
* result.
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
var loop = loop || {};
|
||||
loop.shared = loop.shared || {};
|
||||
loop.shared.utils = (function() {
|
||||
loop.shared.utils = (function(mozL10n) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
|
@ -96,11 +96,37 @@ loop.shared.utils = (function() {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates and opens a mailto: url with call URL information prefilled.
|
||||
* Note: This only works for Desktop.
|
||||
*
|
||||
* @param {String} callUrl The call URL.
|
||||
* @param {String} recipient The recipient email address (optional).
|
||||
*/
|
||||
function composeCallUrlEmail(callUrl, recipient) {
|
||||
if (typeof navigator.mozLoop === "undefined") {
|
||||
console.warn("composeCallUrlEmail isn't available for Loop standalone.");
|
||||
return;
|
||||
}
|
||||
navigator.mozLoop.composeEmail(
|
||||
mozL10n.get("share_email_subject4", {
|
||||
clientShortname: mozL10n.get("clientShortname2")
|
||||
}),
|
||||
mozL10n.get("share_email_body4", {
|
||||
callUrl: callUrl,
|
||||
clientShortname: mozL10n.get("clientShortname2"),
|
||||
learnMoreUrl: navigator.mozLoop.getLoopCharPref("learnMoreUrl")
|
||||
}),
|
||||
recipient
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
CALL_TYPES: CALL_TYPES,
|
||||
Helper: Helper,
|
||||
composeCallUrlEmail: composeCallUrlEmail,
|
||||
formatDate: formatDate,
|
||||
getTargetPlatform: getTargetPlatform,
|
||||
getBoolPreference: getBoolPreference
|
||||
};
|
||||
})();
|
||||
})(document.mozL10n || navigator.mozL10n);
|
||||
|
|
|
@ -6,6 +6,7 @@ var expect = chai.expect;
|
|||
describe("loop.conversationViews", function () {
|
||||
"use strict";
|
||||
|
||||
var sharedUtils = loop.shared.utils;
|
||||
var sandbox, oldTitle, view, dispatcher, contact;
|
||||
|
||||
var CALL_STATES = loop.store.CALL_STATES;
|
||||
|
@ -201,13 +202,25 @@ describe("loop.conversationViews", function () {
|
|||
});
|
||||
|
||||
describe("CallFailedView", function() {
|
||||
var store;
|
||||
|
||||
function mountTestComponent(props) {
|
||||
return TestUtils.renderIntoDocument(
|
||||
loop.conversationViews.CallFailedView({
|
||||
dispatcher: dispatcher
|
||||
dispatcher: dispatcher,
|
||||
store: store,
|
||||
contact: {email: [{value: "test@test.tld"}]}
|
||||
}));
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
store = new loop.store.ConversationStore({}, {
|
||||
dispatcher: dispatcher,
|
||||
client: {},
|
||||
sdkDriver: {}
|
||||
});
|
||||
});
|
||||
|
||||
it("should dispatch a retryCall action when the retry button is pressed",
|
||||
function() {
|
||||
view = mountTestComponent();
|
||||
|
@ -233,6 +246,48 @@ describe("loop.conversationViews", function () {
|
|||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("name", "cancelCall"));
|
||||
});
|
||||
|
||||
it("should dispatch a fetchEmailLink action when the cancel button is pressed",
|
||||
function() {
|
||||
view = mountTestComponent();
|
||||
|
||||
var emailLinkBtn = view.getDOMNode().querySelector('.btn-email');
|
||||
|
||||
React.addons.TestUtils.Simulate.click(emailLinkBtn);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("name", "fetchEmailLink"));
|
||||
});
|
||||
|
||||
it("should disable the email link button once the action is dispatched",
|
||||
function() {
|
||||
view = mountTestComponent();
|
||||
var emailLinkBtn = view.getDOMNode().querySelector('.btn-email');
|
||||
React.addons.TestUtils.Simulate.click(emailLinkBtn);
|
||||
|
||||
expect(view.getDOMNode().querySelector(".btn-email").disabled).eql(true);
|
||||
});
|
||||
|
||||
it("should compose an email once the email link is received", function() {
|
||||
var composeCallUrlEmail = sandbox.stub(sharedUtils, "composeCallUrlEmail");
|
||||
view = mountTestComponent();
|
||||
store.set("emailLink", "http://fake.invalid/");
|
||||
|
||||
sinon.assert.calledOnce(composeCallUrlEmail);
|
||||
sinon.assert.calledWithExactly(composeCallUrlEmail,
|
||||
"http://fake.invalid/", "test@test.tld");
|
||||
});
|
||||
|
||||
it("should close the conversation window once the email link is received",
|
||||
function() {
|
||||
sandbox.stub(window, "close");
|
||||
view = mountTestComponent();
|
||||
|
||||
store.set("emailLink", "http://fake.invalid/");
|
||||
|
||||
sinon.assert.calledOnce(window.close);
|
||||
});
|
||||
});
|
||||
|
||||
describe("OngoingConversationView", function() {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
var expect = chai.expect;
|
||||
var TestUtils = React.addons.TestUtils;
|
||||
var sharedActions = loop.shared.actions;
|
||||
var sharedUtils = loop.shared.utils;
|
||||
|
||||
describe("loop.panel", function() {
|
||||
"use strict";
|
||||
|
@ -449,6 +450,7 @@ describe("loop.panel", function() {
|
|||
|
||||
it("should display a share button for email", function() {
|
||||
fakeClient.requestCallUrl = sandbox.stub();
|
||||
var composeCallUrlEmail = sandbox.stub(sharedUtils, "composeCallUrlEmail");
|
||||
var view = TestUtils.renderIntoDocument(loop.panel.CallUrlResult({
|
||||
notifications: notifications,
|
||||
client: fakeClient
|
||||
|
@ -457,7 +459,9 @@ describe("loop.panel", function() {
|
|||
|
||||
TestUtils.findRenderedDOMComponentWithClass(view, "button-email");
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-email"));
|
||||
sinon.assert.calledOnce(navigator.mozLoop.composeEmail);
|
||||
|
||||
sinon.assert.calledOnce(composeCallUrlEmail);
|
||||
sinon.assert.calledWithExactly(composeCallUrlEmail, "http://example.com");
|
||||
});
|
||||
|
||||
it("should feature a copy button capable of copying the call url when clicked", function() {
|
||||
|
|
|
@ -38,7 +38,8 @@ describe("loop.store.ConversationStore", function () {
|
|||
|
||||
dispatcher = new loop.Dispatcher();
|
||||
client = {
|
||||
setupOutgoingCall: sinon.stub()
|
||||
setupOutgoingCall: sinon.stub(),
|
||||
requestCallUrl: sinon.stub()
|
||||
};
|
||||
sdkDriver = {
|
||||
connectSession: sinon.stub(),
|
||||
|
@ -566,6 +567,28 @@ describe("loop.store.ConversationStore", function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#fetchEmailLink", function() {
|
||||
it("should request a new call url to the server", function() {
|
||||
dispatcher.dispatch(new sharedActions.FetchEmailLink());
|
||||
|
||||
sinon.assert.calledOnce(client.requestCallUrl);
|
||||
sinon.assert.calledWith(client.requestCallUrl, "");
|
||||
});
|
||||
|
||||
it("should update the emailLink attribute when the new call url is received",
|
||||
function() {
|
||||
client.requestCallUrl = function(callId, cb) {
|
||||
cb(null, {callUrl: "http://fake.invalid/"});
|
||||
};
|
||||
dispatcher.dispatch(new sharedActions.FetchEmailLink());
|
||||
|
||||
expect(store.get("emailLink")).eql("http://fake.invalid/");
|
||||
});
|
||||
|
||||
// XXX bug 1048162 Part 2
|
||||
it.skip("should trigger an error in case of failure");
|
||||
});
|
||||
|
||||
describe("Events", function() {
|
||||
describe("Websocket progress", function() {
|
||||
beforeEach(function() {
|
||||
|
|
|
@ -18,6 +18,7 @@ describe("loop.shared.utils", function() {
|
|||
});
|
||||
|
||||
afterEach(function() {
|
||||
navigator.mozLoop = undefined;
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
|
@ -110,7 +111,6 @@ describe("loop.shared.utils", function() {
|
|||
|
||||
describe("#getBoolPreference", function() {
|
||||
afterEach(function() {
|
||||
navigator.mozLoop = undefined;
|
||||
localStorage.removeItem("test.true");
|
||||
});
|
||||
|
||||
|
@ -142,4 +142,31 @@ describe("loop.shared.utils", function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#composeCallUrlEmail", function() {
|
||||
var composeEmail;
|
||||
|
||||
beforeEach(function() {
|
||||
// fake mozL10n
|
||||
sandbox.stub(navigator.mozL10n, "get", function(id) {
|
||||
switch(id) {
|
||||
case "share_email_subject4": return "subject";
|
||||
case "share_email_body4": return "body";
|
||||
}
|
||||
});
|
||||
composeEmail = sandbox.spy();
|
||||
navigator.mozLoop = {
|
||||
getLoopCharPref: sandbox.spy(),
|
||||
composeEmail: composeEmail
|
||||
};
|
||||
});
|
||||
|
||||
it("should compose a call url email", function() {
|
||||
sharedUtils.composeCallUrlEmail("http://invalid", "fake@invalid.tld");
|
||||
|
||||
sinon.assert.calledOnce(composeEmail);
|
||||
sinon.assert.calledWith(composeEmail,
|
||||
"subject", "body", "fake@invalid.tld");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче