Files
superwork/vscode/webview-ui/src/codeReader/diffView.test.ts
T

178 lines
8.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { BlockView, DocumentDiff, RemovedView } from './types'
import { describe, expect, it } from 'vitest'
import { changeStops, diffTree, foldUnchanged, nextStop, openedByDiff, removedTitle, statsParts } from './diffView'
function block(id: string, anchor: string, children: BlockView[] = [], extra: Partial<BlockView> = {}): BlockView {
return { id, anchor, kind: 'function', startLine: 0, endLine: 0, code: '', codeTokens: [], attributes: { kind: 'function' }, concepts: [{ id: 'function', label: '函数', icon: 'square-function' }], children, translation: [], ...extra } as BlockView
}
function removed(parent: string, after: string | undefined, kind = 'function'): RemovedView {
return { parent, ...(after !== undefined && { after }), block: block('9', `${kind} gone`, [], { kind }), line: 3, kindName: '函数' }
}
function diff(parts: Partial<DocumentDiff>): DocumentDiff {
return { base: '上一次提交', version: 2, blocks: {}, removed: [], stats: { add: 0, mod: 0, del: 0 }, lineCount: 10, ...parts }
}
const BLOCKS = [
block('0', 'function a', [block('0.0', 'function a > return @1')]),
block('1', 'function b'),
]
describe('diffTree', () => {
it('块按记号认成页面上的 id;页面上找不到的记号(文档刚改)不标', () => {
const tree = diffTree(BLOCKS, diff({ blocks: { 'function a': { change: 'mod' }, 'function a > return @1': { change: 'mod', own: true }, 'function gone': { change: 'add' } } }), 2, () => true)
expect([...tree!.changes]).toEqual([['0', { change: 'mod' }], ['0.0', { change: 'mod', own: true }]])
expect(tree!.current).toBe(true)
})
it('页面上不是差异算的那一版时,卡片里的标记不画', () => {
expect(diffTree(BLOCKS, diff({}), 3, () => true)?.current).toBe(false)
})
it('删掉的块按层挂:父块、排在后面的那块换成页面上的 id,找不到的不画,被筛掉的不画', () => {
const tree = diffTree(BLOCKS, diff({ removed: [
removed('', 'function b'),
removed('function a', undefined),
removed('function missing', undefined),
removed('', 'function missing'),
removed('', undefined, 'import'),
] }), 2, item => item.block.kind !== 'import')
expect(tree!.removed.get('')).toEqual([{ id: 'removed-0', after: '1', removed: removed('', 'function b') }])
expect(tree!.removed.get('0')).toEqual([{ id: 'removed-1', removed: removed('function a', undefined) }])
expect(tree!.removed.size).toBe(2)
})
it('不比、基准里没有这个文件时不逐块标', () => {
expect(diffTree(BLOCKS, null, 2, () => true)).toBeUndefined()
expect(diffTree(BLOCKS, diff({ newFile: '上一次提交里没有这个文件' }), 2, () => true)).toBeUndefined()
})
it('标了改动的块默认展开', () => {
const tree = diffTree(BLOCKS, diff({ blocks: { 'function b': { change: 'add', outermost: true } } }), 2, () => true)
expect([...openedByDiff(tree)]).toEqual(['1'])
expect(openedByDiff(undefined).size).toBe(0)
})
})
describe('statsParts', () => {
it('没有的那种不写', () => {
expect(statsParts({ add: 2, mod: 0, del: 1 })).toEqual([{ change: 'add', text: '新增 2' }, { change: 'del', text: '删除 1' }])
expect(statsParts({ add: 0, mod: 0, del: 0 })).toEqual([])
})
})
describe('removedTitle', () => {
it('属性表取第一行的值,一行译文取译文,只取第一行', () => {
expect(removedTitle(block('0', 'x', [], { properties: [{ label: '名称', value: [{ text: '_is_owner', role: 'function' }] }] } as Partial<BlockView>))).toBe('_is_owner')
expect(removedTitle(block('0', 'x', [], { translation: [{ text: '调用 f\n接着', role: 'plain' }] }))).toBe('调用 f')
expect(removedTitle(block('0', 'x', [], { kind: 'comment', translation: { markdown: '说明第一行\n第二行' } }))).toBe('说明第一行')
})
})
/**
* 0 function a(修改)
* 0.0 import(没变)
* 0.1 return(修改,自己改了)
* 0.2 return(没变)
* 1 function b(没变)
* 2 function c(没变)
* 3 function d(新增,带子块 3.0)
* 4 function e(没变)
*/
const TREE_BLOCKS = [
block('0', 'a', [block('0.0', 'a0', [], { startLine: 1 }), block('0.1', 'a1', [], { startLine: 2, endLine: 2 }), block('0.2', 'a2', [], { startLine: 3 })], { startLine: 0, endLine: 3 }),
block('1', 'b', [], { startLine: 5 }),
block('2', 'c', [], { startLine: 7 }),
block('3', 'd', [block('3.0', 'd0')], { startLine: 9, endLine: 11 }),
block('4', 'e', [], { startLine: 13 }),
]
function tree(removedList: RemovedView[] = []): NonNullable<ReturnType<typeof diffTree>> {
return diffTree(TREE_BLOCKS, diff({
blocks: { a: { change: 'mod' }, a1: { change: 'mod', own: true }, d: { change: 'add', outermost: true }, d0: { change: 'add' } },
removed: removedList,
}), 2, () => true)!
}
describe('foldUnchanged', () => {
it('每层连着没变的块折成一段,改过的块下面同样按层折', () => {
const { runs, folded } = foldUnchanged(TREE_BLOCKS, tree(), () => true, new Set(), new Set())
expect([...runs]).toEqual([['0.0', 1], ['0.2', 1], ['1', 2], ['4', 1]])
expect([...folded].sort()).toEqual(['0.0', '0.2', '1', '2', '4'])
})
it('删掉的块排在哪块后面,那一段就在哪儿断开', () => {
const { runs } = foldUnchanged(TREE_BLOCKS, tree([removed('', 'b')]), () => true, new Set(), new Set())
expect([...runs]).toContainEqual(['1', 1])
expect([...runs]).toContainEqual(['2', 1])
})
it('被筛掉的块不算也不打断;点开过的段、要露出来的块不折', () => {
expect([...foldUnchanged(TREE_BLOCKS, tree(), block => block.id !== '2', new Set(), new Set()).runs]).toContainEqual(['1', 1])
expect(foldUnchanged(TREE_BLOCKS, tree(), () => true, new Set(['1']), new Set()).runs.has('1')).toBe(false)
expect(foldUnchanged(TREE_BLOCKS, tree(), () => true, new Set(), new Set(['2'])).folded.has('2')).toBe(false)
})
})
/** 一张卡片:两个子块挂在第 8 行的子行上,后面一个留下的子块;只有第二个子块改了,它里面挂在第 2 行子行上的子块自己改了 */
const CARD_BLOCKS = [
block('0', 'card', [
block('0.0', 'm1', [], { slot: { row: 8, item: 0 } }),
block('0.1', 'm2', [block('0.1.0', 'm2s', [], { slot: { row: 2, item: 0 } })], { slot: { row: 8, item: 1 }, startLine: 4 }),
block('0.2', 'rest'),
]),
]
function cardTree(): NonNullable<ReturnType<typeof diffTree>> {
return diffTree(CARD_BLOCKS, diff({ blocks: { card: { change: 'mod' }, m2: { change: 'mod' }, m2s: { change: 'mod', own: true } } }), 2, () => true)!
}
describe('挂在卡片子行上的子块', () => {
it('只看改动:挂在子行上的不折、改了的照样往里看,卡片后面留下的照常折', () => {
const { runs, folded } = foldUnchanged(CARD_BLOCKS, cardTree(), () => true, new Set(), new Set())
expect([...runs]).toEqual([['0.2', 1]])
expect([...folded].sort()).toEqual(['0.2'])
})
it('上一处 / 下一处:要展开的是子块所在的那一行和它自己,不是卡片', () => {
expect(changeStops(CARD_BLOCKS, cardTree(), new Set()).map(stop => [stop.id, stop.ancestors])).toEqual([
['0.1.0', ['0/p8', '0.1', '0.1/p2', '0.1.0']],
])
})
})
describe('changeStops', () => {
it('新增的最外面那块、自己改了的修改块、删掉的块,照页面上的先后;修改块只算自己那几行', () => {
const stops = changeStops(TREE_BLOCKS, tree([removed('', 'c'), removed('a', undefined)]), new Set())
expect(stops.map(stop => [stop.id, stop.change, stop.from, stop.to, stop.ancestors])).toEqual([
['removed-1', 'del', 3, 3, ['0']],
['0.1', 'mod', 2, 2, ['0']],
['removed-0', 'del', 3, 3, []],
['3', 'add', 9, 11, []],
])
})
it('被筛掉的块和它下面的不算', () => {
expect(changeStops(TREE_BLOCKS, tree(), new Set(['3', '3.0'])).map(stop => stop.id)).toEqual(['0.1'])
})
})
describe('nextStop', () => {
const stops = [{ from: 2 }, { from: 9 }, { from: 20 }].map((stop, index) => ({ id: String(index), change: 'mod' as const, to: stop.from, ancestors: [], ...stop }))
it('接着刚跳到的那处走,到头绕回另一头', () => {
expect(nextStop(stops, { index: 0 }, 1)).toBe(1)
expect(nextStop(stops, { index: 2 }, 1)).toBe(0)
expect(nextStop(stops, { index: 0 }, -1)).toBe(2)
})
it('按视口顶上那块的行找:下一处在它后面,上一处在它前面', () => {
expect(nextStop(stops, { line: 9 }, 1)).toBe(2)
expect(nextStop(stops, { line: 9 }, -1)).toBe(0)
expect(nextStop(stops, { line: 30 }, 1)).toBe(0)
expect(nextStop(stops, { line: 0 }, -1)).toBe(2)
expect(nextStop(stops, {}, 1)).toBe(0)
})
})