diff --git a/app/javascript/dashboard/routes/dashboard/onboarding/specs/inbox-setup/InboxChannelsDialog.spec.js b/app/javascript/dashboard/routes/dashboard/onboarding/specs/inbox-setup/InboxChannelsDialog.spec.js
index bf150f95d..a2ddcf7a1 100644
--- a/app/javascript/dashboard/routes/dashboard/onboarding/specs/inbox-setup/InboxChannelsDialog.spec.js
+++ b/app/javascript/dashboard/routes/dashboard/onboarding/specs/inbox-setup/InboxChannelsDialog.spec.js
@@ -1,11 +1,11 @@
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
+import { createStore } from 'vuex';
+import { useRoute } from 'vue-router';
import InboxChannelsDialog from '../../inbox-setup/InboxChannelsDialog.vue';
vi.mock('vue-i18n', () => ({ useI18n: () => ({ t: key => key }) }));
-vi.mock('dashboard/composables/store', () => ({
- useMapGetter: () => ({ value: {} }),
-}));
+vi.mock('vue-router');
vi.mock('../../inbox-setup/useChannelConnect', () => ({
useChannelConnect: () => ({
connectViaOAuth: vi.fn(),
@@ -13,10 +13,30 @@ vi.mock('../../inbox-setup/useChannelConnect', () => ({
}),
}));
+const store = createStore({
+ modules: {
+ globalConfig: {
+ namespaced: true,
+ getters: {
+ get: () => ({}),
+ isOnChatwootCloud: () => false,
+ },
+ },
+ accounts: {
+ namespaced: true,
+ getters: {
+ getAccount: () => () => ({ id: 1, features: {} }),
+ isFeatureEnabledonAccount: () => () => false,
+ },
+ },
+ },
+});
+
const mountDialog = () =>
mount(InboxChannelsDialog, {
props: { inboxes: [] },
global: {
+ plugins: [store],
stubs: {
Dialog: {
template: '
',
@@ -31,6 +51,10 @@ const mountDialog = () =>
});
describe('InboxChannelsDialog Facebook gating', () => {
+ beforeEach(() => {
+ useRoute.mockReturnValue({ params: { accountId: '1' } });
+ });
+
afterEach(() => {
delete window.chatwootConfig;
});
diff --git a/enterprise/app/services/enterprise/message_templates/hook_execution_service.rb b/enterprise/app/services/enterprise/message_templates/hook_execution_service.rb
index 0f377ada0..fa12996b5 100644
--- a/enterprise/app/services/enterprise/message_templates/hook_execution_service.rb
+++ b/enterprise/app/services/enterprise/message_templates/hook_execution_service.rb
@@ -50,7 +50,7 @@ module Enterprise::MessageTemplates::HookExecutionService
end
def should_process_captain_response?
- conversation.pending? && message.incoming? && captain_should_engage?
+ conversation.pending? && message.incoming? && captain_assistant_configured?
end
def perform_handoff
@@ -76,11 +76,10 @@ module Enterprise::MessageTemplates::HookExecutionService
end
def captain_handling_conversation?
- conversation.pending? && captain_should_engage?
+ conversation.pending? && captain_assistant_configured?
end
- def captain_should_engage?
- assistant = inbox.captain_assistant
- assistant.present? && assistant.engages?(conversation.contact, conversation)
+ def captain_assistant_configured?
+ inbox.captain_assistant.present?
end
end
diff --git a/spec/enterprise/services/enterprise/message_templates/hook_execution_service_spec.rb b/spec/enterprise/services/enterprise/message_templates/hook_execution_service_spec.rb
index c0fa2654c..de4937030 100644
--- a/spec/enterprise/services/enterprise/message_templates/hook_execution_service_spec.rb
+++ b/spec/enterprise/services/enterprise/message_templates/hook_execution_service_spec.rb
@@ -126,21 +126,6 @@ RSpec.describe MessageTemplates::HookExecutionService do
end
end
- context 'when the contact is outside the assistant audience' do
- before do
- assistant.update!(config: assistant.config.merge('audience' => {
- 'attribute_key' => 'country_code', 'filter_operator' => 'equal_to', 'values' => ['US']
- }))
- contact.update!(additional_attributes: { 'country_code' => 'CA' })
- end
-
- it 'does not schedule captain response job' do
- expect(Captain::Conversation::ResponseBuilderJob).not_to receive(:perform_later)
-
- create(:message, conversation: conversation, message_type: :incoming, account: account)
- end
- end
-
context 'when the contact is inside the assistant audience' do
before do
assistant.update!(config: assistant.config.merge('audience' => {
@@ -156,6 +141,77 @@ RSpec.describe MessageTemplates::HookExecutionService do
end
end
+ context 'when the conversation stops matching the audience mid-conversation' do
+ it 'still schedules captain response job for the pending conversation' do
+ conversation
+ assistant.update!(config: assistant.config.merge('audience' => {
+ 'attribute_key' => 'country_code', 'filter_operator' => 'equal_to', 'values' => ['US']
+ }))
+ contact.update!(additional_attributes: { 'country_code' => 'CA' })
+
+ expect(Captain::Conversation::ResponseBuilderJob).to receive(:perform_later).with(conversation, assistant)
+
+ create(:message, conversation: conversation, message_type: :incoming, account: account)
+ end
+ end
+
+ context 'when the reply schedule stops matching mid-conversation' do
+ before do
+ inbox.update!(working_hours_enabled: true)
+ inbox.working_hours.find_by(day_of_week: Time.current.in_time_zone(inbox.timezone).wday).update!(
+ open_all_day: true,
+ closed_all_day: false
+ )
+ end
+
+ it 'still schedules captain response job when business hours end after captain took the conversation' do
+ assistant.update!(config: assistant.config.merge('response_window' => 'business_hours'))
+ conversation
+ inbox.working_hours.find_by(day_of_week: Time.current.in_time_zone(inbox.timezone).wday).update!(
+ open_all_day: false,
+ closed_all_day: true
+ )
+
+ expect(Captain::Conversation::ResponseBuilderJob).to receive(:perform_later).with(conversation, assistant)
+
+ create(:message, conversation: conversation, message_type: :incoming, account: account)
+ end
+
+ it 'still schedules captain response job when business hours begin after captain took the conversation' do
+ assistant.update!(config: assistant.config.merge('response_window' => 'outside_business_hours'))
+ inbox.working_hours.find_by(day_of_week: Time.current.in_time_zone(inbox.timezone).wday).update!(
+ open_all_day: false,
+ closed_all_day: true
+ )
+ conversation
+ inbox.working_hours.find_by(day_of_week: Time.current.in_time_zone(inbox.timezone).wday).update!(
+ open_all_day: true,
+ closed_all_day: false
+ )
+
+ expect(Captain::Conversation::ResponseBuilderJob).to receive(:perform_later).with(conversation, assistant)
+
+ create(:message, conversation: conversation, message_type: :incoming, account: account)
+ end
+
+ it 'still schedules captain response job when both audience and schedule stop matching' do
+ assistant.update!(config: assistant.config.merge('response_window' => 'business_hours'))
+ conversation
+ assistant.update!(config: assistant.config.merge('audience' => {
+ 'attribute_key' => 'country_code', 'filter_operator' => 'equal_to', 'values' => ['US']
+ }))
+ contact.update!(additional_attributes: { 'country_code' => 'CA' })
+ inbox.working_hours.find_by(day_of_week: Time.current.in_time_zone(inbox.timezone).wday).update!(
+ open_all_day: false,
+ closed_all_day: true
+ )
+
+ expect(Captain::Conversation::ResponseBuilderJob).to receive(:perform_later).with(conversation, assistant)
+
+ create(:message, conversation: conversation, message_type: :incoming, account: account)
+ end
+ end
+
context 'when message is outgoing' do
it 'does not schedule captain response job' do
expect(Captain::Conversation::ResponseBuilderJob).not_to receive(:perform_later)