HH-598: hide unavailable AI takeover (#155)

* fix(HH-598): hide unavailable AI takeover

* fix: isolate inbox assistant availability

---------

Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
Rogee
2026-08-24 12:49:46 +08:00
committed by GitHub
co-authored by rogee
parent 907d8ea4e1
commit a7e96afef4
6 changed files with 151 additions and 16 deletions
@@ -1,10 +1,15 @@
import { mount } from '@vue/test-utils';
import axios from 'axios';
import { flushPromises, mount } from '@vue/test-utils';
import { createStore } from 'vuex';
import { ref } from 'vue';
import conversationActions from 'dashboard/store/modules/conversations/actions';
import types from 'dashboard/store/mutation-types';
import CopilotMenuBar from './CopilotMenuBar.vue';
import ReplyTopPanel from './ReplyTopPanel.vue';
vi.mock('axios');
vi.mock('dashboard/composables/useCaptain', () => ({
useCaptain: () => ({ captainTasksEnabled: ref(true) }),
}));
@@ -13,19 +18,35 @@ vi.mock('dashboard/composables/useKeyboardEvents', () => ({
useKeyboardEvents: vi.fn(),
}));
const createWrapper = ({ aiTakeoverActive = false, props = {} } = {}) => {
const createWrapper = ({
aiTakeoverActive = false,
inboxAssistant = { id: 7 },
useRealAssistantAction = false,
props = {},
} = {}) => {
const startAITakeover = vi.fn();
const exitAITakeover = vi.fn();
const getInboxCaptainAssistantById = vi.fn();
const store = createStore({
state: {
currentChat: { id: 42, ai_takeover_active: aiTakeoverActive },
copilotAssistant: inboxAssistant,
},
getters: {
getSelectedChat: state => state.currentChat,
getCopilotAssistant: state => state.copilotAssistant,
},
mutations: {
[types.SET_INBOX_CAPTAIN_ASSISTANT](state, data) {
state.copilotAssistant = data.assistant;
},
},
actions: {
startAITakeover,
exitAITakeover,
getInboxCaptainAssistantById: useRealAssistantAction
? conversationActions.getInboxCaptainAssistantById
: getInboxCaptainAssistantById,
},
modules: {
draftMessages: {
@@ -37,7 +58,9 @@ const createWrapper = ({ aiTakeoverActive = false, props = {} } = {}) => {
return {
store,
exitAITakeover,
startAITakeover,
getInboxCaptainAssistantById,
wrapper: mount(ReplyTopPanel, {
props: { conversationId: 42, ...props },
global: {
@@ -93,6 +116,45 @@ describe('ReplyTopPanel', () => {
expect(startAITakeover).toHaveBeenCalledWith(expect.any(Object), 42);
});
it('hides AI takeover when the inbox has no AI configured', () => {
const { getInboxCaptainAssistantById, startAITakeover, wrapper } =
createWrapper({ inboxAssistant: null });
expect(getInboxCaptainAssistantById).toHaveBeenCalledWith(
expect.any(Object),
42
);
expect(wrapper.text()).not.toContain('AI takeover');
expect(startAITakeover).not.toHaveBeenCalled();
});
it('updates after the inbox assistant API returns null', async () => {
axios.get.mockResolvedValue({ data: { assistant: null } });
const { store, wrapper } = createWrapper({
useRealAssistantAction: true,
});
await flushPromises();
expect(store.getters.getCopilotAssistant).toBeNull();
expect(wrapper.text()).not.toContain('AI takeover');
});
it('keeps the exit action when takeover is active without an assistant', async () => {
const { exitAITakeover, wrapper } = createWrapper({
aiTakeoverActive: true,
inboxAssistant: null,
});
const exitButton = wrapper
.get('[data-testid="reply-top-panel-actions"]')
.findAll('button')
.find(button => button.text() === 'Exit AI takeover');
await exitButton.trigger('click');
expect(exitAITakeover).toHaveBeenCalledWith(expect.any(Object), 42);
});
it('renders Copilot actions inline and forwards the selected action', async () => {
const store = createStore({
modules: {