mirror of
https://github.com/EKKOLearnAI/hermes-web-ui.git
synced 2026-07-10 02:30:15 +00:00
899520c1b8
* Add profile config edit affordance * fix: focus profile config editor route --------- Co-authored-by: ekko <fqsy1416@gmail.com>
127 lines
4.0 KiB
TypeScript
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',
|
|
})
|
|
})
|
|
})
|