@@ -28,7 +28,17 @@ type Footer = {
2828type Probe = {
2929 dir ?: string
3030 sessionID ?: string
31- model ?: { providerID : string ; modelID : string }
31+ agent ?: string
32+ model ?: { providerID : string ; modelID : string ; name ?: string }
33+ variant ?: string | null
34+ pick ?: {
35+ agent ?: string
36+ model ?: { providerID : string ; modelID : string }
37+ variant ?: string | null
38+ }
39+ variants ?: string [ ]
40+ models ?: Array < { providerID : string ; modelID : string ; name : string } >
41+ agents ?: Array < { name : string } >
3242}
3343
3444const escape = ( value : string ) => value . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, "\\$&" )
@@ -50,6 +60,86 @@ async function probe(page: Page): Promise<Probe | null> {
5060 } )
5161}
5262
63+ async function currentModel ( page : Page ) {
64+ await expect . poll ( ( ) => probe ( page ) . then ( modelKey ) , { timeout : 30_000 } ) . not . toBe ( null )
65+ const value = await probe ( page ) . then ( modelKey )
66+ if ( ! value ) throw new Error ( "Failed to resolve current model key" )
67+ return value
68+ }
69+
70+ async function waitControl ( page : Page , key : "setAgent" | "setModel" | "setVariant" ) {
71+ await expect
72+ . poll (
73+ ( ) =>
74+ page . evaluate ( ( key ) => {
75+ const win = window as Window & {
76+ __opencode_e2e ?: {
77+ model ?: {
78+ controls ?: Record < string , unknown >
79+ }
80+ }
81+ }
82+ return ! ! win . __opencode_e2e ?. model ?. controls ?. [ key ]
83+ } , key ) ,
84+ { timeout : 30_000 } ,
85+ )
86+ . toBe ( true )
87+ }
88+
89+ async function pickAgent ( page : Page , value : string ) {
90+ await waitControl ( page , "setAgent" )
91+ await page . evaluate ( ( value ) => {
92+ const win = window as Window & {
93+ __opencode_e2e ?: {
94+ model ?: {
95+ controls ?: {
96+ setAgent ?: ( value : string | undefined ) => void
97+ }
98+ }
99+ }
100+ }
101+ const fn = win . __opencode_e2e ?. model ?. controls ?. setAgent
102+ if ( ! fn ) throw new Error ( "Model e2e agent control is not enabled" )
103+ fn ( value )
104+ } , value )
105+ }
106+
107+ async function pickModel ( page : Page , value : { providerID : string ; modelID : string } ) {
108+ await waitControl ( page , "setModel" )
109+ await page . evaluate ( ( value ) => {
110+ const win = window as Window & {
111+ __opencode_e2e ?: {
112+ model ?: {
113+ controls ?: {
114+ setModel ?: ( value : { providerID : string ; modelID : string } | undefined ) => void
115+ }
116+ }
117+ }
118+ }
119+ const fn = win . __opencode_e2e ?. model ?. controls ?. setModel
120+ if ( ! fn ) throw new Error ( "Model e2e model control is not enabled" )
121+ fn ( value )
122+ } , value )
123+ }
124+
125+ async function pickVariant ( page : Page , value : string ) {
126+ await waitControl ( page , "setVariant" )
127+ await page . evaluate ( ( value ) => {
128+ const win = window as Window & {
129+ __opencode_e2e ?: {
130+ model ?: {
131+ controls ?: {
132+ setVariant ?: ( value : string | undefined ) => void
133+ }
134+ }
135+ }
136+ }
137+ const fn = win . __opencode_e2e ?. model ?. controls ?. setVariant
138+ if ( ! fn ) throw new Error ( "Model e2e variant control is not enabled" )
139+ fn ( value )
140+ } , value )
141+ }
142+
53143async function read ( page : Page ) : Promise < Footer > {
54144 return {
55145 agent : await text ( page . locator ( `${ promptAgentSelector } [data-slot="select-select-trigger-value"]` ) . first ( ) ) ,
@@ -82,31 +172,15 @@ async function waitModel(page: Page, value: string) {
82172async function choose ( page : Page , root : string , value : string ) {
83173 const select = page . locator ( root )
84174 await expect ( select ) . toBeVisible ( )
85- await select . locator ( '[data-action], [data-slot="select-select-trigger"]' ) . first ( ) . click ( )
86- const item = page
87- . locator ( '[data-slot="select-select-item"]' )
88- . filter ( { hasText : new RegExp ( `^\\s*${ escape ( value ) } \\s*$` ) } )
89- . first ( )
90- await expect ( item ) . toBeVisible ( )
91- await item . click ( )
175+ await pickAgent ( page , value )
92176}
93177
94178async function variantCount ( page : Page ) {
95- const select = page . locator ( promptVariantSelector )
96- await expect ( select ) . toBeVisible ( )
97- await select . locator ( '[data-slot="select-select-trigger"]' ) . click ( )
98- const count = await page . locator ( '[data-slot="select-select-item"]' ) . count ( )
99- await page . keyboard . press ( "Escape" )
100- return count
179+ return ( await probe ( page ) ) ?. variants ?. length ?? 0
101180}
102181
103182async function agents ( page : Page ) {
104- const select = page . locator ( promptAgentSelector )
105- await expect ( select ) . toBeVisible ( )
106- await select . locator ( '[data-action], [data-slot="select-select-trigger"]' ) . first ( ) . click ( )
107- const labels = await page . locator ( '[data-slot="select-select-item-label"]' ) . allTextContents ( )
108- await page . keyboard . press ( "Escape" )
109- return labels . map ( ( item ) => item . trim ( ) ) . filter ( Boolean )
183+ return ( ( await probe ( page ) ) ?. agents ?? [ ] ) . map ( ( item ) => item . name ) . filter ( Boolean )
110184}
111185
112186async function ensureVariant ( page : Page , directory : string ) : Promise < Footer > {
@@ -132,48 +206,23 @@ async function ensureVariant(page: Page, directory: string): Promise<Footer> {
132206
133207async function chooseDifferentVariant ( page : Page ) : Promise < Footer > {
134208 const current = await read ( page )
135- const select = page . locator ( promptVariantSelector )
136- await expect ( select ) . toBeVisible ( )
137- await select . locator ( '[data-slot="select-select-trigger"]' ) . click ( )
138-
139- const items = page . locator ( '[data-slot="select-select-item"]' )
140- const count = await items . count ( )
141- if ( count < 2 ) throw new Error ( "Current model has no alternate variant to select" )
142-
143- for ( let i = 0 ; i < count ; i ++ ) {
144- const item = items . nth ( i )
145- const next = await text ( item . locator ( '[data-slot="select-select-item-label"]' ) . first ( ) )
146- if ( ! next || next === current . variant ) continue
147- await item . click ( )
148- return waitFooter ( page , { agent : current . agent , model : current . model , variant : next } )
149- }
209+ const next = ( await probe ( page ) ) ?. variants ?. find ( ( item ) => item !== current . variant )
210+ if ( ! next ) throw new Error ( "Current model has no alternate variant to select" )
150211
151- throw new Error ( "Failed to choose a different variant" )
212+ await pickVariant ( page , next )
213+ return waitFooter ( page , { agent : current . agent , model : current . model , variant : next } )
152214}
153215
154- async function chooseOtherModel ( page : Page ) : Promise < Footer > {
155- const current = await read ( page )
156- const button = page . locator ( `${ promptModelSelector } [data-action="prompt-model"]` )
157- await expect ( button ) . toBeVisible ( )
158- await button . click ( )
159-
160- const dialog = page . getByRole ( "dialog" )
161- await expect ( dialog ) . toBeVisible ( )
162- const items = dialog . locator ( '[data-slot="list-item"]' )
163- const count = await items . count ( )
164- expect ( count ) . toBeGreaterThan ( 1 )
165-
166- for ( let i = 0 ; i < count ; i ++ ) {
167- const item = items . nth ( i )
168- const selected = ( await item . getAttribute ( "data-selected" ) ) === "true"
169- if ( selected ) continue
170- await item . click ( )
171- await expect ( dialog ) . toHaveCount ( 0 )
172- await expect . poll ( async ( ) => ( await read ( page ) ) . model !== current . model , { timeout : 30_000 } ) . toBe ( true )
173- return read ( page )
174- }
175-
176- throw new Error ( "Failed to choose a different model" )
216+ async function chooseOtherModel ( page : Page , skip : string [ ] = [ ] ) : Promise < Footer > {
217+ const current = await currentModel ( page )
218+ const next = ( await probe ( page ) ) ?. models ?. find ( ( item ) => {
219+ const key = `${ item . providerID } :${ item . modelID } `
220+ return key !== current && ! skip . includes ( key )
221+ } )
222+ if ( ! next ) throw new Error ( "Failed to choose a different model" )
223+ await pickModel ( page , { providerID : next . providerID , modelID : next . modelID } )
224+ await expect . poll ( async ( ) => ( await read ( page ) ) . model , { timeout : 30_000 } ) . toBe ( next . name )
225+ return read ( page )
177226}
178227
179228async function goto ( page : Page , directory : string , sessionID ?: string ) {
@@ -249,17 +298,14 @@ async function newWorkspaceSession(page: Page, slug: string) {
249298 return waitSession ( page , { directory : next . directory } ) . then ( ( item ) => item . directory )
250299}
251300
252- test ( "session model and variant restore per session without leaking into new sessions" , async ( {
253- page,
254- withProject,
255- } ) => {
301+ test ( "session model restore per session without leaking into new sessions" , async ( { page, withProject } ) => {
256302 await page . setViewportSize ( { width : 1440 , height : 900 } )
257303
258304 await withProject ( async ( { directory, gotoSession, trackSession } ) => {
259305 await gotoSession ( )
260306
261- await ensureVariant ( page , directory )
262- const firstState = await chooseDifferentVariant ( page )
307+ const firstState = await chooseOtherModel ( page )
308+ const firstKey = await currentModel ( page )
263309 const first = await submit ( page , `session variant ${ Date . now ( ) } ` )
264310 trackSession ( first )
265311 await waitUser ( directory , first )
@@ -269,10 +315,10 @@ test("session model and variant restore per session without leaking into new ses
269315 await waitFooter ( page , firstState )
270316
271317 await gotoSession ( )
272- const fresh = await ensureVariant ( page , directory )
273- expect ( fresh . variant ) . not . toBe ( firstState . variant )
318+ const fresh = await read ( page )
319+ expect ( fresh . model ) . not . toBe ( firstState . model )
274320
275- const secondState = await chooseOtherModel ( page )
321+ const secondState = await chooseOtherModel ( page , [ firstKey ] )
276322 const second = await submit ( page , `session model ${ Date . now ( ) } ` )
277323 trackSession ( second )
278324 await waitUser ( directory , second )
@@ -294,8 +340,8 @@ test("session model restore across workspaces", async ({ page, withProject }) =>
294340 await withProject ( async ( { directory : root , slug, gotoSession, trackDirectory, trackSession } ) => {
295341 await gotoSession ( )
296342
297- await ensureVariant ( page , root )
298- const firstState = await chooseDifferentVariant ( page )
343+ const firstState = await chooseOtherModel ( page )
344+ const firstKey = await currentModel ( page )
299345 const first = await submit ( page , `root session ${ Date . now ( ) } ` )
300346 trackSession ( first , root )
301347 await waitUser ( root , first )
@@ -307,7 +353,8 @@ test("session model restore across workspaces", async ({ page, withProject }) =>
307353 const oneDir = await newWorkspaceSession ( page , one . slug )
308354 trackDirectory ( oneDir )
309355
310- const secondState = await chooseOtherModel ( page )
356+ const secondState = await chooseOtherModel ( page , [ firstKey ] )
357+ const secondKey = await currentModel ( page )
311358 const second = await submit ( page , `workspace one ${ Date . now ( ) } ` )
312359 trackSession ( second , oneDir )
313360 await waitUser ( oneDir , second )
@@ -316,8 +363,7 @@ test("session model restore across workspaces", async ({ page, withProject }) =>
316363 const twoDir = await newWorkspaceSession ( page , two . slug )
317364 trackDirectory ( twoDir )
318365
319- await ensureVariant ( page , twoDir )
320- const thirdState = await chooseDifferentVariant ( page )
366+ const thirdState = await chooseOtherModel ( page , [ firstKey , secondKey ] )
321367 const third = await submit ( page , `workspace two ${ Date . now ( ) } ` )
322368 trackSession ( third , twoDir )
323369 await waitUser ( twoDir , third )
0 commit comments