11import type { ToolPart } from "@opencode-ai/sdk/v2/client"
22import type { Page } from "@playwright/test"
33import { test , expect } from "../fixtures"
4- import { withSession } from "../actions"
4+ import { assistantText , sessionIDFromUrl } from "../actions"
55import { promptSelector } from "../selectors"
6+ import { openaiModel , promptMatch , titleMatch , withMockOpenAI } from "./mock"
67
78const text = ( value : string | null ) => ( value ?? "" ) . replace ( / \u200B / g, "" ) . trim ( )
89
@@ -43,20 +44,13 @@ async function wait(page: Page, value: string) {
4344 await expect . poll ( async ( ) => text ( await page . locator ( promptSelector ) . textContent ( ) ) ) . toBe ( value )
4445}
4546
46- async function reply ( sdk : Parameters < typeof withSession > [ 0 ] , sessionID : string , token : string ) {
47+ async function reply (
48+ sdk : { session : { messages : Parameters < typeof assistantText > [ 0 ] [ "session" ] } } ,
49+ sessionID : string ,
50+ token : string ,
51+ ) {
4752 await expect
48- . poll (
49- async ( ) => {
50- const messages = await sdk . session . messages ( { sessionID, limit : 50 } ) . then ( ( r ) => r . data ?? [ ] )
51- return messages
52- . filter ( ( item ) => item . info . role === "assistant" )
53- . flatMap ( ( item ) => item . parts )
54- . filter ( ( item ) => item . type === "text" )
55- . map ( ( item ) => item . text )
56- . join ( "\n" )
57- } ,
58- { timeout : 90_000 } ,
59- )
53+ . poll ( ( ) => assistantText ( sdk as Parameters < typeof assistantText > [ 0 ] , sessionID ) , { timeout : 90_000 } )
6054 . toContain ( token )
6155}
6256
@@ -79,106 +73,145 @@ async function shell(sdk: Parameters<typeof withSession>[0], sessionID: string,
7973 . toContain ( token )
8074}
8175
82- test ( "prompt history restores unsent draft with arrow navigation" , async ( { page, sdk, gotoSession } ) => {
76+ test ( "prompt history restores unsent draft with arrow navigation" , async ( {
77+ page,
78+ llm,
79+ backend,
80+ withBackendProject,
81+ } ) => {
8382 test . setTimeout ( 120_000 )
8483
85- await withSession ( sdk , `e2e prompt history ${ Date . now ( ) } ` , async ( session ) => {
86- await gotoSession ( session . id )
87-
88- const prompt = page . locator ( promptSelector )
89- const firstToken = `E2E_HISTORY_ONE_${ Date . now ( ) } `
90- const secondToken = `E2E_HISTORY_TWO_${ Date . now ( ) } `
91- const first = `Reply with exactly: ${ firstToken } `
92- const second = `Reply with exactly: ${ secondToken } `
93- const draft = `draft ${ Date . now ( ) } `
94-
95- await prompt . click ( )
96- await page . keyboard . type ( first )
97- await page . keyboard . press ( "Enter" )
98- await wait ( page , "" )
99- await reply ( sdk , session . id , firstToken )
100-
101- await prompt . click ( )
102- await page . keyboard . type ( second )
103- await page . keyboard . press ( "Enter" )
104- await wait ( page , "" )
105- await reply ( sdk , session . id , secondToken )
106-
107- await prompt . click ( )
108- await page . keyboard . type ( draft )
109- await wait ( page , draft )
110-
111- // Clear the draft before navigating history (ArrowUp only works when prompt is empty)
112- await prompt . fill ( "" )
113- await wait ( page , "" )
114-
115- await page . keyboard . press ( "ArrowUp" )
116- await wait ( page , second )
117-
118- await page . keyboard . press ( "ArrowUp" )
119- await wait ( page , first )
120-
121- await page . keyboard . press ( "ArrowDown" )
122- await wait ( page , second )
123-
124- await page . keyboard . press ( "ArrowDown" )
125- await wait ( page , "" )
84+ await withMockOpenAI ( {
85+ serverUrl : backend . url ,
86+ llmUrl : llm . url ,
87+ fn : async ( ) => {
88+ const firstToken = `E2E_HISTORY_ONE_${ Date . now ( ) } `
89+ const secondToken = `E2E_HISTORY_TWO_${ Date . now ( ) } `
90+ const first = `Reply with exactly: ${ firstToken } `
91+ const second = `Reply with exactly: ${ secondToken } `
92+ const draft = `draft ${ Date . now ( ) } `
93+
94+ await llm . textMatch ( titleMatch , "E2E Title" )
95+ await llm . textMatch ( promptMatch ( firstToken ) , firstToken )
96+ await llm . textMatch ( promptMatch ( secondToken ) , secondToken )
97+
98+ await withBackendProject (
99+ async ( project ) => {
100+ const prompt = page . locator ( promptSelector )
101+
102+ await prompt . click ( )
103+ await page . keyboard . type ( first )
104+ await page . keyboard . press ( "Enter" )
105+ await wait ( page , "" )
106+
107+ await expect ( page ) . toHaveURL ( / \/ s e s s i o n \/ [ ^ / ? # ] + / , { timeout : 30_000 } )
108+ const sessionID = sessionIDFromUrl ( page . url ( ) ) !
109+ project . trackSession ( sessionID )
110+ await reply ( project . sdk , sessionID , firstToken )
111+
112+ await prompt . click ( )
113+ await page . keyboard . type ( second )
114+ await page . keyboard . press ( "Enter" )
115+ await wait ( page , "" )
116+ await reply ( project . sdk , sessionID , secondToken )
117+
118+ await prompt . click ( )
119+ await page . keyboard . type ( draft )
120+ await wait ( page , draft )
121+
122+ await prompt . fill ( "" )
123+ await wait ( page , "" )
124+
125+ await page . keyboard . press ( "ArrowUp" )
126+ await wait ( page , second )
127+
128+ await page . keyboard . press ( "ArrowUp" )
129+ await wait ( page , first )
130+
131+ await page . keyboard . press ( "ArrowDown" )
132+ await wait ( page , second )
133+
134+ await page . keyboard . press ( "ArrowDown" )
135+ await wait ( page , "" )
136+ } ,
137+ {
138+ model : openaiModel ,
139+ } ,
140+ )
141+ } ,
126142 } )
127143} )
128144
129- test ( "shell history stays separate from normal prompt history" , async ( { page, sdk , gotoSession } ) => {
145+ test ( "shell history stays separate from normal prompt history" , async ( { page, llm , backend , withBackendProject } ) => {
130146 test . setTimeout ( 120_000 )
131147
132- await withSession ( sdk , `e2e shell history ${ Date . now ( ) } ` , async ( session ) => {
133- await gotoSession ( session . id )
134-
135- const prompt = page . locator ( promptSelector )
136- const firstToken = `E2E_SHELL_ONE_${ Date . now ( ) } `
137- const secondToken = `E2E_SHELL_TWO_${ Date . now ( ) } `
138- const normalToken = `E2E_NORMAL_${ Date . now ( ) } `
139- const first = `echo ${ firstToken } `
140- const second = `echo ${ secondToken } `
141- const normal = `Reply with exactly: ${ normalToken } `
142-
143- await prompt . click ( )
144- await page . keyboard . type ( "!" )
145- await page . keyboard . type ( first )
146- await page . keyboard . press ( "Enter" )
147- await wait ( page , "" )
148- await shell ( sdk , session . id , first , firstToken )
149-
150- await prompt . click ( )
151- await page . keyboard . type ( "!" )
152- await page . keyboard . type ( second )
153- await page . keyboard . press ( "Enter" )
154- await wait ( page , "" )
155- await shell ( sdk , session . id , second , secondToken )
156-
157- await prompt . click ( )
158- await page . keyboard . type ( "!" )
159- await page . keyboard . press ( "ArrowUp" )
160- await wait ( page , second )
161-
162- await page . keyboard . press ( "ArrowUp" )
163- await wait ( page , first )
164-
165- await page . keyboard . press ( "ArrowDown" )
166- await wait ( page , second )
167-
168- await page . keyboard . press ( "ArrowDown" )
169- await wait ( page , "" )
170-
171- await page . keyboard . press ( "Escape" )
172- await wait ( page , "" )
173-
174- await prompt . click ( )
175- await page . keyboard . type ( normal )
176- await page . keyboard . press ( "Enter" )
177- await wait ( page , "" )
178- await reply ( sdk , session . id , normalToken )
179-
180- await prompt . click ( )
181- await page . keyboard . press ( "ArrowUp" )
182- await wait ( page , normal )
148+ await withMockOpenAI ( {
149+ serverUrl : backend . url ,
150+ llmUrl : llm . url ,
151+ fn : async ( ) => {
152+ const firstToken = `E2E_SHELL_ONE_${ Date . now ( ) } `
153+ const secondToken = `E2E_SHELL_TWO_${ Date . now ( ) } `
154+ const normalToken = `E2E_NORMAL_${ Date . now ( ) } `
155+ const first = `echo ${ firstToken } `
156+ const second = `echo ${ secondToken } `
157+ const normal = `Reply with exactly: ${ normalToken } `
158+
159+ await llm . textMatch ( titleMatch , "E2E Title" )
160+ await llm . textMatch ( promptMatch ( normalToken ) , normalToken )
161+
162+ await withBackendProject (
163+ async ( project ) => {
164+ const prompt = page . locator ( promptSelector )
165+
166+ await prompt . click ( )
167+ await page . keyboard . type ( "!" )
168+ await page . keyboard . type ( first )
169+ await page . keyboard . press ( "Enter" )
170+ await wait ( page , "" )
171+
172+ await expect ( page ) . toHaveURL ( / \/ s e s s i o n \/ [ ^ / ? # ] + / , { timeout : 30_000 } )
173+ const sessionID = sessionIDFromUrl ( page . url ( ) ) !
174+ project . trackSession ( sessionID )
175+ await shell ( project . sdk , sessionID , first , firstToken )
176+
177+ await prompt . click ( )
178+ await page . keyboard . type ( "!" )
179+ await page . keyboard . type ( second )
180+ await page . keyboard . press ( "Enter" )
181+ await wait ( page , "" )
182+ await shell ( project . sdk , sessionID , second , secondToken )
183+
184+ await prompt . click ( )
185+ await page . keyboard . type ( "!" )
186+ await page . keyboard . press ( "ArrowUp" )
187+ await wait ( page , second )
188+
189+ await page . keyboard . press ( "ArrowUp" )
190+ await wait ( page , first )
191+
192+ await page . keyboard . press ( "ArrowDown" )
193+ await wait ( page , second )
194+
195+ await page . keyboard . press ( "ArrowDown" )
196+ await wait ( page , "" )
197+
198+ await page . keyboard . press ( "Escape" )
199+ await wait ( page , "" )
200+
201+ await prompt . click ( )
202+ await page . keyboard . type ( normal )
203+ await page . keyboard . press ( "Enter" )
204+ await wait ( page , "" )
205+ await reply ( project . sdk , sessionID , normalToken )
206+
207+ await prompt . click ( )
208+ await page . keyboard . press ( "ArrowUp" )
209+ await wait ( page , normal )
210+ } ,
211+ {
212+ model : openaiModel ,
213+ } ,
214+ )
215+ } ,
183216 } )
184217} )
0 commit comments