test(captain): cover FAQ review behavior
This commit is contained in:
+30
-23
@@ -60,11 +60,35 @@ const deferred = () => {
|
||||
return { promise, resolve };
|
||||
};
|
||||
|
||||
const PageLayoutStub = {
|
||||
template: '<div><slot name="body" /></div>',
|
||||
};
|
||||
|
||||
const FaqSuggestionCardStub = {
|
||||
props: ['suggestion'],
|
||||
template: '<div>{{ suggestion.question }}</div>',
|
||||
};
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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
|
||||
|
||||
@@ -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') }
|
||||
|
||||
Reference in New Issue
Block a user