initialize Electron desktop app and remove unused files
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
// @vitest-environment jsdom
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import { defineComponent } from 'vue'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import ArcoVue from '@arco-design/web-vue'
|
||||
import type { FileEntry } from '@/api/hermes/files'
|
||||
|
||||
const mockMessage = vi.hoisted(() => ({
|
||||
error: vi.fn(),
|
||||
success: vi.fn(),
|
||||
}))
|
||||
|
||||
const mockDialog = vi.hoisted(() => ({
|
||||
warning: vi.fn(),
|
||||
}))
|
||||
|
||||
const downloadFileMock = vi.hoisted(() => vi.fn())
|
||||
const copyToClipboardMock = vi.hoisted(() => vi.fn())
|
||||
|
||||
vi.mock('vue-i18n', () => ({
|
||||
useI18n: () => ({
|
||||
t: (key: string) => key,
|
||||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/api/hermes/download', () => ({
|
||||
downloadFile: downloadFileMock,
|
||||
}))
|
||||
|
||||
vi.mock('@/utils/clipboard', () => ({
|
||||
copyToClipboard: copyToClipboardMock,
|
||||
}))
|
||||
|
||||
vi.mock('@/utils/file-path', () => ({
|
||||
getClipboardPathForEntry: (entry: FileEntry) => entry.path,
|
||||
}))
|
||||
|
||||
// Arco's imperative Message/Dialog APIs need a live DOM, so mock the adapters.
|
||||
vi.mock('@/composables/useMessage', () => ({ useMessage: () => mockMessage }))
|
||||
vi.mock('@/composables/useDialog', () => ({ useDialog: () => mockDialog }))
|
||||
|
||||
/**
|
||||
* Arco's a-trigger teleports its popup to body in jsdom and a-doption is a
|
||||
* popup-scoped component, so both are stubbed under Arco's *internal* component
|
||||
* names — stubs key off those, not the `a-*` template tags, because Arco is
|
||||
* registered globally via `app.use(ArcoVue)`.
|
||||
*
|
||||
* The stub renders the #content slot inline and lets clicks bubble from the
|
||||
* option element, so a click behaves like the real Doption -> handleSelect path.
|
||||
*/
|
||||
const ATriggerStub = defineComponent({
|
||||
name: 'Trigger',
|
||||
props: ['popupVisible'],
|
||||
template: '<div class="arco-trigger-stub"><slot /><slot name="content" /></div>',
|
||||
})
|
||||
|
||||
const ADoptionStub = defineComponent({
|
||||
name: 'Doption',
|
||||
emits: ['click'],
|
||||
template: '<div class="arco-doption-stub" @click="$emit(\'click\')"><slot /></div>',
|
||||
})
|
||||
|
||||
const arcoGlobal = {
|
||||
plugins: [ArcoVue],
|
||||
stubs: { Trigger: ATriggerStub, Doption: ADoptionStub },
|
||||
}
|
||||
|
||||
/** Menu item keys, read from the rendered popup rather than a stub attribute. */
|
||||
function menuKeys(wrapper: ReturnType<typeof mount>): string[] {
|
||||
return wrapper.findAll('.file-context-menu > *').map(node => node.text().trim())
|
||||
}
|
||||
|
||||
import FileContextMenu from '@/components/hermes/files/FileContextMenu.vue'
|
||||
import { useFilesStore } from '@/stores/hermes/files'
|
||||
|
||||
describe('FileContextMenu', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia())
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
async function showMenu(wrapper: ReturnType<typeof mount>, entry: FileEntry) {
|
||||
;(wrapper.vm as any).show(new MouseEvent('contextmenu', { clientX: 12, clientY: 34 }), entry)
|
||||
await flushPromises()
|
||||
}
|
||||
|
||||
function menuItem(wrapper: ReturnType<typeof mount>, label: string) {
|
||||
const item = wrapper.findAll('.file-context-menu > *').find(node => node.text().trim() === label)
|
||||
if (!item) throw new Error(`menu item not found: ${label}`)
|
||||
return item
|
||||
}
|
||||
|
||||
it('offers both edit and preview for previewable text files', async () => {
|
||||
const wrapper = mount(FileContextMenu, { global: arcoGlobal })
|
||||
const entry: FileEntry = {
|
||||
name: 'Dockerfile',
|
||||
path: 'Dockerfile',
|
||||
isDir: false,
|
||||
size: 42,
|
||||
modTime: '2026-06-02T00:00:00.000Z',
|
||||
}
|
||||
|
||||
await showMenu(wrapper, entry)
|
||||
|
||||
const labels = menuKeys(wrapper)
|
||||
expect(labels).toContain('files.edit')
|
||||
expect(labels).toContain('files.preview')
|
||||
expect(labels).toContain('files.download')
|
||||
})
|
||||
|
||||
it('invokes the store preview action from the preview menu item', async () => {
|
||||
const store = useFilesStore()
|
||||
const openPreviewSpy = vi.spyOn(store, 'openPreview').mockResolvedValue(undefined)
|
||||
const wrapper = mount(FileContextMenu, { global: arcoGlobal })
|
||||
const entry: FileEntry = {
|
||||
name: 'README',
|
||||
path: 'README',
|
||||
isDir: false,
|
||||
size: 12,
|
||||
modTime: '2026-06-02T00:00:00.000Z',
|
||||
}
|
||||
|
||||
await showMenu(wrapper, entry)
|
||||
await menuItem(wrapper, 'files.preview').trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(openPreviewSpy).toHaveBeenCalledWith(entry)
|
||||
expect(mockMessage.error).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('does not offer preview for non-previewable binary files', async () => {
|
||||
const wrapper = mount(FileContextMenu, { global: arcoGlobal })
|
||||
const entry: FileEntry = {
|
||||
name: 'archive.zip',
|
||||
path: 'archive.zip',
|
||||
isDir: false,
|
||||
size: 128,
|
||||
modTime: '2026-06-02T00:00:00.000Z',
|
||||
}
|
||||
|
||||
await showMenu(wrapper, entry)
|
||||
|
||||
const labels = menuKeys(wrapper)
|
||||
expect(labels).not.toContain('files.preview')
|
||||
expect(labels).not.toContain('files.edit')
|
||||
expect(labels).toContain('files.download')
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user