mirror of
https://github.com/langgenius/dify.git
synced 2026-04-15 09:00:35 -04:00
126 lines
3.3 KiB
TypeScript
126 lines
3.3 KiB
TypeScript
import type { TocItem } from '../use-doc-toc'
|
|
import { act, renderHook } from '@testing-library/react'
|
|
import { useDocToc } from '../use-doc-toc'
|
|
|
|
const mockMatchMedia = (matches: boolean) => {
|
|
vi.stubGlobal('matchMedia', vi.fn().mockImplementation((query: string) => ({
|
|
matches,
|
|
media: query,
|
|
onchange: null,
|
|
addEventListener: vi.fn(),
|
|
removeEventListener: vi.fn(),
|
|
addListener: vi.fn(),
|
|
removeListener: vi.fn(),
|
|
dispatchEvent: vi.fn(),
|
|
})))
|
|
}
|
|
|
|
const setupDocument = () => {
|
|
document.body.innerHTML = `
|
|
<div class="overflow-auto"></div>
|
|
<article>
|
|
<h2 id="intro"><a href="#intro">Intro</a></h2>
|
|
<h2 id="details"><a href="#details">Details</a></h2>
|
|
</article>
|
|
`
|
|
|
|
const scrollContainer = document.querySelector('.overflow-auto') as HTMLDivElement
|
|
scrollContainer.scrollTo = vi.fn()
|
|
|
|
const intro = document.getElementById('intro') as HTMLElement
|
|
const details = document.getElementById('details') as HTMLElement
|
|
|
|
Object.defineProperty(intro, 'offsetTop', { configurable: true, value: 140 })
|
|
Object.defineProperty(details, 'offsetTop', { configurable: true, value: 320 })
|
|
|
|
return {
|
|
scrollContainer,
|
|
intro,
|
|
details,
|
|
}
|
|
}
|
|
|
|
describe('useDocToc', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
vi.useFakeTimers()
|
|
document.body.innerHTML = ''
|
|
mockMatchMedia(false)
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers()
|
|
vi.unstubAllGlobals()
|
|
})
|
|
|
|
it('should extract headings and expand the TOC on wide screens', async () => {
|
|
setupDocument()
|
|
mockMatchMedia(true)
|
|
|
|
const { result } = renderHook(() => useDocToc({
|
|
appDetail: { id: 'app-1' },
|
|
locale: 'en',
|
|
}))
|
|
|
|
act(() => {
|
|
vi.runAllTimers()
|
|
})
|
|
|
|
expect(result.current.toc).toEqual<TocItem[]>([
|
|
{ href: '#intro', text: 'Intro' },
|
|
{ href: '#details', text: 'Details' },
|
|
])
|
|
expect(result.current.activeSection).toBe('intro')
|
|
expect(result.current.isTocExpanded).toBe(true)
|
|
})
|
|
|
|
it('should update the active section when the scroll container scrolls', async () => {
|
|
const { scrollContainer, intro, details } = setupDocument()
|
|
Object.defineProperty(window, 'innerHeight', { configurable: true, value: 800 })
|
|
|
|
intro.getBoundingClientRect = vi.fn(() => ({ top: 500 } as DOMRect))
|
|
details.getBoundingClientRect = vi.fn(() => ({ top: 300 } as DOMRect))
|
|
|
|
const { result } = renderHook(() => useDocToc({
|
|
appDetail: { id: 'app-1' },
|
|
locale: 'en',
|
|
}))
|
|
|
|
act(() => {
|
|
vi.runAllTimers()
|
|
})
|
|
|
|
act(() => {
|
|
scrollContainer.dispatchEvent(new Event('scroll'))
|
|
})
|
|
|
|
expect(result.current.activeSection).toBe('details')
|
|
})
|
|
|
|
it('should scroll the container to the clicked heading offset', async () => {
|
|
const { scrollContainer } = setupDocument()
|
|
const { result } = renderHook(() => useDocToc({
|
|
appDetail: { id: 'app-1' },
|
|
locale: 'en',
|
|
}))
|
|
|
|
act(() => {
|
|
vi.runAllTimers()
|
|
})
|
|
|
|
const preventDefault = vi.fn()
|
|
act(() => {
|
|
result.current.handleTocClick(
|
|
{ preventDefault } as unknown as React.MouseEvent<HTMLAnchorElement>,
|
|
{ href: '#details', text: 'Details' },
|
|
)
|
|
})
|
|
|
|
expect(preventDefault).toHaveBeenCalledTimes(1)
|
|
expect(scrollContainer.scrollTo).toHaveBeenCalledWith({
|
|
top: 240,
|
|
behavior: 'smooth',
|
|
})
|
|
})
|
|
})
|