@@ -113,6 +113,9 @@ test('chat panel sizes survive navigation, chat switches, collapse, and layout c
113113 } )
114114 const shell = app
115115 const page = await shell . firstWindow ( )
116+ const inputSession = await page . context ( ) . newCDPSession ( page )
117+ // Playwright otherwise keeps the document focused when its native window loses focus.
118+ await inputSession . send ( 'Emulation.setFocusEmulationEnabled' , { enabled : false } )
116119 page . on ( 'pageerror' , ( error ) => errors . push ( error . message ) )
117120 page . on ( 'crash' , ( ) => {
118121 rendererCrashed = true
@@ -253,41 +256,102 @@ test('chat panel sizes survive navigation, chat switches, collapse, and layout c
253256 'detach' ,
254257 'chat switch' ,
255258 ] as const ) {
256- await check ( `${ interruption } keeps the previous saved width` , async ( ) => {
259+ const keepsDraggedWidth = interruption === 'capture loss' || interruption === 'blur'
260+ await check (
261+ `${ interruption } keeps the ${ keepsDraggedWidth ? 'dragged' : 'previous saved' } width` ,
262+ async ( ) => {
263+ const before = await page . evaluate ( ( ) => JSON . stringify ( localStorage ) )
264+ const rect = await beginDrag ( )
265+ await page . mouse . move ( rect . x + 100 , rect . y + 100 , { steps : 12 } )
266+ await expectWidth ( 598 )
267+ if ( interruption === 'pointercancel' ) {
268+ await divider . dispatchEvent ( 'pointercancel' , { pointerId : 1 } )
269+ } else if ( interruption === 'capture loss' ) {
270+ await divider . evaluate ( ( element ) => element . releasePointerCapture ( 1 ) )
271+ await page . mouse . move ( rect . x + 100 , rect . y + 100 )
272+ } else if ( interruption === 'blur' ) {
273+ await shell . evaluate ( ( { BrowserWindow } ) => BrowserWindow . getAllWindows ( ) [ 0 ] . blur ( ) )
274+ await expect . poll ( ( ) => page . evaluate ( ( ) => document . hasFocus ( ) ) ) . toBe ( false )
275+ } else if ( interruption === 'chat switch' ) {
276+ await page
277+ . getByRole ( 'button' , { name : 'organization-chat-a' , exact : true } )
278+ . evaluate ( ( element : HTMLButtonElement ) => element . click ( ) )
279+ await expectWidth ( 560 )
280+ } else {
281+ await page
282+ . getByRole ( 'button' , { name : 'Settings' , exact : true } )
283+ . evaluate ( ( element : HTMLButtonElement ) => element . click ( ) )
284+ await expect ( panel ) . toHaveCount ( 0 )
285+ }
286+ if ( keepsDraggedWidth ) {
287+ await expectWidth ( 598 )
288+ expect ( await page . evaluate ( ( ) => JSON . stringify ( localStorage ) ) ) . not . toBe ( before )
289+ if ( interruption === 'blur' ) {
290+ await shell . evaluate ( ( { app, BrowserWindow } ) => {
291+ app . focus ( { steal : true } )
292+ BrowserWindow . getAllWindows ( ) [ 0 ] . focus ( )
293+ } )
294+ }
295+ await page . mouse . move ( rect . x + 150 , rect . y + 100 )
296+ await expectWidth ( 598 )
297+ }
298+ await page . mouse . up ( )
299+ if ( interruption === 'chat switch' ) {
300+ await page . getByRole ( 'button' , { name : 'workspace-chat-b' , exact : true } ) . click ( )
301+ }
302+ if ( interruption === 'detach' ) {
303+ await page . getByRole ( 'button' , { name : 'Back' , exact : true } ) . click ( )
304+ }
305+ await expectWidth ( keepsDraggedWidth ? 598 : 698 )
306+ if ( keepsDraggedWidth ) {
307+ await page . reload ( )
308+ await page . getByRole ( 'button' , { name : 'workspace-chat-b' , exact : true } ) . click ( )
309+ await expectWidth ( 598 )
310+ await dragTo ( 698 )
311+ } else {
312+ expect ( await page . evaluate ( ( ) => JSON . stringify ( localStorage ) ) ) . toBe ( before )
313+ }
314+ }
315+ )
316+ }
317+
318+ for ( const button of [ 'middle' , 'right' ] as const ) {
319+ await check ( `${ button } -button drags leave the panel width unchanged` , async ( ) => {
257320 const before = await page . evaluate ( ( ) => JSON . stringify ( localStorage ) )
258- const rect = await beginDrag ( )
321+ await divider . hover ( { position : { x : 4 , y : 100 } } )
322+ const rect = await panel . boundingBox ( )
323+ if ( ! rect ) throw new Error ( 'Missing panel bounds' )
324+ await page . mouse . down ( { button } )
259325 await page . mouse . move ( rect . x + 100 , rect . y + 100 , { steps : 12 } )
260- await expectWidth ( 598 )
261- if ( interruption === 'pointercancel' ) {
262- await divider . dispatchEvent ( 'pointercancel' , { pointerId : 1 } )
263- } else if ( interruption === 'capture loss' ) {
264- await divider . evaluate ( ( element ) => element . releasePointerCapture ( 1 ) )
265- await page . mouse . move ( rect . x + 101 , rect . y + 100 )
266- } else if ( interruption === 'blur' ) {
267- await page . evaluate ( ( ) => window . dispatchEvent ( new Event ( 'blur' ) ) )
268- } else if ( interruption === 'chat switch' ) {
269- await page
270- . getByRole ( 'button' , { name : 'organization-chat-a' , exact : true } )
271- . evaluate ( ( element : HTMLButtonElement ) => element . click ( ) )
272- await expectWidth ( 560 )
273- } else {
274- await page
275- . getByRole ( 'button' , { name : 'Settings' , exact : true } )
276- . evaluate ( ( element : HTMLButtonElement ) => element . click ( ) )
277- await expect ( panel ) . toHaveCount ( 0 )
278- }
279- await page . mouse . up ( )
280- if ( interruption === 'chat switch' ) {
281- await page . getByRole ( 'button' , { name : 'workspace-chat-b' , exact : true } ) . click ( )
282- }
283- if ( interruption === 'detach' ) {
284- await page . getByRole ( 'button' , { name : 'Back' , exact : true } ) . click ( )
285- }
326+ await page . mouse . up ( { button } )
286327 await expectWidth ( 698 )
287328 expect ( await page . evaluate ( ( ) => JSON . stringify ( localStorage ) ) ) . toBe ( before )
288329 } )
289330 }
290331
332+ await check ( 'a touch drag resizes the panel without becoming a scroll gesture' , async ( ) => {
333+ const rect = await panel . boundingBox ( )
334+ if ( ! rect ) throw new Error ( 'Missing panel bounds' )
335+ await inputSession . send ( 'Emulation.setTouchEmulationEnabled' , { enabled : true } )
336+ await inputSession . send ( 'Input.dispatchTouchEvent' , {
337+ type : 'touchStart' ,
338+ touchPoints : [ { x : rect . x , y : rect . y + 100 } ] ,
339+ } )
340+ for ( let offset = 10 ; offset <= 100 ; offset += 10 ) {
341+ await inputSession . send ( 'Input.dispatchTouchEvent' , {
342+ type : 'touchMove' ,
343+ touchPoints : [ { x : rect . x + offset , y : rect . y + 100 } ] ,
344+ } )
345+ }
346+ await inputSession . send ( 'Input.dispatchTouchEvent' , { type : 'touchEnd' , touchPoints : [ ] } )
347+ await inputSession . send ( 'Emulation.setTouchEmulationEnabled' , { enabled : false } )
348+ await expectWidth ( 598 )
349+ await page . reload ( )
350+ await page . getByRole ( 'button' , { name : 'workspace-chat-b' , exact : true } ) . click ( )
351+ await expectWidth ( 598 )
352+ await dragTo ( 698 )
353+ } )
354+
291355 await check ( 'a viewport change during a drag clamps the committed display width' , async ( ) => {
292356 await expectWidth ( 698 )
293357 const rect = await beginDrag ( )
0 commit comments