@@ -2,7 +2,7 @@ import type { Page } from "@playwright/test"
22import { expectSessionTitle } from "../../utils/waits"
33import { benchmark , expect , withBenchmarkPage } from "../benchmark"
44import { fixture } from "./session-timeline-stress.fixture"
5- import { installStressSessionTabs , mockStressTimeline , stressSessionHref } from "./timeline-test-helpers"
5+ import { installStressSessionTabs , installTimelineSettings , mockStressTimeline , stressSessionHref } from "./timeline-test-helpers"
66import { measureSessionSwitch , waitForStableTimeline } from "./session-tab-switch-probe"
77
88type Result = Awaited < ReturnType < typeof measureSessionSwitch > >
@@ -20,8 +20,38 @@ benchmark("benchmarks cold and hot session tab switching", async ({ browser, rep
2020 report ( { results, summary : summarize ( results ) } )
2121} )
2222
23- async function trial ( page : Page , mode : "cold" | "hot" ) {
24- await mockStressTimeline ( page )
23+ benchmark ( "benchmarks v2 session tab switching with and without the review pane" , async ( { browser, report } , testInfo ) => {
24+ benchmark . setTimeout ( 360_000 )
25+ const runs = Number ( process . env . SESSION_TAB_SWITCH_RUNS ?? 5 )
26+ const results = {
27+ closed : { cold : [ ] as Result [ ] , hot : [ ] as Result [ ] } ,
28+ open : { cold : [ ] as Result [ ] , hot : [ ] as Result [ ] } ,
29+ }
30+ for ( const reviewPane of [ "closed" , "open" ] as const ) {
31+ for ( const mode of [ "cold" , "hot" ] as const ) {
32+ for ( let run = 0 ; run < runs ; run ++ ) {
33+ results [ reviewPane ] [ mode ] . push (
34+ await withBenchmarkPage (
35+ browser ,
36+ `session-tab-switch-v2-${ reviewPane } -${ mode } -${ run } ` ,
37+ ( page ) => trial ( page , mode , { newLayoutDesigns : true , reviewPane } ) ,
38+ testInfo ,
39+ ) ,
40+ )
41+ }
42+ }
43+ }
44+ report ( { results, summary : summarizeReviewPane ( results ) } , { runs, reviewDiffs : createReviewDiffs ( ) . length } )
45+ } )
46+
47+ async function trial (
48+ page : Page ,
49+ mode : "cold" | "hot" ,
50+ options ?: { newLayoutDesigns ?: boolean ; reviewPane ?: "closed" | "open" } ,
51+ ) {
52+ const reviewDiffs = options ?. newLayoutDesigns ? createReviewDiffs ( ) : undefined
53+ await mockStressTimeline ( page , { vcsDiff : reviewDiffs } )
54+ if ( options ?. newLayoutDesigns ) await installTimelineSettings ( page )
2555 await installStressSessionTabs ( page )
2656 if ( mode === "hot" ) {
2757 await page . goto ( stressSessionHref ( fixture . targetID ) )
@@ -33,6 +63,10 @@ async function trial(page: Page, mode: "cold" | "hot") {
3363 await expectSessionTitle ( page , fixture . expected . sourceTitle )
3464 }
3565 await waitForStableTimeline ( page , fixture . expected . sourceMessageIDs . at ( - 1 ) ! )
66+ if ( options ?. reviewPane === "open" ) {
67+ await openReviewPane ( page )
68+ await waitForStableTimeline ( page , fixture . expected . sourceMessageIDs . at ( - 1 ) ! )
69+ }
3670
3771 const destinationIDs = fixture . messages [ fixture . targetID ] . map ( ( message ) => message . info . id )
3872 const sourceIDs = fixture . messages [ fixture . sourceID ] . map ( ( message ) => message . info . id )
@@ -70,10 +104,80 @@ function summarize(results: Record<"cold" | "hot", Result[]>) {
70104 )
71105}
72106
107+ function summarizeReviewPane ( results : Record < "closed" | "open" , Record < "cold" | "hot" , Result [ ] > > ) {
108+ return Object . fromEntries (
109+ Object . entries ( results ) . map ( ( [ reviewPane , values ] ) => [ reviewPane , summarize ( values as Record < "cold" | "hot" , Result [ ] > ) ] ) ,
110+ )
111+ }
112+
73113async function switchSession ( page : Page , sessionID : string , title : string ) {
74114 const href = stressSessionHref ( sessionID )
75115 const tab = page . locator ( `[data-slot="titlebar-tabs"] a[href="${ href } "]` ) . first ( )
76116 await expect ( tab ) . toBeVisible ( )
77117 await tab . click ( )
78118 await expectSessionTitle ( page , title )
79119}
120+
121+ async function openReviewPane ( page : Page ) {
122+ await page . getByRole ( "button" , { name : "Toggle review" } ) . click ( )
123+ const panel = page . locator ( "#review-panel" )
124+ await expect ( panel ) . toBeVisible ( )
125+ // Text-based readiness works across review implementations; the legacy list mounts
126+ // diff viewers lazily while V2 mounts the active preview eagerly.
127+ await page . waitForFunction ( ( ) => {
128+ const panel = document . querySelector < HTMLElement > ( "#review-panel" )
129+ const text = panel ?. textContent ?? ""
130+ return text . includes ( "generated-000.ts" ) && text . includes ( "+3" )
131+ } )
132+ }
133+
134+ function createReviewDiffs ( ) {
135+ return Array . from ( { length : Number ( process . env . REVIEW_PANE_DIFF_COUNT ?? 72 ) } , ( _ , index ) => {
136+ const lines = index % 3 === 0 ? 300 : index % 3 === 1 ? 120 : 38
137+ const file = `src/review/generated-${ String ( index ) . padStart ( 3 , "0" ) } .ts`
138+ const before = reviewSource ( index , lines )
139+ const after = before
140+ . replace ( `value_${ index } _4` , `updated_${ index } _4` )
141+ . replace (
142+ `value_${ index } _${ Math . max ( 8 , Math . floor ( lines / 2 ) ) } ` ,
143+ `updated_${ index } _${ Math . max ( 8 , Math . floor ( lines / 2 ) ) } ` ,
144+ )
145+ . replace ( `value_${ index } _${ lines - 4 } ` , `updated_${ index } _${ lines - 4 } ` )
146+ return {
147+ file,
148+ patch : reviewPatch ( file , before , after ) ,
149+ additions : 3 ,
150+ deletions : 3 ,
151+ status : "modified" as const ,
152+ }
153+ } )
154+ }
155+
156+ function reviewSource ( seed : number , lines : number ) {
157+ return Array . from (
158+ { length : lines } ,
159+ ( _ , index ) =>
160+ `export const value_${ seed } _${ index } = "${ reviewWords ( seed + index , index % 5 === 0 ? 180 : 42 ) } "` ,
161+ ) . join ( "\n" )
162+ }
163+
164+ function reviewPatch ( file : string , before : string , after : string ) {
165+ const beforeLines = before . split ( "\n" )
166+ const afterLines = after . split ( "\n" )
167+ return [
168+ `diff --git a/${ file } b/${ file } ` ,
169+ `--- a/${ file } ` ,
170+ `+++ b/${ file } ` ,
171+ `@@ -1,${ beforeLines . length } +1,${ afterLines . length } @@` ,
172+ ...beforeLines . flatMap ( ( line , index ) => {
173+ const next = afterLines [ index ] !
174+ if ( line === next ) return [ ` ${ line } ` ]
175+ return [ `-${ line } ` , `+${ next } ` ]
176+ } ) ,
177+ ] . join ( "\n" )
178+ }
179+
180+ function reviewWords ( seed : number , length : number ) {
181+ const words = [ "alpha" , "bravo" , "charlie" , "delta" , "echo" , "foxtrot" , "golf" , "hotel" , "india" , "juliet" ]
182+ return Array . from ( { length : Math . ceil ( length / 7 ) } , ( _ , index ) => words [ ( seed + index * 3 ) % words . length ] ) . join ( " " )
183+ }
0 commit comments