Files
apes-sidebar/tests/editor-load.spec.ts
T
Menghuan1918 0467101faf feat(editor): 拆分 EditorHost/TextEditor — code/markdown 成为注册 viewer,fs.read 增加 head 字节
- 新 src/client/EditorHost.tsx:'editor' tab 组件。matchFileViewer →
  按 fetchStrategy 分发(纯逻辑在 editor-load.ts)→ 渲染 viewer.component;
  binary 无匹配时渲染共享 BinaryDownload。标题栏保留在 host
- 新 src/client/TextEditor.tsx:code/markdown 的 viewer 组件(CodeMirror +
  保存/脏点/截断横幅 + md 预览/编辑切换),内容由 host 经 fsRead 传入,
  自身不再 fetch;按 viewerId === 'markdown' 分支(不再解析扩展名)
- 新 src/client/editor-load.ts:planFirstMatch / planFsReadOutcome /
  decodeHead 纯函数,策略分发可单测
- 新 src/client/binary-download.tsx:共享下载兜底组件(viewer 注册与
  host fallback 同一 UI)
- host src/index.ts:fs.read 二进制响应增加 head 字段(base64,前 4KB,
  唯一 host 触碰,设计文档 detect 机制所需);客户端 FsBinaryResult 同步
- builtins:注册 8 个 viewer(新增 markdown exts md/markdown、code
  catch-all -100);binary-download 增加 NUL detect + 复用 BinaryDownload
- 删除 src/client/EditorView.tsx(职责一分为二);陈旧注释同步
- 新增 tests/editor-load.spec.ts(12 用例)
2026-08-11 11:16:36 +08:00

101 lines
3.8 KiB
TypeScript

/**
* Tests for the editor-load planning: the pure strategy dispatch the
* editor host runs (planFirstMatch / planFsReadOutcome / decodeHead).
*/
import { describe, expect, it } from 'vitest'
import { decodeHead, planFirstMatch, planFsReadOutcome } from '../src/client/editor-load.ts'
import type { FileViewerDescriptor } from '../src/client/service.ts'
const viewer = (over: Partial<FileViewerDescriptor>): FileViewerDescriptor => ({
id: 'v',
exts: [],
fetchStrategy: 'fsRead',
component: () => null,
...over,
})
describe('planFirstMatch', () => {
it('no viewer → binary (download UI)', () => {
expect(planFirstMatch(undefined, () => '/m')).toEqual({ kind: 'binary' })
})
it('binary-download strategy → binary', () => {
const v = viewer({ id: 'dl', fetchStrategy: 'binary-download' })
expect(planFirstMatch(v, () => '/m')).toEqual({ kind: 'binary' })
})
it('mediaUrl / none → render with the media URL', () => {
const v = viewer({ id: 'img', fetchStrategy: 'mediaUrl' })
expect(planFirstMatch(v, () => '/media')).toEqual({ kind: 'render', viewer: v, mediaUrl: '/media' })
expect(planFirstMatch(viewer({ id: 'none', fetchStrategy: 'none' }), () => '/m').kind).toBe('render')
})
it('custom → customLoad (the viewer loads its own bytes)', () => {
const v = viewer({ id: 'csv', fetchStrategy: 'custom', load: async () => [] })
expect(planFirstMatch(v, () => '/m')).toEqual({ kind: 'customLoad', viewer: v })
})
it('fsRead → fetchFsRead', () => {
const v = viewer({ id: 'code', fetchStrategy: 'fsRead' })
expect(planFirstMatch(v, () => '/m')).toEqual({ kind: 'fetchFsRead', viewer: v })
})
})
describe('planFsReadOutcome', () => {
const rematch = (head: Uint8Array): FileViewerDescriptor | undefined => {
// The builtin binary-download NUL probe shape.
if (head.includes(0)) return viewer({ id: 'binary-download', fetchStrategy: 'binary-download' })
return undefined
}
it('text result renders with content, same viewer', () => {
const v = viewer({ id: 'code' })
const action = planFsReadOutcome(v, { binary: false, content: 'hello', truncated: true }, rematch, () => '/m')
expect(action).toEqual({ kind: 'render', viewer: v, content: 'hello', truncated: true })
})
it('binary without head bytes → binary', () => {
expect(planFsReadOutcome(viewer({}), { binary: true, content: '', truncated: false }, rematch, () => '/m')).toEqual({ kind: 'binary' })
})
it('binary whose NUL head re-matches nothing renderable → binary', () => {
const action = planFsReadOutcome(
viewer({}),
{ binary: true, content: '', truncated: false, head: 'AAAAAA==' }, // 0x00 bytes
rematch,
() => '/m',
)
// binary-download claims it — still the download UI.
expect(action).toEqual({ kind: 'binary' })
})
it('binary re-matched to a custom viewer → customLoad (external sniffer)', () => {
const sniffer = viewer({ id: 'my:raw', fetchStrategy: 'custom', load: async () => ({}) })
const action = planFsReadOutcome(
viewer({}),
{ binary: true, content: '', truncated: false, head: 'AAAAAA==' },
() => sniffer,
() => '/m',
)
expect(action).toEqual({ kind: 'customLoad', viewer: sniffer })
})
it('binary re-matched to a mediaUrl viewer → render with the media URL', () => {
const media = viewer({ id: 'my:binimg', fetchStrategy: 'mediaUrl' })
const action = planFsReadOutcome(
viewer({}),
{ binary: true, content: '', truncated: false, head: 'AAAAAA==' },
() => media,
() => '/m?x=1',
)
expect(action).toEqual({ kind: 'render', viewer: media, mediaUrl: '/m?x=1' })
})
})
describe('decodeHead', () => {
it('decodes base64 to the exact bytes', () => {
const head = decodeHead('AP//AA==')
expect(Array.from(head)).toEqual([0x00, 0xff, 0xff, 0x00])
})
})