Phase 006: migrate the chat page from naive-ui to Arco Design Vue
Swaps the rendering layer only. Chat store logic, keyboard handling, and the /chat-run Socket.IO contract are untouched, so a mixed UI cannot hide a chat regression behind a library swap. Arco's dropdowns take their items from a #content slot of a-doption children rather than a droplist prop, and Arco Triggers cannot be nested, so the two toolbar menus lost their a-tooltip in favour of a native title. Both rules are recorded in docs/arco-mapping.md, and tests/e2e/chat-input-dropdown.spec.ts plus tests/e2e/chat-rendering-smoke.spec.ts keep the menus, the streaming markdown rendering, and the no-Arco-inside-the-bubble red line from regressing silently.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// @vitest-environment jsdom
|
||||
import { nextTick, defineComponent, h } from 'vue'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { config, mount } from '@vue/test-utils'
|
||||
|
||||
const apiMocks = vi.hoisted(() => ({
|
||||
fetchSessionsMock: vi.fn(),
|
||||
@@ -40,31 +40,32 @@ vi.mock('vue-i18n', () => ({
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('naive-ui', async () => {
|
||||
const actual = await vi.importActual<any>('naive-ui')
|
||||
return {
|
||||
...actual,
|
||||
useMessage: () => ({
|
||||
error: vi.fn(),
|
||||
}),
|
||||
NModal: {
|
||||
props: ['show'],
|
||||
emits: ['update:show'],
|
||||
template: '<div v-if="show" class="n-modal-stub"><slot /></div>',
|
||||
},
|
||||
NInput: {
|
||||
props: ['value', 'size'],
|
||||
emits: ['update:value', 'keydown'],
|
||||
template: '<input class="n-input-stub" :value="value" @input="$emit(\'update:value\', $event.target.value)" @keydown="$emit(\'keydown\', $event)" />',
|
||||
},
|
||||
NSpin: {
|
||||
template: '<div class="n-spin-stub"><slot /></div>',
|
||||
},
|
||||
NButton: {
|
||||
template: '<button class="n-button-stub"><slot /></button>',
|
||||
},
|
||||
}
|
||||
})
|
||||
// The search modal teleports to body in jsdom, so stub the overlays to render
|
||||
// inline and keep the DOM shape the assertions below rely on.
|
||||
config.global.stubs = {
|
||||
AModal: {
|
||||
name: 'AModal',
|
||||
props: ['visible'],
|
||||
emits: ['update:visible'],
|
||||
template: '<div v-if="visible" class="a-modal-stub"><slot /></div>',
|
||||
},
|
||||
AInput: {
|
||||
name: 'AInput',
|
||||
// `size` is an Arco prop, not an HTML input attribute; declaring it keeps
|
||||
// it off the native element.
|
||||
props: ['modelValue', 'size'],
|
||||
emits: ['update:modelValue'],
|
||||
template: '<input class="a-input-stub" :value="modelValue" @input="$emit(\'update:modelValue\', $event.target.value)" />',
|
||||
},
|
||||
ASpin: {
|
||||
name: 'ASpin',
|
||||
template: '<div class="a-spin-stub"><slot /></div>',
|
||||
},
|
||||
AButton: {
|
||||
name: 'AButton',
|
||||
template: '<button class="a-button-stub"><slot /></button>',
|
||||
},
|
||||
}
|
||||
|
||||
import SessionSearchModal from '@/components/hermes/chat/SessionSearchModal.vue'
|
||||
import { useSessionSearch } from '@/composables/useSessionSearch'
|
||||
@@ -167,7 +168,7 @@ describe('session search modal', () => {
|
||||
await flushPromises()
|
||||
await nextTick()
|
||||
|
||||
const input = wrapper.find('input.n-input-stub')
|
||||
const input = wrapper.find('input.a-input-stub')
|
||||
await input.setValue('docker')
|
||||
await vi.advanceTimersByTimeAsync(200)
|
||||
await flushPromises()
|
||||
|
||||
Reference in New Issue
Block a user