From 77709b16afde1f19664c716449cb43f308fb5bf7 Mon Sep 17 00:00:00 2001 From: Aleh Zasypkin Date: Fri, 23 Jan 2015 18:11:36 +0100 Subject: [PATCH] Bug 1122501 - [Messages][Drafts] Unsaved draft is silently discarded when user tries to forward message. r=schung --- apps/sms/js/activity_handler.js | 85 +++-------- apps/sms/js/activity_picker.js | 9 +- apps/sms/js/thread_ui.js | 52 +++++-- apps/sms/locales/sms.en-US.properties | 2 +- apps/sms/test/unit/activity_handler_test.js | 160 ++++++-------------- apps/sms/test/unit/activity_picker_test.js | 13 +- apps/sms/test/unit/mock_activity_handler.js | 3 - apps/sms/test/unit/mock_activity_picker.js | 3 +- apps/sms/test/unit/mock_compose.js | 14 +- apps/sms/test/unit/mock_message_manager.js | 4 - apps/sms/test/unit/thread_ui_test.js | 101 +++++++++++- 11 files changed, 215 insertions(+), 231 deletions(-) diff --git a/apps/sms/js/activity_handler.js b/apps/sms/js/activity_handler.js index 91135eac89bd..592e5f4304e5 100755 --- a/apps/sms/js/activity_handler.js +++ b/apps/sms/js/activity_handler.js @@ -1,7 +1,7 @@ /* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ -/*global Utils, MessageManager, Compose, OptionMenu, NotificationHelper, +/*global Utils, MessageManager, Compose, NotificationHelper, Attachment, Notify, SilentSms, Threads, SMIL, Contacts, ThreadUI, Notification, Settings, Navigation */ /*exported ActivityHandler */ @@ -231,6 +231,11 @@ var ActivityHandler = { return; } + // If we're currently in the target thread, just do nothing + if (Navigation.isCurrentPanel('thread', { id: message.threadId })) { + return; + } + MessageManager.getMessage(message.id).then((message) => { if (!Threads.has(message.threadId)) { Threads.registerMessage(message); @@ -241,7 +246,11 @@ var ActivityHandler = { return; } - Utils.confirm('discard-new-message').then(() => { + Utils.confirm( + 'discard-new-message', + 'unsent-message-title', + { text: 'unsent-message-option-discard', className: 'danger' } + ).then(() => { ThreadUI.cleanFields(); ActivityHandler.toView(message); }); @@ -250,56 +259,6 @@ var ActivityHandler = { }); }, - // The unsent confirmation dialog provides 2 options: edit and discard - // discard: clear the message user typed - // edit: continue to edit the unsent message and ignore the activity - displayUnsentConfirmation: function ah_displayUnsentConfirmtion(activity) { - var msgDiv = document.createElement('div'); - msgDiv.innerHTML = '

' + - '

'; - var options = new OptionMenu({ - type: 'confirm', - section: msgDiv, - items: [{ - l10nId: 'unsent-message-option-edit', - method: function editOptionMethod() { - // we're already in message app, we don't need to do anything - } - }, - { - l10nId: 'unsent-message-option-discard', - method: (activity) => { - ThreadUI.discardDraft(); - this.launchComposer(activity); - }, - params: [activity] - }] - }); - options.show(); - }, - - // Launch the UI properly - launchComposer: function ah_launchComposer(activity) { - Navigation.toPanel('composer', { activity: activity }); - }, - - // Check if we want to go directly to the composer or if we - // want to keep the previously typed text - triggerNewMessage: function ah_triggerNewMessage(body, number, contact) { - var activity = { - body: body || null, - number: number || null, - contact: contact || null - }; - - if (Compose.isEmpty()) { - this.launchComposer(activity); - } else { - // ask user how should we do - ActivityHandler.displayUnsentConfirmation(activity); - } - }, - /** * Delivers the user to the correct view based on the params provided in the * "message" parameter. @@ -315,17 +274,23 @@ var ActivityHandler = { */ toView: function ah_toView(message, focusComposer) { var navigateToView = function act_navigateToView() { - // If we only have a body, just trigger a new message. - if (!message.threadId) { - ActivityHandler.triggerNewMessage( - message.body, message.number, message.contact - ); + // If we have appropriate thread then let's forward user there, otherwise + // open new message composer. + if (message.threadId) { + Navigation.toPanel('thread', { + id: message.threadId, + focusComposer: focusComposer + }); return; } - Navigation.toPanel( - 'thread', { id: message.threadId, focusComposer: focusComposer } - ); + Navigation.toPanel('composer', { + activity: { + body: message.body || null, + number: message.number || null, + contact: message.contact || null + } + }); }; navigator.mozL10n.once(function waitLocalized() { diff --git a/apps/sms/js/activity_picker.js b/apps/sms/js/activity_picker.js index b0d1ea7e87c9..b1e2a6d90b19 100644 --- a/apps/sms/js/activity_picker.js +++ b/apps/sms/js/activity_picker.js @@ -1,4 +1,4 @@ -/*global ActivityHandler, MozActivity */ +/*global MozActivity */ (function(exports) { 'use strict'; @@ -76,13 +76,6 @@ var ActivityPicker = { } }), onsuccess, onerror); }, - sendMessage: function ap_sendMessage(number) { - // Using ActivityHandler here to navigate to Composer view in the same way - // as it's done for real activity. - ActivityHandler.toView({ - number: number - }); - }, openSettings: function ap_openSettings(onsuccess, onerror) { handleActivity(new MozActivity({ name: 'configure', diff --git a/apps/sms/js/thread_ui.js b/apps/sms/js/thread_ui.js index 6a8053961a25..3c58ff0c7b2c 100755 --- a/apps/sms/js/thread_ui.js +++ b/apps/sms/js/thread_ui.js @@ -1105,6 +1105,26 @@ var ThreadUI = { }); }, + /** + * Navigates user to Composer panel with custom parameters. + * @param {*} parameters Optional navigation parameters. + * @returns {Promise} Promise that is resolved once navigation is completed. + */ + navigateToComposer: function(parameters) { + if (Compose.isEmpty()) { + return Navigation.toPanel('composer', parameters); + } + + return Utils.confirm( + 'unsent-message-text', + 'unsent-message-title', + { text: 'unsent-message-option-discard', className: 'danger' } + ).then(() => { + this.discardDraft(); + return Navigation.toPanel('composer', parameters); + }); + }, + _onNavigatingBack: function() { this.stopRendering(); @@ -2054,14 +2074,11 @@ var ThreadUI = { if (!lineClassList.contains('not-downloaded')) { params.items.push({ l10nId: 'forward', - method: function forwardMessage(messageId) { - Navigation.toPanel('composer', { - forward: { - messageId: messageId - } + method: () => { + this.navigateToComposer({ + forward: { messageId: messageId } }); - }, - params: [messageId] + } }); } @@ -2790,10 +2807,14 @@ var ThreadUI = { if (Settings.supportEmailRecipient) { items.push({ l10nId: 'sendMMSToEmail', - method: function oMMS(param) { - ActivityPicker.sendMessage(param); + method: () => { + this.navigateToComposer({ + activity: { number: email } + }); }, - params: [email] + // As we change panel here, we don't want to call 'complete' that + // changes the panel as well + incomplete: true }); } } else { @@ -2809,12 +2830,13 @@ var ThreadUI = { items.push({ l10nId: 'sendMessage', - method: function oMessage(param) { - ActivityPicker.sendMessage(param); + method: () => { + this.navigateToComposer({ + activity: { number: number } + }); }, - params: [number], - // As activity picker changes the panel we don't want - // to call 'complete' that changes the panel as well + // As we change panel here, we don't want to call 'complete' that + // changes the panel as well incomplete: true }); } diff --git a/apps/sms/locales/sms.en-US.properties b/apps/sms/locales/sms.en-US.properties index 7b9d85649a77..fbca06f7dd3e 100755 --- a/apps/sms/locales/sms.en-US.properties +++ b/apps/sms/locales/sms.en-US.properties @@ -220,7 +220,7 @@ replace-attachment-audio = Replace audio replace-attachment-video = Replace video replace-attachment-other = Replace attachment unsent-message-title = Unsent Message -unsent-message-description = Your previous message was not sent. Do you want to edit the unsent message or discard it and continue? +unsent-message-text = Your previous message was not sent. Do you want to discard the unsent message and continue? unsent-message-option-edit = Edit unsent-message-option-discard = Discard message-options = Message options diff --git a/apps/sms/test/unit/activity_handler_test.js b/apps/sms/test/unit/activity_handler_test.js index a09253deea6a..e95fa2017734 100755 --- a/apps/sms/test/unit/activity_handler_test.js +++ b/apps/sms/test/unit/activity_handler_test.js @@ -2,7 +2,7 @@ Attachment, ThreadUI, Settings, Notification, Threads, Navigation, Promise, MessageManager, Utils */ /*global MockNavigatormozSetMessageHandler, MockNavigatormozApps, - MockNavigatorWakeLock, MockOptionMenu, + MockNavigatorWakeLock, MockMessages, MockL10n, MockSilentSms, Settings, Utils @@ -28,7 +28,6 @@ requireApp('sms/test/unit/mock_messages.js'); requireApp('sms/test/unit/mock_message_manager.js'); requireApp('sms/test/unit/mock_threads.js'); requireApp('sms/test/unit/mock_thread_ui.js'); -require('/shared/test/unit/mocks/mock_option_menu.js'); require('/test/unit/mock_settings.js'); require('/test/unit/mock_notify.js'); require('/test/unit/mock_navigation.js'); @@ -47,7 +46,6 @@ var mocksHelperForActivityHandler = new MocksHelper([ 'Notification', 'NotificationHelper', 'Notify', - 'OptionMenu', 'Settings', 'SettingsURL', 'SilentSms', @@ -762,16 +760,6 @@ suite('ActivityHandler', function() { postResult: sinon.stub() }; - var newActivity_empty = { - source: { - name: 'new', - data: { - number: '123' - } - }, - postResult: sinon.stub() - }; - var threadDeferred; setup(function() { @@ -793,68 +781,12 @@ suite('ActivityHandler', function() { ActivityHandler.leaveActivity(); }); - test('Should move to the composer', function(done) { - MockNavigatormozSetMessageHandler.mTrigger('activity', newActivity); - threadDeferred.reject(new Error('No thread for this test')); - - onceNewActivityCompleted().then(function() { - sinon.assert.calledWithMatch(Navigation.toPanel, 'composer', { - activity: { - number: '123', - body: 'foo' - } - }); - }).then(done,done); - }); - - test('new message with empty msg', function() { - // No message in the input field. - Compose.mEmpty = true; - this.sinon.stub(MockOptionMenu.prototype, 'show', function() { - assert.ok(false, 'confirmation dialog should not show'); - }); - - MockNavigatormozSetMessageHandler.mTrigger('activity', newActivity); - threadDeferred.reject(new Error('No thread for this test')); - }); - - test('new message with no body, with empty msg', function() { - // No message in the input field. - Compose.mEmpty = true; - this.sinon.stub(MockOptionMenu.prototype, 'show', function() { - assert.ok(false, 'confirmation dialog should not show'); - }); - - MockNavigatormozSetMessageHandler.mTrigger('activity', newActivity_empty); - threadDeferred.reject(new Error('No thread for this test')); - }); - - test('new message with user input msg, discard it', function(done) { - // User typed message in the input field - Compose.mEmpty = false; - this.sinon.stub(ThreadUI, 'discardDraft'); - this.sinon.stub(MockOptionMenu.prototype, 'show', function() { - assert.equal(MockOptionMenu.calls.length, 1); - assert.equal(MockOptionMenu.calls[0].type, 'confirm'); - - var items = MockOptionMenu.calls[0].items; - assert.isNotNull(items); - assert.equal(items.length, 2); - // discard is the second button - assert.isNotNull(items[1]); - assert.equal(typeof items[1].method, 'function'); - // Check params - assert.equal(newActivity.source.data.number, items[1].params[0].number); - assert.equal(newActivity.source.data.body, items[1].params[0].body); - // Call discard with the params - items[1].method(items[1].params[0]); - }); - + test('Should move to the composer and set activity', function(done) { MockNavigatormozSetMessageHandler.mTrigger('activity', newActivity); threadDeferred.reject(new Error('No thread for this test')); onceNewActivityCompleted().then(function() { - sinon.assert.called(ThreadUI.discardDraft); + assert.isTrue(ActivityHandler.isInActivity()); sinon.assert.calledWithMatch(Navigation.toPanel, 'composer', { activity: { number: '123', @@ -864,42 +796,6 @@ suite('ActivityHandler', function() { }).then(done,done); }); - test('new message with user input msg, edit it', function(done) { - // There is message in the input field. - Compose.mEmpty = false; - this.sinon.stub(MockOptionMenu.prototype, 'show', function() { - assert.equal(MockOptionMenu.calls.length, 1); - assert.equal(MockOptionMenu.calls[0].type, 'confirm'); - - var items = MockOptionMenu.calls[0].items; - assert.isNotNull(items); - assert.equal(items.length, 2); - // edit is the first button - assert.isNotNull(items[0]); - assert.equal(typeof items[0].method, 'function'); - // Check if when keeping the previous message the - // composer keeps the previous status; - assert.isNotNull(items[0].params); - // call edit. - items[0].method(); - }); - MockNavigatormozSetMessageHandler.mTrigger('activity', newActivity); - threadDeferred.reject(new Error('No thread for this test')); - - onceNewActivityCompleted().then(function() { - sinon.assert.notCalled(Navigation.toPanel); - }).then(done,done); - }); - - test('new message should set the current activity', function(done) { - MockNavigatormozSetMessageHandler.mTrigger('activity', newActivity); - threadDeferred.reject(new Error('No thread for this test')); - - onceNewActivityCompleted().then(function() { - assert.isTrue(ActivityHandler.isInActivity()); - }).then(done,done); - }); - test('new message with body only', function(done) { var activity = { source: { @@ -981,14 +877,28 @@ suite('ActivityHandler', function() { this.sinon.stub(Threads, 'has'); this.sinon.stub(Threads, 'registerMessage'); this.sinon.stub(MessageManager, 'getMessage').returns(getMessagePromise); + this.sinon.stub(Navigation, 'isCurrentPanel').returns(false); + this.sinon.stub(Navigation, 'toPanel'); + this.sinon.stub(Compose, 'clear'); + this.sinon.stub(Compose, 'isEmpty').returns(true); + this.sinon.stub(ThreadUI, 'cleanFields'); + }); + + test('when message belongs to currently active thread', function() { + Navigation.isCurrentPanel.withArgs( + 'thread', { id: message.threadId } + ).returns(true); + + ActivityHandler.handleMessageNotification(message); + + sinon.assert.notCalled(MessageManager.getMessage); + sinon.assert.notCalled(Utils.confirm); + sinon.assert.notCalled(Navigation.toPanel); }); suite('When compose is not empty', function() { setup(function() { - this.sinon.stub(Compose, 'clear'); - this.sinon.stub(ThreadUI, 'cleanFields'); - - Compose.append('test'); + Compose.isEmpty.returns(false); }); test('if user does not want to discard draft', function(done) { @@ -1000,7 +910,12 @@ suite('ActivityHandler', function() { getMessagePromise.then(() => confirmPromise).catch(() => { sinon.assert.notCalled(Compose.clear); sinon.assert.notCalled(ThreadUI.cleanFields); - sinon.assert.called(Utils.confirm); + sinon.assert.calledWith( + Utils.confirm, + 'discard-new-message', + 'unsent-message-title', + { text: 'unsent-message-option-discard', className: 'danger' } + ); }).then(done, done); }); @@ -1012,9 +927,28 @@ suite('ActivityHandler', function() { getMessagePromise.then(() => confirmPromise).then(() => { sinon.assert.called(ThreadUI.cleanFields); - sinon.assert.called(Utils.confirm); + sinon.assert.calledWith( + Utils.confirm, + 'discard-new-message', + 'unsent-message-title', + { text: 'unsent-message-option-discard', className: 'danger' } + ); }).then(done, done); }); + + test('if message belongs to currently active thread', function() { + Navigation.isCurrentPanel.withArgs( + 'thread', { id: message.threadId } + ).returns(true); + + ActivityHandler.handleMessageNotification(message); + + // It shouldn't matter if message input has any text or not since target + // thread is already opened + sinon.assert.notCalled(MessageManager.getMessage); + sinon.assert.notCalled(Utils.confirm); + sinon.assert.notCalled(Navigation.toPanel); + }); }); test('registers message in Threads if no related thread', function(done) { diff --git a/apps/sms/test/unit/activity_picker_test.js b/apps/sms/test/unit/activity_picker_test.js index b587e69e8b2a..ea7bde3dd642 100644 --- a/apps/sms/test/unit/activity_picker_test.js +++ b/apps/sms/test/unit/activity_picker_test.js @@ -1,4 +1,4 @@ -/*global MocksHelper, MockL10n, ActivityPicker, MozActivity, ActivityHandler */ +/*global MocksHelper, MockL10n, ActivityPicker, MozActivity */ 'use strict'; @@ -289,17 +289,6 @@ suite('ActivityPicker', function() { }); }); - suite('sendMessage', function() { - test('sendMessage(phone) ', function() { - this.sinon.stub(ActivityHandler, 'toView'); - ActivityPicker.sendMessage('999'); - - sinon.assert.calledWith(ActivityHandler.toView, { - number: '999' - }); - }); - }); - suite('viewContact', function() { test('viewContact(props, success, error) ', function() { diff --git a/apps/sms/test/unit/mock_activity_handler.js b/apps/sms/test/unit/mock_activity_handler.js index 6a7c30eacdc0..063a6f699622 100644 --- a/apps/sms/test/unit/mock_activity_handler.js +++ b/apps/sms/test/unit/mock_activity_handler.js @@ -6,9 +6,6 @@ var MockActivityHandler = { isInActivity: function() {}, leaveActivity: function() {}, handleMessageNotification: function() {}, - displayUnsentConfirmation: function() {}, - launchComposer: function() {}, - triggerNewMessage: function() {}, toView: function() {}, onSmsReceived: function() {}, onNotification: function() {} diff --git a/apps/sms/test/unit/mock_activity_picker.js b/apps/sms/test/unit/mock_activity_picker.js index fdaa901797da..8794d3c2f57d 100644 --- a/apps/sms/test/unit/mock_activity_picker.js +++ b/apps/sms/test/unit/mock_activity_picker.js @@ -6,8 +6,7 @@ var MockActivityPicker = {}; [ 'email', 'dial', 'url', - 'createNewContact', 'addToExistingContact', - 'sendMessage' + 'createNewContact', 'addToExistingContact' ].forEach(function(fn) { MockActivityPicker[fn] = function() { MockActivityPicker[fn].called = true; diff --git a/apps/sms/test/unit/mock_compose.js b/apps/sms/test/unit/mock_compose.js index ce686ae6fc47..235122725b1a 100644 --- a/apps/sms/test/unit/mock_compose.js +++ b/apps/sms/test/unit/mock_compose.js @@ -9,9 +9,7 @@ var MockCompose = { offAll: function() {}, getContent: function() {}, getText: function() {}, - isEmpty: function() { - return this.mEmpty; - }, + isEmpty: () => true, isSubjectMaxLength: () => false, lock: function() {}, unlock: function() {}, @@ -19,12 +17,8 @@ var MockCompose = { scrollToTarget: function(target) {}, scrollMessageContent: function() {}, prepend: function(item) {}, - append: function(aContent) { - this.mEmpty = false; - }, - clear: function() { - this.mEmpty = true; - }, + append: () => {}, + clear: () => {}, focus: function() {}, updateType: function() {}, @@ -35,12 +29,10 @@ var MockCompose = { fromDraft: function() {}, fromMessage: function() {}, - mEmpty: true, mSubjectEmpty: true, mSubjectShowing: false, mSetup: function() { - this.mEmpty = true; this.mSubjectEmpty = true; this.mSubjectShowing = false; this.size = null; diff --git a/apps/sms/test/unit/mock_message_manager.js b/apps/sms/test/unit/mock_message_manager.js index 2c5abc68c27c..09c69fd6f121 100644 --- a/apps/sms/test/unit/mock_message_manager.js +++ b/apps/sms/test/unit/mock_message_manager.js @@ -17,10 +17,6 @@ var MockMessageManager = { callback(); } }, - launchComposer: function() {}, - handleActivity: function() {}, - handleForward: function() {}, - registerMessage: function() {}, sendSMS: function() { return {}; }, diff --git a/apps/sms/test/unit/thread_ui_test.js b/apps/sms/test/unit/thread_ui_test.js index 807064d3e1ff..b856a2746177 100755 --- a/apps/sms/test/unit/thread_ui_test.js +++ b/apps/sms/test/unit/thread_ui_test.js @@ -3777,6 +3777,25 @@ suite('thread_ui.js >', function() { } }).then(done, done); }); + + suite('message context menu actions >', function() { + setup(function() { + this.sinon.stub(Compose, 'isEmpty').returns(true); + link.dispatchEvent(contextMenuEvent); + }); + + test('forward message', function() { + this.sinon.stub(ThreadUI, 'navigateToComposer'); + + var forwardOption = MockOptionMenu.calls[0].items[0]; + forwardOption.method(messageId); + + assert.equal(forwardOption.l10nId, 'forward'); + sinon.assert.calledWith( + ThreadUI.navigateToComposer, { forward: { messageId: messageId } } + ); + }); + }); }); suite('Message resending UI', function() { @@ -4130,7 +4149,7 @@ suite('thread_ui.js >', function() { MockSettings.supportEmailRecipient = true; this.sinon.spy(ActivityPicker, 'email'); - this.sinon.spy(ActivityPicker, 'sendMessage'); + this.sinon.spy(ThreadUI, 'navigateToComposer'); ThreadUI.prompt({ email: 'a@b.com', @@ -4163,11 +4182,16 @@ suite('thread_ui.js >', function() { // The second item is a "sendMMSToEmail" option assert.equal(items[1].l10nId, 'sendMMSToEmail'); + // We shouldn't navigate to Thread panel if we're in Participants + // panel and would like to send new MMS + assert.equal(items[1].incomplete, true); // Trigger the option to ensure that correct Activity is used. items[1].method(); - sinon.assert.called(ActivityPicker.sendMessage); + sinon.assert.calledWith( + ThreadUI.navigateToComposer, { activity: { number: 'a@b.com' } } + ); // The third item is a "createNewContact" option assert.equal(items[2].l10nId, 'createNewContact'); @@ -4289,6 +4313,8 @@ suite('thread_ui.js >', function() { suite('multi recipients, in group view >', function() { setup(function() { + this.sinon.spy(ThreadUI, 'navigateToComposer'); + Navigation.isCurrentPanel.withArgs('group-view').returns(true); Threads.set(1, { @@ -4323,6 +4349,15 @@ suite('thread_ui.js >', function() { // The second item is a "send message" option assert.equal(items[1].l10nId, 'sendMessage'); + // We shouldn't navigate to Thread panel if we're in Participants + // panel and would like to send new SMS + assert.equal(items[1].incomplete, true); + + items[1].method(); + + sinon.assert.calledWith( + ThreadUI.navigateToComposer, { activity: { number: '999' } } + ); // The third and last item is a "cancel" option assert.equal(items[2].l10nId, 'cancel'); @@ -4352,6 +4387,9 @@ suite('thread_ui.js >', function() { // The second item is a "sendMessage" option assert.equal(items[1].l10nId, 'sendMessage'); + // We shouldn't navigate to Thread panel if we're in Participants + // panel and would like to send new SMS + assert.equal(items[1].incomplete, true); // The third item is a "createNewContact" option assert.equal(items[2].l10nId, 'createNewContact'); @@ -4444,6 +4482,65 @@ suite('thread_ui.js >', function() { }); }); + suite('navigateToComposer', function() { + setup(function() { + this.sinon.spy(Navigation, 'toPanel'); + this.sinon.spy(ThreadUI, 'discardDraft'); + this.sinon.stub(Utils, 'confirm'); + this.sinon.stub(Compose, 'isEmpty'); + }); + + test('immediately navigates to Composer if no unsent message', + function(done) { + Compose.isEmpty.returns(true); + + ThreadUI.navigateToComposer({ test: 'test' }).then(() => { + sinon.assert.calledWith( + Navigation.toPanel, 'composer', { test: 'test' } + ); + sinon.assert.notCalled(Utils.confirm); + }).then(done, done); + }); + + test('navigates to Composer when user discards unsent message', + function(done) { + Compose.isEmpty.returns(false); + Utils.confirm.returns(Promise.resolve()); + + ThreadUI.navigateToComposer({ test: 'test' }).then(() => { + sinon.assert.calledWith( + Utils.confirm, + 'unsent-message-text', + 'unsent-message-title', + { text: 'unsent-message-option-discard', className: 'danger' } + ); + sinon.assert.called(ThreadUI.discardDraft); + sinon.assert.calledWith( + Navigation.toPanel, 'composer', { test: 'test' } + ); + }).then(done, done); + }); + + test('stays in the current panel if user wants to keep unsent message', + function(done) { + Compose.isEmpty.returns(false); + Utils.confirm.returns(Promise.reject()); + + ThreadUI.navigateToComposer({ test: 'test' }).then(() => { + throw new Error('navigateToComposer should be rejected!'); + }, () => { + sinon.assert.calledWith( + Utils.confirm, + 'unsent-message-text', + 'unsent-message-title', + { text: 'unsent-message-option-discard', className: 'danger' } + ); + sinon.assert.notCalled(ThreadUI.discardDraft); + sinon.assert.notCalled(Navigation.toPanel); + }).then(done, done); + }); + }); + // See: utils_test.js // Utils.getPhoneDetails //