feat(desktop): embed BoosAPI Desktop client as a subproject
Vendor the rebranded BoosAPI Desktop client (Tauri + Vue) into metapi as a monorepo subdirectory under desktop/. Source only — node_modules, dist, Cargo target and the upstream .git history are excluded. desktop/ keeps its own independent Cargo workspace (desktop/src-tauri with crates/*); it is not part of the backend boosapi crate, so a `cargo check` at the metapi root still compiles only the backend. Build the desktop app from desktop/src-tauri per desktop/README.md. Note: app icons are still placeholder assets pending the final BoosAPI logo. The updater signing keys live outside the repo and are not included.
This commit is contained in:
@@ -0,0 +1,762 @@
|
||||
import { describe, expect, it, vi } from 'vitest'
|
||||
|
||||
vi.mock('../src/monaco-shim', () => {
|
||||
class Range {
|
||||
constructor(
|
||||
public startLineNumber: number,
|
||||
public startColumn: number,
|
||||
public endLineNumber: number,
|
||||
public endColumn: number,
|
||||
) {}
|
||||
}
|
||||
|
||||
return {
|
||||
default: { editor: { EditorOption: { lineHeight: 'lineHeight' } }, Range },
|
||||
editor: { EditorOption: { lineHeight: 'lineHeight' } },
|
||||
Range,
|
||||
}
|
||||
})
|
||||
|
||||
import { DiffEditorManager } from '../src/core/DiffEditorManager'
|
||||
|
||||
function createClassList() {
|
||||
const classes = new Set<string>()
|
||||
return {
|
||||
classList: {
|
||||
toggle(name: string, force?: boolean) {
|
||||
if (force)
|
||||
classes.add(name)
|
||||
else classes.delete(name)
|
||||
},
|
||||
remove(...names: string[]) {
|
||||
names.forEach(name => classes.delete(name))
|
||||
},
|
||||
contains(name: string) {
|
||||
return classes.has(name)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function createToggleClassList(initial: string[] = []) {
|
||||
const classes = new Set(initial)
|
||||
return {
|
||||
classList: {
|
||||
add(...names: string[]) {
|
||||
names.forEach(name => classes.add(name))
|
||||
},
|
||||
remove(...names: string[]) {
|
||||
names.forEach(name => classes.delete(name))
|
||||
},
|
||||
toggle(name: string, force?: boolean) {
|
||||
if (force === undefined) {
|
||||
if (classes.has(name)) {
|
||||
classes.delete(name)
|
||||
return false
|
||||
}
|
||||
classes.add(name)
|
||||
return true
|
||||
}
|
||||
if (force)
|
||||
classes.add(name)
|
||||
else classes.delete(name)
|
||||
return force
|
||||
},
|
||||
contains(name: string) {
|
||||
return classes.has(name)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function createPresentationHarness(
|
||||
preserveNativeDiffDecorationsOnStaleAppend: boolean,
|
||||
) {
|
||||
const manager = new DiffEditorManager(
|
||||
{ readOnly: true } as any,
|
||||
600,
|
||||
'600px',
|
||||
true,
|
||||
true,
|
||||
32,
|
||||
2,
|
||||
true,
|
||||
75,
|
||||
)
|
||||
|
||||
const { classList } = createClassList()
|
||||
const originalDeltaDecorations = vi.fn((_: string[], next: unknown[]) =>
|
||||
next.map((_, index) => `original-${index}`))
|
||||
const modifiedDeltaDecorations = vi.fn((_: string[], next: unknown[]) =>
|
||||
next.map((_, index) => `modified-${index}`))
|
||||
|
||||
;(manager as any).lastContainer = { classList }
|
||||
;(manager as any).isDiffInlineMode = () => false
|
||||
;(manager as any).diffComputedVersions = { original: 1, modified: 1 }
|
||||
;(manager as any).preserveNativeDiffDecorationsOnStaleAppend
|
||||
= preserveNativeDiffDecorationsOnStaleAppend
|
||||
;(manager as any).originalModel = {
|
||||
getAlternativeVersionId: () => 2,
|
||||
getValue: () => 'const value = 1\nreturn value',
|
||||
}
|
||||
;(manager as any).modifiedModel = {
|
||||
getAlternativeVersionId: () => 2,
|
||||
getValue: () => 'const value = 2\nreturn value\nconsole.log(value)',
|
||||
}
|
||||
;(manager as any).diffEditorView = {
|
||||
getLineChanges: () => [
|
||||
{
|
||||
originalStartLineNumber: 1,
|
||||
originalEndLineNumber: 1,
|
||||
modifiedStartLineNumber: 1,
|
||||
modifiedEndLineNumber: 1,
|
||||
charChanges: [],
|
||||
},
|
||||
],
|
||||
getOriginalEditor: () => ({
|
||||
deltaDecorations: originalDeltaDecorations,
|
||||
}),
|
||||
getModifiedEditor: () => ({
|
||||
deltaDecorations: modifiedDeltaDecorations,
|
||||
}),
|
||||
}
|
||||
|
||||
return {
|
||||
manager,
|
||||
classList,
|
||||
originalDeltaDecorations,
|
||||
modifiedDeltaDecorations,
|
||||
}
|
||||
}
|
||||
|
||||
describe('DiffEditorManager diff presentation', () => {
|
||||
it('forces glyph margin when unchanged-region folding is enabled', () => {
|
||||
const manager = new DiffEditorManager(
|
||||
{ readOnly: true, glyphMargin: false } as any,
|
||||
600,
|
||||
'600px',
|
||||
true,
|
||||
true,
|
||||
32,
|
||||
2,
|
||||
true,
|
||||
75,
|
||||
)
|
||||
|
||||
const options = (manager as any).resolveDiffPresentationEditorOptions({
|
||||
enabled: true,
|
||||
})
|
||||
const disabledOptions = (manager as any).resolveDiffPresentationEditorOptions({
|
||||
enabled: false,
|
||||
})
|
||||
|
||||
expect(options.glyphMargin).toBe(true)
|
||||
expect(disabledOptions.glyphMargin).toBe(false)
|
||||
})
|
||||
|
||||
it('hides the native fold glyph that overlaps the folded inline summary row', () => {
|
||||
const manager = new DiffEditorManager(
|
||||
{ readOnly: true } as any,
|
||||
600,
|
||||
'600px',
|
||||
true,
|
||||
true,
|
||||
32,
|
||||
2,
|
||||
true,
|
||||
75,
|
||||
)
|
||||
|
||||
const modifiedCenter = {
|
||||
...createToggleClassList(),
|
||||
getBoundingClientRect: () => ({
|
||||
top: 120,
|
||||
height: 32,
|
||||
width: 220,
|
||||
}),
|
||||
}
|
||||
const overlappingGlyph = {
|
||||
...createToggleClassList(),
|
||||
getBoundingClientRect: () => ({
|
||||
top: 127,
|
||||
height: 18,
|
||||
}),
|
||||
}
|
||||
const distantGlyph = {
|
||||
...createToggleClassList(),
|
||||
getBoundingClientRect: () => ({
|
||||
top: 180,
|
||||
height: 18,
|
||||
}),
|
||||
}
|
||||
|
||||
;(manager as any).lastContainer = {
|
||||
querySelectorAll(selector: string) {
|
||||
if (selector === '.diff-hidden-lines .center')
|
||||
return [modifiedCenter]
|
||||
if (selector === '.editor.modified .diff-hidden-lines .center')
|
||||
return [modifiedCenter]
|
||||
if (selector === '.diff-hidden-lines .top, .diff-hidden-lines .bottom')
|
||||
return []
|
||||
if (selector === '.fold-unchanged')
|
||||
return [overlappingGlyph, distantGlyph]
|
||||
return []
|
||||
},
|
||||
}
|
||||
;(manager as any).applyDiffRootAppearanceClass = vi.fn()
|
||||
;(manager as any).syncDiffUnchangedViewZoneHeights = vi.fn(() => false)
|
||||
;(manager as any).patchDiffUnchangedCenter = vi.fn()
|
||||
;(manager as any).clearDiffUnchangedBridgeSources = vi.fn()
|
||||
;(manager as any).renderMergedDiffUnchangedBridge = vi.fn(() => null)
|
||||
;(manager as any).pruneDiffUnchangedBridgeEntries = vi.fn()
|
||||
;(manager as any).syncDiffUnchangedOverlayScrollBaseline = vi.fn()
|
||||
;(manager as any).patchDiffUnchangedFoldGlyph = vi.fn()
|
||||
|
||||
;(manager as any).scanAndPatchDiffUnchangedRegions()
|
||||
|
||||
expect(
|
||||
overlappingGlyph.classList.contains('stream-monaco-fold-unchanged-hidden'),
|
||||
).toBe(true)
|
||||
expect(
|
||||
distantGlyph.classList.contains('stream-monaco-fold-unchanged-hidden'),
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('does not overwrite persisted unchanged state while folding is disabled', () => {
|
||||
const manager = new DiffEditorManager(
|
||||
{ readOnly: true } as any,
|
||||
600,
|
||||
'600px',
|
||||
true,
|
||||
true,
|
||||
32,
|
||||
2,
|
||||
true,
|
||||
75,
|
||||
)
|
||||
|
||||
;(manager as any).diffPersistedUnchangedModelState = { collapsed: true }
|
||||
;(manager as any).diffHideUnchangedRegionsResolved = { enabled: false }
|
||||
;(manager as any).diffEditorView = {
|
||||
saveViewState: () => ({
|
||||
modelState: { expanded: true },
|
||||
}),
|
||||
}
|
||||
|
||||
;(manager as any).capturePersistedDiffUnchangedState()
|
||||
|
||||
expect((manager as any).diffPersistedUnchangedModelState).toEqual({
|
||||
collapsed: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('restores the previous unchanged state instead of collapsing all context', () => {
|
||||
const manager = new DiffEditorManager(
|
||||
{ readOnly: true } as any,
|
||||
600,
|
||||
'600px',
|
||||
true,
|
||||
true,
|
||||
32,
|
||||
2,
|
||||
true,
|
||||
75,
|
||||
)
|
||||
const restoreViewState = vi.fn()
|
||||
|
||||
;(manager as any).diffPreviousUnchangedModelState = {
|
||||
hiddenAreas: [{ startLineNumber: 7, endLineNumber: 25 }],
|
||||
}
|
||||
;(manager as any).diffEditorView = {
|
||||
saveViewState: () => ({
|
||||
original: { scrollTop: 10 },
|
||||
modified: { scrollTop: 10 },
|
||||
modelState: { hiddenAreas: [] },
|
||||
}),
|
||||
restoreViewState,
|
||||
}
|
||||
;(manager as any).applyPendingDiffScrollRestore = vi.fn()
|
||||
|
||||
const restored = (manager as any).restorePreviousDiffUnchangedState()
|
||||
|
||||
expect(restored).toBe(true)
|
||||
expect(restoreViewState).toHaveBeenCalledWith({
|
||||
original: { scrollTop: 10 },
|
||||
modified: { scrollTop: 10 },
|
||||
modelState: { hiddenAreas: [{ startLineNumber: 7, endLineNumber: 25 }] },
|
||||
})
|
||||
expect((manager as any).diffPersistedUnchangedModelState).toEqual({
|
||||
hiddenAreas: [{ startLineNumber: 7, endLineNumber: 25 }],
|
||||
})
|
||||
})
|
||||
|
||||
it('recomputes unchanged-region folding without restoring the stale model state', () => {
|
||||
const manager = new DiffEditorManager(
|
||||
{ readOnly: true } as any,
|
||||
600,
|
||||
'600px',
|
||||
true,
|
||||
true,
|
||||
32,
|
||||
2,
|
||||
true,
|
||||
75,
|
||||
)
|
||||
const setDiffModels = vi.fn()
|
||||
|
||||
;(manager as any).originalModel = { getValue: () => 'a' }
|
||||
;(manager as any).modifiedModel = { getValue: () => 'b' }
|
||||
;(manager as any).diffEditorView = {
|
||||
updateOptions: vi.fn(),
|
||||
}
|
||||
;(manager as any).diffHideUnchangedRegionsDeferred = false
|
||||
;(manager as any).diffHideUnchangedRegionsResolved = { enabled: false }
|
||||
;(manager as any).resolveDiffHideUnchangedRegionsOption = () => ({ enabled: true })
|
||||
;(manager as any).resolveDiffPresentationEditorOptions = () => ({})
|
||||
;(manager as any).withLockedDiffScrollPosition = (fn: () => void) => fn()
|
||||
;(manager as any).diffHeightManager = { update: vi.fn() }
|
||||
;(manager as any).applyDiffRootAppearanceClass = vi.fn()
|
||||
;(manager as any).schedulePatchDiffUnchangedRegionsAfterInteraction = vi.fn()
|
||||
;(manager as any).repositionDiffHunkNodes = vi.fn()
|
||||
;(manager as any).setDiffModels = setDiffModels
|
||||
|
||||
;(manager as any).refreshDiffPresentation()
|
||||
|
||||
expect(setDiffModels).toHaveBeenCalledWith(
|
||||
{
|
||||
original: (manager as any).originalModel,
|
||||
modified: (manager as any).modifiedModel,
|
||||
},
|
||||
{
|
||||
preserveViewState: true,
|
||||
preserveModelState: false,
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
it('preserves scroll state without restoring the saved model state when requested', async () => {
|
||||
const manager = new DiffEditorManager(
|
||||
{ readOnly: true } as any,
|
||||
600,
|
||||
'600px',
|
||||
true,
|
||||
true,
|
||||
32,
|
||||
2,
|
||||
true,
|
||||
75,
|
||||
)
|
||||
const restoreViewState = vi.fn()
|
||||
const setModel = vi.fn()
|
||||
const waitForDiff = vi.fn(async () => {})
|
||||
const preparedViewModel = {
|
||||
waitForDiff,
|
||||
dispose: vi.fn(),
|
||||
}
|
||||
const originalModel = {
|
||||
getValue: () => 'const a = 1',
|
||||
getLineCount: () => 1,
|
||||
getLanguageId: () => 'ts',
|
||||
}
|
||||
const modifiedModel = {
|
||||
getValue: () => 'const b = 2',
|
||||
getLineCount: () => 1,
|
||||
getLanguageId: () => 'ts',
|
||||
}
|
||||
|
||||
;(manager as any).originalModel = originalModel
|
||||
;(manager as any).modifiedModel = modifiedModel
|
||||
;(manager as any).originalModelOwned = false
|
||||
;(manager as any).modifiedModelOwned = false
|
||||
;(manager as any).captureDiffScrollPosition = () => null
|
||||
;(manager as any).captureModifiedViewportAnchor = () => null
|
||||
;(manager as any).queuePendingDiffScrollRestore = vi.fn()
|
||||
;(manager as any).withLockedDiffScrollPosition = (fn: () => void) => fn()
|
||||
;(manager as any).flushOriginalAppendBufferSync = vi.fn()
|
||||
;(manager as any).flushModifiedAppendBufferSync = vi.fn()
|
||||
;(manager as any).computedHeight = () => 0
|
||||
;(manager as any).getEffectiveLineChanges = () => []
|
||||
;(manager as any).hideDiffHunkActions = vi.fn()
|
||||
;(manager as any).clearDiffUnchangedBridgeOverlay = vi.fn()
|
||||
;(manager as any).syncDiffUnchangedViewZoneHeights = vi.fn()
|
||||
;(manager as any).refreshDiffPresentation = vi.fn()
|
||||
;(manager as any).scheduleSyncDiffPresentationDecorations = vi.fn()
|
||||
;(manager as any).applyPendingDiffScrollRestore = vi.fn()
|
||||
;(manager as any).scheduleRestoreModifiedViewportAnchor = vi.fn()
|
||||
;(manager as any).disposePreviousDiffModel = vi.fn()
|
||||
;(manager as any).diffEditorView = {
|
||||
createViewModel: vi.fn(() => preparedViewModel),
|
||||
setModel,
|
||||
saveViewState: vi.fn(() => ({
|
||||
original: { scrollTop: 10 },
|
||||
modified: { scrollTop: 10 },
|
||||
modelState: { hiddenAreas: [{ startLineNumber: 4, endLineNumber: 20 }] },
|
||||
})),
|
||||
restoreViewState,
|
||||
getModifiedEditor: () => ({
|
||||
getScrollHeight: () => 0,
|
||||
getOption: () => 18,
|
||||
}),
|
||||
}
|
||||
|
||||
await (manager as any).setDiffModels(
|
||||
{
|
||||
original: originalModel,
|
||||
modified: modifiedModel,
|
||||
},
|
||||
{
|
||||
preserveViewState: true,
|
||||
preserveModelState: false,
|
||||
},
|
||||
)
|
||||
|
||||
expect(waitForDiff).toHaveBeenCalled()
|
||||
expect(setModel).toHaveBeenCalled()
|
||||
expect(restoreViewState).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('keeps native diff blocks visible while stale during tail appends', () => {
|
||||
const {
|
||||
manager,
|
||||
classList,
|
||||
originalDeltaDecorations,
|
||||
modifiedDeltaDecorations,
|
||||
} = createPresentationHarness(true)
|
||||
|
||||
;(manager as any).syncDiffPresentationDecorations()
|
||||
|
||||
expect(classList.contains('stream-monaco-diff-native-stale')).toBe(false)
|
||||
expect(originalDeltaDecorations).toHaveBeenCalledOnce()
|
||||
expect(modifiedDeltaDecorations).toHaveBeenCalledOnce()
|
||||
expect(originalDeltaDecorations.mock.calls[0][1]).not.toHaveLength(0)
|
||||
expect(modifiedDeltaDecorations.mock.calls[0][1]).not.toHaveLength(0)
|
||||
})
|
||||
|
||||
it('keeps native diff blocks visible when only the DOM still has stale native markers', () => {
|
||||
const { manager, classList } = createPresentationHarness(true)
|
||||
|
||||
;(manager as any).diffEditorView.getLineChanges = () => null
|
||||
;(manager as any).lastContainer.querySelector = vi.fn(() => ({}))
|
||||
|
||||
;(manager as any).syncDiffPresentationDecorations()
|
||||
|
||||
expect(classList.contains('stream-monaco-diff-native-stale')).toBe(false)
|
||||
})
|
||||
|
||||
it('still hides native diff blocks for non-append stale updates', () => {
|
||||
const { manager, classList } = createPresentationHarness(false)
|
||||
|
||||
;(manager as any).syncDiffPresentationDecorations()
|
||||
|
||||
expect(classList.contains('stream-monaco-diff-native-stale')).toBe(true)
|
||||
})
|
||||
|
||||
it('adds fallback inline delete zones while native inline diff is stale', () => {
|
||||
const createdNodes: Array<{
|
||||
className: string
|
||||
style: Record<string, string>
|
||||
children: any[]
|
||||
append: (...nodes: any[]) => void
|
||||
setAttribute: () => void
|
||||
textContent: string
|
||||
}> = []
|
||||
const originalDocument = (globalThis as any).document
|
||||
;(globalThis as any).document = {
|
||||
createElement() {
|
||||
const node = {
|
||||
className: '',
|
||||
style: {} as Record<string, string>,
|
||||
children: [] as any[],
|
||||
append(...next: any[]) {
|
||||
this.children.push(...next)
|
||||
},
|
||||
setAttribute() {},
|
||||
textContent: '',
|
||||
}
|
||||
createdNodes.push(node)
|
||||
return node
|
||||
},
|
||||
}
|
||||
|
||||
try {
|
||||
const { manager, classList } = createPresentationHarness(true)
|
||||
const addedZones: any[] = []
|
||||
const removedZoneIds: string[] = []
|
||||
const applyFontInfo = vi.fn()
|
||||
|
||||
;(manager as any).isDiffInlineMode = () => true
|
||||
;(manager as any).clearFallbackInlineDeletedZones = DiffEditorManager.prototype[
|
||||
'clearFallbackInlineDeletedZones'
|
||||
]
|
||||
;(manager as any).originalModel = {
|
||||
getAlternativeVersionId: () => 2,
|
||||
getValue: () => 'const value = 1\nreturn value',
|
||||
getLineContent: (line: number) =>
|
||||
['const value = 1', 'return value'][line - 1] ?? '',
|
||||
}
|
||||
;(manager as any).modifiedModel = {
|
||||
getAlternativeVersionId: () => 2,
|
||||
getValue: () => 'const value = 2\nreturn value',
|
||||
getLineCount: () => 2,
|
||||
}
|
||||
;(manager as any).diffEditorView = {
|
||||
getLineChanges: () => null,
|
||||
getOriginalEditor: () => ({
|
||||
deltaDecorations: vi.fn((_: string[], next: unknown[]) =>
|
||||
next.map((_, index) => `original-${index}`)),
|
||||
}),
|
||||
getModifiedEditor: () => ({
|
||||
deltaDecorations: vi.fn((_: string[], next: unknown[]) =>
|
||||
next.map((_, index) => `modified-${index}`)),
|
||||
getModel: () => (manager as any).modifiedModel,
|
||||
getOption: () => 20,
|
||||
applyFontInfo,
|
||||
changeViewZones(callback: (accessor: {
|
||||
addZone: (zone: any) => string
|
||||
removeZone: (id: string) => void
|
||||
}) => void) {
|
||||
callback({
|
||||
addZone(zone) {
|
||||
addedZones.push(zone)
|
||||
return `zone-${addedZones.length}`
|
||||
},
|
||||
removeZone(id) {
|
||||
removedZoneIds.push(id)
|
||||
},
|
||||
})
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
;(manager as any).syncDiffPresentationDecorations()
|
||||
|
||||
expect(classList.contains('stream-monaco-diff-native-stale')).toBe(true)
|
||||
expect(addedZones).toHaveLength(1)
|
||||
expect(addedZones[0].afterLineNumber).toBe(0)
|
||||
expect(addedZones[0].heightInLines).toBe(1)
|
||||
expect(addedZones[0].domNode.className).toBe(
|
||||
'stream-monaco-fallback-inline-delete-zone',
|
||||
)
|
||||
expect(addedZones[0].domNode.children).toHaveLength(1)
|
||||
expect(addedZones[0].domNode.children[0].textContent).toBe(
|
||||
'const value = 1',
|
||||
)
|
||||
expect(applyFontInfo).toHaveBeenCalledWith(addedZones[0].domNode)
|
||||
expect(applyFontInfo).toHaveBeenCalledWith(addedZones[0].marginDomNode)
|
||||
expect(removedZoneIds).toHaveLength(0)
|
||||
expect(createdNodes.length).toBeGreaterThan(0)
|
||||
}
|
||||
finally {
|
||||
;(globalThis as any).document = originalDocument
|
||||
}
|
||||
})
|
||||
|
||||
it('clears fallback inline delete zones as soon as native inline delete zones appear', () => {
|
||||
const originalDocument = (globalThis as any).document
|
||||
const originalHTMLElement = (globalThis as any).HTMLElement
|
||||
class FakeElement {
|
||||
constructor(public className: string) {}
|
||||
|
||||
matches(selector: string) {
|
||||
return selector.split(',').some((entry) => {
|
||||
const parts = entry.trim().split('.').filter(Boolean)
|
||||
return parts.every(part => this.className.split(/\s+/).includes(part))
|
||||
})
|
||||
}
|
||||
|
||||
querySelector() {
|
||||
return null
|
||||
}
|
||||
}
|
||||
;(globalThis as any).HTMLElement = FakeElement
|
||||
;(globalThis as any).document = {
|
||||
createElement() {
|
||||
return {
|
||||
className: '',
|
||||
style: {} as Record<string, string>,
|
||||
children: [] as any[],
|
||||
append() {},
|
||||
setAttribute() {},
|
||||
textContent: '',
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
try {
|
||||
const { manager, classList } = createPresentationHarness(true)
|
||||
const addedZones: any[] = []
|
||||
|
||||
;(manager as any).isDiffInlineMode = () => true
|
||||
;(manager as any).inlineDiffStreamingPresentationActive = true
|
||||
;(manager as any).hasFreshNativeDiffResult = () => false
|
||||
;(manager as any).clearFallbackInlineDeletedZones = DiffEditorManager.prototype[
|
||||
'clearFallbackInlineDeletedZones'
|
||||
]
|
||||
;(manager as any).lastContainer = {
|
||||
classList,
|
||||
querySelectorAll(selector: string) {
|
||||
if (selector.includes('.editor.modified .view-zones [monaco-view-zone]'))
|
||||
return [new FakeElement('view-lines line-delete monaco-mouse-cursor-text')]
|
||||
if (selector.includes('.editor.modified .margin-view-zones [monaco-view-zone]'))
|
||||
return [new FakeElement('inline-deleted-margin-view-zone')]
|
||||
return []
|
||||
},
|
||||
querySelector: vi.fn(() => null),
|
||||
}
|
||||
;(manager as any).originalModel = {
|
||||
getAlternativeVersionId: () => 2,
|
||||
getValue: () => 'const value = 1\nreturn value',
|
||||
getLineContent: (line: number) =>
|
||||
['const value = 1', 'return value'][line - 1] ?? '',
|
||||
}
|
||||
;(manager as any).modifiedModel = {
|
||||
getAlternativeVersionId: () => 2,
|
||||
getValue: () => 'const value = 2\nreturn value',
|
||||
getLineCount: () => 2,
|
||||
}
|
||||
;(manager as any).diffEditorView = {
|
||||
getLineChanges: () => null,
|
||||
getOriginalEditor: () => ({
|
||||
deltaDecorations: vi.fn((_: string[], next: unknown[]) =>
|
||||
next.map((_, index) => `original-${index}`)),
|
||||
}),
|
||||
getModifiedEditor: () => ({
|
||||
deltaDecorations: vi.fn((_: string[], next: unknown[]) =>
|
||||
next.map((_, index) => `modified-${index}`)),
|
||||
getModel: () => (manager as any).modifiedModel,
|
||||
getOption: () => 20,
|
||||
changeViewZones(callback: (accessor: {
|
||||
addZone: (zone: any) => string
|
||||
removeZone: (id: string) => void
|
||||
}) => void) {
|
||||
callback({
|
||||
addZone(zone) {
|
||||
addedZones.push(zone)
|
||||
return `zone-${addedZones.length}`
|
||||
},
|
||||
removeZone() {},
|
||||
})
|
||||
},
|
||||
}),
|
||||
}
|
||||
|
||||
;(manager as any).syncDiffPresentationDecorations()
|
||||
|
||||
expect(classList.contains('stream-monaco-diff-inline-native-ready')).toBe(true)
|
||||
expect(addedZones).toHaveLength(0)
|
||||
}
|
||||
finally {
|
||||
;(globalThis as any).document = originalDocument
|
||||
;(globalThis as any).HTMLElement = originalHTMLElement
|
||||
}
|
||||
})
|
||||
|
||||
it('reuses native inline delete wrappers instead of adding another fallback zone', () => {
|
||||
class FakeElement {
|
||||
className = ''
|
||||
style: Record<string, string> = {}
|
||||
children: any[] = []
|
||||
attributes = new Map<string, string>()
|
||||
|
||||
constructor(
|
||||
private readonly selectorMap: Record<string, unknown> = {},
|
||||
) {}
|
||||
|
||||
querySelector(selector: string) {
|
||||
return this.selectorMap[selector] ?? null
|
||||
}
|
||||
|
||||
append(node: any) {
|
||||
this.children.push(node)
|
||||
node.parentElement = this
|
||||
}
|
||||
|
||||
removeChild(node: any) {
|
||||
this.children = this.children.filter(child => child !== node)
|
||||
}
|
||||
|
||||
setAttribute(name: string, value: string) {
|
||||
this.attributes.set(name, value)
|
||||
}
|
||||
|
||||
getAttribute(name: string) {
|
||||
return this.attributes.get(name) ?? null
|
||||
}
|
||||
}
|
||||
|
||||
const originalDocument = (globalThis as any).document
|
||||
const originalHTMLElement = (globalThis as any).HTMLElement
|
||||
;(globalThis as any).HTMLElement = FakeElement
|
||||
;(globalThis as any).document = {
|
||||
createElement() {
|
||||
return new FakeElement()
|
||||
},
|
||||
}
|
||||
|
||||
try {
|
||||
const { manager } = createPresentationHarness(true)
|
||||
const applyFontInfo = vi.fn()
|
||||
const nativeDeleteMarker = new FakeElement()
|
||||
nativeDeleteMarker.className = 'view-lines line-delete monaco-mouse-cursor-text'
|
||||
const nativeMarginMarker = new FakeElement()
|
||||
nativeMarginMarker.className = 'inline-deleted-margin-view-zone'
|
||||
const viewWrapper = new FakeElement({
|
||||
'.view-lines.line-delete': nativeDeleteMarker,
|
||||
})
|
||||
viewWrapper.style.top = '54px'
|
||||
viewWrapper.setAttribute('monaco-view-zone', 'native-1')
|
||||
const marginWrapper = new FakeElement({
|
||||
'.inline-deleted-margin-view-zone': nativeMarginMarker,
|
||||
})
|
||||
marginWrapper.style.top = '54px'
|
||||
marginWrapper.setAttribute('monaco-view-zone', 'native-1')
|
||||
|
||||
;(manager as any).lastContainer = {
|
||||
querySelectorAll(selector: string) {
|
||||
if (selector.includes('.editor.modified .view-zones [monaco-view-zone]'))
|
||||
return [viewWrapper]
|
||||
if (selector.includes('.editor.modified .margin-view-zones [monaco-view-zone]'))
|
||||
return [marginWrapper]
|
||||
return []
|
||||
},
|
||||
}
|
||||
;(manager as any).diffEditorView = {
|
||||
getModifiedEditor: () => ({
|
||||
getModel: () => ({
|
||||
getLineCount: () => 4,
|
||||
}),
|
||||
getOption: () => 18,
|
||||
applyFontInfo,
|
||||
changeViewZones: vi.fn(),
|
||||
}),
|
||||
}
|
||||
;(manager as any).originalModel = {
|
||||
getLineContent: () => '"version": "0.0.49",',
|
||||
}
|
||||
;(manager as any).isDiffInlineMode = () => true
|
||||
|
||||
;(manager as any).syncFallbackInlineDeletedZones([
|
||||
{
|
||||
originalStartLineNumber: 1,
|
||||
originalEndLineNumber: 1,
|
||||
modifiedStartLineNumber: 4,
|
||||
modifiedEndLineNumber: 4,
|
||||
charChanges: [],
|
||||
},
|
||||
])
|
||||
|
||||
expect(viewWrapper.children).toHaveLength(1)
|
||||
expect(viewWrapper.children[0].className).toBe(
|
||||
'stream-monaco-fallback-inline-delete-zone',
|
||||
)
|
||||
expect(marginWrapper.children).toHaveLength(1)
|
||||
expect(marginWrapper.children[0].className).toBe(
|
||||
'stream-monaco-fallback-inline-delete-margin',
|
||||
)
|
||||
}
|
||||
finally {
|
||||
;(globalThis as any).document = originalDocument
|
||||
;(globalThis as any).HTMLElement = originalHTMLElement
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user