diff --git a/app/javascript/dashboard/mixins/macrosMixin.js b/app/javascript/dashboard/mixins/macrosMixin.js index 3d02c4b01..fc595d5fe 100644 --- a/app/javascript/dashboard/mixins/macrosMixin.js +++ b/app/javascript/dashboard/mixins/macrosMixin.js @@ -7,7 +7,7 @@ export default { case 'send_email_to_team': return this.teams; case 'assign_agent': - return this.agents; + return [{ id: 'self', name: 'Self' }, ...this.agents]; case 'add_label': case 'remove_label': return this.labels.map(i => { diff --git a/app/javascript/dashboard/mixins/specs/macros.spec.js b/app/javascript/dashboard/mixins/specs/macros.spec.js index c9dfd0475..a8b3ee2b1 100644 --- a/app/javascript/dashboard/mixins/specs/macros.spec.js +++ b/app/javascript/dashboard/mixins/specs/macros.spec.js @@ -37,7 +37,10 @@ describe('webhookMixin', () => { expect(wrapper.vm.getDropdownValues('assign_team')).toEqual(teams); expect(wrapper.vm.getDropdownValues('send_email_to_team')).toEqual(teams); expect(wrapper.vm.getDropdownValues('add_label')).toEqual(resolvedLabels); - expect(wrapper.vm.getDropdownValues('assign_agent')).toEqual(agents); + expect(wrapper.vm.getDropdownValues('assign_agent')).toEqual([ + { id: 'self', name: 'Self' }, + ...agents, + ]); expect(wrapper.vm.getDropdownValues('change_priority')).toEqual( PRIORITY_CONDITION_VALUES ); diff --git a/app/services/macros/execution_service.rb b/app/services/macros/execution_service.rb index 4610500ab..81da53fca 100644 --- a/app/services/macros/execution_service.rb +++ b/app/services/macros/execution_service.rb @@ -22,6 +22,11 @@ class Macros::ExecutionService < ActionService private + def assign_agent(agent_ids) + agent_ids = agent_ids.map { |id| id == 'self' ? @user.id : id } + super(agent_ids) + end + def add_private_note(message) return if conversation_a_tweet? diff --git a/spec/services/action_service_spec.rb b/spec/services/action_service_spec.rb index c45c67fc1..edae23974 100644 --- a/spec/services/action_service_spec.rb +++ b/spec/services/action_service_spec.rb @@ -40,6 +40,4 @@ describe ActionService do expect(conversation.reload.assignee).to be_nil end end - - # TODO: Expand this test suite end diff --git a/spec/services/macros/execution_service_spec.rb b/spec/services/macros/execution_service_spec.rb new file mode 100644 index 000000000..446b47fbc --- /dev/null +++ b/spec/services/macros/execution_service_spec.rb @@ -0,0 +1,150 @@ +require 'rails_helper' + +RSpec.describe Macros::ExecutionService, type: :service do + let(:account) { create(:account) } + let(:conversation) { create(:conversation, account: account) } + let(:user) { create(:user, account: account) } + let(:macro) { create(:macro, account: account) } + let(:service) { described_class.new(macro, conversation, user) } + + before do + create(:inbox_member, user: user, inbox: conversation.inbox) + end + + describe '#perform' do + context 'when actions are present' do + before do + allow(macro).to receive(:actions).and_return([ + { action_name: 'assign_agent', action_params: ['self'] }, + { action_name: 'add_private_note', action_params: ['Test note'] }, + { action_name: 'send_message', action_params: ['Test message'] }, + { action_name: 'send_attachment', action_params: [1, 2] } + ]) + end + + it 'executes the actions' do + expect(service).to receive(:assign_agent).with(['self']).and_call_original + expect(service).to receive(:add_private_note).with(['Test note']).and_call_original + expect(service).to receive(:send_message).with(['Test message']).and_call_original + expect(service).to receive(:send_attachment).with([1, 2]).and_call_original + + service.perform + end + + context 'when an action raises an error' do + let(:exception_tracker) { instance_spy(ChatwootExceptionTracker) } + + before do + allow(ChatwootExceptionTracker).to receive(:new).and_return(exception_tracker) + end + + it 'captures the exception' do + allow(service).to receive(:assign_agent).and_raise(StandardError.new('Random error')) + expect(exception_tracker).to receive(:capture_exception) + + service.perform + end + end + end + end + + describe '#assign_agent' do + context 'when agent_ids contains self' do + it 'updates the conversation assignee to the current user' do + service.send(:assign_agent, ['self']) + expect(conversation.reload.assignee).to eq(user) + end + end + + context 'when agent_ids does not contain self' do + let(:other_user) { create(:user, account: account) } + + before do + create(:inbox_member, user: other_user, inbox: conversation.inbox) + end + + it 'calls the super method' do + service.send(:assign_agent, [other_user.id]) + expect(conversation.reload.assignee).to eq(other_user) + end + end + end + + describe '#add_private_note' do + context 'when conversation is not a tweet' do + it 'creates a new private message' do + expect do + service.send(:add_private_note, ['Test private note']) + end.to change(Message, :count).by(1) + + message = Message.last + expect(message.content).to eq('Test private note') + expect(message.private).to be(true) + end + end + + context 'when conversation is a tweet' do + before { allow(service).to receive(:conversation_a_tweet?).and_return(true) } + + it 'does not create a new message' do + expect do + service.send(:add_private_note, ['Test private note']) + end.not_to change(Message, :count) + end + end + end + + describe '#send_message' do + context 'when conversation is not a tweet' do + it 'creates a new public message' do + expect do + service.send(:send_message, ['Test message']) + end.to change(Message, :count).by(1) + + message = Message.last + expect(message.content).to eq('Test message') + expect(message.private).to be(false) + end + end + + context 'when conversation is a tweet' do + before { allow(service).to receive(:conversation_a_tweet?).and_return(true) } + + it 'does not create a new message' do + expect do + service.send(:send_message, ['Test message']) + end.not_to change(Message, :count) + end + end + end + + describe '#send_attachment' do + before do + macro.files.attach(io: Rails.root.join('spec/assets/avatar.png').open, filename: 'avatar.png', content_type: 'image/png') + macro.save! + end + + context 'when conversation is not a tweet and macro has files attached' do + before { allow(service).to receive(:conversation_a_tweet?).and_return(false) } + + it 'creates a new message with attachments' do + expect do + service.send(:send_attachment, [macro.files.first.blob_id]) + end.to change(Message, :count).by(1) + + message = Message.last + expect(message.attachments).to be_present + end + end + + context 'when conversation is a tweet or macro has no files attached' do + before { allow(service).to receive(:conversation_a_tweet?).and_return(true) } + + it 'does not create a new message' do + expect do + service.send(:send_attachment, [macro.files.first.blob_id]) + end.not_to change(Message, :count) + end + end + end +end