Merge remote-tracking branch 'origin/master' into worktree/fix-ui-polish

This commit is contained in:
imccyu
2026-07-24 01:12:29 +08:00
734 changed files with 33423 additions and 7286 deletions

View File

@@ -1,14 +1,93 @@
// @vitest-environment jsdom
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
import { afterEach, describe, expect, it } from 'vitest'
import { JsonBlock, MessageText } from '@deepseek-ai/dsh-client-ui-primitives'
import { JsonBlock, MarkdownText, MessageText } from '@deepseek-ai/dsh-client-ui-primitives'
afterEach(cleanup)
describe('MessageText', () => {
it('renders the text verbatim', () => {
const { container } = render(<MessageText text={'line1\nline2'} />)
expect(container.textContent).toBe('line1\nline2')
const { container } = render(<MessageText text={'# line1\n`line2`'} />)
expect(container.textContent).toBe('# line1\n`line2`')
expect(container.querySelector('h1')).toBeNull()
})
})
describe('MarkdownText', () => {
it('renders CommonMark and GFM elements as semantic DOM', () => {
const markdown = [
'# Heading',
'',
'Paragraph with **strong**, *emphasis*, ~~deleted~~, `inline`, and [safe](https://example.com). ',
'Hard break.',
'',
'> Quote',
'',
'- parent',
' - child',
'',
'1. first',
'2. second',
'',
'- [x] done',
'- [ ] pending',
'',
'| Name | Value |',
'| --- | --- |',
'| alpha | beta |',
'',
'---',
'',
'```ts',
'const answer = 42',
'```',
'',
'<https://deepseek.com>',
].join('\n')
const { container } = render(<MarkdownText text={markdown} />)
expect(screen.getByRole('heading', { level: 1, name: 'Heading' })).toBeTruthy()
expect(container.querySelector('strong')?.textContent).toBe('strong')
expect(container.querySelector('em')?.textContent).toBe('emphasis')
expect(container.querySelector('del')?.textContent).toBe('deleted')
expect(container.querySelector('blockquote')?.textContent?.trim()).toBe('Quote')
expect(container.querySelectorAll('ul')).toHaveLength(3)
expect(container.querySelector('ol')).not.toBeNull()
expect(container.querySelectorAll('input[type="checkbox"]')).toHaveLength(2)
expect(container.querySelector('table')?.textContent).toContain('alphabeta')
expect(container.querySelector('hr')).not.toBeNull()
expect(container.querySelector('pre code')?.textContent).toContain('const answer = 42')
expect(container.querySelector('br')).not.toBeNull()
expect(screen.getByRole('link', { name: 'safe' }).getAttribute('target')).toBe('_blank')
expect(screen.getByRole('link', { name: 'https://deepseek.com' })).toBeTruthy()
})
it('neutralizes raw HTML, unsafe or relative links, and remote images', () => {
const markdown = [
'<script>globalThis.compromised = true</script>',
'<img src="x" onerror="globalThis.compromised = true">',
'[script](javascript:alert(1)) [relative](/settings)',
'[mail](mailto:dev@example.com) [web](http://example.com) [upper](HTTPS://example.com)',
'![remote diagram](https://example.com/private.png)',
].join('\n\n')
const { container } = render(<MarkdownText text={markdown} />)
expect(container.querySelector('script')).toBeNull()
expect(container.querySelector('img')).toBeNull()
const neutralized = [...container.querySelectorAll('p')]
.find(paragraph => paragraph.textContent === 'script relative')
expect(neutralized?.querySelector('a')).toBeNull()
expect(screen.getByRole('link', { name: 'mail' }).getAttribute('target')).toBeNull()
expect(screen.getByRole('link', { name: 'web' }).getAttribute('rel')).toBe('noopener noreferrer')
expect(screen.getByRole('link', { name: 'upper' }).getAttribute('target')).toBe('_blank')
expect(screen.getByText('remote diagram')).toBeTruthy()
})
it('keeps incomplete streaming Markdown renderable', () => {
const { container } = render(<MarkdownText text={'## Streaming\n\n- first\n- **unfinished'} />)
expect(screen.getByRole('heading', { level: 2, name: 'Streaming' })).toBeTruthy()
expect(container.querySelectorAll('li')).toHaveLength(2)
expect(screen.getByText('**unfinished')).toBeTruthy()
})
})