@@ -217,6 +217,97 @@ async function expectSessionInspectorV2DetailAccordionsIndependent(page) {
217217 }
218218}
219219
220+ const SESSION_INSPECTOR_V2_DETAIL_SECTIONS = [
221+ {
222+ contentId : "sessionInspectorV2JsonContent" ,
223+ outputSelector : "#sessionInspectorV2JsonOutput"
224+ } ,
225+ {
226+ contentId : "sessionInspectorV2DataContent" ,
227+ outputSelector : "#sessionInspectorV2DataOutput"
228+ } ,
229+ {
230+ contentId : "sessionInspectorV2DirtyContent" ,
231+ outputSelector : "#sessionInspectorV2DirtyOutput"
232+ } ,
233+ {
234+ contentId : "sessionInspectorV2StatusContent" ,
235+ outputSelector : "#statusLog"
236+ }
237+ ] ;
238+
239+ async function setSessionInspectorV2DetailSectionsOpen ( page , openContentIds ) {
240+ const openSet = new Set ( openContentIds ) ;
241+ for ( const { contentId } of SESSION_INSPECTOR_V2_DETAIL_SECTIONS ) {
242+ const header = page . locator ( `.accordion-v2__header[aria-controls="${ contentId } "]` ) ;
243+ const content = page . locator ( `#${ contentId } ` ) ;
244+ const isOpen = await content . evaluate ( ( element ) => ! element . hidden ) ;
245+ if ( isOpen !== openSet . has ( contentId ) ) {
246+ await header . click ( ) ;
247+ }
248+ if ( openSet . has ( contentId ) ) {
249+ await expect ( content ) . toBeVisible ( ) ;
250+ await expect ( header ) . toHaveAttribute ( "aria-expanded" , "true" ) ;
251+ } else {
252+ await expect ( content ) . toBeHidden ( ) ;
253+ await expect ( header ) . toHaveAttribute ( "aria-expanded" , "false" ) ;
254+ }
255+ }
256+ }
257+
258+ async function expectSessionInspectorV2SharedDetailSpace ( page , openContentIds ) {
259+ await setSessionInspectorV2DetailSectionsOpen ( page , openContentIds ) ;
260+ const state = await page . evaluate ( ( sections ) => {
261+ const rightPanel = document . querySelector ( ".session-inspector-v2__panel--right" ) ;
262+ const rightRect = rightPanel . getBoundingClientRect ( ) ;
263+ const details = sections . map ( ( sectionInfo ) => {
264+ const content = document . querySelector ( `#${ sectionInfo . contentId } ` ) ;
265+ const section = content . closest ( ".session-inspector-v2__accordion" ) ;
266+ const header = section . querySelector ( ".session-inspector-v2__accordion-header-row" ) || section . querySelector ( ".accordion-v2__header" ) ;
267+ const output = document . querySelector ( sectionInfo . outputSelector ) ;
268+ const outputStyle = getComputedStyle ( output ) ;
269+ const sectionRect = section . getBoundingClientRect ( ) ;
270+ const headerRect = header . getBoundingClientRect ( ) ;
271+ const outputRect = output . getBoundingClientRect ( ) ;
272+ const isOpen = ! content . hidden ;
273+ return {
274+ contentId : sectionInfo . contentId ,
275+ closedHeaderOnly : isOpen || sectionRect . height <= headerRect . height + 5 ,
276+ headerReachable : headerRect . top >= rightRect . top - 1 && headerRect . bottom <= rightRect . bottom + 1 ,
277+ isOpen,
278+ noHorizontalScrollbar : output . scrollWidth <= output . clientWidth + 1 ,
279+ outputHeight : isOpen ? outputRect . height : 0 ,
280+ overflowY : outputStyle . overflowY ,
281+ wrapsLongLines : outputStyle . whiteSpace === "pre-wrap" && outputStyle . overflowWrap === "anywhere"
282+ } ;
283+ } ) ;
284+ const openDetails = details . filter ( ( detail ) => detail . isOpen ) ;
285+ const totalOpenOutputHeight = openDetails . reduce ( ( total , detail ) => total + detail . outputHeight , 0 ) ;
286+ const expectedOpenOutputHeight = totalOpenOutputHeight / Math . max ( openDetails . length , 1 ) ;
287+ return {
288+ closedSectionsHeaderOnly : details . every ( ( detail ) => detail . closedHeaderOnly ) ,
289+ headersReachable : details . every ( ( detail ) => detail . headerReachable ) ,
290+ openContentIds : openDetails . map ( ( detail ) => detail . contentId ) ,
291+ openCount : openDetails . length ,
292+ openOutputHeight : expectedOpenOutputHeight ,
293+ openOutputsSplitEvenly : openDetails . every ( ( detail ) => Math . abs ( detail . outputHeight - expectedOpenOutputHeight ) <= 1 ) ,
294+ openOutputsUseInternalVerticalScroll : openDetails . every ( ( detail ) => detail . overflowY === "auto" ) ,
295+ outputsHaveNoHorizontalScrollbars : openDetails . every ( ( detail ) => detail . noHorizontalScrollbar ) ,
296+ outputsWrapLongLines : openDetails . every ( ( detail ) => detail . wrapsLongLines )
297+ } ;
298+ } , SESSION_INSPECTOR_V2_DETAIL_SECTIONS ) ;
299+
300+ expect ( state . openContentIds ) . toEqual ( openContentIds ) ;
301+ expect ( state . openCount ) . toBe ( openContentIds . length ) ;
302+ expect ( state . openOutputsSplitEvenly ) . toBe ( true ) ;
303+ expect ( state . openOutputsUseInternalVerticalScroll ) . toBe ( true ) ;
304+ expect ( state . outputsHaveNoHorizontalScrollbars ) . toBe ( true ) ;
305+ expect ( state . outputsWrapLongLines ) . toBe ( true ) ;
306+ expect ( state . closedSectionsHeaderOnly ) . toBe ( true ) ;
307+ expect ( state . headersReachable ) . toBe ( true ) ;
308+ return state . openOutputHeight ;
309+ }
310+
220311async function expectSessionInspectorV2FullscreenShell ( page ) {
221312 const summary = page . locator ( "[data-session-inspector-v2-summary]" ) ;
222313 await summary . click ( ) ;
@@ -665,9 +756,9 @@ test.describe("Workspace Manager V2 bootstrap", () => {
665756 await page . locator ( "#copySessionInspectorV2AllButton" ) . click ( ) ;
666757 await expect ( page . locator ( "#statusLog" ) ) . toHaveValue ( / W A R N C o p i e d J S O N , D a t a , a n d D i r t y s e c t i o n s w i t h e m p t y - s t a t e t e x t f o r m i s s i n g D a t a a n d D i r t y \. / ) ;
667758 const copiedValidationText = await page . evaluate ( ( ) => window . __sessionInspectorV2ClipboardText ) ;
668- expect ( copiedValidationText ) . toContain ( "=== JSON ===\ntrue" ) ;
669- expect ( copiedValidationText ) . toContain ( "=== Data ===\nNo data section is present for sessionStorage:session-inspector-v2-alpha." ) ;
670- expect ( copiedValidationText ) . toContain ( "=== Dirty ===\nNo dirty section is present for sessionStorage:session-inspector-v2-alpha." ) ;
759+ expect ( copiedValidationText ) . toContain ( "=== JSON ===\nSession: sessionStorage:session-inspector-v2-alpha\ ntrue" ) ;
760+ expect ( copiedValidationText ) . toContain ( "=== Data ===\nSession: sessionStorage:session-inspector-v2-alpha\ nNo data section is present for sessionStorage:session-inspector-v2-alpha." ) ;
761+ expect ( copiedValidationText ) . toContain ( "=== Dirty ===\nSession: sessionStorage:session-inspector-v2-alpha\ nNo dirty section is present for sessionStorage:session-inspector-v2-alpha." ) ;
671762 await page . locator ( "#clearSessionInspectorV2StatusButton" ) . click ( ) ;
672763 await expect ( page . locator ( "#statusLog" ) ) . toHaveValue ( "" ) ;
673764 await page . locator ( '[data-session-inspector-v2-delete-entry-id="sessionStorage:session-inspector-v2-alpha"]' ) . click ( ) ;
@@ -892,62 +983,36 @@ test.describe("Workspace Manager V2 bootstrap", () => {
892983 await expect ( page . locator ( "#sessionInspectorV2DirtyOutput" ) ) . not . toContainText ( '"workspace"' ) ;
893984 await expect ( page . locator ( "#sessionInspectorV2DirtyOutput" ) ) . not . toContainText ( '"schema"' ) ;
894985 await expect ( page . locator ( "#sessionInspectorV2DirtyHeaderValue" ) ) . toHaveText ( "Dirty: false" ) ;
986+ const allDetailContentIds = SESSION_INSPECTOR_V2_DETAIL_SECTIONS . map ( ( section ) => section . contentId ) ;
987+ const fourOpenOutputHeight = await expectSessionInspectorV2SharedDetailSpace ( page , allDetailContentIds ) ;
988+ const threeOpenOutputHeight = await expectSessionInspectorV2SharedDetailSpace ( page , allDetailContentIds . slice ( 0 , 3 ) ) ;
989+ const twoOpenOutputHeight = await expectSessionInspectorV2SharedDetailSpace ( page , allDetailContentIds . slice ( 0 , 2 ) ) ;
990+ const oneOpenOutputHeight = await expectSessionInspectorV2SharedDetailSpace ( page , allDetailContentIds . slice ( 0 , 1 ) ) ;
991+ expect ( oneOpenOutputHeight ) . toBeGreaterThan ( twoOpenOutputHeight ) ;
992+ expect ( twoOpenOutputHeight ) . toBeGreaterThan ( threeOpenOutputHeight ) ;
993+ expect ( threeOpenOutputHeight ) . toBeGreaterThan ( fourOpenOutputHeight ) ;
994+ await setSessionInspectorV2DetailSectionsOpen ( page , allDetailContentIds ) ;
895995 const detailPanelState = await page . evaluate ( ( ) => {
896- const jsonContent = document . querySelector ( "#sessionInspectorV2JsonContent" ) ;
897- const dataContent = document . querySelector ( "#sessionInspectorV2DataContent" ) ;
898996 const jsonOutput = document . querySelector ( "#sessionInspectorV2JsonOutput" ) ;
899997 const dataOutput = document . querySelector ( "#sessionInspectorV2DataOutput" ) ;
900- const dirtyHeader = document . querySelector ( ".session-inspector-v2__dirty-accordion-header" ) ;
901- const statusHeader = document . querySelector ( ".session-inspector-v2__status-accordion-header" ) ;
902- const rightPanel = document . querySelector ( ".session-inspector-v2__panel--right" ) ;
903- const statusOutput = document . querySelector ( "#statusLog" ) ;
904- const jsonOutputStyle = getComputedStyle ( jsonOutput ) ;
905- const dataOutputStyle = getComputedStyle ( dataOutput ) ;
906- const jsonContentStyle = getComputedStyle ( jsonContent ) ;
907- const dataContentStyle = getComputedStyle ( dataContent ) ;
908- const rectFor = ( element ) => element . getBoundingClientRect ( ) ;
909- const rightRect = rectFor ( rightPanel ) ;
910- const dirtyHeaderRect = rectFor ( dirtyHeader ) ;
911- const statusHeaderRect = rectFor ( statusHeader ) ;
912- rightPanel . scrollTop = rightPanel . scrollHeight ;
913- const scrolledDirtyHeaderRect = rectFor ( dirtyHeader ) ;
914- const scrolledStatusHeaderRect = rectFor ( statusHeader ) ;
915- const statusOutputHeight = Math . round ( rectFor ( statusOutput ) . height ) ;
916998 return {
917- dataContentDoesNotOwnScrollbar : dataContentStyle . overflowY === "hidden" && dataContentStyle . overflowX === "hidden" ,
918- dataOutputHasNoHorizontalScrollbar : dataOutput . scrollWidth <= dataOutput . clientWidth + 1 ,
919999 dataOutputScrollsVertically : dataOutput . scrollHeight > dataOutput . clientHeight + 1 ,
920- dataOutputHeightMatchesStatus : Math . abs ( Math . round ( rectFor ( dataOutput ) . height ) - statusOutputHeight ) <= 1 ,
921- dataOutputWrapsLongLines : dataOutputStyle . whiteSpace === "pre-wrap" && dataOutputStyle . overflowWrap === "anywhere" ,
922- dirtyHeaderReachable : dirtyHeaderRect . top >= rightRect . top || ( scrolledDirtyHeaderRect . top >= rightRect . top && scrolledDirtyHeaderRect . bottom <= rightRect . bottom ) ,
923- jsonContentDoesNotOwnScrollbar : jsonContentStyle . overflowY === "hidden" && jsonContentStyle . overflowX === "hidden" ,
924- jsonOutputHasNoHorizontalScrollbar : jsonOutput . scrollWidth <= jsonOutput . clientWidth + 1 ,
925- jsonOutputScrollsVertically : jsonOutput . scrollHeight > jsonOutput . clientHeight + 1 ,
926- jsonOutputHeightMatchesStatus : Math . abs ( Math . round ( rectFor ( jsonOutput ) . height ) - statusOutputHeight ) <= 1 ,
927- jsonOutputWrapsLongLines : jsonOutputStyle . whiteSpace === "pre-wrap" && jsonOutputStyle . overflowWrap === "anywhere" ,
928- statusHeaderReachable : statusHeaderRect . top >= rightRect . top || ( scrolledStatusHeaderRect . top >= rightRect . top && scrolledStatusHeaderRect . bottom <= rightRect . bottom )
1000+ jsonOutputScrollsVertically : jsonOutput . scrollHeight > jsonOutput . clientHeight + 1
9291001 } ;
9301002 } ) ;
9311003 expect ( detailPanelState ) . toEqual ( {
932- dataContentDoesNotOwnScrollbar : true ,
933- dataOutputHasNoHorizontalScrollbar : true ,
9341004 dataOutputScrollsVertically : true ,
935- dataOutputHeightMatchesStatus : true ,
936- dataOutputWrapsLongLines : true ,
937- dirtyHeaderReachable : true ,
938- jsonContentDoesNotOwnScrollbar : true ,
939- jsonOutputHasNoHorizontalScrollbar : true ,
940- jsonOutputScrollsVertically : true ,
941- jsonOutputHeightMatchesStatus : true ,
942- jsonOutputWrapsLongLines : true ,
943- statusHeaderReachable : true
1005+ jsonOutputScrollsVertically : true
9441006 } ) ;
9451007 await page . locator ( "#copySessionInspectorV2AllButton" ) . click ( ) ;
9461008 await expect ( page . locator ( "#statusLog" ) ) . toHaveValue ( / O K C o p i e d J S O N , D a t a , a n d D i r t y s e c t i o n s t o c l i p b o a r d \. / ) ;
9471009 const copiedToolPayload = await page . evaluate ( ( ) => window . __sessionInspectorV2ClipboardText ) ;
9481010 expect ( copiedToolPayload ) . toContain ( "=== JSON ===" ) ;
1011+ expect ( copiedToolPayload ) . toContain ( "=== JSON ===\nSession: sessionStorage:workspace.tools.asset-manager-v2\n" ) ;
9491012 expect ( copiedToolPayload ) . toContain ( "=== Data ===" ) ;
1013+ expect ( copiedToolPayload ) . toContain ( "=== Data ===\nSession: sessionStorage:workspace.tools.asset-manager-v2\n" ) ;
9501014 expect ( copiedToolPayload ) . toContain ( "=== Dirty ===" ) ;
1015+ expect ( copiedToolPayload ) . toContain ( "=== Dirty ===\nSession: sessionStorage:workspace.tools.asset-manager-v2\n" ) ;
9511016 expect ( copiedToolPayload ) . toContain ( '"workspace"' ) ;
9521017 expect ( copiedToolPayload ) . toContain ( '"data"' ) ;
9531018 expect ( copiedToolPayload ) . toContain ( '"dirty"' ) ;
0 commit comments