The project now has exactly one UI library. Every naive-ui component in
packages/client/src is replaced by its Arco equivalent across 24 views and
roughly 80 components, and naive-ui is dropped from package.json along with
its transitive vueuc / @css-render / vdirs / seemly dependencies.
The migration is rendering-layer only. No store logic, no API contract, and
no /chat-run payload was touched.
Three classes of leftover were invisible to `grep naive-ui` and are now
covered by tests/client/no-naive-ui.test.ts, which asserts all three:
- dead .n-* class names in style blocks
- dead --n-* custom properties
- naive-ui in package.json
Notable behavioural traps handled along the way, recorded in
docs/arco-migration-rules.md:
- a-input-password defaults to masked; passing default-visibility="false"
would have exposed API keys in clear text
- a-upload fires a real XHR unless auto-upload is false
- nested Trigger components fight over the first element child, so the
group-chat settings dropdown was flattened
- Arco renders tooltip default-slot text inline, which was silently
collapsing the chat and group-chat toolbars
54 lines
2.2 KiB
TypeScript
54 lines
2.2 KiB
TypeScript
import { readFileSync } from 'fs'
|
||
import { describe, expect, it } from 'vitest'
|
||
|
||
const readClientFile = (path: string) => readFileSync(`packages/client/src/${path}`, 'utf8')
|
||
|
||
describe('client style system', () => {
|
||
it('keeps shared page headers on one layout baseline', () => {
|
||
const globalStyles = readClientFile('styles/global.scss')
|
||
|
||
expect(globalStyles).toContain('min-height: 64px;')
|
||
expect(globalStyles).toContain('padding: 14px 20px;')
|
||
expect(globalStyles).toContain('.page-header > .header-actions')
|
||
})
|
||
|
||
it('aligns SCSS surfaces and radii with the shared theme tokens', () => {
|
||
const variables = readClientFile('styles/variables.scss')
|
||
|
||
expect(variables).toContain('--bg-card: #ffffff;')
|
||
// 深色主题必须重新定义 card surface,否则浅色值会漏到深色模式
|
||
expect(variables).toContain('--bg-card: #2a2a2a;')
|
||
expect(variables).toContain('$radius-sm: 6px;')
|
||
expect(variables).toContain('$radius-md: 8px;')
|
||
expect(variables).toContain('$radius-lg: 8px;')
|
||
})
|
||
|
||
it('exports the frozen Arco theme tokens', async () => {
|
||
const { arcoTheme } = await import('../../packages/client/src/styles/arco-theme')
|
||
|
||
expect(arcoTheme.colorPrimary).toBeTruthy()
|
||
expect(arcoTheme.colorBgLayout).toBeTruthy()
|
||
expect(arcoTheme.colorBgContainer).toBeTruthy()
|
||
expect(arcoTheme.borderRadius).toBeTypeOf('number')
|
||
expect(arcoTheme.fontFamily).toContain('Inter')
|
||
expect(arcoTheme.fontFamilyMono).toContain('JetBrains Mono')
|
||
})
|
||
|
||
it('carries dark mode through the arco-theme attribute on the root element', () => {
|
||
// 不 import useTheme:它在模块顶层读 localStorage,node 环境下会抛。
|
||
// 断言源码契约即可,真实行为由 useTheme 自己的测试覆盖。
|
||
const source = readClientFile('composables/useTheme.ts')
|
||
|
||
expect(source).toContain("setAttribute('arco-theme'")
|
||
expect(source).toMatch(/'dark' : 'light'|'dark'|'light'/)
|
||
})
|
||
|
||
it('keeps mobile inputs at 16px so iOS does not zoom on focus', () => {
|
||
const globalStyles = readClientFile('styles/global.scss')
|
||
|
||
// Arco 的输入框类名,不是 naive 的 .n-input__*
|
||
expect(globalStyles).toContain('.arco-input')
|
||
expect(globalStyles).not.toMatch(/\.n-input/)
|
||
})
|
||
})
|