* feat: Adds model for scheduling messages * feat: Implement scheduled message handling and processing jobs * feat: Add ScheduledMessagesController and associated specs for managing scheduled messages * refactor: Simplify scheduled message job specs and improve metadata handling * feat: Add ScheduledMessagePolicy for managing access to scheduled messages * feat: Add routes for managing scheduled messages * feat: Add scheduled message event handling and broadcasting * feat: Add JSON views for scheduled messages creation, destruction, updating, and indexing * feat: Update scheduled message status and dispatch update event after message creation * feat: Ensure scheduled message updates trigger dispatch event * feat: Add mutation types for managing scheduled messages * feat: Add additionalAttributes prop to Message component and provider * feat: Implement scheduled message handling in ActionCable and Vuex store * feat: Add unit tests for scheduled messages actions and mutations * feat: implement scheduled messages functionality - Added support for scheduling messages in the conversation dashboard. - Introduced new components: ScheduledMessageModal and ScheduledMessages for managing scheduled messages. - Enhanced ReplyBottomPanel to include scheduling options. - Updated Base.vue to handle scheduled message styling. - Integrated Vuex store module for managing scheduled messages state. - Added necessary translations for scheduled messages in English and Portuguese. * feat: add pagination to scheduled messages index and update tests accordingly * chore: update scheduled messages specs for future time validation and response status * chore: enhance scheduled messages API with pagination and add skeleton loader component * feat: add create_scheduled_message action to automation rule attributes * feat: implement create_scheduled_message action and enhance attachment handling * feat: add scheduled message functionality with UI components and localization * test: enhance scheduledMessages mutations tests with meta handling and structure * chore: update label to display file name upon successful upload in AutomationFileInput component * feat: add initialAttachment prop to ScheduledMessageModal and update ReplyBox to pass attachment * chore: prepend_mod_with to ScheduledMessagesController for better module handling * fix: attachment visibility in ScheduledMessageItem component * chore: enhance ScheduledMessage model with validations and reduce controller load * refactor: simplify ScheduledMessagesAPI methods by removing unnecessary instance variable * chore: update event emission for scheduled message creation in ReplyBox and ScheduledMessageModal * refactor: update status configuration to use label keys * chore: update date formatting in ScheduledMessageItem component * refactor: collapse logic to checkOverflow and update related functionality * chore: add author indication for current user in scheduled messages * chore: enhance scheduled message metadata with author information and localization * fix: send message shortcut * chore: handle errors in scheduled message submission * chore: update scheduled message modal to use combined date and time input * chore: refactor scheduled messages handling to remove pagination and update related tests * fix: ensure scheduled messages update status and dispatch on failure * fix: update scheduled message due date logic and simplify sending checks * refactor: rename build_message method for send_message * fix: update scheduled message creation time and improve test reliability * chore: ignore unnecessary check * chore: add scheduled message metadata handling in message builder, add scheduled message factorie and update specs * refactor: use scheduled message factorie creation in specs * chore: streamline error handling in scheduled message job and remove dispatch logic * fix: change scheduled_messages association to destroy dependent records * refactor: remove unused attributes from scheduled message payload builder * chore: update scheduled message retrieval to use conversation association * chore: correct cron format for scheduled messages job * chore: remove migration for author_type in scheduled_messages * feat: enhance scheduled messages management with delete confirmation and error handling * chore: set cron poll interval to 10 seconds for improved scheduling precision * feat: include additional_attributes in message JSON response * feat: enhance scheduled message validation and localization support * chore: update scheduled message display * Merge branch 'main' into Cayo-Oliveira/CU-86aenh268/Mensagens-agendadas * feat: add scheduled message indicators and validation for message length * fix: remove unnecessary condition from line-clamp class binding * feat: update scheduled messages localization and enhance content validation * feat: update scheduled messages order, enhance scheduledAt computation, and add message association * fix: reorder condition for Facebook channel message length computation * fix: change detection for attachments in scheduled messages * fix: remove unnecessary colon from close-on-backdrop-click prop in ScheduledMessageModal * chore: add error handling for scheduled message deletion and update localization for delete failure * fix: enforce minimum delay of 1 minute for scheduled messages and update validation * fix: remove unused private property and improve locale formatting for scheduled messages * fix: adjust positioning of DropdownBody in ReplyBottomPanel and clean up schema foreign keys * docs: add scheduled messages management APIs and payload definitions --------- Co-authored-by: gabrieljablonski <contact@gabrieljablonski.com>
200 lines
8.4 KiB
Ruby
200 lines
8.4 KiB
Ruby
require 'rails_helper'
|
|
|
|
RSpec.describe AutomationRules::ActionService do
|
|
let(:account) { create(:account) }
|
|
let(:agent) { create(:user, account: account) }
|
|
let(:conversation) { create(:conversation, account: account) }
|
|
let!(:rule) do
|
|
create(:automation_rule, account: account,
|
|
actions: [
|
|
{ action_name: 'send_webhook_event', action_params: ['https://example.com'] },
|
|
{ action_name: 'send_message', action_params: { message: 'Hello' } }
|
|
])
|
|
end
|
|
|
|
describe '#perform' do
|
|
context 'when actions are defined in the rule' do
|
|
it 'will call the actions' do
|
|
expect(Messages::MessageBuilder).to receive(:new)
|
|
expect(WebhookJob).to receive(:perform_later)
|
|
described_class.new(rule, account, conversation).perform
|
|
end
|
|
end
|
|
|
|
describe '#perform with send_attachment action' do
|
|
let(:message_builder) { double }
|
|
|
|
before do
|
|
allow(Messages::MessageBuilder).to receive(:new).and_return(message_builder)
|
|
rule.actions.delete_if { |a| a['action_name'] == 'send_message' }
|
|
rule.files.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png')
|
|
rule.save!
|
|
rule.actions << { action_name: 'send_attachment', action_params: [rule.files.first.blob_id] }
|
|
end
|
|
|
|
it 'will send attachment' do
|
|
expect(message_builder).to receive(:perform)
|
|
described_class.new(rule, account, conversation).perform
|
|
end
|
|
|
|
it 'will not send attachment is conversation is a tweet' do
|
|
twitter_inbox = create(:inbox, channel: create(:channel_twitter_profile, account: account))
|
|
conversation = create(:conversation, inbox: twitter_inbox, additional_attributes: { type: 'tweet' })
|
|
expect(message_builder).not_to receive(:perform)
|
|
described_class.new(rule, account, conversation).perform
|
|
end
|
|
end
|
|
|
|
describe '#perform with send_webhook_event action' do
|
|
it 'will send webhook event' do
|
|
expect(rule.actions.pluck('action_name')).to include('send_webhook_event')
|
|
expect(WebhookJob).to receive(:perform_later)
|
|
described_class.new(rule, account, conversation).perform
|
|
end
|
|
end
|
|
|
|
describe '#perform with send_message action' do
|
|
let(:message_builder) { double }
|
|
|
|
before do
|
|
allow(Messages::MessageBuilder).to receive(:new).and_return(message_builder)
|
|
end
|
|
|
|
it 'will send message' do
|
|
expect(rule.actions.pluck('action_name')).to include('send_message')
|
|
expect(message_builder).to receive(:perform)
|
|
described_class.new(rule, account, conversation).perform
|
|
end
|
|
|
|
it 'will not send message if conversation is a tweet' do
|
|
expect(rule.actions.pluck('action_name')).to include('send_message')
|
|
twitter_inbox = create(:inbox, channel: create(:channel_twitter_profile, account: account))
|
|
conversation = create(:conversation, inbox: twitter_inbox, additional_attributes: { type: 'tweet' })
|
|
expect(message_builder).not_to receive(:perform)
|
|
described_class.new(rule, account, conversation).perform
|
|
end
|
|
end
|
|
|
|
describe '#perform with send_email_to_team action' do
|
|
let!(:team) { create(:team, account: account) }
|
|
|
|
before do
|
|
rule.actions << { action_name: 'send_email_to_team', action_params: [{ team_ids: [team.id], message: 'Hello' }] }
|
|
end
|
|
|
|
it 'will send email to team' do
|
|
expect(TeamNotifications::AutomationNotificationMailer).to receive(:conversation_creation).with(conversation, team, 'Hello').and_call_original
|
|
described_class.new(rule, account, conversation).perform
|
|
end
|
|
end
|
|
|
|
describe '#perform with send_email_transcript action' do
|
|
before do
|
|
rule.actions << { action_name: 'send_email_transcript', action_params: ['contact@example.com, agent@example.com,agent1@example.com'] }
|
|
rule.save!
|
|
end
|
|
|
|
it 'will send email to transcript to action params emails' do
|
|
mailer = double
|
|
allow(ConversationReplyMailer).to receive(:with).and_return(mailer)
|
|
allow(mailer).to receive(:conversation_transcript).with(conversation, 'contact@example.com')
|
|
allow(mailer).to receive(:conversation_transcript).with(conversation, 'agent@example.com')
|
|
allow(mailer).to receive(:conversation_transcript).with(conversation, 'agent1@example.com')
|
|
|
|
described_class.new(rule, account, conversation).perform
|
|
expect(mailer).to have_received(:conversation_transcript).exactly(3).times
|
|
end
|
|
|
|
it 'will send email to transcript to contacts' do
|
|
rule.actions = [{ action_name: 'send_email_transcript', action_params: ['{{contact.email}}'] }]
|
|
rule.save!
|
|
|
|
mailer = double
|
|
allow(ConversationReplyMailer).to receive(:with).and_return(mailer)
|
|
allow(mailer).to receive(:conversation_transcript).with(conversation, conversation.contact.email)
|
|
|
|
described_class.new(rule.reload, account, conversation).perform
|
|
expect(mailer).to have_received(:conversation_transcript).exactly(1).times
|
|
end
|
|
end
|
|
|
|
describe '#perform with add_label action' do
|
|
before do
|
|
rule.actions << { action_name: 'add_label', action_params: %w[bug feature] }
|
|
rule.save!
|
|
end
|
|
|
|
it 'will add labels to conversation' do
|
|
described_class.new(rule, account, conversation).perform
|
|
expect(conversation.reload.label_list).to include('bug', 'feature')
|
|
end
|
|
|
|
it 'will not duplicate existing labels' do
|
|
conversation.add_labels(['bug'])
|
|
described_class.new(rule, account, conversation).perform
|
|
expect(conversation.reload.label_list.count('bug')).to eq(1)
|
|
expect(conversation.reload.label_list).to include('feature')
|
|
end
|
|
end
|
|
|
|
describe '#perform with remove_label action' do
|
|
before do
|
|
conversation.add_labels(%w[bug feature support])
|
|
rule.actions << { action_name: 'remove_label', action_params: %w[bug feature] }
|
|
rule.save!
|
|
end
|
|
|
|
it 'will remove specified labels from conversation' do
|
|
described_class.new(rule, account, conversation).perform
|
|
expect(conversation.reload.label_list).not_to include('bug', 'feature')
|
|
expect(conversation.reload.label_list).to include('support')
|
|
end
|
|
|
|
it 'will not fail if labels do not exist on conversation' do
|
|
conversation.update_labels(['support']) # Remove bug and feature first
|
|
expect { described_class.new(rule, account, conversation).perform }.not_to raise_error
|
|
expect(conversation.reload.label_list).to include('support')
|
|
end
|
|
end
|
|
|
|
describe '#perform with add_private_note action' do
|
|
let(:message_builder) { double }
|
|
|
|
before do
|
|
allow(Messages::MessageBuilder).to receive(:new).and_return(message_builder)
|
|
rule.actions.delete_if { |a| a['action_name'] == 'send_message' }
|
|
rule.actions << { action_name: 'add_private_note', action_params: ['Note'] }
|
|
end
|
|
|
|
it 'will add private note' do
|
|
expect(message_builder).to receive(:perform)
|
|
described_class.new(rule, account, conversation).perform
|
|
end
|
|
|
|
it 'will not add note if conversation is a tweet' do
|
|
twitter_inbox = create(:inbox, channel: create(:channel_twitter_profile, account: account))
|
|
conversation = create(:conversation, inbox: twitter_inbox, additional_attributes: { type: 'tweet' })
|
|
expect(message_builder).not_to receive(:perform)
|
|
described_class.new(rule, account, conversation).perform
|
|
end
|
|
end
|
|
|
|
describe '#perform with create_scheduled_message action' do
|
|
it 'creates scheduled message with attachment from rule files' do
|
|
rule.files.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png')
|
|
rule.save!
|
|
rule.actions = [{ action_name: 'create_scheduled_message',
|
|
action_params: [{ content: 'Scheduled', delay_minutes: 5, blob_id: rule.files.first.blob_id }] }]
|
|
|
|
expect { described_class.new(rule, account, conversation).perform }
|
|
.to change { conversation.scheduled_messages.count }.by(1)
|
|
|
|
scheduled_message = conversation.scheduled_messages.last
|
|
expect(scheduled_message.content).to eq('Scheduled')
|
|
expect(scheduled_message.author).to eq(rule)
|
|
expect(scheduled_message.attachment).to be_attached
|
|
end
|
|
end
|
|
end
|
|
end
|