🐛 fix(vscode): 代码阅读的 Markdown 预览固定同一份 HTML,悬停链接不再闪
This commit is contained in:
1 parent
079657da37
commit
3abc87dbef
5 files changed
+256
-10
No files matched your search
@@ -0,0 +1,172 @@
|
||||
// @vitest-environment happy-dom
|
||||
|
||||
import type { ReactElement } from 'react'
|
||||
import type { Root } from 'react-dom/client'
|
||||
import type { ShownPeek } from './definitionPeek'
|
||||
import type { DefinitionPeek } from './types'
|
||||
import { act, useState } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { DefinitionPeeks, linkPeekSource } from './definitionPeek'
|
||||
import { DiffText } from './DiffText'
|
||||
import { MarkdownKindContext, TextPreview } from './TextPreview'
|
||||
|
||||
let container: HTMLDivElement
|
||||
let root: Root
|
||||
let peeks: DefinitionPeeks<HTMLAnchorElement> | undefined
|
||||
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('IS_REACT_ACT_ENVIRONMENT', true)
|
||||
container = document.createElement('div')
|
||||
document.body.append(container)
|
||||
root = createRoot(container)
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
peeks?.dispose()
|
||||
peeks = undefined
|
||||
await act(async () => root.unmount())
|
||||
container.remove()
|
||||
vi.useRealTimers()
|
||||
vi.unstubAllGlobals()
|
||||
})
|
||||
|
||||
async function render(element: ReactElement): Promise<void> {
|
||||
await act(async () => root.render(element))
|
||||
}
|
||||
|
||||
function anchor(): HTMLAnchorElement {
|
||||
const link = container.querySelector('a')
|
||||
if (!link)
|
||||
throw new Error('没有渲染符号链接')
|
||||
return link
|
||||
}
|
||||
|
||||
describe.each(['TextPreview', 'DiffText'] as const)('%s 的 Markdown 页面元素', (component) => {
|
||||
function preview(markdown: string): ReactElement {
|
||||
return component === 'TextPreview'
|
||||
? <TextPreview text={{ markdown }} className="custom-preview" />
|
||||
: <DiffText diff={{ markdown: [{ text: markdown }] }} className="custom-preview" />
|
||||
}
|
||||
|
||||
it('父级无关状态更新、传入等值新对象时,保留原链接且仍在页面中', async () => {
|
||||
function Parent(): ReactElement {
|
||||
const [count, setCount] = useState(0)
|
||||
return (
|
||||
<>
|
||||
<button onClick={() => setCount(value => value + 1)}>{count}</button>
|
||||
{preview('[AsyncOpenAI]')}
|
||||
</>
|
||||
)
|
||||
}
|
||||
await render(<Parent />)
|
||||
const original = anchor()
|
||||
for (let count = 1; count <= 3; count++) {
|
||||
await act(async () => container.querySelector('button')!.click())
|
||||
expect(container.querySelector('button')!.textContent).toBe(String(count))
|
||||
expect(anchor() === original).toBe(true)
|
||||
expect(original.isConnected).toBe(true)
|
||||
}
|
||||
expect(container.querySelector('.cr-markdown.custom-preview')).not.toBeNull()
|
||||
})
|
||||
|
||||
it('正文变化时更新链接和内容', async () => {
|
||||
await render(preview('[AsyncOpenAI]'))
|
||||
const original = anchor()
|
||||
await render(preview('[Client] 新说明'))
|
||||
expect(anchor().textContent).toBe('Client')
|
||||
expect(anchor().getAttribute('href')).toBe('symbol:Client')
|
||||
expect(container.textContent).toContain('新说明')
|
||||
expect(original.isConnected).toBe(false)
|
||||
})
|
||||
|
||||
it('空 Markdown 与着色代码片段可以来回切换', async () => {
|
||||
const empty = (): ReactElement => component === 'TextPreview'
|
||||
? <TextPreview text={{ markdown: '' }} />
|
||||
: <DiffText diff={{ markdown: [] }} />
|
||||
await render(preview('[Client]'))
|
||||
await render(empty())
|
||||
expect(container.querySelector('.cr-markdown')?.innerHTML).toBe('')
|
||||
expect(container.querySelector('a')).toBeNull()
|
||||
await render(component === 'TextPreview'
|
||||
? <TextPreview text={[{ text: 'client', role: 'variable' }]} />
|
||||
: <DiffText diff={{ inline: [{ segments: [{ text: 'client', role: 'variable' }], change: 'add' }] }} />)
|
||||
expect(container.querySelector('.cr-code .cr-role-variable')?.textContent).toBe('client')
|
||||
expect(container.querySelector('.cr-markdown')).toBeNull()
|
||||
if (component === 'DiffText')
|
||||
expect(container.querySelector('[data-change="add"]')?.textContent).toBe('client')
|
||||
await render(empty())
|
||||
expect(container.querySelector('.cr-markdown')?.innerHTML).toBe('')
|
||||
expect(container.querySelector('.cr-code')).toBeNull()
|
||||
await render(preview('[Client]'))
|
||||
expect(anchor().textContent).toBe('Client')
|
||||
})
|
||||
|
||||
it('显示定义预览引起父级更新后,继续指向同一链接不重复请求也不关闭', async () => {
|
||||
vi.useFakeTimers()
|
||||
const post = vi.fn()
|
||||
function Parent(): ReactElement {
|
||||
const [shown, setShown] = useState<ShownPeek<HTMLAnchorElement>>()
|
||||
const [controller] = useState(() => new DefinitionPeeks<HTMLAnchorElement>(post, setShown))
|
||||
peeks = controller
|
||||
return (
|
||||
<>
|
||||
{preview('[AsyncOpenAI]')}
|
||||
<output>{shown ? '预览已显示' : '预览未显示'}</output>
|
||||
</>
|
||||
)
|
||||
}
|
||||
await render(<Parent />)
|
||||
const controller = peeks!
|
||||
controller.setDelays(300, 500)
|
||||
const point = (): void => {
|
||||
const link = anchor()
|
||||
const source = linkPeekSource(link.getAttribute('href')!)
|
||||
if (!source)
|
||||
throw new Error('符号链接不能预览')
|
||||
controller.point({ anchor: link, source })
|
||||
}
|
||||
point()
|
||||
await act(async () => vi.advanceTimersByTime(300))
|
||||
expect(post).toHaveBeenCalledTimes(1)
|
||||
expect(post).toHaveBeenLastCalledWith({ type: 'peekLink', id: 1, symbol: 'AsyncOpenAI' })
|
||||
const peek: DefinitionPeek = {
|
||||
path: 'client.py',
|
||||
line: 1,
|
||||
block: { id: '1', anchor: '1', kind: 'class', startLine: 1, endLine: 2, code: '', codeTokens: [], attributes: { kind: 'class' }, concepts: [], children: [], translation: [] },
|
||||
names: [],
|
||||
}
|
||||
await act(async () => controller.answer(1, peek))
|
||||
expect(container.querySelector('output')?.textContent).toBe('预览已显示')
|
||||
point()
|
||||
await act(async () => vi.advanceTimersByTime(1000))
|
||||
expect(post).toHaveBeenCalledTimes(1)
|
||||
expect(container.querySelector('output')?.textContent).toBe('预览已显示')
|
||||
})
|
||||
})
|
||||
|
||||
it('组件 TextPreview 随注释与定义预览模式切换更新相同正文的链接', async () => {
|
||||
const preview = (kind: 'comment' | 'peek'): ReactElement => (
|
||||
<MarkdownKindContext value={kind}>
|
||||
<TextPreview text={{ markdown: '[Client]' }} />
|
||||
</MarkdownKindContext>
|
||||
)
|
||||
await render(preview('comment'))
|
||||
const original = anchor()
|
||||
await render(preview('peek'))
|
||||
expect(container.querySelector('a')).toBeNull()
|
||||
expect(container.textContent).toBe('[Client]\n')
|
||||
expect(original.isConnected).toBe(false)
|
||||
await render(preview('comment'))
|
||||
expect(anchor().getAttribute('href')).toBe('symbol:Client')
|
||||
})
|
||||
|
||||
it('组件 DiffText 更新正文时保留 Markdown 排版和增删标记', async () => {
|
||||
await render(<DiffText diff={{ markdown: [{ text: '[Client] ' }, { text: '旧说明', change: 'del' }, { text: '新说明', change: 'add' }] }} />)
|
||||
expect(anchor().textContent).toBe('Client')
|
||||
expect(container.querySelector('del')?.textContent).toBe('旧说明')
|
||||
expect(container.querySelector('ins')?.textContent).toBe('新说明')
|
||||
await render(<DiffText diff={{ markdown: [{ text: '[Client] ' }, { text: '**更新**', change: 'add' }] }} />)
|
||||
expect(container.querySelector('del')).toBeNull()
|
||||
expect(container.querySelector('ins strong')?.textContent).toBe('更新')
|
||||
})
|
||||
Reference in new issue
Block a user