@@ -67,6 +67,7 @@ import { useTerminalStateStore } from "../terminalStateStore";
6767import { resetRetainedThreadDetailSubscriptionsForTests } from "../threadDetailSubscriptionRetention" ;
6868import { useWorkspacePathsStore } from "../workspacePathsStore" ;
6969import { resetWsNativeApiForTest } from "../wsNativeApi" ;
70+ import { useRightDockStore } from "../rightDockStore" ;
7071// Pre-transform the compiler-heavy component outside the first case's timeout.
7172// The router's auto-split route otherwise requests this module on first mount.
7273import "./ChatView" ;
@@ -2151,6 +2152,7 @@ describe("ChatView timeline estimator parity (full app)", () => {
21512152 useTerminalStateStore . setState ( {
21522153 terminalStateByThreadId : { } ,
21532154 } ) ;
2155+ useRightDockStore . setState ( { dockStateByThreadId : { } } ) ;
21542156 useSplitViewStore . setState ( {
21552157 splitViewsById : { } ,
21562158 splitViewIdBySourceThreadId : { } ,
@@ -8657,4 +8659,102 @@ describe("ChatView timeline estimator parity (full app)", () => {
86578659 await mounted . cleanup ( ) ;
86588660 }
86598661 } ) ;
8662+
8663+ it ( "[geometry:linux] coordinates sidebar, dock, Environment, and composer bounds" , async ( ) => {
8664+ useRightDockStore . getState ( ) . openPane ( THREAD_ID , {
8665+ kind : "browser" ,
8666+ paneId : "layout-browser" ,
8667+ } ) ;
8668+ const mounted = await mountChatView ( {
8669+ viewport : { ...DEFAULT_VIEWPORT , width : 1_440 , height : 900 } ,
8670+ snapshot : createSnapshotForTargetUser ( {
8671+ targetMessageId : "msg-user-workspace-layout" as MessageId ,
8672+ targetText : "Keep the workspace controls reachable." ,
8673+ } ) ,
8674+ } ) ;
8675+
8676+ try {
8677+ const environmentToggle = await waitForElement (
8678+ ( ) =>
8679+ document . querySelector < HTMLButtonElement > (
8680+ 'button[aria-label="Toggle environment panel"]' ,
8681+ ) ,
8682+ "Environment toggle did not mount." ,
8683+ ) ;
8684+ await userEvent . click ( environmentToggle ) ;
8685+ const environment = await waitForElement (
8686+ ( ) =>
8687+ document . querySelector < HTMLElement > (
8688+ '[data-environment-panel-variant]:not([aria-hidden="true"])' ,
8689+ ) ,
8690+ "Environment panel did not open." ,
8691+ ) ;
8692+ const composer = await waitForElement (
8693+ ( ) => document . querySelector < HTMLElement > ( '[data-chat-composer-form="true"]' ) ,
8694+ "Composer did not mount." ,
8695+ ) ;
8696+ const dock = await waitForElement (
8697+ ( ) => document . querySelector < HTMLElement > ( "[data-right-dock-content]" ) ,
8698+ "Right dock did not mount." ,
8699+ ) ;
8700+ const chat = await waitForElement (
8701+ ( ) => document . querySelector < HTMLElement > ( '[data-chat-pane-drop-overlay="true"]' ) ,
8702+ "Chat surface did not mount." ,
8703+ ) ;
8704+
8705+ await vi . waitFor (
8706+ ( ) => {
8707+ expect ( environment . getBoundingClientRect ( ) . bottom ) . toBeLessThanOrEqual (
8708+ composer . getBoundingClientRect ( ) . top + 1 ,
8709+ ) ;
8710+ expect ( chat . getBoundingClientRect ( ) . width ) . toBeGreaterThanOrEqual ( 36 * 16 ) ;
8711+ expect ( dock . getBoundingClientRect ( ) . width ) . toBeLessThanOrEqual ( 608 ) ;
8712+ } ,
8713+ { timeout : 8_000 , interval : 16 } ,
8714+ ) ;
8715+
8716+ const forkTools = Array . from ( document . querySelectorAll < HTMLButtonElement > ( "button" ) ) . find (
8717+ ( button ) => button . textContent ?. includes ( "Fork Tools" ) ,
8718+ ) ;
8719+ const forkLore = Array . from ( document . querySelectorAll < HTMLButtonElement > ( "button" ) ) . find (
8720+ ( button ) => button . textContent ?. includes ( "Fork Lore" ) ,
8721+ ) ;
8722+ expect ( forkTools ?. getAttribute ( "aria-expanded" ) ) . toBe ( "false" ) ;
8723+ expect ( forkLore ?. getAttribute ( "aria-expanded" ) ) . toBe ( "false" ) ;
8724+ if ( ! forkTools ) throw new Error ( "Fork Tools disclosure did not mount." ) ;
8725+ await userEvent . click ( forkTools ) ;
8726+ expect ( forkTools . getAttribute ( "aria-expanded" ) ) . toBe ( "true" ) ;
8727+
8728+ await mounted . setViewport ( { ...DEFAULT_VIEWPORT , width : 1_024 , height : 768 } ) ;
8729+ await vi . waitFor (
8730+ ( ) => {
8731+ expect (
8732+ document . querySelector ( '[data-workspace-sidebar-suppressed="true"]' ) ,
8733+ ) . toBeTruthy ( ) ;
8734+ expect ( chat . getBoundingClientRect ( ) . width ) . toBeGreaterThanOrEqual ( 36 * 16 ) ;
8735+ expect ( dock . getBoundingClientRect ( ) . width ) . toBeLessThanOrEqual ( 448 ) ;
8736+ expect ( environment . getBoundingClientRect ( ) . bottom ) . toBeLessThanOrEqual (
8737+ composer . getBoundingClientRect ( ) . top + 1 ,
8738+ ) ;
8739+ } ,
8740+ { timeout : 8_000 , interval : 16 } ,
8741+ ) ;
8742+
8743+ await mounted . setViewport ( { ...DEFAULT_VIEWPORT , width : 1_440 , height : 900 } ) ;
8744+ await vi . waitFor (
8745+ ( ) => {
8746+ expect (
8747+ document . querySelector ( '[data-workspace-sidebar-suppressed="true"]' ) ,
8748+ ) . toBeNull ( ) ;
8749+ const sidebarGap = document . querySelector < HTMLElement > (
8750+ "[data-sidebar-side='left'] [data-slot='sidebar-gap']" ,
8751+ ) ;
8752+ expect ( sidebarGap ?. getBoundingClientRect ( ) . width ?? 0 ) . toBeGreaterThan ( 0 ) ;
8753+ } ,
8754+ { timeout : 8_000 , interval : 16 } ,
8755+ ) ;
8756+ } finally {
8757+ await mounted . cleanup ( ) ;
8758+ }
8759+ } ) ;
86608760} ) ;
0 commit comments