Files
hermes-web-ui/tests/client/files-store.test.ts
Zhicheng Han 899520c1b8 新增 profile 配置编辑入口 (#1860)
* Add profile config edit affordance

* fix: focus profile config editor route

---------

Co-authored-by: ekko <fqsy1416@gmail.com>
2026-06-30 20:02:24 +08:00

127 lines
4.0 KiB
TypeScript

// @vitest-environment jsdom
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createPinia, setActivePinia } from 'pinia'
const mockFilesApi = vi.hoisted(() => ({
listFiles: vi.fn(),
readFile: vi.fn(),
writeFile: vi.fn(),
deleteFile: vi.fn(),
renameFile: vi.fn(),
mkDir: vi.fn(),
copyFile: vi.fn(),
uploadFiles: vi.fn(),
}))
vi.mock('@/api/hermes/files', () => mockFilesApi)
import { getLanguageFromPath, isPreviewableFile, isTextFile, useFilesStore } from '@/stores/hermes/files'
import type { FileEntry } from '@/api/hermes/files'
describe('files store', () => {
beforeEach(() => {
setActivePinia(createPinia())
vi.clearAllMocks()
})
it('detects special workspace filenames and extensionless text files', () => {
expect(getLanguageFromPath('Dockerfile')).toBe('dockerfile')
expect(getLanguageFromPath('Makefile')).toBe('makefile')
expect(getLanguageFromPath('CMakeLists.txt')).toBe('cmake')
expect(getLanguageFromPath('.gitignore')).toBe('gitignore')
expect(getLanguageFromPath('.dockerignore')).toBe('gitignore')
expect(getLanguageFromPath('README')).toBe('plaintext')
expect(isTextFile('README')).toBe(true)
expect(isTextFile('LICENSE')).toBe(true)
expect(isTextFile('.env.local')).toBe(true)
expect(isTextFile('script.ts')).toBe(true)
expect(isTextFile('unknown-extensionless-binary')).toBe(false)
expect(isPreviewableFile('README')).toBe(true)
expect(isPreviewableFile('archive.zip')).toBe(false)
expect(isPreviewableFile('font.woff2')).toBe(false)
expect(isPreviewableFile('module.wasm')).toBe(false)
})
it('opens text previews with detected syntax language', async () => {
mockFilesApi.readFile.mockResolvedValue({
content: 'FROM node:20\nRUN npm test\n',
path: 'Dockerfile',
size: 27,
})
const store = useFilesStore()
const entry: FileEntry = {
name: 'Dockerfile',
path: 'Dockerfile',
isDir: false,
size: 27,
modTime: '2026-06-02T00:00:00.000Z',
}
await store.openPreview(entry)
expect(mockFilesApi.readFile).toHaveBeenCalledWith('Dockerfile', null)
expect(store.previewFile).toEqual({
path: 'Dockerfile',
profile: null,
type: 'text',
content: 'FROM node:20\nRUN npm test\n',
language: 'dockerfile',
})
})
it('resets profile scope for unscoped file panels', async () => {
mockFilesApi.listFiles.mockResolvedValue({ entries: [], path: '' })
const store = useFilesStore()
await store.fetchEntries('', { profile: 'reviewer' })
await store.fetchEntries('', { profile: null })
expect(store.currentProfile).toBeNull()
expect(mockFilesApi.listFiles).toHaveBeenLastCalledWith('', null)
})
it('keeps an explicit profile scope for config editor actions', async () => {
mockFilesApi.listFiles.mockResolvedValue({ entries: [], path: '' })
mockFilesApi.readFile.mockResolvedValue({
content: 'model:\n default: gpt-5.4\n',
path: 'config.yaml',
size: 28,
})
const store = useFilesStore()
await store.fetchEntries('', { profile: 'reviewer' })
await store.openEditor('config.yaml')
store.editingFile!.content = 'model:\n default: gpt-5.4-mini\n'
await store.saveEditor()
expect(store.currentProfile).toBe('reviewer')
expect(mockFilesApi.listFiles).toHaveBeenCalledWith('', 'reviewer')
expect(mockFilesApi.readFile).toHaveBeenCalledWith('config.yaml', 'reviewer')
expect(mockFilesApi.writeFile).toHaveBeenCalledWith('config.yaml', 'model:\n default: gpt-5.4-mini\n', 'reviewer')
})
it('opens image previews without reading file contents', async () => {
const store = useFilesStore()
const entry: FileEntry = {
name: 'diagram.png',
path: 'diagram.png',
isDir: false,
size: 128,
modTime: '2026-06-02T00:00:00.000Z',
}
await store.openPreview(entry)
expect(mockFilesApi.readFile).not.toHaveBeenCalled()
expect(store.previewFile).toEqual({
path: 'diagram.png',
profile: null,
type: 'image',
})
})
})