@@ -60,6 +60,7 @@ const runtimeReports = [];
6060const dashboardReports = [ ] ;
6161const folderEditorReports = [ ] ;
6262const dockerDiagnosticsReports = [ ] ;
63+ const dockerUpdateFlowReports = [ ] ;
6364
6465const resolveRuntimeUrl = ( baseUrl , type ) => {
6566 try {
@@ -570,6 +571,170 @@ const runDockerDiagnosticsSmoke = async (page, { browserName, url }) => {
570571 } ;
571572} ;
572573
574+ const runDockerUpdateFlowSmoke = async ( page , { browserName, url } ) => {
575+ await page . goto ( url , { waitUntil : 'domcontentloaded' , timeout : timeoutMs } ) ;
576+ await page . waitForTimeout ( 1200 ) ;
577+
578+ const report = await page . evaluate ( async ( ) => {
579+ const bulkKey = 'fv.support.bundle.docker.bulkUpdateTrace.v1' ;
580+ const requestKey = 'fv.support.bundle.docker.requestBundleTrace.v1' ;
581+ const pageKey = 'fv.support.bundle.docker.page.v1' ;
582+ const readRecord = ( storageKey ) => {
583+ try {
584+ const raw = window . localStorage ?. getItem ( storageKey ) ;
585+ return raw ? JSON . parse ( raw ) : null ;
586+ } catch ( _error ) {
587+ return null ;
588+ }
589+ } ;
590+ const waitForRecord = async ( predicate , timeoutMs = 6000 , stepMs = 100 ) => {
591+ const startedAt = Date . now ( ) ;
592+ while ( ( Date . now ( ) - startedAt ) < timeoutMs ) {
593+ const value = predicate ( ) ;
594+ if ( value ) {
595+ return value ;
596+ }
597+ await new Promise ( ( resolve ) => window . setTimeout ( resolve , stepMs ) ) ;
598+ }
599+ return null ;
600+ } ;
601+ if ( typeof window . updateFolder !== 'function' ) {
602+ return {
603+ skipped : true ,
604+ reason : 'window.updateFolder is unavailable on the Docker page.'
605+ } ;
606+ }
607+ if ( typeof window . loadlist !== 'function' ) {
608+ return {
609+ skipped : true ,
610+ reason : 'window.loadlist is unavailable on the Docker page.'
611+ } ;
612+ }
613+ const updateLink = Array . from ( document . querySelectorAll ( '#docker_list td.updatecolumn a.exec, #docker_view td.updatecolumn a.exec' ) )
614+ . find ( ( node ) => {
615+ const text = String ( node ?. closest ( 'td.updatecolumn' ) ?. textContent || '' ) . replace ( / \s + / g, ' ' ) . trim ( ) . toLowerCase ( ) ;
616+ return / a p p l y u p d a t e / . test ( text ) && / u p d a t e F o l d e r \( / . test ( String ( node ?. getAttribute ( 'onclick' ) || '' ) ) ;
617+ } ) ;
618+ if ( ! ( updateLink instanceof HTMLAnchorElement ) ) {
619+ return {
620+ skipped : true ,
621+ reason : 'No folder apply update row available for synthetic update-flow smoke.'
622+ } ;
623+ }
624+ const folderRow = updateLink . closest ( 'tr' ) ;
625+ const folderIdMatch = String ( updateLink . getAttribute ( 'onclick' ) || '' ) . match ( / u p d a t e F o l d e r \( \s * [ ' " ] ? ( [ ^ , ' " ) \s ] + ) [ ' " ] ? / i) ;
626+ const folderId = String ( folderIdMatch ?. [ 1 ] || '' ) . trim ( ) ;
627+ if ( ! folderId ) {
628+ return {
629+ skipped : true ,
630+ reason : 'Unable to resolve the target folder id for synthetic update-flow smoke.'
631+ } ;
632+ }
633+
634+ const beforeBulkTrace = readRecord ( bulkKey ) ;
635+ const beforeRequestTrace = readRecord ( requestKey ) ;
636+ const beforeBulkCount = Number ( beforeBulkTrace ?. count || 0 ) ;
637+ const beforeRequestCount = Number ( beforeRequestTrace ?. count || 0 ) ;
638+ const capturedCommands = [ ] ;
639+ const originalOpenDocker = window . openDocker ;
640+
641+ try {
642+ window . openDocker = function ( ...args ) {
643+ capturedCommands . push ( Array . isArray ( args ) ? args . map ( ( entry ) => String ( entry ?? '' ) ) : [ ] ) ;
644+ return undefined ;
645+ } ;
646+
647+ window . updateFolder ( folderId ) ;
648+
649+ const bulkTrace = await waitForRecord ( ( ) => {
650+ const record = readRecord ( bulkKey ) ;
651+ if ( ! record || Number ( record ?. count || 0 ) < beforeBulkCount ) {
652+ return null ;
653+ }
654+ const entries = Array . isArray ( record ?. entries ) ? record . entries : [ ] ;
655+ const nextEntries = entries . slice ( beforeBulkCount ) ;
656+ const sawDispatch = nextEntries . some ( ( entry ) => entry ?. eventType === 'updateFolderDispatch' ) ;
657+ const sawDialogOpened = nextEntries . some ( ( entry ) => entry ?. eventType === 'dialogOpened' ) ;
658+ const sawReconcileWindow = nextEntries . some ( ( entry ) => entry ?. eventType === 'reconcileWindowArmed' ) ;
659+ return sawDispatch && sawDialogOpened && sawReconcileWindow ? record : null ;
660+ } , 6000 ) ;
661+
662+ window . loadlist ( ) ;
663+
664+ const requestTrace = await waitForRecord ( ( ) => {
665+ const record = readRecord ( requestKey ) ;
666+ if ( ! record || Number ( record ?. count || 0 ) < beforeRequestCount ) {
667+ return null ;
668+ }
669+ const entries = Array . isArray ( record ?. entries ) ? record . entries : [ ] ;
670+ const nextEntries = entries . slice ( beforeRequestCount ) ;
671+ const sawLoadlist = nextEntries . some ( ( entry ) => entry ?. eventType === 'loadlist' ) ;
672+ const matchingBuild = nextEntries . find ( ( entry ) =>
673+ entry ?. eventType === 'buildDockerFolderReq'
674+ && entry ?. liveUpdateStatus === true
675+ && entry ?. hostSyncSuspended === true
676+ ) ;
677+ const sawListview = nextEntries . some ( ( entry ) => entry ?. eventType === 'listview' ) ;
678+ return sawLoadlist && matchingBuild && sawListview ? { record, matchingBuild } : null ;
679+ } , 6000 ) ;
680+
681+ const pageSnapshot = await waitForRecord ( ( ) => {
682+ const record = readRecord ( pageKey ) ;
683+ return record ?. currentPage === '/Docker' ? record : null ;
684+ } , 3000 ) ;
685+
686+ return {
687+ skipped : false ,
688+ pass : Boolean ( bulkTrace ) && Boolean ( requestTrace ) ,
689+ folderId,
690+ capturedCommandCount : capturedCommands . length ,
691+ capturedFirstCommand : capturedCommands [ 0 ] ?. [ 0 ] || null ,
692+ visibleFolderClass : String ( folderRow ?. className || '' ) ,
693+ bulkTraceCount : Number ( bulkTrace ?. count || 0 ) ,
694+ requestTraceCount : Number ( requestTrace ?. record ?. count || 0 ) ,
695+ liveUpdateStatusObserved : requestTrace ?. matchingBuild ?. liveUpdateStatus === true ,
696+ hostSyncSuspendedObserved : requestTrace ?. matchingBuild ?. hostSyncSuspended === true ,
697+ pageSnapshotAvailable : Boolean ( pageSnapshot )
698+ } ;
699+ } finally {
700+ window . openDocker = originalOpenDocker ;
701+ }
702+ } ) ;
703+
704+ const screenshotName = `${ sanitizeToken ( scenarioLabel ) } -${ sanitizeToken ( browserName ) } -docker-update-flow.png` ;
705+ const screenshotPath = path . join ( artifactRoot , screenshotName ) ;
706+ await page . screenshot ( { path : screenshotPath , fullPage : true } ) ;
707+
708+ if ( report ?. skipped ) {
709+ console . warn ( `Docker update-flow smoke skipped for ${ browserName } : ${ report . reason } ` ) ;
710+ return {
711+ browserName,
712+ url,
713+ skipped : true ,
714+ pass : false ,
715+ reason : report . reason ,
716+ screenshotPath
717+ } ;
718+ }
719+
720+ if ( ! report ?. pass ) {
721+ throw new Error (
722+ `Docker update-flow smoke failed for ${ browserName } : ${ JSON . stringify ( report ) } . `
723+ + `Screenshot: ${ screenshotPath } `
724+ ) ;
725+ }
726+
727+ console . log ( `Docker update-flow smoke passed: ${ browserName } ${ JSON . stringify ( report ) } ` ) ;
728+ return {
729+ browserName,
730+ url,
731+ skipped : false ,
732+ pass : true ,
733+ screenshotPath,
734+ ...report
735+ } ;
736+ } ;
737+
573738const runDashboardQuickRailSmoke = async ( page , { browserName, url } ) => {
574739 await page . goto ( url , { waitUntil : 'domcontentloaded' , timeout : timeoutMs } ) ;
575740 await page . waitForTimeout ( 1200 ) ;
@@ -1172,6 +1337,13 @@ const runBrowserSmoke = async (browserName, browserType) => {
11721337 if ( diagnosticsReport ) {
11731338 dockerDiagnosticsReports . push ( diagnosticsReport ) ;
11741339 }
1340+ const updateFlowReport = await runDockerUpdateFlowSmoke ( page , {
1341+ browserName,
1342+ url : dockerRuntimeTarget . url
1343+ } ) ;
1344+ if ( updateFlowReport ) {
1345+ dockerUpdateFlowReports . push ( updateFlowReport ) ;
1346+ }
11751347 }
11761348
11771349 if ( dashboardUrl ) {
@@ -1223,6 +1395,7 @@ try {
12231395 scenarioLabel,
12241396 reports : runtimeReports ,
12251397 dockerDiagnosticsReports,
1398+ dockerUpdateFlowReports,
12261399 dashboardReports,
12271400 folderEditorReports
12281401 } ;
0 commit comments