194 lines
7.2 KiB
TypeScript
194 lines
7.2 KiB
TypeScript
// 文件树回归:子文件夹展开、空文件夹提示、文件夹右键新建文档/文件夹。
|
|
import { describe, it, expect, vi } from 'vitest'
|
|
import { createRoot } from 'react-dom/client'
|
|
import { act } from 'react-dom/test-utils'
|
|
|
|
vi.mock('../src/services/platform', async (importOriginal) => {
|
|
const orig = await importOriginal<typeof import('../src/services/platform')>()
|
|
return {
|
|
...orig,
|
|
listDir: async (dir: string) => {
|
|
if (dir === 'C:/ws') {
|
|
return [
|
|
{ name: 'sub', path: 'C:/ws/sub', isDir: true },
|
|
{ name: 'empty', path: 'C:/ws/empty', isDir: true },
|
|
{ name: 'a.md', path: 'C:/ws/a.md', isDir: false },
|
|
]
|
|
}
|
|
if (dir === 'C:/ws/sub') return [{ name: 'b.md', path: 'C:/ws/sub/b.md', isDir: false }]
|
|
return []
|
|
},
|
|
}
|
|
})
|
|
|
|
import Sidebar from '../src/components/Sidebar'
|
|
|
|
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))
|
|
|
|
async function renderSidebar(extra: Record<string, unknown> = {}) {
|
|
const container = document.createElement('div')
|
|
document.body.appendChild(container)
|
|
const root = createRoot(container)
|
|
await act(async () => {
|
|
root.render(
|
|
<Sidebar
|
|
mode="files"
|
|
onModeChange={() => undefined}
|
|
workspaceRoot="C:/ws"
|
|
activePath={null}
|
|
onOpenFile={() => undefined}
|
|
onOpenFolder={() => undefined}
|
|
outline={[]}
|
|
onJumpHeading={() => undefined}
|
|
tauriMode
|
|
{...extra}
|
|
/>,
|
|
)
|
|
})
|
|
await act(async () => { await sleep(30) })
|
|
return { container, root }
|
|
}
|
|
|
|
function findButton(container: HTMLElement, text: string) {
|
|
return [...container.querySelectorAll('button')].find((b) => b.textContent?.includes(text))
|
|
}
|
|
|
|
describe('文件树', () => {
|
|
it('子文件夹点击展开能看到下级文件', async () => {
|
|
const { container, root } = await renderSidebar()
|
|
await act(async () => {
|
|
findButton(container, 'sub')?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
|
})
|
|
await act(async () => { await sleep(30) })
|
|
expect(container.innerHTML).toContain('b.md')
|
|
await act(async () => { root.unmount() })
|
|
container.remove()
|
|
})
|
|
|
|
it('空文件夹展开后有(空)提示而不是空白', async () => {
|
|
const { container, root } = await renderSidebar()
|
|
await act(async () => {
|
|
findButton(container, 'empty')?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
|
})
|
|
await act(async () => { await sleep(30) })
|
|
expect(container.innerHTML).toContain('(空)')
|
|
await act(async () => { root.unmount() })
|
|
container.remove()
|
|
})
|
|
|
|
it('文件夹右键菜单有新建文档/文件夹并回传该文件夹', async () => {
|
|
const created: string[] = []
|
|
const folders: string[] = []
|
|
const { container, root } = await renderSidebar({
|
|
onCreateFileIn: (dir: string) => created.push(dir),
|
|
onCreateFolderIn: (dir: string) => folders.push(dir),
|
|
})
|
|
await act(async () => {
|
|
findButton(container, 'sub')?.dispatchEvent(new MouseEvent('contextmenu', { bubbles: true }))
|
|
})
|
|
await act(async () => { await sleep(10) })
|
|
const fileItem = findButton(container, '在此新建文档')
|
|
const folderItem = findButton(container, '在此新建文件夹')
|
|
expect(fileItem).toBeTruthy()
|
|
expect(folderItem).toBeTruthy()
|
|
await act(async () => {
|
|
fileItem?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
|
})
|
|
expect(created).toEqual(['C:/ws/sub'])
|
|
await act(async () => { root.unmount() })
|
|
container.remove()
|
|
})
|
|
})
|
|
|
|
describe('文件树拖拽移动', () => {
|
|
const fireDrag = async (el: Element | undefined | null, type: string) => {
|
|
if (!el) throw new Error(`元素不存在,无法派发 ${type}`)
|
|
const e = new Event(type, { bubbles: true, cancelable: true }) as Event & { dataTransfer: Record<string, unknown> }
|
|
e.dataTransfer = { effectAllowed: '', dropEffect: '', setData: () => undefined, getData: () => '' }
|
|
await act(async () => { el.dispatchEvent(e) })
|
|
}
|
|
|
|
it('把文件拖到文件夹上触发移动回调', async () => {
|
|
const moves: { path: string; dest: string }[] = []
|
|
const { container, root } = await renderSidebar({
|
|
onMoveEntry: (entry: { path: string }, dest: string) => moves.push({ path: entry.path, dest }),
|
|
})
|
|
const file = findButton(container, 'a.md')
|
|
const folder = findButton(container, 'sub')
|
|
await fireDrag(file, 'dragstart')
|
|
await fireDrag(folder, 'dragover')
|
|
expect(folder?.className).toContain('drop-target')
|
|
await fireDrag(folder, 'drop')
|
|
expect(moves).toEqual([{ path: 'C:/ws/a.md', dest: 'C:/ws/sub' }])
|
|
await act(async () => { root.unmount() })
|
|
container.remove()
|
|
})
|
|
|
|
it('拖回原文件夹(根目录)不触发移动', async () => {
|
|
const moves: unknown[] = []
|
|
const { container, root } = await renderSidebar({
|
|
onMoveEntry: (...args: unknown[]) => moves.push(args),
|
|
})
|
|
const file = findButton(container, 'a.md')
|
|
const head = container.querySelector('.filetree-head')
|
|
await fireDrag(file, 'dragstart')
|
|
await fireDrag(head, 'dragover')
|
|
await fireDrag(head, 'drop')
|
|
expect(moves).toEqual([])
|
|
await act(async () => { root.unmount() })
|
|
container.remove()
|
|
})
|
|
|
|
it('子文件夹里的文件可以拖回根目录', async () => {
|
|
const moves: { path: string; dest: string }[] = []
|
|
const { container, root } = await renderSidebar({
|
|
onMoveEntry: (entry: { path: string }, dest: string) => moves.push({ path: entry.path, dest }),
|
|
})
|
|
await act(async () => {
|
|
findButton(container, 'sub')?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
|
})
|
|
await act(async () => { await sleep(30) })
|
|
const inner = findButton(container, 'b.md')
|
|
const head = container.querySelector('.filetree-head')
|
|
await fireDrag(inner, 'dragstart')
|
|
await fireDrag(head, 'dragover')
|
|
await fireDrag(head, 'drop')
|
|
expect(moves).toEqual([{ path: 'C:/ws/sub/b.md', dest: 'C:/ws' }])
|
|
await act(async () => { root.unmount() })
|
|
container.remove()
|
|
})
|
|
|
|
it('文件夹拖到自己身上不触发移动', async () => {
|
|
const moves: unknown[] = []
|
|
const { container, root } = await renderSidebar({
|
|
onMoveEntry: (...args: unknown[]) => moves.push(args),
|
|
})
|
|
const folder = findButton(container, 'sub')
|
|
await fireDrag(folder, 'dragstart')
|
|
await fireDrag(folder, 'dragover')
|
|
await fireDrag(folder, 'drop')
|
|
expect(moves).toEqual([])
|
|
await act(async () => { root.unmount() })
|
|
container.remove()
|
|
})
|
|
|
|
it('右键菜单含「打开所在位置」并回传条目', async () => {
|
|
const revealed: string[] = []
|
|
const { container, root } = await renderSidebar({
|
|
onRevealEntry: (entry: { path: string }) => revealed.push(entry.path),
|
|
})
|
|
await act(async () => {
|
|
findButton(container, 'a.md')?.dispatchEvent(new MouseEvent('contextmenu', { bubbles: true }))
|
|
})
|
|
await act(async () => { await sleep(10) })
|
|
const item = findButton(container, '打开所在位置')
|
|
expect(item).toBeTruthy()
|
|
await act(async () => {
|
|
item?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
|
})
|
|
expect(revealed).toEqual(['C:/ws/a.md'])
|
|
await act(async () => { root.unmount() })
|
|
container.remove()
|
|
})
|
|
})
|