1+ import { render , screen } from '@testing-library/react' ;
2+ import { describe , it , expect , vi } from 'vitest' ;
3+ import { RenderMarkdown } from '../RenderMarkdown' ;
4+
5+ vi . mock ( '@i18n/react-i18next-compat' , ( ) => ( {
6+ useTranslation : ( ) => ( {
7+ t : ( key : string ) => key
8+ } )
9+ } ) )
10+
11+ // Mock clipboard API
12+ Object . assign ( navigator , {
13+ clipboard : {
14+ writeText : vi . fn ( )
15+ }
16+ } )
17+
18+ describe ( 'RenderMarkdown' , ( ) => {
19+ it ( 'preserves line breaks in model responses (when isUser == undefined)' , ( ) => {
20+ const modelResponseWithNewLines = `This is line 1
21+ This is line 2
22+ This is line 3`
23+ render (
24+ < RenderMarkdown
25+ content = { modelResponseWithNewLines }
26+ />
27+ )
28+ const markdownContainer = document . querySelector ( '.markdown' )
29+ expect ( markdownContainer ?. innerHTML ) . toContain ( '<br>' )
30+ // Match either <br> or <br/>
31+ const brCount = ( markdownContainer ?. innerHTML . match ( / < b r \s * \/ ? > / g) || [ ] ) . length
32+ expect ( brCount ) . toBe ( 2 )
33+ } )
34+
35+ it ( 'preserves line breaks in user message (when isUser == true)' , ( ) => {
36+ const userMessageWithNewlines = `User question line 1
37+ User question line 2
38+ User question line 3`
39+ render (
40+ < RenderMarkdown
41+ content = { userMessageWithNewlines }
42+ isUser = { true }
43+ />
44+ )
45+ const markdownContainer = document . querySelector ( '.markdown' )
46+ expect ( markdownContainer ) . toBeTruthy ( )
47+ expect ( markdownContainer ?. innerHTML ) . toContain ( '<br>' )
48+ const brCount = ( markdownContainer ?. innerHTML . match ( / < b r \s * \/ ? > / g) || [ ] ) . length
49+ expect ( brCount ) . toBe ( 2 )
50+ } )
51+
52+ it ( 'preserves line breaks with different line ending types' , ( ) => {
53+ const contentWithDifferentLineEndings = "Line1\nLine2\r\nLine3\rLine4"
54+ render (
55+ < RenderMarkdown
56+ content = { contentWithDifferentLineEndings }
57+ />
58+ )
59+ const markdownContainer = document . querySelector ( '.markdown' )
60+ expect ( markdownContainer ?. innerHTML ) . toContain ( '<br>' )
61+ const brCount = ( markdownContainer ?. innerHTML . match ( / < b r \s * \/ ? > / g) || [ ] ) . length
62+ expect ( brCount ) . toBe ( 3 )
63+ } )
64+
65+ it ( 'handles empty lines correctly' , ( ) => {
66+ const contentWithEmptyLines = 'Line 1\n\nLine 3 (after empty line)\n\nLine 5 (after two empty lines)'
67+ render (
68+ < RenderMarkdown
69+ content = { contentWithEmptyLines }
70+ />
71+ )
72+ const markdownContainer = document . querySelector ( '.markdown' )
73+ const html = markdownContainer ?. innerHTML || ''
74+ // Double new lines (`\n\n`) creates paragraph breaks, not line breaks
75+ expect ( html ) . not . toContain ( '<br>' )
76+ const paragraphCount = ( html . match ( / < p > / g) || [ ] ) . length
77+ expect ( paragraphCount ) . toBe ( 3 ) // Expect 3 paragraphs for 2 empty lines
78+ } )
79+ } )
0 commit comments