diff --git a/app/javascript/dashboard/routes/dashboard/captain/responses/FaqSuggestions.spec.js b/app/javascript/dashboard/routes/dashboard/captain/responses/FaqSuggestions.spec.js
index 561b4f668..e72cc5a08 100644
--- a/app/javascript/dashboard/routes/dashboard/captain/responses/FaqSuggestions.spec.js
+++ b/app/javascript/dashboard/routes/dashboard/captain/responses/FaqSuggestions.spec.js
@@ -60,11 +60,35 @@ const deferred = () => {
return { promise, resolve };
};
+const PageLayoutStub = {
+ template: '
',
+};
+
+const FaqSuggestionCardStub = {
+ props: ['suggestion'],
+ template: '{{ suggestion.question }}
',
+};
+
describe('FaqSuggestions', () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.route.params.assistantId = 1;
mocks.route.query = {};
+ mocks.getterValues['captainFaqSuggestions/getRecords'].value = [];
+ mocks.getterValues['captainFaqSuggestions/getMeta'].value = {
+ totalCount: 0,
+ page: 1,
+ };
+ mocks.dispatch.mockImplementation((action, payload) => {
+ if (action !== 'captainFaqSuggestions/setRecords') return;
+
+ mocks.getterValues['captainFaqSuggestions/getRecords'].value =
+ payload.records;
+ mocks.getterValues['captainFaqSuggestions/getMeta'].value = {
+ totalCount: payload.meta.total_count,
+ page: payload.meta.page,
+ };
+ });
});
it('keeps the latest assistant results when requests finish in the wrong order', async () => {
@@ -77,6 +101,10 @@ describe('FaqSuggestions', () => {
const wrapper = shallowMount(FaqSuggestions, {
global: {
mocks: { $t: key => key },
+ stubs: {
+ PageLayout: PageLayoutStub,
+ FaqSuggestionCard: FaqSuggestionCardStub,
+ },
},
});
@@ -100,29 +128,8 @@ describe('FaqSuggestions', () => {
});
await flushPromises();
- expect(mocks.apiGet).toHaveBeenNthCalledWith(1, {
- page: 1,
- search: '',
- assistantId: 1,
- });
- expect(mocks.apiGet).toHaveBeenNthCalledWith(2, {
- page: 1,
- search: '',
- assistantId: 2,
- });
- expect(mocks.dispatch).toHaveBeenCalledWith(
- 'captainFaqSuggestions/setRecords',
- {
- records: [{ id: 2, question: 'Current assistant' }],
- meta: { page: 1, total_count: 1 },
- }
- );
- expect(mocks.dispatch).not.toHaveBeenCalledWith(
- 'captainFaqSuggestions/setRecords',
- expect.objectContaining({
- records: [{ id: 1, question: 'Previous assistant' }],
- })
- );
+ expect(wrapper.text()).toContain('Current assistant');
+ expect(wrapper.text()).not.toContain('Previous assistant');
wrapper.unmount();
});
diff --git a/app/javascript/dashboard/store/captain/faqSuggestions.spec.js b/app/javascript/dashboard/store/captain/faqSuggestions.spec.js
deleted file mode 100644
index afd15da1a..000000000
--- a/app/javascript/dashboard/store/captain/faqSuggestions.spec.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import CaptainFaqSuggestionsAPI from 'dashboard/api/captain/faqSuggestions';
-import faqSuggestions from './faqSuggestions';
-
-vi.mock('dashboard/api/captain/faqSuggestions', () => ({
- default: {
- get: vi.fn(),
- approve: vi.fn(),
- dismiss: vi.fn(),
- },
-}));
-
-const deferred = () => {
- let resolve;
- const promise = new Promise(resolvePromise => {
- resolve = resolvePromise;
- });
- return { promise, resolve };
-};
-
-describe('captainFaqSuggestions', () => {
- it('keeps the latest assistant count when requests finish in the wrong order', async () => {
- const firstRequest = deferred();
- const secondRequest = deferred();
- CaptainFaqSuggestionsAPI.get
- .mockReturnValueOnce(firstRequest.promise)
- .mockReturnValueOnce(secondRequest.promise);
- const commit = vi.fn();
-
- const firstAction = faqSuggestions.actions.fetchOpenCount({ commit }, 1);
- const secondAction = faqSuggestions.actions.fetchOpenCount({ commit }, 2);
-
- secondRequest.resolve({ data: { meta: { total_count: 4 } } });
- await secondAction;
- firstRequest.resolve({ data: { meta: { total_count: 9 } } });
- await firstAction;
-
- expect(CaptainFaqSuggestionsAPI.get).toHaveBeenNthCalledWith(1, {
- assistantId: 1,
- page: 1,
- });
- expect(CaptainFaqSuggestionsAPI.get).toHaveBeenNthCalledWith(2, {
- assistantId: 2,
- page: 1,
- });
- expect(commit).toHaveBeenLastCalledWith('SET_OPEN_COUNT', 4);
- });
-});
diff --git a/spec/enterprise/builders/captain/assistant_stats_builder_spec.rb b/spec/enterprise/builders/captain/assistant_stats_builder_spec.rb
index 471f2ed28..785c80e8e 100644
--- a/spec/enterprise/builders/captain/assistant_stats_builder_spec.rb
+++ b/spec/enterprise/builders/captain/assistant_stats_builder_spec.rb
@@ -229,31 +229,6 @@ RSpec.describe Captain::AssistantStatsBuilder do
end
end
- describe '#metrics knowledge' do
- before do
- create_list(:captain_assistant_response, 3, assistant: assistant, account: account, status: :approved)
- assistant.faq_suggestions.create!(
- question: 'How do I enable the feature?',
- answer: 'Turn it on in settings.'
- )
- create_list(:captain_document, 2, assistant: assistant, account: account)
- end
-
- it 'returns approved FAQ, open suggestion, document counts and coverage' do
- knowledge = described_class.new(assistant, '30').metrics[:knowledge]
-
- expect(knowledge).to eq(approved: 3, suggestions: 1, documents: 2, coverage: 75)
- end
-
- it 'reports zero coverage when there are no responses' do
- Captain::AssistantResponse.where(assistant: assistant).delete_all
-
- knowledge = described_class.new(assistant, '30').metrics[:knowledge]
-
- expect(knowledge[:coverage]).to eq(0)
- end
- end
-
describe '#period' do
it 'labels a day range and exposes its bounds' do
period = described_class.new(assistant, '30').period
diff --git a/spec/enterprise/controllers/api/v1/accounts/captain/assistants_controller_spec.rb b/spec/enterprise/controllers/api/v1/accounts/captain/assistants_controller_spec.rb
index c43924fc4..aa70c3a9b 100644
--- a/spec/enterprise/controllers/api/v1/accounts/captain/assistants_controller_spec.rb
+++ b/spec/enterprise/controllers/api/v1/accounts/captain/assistants_controller_spec.rb
@@ -252,6 +252,64 @@ RSpec.describe 'Api::V1::Accounts::Captain::Assistants', type: :request do
end
end
+ describe 'GET /api/v1/accounts/{account.id}/captain/assistants/{id}/stats' do
+ let(:assistant) { create(:captain_assistant, account: account) }
+
+ it 'returns approved FAQ, open suggestion, document counts and coverage' do
+ create_list(:captain_assistant_response, 3, assistant: assistant, account: account, status: :approved)
+ assistant.faq_suggestions.create!(question: 'How do I enable the feature?', answer: 'Turn it on in settings.')
+ create_list(:captain_document, 2, assistant: assistant, account: account)
+
+ get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/stats",
+ params: { range: '30' },
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ expect(json_response[:knowledge]).to eq(approved: 3, suggestions: 1, documents: 2, coverage: 75)
+ end
+
+ it 'returns zero coverage when there are no FAQs or suggestions' do
+ get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/stats",
+ params: { range: '30' },
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ expect(json_response[:knowledge]).to include(approved: 0, suggestions: 0, coverage: 0)
+ end
+
+ it 'counts only suggestions backed by conversations the agent can access' do
+ accessible_inbox = create(:inbox, account: account)
+ hidden_inbox = create(:inbox, account: account)
+ create(:inbox_member, user: agent, inbox: accessible_inbox)
+ create(:captain_assistant_response, assistant: assistant, account: account, status: :approved)
+
+ accessible_suggestion = assistant.faq_suggestions.create!(question: 'Visible question', answer: 'Visible answer')
+ accessible_suggestion.observations.create!(
+ conversation: create(:conversation, account: account, inbox: accessible_inbox),
+ generated_question: accessible_suggestion.question,
+ generated_answer: accessible_suggestion.answer,
+ language: accessible_suggestion.language
+ )
+ hidden_suggestion = assistant.faq_suggestions.create!(question: 'Hidden question', answer: 'Hidden answer')
+ hidden_suggestion.observations.create!(
+ conversation: create(:conversation, account: account, inbox: hidden_inbox),
+ generated_question: hidden_suggestion.question,
+ generated_answer: hidden_suggestion.answer,
+ language: hidden_suggestion.language
+ )
+
+ get "/api/v1/accounts/#{account.id}/captain/assistants/#{assistant.id}/stats",
+ params: { range: '30' },
+ headers: agent.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ expect(json_response[:knowledge]).to include(approved: 1, suggestions: 1, coverage: 50)
+ end
+ end
+
describe 'GET /api/v1/accounts/{account.id}/captain/assistants/{id}/summary' do
let(:assistant) { create(:captain_assistant, account: account) }
let(:alice) { create(:user, account: account, role: :administrator, name: 'Alice Adams') }