* H-289: add Captain Skill management UI * H-289: guard Captain Skill assistant switches --------- Co-authored-by: Rogee <rogee@ipao.vip>
159 lines
4.0 KiB
JavaScript
159 lines
4.0 KiB
JavaScript
import { shallowMount } from '@vue/test-utils';
|
|
import SkillDialog from './SkillDialog.vue';
|
|
|
|
vi.mock('dashboard/composables', () => ({ useAlert: vi.fn() }));
|
|
const mockedStore = vi.hoisted(() => ({ dispatch: vi.fn() }));
|
|
vi.mock('dashboard/composables/store', () => ({
|
|
useStore: () => mockedStore,
|
|
}));
|
|
|
|
const Dialog = {
|
|
name: 'Dialog',
|
|
emits: ['close'],
|
|
methods: {
|
|
open() {},
|
|
close() {
|
|
this.$emit('close');
|
|
},
|
|
},
|
|
template: '<div><slot /><slot name="footer" /></div>',
|
|
};
|
|
|
|
const Button = {
|
|
name: 'Button',
|
|
props: ['label'],
|
|
emits: ['click'],
|
|
template:
|
|
'<button type="button" @click="$emit(\'click\')">{{ label }}</button>',
|
|
};
|
|
|
|
const mountDialog = ({ skill = null, dispatch = vi.fn() } = {}) => {
|
|
mockedStore.dispatch = dispatch;
|
|
return shallowMount(SkillDialog, {
|
|
props: { skill, assistantId: 9 },
|
|
global: {
|
|
stubs: {
|
|
Dialog,
|
|
Button,
|
|
Input: true,
|
|
TextArea: true,
|
|
Editor: true,
|
|
},
|
|
},
|
|
});
|
|
};
|
|
|
|
const detail = {
|
|
id: 3,
|
|
name: 'Refunds',
|
|
description: 'Handle refund requests',
|
|
instructions_md: 'Ask for the order number.',
|
|
status: 'active',
|
|
version: 4,
|
|
bound_assistant_count: 2,
|
|
references: [
|
|
{
|
|
reference_key: 'refund-policy',
|
|
content_md: 'Thirty days.',
|
|
position: 0,
|
|
},
|
|
],
|
|
};
|
|
|
|
describe('SkillDialog', () => {
|
|
beforeEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it('creates a validated draft with the management API shape', async () => {
|
|
const dispatch = vi.fn().mockResolvedValue({ id: 5 });
|
|
const wrapper = mountDialog({ dispatch });
|
|
Object.assign(wrapper.vm.form, {
|
|
name: ' Refunds ',
|
|
description: ' Handle refunds ',
|
|
instructions_md: 'Use the policy.',
|
|
});
|
|
|
|
await wrapper.vm.submit('draft');
|
|
|
|
expect(dispatch).toHaveBeenCalledWith('captainSkills/create', {
|
|
name: 'Refunds',
|
|
description: 'Handle refunds',
|
|
instructions_md: 'Use the policy.',
|
|
status: 'draft',
|
|
references: [],
|
|
});
|
|
expect(wrapper.emitted('saved')).toHaveLength(1);
|
|
});
|
|
|
|
it('publishes then binds to only the current assistant', async () => {
|
|
let resolveCreate;
|
|
const dispatch = vi.fn().mockReturnValueOnce(
|
|
new Promise(resolve => {
|
|
resolveCreate = resolve;
|
|
})
|
|
);
|
|
const wrapper = mountDialog({ dispatch });
|
|
Object.assign(wrapper.vm.form, {
|
|
name: 'Refunds',
|
|
description: 'Handle refunds',
|
|
instructions_md: 'Use the policy.',
|
|
});
|
|
|
|
const publish = wrapper.vm.submit('active', true);
|
|
await wrapper.setProps({ assistantId: 10 });
|
|
resolveCreate({ id: 5 });
|
|
await publish;
|
|
|
|
expect(dispatch).toHaveBeenLastCalledWith('captainSkills/bind', {
|
|
assistantId: 9,
|
|
skillId: 5,
|
|
});
|
|
});
|
|
|
|
it('warns before changing an active skill used by assistants', async () => {
|
|
const dispatch = vi.fn();
|
|
vi.spyOn(window, 'confirm').mockReturnValue(false);
|
|
const wrapper = mountDialog({ skill: detail, dispatch });
|
|
|
|
await wrapper.vm.submit('active');
|
|
|
|
expect(window.confirm).toHaveBeenCalledWith(
|
|
expect.stringContaining('2 assistants')
|
|
);
|
|
expect(dispatch).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('refreshes the saved skill when binding fails instead of creating a duplicate', async () => {
|
|
const dispatch = vi
|
|
.fn()
|
|
.mockResolvedValueOnce({ id: 5 })
|
|
.mockRejectedValueOnce({ response: { status: 409 } });
|
|
const wrapper = mountDialog({ dispatch });
|
|
Object.assign(wrapper.vm.form, {
|
|
name: 'Refunds',
|
|
description: 'Handle refunds',
|
|
instructions_md: 'Use the policy.',
|
|
});
|
|
|
|
await wrapper.vm.submit('active', true);
|
|
|
|
expect(wrapper.emitted('saved')).toHaveLength(1);
|
|
expect(dispatch).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it('keeps form input and reports a concurrent update', async () => {
|
|
const dispatch = vi.fn().mockRejectedValue({
|
|
response: { status: 409, data: { error: 'version conflict' } },
|
|
});
|
|
vi.spyOn(window, 'confirm').mockReturnValue(true);
|
|
const wrapper = mountDialog({ skill: detail, dispatch });
|
|
|
|
await wrapper.vm.submit('active');
|
|
|
|
expect(wrapper.vm.errorMessage).toContain('version conflict');
|
|
expect(wrapper.vm.form.name).toBe('Refunds');
|
|
expect(wrapper.emitted('saved')).toBeUndefined();
|
|
});
|
|
});
|