@@ -70,10 +70,21 @@ export class AppCatalogPage extends BasePage {
7070
7171 if ( await this . elementExists ( installButton , 3000 ) ) {
7272 await this . smartClick ( installButton , 'Install button' ) ;
73- await this . waiter . delay ( 2000 ) ;
73+ this . logger . info ( 'Clicked Install button, waiting for install page to load' ) ;
74+
75+ // Wait for URL to change to install page and page to stabilize
76+ await this . page . waitForURL ( / \/ f o u n d r y \/ a p p - c a t a l o g \/ [ ^ \/ ] + \/ i n s t a l l $ / , { timeout : 10000 } ) ;
77+ await this . page . waitForLoadState ( 'domcontentloaded' ) ;
78+ await this . page . waitForLoadState ( 'networkidle' ) ;
79+
80+ // Handle permissions dialog
81+ await this . handlePermissionsDialog ( ) ;
7482
7583 // Check for ServiceNow configuration screen
76- await this . handleServiceNowConfiguration ( ) ;
84+ await this . configureServiceNowIfNeeded ( ) ;
85+
86+ // Click final Install app button
87+ await this . clickInstallAppButton ( ) ;
7788
7889 // Wait for installation to complete
7990 await this . waitForInstallation ( appName ) ;
@@ -87,50 +98,94 @@ export class AppCatalogPage extends BasePage {
8798 }
8899
89100 /**
90- * Handle ServiceNow configuration screen if present
101+ * Handle permissions dialog if present
91102 */
92- private async handleServiceNowConfiguration ( ) : Promise < void > {
93- // Check if ServiceNow configuration modal appears
94- const configHeading = this . page . getByRole ( 'heading' , { name : / s e r v i c e n o w | c o n f i g u r a t i o n / i } ) ;
95-
96- if ( await this . elementExists ( configHeading , 3000 ) ) {
97- this . logger . info ( 'ServiceNow configuration screen detected, filling dummy values' ) ;
98-
99- // Fill Instance URL
100- const instanceUrlField = this . page . getByLabel ( / i n s t a n c e .* u r l / i)
101- . or ( this . page . getByPlaceholder ( / i n s t a n c e .* u r l / i) )
102- . first ( ) ;
103- if ( await this . elementExists ( instanceUrlField , 2000 ) ) {
104- await instanceUrlField . fill ( 'https://dummy.service-now.com' ) ;
105- this . logger . info ( 'Filled Instance URL' ) ;
106- }
103+ private async handlePermissionsDialog ( ) : Promise < void > {
104+ const acceptButton = this . page . getByRole ( 'button' , { name : / a c c e p t .* c o n t i n u e / i } ) ;
107105
108- // Fill Username
109- const usernameField = this . page . getByLabel ( / u s e r n a m e / i)
110- . or ( this . page . getByPlaceholder ( / u s e r n a m e / i) )
111- . first ( ) ;
112- if ( await this . elementExists ( usernameField , 2000 ) ) {
113- await usernameField . fill ( 'dummy_user' ) ;
114- this . logger . info ( 'Filled Username' ) ;
115- }
106+ if ( await this . elementExists ( acceptButton , 3000 ) ) {
107+ this . logger . info ( 'Permissions dialog detected, accepting' ) ;
108+ await this . smartClick ( acceptButton , 'Accept and continue button' ) ;
109+ await this . waiter . delay ( 2000 ) ;
110+ }
111+ }
116112
117- // Fill Password (must be >8 characters)
118- const passwordField = this . page . getByLabel ( / p a s s w o r d / i)
119- . or ( this . page . getByPlaceholder ( / p a s s w o r d / i) )
120- . first ( ) ;
121- if ( await this . elementExists ( passwordField , 2000 ) ) {
122- await passwordField . fill ( 'DummyPassword123' ) ;
123- this . logger . info ( 'Filled Password' ) ;
124- }
113+ /**
114+ * Configure ServiceNow API integration if configuration form is present
115+ */
116+ private async configureServiceNowIfNeeded ( ) : Promise < void > {
117+ this . logger . info ( 'Checking if ServiceNow API configuration is required...' ) ;
118+
119+ // Check if there are text input fields (configuration form)
120+ const textInputs = this . page . locator ( 'input[type="text"]' ) ;
121+
122+ try {
123+ await textInputs . first ( ) . waitFor ( { state : 'visible' , timeout : 15000 } ) ;
124+ const count = await textInputs . count ( ) ;
125+ this . logger . info ( `ServiceNow configuration form detected with ${ count } input fields` ) ;
126+ } catch ( error ) {
127+ this . logger . info ( 'No ServiceNow configuration required - no input fields found' ) ;
128+ return ;
129+ }
130+
131+ this . logger . info ( 'ServiceNow configuration required, filling dummy values' ) ;
132+
133+ // Fill configuration fields using index-based selection
134+ // Field 1: Name
135+ const nameField = this . page . locator ( 'input[type="text"]' ) . first ( ) ;
136+ await nameField . fill ( 'ServiceNow Test Instance' ) ;
137+ this . logger . debug ( 'Filled Name field' ) ;
138+
139+ // Field 2: Instance (the {instance} part of {instance}.service-now.com)
140+ const instanceField = this . page . locator ( 'input[type="text"]' ) . nth ( 1 ) ;
141+ await instanceField . fill ( 'dev12345' ) ;
142+ this . logger . debug ( 'Filled Instance field' ) ;
143+
144+ // Field 3: Username
145+ const usernameField = this . page . locator ( 'input[type="text"]' ) . nth ( 2 ) ;
146+ await usernameField . fill ( 'dummy_user' ) ;
147+ this . logger . debug ( 'Filled Username field' ) ;
148+
149+ // Field 4: Password (must be >8 characters)
150+ const passwordField = this . page . locator ( 'input[type="password"]' ) . first ( ) ;
151+ if ( await this . elementExists ( passwordField , 2000 ) ) {
152+ await passwordField . fill ( 'DummyPassword123' ) ;
153+ this . logger . debug ( 'Filled Password field' ) ;
154+ } else {
155+ // Try as text input if password field not found
156+ const passwordTextInput = this . page . locator ( 'input[type="text"]' ) . nth ( 3 ) ;
157+ await passwordTextInput . fill ( 'DummyPassword123' ) ;
158+ this . logger . debug ( 'Filled Password field (text input)' ) ;
159+ }
160+
161+ // Wait for network to settle after filling form
162+ await this . page . waitForLoadState ( 'networkidle' ) ;
125163
126- // Click Next or Install button to proceed
127- const proceedButton = this . page . getByRole ( 'button' , { name : / n e x t | i n s t a l l | c o n t i n u e / i } ) ;
128- if ( await this . elementExists ( proceedButton , 2000 ) ) {
129- await this . smartClick ( proceedButton , 'Proceed button' ) ;
130- this . logger . info ( 'Clicked proceed button after configuration' ) ;
131- await this . waiter . delay ( 2000 ) ;
164+ this . logger . success ( 'ServiceNow API configuration completed' ) ;
165+ }
166+
167+ /**
168+ * Click the final "Install app" button
169+ */
170+ private async clickInstallAppButton ( ) : Promise < void > {
171+ const installButton = this . page . getByRole ( 'button' , { name : 'Install app' } ) ;
172+
173+ // Wait for button to be enabled after form completion
174+ await this . waiter . waitForVisible ( installButton , { description : 'Install app button' } ) ;
175+
176+ // Ensure button is enabled before clicking
177+ let attempts = 0 ;
178+ while ( attempts < 10 ) {
179+ const isEnabled = await installButton . isEnabled ( ) ;
180+ if ( isEnabled ) {
181+ break ;
132182 }
183+ await this . waiter . delay ( 1000 ) ;
184+ attempts ++ ;
133185 }
186+
187+ await this . smartClick ( installButton , 'Install app button' ) ;
188+ this . logger . info ( 'Clicked Install app button' ) ;
134189 }
135190
136191 /**
0 commit comments