@@ -70,3 +70,104 @@ describe('MudScreenReaderAnnouncer', () => {
7070 expect ( liveRegion . textContent ) . toBe ( '' ) ;
7171 } ) ;
7272} ) ;
73+
74+ describe ( 'MudScreenReaderAnnouncer - appendToHistory' , ( ) => {
75+ let historyRegion : HTMLElement ;
76+ let announcer : MudScreenReaderAnnouncer ;
77+
78+ beforeEach ( ( ) => {
79+ historyRegion = document . createElement ( 'div' ) ;
80+ announcer = new MudScreenReaderAnnouncer (
81+ document . createElement ( 'div' ) ,
82+ historyRegion ,
83+ ) ;
84+ } ) ;
85+
86+ afterEach ( ( ) => {
87+ announcer . dispose ( ) ;
88+ } ) ;
89+
90+ it ( 'splits multiline output into separate log items' , ( ) => {
91+ announcer . appendToHistory ( 'Line 1\nLine 2\nLine 3' ) ;
92+
93+ const items = historyRegion . querySelectorAll ( 'p.sr-log-item' ) ;
94+ expect ( items . length ) . toBe ( 3 ) ;
95+ expect ( items [ 0 ] . textContent ) . toBe ( 'Line 1' ) ;
96+ expect ( items [ 1 ] . textContent ) . toBe ( 'Line 2' ) ;
97+ expect ( items [ 2 ] . textContent ) . toBe ( 'Line 3' ) ;
98+ } ) ;
99+
100+ it ( 'skips empty lines' , ( ) => {
101+ announcer . appendToHistory ( 'Line 1\n\nLine 3\n' ) ;
102+
103+ const items = historyRegion . querySelectorAll ( 'p.sr-log-item' ) ;
104+ expect ( items . length ) . toBe ( 2 ) ;
105+ expect ( items [ 0 ] . textContent ) . toBe ( 'Line 1' ) ;
106+ expect ( items [ 1 ] . textContent ) . toBe ( 'Line 3' ) ;
107+ } ) ;
108+
109+ it ( 'skips lines with only whitespace' , ( ) => {
110+ announcer . appendToHistory ( 'Line 1\n \n\t\nLine 4' ) ;
111+
112+ const items = historyRegion . querySelectorAll ( 'p.sr-log-item' ) ;
113+ expect ( items . length ) . toBe ( 2 ) ;
114+ expect ( items [ 0 ] . textContent ) . toBe ( 'Line 1' ) ;
115+ expect ( items [ 1 ] . textContent ) . toBe ( 'Line 4' ) ;
116+ } ) ;
117+
118+ it ( 'handles CRLF line endings correctly' , ( ) => {
119+ announcer . appendToHistory ( 'Line 1\r\nLine 2\r\nLine 3' ) ;
120+
121+ const items = historyRegion . querySelectorAll ( 'p.sr-log-item' ) ;
122+ expect ( items . length ) . toBe ( 3 ) ;
123+ expect ( items [ 0 ] . textContent ) . toBe ( 'Line 1' ) ;
124+ expect ( items [ 1 ] . textContent ) . toBe ( 'Line 2' ) ;
125+ expect ( items [ 2 ] . textContent ) . toBe ( 'Line 3' ) ;
126+ } ) ;
127+
128+ it ( 'strips ANSI escape sequences from each line' , ( ) => {
129+ announcer . appendToHistory (
130+ 'Line 1 \x1b[31mRed\x1b[0m\nLine 2 \x1b[32mGreen\x1b[0m' ,
131+ ) ;
132+
133+ const items = historyRegion . querySelectorAll ( 'p.sr-log-item' ) ;
134+ expect ( items . length ) . toBe ( 2 ) ;
135+ expect ( items [ 0 ] . textContent ) . toBe ( 'Line 1 Red' ) ;
136+ expect ( items [ 1 ] . textContent ) . toBe ( 'Line 2 Green' ) ;
137+ } ) ;
138+
139+ it ( 'sets role="text" on each log item' , ( ) => {
140+ announcer . appendToHistory ( 'Line 1\nLine 2' ) ;
141+
142+ const items = historyRegion . querySelectorAll ( 'p.sr-log-item' ) ;
143+ expect ( items [ 0 ] . getAttribute ( 'role' ) ) . toBe ( 'text' ) ;
144+ expect ( items [ 1 ] . getAttribute ( 'role' ) ) . toBe ( 'text' ) ;
145+ } ) ;
146+
147+ it ( 'ignores empty normalized output' , ( ) => {
148+ announcer . appendToHistory ( '\x1b[31m\x1b[0m\r\n\x1b[32m\x1b[0m' ) ;
149+
150+ const items = historyRegion . querySelectorAll ( 'p.sr-log-item' ) ;
151+ expect ( items . length ) . toBe ( 0 ) ;
152+ } ) ;
153+
154+ it ( 'does nothing when history region is not provided' , ( ) => {
155+ const announcer2 = new MudScreenReaderAnnouncer (
156+ document . createElement ( 'div' ) ,
157+ undefined , // no history region
158+ ) ;
159+
160+ // Should not throw
161+ announcer2 . appendToHistory ( 'Line 1\nLine 2' ) ;
162+ } ) ;
163+
164+ it ( 'collapses excessive blank lines before splitting' , ( ) => {
165+ announcer . appendToHistory ( 'Line 1\n\n\n\nLine 5' ) ;
166+
167+ const items = historyRegion . querySelectorAll ( 'p.sr-log-item' ) ;
168+ // Should be 3 items: 'Line 1', '', 'Line 5' -> after filtering empty: 2 items
169+ expect ( items . length ) . toBe ( 2 ) ;
170+ expect ( items [ 0 ] . textContent ) . toBe ( 'Line 1' ) ;
171+ expect ( items [ 1 ] . textContent ) . toBe ( 'Line 5' ) ;
172+ } ) ;
173+ } ) ;
0 commit comments