@@ -14,7 +14,8 @@ import { appMeta, AppWithMocks, type AppStory } from "./meta.js";
1414import { createWorkspace , groupWorkspacesByProject } from "./mockFactory" ;
1515import { selectWorkspace } from "./storyHelpers" ;
1616import { createMockORPCClient } from "../../../.storybook/mocks/orpc" ;
17- import { within , waitFor , userEvent } from "@storybook/test" ;
17+ import { within , userEvent } from "@storybook/test" ;
18+ import { getExperimentKey , EXPERIMENT_IDS } from "@/common/constants/experiments" ;
1819
1920export default {
2021 ...appMeta ,
@@ -29,11 +30,21 @@ export default {
2930function setupSettingsStory ( options : {
3031 providersConfig ?: Record < string , { apiKeySet : boolean ; baseUrl ?: string ; models ?: string [ ] } > ;
3132 providersList ?: string [ ] ;
33+ /** Pre-set experiment states in localStorage before render */
34+ experiments ?: Partial < Record < string , boolean > > ;
3235} ) : APIClient {
3336 const workspaces = [ createWorkspace ( { id : "ws-1" , name : "main" , projectName : "my-app" } ) ] ;
3437
3538 selectWorkspace ( workspaces [ 0 ] ) ;
3639
40+ // Pre-set experiment states if provided
41+ if ( options . experiments ) {
42+ for ( const [ experimentId , enabled ] of Object . entries ( options . experiments ) ) {
43+ const key = getExperimentKey ( experimentId as typeof EXPERIMENT_IDS . POST_COMPACTION_CONTEXT ) ;
44+ window . localStorage . setItem ( key , JSON . stringify ( enabled ) ) ;
45+ }
46+ }
47+
3748 return createMockORPCClient ( {
3849 projects : groupWorkspacesByProject ( workspaces ) ,
3950 workspaces,
@@ -45,33 +56,23 @@ function setupSettingsStory(options: {
4556/** Open settings modal and optionally navigate to a section */
4657async function openSettingsToSection ( canvasElement : HTMLElement , section ?: string ) : Promise < void > {
4758 const canvas = within ( canvasElement ) ;
59+ // Use ownerDocument.body to scope to iframe, not parent Storybook UI
60+ const body = within ( canvasElement . ownerDocument . body ) ;
4861
4962 // Wait for app to fully load (sidebar with settings button should appear)
50- // Use longer timeout since app initialization can take time
5163 const settingsButton = await canvas . findByTestId ( "settings-button" , { } , { timeout : 10000 } ) ;
5264 await userEvent . click ( settingsButton ) ;
5365
54- // Wait for modal to appear - Radix Dialog uses a portal so we need to search the entire document
55- const body = within ( document . body ) ;
56- await waitFor (
57- ( ) => {
58- const modal = body . getByRole ( "dialog" ) ;
59- if ( ! modal ) throw new Error ( "Settings modal not found" ) ;
60- } ,
61- { timeout : 5000 }
62- ) ;
66+ // Wait for dialog to appear (portal renders outside canvasElement but inside iframe body)
67+ await body . findByRole ( "dialog" ) ;
6368
6469 // Navigate to specific section if requested
65- // The sidebar nav has buttons with exact section names
6670 if ( section && section !== "general" ) {
67- const modal = body . getByRole ( "dialog" ) ;
68- const modalCanvas = within ( modal ) ;
69- // Find the nav section button (exact text match)
70- const navButtons = await modalCanvas . findAllByRole ( "button" ) ;
71- const sectionButton = navButtons . find (
72- ( btn ) => btn . textContent ?. toLowerCase ( ) . trim ( ) === section . toLowerCase ( )
73- ) ;
74- if ( ! sectionButton ) throw new Error ( `Section button "${ section } " not found` ) ;
71+ // Capitalize first letter to match the button text (e.g., "experiments" -> "Experiments")
72+ const sectionLabel = section . charAt ( 0 ) . toUpperCase ( ) + section . slice ( 1 ) ;
73+ const sectionButton = await body . findByRole ( "button" , {
74+ name : new RegExp ( sectionLabel , "i" ) ,
75+ } ) ;
7576 await userEvent . click ( sectionButton ) ;
7677 }
7778}
@@ -175,43 +176,27 @@ export const Experiments: AppStory = {
175176 } ,
176177} ;
177178
178- /** Experiments section - toggle experiment on */
179+ /** Experiments section - shows experiment in ON state (pre-enabled via localStorage) */
179180export const ExperimentsToggleOn : AppStory = {
180- render : ( ) => < AppWithMocks setup = { ( ) => setupSettingsStory ( { } ) } /> ,
181+ render : ( ) => (
182+ < AppWithMocks
183+ setup = { ( ) =>
184+ setupSettingsStory ( {
185+ experiments : { [ EXPERIMENT_IDS . POST_COMPACTION_CONTEXT ] : true } ,
186+ } )
187+ }
188+ />
189+ ) ,
181190 play : async ( { canvasElement } : { canvasElement : HTMLElement } ) => {
182191 await openSettingsToSection ( canvasElement , "experiments" ) ;
183-
184- // Find and click the switch to toggle it on
185- const body = within ( document . body ) ;
186- const modal = body . getByRole ( "dialog" ) ;
187- const modalCanvas = within ( modal ) ;
188-
189- // Find the switch by its role - experiments use role="switch"
190- const switches = await modalCanvas . findAllByRole ( "switch" ) ;
191- if ( switches . length > 0 ) {
192- // Toggle the first experiment on
193- await userEvent . click ( switches [ 0 ] ) ;
194- }
195192 } ,
196193} ;
197194
198- /** Experiments section - toggle experiment off (starts enabled, then toggles off ) */
195+ /** Experiments section - shows experiment in OFF state (default ) */
199196export const ExperimentsToggleOff : AppStory = {
200197 render : ( ) => < AppWithMocks setup = { ( ) => setupSettingsStory ( { } ) } /> ,
201198 play : async ( { canvasElement } : { canvasElement : HTMLElement } ) => {
202199 await openSettingsToSection ( canvasElement , "experiments" ) ;
203-
204- const body = within ( document . body ) ;
205- const modal = body . getByRole ( "dialog" ) ;
206- const modalCanvas = within ( modal ) ;
207-
208- // Find the switch
209- const switches = await modalCanvas . findAllByRole ( "switch" ) ;
210- if ( switches . length > 0 ) {
211- // Toggle on first
212- await userEvent . click ( switches [ 0 ] ) ;
213- // Then toggle off
214- await userEvent . click ( switches [ 0 ] ) ;
215- }
200+ // Default state is OFF - no clicks needed
216201 } ,
217202} ;
0 commit comments