feat(handoff): 转人工状态、待接管列表与接待时段

同会话内 AI 转人工,待接管列表按分组和在线状态展示,接管后保留上下文。
This commit is contained in:
WangShen007 committed 2026-10-06 21:45:00 +08:00
1 parent d9fe727c3f
commit 8e3a4d95da
6 files changed
+803

No files matched your search

@@ -0,0 +1,124 @@
/*
Copyright 2024-2026 ReplyPier
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { mount, VueWrapper } from '@vue/test-utils'
import HumanAgentView from '../../views/HumanAgentView.vue'
import { createPinia, setActivePinia } from 'pinia'
// Mock the components
vi.mock('@/layouts/DashboardLayout.vue', () => ({
default: {
name: 'DashboardLayout',
template: '<div class="dashboard-layout"><slot /></div>'
}
}))
vi.mock('@/components/human-agent/UserList.vue', () => ({
default: {
name: 'UserList',
template: '<div class="user-list"></div>'
}
}))
vi.mock('@/components/human-agent/GroupList.vue', () => ({
default: {
name: 'GroupList',
template: '<div class="group-list"></div>'
}
}))
vi.mock('@/components/human-agent/RoleList.vue', () => ({
default: {
name: 'RoleList',
template: '<div class="role-list"></div>'
}
}))
describe('HumanAgentView', () => {
let wrapper: VueWrapper
beforeEach(() => {
setActivePinia(createPinia())
wrapper = mount(HumanAgentView)
})
it('renders properly', () => {
expect(wrapper.find('.human-agent-container').exists()).toBe(true)
expect(wrapper.find('.tab-pills').exists()).toBe(true)
expect(wrapper.find('.content-area').exists()).toBe(true)
})
it('displays all tab buttons', () => {
const tabButtons = wrapper.findAll('.tab-pill')
expect(tabButtons).toHaveLength(3)
// Labels may include an optional count badge, so match on the label text.
expect(tabButtons[0].text()).toContain('人员')
expect(tabButtons[1].text()).toContain('团队')
expect(tabButtons[2].text()).toContain('角色')
})
it('shows UserList component by default', () => {
expect(wrapper.findComponent({ name: 'UserList' }).exists()).toBe(true)
expect(wrapper.findComponent({ name: 'GroupList' }).exists()).toBe(false)
expect(wrapper.findComponent({ name: 'RoleList' }).exists()).toBe(false)
})
it('switches to GroupList when Groups tab is clicked', async () => {
const groupsTab = wrapper.findAll('.tab-pill')[1]
await groupsTab.trigger('click')
expect(wrapper.findComponent({ name: 'UserList' }).exists()).toBe(false)
expect(wrapper.findComponent({ name: 'GroupList' }).exists()).toBe(true)
expect(wrapper.findComponent({ name: 'RoleList' }).exists()).toBe(false)
})
it('switches to RoleList when Roles tab is clicked', async () => {
const rolesTab = wrapper.findAll('.tab-pill')[2]
await rolesTab.trigger('click')
expect(wrapper.findComponent({ name: 'UserList' }).exists()).toBe(false)
expect(wrapper.findComponent({ name: 'GroupList' }).exists()).toBe(false)
expect(wrapper.findComponent({ name: 'RoleList' }).exists()).toBe(true)
})
it('applies active class to selected tab button', async () => {
// Initially Users tab should be active
let tabButtons = wrapper.findAll('.tab-pill')
expect(tabButtons[0].classes()).toContain('active')
expect(tabButtons[1].classes()).not.toContain('active')
expect(tabButtons[2].classes()).not.toContain('active')
// Click Groups tab
await tabButtons[1].trigger('click')
tabButtons = wrapper.findAll('.tab-pill')
expect(tabButtons[0].classes()).not.toContain('active')
expect(tabButtons[1].classes()).toContain('active')
expect(tabButtons[2].classes()).not.toContain('active')
// Click Roles tab
await tabButtons[2].trigger('click')
tabButtons = wrapper.findAll('.tab-pill')
expect(tabButtons[0].classes()).not.toContain('active')
expect(tabButtons[1].classes()).not.toContain('active')
expect(tabButtons[2].classes()).toContain('active')
})
it('is wrapped in DashboardLayout', () => {
const dashboardLayout = wrapper.findComponent({ name: 'DashboardLayout' })
expect(dashboardLayout.exists()).toBe(true)
})
})