11import { Page , expect , Locator } from '@playwright/test' ;
22
3+ //timeouts
4+ const TIMEOUTS = {
5+ short : 3000 ,
6+ modal : 5000 ,
7+ panel : 10000 ,
8+ default : 15000 ,
9+ load : 20000 ,
10+ render : 30000 ,
11+ sync : 120000 ,
12+ status : 180000
13+ } ;
14+
315export class ApplicationsPage {
416 readonly page : Page ;
517 readonly newAppButton : Locator ;
@@ -43,40 +55,39 @@ export class ApplicationsPage {
4355 const errorBanner = this . page . getByText ( 'try again' ) ;
4456 try {
4557 //wait 3 secs
46- await errorBanner . waitFor ( { state : 'visible' , timeout : 3000 } ) ;
58+ await errorBanner . waitFor ( { state : 'visible' , timeout : TIMEOUTS . short } ) ;
4759 await errorBanner . click ( ) ;
4860 } catch ( error ) {
4961 //banner didn't appear so just continue
5062 }
5163
52- await expect ( this . newAppButton ) . toBeVisible ( { timeout : 15000 } ) ;
64+ await expect ( this . newAppButton ) . toBeVisible ( { timeout : TIMEOUTS . default } ) ;
5365 }
5466
5567 //helper for fields that need to have select a pre existing option
5668 async fillDropdown ( locator : Locator , value : string ) {
5769 await locator . click ( ) ;
5870 await locator . pressSequentially ( value , { delay : 50 } ) ;
5971
60- //Wait for the dropdown
61- await expect ( locator ) . toHaveValue ( value , { timeout : 5000 } ) ;
72+ //wait for the dropdown
73+ await expect ( locator ) . toHaveValue ( value , { timeout : TIMEOUTS . modal } ) ;
6274
6375 await locator . press ( 'Enter' ) ;
6476 }
6577
66- async createApp ( appName : string , repoUrl : string , repoPath : string ) {
78+ async createApp ( appName : string , repoUrl : string , repoPath : string ) {
6779 await this . newAppButton . click ( ) ;
6880
6981 //handle the "failed to load data" banner if it appears inside the slide-out panel
7082 const errorBanner = this . page . getByText ( 'try again' ) ;
7183 try {
72- //wait 3 secs
73- await errorBanner . waitFor ( { state : 'visible' , timeout : 3000 } ) ;
84+ await errorBanner . waitFor ( { state : 'visible' , timeout : TIMEOUTS . short } ) ;
7485 await errorBanner . click ( ) ;
7586 } catch ( error ) {
7687 //banner didn't appear so just continue
7788 }
7889
79- await this . page . getByText ( 'Loading...' ) . first ( ) . waitFor ( { state : 'hidden' , timeout : 15000 } ) ;
90+ await this . page . getByText ( 'Loading...' ) . first ( ) . waitFor ( { state : 'hidden' , timeout : TIMEOUTS . default } ) ;
8091
8192 await this . appNameInput . fill ( appName ) ;
8293 await this . fillDropdown ( this . projectInput , 'default' ) ;
@@ -88,8 +99,9 @@ async createApp(appName: string, repoUrl: string, repoPath: string) {
8899 //dest
89100 await this . clusterUrlInput . fill ( 'https://kubernetes.default.svc' ) ;
90101
91- //deploy
92- await this . namespaceInput . fill ( 'openshift-gitops' ) ;
102+ //deploy to namespace
103+ await this . namespaceInput . fill ( 'openshift-gitops' ) ;
104+
93105 await this . createButton . click ( ) ;
94106 }
95107
@@ -98,38 +110,47 @@ async createApp(appName: string, repoUrl: string, repoPath: string) {
98110 await this . page . getByPlaceholder ( / S e a r c h a p p l i c a t i o n s / i) . fill ( appName ) ;
99111
100112 const appContainer = this . page . locator ( '.white-box, .argo-table-list__row' ) . filter ( { hasText : appName } ) ;
101- await appContainer . waitFor ( { state : 'visible' , timeout : 20000 } ) ;
102- await expect ( appContainer . getByText ( / O u t O f S y n c | O u t o f S y n c / i) . first ( ) ) . toBeVisible ( { timeout : 45000 } ) ;
103- //safe to open the panel
113+ await appContainer . waitFor ( { state : 'visible' , timeout : TIMEOUTS . load } ) ;
114+
115+ //critical cross-version fix: wait for Argo CD to finish its initial Git clone
116+ //if we open the Sync panel before this happens, the resources list will be empty!
117+ await expect ( appContainer . getByText ( / O u t O f S y n c | O u t o f S y n c / i) . first ( ) ) . toBeVisible ( { timeout : TIMEOUTS . sync } ) ;
118+
119+ //now it is safe to open the panel
104120 await appContainer . getByText ( 'Sync' , { exact : true } ) . click ( ) ;
105121
106- //click 'all'
122+ //click 'all' first to ensure all resource checkboxes are ticked across newer Argo CD versions
107123 const allLink = this . page . getByRole ( 'link' , { name : 'all' , exact : true } ) ;
108124 try {
109- await allLink . waitFor ( { state : 'visible' , timeout : 5000 } ) ;
125+ await allLink . waitFor ( { state : 'visible' , timeout : TIMEOUTS . modal } ) ;
110126 await allLink . click ( ) ;
111127 } catch ( error ) {
112- // all link didn't appear within 5 sec
128+ //' all' link didn't appear which is normal for this version so do nothing.
113129 }
114-
115- //wait for the manifests to render on the panel
116- await expect ( this . page . getByText ( expectedResource ) . first ( ) ) . toBeVisible ( { timeout : 30000 } ) ;
130+
131+ //wait for the manifests to render on the panel (generous timeout for slower FIPS clusters)
132+ await expect ( this . page . getByText ( expectedResource ) . first ( ) ) . toBeVisible ( { timeout : TIMEOUTS . render } ) ;
117133
118134 //click the main sync button
119135 await this . page . getByRole ( 'button' , { name : / ^ s y n c h r o n i z e $ / i } ) . first ( ) . click ( ) ;
120136
121137 //wait for the panel to close
122- await expect ( this . page . getByText ( 'SYNCHRONIZE RESOURCES' ) ) . toBeHidden ( { timeout : 15000 } ) ;
138+ await expect ( this . page . getByText ( 'SYNCHRONIZE RESOURCES' ) ) . toBeHidden ( { timeout : TIMEOUTS . panel } ) ;
123139 }
124140
125141 async verifyStatus ( appName : string ) {
126- //re-apply search filter just in case
127142 await this . page . getByPlaceholder ( / S e a r c h a p p l i c a t i o n s / i) . fill ( appName ) ;
128143 const appContainer = this . page . locator ( '.white-box, .argo-table-list__row' ) . filter ( { hasText : appName } ) ;
129144
130- //90 secs
131- await expect ( appContainer . getByText ( / s y n c e d / i) ) . toBeVisible ( { timeout : 90000 } ) ;
132- await expect ( appContainer . getByText ( / h e a l t h y / i) ) . toBeVisible ( { timeout : 90000 } ) ;
145+ //pass the message
146+ await expect (
147+ appContainer . getByText ( / S y n c f a i l e d / i) ,
148+ `Argo CD failed to sync the application manifests for ${ appName } .`
149+ ) . toBeHidden ( { timeout : TIMEOUTS . panel } ) ;
150+
151+ //if it didn't fail to wait for success states
152+ await expect ( appContainer . getByText ( / s y n c e d / i) ) . toBeVisible ( { timeout : TIMEOUTS . status } ) ;
153+ await expect ( appContainer . getByText ( / h e a l t h y / i) ) . toBeVisible ( { timeout : TIMEOUTS . status } ) ;
133154 }
134155
135156 async openApplication ( appName : string ) {
@@ -141,10 +162,10 @@ async createApp(appName: string, repoUrl: string, repoPath: string) {
141162 . filter ( { hasText : appName } )
142163 . getByRole ( 'link' , { name : appName } ) ;
143164
144- await appLink . waitFor ( { state : 'visible' , timeout : 15000 } ) ;
165+ await appLink . waitFor ( { state : 'visible' , timeout : TIMEOUTS . default } ) ;
145166 await appLink . click ( ) ;
146167
147168 //wait for the URL to change to the details page to ensure the click worked
148- await expect ( this . page ) . toHaveURL ( / .* \/ a p p l i c a t i o n s \/ .* \/ .* / , { timeout : 15000 } ) ;
169+ await expect ( this . page ) . toHaveURL ( / .* \/ a p p l i c a t i o n s \/ .* \/ .* / , { timeout : TIMEOUTS . default } ) ;
149170 }
150171}
0 commit comments