@@ -4,6 +4,7 @@ import { rgbToHex, type CapturedSpan, type RGBA } from "@opentui/core";
44import { defined } from "../../tests/helpers/defined.js" ;
55import { withTestRenderer } from "./harness.js" ;
66import { runProviderSetup } from "./provider/setup.js" ;
7+ import { setPendingQueue } from "./shell/chrome.js" ;
78import { createAppShell } from "./shell/index.js" ;
89import { openSettingsOverlay } from "./shell/palette.js" ;
910import { runWelcome , WELCOME_LINE } from "./welcome.js" ;
@@ -237,6 +238,78 @@ describe("rendered semantic surfaces", () => {
237238 } ) ;
238239 }
239240
241+ for ( const [ mode , env , unselectedAlpha ] of [
242+ [ "default" , { } , 255 ] ,
243+ [ "transparent" , REQUESTED , 0 ] ,
244+ ] as const ) {
245+ test ( `${ themeName } ${ mode } pending selection has opaque backing` , async ( ) => {
246+ setTheme ( themeName ) ;
247+ configureTransparentBackground ( env ) ;
248+
249+ await withTestRenderer (
250+ async ( h ) => {
251+ const shell = createAppShell ( h . renderer , {
252+ terminal : { columns : 80 , rows : 24 } ,
253+ wireKeys : true ,
254+ } ) ;
255+ try {
256+ setPendingQueue ( shell , 3 ) ;
257+ await h . renderOnce ( ) ;
258+ const idle = h . captureSpans ( ) ;
259+ for ( const text of [ "pad-1" , "pad-2" , "pad-3" ] ) {
260+ expect ( alpha ( findSpan ( idle . lines , text ) . bg ) ) . toBe (
261+ unselectedAlpha ,
262+ ) ;
263+ }
264+
265+ h . pressKey ( "ARROW_UP" ) ;
266+ await h . renderOnce ( ) ;
267+ const initialSelection = h . captureSpans ( ) ;
268+ const initialMarker = findSpan ( initialSelection . lines , "▸" ) ;
269+ const initialSelected = findSpan ( initialSelection . lines , "pad-3" ) ;
270+ expect ( alpha ( initialMarker . bg ) ) . toBe ( 255 ) ;
271+ expect ( alpha ( initialSelected . bg ) ) . toBe ( 255 ) ;
272+ expect (
273+ rgbToHex ( initialSelected . bg ) . toLowerCase ( ) . slice ( 0 , 7 ) ,
274+ ) . toBe ( theme . ground ) ;
275+ expect (
276+ rgbToHex ( initialSelected . fg ) . toLowerCase ( ) . slice ( 0 , 7 ) ,
277+ ) . toBe ( theme . text ) ;
278+ expect (
279+ contrast ( initialSelected . fg , initialSelected . bg ) ,
280+ ) . toBeGreaterThanOrEqual ( 4.5 ) ;
281+
282+ h . pressKey ( "ARROW_UP" ) ;
283+ await h . renderOnce ( ) ;
284+ const movedSelection = h . captureSpans ( ) ;
285+ const unselected = findSpan ( movedSelection . lines , "pad-3" ) ;
286+ const selected = findSpan ( movedSelection . lines , "pad-2" ) ;
287+ const untouched = findSpan ( movedSelection . lines , "pad-1" ) ;
288+ expect ( alpha ( findSpan ( movedSelection . lines , "▸" ) . bg ) ) . toBe ( 255 ) ;
289+ expect ( alpha ( selected . bg ) ) . toBe ( 255 ) ;
290+ expect ( alpha ( unselected . bg ) ) . toBe ( unselectedAlpha ) ;
291+ expect ( alpha ( untouched . bg ) ) . toBe ( unselectedAlpha ) ;
292+ expect ( rgbToHex ( unselected . fg ) . toLowerCase ( ) . slice ( 0 , 7 ) ) . toBe (
293+ theme . textDim ,
294+ ) ;
295+ expect ( rgbToHex ( selected . bg ) . toLowerCase ( ) . slice ( 0 , 7 ) ) . toBe (
296+ theme . ground ,
297+ ) ;
298+ expect ( rgbToHex ( selected . fg ) . toLowerCase ( ) . slice ( 0 , 7 ) ) . toBe (
299+ theme . text ,
300+ ) ;
301+ expect ( contrast ( selected . fg , selected . bg ) ) . toBeGreaterThanOrEqual (
302+ 4.5 ,
303+ ) ;
304+ } finally {
305+ shell . dispose ( ) ;
306+ }
307+ } ,
308+ { width : 80 , height : 24 } ,
309+ ) ;
310+ } ) ;
311+ }
312+
240313 test ( `${ themeName } transparent welcome uses selected text roles` , async ( ) => {
241314 setTheme ( themeName ) ;
242315 configureTransparentBackground ( REQUESTED ) ;
0 commit comments