@@ -2,50 +2,91 @@ import { afterEach, describe, expect, test, vi } from 'vitest'
22import { generateConsolePipeCode } from './virtual-console'
33
44const TEST_VITE_URL = 'http://localhost:5173'
5+ const SERVER_PREFIX = '%c[Server]%c'
6+ const SERVER_PREFIX_STYLE = 'color: #9333ea; font-weight: bold;'
7+ const SERVER_RESET_STYLE = 'color: inherit;'
58
69afterEach ( ( ) => {
710 vi . useRealTimers ( )
811 vi . unstubAllGlobals ( )
912 delete ( window as any ) . __TSD_CONSOLE_PIPE_INITIALIZED__
1013} )
1114
12- function setupWarnConsolePipe ( ) {
13- const originalWarn = console . warn
14- const originalWarnMock = vi . fn ( )
15+ function setupConsolePipe (
16+ levels : Parameters < typeof generateConsolePipeCode > [ 0 ] ,
17+ ) {
18+ const originalConsoleMethods : Partial < Record < string , typeof console . log > > = { }
19+ const consoleMocks : Record < string , ReturnType < typeof vi . fn > > = { }
1520 const fetchMock = vi . fn ( ) . mockResolvedValue ( undefined )
1621 const eventSourceUrls : Array < string > = [ ]
22+ const eventSources : Array < {
23+ onmessage : ( ( event : MessageEvent ) => void ) | null
24+ onerror : ( ( ) => void ) | null
25+ } > = [ ]
1726
1827 class MockEventSource {
1928 onmessage : ( ( event : MessageEvent ) => void ) | null = null
2029 onerror : ( ( ) => void ) | null = null
2130
2231 constructor ( url : string ) {
2332 eventSourceUrls . push ( url )
33+ eventSources . push ( this )
2434 }
2535 }
2636
27- console . warn = originalWarnMock
37+ for ( const level of levels ) {
38+ originalConsoleMethods [ level ] = console [ level ]
39+ consoleMocks [ level ] = vi . fn ( )
40+ console [ level ] = consoleMocks [ level ] as typeof console . log
41+ }
42+
2843 vi . stubGlobal ( 'fetch' , fetchMock )
2944 vi . stubGlobal ( 'EventSource' , MockEventSource )
3045
31- const code = generateConsolePipeCode ( [ 'warn' ] , TEST_VITE_URL )
46+ const code = generateConsolePipeCode ( levels , TEST_VITE_URL )
3247 new Function ( code ) ( )
3348
3449 return {
50+ consoleMocks,
51+ eventSources,
3552 eventSourceUrls,
3653 fetchMock,
37- originalWarnMock,
3854 restore : ( ) => {
39- console . warn = originalWarn
55+ for ( const level of levels ) {
56+ const original = originalConsoleMethods [ level ]
57+ if ( original ) {
58+ console [ level ] = original
59+ }
60+ }
4061 } ,
4162 }
4263}
4364
65+ function setupWarnConsolePipe ( ) {
66+ const setup = setupConsolePipe ( [ 'warn' ] )
67+
68+ return {
69+ ...setup ,
70+ originalWarnMock : setup . consoleMocks . warn ,
71+ }
72+ }
73+
4474function getFirstFetchBody ( fetchMock : ReturnType < typeof vi . fn > ) {
4575 const [ , init ] = fetchMock . mock . calls [ 0 ] !
4676 return JSON . parse ( init . body )
4777}
4878
79+ function dispatchServerEntries (
80+ eventSource : { onmessage : ( ( event : MessageEvent ) => void ) | null } ,
81+ entries : Array < { level : string ; args : Array < unknown > } > ,
82+ ) {
83+ eventSource . onmessage ?.(
84+ new MessageEvent ( 'message' , {
85+ data : JSON . stringify ( { entries } ) ,
86+ } ) ,
87+ )
88+ }
89+
4990describe ( 'virtual-console' , ( ) => {
5091 test ( 'generates inline code with specified levels' , ( ) => {
5192 const code = generateConsolePipeCode ( [ 'log' , 'error' ] , TEST_VITE_URL )
@@ -68,6 +109,120 @@ describe('virtual-console', () => {
68109 expect ( code ) . toContain ( "new EventSource('/__tsd/console-pipe/sse')" )
69110 } )
70111
112+ test ( 'preserves server log format substitutions' , ( ) => {
113+ const { consoleMocks, eventSources, restore } = setupConsolePipe ( [ 'log' ] )
114+
115+ try {
116+ dispatchServerEntries ( eventSources [ 0 ] ! , [
117+ {
118+ level : 'log' ,
119+ args : [ '%s info GET %s %d' , 'ts' , '/route' , 200 ] ,
120+ } ,
121+ ] )
122+
123+ expect ( consoleMocks . log ) . toHaveBeenCalledWith (
124+ SERVER_PREFIX + ' %s info GET %s %d' ,
125+ SERVER_PREFIX_STYLE ,
126+ SERVER_RESET_STYLE ,
127+ 'ts' ,
128+ '/route' ,
129+ 200 ,
130+ )
131+ } finally {
132+ restore ( )
133+ }
134+ } )
135+
136+ test ( 'preserves server log css format substitutions' , ( ) => {
137+ const { consoleMocks, eventSources, restore } = setupConsolePipe ( [ 'log' ] )
138+
139+ try {
140+ dispatchServerEntries ( eventSources [ 0 ] ! , [
141+ {
142+ level : 'log' ,
143+ args : [ '%cstarted' , 'color: green' ] ,
144+ } ,
145+ ] )
146+
147+ expect ( consoleMocks . log ) . toHaveBeenCalledWith (
148+ SERVER_PREFIX + ' %cstarted' ,
149+ SERVER_PREFIX_STYLE ,
150+ SERVER_RESET_STYLE ,
151+ 'color: green' ,
152+ )
153+ } finally {
154+ restore ( )
155+ }
156+ } )
157+
158+ test ( 'preserves empty server log format substitutions' , ( ) => {
159+ const { consoleMocks, eventSources, restore } = setupConsolePipe ( [ 'log' ] )
160+
161+ try {
162+ dispatchServerEntries ( eventSources [ 0 ] ! , [
163+ {
164+ level : 'log' ,
165+ args : [ '%s%s%s' , '' , ' ' , 'ok' ] ,
166+ } ,
167+ ] )
168+
169+ expect ( consoleMocks . log ) . toHaveBeenCalledWith (
170+ SERVER_PREFIX + ' %s%s%s' ,
171+ SERVER_PREFIX_STYLE ,
172+ SERVER_RESET_STYLE ,
173+ '' ,
174+ ' ' ,
175+ 'ok' ,
176+ )
177+ } finally {
178+ restore ( )
179+ }
180+ } )
181+
182+ test ( 'keeps plain server log prefix arguments unchanged' , ( ) => {
183+ const { consoleMocks, eventSources, restore } = setupConsolePipe ( [ 'warn' ] )
184+
185+ try {
186+ dispatchServerEntries ( eventSources [ 0 ] ! , [
187+ {
188+ level : 'warn' ,
189+ args : [ 'plain message' ] ,
190+ } ,
191+ ] )
192+
193+ expect ( consoleMocks . warn ) . toHaveBeenCalledWith (
194+ SERVER_PREFIX ,
195+ SERVER_PREFIX_STYLE ,
196+ SERVER_RESET_STYLE ,
197+ 'plain message' ,
198+ )
199+ } finally {
200+ restore ( )
201+ }
202+ } )
203+
204+ test ( 'does not treat non-string first server log args as format strings' , ( ) => {
205+ const { consoleMocks, eventSources, restore } = setupConsolePipe ( [ 'log' ] )
206+
207+ try {
208+ dispatchServerEntries ( eventSources [ 0 ] ! , [
209+ {
210+ level : 'log' ,
211+ args : [ { a : 1 } ] ,
212+ } ,
213+ ] )
214+
215+ expect ( consoleMocks . log ) . toHaveBeenCalledWith (
216+ SERVER_PREFIX ,
217+ SERVER_PREFIX_STYLE ,
218+ SERVER_RESET_STYLE ,
219+ { a : 1 } ,
220+ )
221+ } finally {
222+ restore ( )
223+ }
224+ } )
225+
71226 test ( 'includes environment detection' , ( ) => {
72227 const code = generateConsolePipeCode ( [ 'log' ] , TEST_VITE_URL )
73228
0 commit comments