From 49559d6ce1735347f3153510998adc8fd1202561 Mon Sep 17 00:00:00 2001 From: Rogee Date: Mon, 24 Aug 2026 14:00:17 +0800 Subject: [PATCH] HH-606: cover soft-deleted inbox assistants (#162) * test(HH-606): cover soft-deleted inbox assistants * test(HH-606): bind soft-deleted inbox to persisted assistant --------- Co-authored-by: Rogee --- .../api/v1/conversation_handler_crud_test.go | 29 + .../specs/conversations/actions.spec.js | 1894 ++++++++--------- 2 files changed, 966 insertions(+), 957 deletions(-) diff --git a/backend/internal/handler/api/v1/conversation_handler_crud_test.go b/backend/internal/handler/api/v1/conversation_handler_crud_test.go index 21f3efc5..a7121b17 100644 --- a/backend/internal/handler/api/v1/conversation_handler_crud_test.go +++ b/backend/internal/handler/api/v1/conversation_handler_crud_test.go @@ -555,6 +555,35 @@ func (s *ConversationCrudTestSuite) TestInboxAssistant_ReturnsNilWhenBoundAssist assertUnavailable() } +func (s *ConversationCrudTestSuite) TestInboxAssistant_ReturnsNilWhenCaptainInboxSoftDeleted() { + assistant := &model.CaptainAssistant{AccountID: s.testAccount.ID, Name: "Deleted Binding Helper", Config: json.RawMessage(`{}`), Status: model.AssistantStatusActive} + s.Require().NoError(s.db.Create(assistant).Error) + binding := &model.CaptainInbox{AccountID: s.testAccount.ID, AssistantID: assistant.ID, InboxID: s.testInbox.ID} + s.Require().NoError(s.db.Create(binding).Error) + s.Require().NoError(s.db.Delete(binding).Error) + + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", s.convURL(s.testConv.ID)+"/inbox_assistant", nil) + s.router.ServeHTTP(w, req) + + assert.Equal(s.T(), http.StatusOK, w.Code) + assert.JSONEq(s.T(), `{"assistant":null}`, w.Body.String()) +} + +func (s *ConversationCrudTestSuite) TestInboxAssistant_ReturnsNilWhenCaptainAssistantSoftDeleted() { + assistant := &model.CaptainAssistant{AccountID: s.testAccount.ID, Name: "Deleted Assistant", Config: json.RawMessage(`{}`), Status: model.AssistantStatusActive} + s.Require().NoError(s.db.Create(assistant).Error) + s.Require().NoError(s.db.Create(&model.CaptainInbox{AccountID: s.testAccount.ID, AssistantID: assistant.ID, InboxID: s.testInbox.ID}).Error) + s.Require().NoError(s.db.Delete(assistant).Error) + + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", s.convURL(s.testConv.ID)+"/inbox_assistant", nil) + s.router.ServeHTTP(w, req) + + assert.Equal(s.T(), http.StatusOK, w.Code) + assert.JSONEq(s.T(), `{"assistant":null}`, w.Body.String()) +} + func (s *ConversationCrudTestSuite) TestInboxAssistant_UsesDisplayIDRoute() { displayID := uint(77) s.testConv.DisplayID = &displayID diff --git a/frontend/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js b/frontend/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js index aff2319f..6d383e1b 100644 --- a/frontend/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js +++ b/frontend/app/javascript/dashboard/store/modules/specs/conversations/actions.spec.js @@ -1,17 +1,17 @@ import axios from 'axios'; import actions, { - hasMessageFailedWithExternalError, + hasMessageFailedWithExternalError, } from '../../conversations/actions'; import types from '../../../mutation-types'; const dataToSend = { - payload: [ - { - attribute_key: 'status', - filter_operator: 'equal_to', - values: ['open'], - query_operator: null, - }, - ], + payload: [ + { + attribute_key: 'status', + filter_operator: 'equal_to', + values: ['open'], + query_operator: null, + }, + ], }; import { dataReceived } from './testConversationResponse'; @@ -21,1028 +21,1008 @@ global.axios = axios; vi.mock('axios'); describe('#hasMessageFailedWithExternalError', () => { - it('returns false if message is sent', () => { - const pendingMessage = { - status: 'sent', - content_attributes: {}, - }; - expect(hasMessageFailedWithExternalError(pendingMessage)).toBe(false); - }); - it('returns false if status is not failed', () => { - const pendingMessage = { - status: 'progress', - content_attributes: {}, - }; - expect(hasMessageFailedWithExternalError(pendingMessage)).toBe(false); - }); + it('returns false if message is sent', () => { + const pendingMessage = { + status: 'sent', + content_attributes: {}, + }; + expect(hasMessageFailedWithExternalError(pendingMessage)).toBe(false); + }); + it('returns false if status is not failed', () => { + const pendingMessage = { + status: 'progress', + content_attributes: {}, + }; + expect(hasMessageFailedWithExternalError(pendingMessage)).toBe(false); + }); - it('returns false if status is failed but no external error', () => { - const pendingMessage = { - status: 'failed', - content_attributes: {}, - }; - expect(hasMessageFailedWithExternalError(pendingMessage)).toBe(false); - }); + it('returns false if status is failed but no external error', () => { + const pendingMessage = { + status: 'failed', + content_attributes: {}, + }; + expect(hasMessageFailedWithExternalError(pendingMessage)).toBe(false); + }); - it('returns true if status is failed and has external error', () => { - const pendingMessage = { - status: 'failed', - content_attributes: { - external_error: 'error', - }, - }; - expect(hasMessageFailedWithExternalError(pendingMessage)).toBe(true); - }); + it('returns true if status is failed and has external error', () => { + const pendingMessage = { + status: 'failed', + content_attributes: { + external_error: 'error', + }, + }; + expect(hasMessageFailedWithExternalError(pendingMessage)).toBe(true); + }); }); describe('#actions', () => { - describe('#getConversation', () => { - it('sends correct actions if API is success', async () => { - axios.get.mockResolvedValue({ - data: { id: 1, meta: { sender: { id: 1, name: 'Contact 1' } } }, - }); - await actions.getConversation({ commit }, 1); - expect(commit.mock.calls).toEqual([ - [ - types.UPDATE_CONVERSATION, - { id: 1, meta: { sender: { id: 1, name: 'Contact 1' } } }, - ], - ['contacts/SET_CONTACT_ITEM', { id: 1, name: 'Contact 1' }], - ]); - }); - it('sends correct actions if API is error', async () => { - axios.get.mockRejectedValue({ message: 'Incorrect header' }); - await actions.getConversation({ commit }); - expect(commit.mock.calls).toEqual([]); - }); - }); - describe('#muteConversation', () => { - it('sends correct actions if API is success', async () => { - axios.get.mockResolvedValue(null); - await actions.muteConversation({ commit }, 1); - expect(commit.mock.calls).toEqual([[types.MUTE_CONVERSATION]]); - }); - it('sends correct actions if API is error', async () => { - axios.get.mockRejectedValue({ message: 'Incorrect header' }); - await actions.getConversation({ commit }); - expect(commit.mock.calls).toEqual([]); - }); - }); + describe('#getConversation', () => { + it('sends correct actions if API is success', async () => { + axios.get.mockResolvedValue({ + data: { id: 1, meta: { sender: { id: 1, name: 'Contact 1' } } }, + }); + await actions.getConversation({ commit }, 1); + expect(commit.mock.calls).toEqual([ + [ + types.UPDATE_CONVERSATION, + { id: 1, meta: { sender: { id: 1, name: 'Contact 1' } } }, + ], + ['contacts/SET_CONTACT_ITEM', { id: 1, name: 'Contact 1' }], + ]); + }); + it('sends correct actions if API is error', async () => { + axios.get.mockRejectedValue({ message: 'Incorrect header' }); + await actions.getConversation({ commit }); + expect(commit.mock.calls).toEqual([]); + }); + }); + describe('#muteConversation', () => { + it('sends correct actions if API is success', async () => { + axios.get.mockResolvedValue(null); + await actions.muteConversation({ commit }, 1); + expect(commit.mock.calls).toEqual([[types.MUTE_CONVERSATION]]); + }); + it('sends correct actions if API is error', async () => { + axios.get.mockRejectedValue({ message: 'Incorrect header' }); + await actions.getConversation({ commit }); + expect(commit.mock.calls).toEqual([]); + }); + }); - describe('#updateConversation', () => { - it('sends setContact action and update_conversation mutation', () => { - const conversation = { - id: 1, - messages: [], - meta: { sender: { id: 1, name: 'john-doe' } }, - labels: ['support'], - }; - actions.updateConversation( - { commit, rootState: { route: { name: 'home' } }, dispatch }, - conversation - ); - expect(commit.mock.calls).toEqual([ - [types.UPDATE_CONVERSATION, conversation], - ]); - expect(dispatch.mock.calls).toEqual([ - [ - 'conversationLabels/setConversationLabel', - { id: 1, data: ['support'] }, - ], - [ - 'contacts/setContact', - { - id: 1, - name: 'john-doe', - }, - ], - ]); - }); - }); + describe('#updateConversation', () => { + it('sends setContact action and update_conversation mutation', () => { + const conversation = { + id: 1, + messages: [], + meta: { sender: { id: 1, name: 'john-doe' } }, + labels: ['support'], + }; + actions.updateConversation( + { commit, rootState: { route: { name: 'home' } }, dispatch }, + conversation + ); + expect(commit.mock.calls).toEqual([ + [types.UPDATE_CONVERSATION, conversation], + ]); + expect(dispatch.mock.calls).toEqual([ + [ + 'conversationLabels/setConversationLabel', + { id: 1, data: ['support'] }, + ], + [ + 'contacts/setContact', + { + id: 1, + name: 'john-doe', + }, + ], + ]); + }); + }); - describe('#addConversation', () => { - it('doesnot send mutation if conversation is from a different inbox', () => { - const conversation = { - id: 1, - messages: [], - meta: { sender: { id: 1, name: 'john-doe' } }, - inbox_id: 2, - }; - actions.addConversation( - { - commit, - rootState: { route: { name: 'home' } }, - dispatch, - state: { currentInbox: 1, appliedFilters: [] }, - }, - conversation - ); - expect(commit.mock.calls).toEqual([]); - expect(dispatch.mock.calls).toEqual([]); - }); + describe('#addConversation', () => { + it('doesnot send mutation if conversation is from a different inbox', () => { + const conversation = { + id: 1, + messages: [], + meta: { sender: { id: 1, name: 'john-doe' } }, + inbox_id: 2, + }; + actions.addConversation( + { + commit, + rootState: { route: { name: 'home' } }, + dispatch, + state: { currentInbox: 1, appliedFilters: [] }, + }, + conversation + ); + expect(commit.mock.calls).toEqual([]); + expect(dispatch.mock.calls).toEqual([]); + }); - it('doesnot send mutation if conversation filters are applied', () => { - const conversation = { - id: 1, - messages: [], - meta: { sender: { id: 1, name: 'john-doe' } }, - inbox_id: 1, - }; - actions.addConversation( - { - commit, - rootState: { route: { name: 'home' } }, - dispatch, - state: { - currentInbox: 1, - appliedFilters: [{ id: 'random-filter' }], - }, - }, - conversation - ); - expect(commit.mock.calls).toEqual([]); - expect(dispatch.mock.calls).toEqual([]); - }); + it('doesnot send mutation if conversation filters are applied', () => { + const conversation = { + id: 1, + messages: [], + meta: { sender: { id: 1, name: 'john-doe' } }, + inbox_id: 1, + }; + actions.addConversation( + { + commit, + rootState: { route: { name: 'home' } }, + dispatch, + state: { + currentInbox: 1, + appliedFilters: [{ id: 'random-filter' }], + }, + }, + conversation + ); + expect(commit.mock.calls).toEqual([]); + expect(dispatch.mock.calls).toEqual([]); + }); - it('doesnot send mutation if the view is conversation mentions', () => { - const conversation = { - id: 1, - messages: [], - meta: { sender: { id: 1, name: 'john-doe' } }, - inbox_id: 1, - }; - actions.addConversation( - { - commit, - rootState: { route: { name: 'conversation_mentions' } }, - dispatch, - state: { - currentInbox: 1, - appliedFilters: [{ id: 'random-filter' }], - }, - }, - conversation - ); - expect(commit.mock.calls).toEqual([]); - expect(dispatch.mock.calls).toEqual([]); - }); + it('doesnot send mutation if the view is conversation mentions', () => { + const conversation = { + id: 1, + messages: [], + meta: { sender: { id: 1, name: 'john-doe' } }, + inbox_id: 1, + }; + actions.addConversation( + { + commit, + rootState: { route: { name: 'conversation_mentions' } }, + dispatch, + state: { + currentInbox: 1, + appliedFilters: [{ id: 'random-filter' }], + }, + }, + conversation + ); + expect(commit.mock.calls).toEqual([]); + expect(dispatch.mock.calls).toEqual([]); + }); - it('doesnot send mutation if the view is conversation folders', () => { - const conversation = { - id: 1, - messages: [], - meta: { sender: { id: 1, name: 'john-doe' } }, - inbox_id: 1, - }; - actions.addConversation( - { - commit, - rootState: { route: { name: 'folder_conversations' } }, - dispatch, - state: { - currentInbox: 1, - appliedFilters: [{ id: 'random-filter' }], - }, - }, - conversation - ); - expect(commit.mock.calls).toEqual([]); - expect(dispatch.mock.calls).toEqual([]); - }); + it('doesnot send mutation if the view is conversation folders', () => { + const conversation = { + id: 1, + messages: [], + meta: { sender: { id: 1, name: 'john-doe' } }, + inbox_id: 1, + }; + actions.addConversation( + { + commit, + rootState: { route: { name: 'folder_conversations' } }, + dispatch, + state: { + currentInbox: 1, + appliedFilters: [{ id: 'random-filter' }], + }, + }, + conversation + ); + expect(commit.mock.calls).toEqual([]); + expect(dispatch.mock.calls).toEqual([]); + }); - it('sends correct mutations', () => { - const conversation = { - id: 1, - messages: [], - meta: { sender: { id: 1, name: 'john-doe' } }, - inbox_id: 1, - }; - actions.addConversation( - { - commit, - rootState: { route: { name: 'home' } }, - dispatch, - state: { currentInbox: 1, appliedFilters: [] }, - }, - conversation - ); - expect(commit.mock.calls).toEqual([ - [types.ADD_CONVERSATION, conversation], - ]); - expect(dispatch.mock.calls).toEqual([ - [ - 'contacts/setContact', - { - id: 1, - name: 'john-doe', - }, - ], - ]); - }); + it('sends correct mutations', () => { + const conversation = { + id: 1, + messages: [], + meta: { sender: { id: 1, name: 'john-doe' } }, + inbox_id: 1, + }; + actions.addConversation( + { + commit, + rootState: { route: { name: 'home' } }, + dispatch, + state: { currentInbox: 1, appliedFilters: [] }, + }, + conversation + ); + expect(commit.mock.calls).toEqual([ + [types.ADD_CONVERSATION, conversation], + ]); + expect(dispatch.mock.calls).toEqual([ + [ + 'contacts/setContact', + { + id: 1, + name: 'john-doe', + }, + ], + ]); + }); - it('sends correct mutations if inbox filter is not available', () => { - const conversation = { - id: 1, - messages: [], - meta: { sender: { id: 1, name: 'john-doe' } }, - inbox_id: 1, - }; - actions.addConversation( - { - commit, - rootState: { route: { name: 'home' } }, - dispatch, - state: { appliedFilters: [] }, - }, - conversation - ); - expect(commit.mock.calls).toEqual([ - [types.ADD_CONVERSATION, conversation], - ]); - expect(dispatch.mock.calls).toEqual([ - [ - 'contacts/setContact', - { - id: 1, - name: 'john-doe', - }, - ], - ]); - }); - }); + it('sends correct mutations if inbox filter is not available', () => { + const conversation = { + id: 1, + messages: [], + meta: { sender: { id: 1, name: 'john-doe' } }, + inbox_id: 1, + }; + actions.addConversation( + { + commit, + rootState: { route: { name: 'home' } }, + dispatch, + state: { appliedFilters: [] }, + }, + conversation + ); + expect(commit.mock.calls).toEqual([ + [types.ADD_CONVERSATION, conversation], + ]); + expect(dispatch.mock.calls).toEqual([ + [ + 'contacts/setContact', + { + id: 1, + name: 'john-doe', + }, + ], + ]); + }); + }); - describe('#addMessage', () => { - it('sends correct mutations if message is incoming', () => { - const message = { - id: 1, - message_type: 0, - conversation_id: 1, - }; - const state = { - allConversations: [{ id: 1, messages: [] }], - }; - const applied = actions.addMessage({ commit, state }, message); - expect(applied).toBe(true); - expect(commit.mock.calls).toEqual([ - [types.ADD_MESSAGE, message], - [ - types.SET_CONVERSATION_CAN_REPLY, - { conversationId: 1, canReply: true }, - ], - [types.ADD_CONVERSATION_ATTACHMENTS, message], - ]); - }); - it('sends correct mutations if message is not an incoming message', () => { - const message = { - id: 1, - message_type: 1, - conversation_id: 1, - }; - const state = { - allConversations: [{ id: 1, messages: [] }], - }; - actions.addMessage({ commit, state }, message); - expect(commit.mock.calls).toEqual([[types.ADD_MESSAGE, message]]); - }); + describe('#addMessage', () => { + it('sends correct mutations if message is incoming', () => { + const message = { + id: 1, + message_type: 0, + conversation_id: 1, + }; + const state = { + allConversations: [{ id: 1, messages: [] }], + }; + const applied = actions.addMessage({ commit, state }, message); + expect(applied).toBe(true); + expect(commit.mock.calls).toEqual([ + [types.ADD_MESSAGE, message], + [ + types.SET_CONVERSATION_CAN_REPLY, + { conversationId: 1, canReply: true }, + ], + [types.ADD_CONVERSATION_ATTACHMENTS, message], + ]); + }); + it('sends correct mutations if message is not an incoming message', () => { + const message = { + id: 1, + message_type: 1, + conversation_id: 1, + }; + const state = { + allConversations: [{ id: 1, messages: [] }], + }; + actions.addMessage({ commit, state }, message); + expect(commit.mock.calls).toEqual([[types.ADD_MESSAGE, message]]); + }); - it('returns false if the conversation is not loaded', () => { - const message = { - id: 1, - message_type: 1, - conversation_id: 1, - }; + it('returns false if the conversation is not loaded', () => { + const message = { + id: 1, + message_type: 1, + conversation_id: 1, + }; - const applied = actions.addMessage( - { commit, state: { allConversations: [] } }, - message - ); + const applied = actions.addMessage( + { commit, state: { allConversations: [] } }, + message + ); - expect(applied).toBe(false); - expect(commit).not.toHaveBeenCalled(); - }); + expect(applied).toBe(false); + expect(commit).not.toHaveBeenCalled(); + }); - it('ignores a replayed message with the same id and version', () => { - const message = { - id: 1, - message_type: 1, - conversation_id: 1, - updated_at: '2026-08-24T02:00:00Z', - }; - const state = { - allConversations: [{ id: 1, messages: [message] }], - }; + it('ignores a replayed message with the same id and version', () => { + const message = { + id: 1, + message_type: 1, + conversation_id: 1, + updated_at: '2026-08-24T02:00:00Z', + }; + const state = { + allConversations: [{ id: 1, messages: [message] }], + }; - const isNew = actions.addMessage({ commit, state }, message); + const isNew = actions.addMessage({ commit, state }, message); - expect(isNew).toBe(false); - expect(commit).not.toHaveBeenCalled(); - }); - }); + expect(isNew).toBe(false); + expect(commit).not.toHaveBeenCalled(); + }); + }); - describe('#updateMessage', () => { - it('ignores an older realtime update', () => { - const message = { - id: 1, - conversation_id: 1, - updated_at: '2026-08-24T01:00:00Z', - }; - const state = { - allConversations: [ - { - id: 1, - messages: [ - { - id: 1, - updated_at: '2026-08-24T02:00:00Z', - }, - ], - }, - ], - }; + describe('#updateMessage', () => { + it('ignores an older realtime update', () => { + const message = { + id: 1, + conversation_id: 1, + updated_at: '2026-08-24T01:00:00Z', + }; + const state = { + allConversations: [ + { + id: 1, + messages: [ + { + id: 1, + updated_at: '2026-08-24T02:00:00Z', + }, + ], + }, + ], + }; - const applied = actions.updateMessage({ commit, state }, message); + const applied = actions.updateMessage({ commit, state }, message); - expect(applied).toBe(false); - expect(commit).not.toHaveBeenCalled(); - }); - }); + expect(applied).toBe(false); + expect(commit).not.toHaveBeenCalled(); + }); + }); - describe('#sendMessageWithData', () => { - it('keeps the status returned by the server', async () => { - const localCommit = vi.fn(); - const pendingMessage = { - id: 'echo-1', - conversation_id: 7, - message: 'hello', - status: 'progress', - content_attributes: {}, - }; - const responseMessage = { - id: 42, - conversation_id: 7, - content: 'hello', - status: 'progress', - }; - axios.mockResolvedValue({ data: responseMessage }); + describe('#sendMessageWithData', () => { + it('keeps the status returned by the server', async () => { + const localCommit = vi.fn(); + const pendingMessage = { + id: 'echo-1', + conversation_id: 7, + message: 'hello', + status: 'progress', + content_attributes: {}, + }; + const responseMessage = { + id: 42, + conversation_id: 7, + content: 'hello', + status: 'progress', + }; + axios.mockResolvedValue({ data: responseMessage }); - await actions.sendMessageWithData( - { commit: localCommit }, - pendingMessage - ); + await actions.sendMessageWithData( + { commit: localCommit }, + pendingMessage + ); - expect(localCommit).toHaveBeenNthCalledWith( - 2, - types.ADD_MESSAGE, - responseMessage - ); - expect(localCommit).toHaveBeenNthCalledWith( - 3, - types.ADD_CONVERSATION_ATTACHMENTS, - responseMessage - ); - }); - }); + expect(localCommit).toHaveBeenNthCalledWith( + 2, + types.ADD_MESSAGE, + responseMessage + ); + expect(localCommit).toHaveBeenNthCalledWith( + 3, + types.ADD_CONVERSATION_ATTACHMENTS, + responseMessage + ); + }); + }); - describe('#markMessagesRead', () => { - it('sends correct mutations if api is successful', async () => { - const lastSeen = new Date().getTime() / 1000; - axios.post.mockResolvedValue({ - data: { id: 1, agent_last_seen_at: lastSeen }, - }); - await actions.markMessagesRead({ commit }, { id: 1 }); - expect(commit).toHaveBeenCalledTimes(1); - expect(commit.mock.calls).toEqual([ - [types.UPDATE_MESSAGE_UNREAD_COUNT, { id: 1, lastSeen }], - ]); - }); - it('sends correct mutations if api is unsuccessful', async () => { - axios.post.mockRejectedValue({ message: 'Incorrect header' }); - await actions.markMessagesRead({ commit }, { id: 1 }); - expect(commit.mock.calls).toEqual([]); - }); - }); + describe('#markMessagesRead', () => { + it('sends correct mutations if api is successful', async () => { + const lastSeen = new Date().getTime() / 1000; + axios.post.mockResolvedValue({ + data: { id: 1, agent_last_seen_at: lastSeen }, + }); + await actions.markMessagesRead({ commit }, { id: 1 }); + expect(commit).toHaveBeenCalledTimes(1); + expect(commit.mock.calls).toEqual([ + [types.UPDATE_MESSAGE_UNREAD_COUNT, { id: 1, lastSeen }], + ]); + }); + it('sends correct mutations if api is unsuccessful', async () => { + axios.post.mockRejectedValue({ message: 'Incorrect header' }); + await actions.markMessagesRead({ commit }, { id: 1 }); + expect(commit.mock.calls).toEqual([]); + }); + }); - describe('#markMessagesUnread', () => { - it('sends correct mutations if API is successful', async () => { - const lastSeen = new Date().getTime() / 1000; - axios.post.mockResolvedValue({ - data: { id: 1, agent_last_seen_at: lastSeen, unread_count: 1 }, - }); - await actions.markMessagesUnread({ commit }, { id: 1 }); - expect(commit).toHaveBeenCalledTimes(1); - expect(commit.mock.calls).toEqual([ - [ - types.UPDATE_MESSAGE_UNREAD_COUNT, - { id: 1, lastSeen, unreadCount: 1 }, - ], - ]); - }); - it('sends correct mutations if API is unsuccessful', async () => { - axios.post.mockRejectedValue({ message: 'Incorrect header' }); - await expect( - actions.markMessagesUnread({ commit }, { id: 1 }) - ).rejects.toThrow(Error); - }); - }); + describe('#markMessagesUnread', () => { + it('sends correct mutations if API is successful', async () => { + const lastSeen = new Date().getTime() / 1000; + axios.post.mockResolvedValue({ + data: { id: 1, agent_last_seen_at: lastSeen, unread_count: 1 }, + }); + await actions.markMessagesUnread({ commit }, { id: 1 }); + expect(commit).toHaveBeenCalledTimes(1); + expect(commit.mock.calls).toEqual([ + [ + types.UPDATE_MESSAGE_UNREAD_COUNT, + { id: 1, lastSeen, unreadCount: 1 }, + ], + ]); + }); + it('sends correct mutations if API is unsuccessful', async () => { + axios.post.mockRejectedValue({ message: 'Incorrect header' }); + await expect( + actions.markMessagesUnread({ commit }, { id: 1 }) + ).rejects.toThrow(Error); + }); + }); - describe('#sendEmailTranscript', () => { - it('sends correct mutations if api is successful', async () => { - axios.post.mockResolvedValue({}); - await actions.sendEmailTranscript( - { commit }, - { conversationId: 1, email: 'testemail@example.com' } - ); - expect(commit).toHaveBeenCalledTimes(0); - expect(commit.mock.calls).toEqual([]); - }); - }); + describe('#sendEmailTranscript', () => { + it('sends correct mutations if api is successful', async () => { + axios.post.mockResolvedValue({}); + await actions.sendEmailTranscript( + { commit }, + { conversationId: 1, email: 'testemail@example.com' } + ); + expect(commit).toHaveBeenCalledTimes(0); + expect(commit.mock.calls).toEqual([]); + }); + }); - describe('#assignAgent', () => { - it('sends correct mutations if assignment is successful', async () => { - axios.post.mockResolvedValue({ - data: { id: 1, name: 'User' }, - }); - await actions.assignAgent( - { dispatch }, - { conversationId: 1, agentId: 1 } - ); - expect(dispatch).toHaveBeenCalledWith('setCurrentChatAssignee', { - conversationId: 1, - assignee: { id: 1, name: 'User' }, - }); - }); - }); + describe('#assignAgent', () => { + it('sends correct mutations if assignment is successful', async () => { + axios.post.mockResolvedValue({ + data: { id: 1, name: 'User' }, + }); + await actions.assignAgent( + { dispatch }, + { conversationId: 1, agentId: 1 } + ); + expect(dispatch).toHaveBeenCalledWith('setCurrentChatAssignee', { + conversationId: 1, + assignee: { id: 1, name: 'User' }, + }); + }); + }); - describe('#setCurrentChatAssignee', () => { - it('sends correct mutations if assignment is successful', async () => { - const payload = { - conversationId: 1, - assignee: { id: 1, name: 'User' }, - }; - await actions.setCurrentChatAssignee({ commit }, payload); - expect(commit).toHaveBeenCalledTimes(1); - expect(commit.mock.calls).toEqual([['ASSIGN_AGENT', payload]]); - }); - }); + describe('#setCurrentChatAssignee', () => { + it('sends correct mutations if assignment is successful', async () => { + const payload = { + conversationId: 1, + assignee: { id: 1, name: 'User' }, + }; + await actions.setCurrentChatAssignee({ commit }, payload); + expect(commit).toHaveBeenCalledTimes(1); + expect(commit.mock.calls).toEqual([['ASSIGN_AGENT', payload]]); + }); + }); - describe('#toggleStatus', () => { - it('sends correct mutations if toggle status is successful', async () => { - axios.post.mockResolvedValue({ - data: { - payload: { - conversation_id: 1, - current_status: 'snoozed', - snoozed_until: null, - }, - }, - }); - await actions.toggleStatus( - { commit }, - { conversationId: 1, status: 'snoozed' } - ); - expect(commit).toHaveBeenCalledTimes(1); - expect(commit.mock.calls).toEqual([ - [ - 'CHANGE_CONVERSATION_STATUS', - { - conversationId: 1, - status: 'snoozed', - snoozedUntil: null, - }, - ], - ]); - }); - }); + describe('#toggleStatus', () => { + it('sends correct mutations if toggle status is successful', async () => { + axios.post.mockResolvedValue({ + data: { + payload: { + conversation_id: 1, + current_status: 'snoozed', + snoozed_until: null, + }, + }, + }); + await actions.toggleStatus( + { commit }, + { conversationId: 1, status: 'snoozed' } + ); + expect(commit).toHaveBeenCalledTimes(1); + expect(commit.mock.calls).toEqual([ + [ + 'CHANGE_CONVERSATION_STATUS', + { + conversationId: 1, + status: 'snoozed', + snoozedUntil: null, + }, + ], + ]); + }); + }); - describe('#assignTeam', () => { - it('sends correct mutations if assignment is successful', async () => { - axios.post.mockResolvedValue({ - data: { id: 1, name: 'Team' }, - }); - await actions.assignTeam( - { commit }, - { conversationId: 1, teamId: 1 } - ); - expect(commit).toHaveBeenCalledTimes(0); - expect(commit.mock.calls).toEqual([]); - }); - }); + describe('#assignTeam', () => { + it('sends correct mutations if assignment is successful', async () => { + axios.post.mockResolvedValue({ + data: { id: 1, name: 'Team' }, + }); + await actions.assignTeam({ commit }, { conversationId: 1, teamId: 1 }); + expect(commit).toHaveBeenCalledTimes(0); + expect(commit.mock.calls).toEqual([]); + }); + }); - describe('#setCurrentChatTeam', () => { - it('sends correct mutations if assignment is successful', async () => { - axios.post.mockResolvedValue({ - data: { id: 1, name: 'Team' }, - }); - await actions.setCurrentChatTeam( - { commit }, - { team: { id: 1, name: 'Team' }, conversationId: 1 } - ); - expect(commit).toHaveBeenCalledTimes(1); - expect(commit.mock.calls).toEqual([ - [ - 'ASSIGN_TEAM', - { team: { id: 1, name: 'Team' }, conversationId: 1 }, - ], - ]); - }); - }); + describe('#setCurrentChatTeam', () => { + it('sends correct mutations if assignment is successful', async () => { + axios.post.mockResolvedValue({ + data: { id: 1, name: 'Team' }, + }); + await actions.setCurrentChatTeam( + { commit }, + { team: { id: 1, name: 'Team' }, conversationId: 1 } + ); + expect(commit).toHaveBeenCalledTimes(1); + expect(commit.mock.calls).toEqual([ + ['ASSIGN_TEAM', { team: { id: 1, name: 'Team' }, conversationId: 1 }], + ]); + }); + }); - describe('#fetchFilteredConversations', () => { - it('fetches filtered conversations and clears the loading state', async () => { - const localCommit = vi.fn(); - const localDispatch = vi.fn(); - axios.post.mockResolvedValue({ - data: dataReceived, - }); - await actions.fetchFilteredConversations( - { commit: localCommit, dispatch: localDispatch }, - dataToSend - ); - expect(localCommit.mock.calls).toEqual([ - [types.CLEAR_LIST_ERROR_STATUS], - [types.SET_LIST_LOADING_STATUS], - [types.SET_ALL_CONVERSATION, dataReceived.payload], - [ - `contacts/${types.SET_CONTACTS}`, - dataReceived.payload.map( - conversation => conversation.meta.sender - ), - ], - [types.CLEAR_LIST_ERROR_STATUS], - [types.CLEAR_LIST_LOADING_STATUS], - ]); - expect(localDispatch).toHaveBeenCalledWith( - 'conversationStats/set', - dataReceived.meta - ); - }); + describe('#fetchFilteredConversations', () => { + it('fetches filtered conversations and clears the loading state', async () => { + const localCommit = vi.fn(); + const localDispatch = vi.fn(); + axios.post.mockResolvedValue({ + data: dataReceived, + }); + await actions.fetchFilteredConversations( + { commit: localCommit, dispatch: localDispatch }, + dataToSend + ); + expect(localCommit.mock.calls).toEqual([ + [types.CLEAR_LIST_ERROR_STATUS], + [types.SET_LIST_LOADING_STATUS], + [types.SET_ALL_CONVERSATION, dataReceived.payload], + [ + `contacts/${types.SET_CONTACTS}`, + dataReceived.payload.map(conversation => conversation.meta.sender), + ], + [types.CLEAR_LIST_ERROR_STATUS], + [types.CLEAR_LIST_LOADING_STATUS], + ]); + expect(localDispatch).toHaveBeenCalledWith( + 'conversationStats/set', + dataReceived.meta + ); + }); - it('clears the loading state when the API request fails', async () => { - const localCommit = vi.fn(); - axios.post.mockRejectedValue(new Error('Request failed')); + it('clears the loading state when the API request fails', async () => { + const localCommit = vi.fn(); + axios.post.mockRejectedValue(new Error('Request failed')); - await actions.fetchFilteredConversations( - { commit: localCommit, dispatch: vi.fn() }, - dataToSend - ); + await actions.fetchFilteredConversations( + { commit: localCommit, dispatch: vi.fn() }, + dataToSend + ); - expect(localCommit.mock.calls).toEqual([ - [types.CLEAR_LIST_ERROR_STATUS], - [types.SET_LIST_LOADING_STATUS], - [types.SET_LIST_ERROR_STATUS], - [types.CLEAR_LIST_LOADING_STATUS], - ]); - }); - }); + expect(localCommit.mock.calls).toEqual([ + [types.CLEAR_LIST_ERROR_STATUS], + [types.SET_LIST_LOADING_STATUS], + [types.SET_LIST_ERROR_STATUS], + [types.CLEAR_LIST_LOADING_STATUS], + ]); + }); + }); - describe('#fetchAllConversations', () => { - it('fetches conversations and clears the loading state', async () => { - const localCommit = vi.fn(); - const localDispatch = vi.fn(); - const filters = { - inboxId: 4, - status: 'open', - assigneeType: 'all', - page: 1, - }; - axios.get.mockResolvedValue({ data: { data: dataReceived } }); + describe('#fetchAllConversations', () => { + it('fetches conversations and clears the loading state', async () => { + const localCommit = vi.fn(); + const localDispatch = vi.fn(); + const filters = { + inboxId: 4, + status: 'open', + assigneeType: 'all', + page: 1, + }; + axios.get.mockResolvedValue({ data: { data: dataReceived } }); - await actions.fetchAllConversations({ - commit: localCommit, - dispatch: localDispatch, - state: { conversationFilters: filters }, - }); + await actions.fetchAllConversations({ + commit: localCommit, + dispatch: localDispatch, + state: { conversationFilters: filters }, + }); - expect(localCommit.mock.calls).toEqual([ - [types.CLEAR_LIST_ERROR_STATUS], - [types.SET_LIST_LOADING_STATUS], - [types.SET_ALL_CONVERSATION, dataReceived.payload], - [ - `contacts/${types.SET_CONTACTS}`, - dataReceived.payload.map( - conversation => conversation.meta.sender - ), - ], - [types.CLEAR_LIST_ERROR_STATUS], - [types.CLEAR_LIST_LOADING_STATUS], - ]); - expect(localDispatch).toHaveBeenCalledWith( - 'conversationStats/set', - dataReceived.meta - ); - }); + expect(localCommit.mock.calls).toEqual([ + [types.CLEAR_LIST_ERROR_STATUS], + [types.SET_LIST_LOADING_STATUS], + [types.SET_ALL_CONVERSATION, dataReceived.payload], + [ + `contacts/${types.SET_CONTACTS}`, + dataReceived.payload.map(conversation => conversation.meta.sender), + ], + [types.CLEAR_LIST_ERROR_STATUS], + [types.CLEAR_LIST_LOADING_STATUS], + ]); + expect(localDispatch).toHaveBeenCalledWith( + 'conversationStats/set', + dataReceived.meta + ); + }); - it('clears the loading state when the API request fails', async () => { - const localCommit = vi.fn(); - axios.get.mockRejectedValue(new Error('Request failed')); + it('clears the loading state when the API request fails', async () => { + const localCommit = vi.fn(); + axios.get.mockRejectedValue(new Error('Request failed')); - await actions.fetchAllConversations({ - commit: localCommit, - dispatch: vi.fn(), - state: { conversationFilters: {} }, - }); + await actions.fetchAllConversations({ + commit: localCommit, + dispatch: vi.fn(), + state: { conversationFilters: {} }, + }); - expect(localCommit.mock.calls).toEqual([ - [types.CLEAR_LIST_ERROR_STATUS], - [types.SET_LIST_LOADING_STATUS], - [types.SET_LIST_ERROR_STATUS], - [types.CLEAR_LIST_LOADING_STATUS], - ]); - }); - }); + expect(localCommit.mock.calls).toEqual([ + [types.CLEAR_LIST_ERROR_STATUS], + [types.SET_LIST_LOADING_STATUS], + [types.SET_LIST_ERROR_STATUS], + [types.CLEAR_LIST_LOADING_STATUS], + ]); + }); + }); - describe('#setConversationFilter', () => { - it('commits the correct mutation and sets filter state', () => { - const filters = [ - { - attribute_key: 'status', - filter_operator: 'equal_to', - values: [{ id: 'snoozed', name: 'Snoozed' }], - query_operator: 'and', - }, - ]; - actions.setConversationFilters({ commit }, filters); - expect(commit.mock.calls).toEqual([ - [types.SET_CONVERSATION_FILTERS, filters], - ]); - }); - }); + describe('#setConversationFilter', () => { + it('commits the correct mutation and sets filter state', () => { + const filters = [ + { + attribute_key: 'status', + filter_operator: 'equal_to', + values: [{ id: 'snoozed', name: 'Snoozed' }], + query_operator: 'and', + }, + ]; + actions.setConversationFilters({ commit }, filters); + expect(commit.mock.calls).toEqual([ + [types.SET_CONVERSATION_FILTERS, filters], + ]); + }); + }); - describe('#clearConversationFilter', () => { - it('commits the correct mutation and clears filter state', () => { - actions.clearConversationFilters({ commit }); - expect(commit.mock.calls).toEqual([ - [types.CLEAR_CONVERSATION_FILTERS], - ]); - }); - }); + describe('#clearConversationFilter', () => { + it('commits the correct mutation and clears filter state', () => { + actions.clearConversationFilters({ commit }); + expect(commit.mock.calls).toEqual([[types.CLEAR_CONVERSATION_FILTERS]]); + }); + }); - describe('#updateConversationLastActivity', () => { - it('sends correct action', async () => { - await actions.updateConversationLastActivity( - { commit }, - { conversationId: 1, lastActivityAt: 12121212 } - ); - expect(commit.mock.calls).toEqual([ - [ - 'UPDATE_CONVERSATION_LAST_ACTIVITY', - { conversationId: 1, lastActivityAt: 12121212 }, - ], - ]); - }); - }); + describe('#updateConversationLastActivity', () => { + it('sends correct action', async () => { + await actions.updateConversationLastActivity( + { commit }, + { conversationId: 1, lastActivityAt: 12121212 } + ); + expect(commit.mock.calls).toEqual([ + [ + 'UPDATE_CONVERSATION_LAST_ACTIVITY', + { conversationId: 1, lastActivityAt: 12121212 }, + ], + ]); + }); + }); - describe('#setChatSortFilter', () => { - it('sends correct action', async () => { - await actions.setChatSortFilter( - { commit }, - { data: 'sort_on_created_at' } - ); - expect(commit.mock.calls).toEqual([ - ['CHANGE_CHAT_SORT_FILTER', { data: 'sort_on_created_at' }], - ]); - }); - }); + describe('#setChatSortFilter', () => { + it('sends correct action', async () => { + await actions.setChatSortFilter( + { commit }, + { data: 'sort_on_created_at' } + ); + expect(commit.mock.calls).toEqual([ + ['CHANGE_CHAT_SORT_FILTER', { data: 'sort_on_created_at' }], + ]); + }); + }); }); describe('#deleteMessage', () => { - it('sends correct actions if API is success', async () => { - const [conversationId, messageId] = [1, 1]; - axios.delete.mockResolvedValue({ - data: { id: 1, content: 'deleted' }, - }); - await actions.deleteMessage({ commit }, { conversationId, messageId }); - expect(commit.mock.calls).toEqual([ - [types.ADD_MESSAGE, { id: 1, content: 'deleted' }], - [ - types.DELETE_CONVERSATION_ATTACHMENTS, - { id: 1, content: 'deleted' }, - ], - ]); - }); - it('sends no actions if API is error', async () => { - const [conversationId, messageId] = [1, 1]; - axios.delete.mockRejectedValue({ message: 'Incorrect header' }); - await expect( - actions.deleteMessage({ commit }, { conversationId, messageId }) - ).rejects.toThrow(Error); - expect(commit.mock.calls).toEqual([]); - }); + it('sends correct actions if API is success', async () => { + const [conversationId, messageId] = [1, 1]; + axios.delete.mockResolvedValue({ + data: { id: 1, content: 'deleted' }, + }); + await actions.deleteMessage({ commit }, { conversationId, messageId }); + expect(commit.mock.calls).toEqual([ + [types.ADD_MESSAGE, { id: 1, content: 'deleted' }], + [types.DELETE_CONVERSATION_ATTACHMENTS, { id: 1, content: 'deleted' }], + ]); + }); + it('sends no actions if API is error', async () => { + const [conversationId, messageId] = [1, 1]; + axios.delete.mockRejectedValue({ message: 'Incorrect header' }); + await expect( + actions.deleteMessage({ commit }, { conversationId, messageId }) + ).rejects.toThrow(Error); + expect(commit.mock.calls).toEqual([]); + }); - describe('#deleteConversation', () => { - it('send correct actions if API is success', async () => { - axios.delete.mockResolvedValue({ - data: { id: 1 }, - }); - await actions.deleteConversation({ commit, dispatch }, 1); - expect(commit.mock.calls).toEqual([[types.DELETE_CONVERSATION, 1]]); - expect(dispatch.mock.calls).toEqual([ - ['conversationStats/get', {}, { root: true }], - ]); - }); + describe('#deleteConversation', () => { + it('send correct actions if API is success', async () => { + axios.delete.mockResolvedValue({ + data: { id: 1 }, + }); + await actions.deleteConversation({ commit, dispatch }, 1); + expect(commit.mock.calls).toEqual([[types.DELETE_CONVERSATION, 1]]); + expect(dispatch.mock.calls).toEqual([ + ['conversationStats/get', {}, { root: true }], + ]); + }); - it('send no actions if API is error', async () => { - axios.delete.mockRejectedValue({ message: 'Incorrect header' }); - await expect( - actions.deleteConversation({ commit, dispatch }, 1) - ).rejects.toThrow(Error); - expect(commit.mock.calls).toEqual([]); - expect(dispatch.mock.calls).toEqual([]); - }); - }); + it('send no actions if API is error', async () => { + axios.delete.mockRejectedValue({ message: 'Incorrect header' }); + await expect( + actions.deleteConversation({ commit, dispatch }, 1) + ).rejects.toThrow(Error); + expect(commit.mock.calls).toEqual([]); + expect(dispatch.mock.calls).toEqual([]); + }); + }); - describe('#updateCustomAttributes', () => { - it('update conversation custom attributes', async () => { - axios.post.mockResolvedValue({ - data: { custom_attributes: { order_d: '1001' } }, - }); - await actions.updateCustomAttributes( - { commit }, - { - conversationId: 1, - customAttributes: { order_d: '1001' }, - } - ); - expect(commit.mock.calls).toEqual([ - [ - types.UPDATE_CONVERSATION_CUSTOM_ATTRIBUTES, - { - conversationId: 1, - customAttributes: { order_d: '1001' }, - }, - ], - ]); - }); - }); + describe('#updateCustomAttributes', () => { + it('update conversation custom attributes', async () => { + axios.post.mockResolvedValue({ + data: { custom_attributes: { order_d: '1001' } }, + }); + await actions.updateCustomAttributes( + { commit }, + { + conversationId: 1, + customAttributes: { order_d: '1001' }, + } + ); + expect(commit.mock.calls).toEqual([ + [ + types.UPDATE_CONVERSATION_CUSTOM_ATTRIBUTES, + { + conversationId: 1, + customAttributes: { order_d: '1001' }, + }, + ], + ]); + }); + }); }); describe('#addMentions', () => { - it('does not send mutations if the view is not mentions', () => { - actions.addMentions( - { commit, dispatch, rootState: { route: { name: 'home' } } }, - { id: 1 } - ); - expect(commit.mock.calls).toEqual([]); - expect(dispatch.mock.calls).toEqual([]); - }); + it('does not send mutations if the view is not mentions', () => { + actions.addMentions( + { commit, dispatch, rootState: { route: { name: 'home' } } }, + { id: 1 } + ); + expect(commit.mock.calls).toEqual([]); + expect(dispatch.mock.calls).toEqual([]); + }); - it('send mutations if the view is mentions', () => { - actions.addMentions( - { - dispatch, - rootState: { route: { name: 'conversation_mentions' } }, - }, - { id: 1, meta: { sender: { id: 1 } } } - ); - expect(dispatch.mock.calls).toEqual([ - ['updateConversation', { id: 1, meta: { sender: { id: 1 } } }], - ]); - }); + it('send mutations if the view is mentions', () => { + actions.addMentions( + { + dispatch, + rootState: { route: { name: 'conversation_mentions' } }, + }, + { id: 1, meta: { sender: { id: 1 } } } + ); + expect(dispatch.mock.calls).toEqual([ + ['updateConversation', { id: 1, meta: { sender: { id: 1 } } }], + ]); + }); - it('#syncActiveConversationMessages', async () => { - const conversations = [ - { - id: 1, - messages: [ - { - id: 1, - content: 'Hello', - }, - ], - meta: { sender: { id: 1, name: 'john-doe' } }, - inbox_id: 1, - }, - ]; - axios.get.mockResolvedValue({ - data: { - payload: [{ id: 2, content: 'Welcome' }], - meta: { - agent_last_seen_at: '2023-04-20T05:22:42.990Z', - }, - }, - }); - await actions.syncActiveConversationMessages( - { - commit, - dispatch, - state: { - allConversations: conversations, - syncConversationsMessages: { - 1: 1, - }, - }, - }, - { conversationId: 1 } - ); - expect(commit.mock.calls).toEqual([ - [ - 'conversationMetadata/SET_CONVERSATION_METADATA', - { - id: 1, - data: { - agent_last_seen_at: '2023-04-20T05:22:42.990Z', - }, - }, - ], - [ - 'SET_MISSING_MESSAGES', - { - id: 1, - data: [ - { id: 1, content: 'Hello' }, - { id: 2, content: 'Welcome' }, - ], - }, - ], - [ - 'SET_LAST_MESSAGE_ID_FOR_SYNC_CONVERSATION', - { conversationId: 1, messageId: null }, - ], - ]); - }); + it('#syncActiveConversationMessages', async () => { + const conversations = [ + { + id: 1, + messages: [ + { + id: 1, + content: 'Hello', + }, + ], + meta: { sender: { id: 1, name: 'john-doe' } }, + inbox_id: 1, + }, + ]; + axios.get.mockResolvedValue({ + data: { + payload: [{ id: 2, content: 'Welcome' }], + meta: { + agent_last_seen_at: '2023-04-20T05:22:42.990Z', + }, + }, + }); + await actions.syncActiveConversationMessages( + { + commit, + dispatch, + state: { + allConversations: conversations, + syncConversationsMessages: { + 1: 1, + }, + }, + }, + { conversationId: 1 } + ); + expect(commit.mock.calls).toEqual([ + [ + 'conversationMetadata/SET_CONVERSATION_METADATA', + { + id: 1, + data: { + agent_last_seen_at: '2023-04-20T05:22:42.990Z', + }, + }, + ], + [ + 'SET_MISSING_MESSAGES', + { + id: 1, + data: [ + { id: 1, content: 'Hello' }, + { id: 2, content: 'Welcome' }, + ], + }, + ], + [ + 'SET_LAST_MESSAGE_ID_FOR_SYNC_CONVERSATION', + { conversationId: 1, messageId: null }, + ], + ]); + }); - describe('#fetchAllAttachments', () => { - it('fetches all attachments', async () => { - axios.get.mockResolvedValue({ - data: { - payload: [ - { - id: 1, - message_id: 1, - file_type: 'image', - data_url: '', - thumb_url: '', - }, - ], - }, - }); - await actions.fetchAllAttachments({ commit }, 1); - expect(commit.mock.calls).toEqual([ - [ - types.SET_ALL_ATTACHMENTS, - { - id: 1, - data: [ - { - id: 1, - message_id: 1, - file_type: 'image', - data_url: '', - thumb_url: '', - }, - ], - }, - ], - ]); - }); - }); + describe('#fetchAllAttachments', () => { + it('fetches all attachments', async () => { + axios.get.mockResolvedValue({ + data: { + payload: [ + { + id: 1, + message_id: 1, + file_type: 'image', + data_url: '', + thumb_url: '', + }, + ], + }, + }); + await actions.fetchAllAttachments({ commit }, 1); + expect(commit.mock.calls).toEqual([ + [ + types.SET_ALL_ATTACHMENTS, + { + id: 1, + data: [ + { + id: 1, + message_id: 1, + file_type: 'image', + data_url: '', + thumb_url: '', + }, + ], + }, + ], + ]); + }); + }); - describe('#setContextMenuChatId', () => { - it('sets the context menu chat id', () => { - actions.setContextMenuChatId({ commit }, 1); - expect(commit.mock.calls).toEqual([ - [types.SET_CONTEXT_MENU_CHAT_ID, 1], - ]); - }); - }); + describe('#setContextMenuChatId', () => { + it('sets the context menu chat id', () => { + actions.setContextMenuChatId({ commit }, 1); + expect(commit.mock.calls).toEqual([[types.SET_CONTEXT_MENU_CHAT_ID, 1]]); + }); + }); - describe('#setChatListFilters', () => { - it('set chat list filters', () => { - const filters = { - inboxId: 1, - assigneeType: 'me', - status: 'open', - sortBy: 'created_at', - page: 1, - labels: ['label'], - teamId: 1, - conversationType: 'mention', - }; - actions.setChatListFilters({ commit }, filters); - expect(commit.mock.calls).toEqual([ - [types.SET_CHAT_LIST_FILTERS, filters], - ]); - }); - }); + describe('#setChatListFilters', () => { + it('set chat list filters', () => { + const filters = { + inboxId: 1, + assigneeType: 'me', + status: 'open', + sortBy: 'created_at', + page: 1, + labels: ['label'], + teamId: 1, + conversationType: 'mention', + }; + actions.setChatListFilters({ commit }, filters); + expect(commit.mock.calls).toEqual([ + [types.SET_CHAT_LIST_FILTERS, filters], + ]); + }); + }); - describe('#updateChatListFilters', () => { - it('update chat list filters', () => { - actions.updateChatListFilters({ commit }, { updatedWithin: 20 }); - expect(commit.mock.calls).toEqual([ - [types.UPDATE_CHAT_LIST_FILTERS, { updatedWithin: 20 }], - ]); - }); - }); + describe('#updateChatListFilters', () => { + it('update chat list filters', () => { + actions.updateChatListFilters({ commit }, { updatedWithin: 20 }); + expect(commit.mock.calls).toEqual([ + [types.UPDATE_CHAT_LIST_FILTERS, { updatedWithin: 20 }], + ]); + }); + }); - describe('#setActiveChat', () => { - it('should commit SET_CHAT_DATA_FETCHED with conversation ID after fetch', async () => { - const localCommit = vi.fn(); - const localDispatch = vi.fn().mockResolvedValue(); - const data = { id: 42, messages: [{ id: 100 }] }; + describe('#setActiveChat', () => { + it('should commit SET_CHAT_DATA_FETCHED with conversation ID after fetch', async () => { + const localCommit = vi.fn(); + const localDispatch = vi.fn().mockResolvedValue(); + const data = { id: 42, messages: [{ id: 100 }] }; - await actions.setActiveChat( - { commit: localCommit, dispatch: localDispatch }, - { data, after: 99 } - ); + await actions.setActiveChat( + { commit: localCommit, dispatch: localDispatch }, + { data, after: 99 } + ); - expect(localCommit.mock.calls).toEqual([ - [types.SET_CURRENT_CHAT_WINDOW, data], - [types.CLEAR_ALL_MESSAGES_LOADED, 42], - [types.SET_CHAT_DATA_FETCHED, 42], - ]); - expect(localDispatch).toHaveBeenCalledWith( - 'fetchPreviousMessages', - { - after: 99, - before: 100, - conversationId: 42, - } - ); - }); + expect(localCommit.mock.calls).toEqual([ + [types.SET_CURRENT_CHAT_WINDOW, data], + [types.CLEAR_ALL_MESSAGES_LOADED, 42], + [types.SET_CHAT_DATA_FETCHED, 42], + ]); + expect(localDispatch).toHaveBeenCalledWith('fetchPreviousMessages', { + after: 99, + before: 100, + conversationId: 42, + }); + }); - it('should not dispatch fetchPreviousMessages if dataFetched is already set', async () => { - const localCommit = vi.fn(); - const localDispatch = vi.fn(); - const data = { id: 42, messages: [{ id: 100 }], dataFetched: true }; + it('should not dispatch fetchPreviousMessages if dataFetched is already set', async () => { + const localCommit = vi.fn(); + const localDispatch = vi.fn(); + const data = { id: 42, messages: [{ id: 100 }], dataFetched: true }; - await actions.setActiveChat( - { commit: localCommit, dispatch: localDispatch }, - { data } - ); + await actions.setActiveChat( + { commit: localCommit, dispatch: localDispatch }, + { data } + ); - expect(localCommit.mock.calls).toEqual([ - [types.SET_CURRENT_CHAT_WINDOW, data], - [types.CLEAR_ALL_MESSAGES_LOADED, 42], - ]); - expect(localDispatch).not.toHaveBeenCalled(); - }); + expect(localCommit.mock.calls).toEqual([ + [types.SET_CURRENT_CHAT_WINDOW, data], + [types.CLEAR_ALL_MESSAGES_LOADED, 42], + ]); + expect(localDispatch).not.toHaveBeenCalled(); + }); - it('should commit SET_CHAT_DATA_FETCHED by ID, not mutate the data object directly (race condition fix)', async () => { - const localCommit = vi.fn(); - const localDispatch = vi.fn().mockResolvedValue(); - const data = { id: 42, messages: [{ id: 100 }] }; + it('should commit SET_CHAT_DATA_FETCHED by ID, not mutate the data object directly (race condition fix)', async () => { + const localCommit = vi.fn(); + const localDispatch = vi.fn().mockResolvedValue(); + const data = { id: 42, messages: [{ id: 100 }] }; - await actions.setActiveChat( - { commit: localCommit, dispatch: localDispatch }, - { data } - ); + await actions.setActiveChat( + { commit: localCommit, dispatch: localDispatch }, + { data } + ); - // The action must NOT set dataFetched on the data object directly - expect(data.dataFetched).toBeUndefined(); + // The action must NOT set dataFetched on the data object directly + expect(data.dataFetched).toBeUndefined(); - // Instead it commits a mutation that finds the conversation by ID in the store - expect(localCommit).toHaveBeenCalledWith( - types.SET_CHAT_DATA_FETCHED, - 42 - ); - }); - }); + // Instead it commits a mutation that finds the conversation by ID in the store + expect(localCommit).toHaveBeenCalledWith(types.SET_CHAT_DATA_FETCHED, 42); + }); + }); - describe('#getInboxCaptainAssistantById', () => { - it('fetches inbox assistant by id', async () => { - axios.get.mockResolvedValue({ - data: { - assistant: { - id: 1, - name: 'Assistant', - description: 'Assistant description', - }, - }, - }); - await actions.getInboxCaptainAssistantById({ commit }, 1); - expect(commit.mock.calls).toEqual([ - [types.SET_INBOX_CAPTAIN_ASSISTANT, { assistant: null }], - [ - types.SET_INBOX_CAPTAIN_ASSISTANT, - { - assistant: { - id: 1, - name: 'Assistant', - description: 'Assistant description', - }, - }, - ], - ]); - }); + describe('#getInboxCaptainAssistantById', () => { + it('fetches inbox assistant by id', async () => { + axios.get.mockResolvedValue({ + data: { + assistant: { + id: 1, + name: 'Assistant', + description: 'Assistant description', + }, + }, + }); + await actions.getInboxCaptainAssistantById({ commit }, 1); + expect(commit.mock.calls).toEqual([ + [types.SET_INBOX_CAPTAIN_ASSISTANT, { assistant: null }], + [ + types.SET_INBOX_CAPTAIN_ASSISTANT, + { + assistant: { + id: 1, + name: 'Assistant', + description: 'Assistant description', + }, + }, + ], + ]); + }); - it('ignores an older response after switching conversations', async () => { - const resolvers = []; - axios.get.mockImplementation( - () => new Promise(resolve => resolvers.push(resolve)) - ); + it('ignores an older response after switching conversations', async () => { + const resolvers = []; + axios.get.mockImplementation( + () => + new Promise(resolve => { + resolvers.push(resolve); + }) + ); - const requestA = actions.getInboxCaptainAssistantById({ commit }, 1); - const requestB = actions.getInboxCaptainAssistantById({ commit }, 2); - resolvers[1]({ data: { assistant: null } }); - await requestB; - resolvers[0]({ data: { assistant: { id: 1, name: 'Assistant A' } } }); - await requestA; + const requestA = actions.getInboxCaptainAssistantById({ commit }, 1); + const requestB = actions.getInboxCaptainAssistantById({ commit }, 2); + resolvers[1]({ data: { assistant: null } }); + await requestB; + resolvers[0]({ data: { assistant: { id: 1, name: 'Assistant A' } } }); + await requestA; - expect(commit.mock.calls).toEqual([ - [types.SET_INBOX_CAPTAIN_ASSISTANT, { assistant: null }], - [types.SET_INBOX_CAPTAIN_ASSISTANT, { assistant: null }], - [types.SET_INBOX_CAPTAIN_ASSISTANT, { assistant: null }], - ]); - }); - }); + expect(commit.mock.calls).toEqual([ + [types.SET_INBOX_CAPTAIN_ASSISTANT, { assistant: null }], + [types.SET_INBOX_CAPTAIN_ASSISTANT, { assistant: null }], + [types.SET_INBOX_CAPTAIN_ASSISTANT, { assistant: null }], + ]); + }); + }); });