mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 16:02:32 +00:00
316 lines
11 KiB
TypeScript
316 lines
11 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import type { OpenFile } from '@/store/slices/editor'
|
|
import { RICH_MARKDOWN_MAX_SIZE_BYTES } from '../../../../shared/constants'
|
|
import type { GitStatusEntry } from '../../../../shared/git-status-types'
|
|
import type { FileContent } from './editor-panel-content-types'
|
|
import { getEditorPanelRenderModel } from './editor-panel-render-model'
|
|
|
|
function markdownFile(overrides: Partial<OpenFile> = {}): OpenFile {
|
|
return {
|
|
id: '/repo/README.md',
|
|
filePath: '/repo/README.md',
|
|
relativePath: 'README.md',
|
|
worktreeId: 'wt-1',
|
|
language: 'markdown',
|
|
mode: 'edit',
|
|
isDirty: false,
|
|
...overrides
|
|
}
|
|
}
|
|
|
|
function textContent(overrides: Partial<FileContent> = {}): FileContent {
|
|
return {
|
|
content: '# Hello',
|
|
isBinary: false,
|
|
...overrides
|
|
}
|
|
}
|
|
|
|
function renderModel(args: {
|
|
activeFile?: OpenFile
|
|
fileContents?: Record<string, FileContent>
|
|
editorDrafts?: Record<string, string>
|
|
markdownViewMode?: Record<string, 'source' | 'rich' | 'preview'>
|
|
markdownRichModeSizeOverridden?: boolean
|
|
markdownRichModeFaultedContent?: Record<string, string>
|
|
isChangesMode?: boolean
|
|
gitStatusByWorktree?: Record<string, GitStatusEntry[]>
|
|
}) {
|
|
const activeFile = args.activeFile ?? markdownFile()
|
|
return getEditorPanelRenderModel({
|
|
activeFile,
|
|
fileContents: args.fileContents ?? { '/repo/README.md': textContent() },
|
|
editorDrafts: args.editorDrafts ?? {},
|
|
gitStatusEntries: args.gitStatusByWorktree?.[activeFile.worktreeId],
|
|
gitBranchEntries: undefined,
|
|
markdownViewMode: args.markdownViewMode ?? {},
|
|
markdownRichModeSizeOverridden: args.markdownRichModeSizeOverridden ?? false,
|
|
markdownRichModeFaultedContent: args.markdownRichModeFaultedContent ?? {},
|
|
isChangesMode: args.isChangesMode ?? false,
|
|
canOpenWorkspaceFileBrowser: true
|
|
})
|
|
}
|
|
|
|
function htmlFile(overrides: Partial<OpenFile> = {}): OpenFile {
|
|
return {
|
|
id: '/repo/mock.html',
|
|
filePath: '/repo/mock.html',
|
|
relativePath: 'mock.html',
|
|
worktreeId: 'wt-1',
|
|
language: 'html',
|
|
mode: 'edit',
|
|
isDirty: false,
|
|
...overrides
|
|
}
|
|
}
|
|
|
|
describe('getEditorPanelRenderModel rich-mode fallback toggle', () => {
|
|
it('omits Preview in Source view until a fault is recorded for this content, even though it would fault', () => {
|
|
const model = renderModel({
|
|
markdownViewMode: { '/repo/README.md': 'source' },
|
|
editorDrafts: { '/repo/README.md': '[reference]: https://example.com' }
|
|
})
|
|
|
|
expect(model.availableEditorToggleModes).toEqual(['source', 'rich', 'changes'])
|
|
})
|
|
|
|
it('offers Preview in Source view once a stored fault matches the current content', () => {
|
|
const model = renderModel({
|
|
markdownViewMode: { '/repo/README.md': 'source' },
|
|
fileContents: {
|
|
'/repo/README.md': textContent({ content: '[reference]: https://example.com' })
|
|
},
|
|
markdownRichModeFaultedContent: {
|
|
'/repo/README.md': '[reference]: https://example.com'
|
|
}
|
|
})
|
|
|
|
expect(model.availableEditorToggleModes).toEqual(['source', 'rich', 'preview', 'changes'])
|
|
})
|
|
|
|
it('omits Preview when the stored fault is for different, stale content', () => {
|
|
const model = renderModel({
|
|
markdownViewMode: { '/repo/README.md': 'source' },
|
|
editorDrafts: { '/repo/README.md': '# Edited since the fault' },
|
|
markdownRichModeFaultedContent: {
|
|
'/repo/README.md': '[reference]: https://example.com'
|
|
}
|
|
})
|
|
|
|
expect(model.availableEditorToggleModes).toEqual(['source', 'rich', 'changes'])
|
|
})
|
|
|
|
it('offers Preview when Rich view classifies live and this content falls back', () => {
|
|
const model = renderModel({
|
|
fileContents: {
|
|
'/repo/README.md': textContent({ content: '[reference]: https://example.com' })
|
|
}
|
|
})
|
|
|
|
expect(model.availableEditorToggleModes).toEqual(['source', 'rich', 'preview', 'changes'])
|
|
})
|
|
|
|
it('omits Preview for ordinary markdown content', () => {
|
|
const model = renderModel({})
|
|
|
|
expect(model.availableEditorToggleModes).toEqual(['source', 'rich', 'changes'])
|
|
})
|
|
})
|
|
|
|
describe('getEditorPanelRenderModel HTML preview affordance', () => {
|
|
it('enables preview for HTML edit tabs', () => {
|
|
expect(renderModel({ activeFile: htmlFile(), fileContents: {} }).canOpenPreviewToSide).toBe(
|
|
true
|
|
)
|
|
})
|
|
|
|
it('disables preview when the workspace browser provider is unavailable', () => {
|
|
const activeFile = htmlFile()
|
|
const model = getEditorPanelRenderModel({
|
|
activeFile,
|
|
fileContents: {},
|
|
editorDrafts: {},
|
|
gitStatusEntries: undefined,
|
|
gitBranchEntries: undefined,
|
|
markdownViewMode: {},
|
|
markdownRichModeSizeOverridden: false,
|
|
markdownRichModeFaultedContent: {},
|
|
isChangesMode: false,
|
|
canOpenWorkspaceFileBrowser: false
|
|
})
|
|
|
|
expect(model.canOpenPreviewToSide).toBe(false)
|
|
})
|
|
|
|
it('enables preview for single HTML diffs whose file exists on disk', () => {
|
|
const model = renderModel({
|
|
activeFile: htmlFile({ mode: 'diff', diffSource: 'unstaged' } as Partial<OpenFile>),
|
|
fileContents: {},
|
|
gitStatusByWorktree: {
|
|
'wt-1': [{ path: 'mock.html', status: 'modified', area: 'unstaged' }]
|
|
}
|
|
})
|
|
|
|
expect(model.canOpenPreviewToSide).toBe(true)
|
|
})
|
|
|
|
it('disables preview for diffs of deleted HTML files', () => {
|
|
const model = renderModel({
|
|
activeFile: htmlFile({ mode: 'diff', diffSource: 'unstaged' } as Partial<OpenFile>),
|
|
fileContents: {},
|
|
gitStatusByWorktree: {
|
|
'wt-1': [{ path: 'mock.html', status: 'deleted', area: 'unstaged' }]
|
|
}
|
|
})
|
|
|
|
expect(model.canOpenPreviewToSide).toBe(false)
|
|
})
|
|
|
|
it('disables preview for commit diffs whose content may not match disk', () => {
|
|
const model = renderModel({
|
|
activeFile: htmlFile({ mode: 'diff', diffSource: 'commit' } as Partial<OpenFile>),
|
|
fileContents: {}
|
|
})
|
|
|
|
expect(model.canOpenPreviewToSide).toBe(false)
|
|
})
|
|
|
|
it('disables preview for non-HTML diffs', () => {
|
|
const model = renderModel({
|
|
activeFile: markdownFile({ mode: 'diff', diffSource: 'unstaged' } as Partial<OpenFile>),
|
|
fileContents: {}
|
|
})
|
|
|
|
expect(model.canOpenPreviewToSide).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('getEditorPanelRenderModel read-only raw rendering (AI Vault View Log)', () => {
|
|
it('renders a read-only markdown log as raw source with no markdown viewer or chrome', () => {
|
|
const model = renderModel({ activeFile: markdownFile({ readOnly: true }) })
|
|
|
|
// Raw text renderer, not the rich/preview markdown surface.
|
|
expect(model.isMarkdown).toBe(false)
|
|
expect(model.hasViewModeToggle).toBe(false)
|
|
expect(model.canShowMarkdownPreview).toBe(false)
|
|
expect(model.canExportMarkdownToPdf).toBe(false)
|
|
expect(model.canShowMarkdownTableOfContents).toBe(false)
|
|
// Real language is preserved so Monaco still tokenizes the read-only source.
|
|
expect(model.resolvedLanguage).toBe('markdown')
|
|
})
|
|
|
|
it('keeps markdown chrome for ordinary writable markdown tabs', () => {
|
|
const model = renderModel({ activeFile: markdownFile() })
|
|
expect(model.isMarkdown).toBe(true)
|
|
expect(model.hasViewModeToggle).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('getEditorPanelRenderModel markdown export affordance', () => {
|
|
it('enables export for rendered markdown edit tabs', () => {
|
|
expect(renderModel({}).canExportMarkdownToPdf).toBe(true)
|
|
})
|
|
|
|
it('disables export when an inline markdown tab renders Changes mode', () => {
|
|
expect(renderModel({ isChangesMode: true }).canExportMarkdownToPdf).toBe(false)
|
|
})
|
|
|
|
it('uses unsaved drafts when resolving rich markdown fallback', () => {
|
|
const model = renderModel({
|
|
markdownViewMode: { '/repo/README.md': 'rich' },
|
|
editorDrafts: { '/repo/README.md': '[example]: https://example.com' }
|
|
})
|
|
|
|
expect(model.canExportMarkdownToPdf).toBe(false)
|
|
})
|
|
|
|
it('disables rich export when a multibyte character crosses the byte limit', () => {
|
|
const model = renderModel({
|
|
markdownViewMode: { '/repo/README.md': 'rich' },
|
|
editorDrafts: {
|
|
'/repo/README.md': `${'a'.repeat(RICH_MARKDOWN_MAX_SIZE_BYTES)}\u00e9`
|
|
}
|
|
})
|
|
|
|
expect(model.shouldShowMarkdownExportAction).toBe(true)
|
|
expect(model.canExportMarkdownToPdf).toBe(false)
|
|
})
|
|
|
|
it('keeps rich mode for an oversized file the user chose to open anyway', () => {
|
|
const model = renderModel({
|
|
markdownViewMode: { '/repo/README.md': 'rich' },
|
|
editorDrafts: { '/repo/README.md': 'a'.repeat(RICH_MARKDOWN_MAX_SIZE_BYTES + 1) },
|
|
markdownRichModeSizeOverridden: true
|
|
})
|
|
|
|
expect(model.canExportMarkdownToPdf).toBe(true)
|
|
})
|
|
|
|
it('scopes the open-anyway override to its own file', () => {
|
|
const model = renderModel({
|
|
markdownViewMode: { '/repo/README.md': 'rich' },
|
|
editorDrafts: { '/repo/README.md': 'a'.repeat(RICH_MARKDOWN_MAX_SIZE_BYTES + 1) },
|
|
markdownRichModeSizeOverridden: false
|
|
})
|
|
|
|
expect(model.canExportMarkdownToPdf).toBe(false)
|
|
})
|
|
|
|
it('disables edit export while content is still loading, even with a draft', () => {
|
|
const model = renderModel({
|
|
fileContents: {},
|
|
editorDrafts: { '/repo/README.md': '# Draft' }
|
|
})
|
|
|
|
expect(model.shouldShowMarkdownExportAction).toBe(true)
|
|
expect(model.canExportMarkdownToPdf).toBe(false)
|
|
})
|
|
|
|
it('enables export for loaded markdown preview tabs', () => {
|
|
const preview = markdownFile({
|
|
id: 'preview:/repo/README.md',
|
|
mode: 'markdown-preview',
|
|
markdownPreviewSourceFileId: '/repo/README.md'
|
|
} as Partial<OpenFile>)
|
|
|
|
expect(
|
|
renderModel({
|
|
activeFile: preview,
|
|
fileContents: { [preview.id]: textContent() }
|
|
}).canExportMarkdownToPdf
|
|
).toBe(true)
|
|
})
|
|
|
|
it('disables preview export until rendered content can exist', () => {
|
|
const preview = markdownFile({
|
|
id: 'preview:/repo/README.md',
|
|
mode: 'markdown-preview',
|
|
markdownPreviewSourceFileId: '/repo/README.md'
|
|
} as Partial<OpenFile>)
|
|
|
|
expect(renderModel({ activeFile: preview, fileContents: {} }).canExportMarkdownToPdf).toBe(
|
|
false
|
|
)
|
|
expect(
|
|
renderModel({
|
|
activeFile: preview,
|
|
fileContents: { [preview.id]: textContent({ loadError: 'missing' }) }
|
|
}).canExportMarkdownToPdf
|
|
).toBe(false)
|
|
expect(
|
|
renderModel({
|
|
activeFile: preview,
|
|
fileContents: { [preview.id]: textContent({ isBinary: true }) }
|
|
}).canExportMarkdownToPdf
|
|
).toBe(false)
|
|
})
|
|
})
|
|
|
|
it('hides disk preview while a fallback draft is unsaved', () => {
|
|
const model = renderModel({
|
|
editorDrafts: { '/repo/README.md': '[reference]: https://example.com' }
|
|
})
|
|
expect(model.availableEditorToggleModes).not.toContain('preview')
|
|
expect(model.canOpenPreviewToSide).toBe(false)
|
|
})
|