Chore: Cleanup attachment handling for Facebook & Whatsapp (#1051)

* Chore: Enable file upload for facebook messenger
* Chore: Fix attachments
* Chore: Fix Specs
* Fix ReplyBox file attachment logic
* Set default value for message

Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
This commit is contained in:
Sojan Jose 2020-07-17 00:32:32 +05:30 committed by GitHub
parent 196741d975
commit a18d54b706
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 212 additions and 108 deletions

View file

@ -15,9 +15,9 @@
/> />
<resizable-text-area <resizable-text-area
ref="messageInput" ref="messageInput"
v-model="message" v-model.trim="message"
class="input" class="input"
:placeholder="$t(messagePlaceHolder())" :placeholder="messagePlaceHolder"
:min-height="4" :min-height="4"
@focus="onFocus" @focus="onFocus"
@blur="onBlur" @blur="onBlur"
@ -25,46 +25,43 @@
<file-upload <file-upload
v-if="showFileUpload" v-if="showFileUpload"
:size="4096 * 4096" :size="4096 * 4096"
accept="jpg,jpeg,png,mp3,ogg,amr,pdf,mp4" accept="image/*, application/pdf, audio/mpeg, video/mp4, audio/ogg"
@input-file="onFileUpload" @input-file="onFileUpload"
> >
<i <i v-if="!isUploading" class="icon ion-android-attach attachment" />
v-if="!isUploading.image" <woot-spinner v-if="isUploading" />
class="icon ion-android-attach attachment"
/>
<woot-spinner v-if="isUploading.image" />
</file-upload> </file-upload>
<i <i
class="icon ion-happy-outline" class="icon ion-happy-outline"
:class="{ active: showEmojiPicker }" :class="{ active: showEmojiPicker }"
@click="toggleEmojiPicker()" @click="toggleEmojiPicker"
/> />
</div> </div>
<div class="reply-box__bottom"> <div class="reply-box__bottom">
<ul class="tabs"> <ul class="tabs">
<li class="tabs-title" :class="{ 'is-active': !isPrivate }"> <li class="tabs-title" :class="{ 'is-active': !isPrivate }">
<a href="#" @click="makeReply">{{ <a href="#" @click="setReplyMode">{{
$t('CONVERSATION.REPLYBOX.REPLY') $t('CONVERSATION.REPLYBOX.REPLY')
}}</a> }}</a>
</li> </li>
<li class="tabs-title is-private" :class="{ 'is-active': isPrivate }"> <li class="tabs-title is-private" :class="{ 'is-active': isPrivate }">
<a href="#" @click="makePrivate">{{ <a href="#" @click="setPrivateReplyMode">{{
$t('CONVERSATION.REPLYBOX.PRIVATE_NOTE') $t('CONVERSATION.REPLYBOX.PRIVATE_NOTE')
}}</a> }}</a>
</li> </li>
<li v-if="message.length" class="tabs-title message-length"> <li v-if="message.length" class="tabs-title message-length">
<a :class="{ 'message-error': message.length > maxLength - 40 }"> <a :class="{ 'message-error': isMessageLengthReachingThreshold }">{{
{{ message.length }} / {{ maxLength }} characterCountIndicator
</a> }}</a>
</li> </li>
</ul> </ul>
<button <button
type="button" type="button"
class="button send-button" class="button send-button"
:disabled="disableButton()" :disabled="isReplyButtonDisabled"
:class="{ :class="{
disabled: message.length === 0 || message.length > maxLength, disabled: isReplyButtonDisabled,
warning: isPrivate, warning: isPrivate,
}" }"
@click="sendMessage" @click="sendMessage"
@ -93,6 +90,12 @@ import FileUpload from 'vue-upload-component';
import EmojiInput from '../emoji/EmojiInput'; import EmojiInput from '../emoji/EmojiInput';
import CannedResponse from './CannedResponse'; import CannedResponse from './CannedResponse';
import ResizableTextArea from 'shared/components/ResizableTextArea'; import ResizableTextArea from 'shared/components/ResizableTextArea';
import {
isEscape,
isEnter,
hasPressedShift,
} from 'shared/helpers/KeyboardHelpers';
import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper';
export default { export default {
components: { components: {
@ -109,19 +112,38 @@ export default {
isFocused: false, isFocused: false,
showEmojiPicker: false, showEmojiPicker: false,
showCannedResponsesList: false, showCannedResponsesList: false,
isUploading: { isUploading: false,
audio: false,
video: false,
image: false,
},
}; };
}, },
computed: { computed: {
...mapGetters({ ...mapGetters({ currentChat: 'getSelectedChat' }),
currentChat: 'getSelectedChat', inboxId() {
}), return this.currentChat.inbox_id;
},
inbox() {
return this.$store.getters['inboxes/getInbox'](this.inboxId);
},
messagePlaceHolder() {
return this.isPrivate
? this.$t('CONVERSATION.FOOTER.PRIVATE_MSG_INPUT')
: this.$t('CONVERSATION.FOOTER.MSG_INPUT');
},
isMessageLengthReachingThreshold() {
return this.message.length > this.maxLength - 40;
},
characterCountIndicator() {
return `${this.message.length} / ${this.maxLength}`;
},
isReplyButtonDisabled() {
const isMessageEmpty = !this.message.replace(/\n/g, '').length;
return (
isMessageEmpty ||
this.message.length === 0 ||
this.message.length > this.maxLength
);
},
channelType() { channelType() {
return this.currentChat.meta.channel; return this.inbox.channel_type;
}, },
conversationType() { conversationType() {
const { additional_attributes: additionalAttributes } = this.currentChat; const { additional_attributes: additionalAttributes } = this.currentChat;
@ -129,18 +151,52 @@ export default {
return type || ''; return type || '';
}, },
maxLength() { maxLength() {
if (this.channelType === 'Channel::FacebookPage') { if (this.isPrivate) {
return 640; return MESSAGE_MAX_LENGTH.GENERAL;
} }
if (this.channelType === 'Channel::TwitterProfile') {
if (this.isAFacebookInbox) {
return MESSAGE_MAX_LENGTH.FACEBOOK;
}
if (this.isATwilioSMSChannel) {
return MESSAGE_MAX_LENGTH.TWILIO_SMS;
}
if (this.isATwitterInbox) {
if (this.conversationType === 'tweet') { if (this.conversationType === 'tweet') {
return 280; return MESSAGE_MAX_LENGTH.TWEET;
} }
} }
return 10000; return MESSAGE_MAX_LENGTH.GENERAL;
},
isATwitterInbox() {
return this.channelType === 'Channel::TwitterProfile';
},
isAFacebookInbox() {
return this.channelType === 'Channel::FacebookPage';
},
isAWebWidgetInbox() {
return this.channelType === 'Channel::WebWidget';
},
isATwilioSMSChannel() {
const { phone_number: phoneNumber = '' } = this.inbox;
return (
this.channelType === 'Channel::TwilioSms' &&
!phoneNumber.startsWith('whatsapp')
);
},
isATwilioWhatsappChannel() {
const { phone_number: phoneNumber = '' } = this.inbox;
return (
this.channelType === 'Channel::TwilioSms' &&
phoneNumber.startsWith('whatsapp')
);
}, },
showFileUpload() { showFileUpload() {
return this.channelType === 'Channel::WebWidget'; return (
this.isAWebWidgetInbox ||
this.isAFacebookInbox ||
this.isATwilioWhatsappChannel
);
}, },
replyButtonLabel() { replyButtonLabel() {
if (this.isPrivate) { if (this.isPrivate) {
@ -158,20 +214,18 @@ export default {
}, },
}, },
watch: { watch: {
message(val) { message(updatedMessage) {
if (this.isPrivate) { if (this.isPrivate) {
return; return;
} }
const isSlashCommand = val[0] === '/'; const isSlashCommand = updatedMessage[0] === '/';
const hasNextWord = val.includes(' '); const hasNextWord = updatedMessage.includes(' ');
const isShortCodeActive = isSlashCommand && !hasNextWord; const isShortCodeActive = isSlashCommand && !hasNextWord;
if (isShortCodeActive) { if (isShortCodeActive) {
this.showCannedResponsesList = true; this.showCannedResponsesList = true;
if (val.length > 1) { if (updatedMessage.length > 1) {
const searchKey = val.substr(1, val.length); const searchKey = updatedMessage.substr(1, updatedMessage.length);
this.$store.dispatch('getCannedResponse', { this.$store.dispatch('getCannedResponse', { searchKey });
searchKey,
});
} else { } else {
this.$store.dispatch('getCannedResponse'); this.$store.dispatch('getCannedResponse');
} }
@ -188,26 +242,18 @@ export default {
}, },
methods: { methods: {
handleKeyEvents(e) { handleKeyEvents(e) {
if (this.isEscape(e)) { if (isEscape(e)) {
this.hideEmojiPicker(); this.hideEmojiPicker();
this.hideCannedResponse(); this.hideCannedResponse();
} else if (this.isEnter(e)) { } else if (isEnter(e)) {
if (!e.shiftKey) { if (!hasPressedShift(e)) {
e.preventDefault(); e.preventDefault();
this.sendMessage(); this.sendMessage();
} }
} }
}, },
isEnter(e) {
return e.keyCode === 13;
},
isEscape(e) {
return e.keyCode === 27; // ESCAPE
},
async sendMessage() { async sendMessage() {
const isMessageEmpty = !this.message.replace(/\n/g, '').length; if (this.isReplyButtonDisabled) {
if (isMessageEmpty) return;
if (this.message.length > this.maxLength) {
return; return;
} }
const newMessage = this.message; const newMessage = this.message;
@ -231,11 +277,11 @@ export default {
this.message = message; this.message = message;
}, 100); }, 100);
}, },
makePrivate() { setPrivateReplyMode() {
this.isPrivate = true; this.isPrivate = true;
this.$refs.messageInput.focus(); this.$refs.messageInput.focus();
}, },
makeReply() { setReplyMode() {
this.isPrivate = false; this.isPrivate = false;
this.$refs.messageInput.focus(); this.$refs.messageInput.focus();
}, },
@ -258,7 +304,6 @@ export default {
hideCannedResponse() { hideCannedResponse() {
this.showCannedResponsesList = false; this.showCannedResponsesList = false;
}, },
onBlur() { onBlur() {
this.isFocused = false; this.isFocused = false;
this.toggleTyping('off'); this.toggleTyping('off');
@ -267,9 +312,8 @@ export default {
this.isFocused = true; this.isFocused = true;
this.toggleTyping('on'); this.toggleTyping('on');
}, },
toggleTyping(status) { toggleTyping(status) {
if (this.channelType === 'Channel::WebWidget' && !this.isPrivate) { if (this.isAWebWidgetInbox && !this.isPrivate) {
const conversationId = this.currentChat.id; const conversationId = this.currentChat.id;
this.$store.dispatch('conversationTypingStatus/toggleTyping', { this.$store.dispatch('conversationTypingStatus/toggleTyping', {
status, status,
@ -277,35 +321,19 @@ export default {
}); });
} }
}, },
disableButton() {
const messageHasOnlyNewLines = !this.message.replace(/\n/g, '').length;
return (
this.message.length === 0 ||
this.message.length > 640 ||
messageHasOnlyNewLines
);
},
messagePlaceHolder() {
const placeHolder = this.isPrivate
? 'CONVERSATION.FOOTER.PRIVATE_MSG_INPUT'
: 'CONVERSATION.FOOTER.MSG_INPUT';
return placeHolder;
},
onFileUpload(file) { onFileUpload(file) {
if (!file) { if (!file) {
return; return;
} }
this.isUploading.image = true; this.isUploading = true;
this.$store this.$store
.dispatch('sendAttachment', [this.currentChat.id, { file: file.file }]) .dispatch('sendAttachment', [this.currentChat.id, { file: file.file }])
.then(() => { .then(() => {
this.isUploading.image = false; this.isUploading = false;
this.$emit('scrollToMessage'); this.$emit('scrollToMessage');
}) })
.catch(() => { .catch(() => {
this.isUploading.image = false; this.isUploading = false;
this.$emit('scrollToMessage'); this.$emit('scrollToMessage');
}); });
}, },

View file

@ -12,6 +12,10 @@ const initialSelectedChat = {
status: null, status: null,
muted: false, muted: false,
seen: false, seen: false,
inbox_id: null,
additional_attributes: {
type: '',
},
dataFetched: false, dataFetched: false,
}; };
const state = { const state = {

View file

@ -0,0 +1,11 @@
export const isEnter = e => {
return e.keyCode === 13;
};
export const isEscape = e => {
return e.keyCode === 27;
};
export const hasPressedShift = e => {
return e.shiftKey;
};

View file

@ -2,7 +2,7 @@ import { escapeHtml } from './HTMLSanitizer';
class MessageFormatter { class MessageFormatter {
constructor(message) { constructor(message) {
this.message = escapeHtml(message) || ''; this.message = escapeHtml(message || '') || '';
} }
formatMessage() { formatMessage() {

View file

@ -1,3 +1,10 @@
export const isAFormMessage = message => message.content_type === 'form'; export const isAFormMessage = message => message.content_type === 'form';
export const isASubmittedFormMessage = (message = {}) => export const isASubmittedFormMessage = (message = {}) =>
isAFormMessage(message) && !!message.content_attributes?.submitted_values; isAFormMessage(message) && !!message.content_attributes?.submitted_values;
export const MESSAGE_MAX_LENGTH = {
GENERAL: 10000,
FACEBOOK: 640,
TWILIO_SMS: 160,
TWEET: 280,
};

View file

@ -0,0 +1,21 @@
import { isEnter, isEscape, hasPressedShift } from '../KeyboardHelpers';
describe('#KeyboardHelpers', () => {
describe('#isEnter', () => {
it('return correct values', () => {
expect(isEnter({ keyCode: 13 })).toEqual(true);
});
});
describe('#isEscape', () => {
it('return correct values', () => {
expect(isEscape({ keyCode: 27 })).toEqual(true);
});
});
describe('#hasPressedShift', () => {
it('return correct values', () => {
expect(hasPressedShift({ shiftKey: true })).toEqual(true);
});
});
});

View file

@ -0,0 +1,15 @@
class SendReplyJob < ApplicationJob
queue_as :high
def perform(message_id)
message = Message.find(message_id)
channel_name = message.conversation.inbox.channel.class.to_s
if channel_name == 'Channel::FacebookPage'
::Facebook::SendOnFacebookService.new(message: message).perform
elsif channel_name == 'Channel::TwitterProfile'
::Twitter::SendOnTwitterService.new(message: message).perform
elsif channel_name == 'Channel::TwilioSms'
::Twilio::SendOnTwilioService.new(message: message).perform
end
end
end

View file

@ -134,14 +134,7 @@ class Message < ApplicationRecord
end end
def send_reply def send_reply
channel_name = conversation.inbox.channel.class.to_s ::SendReplyJob.perform_later(id)
if channel_name == 'Channel::FacebookPage'
::Facebook::SendOnFacebookService.new(message: self).perform
elsif channel_name == 'Channel::TwitterProfile'
::Twitter::SendOnTwitterService.new(message: self).perform
elsif channel_name == 'Channel::TwilioSms'
::Twilio::SendOnTwilioService.new(message: self).perform
end
end end
def reopen_conversation def reopen_conversation

View file

@ -17,19 +17,26 @@ class Facebook::SendOnFacebookService < Base::SendOnChannelService
end end
def fb_attachment_message_params def fb_attachment_message_params
attachment = message.attachments.first
{ {
recipient: { id: contact.get_source_id(inbox.id) }, recipient: { id: contact.get_source_id(inbox.id) },
message: { message: {
attachment: { attachment: {
type: 'image', type: attachment_type(attachment),
payload: { payload: {
url: message.attachments.first.file_url url: attachment.file_url
} }
} }
} }
} }
end end
def attachment_type(attachment)
return attachment.file_type if %w[image audio video file].include? attachment.file_type
'file'
end
def fb_message_params def fb_message_params
if message.attachments.blank? if message.attachments.blank?
fb_text_message_params fb_text_message_params

View file

@ -56,10 +56,10 @@ title: "Supported features on channels"
| Channel | Channel greeting | Attachments | Agent Auto assignment | Slack | | Channel | Channel greeting | Attachments | Agent Auto assignment | Slack |
| -- | -- | -- | -- | -- | | -- | -- | -- | -- | -- |
| Website | Yes | Yes | Yes | Yes | | Website | Yes | Yes | Yes | Yes |
| Facebook | Yes | No | Yes | No | | Facebook | Yes | Yes | Yes | No |
| Twitter DM | Yes | No | Yes | No | | Twitter DM | Yes | No | Yes | No |
| Tweets | Yes | No | Yes | No | | Tweets | Yes | No | Yes | No |
| SMS | Yes | No | Yes | No | | SMS | Yes | No | Yes | No |
| Whatsapp | Yes | No | Yes | No | | Whatsapp | Yes | Yes | Yes | No |
</div> </div>

View file

@ -10,8 +10,8 @@ FactoryBot.define do
after(:build) do |message| after(:build) do |message|
message.sender ||= create(:user, account: message.account) message.sender ||= create(:user, account: message.account)
message.conversation ||= create(:conversation, account: message.account)
message.inbox ||= create(:inbox, account: message.account) message.inbox ||= create(:inbox, account: message.account)
message.conversation ||= create(:conversation, account: message.account, inbox: message.inbox)
end end
end end
end end

View file

@ -6,6 +6,7 @@ describe Facebook::SendOnFacebookService do
before do before do
allow(Facebook::Messenger::Subscriptions).to receive(:subscribe).and_return(true) allow(Facebook::Messenger::Subscriptions).to receive(:subscribe).and_return(true)
allow(bot).to receive(:deliver) allow(bot).to receive(:deliver)
create(:message, message_type: :incoming, inbox: facebook_inbox, account: account, conversation: conversation)
end end
let!(:account) { create(:account) } let!(:account) { create(:account) }
@ -20,39 +21,43 @@ describe Facebook::SendOnFacebookService do
describe '#perform' do describe '#perform' do
context 'without reply' do context 'without reply' do
it 'if message is private' do it 'if message is private' do
create(:message, message_type: 'outgoing', private: true, inbox: facebook_inbox, account: account) message = create(:message, message_type: 'outgoing', private: true, inbox: facebook_inbox, account: account)
::Facebook::SendOnFacebookService.new(message: message).perform
expect(bot).not_to have_received(:deliver) expect(bot).not_to have_received(:deliver)
end end
it 'if inbox channel is not facebook page' do it 'if inbox channel is not facebook page' do
create(:message, message_type: 'outgoing', inbox: widget_inbox, account: account) message = create(:message, message_type: 'outgoing', inbox: widget_inbox, account: account)
expect { ::Facebook::SendOnFacebookService.new(message: message).perform }.to raise_error 'Invalid channel service was called'
expect(bot).not_to have_received(:deliver) expect(bot).not_to have_received(:deliver)
end end
it 'if message is not outgoing' do it 'if message is not outgoing' do
create(:message, message_type: 'incoming', inbox: facebook_inbox, account: account) message = create(:message, message_type: 'incoming', inbox: facebook_inbox, account: account)
::Facebook::SendOnFacebookService.new(message: message).perform
expect(bot).not_to have_received(:deliver) expect(bot).not_to have_received(:deliver)
end end
it 'if message has an FB ID' do it 'if message has an FB ID' do
create(:message, message_type: 'outgoing', inbox: facebook_inbox, account: account, source_id: SecureRandom.uuid) message = create(:message, message_type: 'outgoing', inbox: facebook_inbox, account: account, source_id: SecureRandom.uuid)
::Facebook::SendOnFacebookService.new(message: message).perform
expect(bot).not_to have_received(:deliver) expect(bot).not_to have_received(:deliver)
end end
end end
context 'with reply' do context 'with reply' do
it 'if message is sent from chatwoot and is outgoing' do it 'if message is sent from chatwoot and is outgoing' do
create(:message, message_type: :incoming, inbox: facebook_inbox, account: account, conversation: conversation) message = create(:message, message_type: 'outgoing', inbox: facebook_inbox, account: account, conversation: conversation)
create(:message, message_type: 'outgoing', inbox: facebook_inbox, account: account, conversation: conversation) ::Facebook::SendOnFacebookService.new(message: message).perform
expect(bot).to have_received(:deliver) expect(bot).to have_received(:deliver)
end end
it 'if message with attachment is sent from chatwoot and is outgoing' do it 'if message with attachment is sent from chatwoot and is outgoing' do
create(:message, message_type: :incoming, inbox: facebook_inbox, account: account, conversation: conversation)
message = build(:message, message_type: 'outgoing', inbox: facebook_inbox, account: account, conversation: conversation) message = build(:message, message_type: 'outgoing', inbox: facebook_inbox, account: account, conversation: conversation)
attachment = message.attachments.new(account_id: message.account_id, file_type: :image) attachment = message.attachments.new(account_id: message.account_id, file_type: :image)
attachment.file.attach(io: File.open(Rails.root.join('spec/assets/avatar.png')), filename: 'avatar.png', content_type: 'image/png') attachment.file.attach(io: File.open(Rails.root.join('spec/assets/avatar.png')), filename: 'avatar.png', content_type: 'image/png')
message.save! message.save!
::Facebook::SendOnFacebookService.new(message: message).perform
expect(bot).to have_received(:deliver) expect(bot).to have_received(:deliver)
end end
end end

View file

@ -25,22 +25,26 @@ describe Twilio::SendOnTwilioService do
describe '#perform' do describe '#perform' do
context 'without reply' do context 'without reply' do
it 'if message is private' do it 'if message is private' do
create(:message, message_type: 'outgoing', private: true, inbox: twilio_inbox, account: account) message = create(:message, message_type: 'outgoing', private: true, inbox: twilio_inbox, account: account)
::Twilio::SendOnTwilioService.new(message: message).perform
expect(twilio_client).not_to have_received(:messages) expect(twilio_client).not_to have_received(:messages)
end end
it 'if inbox channel is not facebook page' do it 'if inbox channel is not twilio' do
create(:message, message_type: 'outgoing', inbox: widget_inbox, account: account) message = create(:message, message_type: 'outgoing', inbox: widget_inbox, account: account)
expect { ::Twilio::SendOnTwilioService.new(message: message).perform }.to raise_error 'Invalid channel service was called'
expect(twilio_client).not_to have_received(:messages) expect(twilio_client).not_to have_received(:messages)
end end
it 'if message is not outgoing' do it 'if message is not outgoing' do
create(:message, message_type: 'incoming', inbox: twilio_inbox, account: account) message = create(:message, message_type: 'incoming', inbox: twilio_inbox, account: account)
::Twilio::SendOnTwilioService.new(message: message).perform
expect(twilio_client).not_to have_received(:messages) expect(twilio_client).not_to have_received(:messages)
end end
it 'if message has an source id' do it 'if message has an source id' do
create(:message, message_type: 'outgoing', inbox: twilio_inbox, account: account, source_id: SecureRandom.uuid) message = create(:message, message_type: 'outgoing', inbox: twilio_inbox, account: account, source_id: SecureRandom.uuid)
::Twilio::SendOnTwilioService.new(message: message).perform
expect(twilio_client).not_to have_received(:messages) expect(twilio_client).not_to have_received(:messages)
end end
end end
@ -53,6 +57,7 @@ describe Twilio::SendOnTwilioService do
outgoing_message = create( outgoing_message = create(
:message, message_type: 'outgoing', inbox: twilio_inbox, account: account, conversation: conversation :message, message_type: 'outgoing', inbox: twilio_inbox, account: account, conversation: conversation
) )
::Twilio::SendOnTwilioService.new(message: outgoing_message).perform
expect(outgoing_message.reload.source_id).to eq('1234') expect(outgoing_message.reload.source_id).to eq('1234')
end end
@ -69,6 +74,8 @@ describe Twilio::SendOnTwilioService do
attachment = message.attachments.new(account_id: message.account_id, file_type: :image) attachment = message.attachments.new(account_id: message.account_id, file_type: :image)
attachment.file.attach(io: File.open(Rails.root.join('spec/assets/avatar.png')), filename: 'avatar.png', content_type: 'image/png') attachment.file.attach(io: File.open(Rails.root.join('spec/assets/avatar.png')), filename: 'avatar.png', content_type: 'image/png')
message.save! message.save!
::Twilio::SendOnTwilioService.new(message: message).perform
end end
end end
end end

View file

@ -41,22 +41,26 @@ describe Twitter::SendOnTwitterService do
describe '#perform' do describe '#perform' do
context 'without reply' do context 'without reply' do
it 'if inbox channel is not twitter profile' do it 'if inbox channel is not twitter profile' do
create(:message, message_type: 'outgoing', inbox: widget_inbox, account: account) message = create(:message, message_type: 'outgoing', inbox: widget_inbox, account: account)
expect { ::Twitter::SendOnTwitterService.new(message: message).perform }.to raise_error 'Invalid channel service was called'
expect(twitter_client).not_to have_received(:send_direct_message) expect(twitter_client).not_to have_received(:send_direct_message)
end end
it 'if message is private' do it 'if message is private' do
create(:message, message_type: 'outgoing', private: true, inbox: twitter_inbox, account: account) message = create(:message, message_type: 'outgoing', private: true, inbox: twitter_inbox, account: account)
::Twitter::SendOnTwitterService.new(message: message).perform
expect(twitter_client).not_to have_received(:send_direct_message) expect(twitter_client).not_to have_received(:send_direct_message)
end end
it 'if message has source_id' do it 'if message has source_id' do
create(:message, message_type: 'outgoing', source_id: '123', inbox: widget_inbox, account: account) message = create(:message, message_type: 'outgoing', source_id: '123', inbox: twitter_inbox, account: account)
::Twitter::SendOnTwitterService.new(message: message).perform
expect(twitter_client).not_to have_received(:send_direct_message) expect(twitter_client).not_to have_received(:send_direct_message)
end end
it 'if message is not outgoing' do it 'if message is not outgoing' do
create(:message, message_type: 'incoming', inbox: twitter_inbox, account: account) message = create(:message, message_type: 'incoming', inbox: twitter_inbox, account: account)
::Twitter::SendOnTwitterService.new(message: message).perform
expect(twitter_client).not_to have_received(:send_direct_message) expect(twitter_client).not_to have_received(:send_direct_message)
end end
end end
@ -64,15 +68,17 @@ describe Twitter::SendOnTwitterService do
context 'with reply' do context 'with reply' do
it 'if conversation is a direct message' do it 'if conversation is a direct message' do
create(:message, message_type: :incoming, inbox: twitter_inbox, account: account, conversation: dm_conversation) create(:message, message_type: :incoming, inbox: twitter_inbox, account: account, conversation: dm_conversation)
create(:message, message_type: :outgoing, inbox: twitter_inbox, account: account, conversation: dm_conversation) message = create(:message, message_type: :outgoing, inbox: twitter_inbox, account: account, conversation: dm_conversation)
::Twitter::SendOnTwitterService.new(message: message).perform
expect(twitter_client).to have_received(:send_direct_message) expect(twitter_client).to have_received(:send_direct_message)
end end
it 'if conversation is a tweet' do it 'if conversation is a tweet' do
create(:message, message_type: :incoming, inbox: twitter_inbox, account: account, conversation: tweet_conversation) create(:message, message_type: :incoming, inbox: twitter_inbox, account: account, conversation: tweet_conversation)
tweet = create(:message, message_type: :outgoing, inbox: twitter_inbox, account: account, conversation: tweet_conversation) message = create(:message, message_type: :outgoing, inbox: twitter_inbox, account: account, conversation: tweet_conversation)
::Twitter::SendOnTwitterService.new(message: message).perform
expect(twitter_client).to have_received(:send_tweet_reply) expect(twitter_client).to have_received(:send_tweet_reply)
expect(tweet.reload.source_id).to eq '12345' expect(message.reload.source_id).to eq '12345'
end end
end end
end end