import type { StreamdownProps } from 'streamdown' import type { SimplePluginInfo } from '../streamdown-wrapper' import { render, screen } from '@testing-library/react' import { Markdown } from '../index' const { mockReactMarkdownWrapper } = vi.hoisted(() => ({ mockReactMarkdownWrapper: vi.fn(), })) vi.mock('@/next/dynamic', () => ({ default: () => { const MockStreamdownWrapper = (props: { latexContent: string }) => { mockReactMarkdownWrapper(props) return
{props.latexContent}
} MockStreamdownWrapper.displayName = 'MockStreamdownWrapper' return MockStreamdownWrapper }, })) type CapturedProps = { latexContent: string pluginInfo?: SimplePluginInfo customComponents?: StreamdownProps['components'] customDisallowedElements?: string[] rehypePlugins?: StreamdownProps['rehypePlugins'] isAnimating?: StreamdownProps['isAnimating'] mode?: StreamdownProps['mode'] } const getLastWrapperProps = (): CapturedProps => { const calls = mockReactMarkdownWrapper.mock.calls const lastCall = calls[calls.length - 1] return lastCall[0] as CapturedProps } describe('Markdown', () => { beforeEach(() => { vi.clearAllMocks() }) it('should render wrapper content', () => { render() expect(screen.getByTestId('react-markdown-wrapper')).toHaveTextContent('Hello World') }) it('should apply default classes', () => { const { container } = render() const markdownDiv = container.querySelector('.markdown-body') expect(markdownDiv).toHaveClass('markdown-body', '!text-text-primary') }) it('should merge custom className with default classes', () => { const { container } = render() const markdownDiv = container.querySelector('.markdown-body') expect(markdownDiv).toHaveClass('markdown-body', '!text-text-primary', 'custom', 'another') }) it('should not include undefined in className', () => { const { container } = render() const markdownDiv = container.querySelector('.markdown-body') expect(markdownDiv?.className).not.toContain('undefined') }) it('should preprocess think tags', () => { render() const props = getLastWrapperProps() expect(props.latexContent).toContain('
') expect(props.latexContent).toContain('Thought') expect(props.latexContent).toContain('[ENDTHINKFLAG]
') }) it('should preprocess latex block notation', () => { render() const props = getLastWrapperProps() expect(props.latexContent).toContain('$$x^2 + y^2 = z^2$$') }) it('should preprocess latex parentheses notation', () => { render() const props = getLastWrapperProps() expect(props.latexContent).toContain('$$a + b$$') }) it('should preserve latex inside code blocks', () => { render() const props = getLastWrapperProps() expect(props.latexContent).toContain('$E = mc^2$') }) it('should pass pluginInfo through', () => { const pluginInfo = { pluginUniqueIdentifier: 'plugin-unique', pluginId: 'plugin-id', } render() const props = getLastWrapperProps() expect(props.pluginInfo).toEqual(pluginInfo) }) it('should pass default empty customComponents when omitted', () => { render() const props = getLastWrapperProps() expect(props.customComponents).toEqual({}) }) it('should pass customComponents through', () => { const customComponents = { h1: ({ children }: { children?: React.ReactNode }) =>

{children}

, } render() const props = getLastWrapperProps() expect(props.customComponents).toBe(customComponents) }) it('should pass customDisallowedElements through', () => { const customDisallowedElements = ['strong', 'em'] render() const props = getLastWrapperProps() expect(props.customDisallowedElements).toBe(customDisallowedElements) }) it('should pass rehypePlugins through', () => { const plugin = () => (tree: unknown) => tree const rehypePlugins = [plugin] render() const props = getLastWrapperProps() expect(props.rehypePlugins).toBe(rehypePlugins) }) it('should pass isAnimating through', () => { render() const props = getLastWrapperProps() expect(props.isAnimating).toBe(true) }) it('should pass mode through', () => { render() const props = getLastWrapperProps() expect(props.mode).toBe('streaming') }) })