Files
gochat/frontend/app/javascript/dashboard/components/widgets/WootWriter/ReplyTopPanel.spec.js
T
Rogeeandrogee e41d40feb8 HH-546: flatten conversation AI actions (#128)
* fix(HH-546): flatten conversation AI actions

* fix(HH-546): wrap AI actions on narrow screens

---------

Co-authored-by: Rogee <rogee@ipao.vip>
2026-08-23 21:16:19 +08:00

137 lines
4.0 KiB
JavaScript

import { mount } from '@vue/test-utils';
import { createStore } from 'vuex';
import { ref } from 'vue';
import CopilotMenuBar from './CopilotMenuBar.vue';
import ReplyTopPanel from './ReplyTopPanel.vue';
vi.mock('dashboard/composables/useCaptain', () => ({
useCaptain: () => ({ captainTasksEnabled: ref(true) }),
}));
vi.mock('dashboard/composables/useKeyboardEvents', () => ({
useKeyboardEvents: vi.fn(),
}));
const createWrapper = ({ aiTakeoverActive = false, props = {} } = {}) => {
const startAITakeover = vi.fn();
const exitAITakeover = vi.fn();
const store = createStore({
state: {
currentChat: { id: 42, ai_takeover_active: aiTakeoverActive },
},
getters: {
getSelectedChat: state => state.currentChat,
},
actions: {
startAITakeover,
exitAITakeover,
},
modules: {
draftMessages: {
namespaced: true,
getters: { getReplyEditorMode: () => 'REPLY' },
},
},
});
return {
store,
startAITakeover,
wrapper: mount(ReplyTopPanel, {
props: { conversationId: 42, ...props },
global: {
plugins: [store],
stubs: { NextButton: false },
},
}),
};
};
describe('ReplyTopPanel', () => {
it('shows AI takeover and flat Copilot actions in the same action row', () => {
const { wrapper } = createWrapper();
const actions = wrapper.get('[data-testid="reply-top-panel-actions"]');
const copilotActions = actions.getComponent(CopilotMenuBar);
expect(copilotActions.props('inline')).toBe(true);
expect(actions.text()).toContain('AI takeover');
expect(actions.get('[data-testid="copilot-inline-actions"]')).toBeTruthy();
});
it('wraps all eight primary actions on narrow screens when content is present', () => {
const { wrapper } = createWrapper({ props: { hasContent: true } });
const actions = wrapper.get('[data-testid="reply-top-panel-actions"]');
const copilotActions = actions.get(
'[data-testid="copilot-inline-actions"]'
);
expect(wrapper.classes()).toEqual(
expect.arrayContaining(['flex-wrap', 'sm:flex-nowrap'])
);
expect(actions.classes()).toEqual(
expect.arrayContaining([
'w-full',
'flex-wrap',
'sm:w-auto',
'sm:flex-nowrap',
])
);
expect(actions.findAll(':scope > button')).toHaveLength(2);
expect(copilotActions.findAll(':scope > div > button')).toHaveLength(6);
});
it('keeps AI takeover behavior in the new action row', async () => {
const { startAITakeover, wrapper } = createWrapper();
const takeoverButton = wrapper
.get('[data-testid="reply-top-panel-actions"]')
.findAll('button')
.find(button => button.text() === 'AI takeover');
await takeoverButton.trigger('click');
expect(startAITakeover).toHaveBeenCalledWith(expect.any(Object), 42);
});
it('renders Copilot actions inline and forwards the selected action', async () => {
const store = createStore({
modules: {
draftMessages: {
namespaced: true,
getters: { getReplyEditorMode: () => 'REPLY' },
},
},
});
const wrapper = mount(CopilotMenuBar, {
props: { inline: true, conversationId: 42 },
global: {
plugins: [store],
stubs: { NextButton: false },
},
});
const suggestionButton = wrapper.get(
'[data-testid="copilot-inline-actions"] button[aria-label="Suggest a reply"]'
);
await suggestionButton.trigger('click');
expect(wrapper.emitted('executeCopilotAction')).toEqual([
['reply_suggestion'],
]);
});
it('disables every flat Copilot action with the editor', () => {
const { wrapper } = createWrapper({
props: { hasContent: true, isEditorDisabled: true },
});
const copilotButtons = wrapper
.get('[data-testid="copilot-inline-actions"]')
.findAll('button');
expect(copilotButtons.length).toBeGreaterThan(0);
expect(
copilotButtons.every(button => button.attributes('disabled') === '')
).toBe(true);
});
});