@@ -221,7 +221,7 @@ function kill(s: Session): void {
221221
222222interface PageState { consoleErrors : string [ ] , console : string [ ] , responses : { url : string , status : number , ms : number , type : string } [ ] , mainStatus : number | null }
223223
224- async function gotoAndInstrument ( cdp : Cdp , url : string , opts : { viewport ?: { w : number , h : number } , scale ?: number , timeoutMs ?: number , cookies ?: string [ ] , settleMs ?: number } = { } ) : Promise < PageState > {
224+ async function gotoAndInstrument ( cdp : Cdp , url : string , opts : { viewport ?: { w : number , h : number } , scale ?: number , timeoutMs ?: number , cookies ?: string [ ] , settleMs ?: number , scheme ?: string } = { } ) : Promise < PageState > {
225225 const state : PageState = { consoleErrors : [ ] , console : [ ] , responses : [ ] , mainStatus : null }
226226 const startById = new Map < string , number > ( )
227227
@@ -256,6 +256,14 @@ async function gotoAndInstrument(cdp: Cdp, url: string, opts: { viewport?: { w:
256256 } )
257257 }
258258
259+ // Emulate light/dark so prefers-color-scheme pages can be QA'd in both
260+ // schemes without flipping the host OS setting.
261+ if ( opts . scheme === 'light' || opts . scheme === 'dark' ) {
262+ await cdp . send ( 'Emulation.setEmulatedMedia' , {
263+ features : [ { name : 'prefers-color-scheme' , value : opts . scheme } ] ,
264+ } )
265+ }
266+
259267 cdp . on ( ( e ) => {
260268 if ( e . method === 'Runtime.consoleAPICalled' ) {
261269 const text = ( e . params . args || [ ] ) . map ( ( a : any ) => a . value ?? a . description ?? a . type ) . join ( ' ' )
@@ -371,17 +379,19 @@ async function main() {
371379 console . log ( 'Usage: bun browse.ts <navigate|screenshot|responsive|monitor|snapshot> <url> [flags]' )
372380 console . log ( ' --cookie "name=value" repeatable; pre-seeds cookies (e.g. coming-soon bypass)' )
373381 console . log ( ' --settle 1500 ms to wait after load before acting (default 700; stretch for entrance animations)' )
382+ console . log ( ' --scheme dark emulate prefers-color-scheme (light|dark) for QA of theme-aware pages' )
374383 process . exit ( url ? 0 : 1 )
375384 }
376385
377386 const session = await launch ( )
378387 const cookies = flagList ( flags . cookie )
379388 const settleMs = flags . settle ? Number ( flags . settle ) : undefined
389+ const scheme = typeof flags . scheme === 'string' ? flags . scheme : undefined
380390 try {
381391 if ( command === 'navigate' || command === 'go' ) {
382392 const cdp = await openPage ( session . port )
383393 const t0 = performance . now ( )
384- const state = await gotoAndInstrument ( cdp , url , { cookies, settleMs } )
394+ const state = await gotoAndInstrument ( cdp , url , { cookies, settleMs, scheme } )
385395 const loadMs = Math . round ( performance . now ( ) - t0 )
386396 console . log ( JSON . stringify ( {
387397 browser : session . browser ,
@@ -401,7 +411,7 @@ async function main() {
401411 const scale = flags . scale ? Number ( flags . scale ) : 1
402412 const out = ( flags . out as string ) || `.stacks/shots/${ new URL ( url ) . pathname . replace ( / [ ^ a - z 0 - 9 ] + / gi, '-' ) . replace ( / ^ - | - $ / g, '' ) || 'home' } .png`
403413 mkdirSync ( out . split ( '/' ) . slice ( 0 , - 1 ) . join ( '/' ) || '.' , { recursive : true } )
404- await gotoAndInstrument ( cdp , url , { viewport : { w : vp [ 0 ] , h : vp [ 1 ] } , scale, cookies, settleMs } )
414+ await gotoAndInstrument ( cdp , url , { viewport : { w : vp [ 0 ] , h : vp [ 1 ] } , scale, cookies, settleMs, scheme } )
405415 const png = await captureScreenshot ( cdp , { full : ! ! flags . full , element : flags . element as string | undefined } )
406416 await Bun . write ( out , png )
407417 console . log ( JSON . stringify ( { url, out, viewport : `${ vp [ 0 ] } x${ vp [ 1 ] } ` , scale, full : ! ! flags . full , element : flags . element ?? null , bytes : png . length } , null , 2 ) )
@@ -414,7 +424,7 @@ async function main() {
414424 const results : any [ ] = [ ]
415425 for ( const bp of BREAKPOINTS ) {
416426 const cdp = await openPage ( session . port )
417- await gotoAndInstrument ( cdp , url , { viewport : { w : bp . w , h : bp . h } , cookies, settleMs } )
427+ await gotoAndInstrument ( cdp , url , { viewport : { w : bp . w , h : bp . h } , cookies, settleMs, scheme } )
418428 const overflow = await cdp . send ( 'Runtime.evaluate' , {
419429 expression : 'document.documentElement.scrollWidth > window.innerWidth ? document.documentElement.scrollWidth - window.innerWidth : 0' ,
420430 returnByValue : true ,
@@ -430,7 +440,7 @@ async function main() {
430440 else if ( command === 'monitor' ) {
431441 const cdp = await openPage ( session . port )
432442 const ms = flags . ms ? Number ( flags . ms ) : 5000
433- const state = await gotoAndInstrument ( cdp , url , { cookies, settleMs } )
443+ const state = await gotoAndInstrument ( cdp , url , { cookies, settleMs, scheme } )
434444 await Bun . sleep ( ms )
435445 const failed = state . responses . filter ( r => r . status >= 400 )
436446 const slow = state . responses . filter ( r => r . ms > 3000 )
@@ -447,7 +457,7 @@ async function main() {
447457
448458 else if ( command === 'snapshot' ) {
449459 const cdp = await openPage ( session . port )
450- await gotoAndInstrument ( cdp , url , { cookies, settleMs } )
460+ await gotoAndInstrument ( cdp , url , { cookies, settleMs, scheme } )
451461 const expr = `(() => {
452462 const sel = (q) => Array.from(document.querySelectorAll(q));
453463 const txt = (e) => (e.innerText || e.textContent || '').trim().slice(0, 80);
0 commit comments