@digital-ai/devops-page-object-deploy 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -1,5 +1,6 @@
1
1
  var $1UEKj$playwrighttest = require("@playwright/test");
2
2
 
3
+
3
4
  function $parcel$export(e, n, v, s) {
4
5
  Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
5
6
  }
@@ -14,117 +15,2553 @@ class $ae23b16684112d0b$export$2b65d1d97338f32b {
14
15
  }
15
16
 
16
17
 
18
+ class $84f8d1abd29d48fd$export$cb21416f3c2fd176 extends (0, $ae23b16684112d0b$export$2b65d1d97338f32b) {
19
+ async handleSaveToastOrAlreadyExists() {
20
+ const toastMessage = this.page.locator('.redux-toastr .rrt-text:visible');
21
+ try {
22
+ await toastMessage.waitFor({
23
+ state: 'visible',
24
+ timeout: 10000
25
+ });
26
+ } catch {
27
+ // No toast appeared — assume the operation succeeded silently (e.g. fast CI environments).
28
+ return;
29
+ }
30
+ const toastText = (await toastMessage.textContent())?.trim() ?? '';
31
+ if (toastText === 'Your change was saved.') return;
32
+ if (/already exists/i.test(toastText)) {
33
+ const cancelButton = this.page.getByRole('button', {
34
+ name: 'Cancel'
35
+ });
36
+ if (await cancelButton.isVisible()) await cancelButton.click();
37
+ else await this.page.locator('i.xl-icon.close-icon').first().click();
38
+ return;
39
+ }
40
+ await (0, $1UEKj$playwrighttest.expect)(toastMessage).toHaveText('Your change was saved.');
41
+ }
42
+ async openRootContextMenu(rootId) {
43
+ await this.closeModalIfPresent();
44
+ await this.ensureExplorerTreeReady();
45
+ const rootTitle = this.page.locator(`span[data-id="${rootId}"]`);
46
+ await rootTitle.scrollIntoViewIfNeeded();
47
+ await rootTitle.click();
48
+ const menuButton = this.page.locator(`i.context-menu-button[data-id="${rootId}"]`);
49
+ await menuButton.scrollIntoViewIfNeeded();
50
+ for(let attempt = 0; attempt < 3; attempt += 1)try {
51
+ await menuButton.click({
52
+ force: true
53
+ });
54
+ return;
55
+ } catch {
56
+ if (attempt === 2) throw new Error(`Failed to open context menu for ${rootId}`);
57
+ await this.page.waitForTimeout(300);
58
+ }
59
+ }
60
+ async ensureExplorerTreeReady() {
61
+ const applicationsRoot = this.page.locator('div[data-id="Applications"]');
62
+ try {
63
+ await applicationsRoot.waitFor({
64
+ state: 'visible',
65
+ timeout: 3000
66
+ });
67
+ return;
68
+ } catch {
69
+ // Fall through to open Explorer.
70
+ }
71
+ const explorerCandidates = [
72
+ this.page.locator('p.MuiTypography-root', {
73
+ hasText: 'Explorer'
74
+ }),
75
+ this.page.getByRole('link', {
76
+ name: 'Explorer'
77
+ }),
78
+ this.page.getByRole('button', {
79
+ name: 'Explorer'
80
+ })
81
+ ];
82
+ for (const candidate of explorerCandidates)try {
83
+ if (await candidate.count() > 0) {
84
+ await candidate.first().click({
85
+ timeout: 10000
86
+ });
87
+ break;
88
+ }
89
+ } catch {
90
+ // Try next candidate.
91
+ }
92
+ await applicationsRoot.waitFor({
93
+ state: 'visible',
94
+ timeout: 10000
95
+ });
96
+ }
97
+ async closeModalIfPresent() {
98
+ const modal = this.page.locator('div.xl-react-widget-modal-dialog.in');
99
+ if (await modal.count() === 0) return;
100
+ if (!await modal.first().isVisible()) return;
101
+ const cancelButton = modal.getByRole('button', {
102
+ name: 'Cancel'
103
+ }).first();
104
+ if (await cancelButton.isVisible()) await cancelButton.click();
105
+ else {
106
+ const closeIcon = modal.locator('i.xl-icon.close-icon').first();
107
+ if (await closeIcon.isVisible()) await closeIcon.click();
108
+ }
109
+ try {
110
+ await this.page.locator('body.xl-react-modal-open').waitFor({
111
+ state: 'hidden',
112
+ timeout: 10000
113
+ });
114
+ } catch {
115
+ // Modal may already be closing.
116
+ }
117
+ }
118
+ }
119
+
120
+
121
+
122
+
17
123
  class $f280624787ceceed$export$36d69433c4f81145 extends (0, $ae23b16684112d0b$export$2b65d1d97338f32b) {
18
124
  async clickUserManagement() {
19
- await this.page.locator("p.MuiTypography-root", {
20
- hasText: "User management"
125
+ await this.page.locator('p.MuiTypography-root', {
126
+ hasText: 'User management'
21
127
  }).click();
22
- await this.page.getByRole("link", {
23
- name: "Users"
128
+ await this.page.getByRole('link', {
129
+ name: 'Users'
130
+ }).click();
131
+ }
132
+ async clickExplorer() {
133
+ await this.page.locator('p.MuiTypography-root', {
134
+ hasText: 'Explorer'
24
135
  }).click();
25
136
  }
26
137
  async verifyDeployLogoIsPresent() {
27
- const logo = this.page.getByAltText("Deploy");
138
+ const logo = this.page.getByAltText('Deploy');
28
139
  await (0, $1UEKj$playwrighttest.expect)(logo).toBeVisible();
29
140
  }
30
141
  async verifyDeployTextIsPresent() {
31
- const title = this.page.getByTitle("digital.ai");
142
+ const title = this.page.getByTitle('digital.ai');
32
143
  await (0, $1UEKj$playwrighttest.expect)(title).toBeVisible();
33
144
  }
145
+ async clickReports() {
146
+ await this.page.locator('p.MuiTypography-root', {
147
+ hasText: 'Reports'
148
+ }).click();
149
+ }
150
+ async verifyReportsOnlyAccess() {
151
+ const sidebar = this.page.locator('.dot-sidebar');
152
+ await (0, $1UEKj$playwrighttest.expect)(sidebar.getByText('Reports', {
153
+ exact: true
154
+ })).toBeVisible();
155
+ await (0, $1UEKj$playwrighttest.expect)(sidebar.getByText('Explorer', {
156
+ exact: true
157
+ })).toBeVisible();
158
+ await (0, $1UEKj$playwrighttest.expect)(sidebar.getByText('Rules and Macros', {
159
+ exact: true
160
+ })).toHaveCount(0);
161
+ await (0, $1UEKj$playwrighttest.expect)(sidebar.getByText('Gitops', {
162
+ exact: true
163
+ })).toHaveCount(0);
164
+ await (0, $1UEKj$playwrighttest.expect)(sidebar.getByText('Local sources', {
165
+ exact: true
166
+ })).toHaveCount(0);
167
+ await (0, $1UEKj$playwrighttest.expect)(sidebar.getByText('Plugins', {
168
+ exact: true
169
+ })).toHaveCount(0);
170
+ await (0, $1UEKj$playwrighttest.expect)(sidebar.getByText('User management', {
171
+ exact: true
172
+ })).toHaveCount(0);
173
+ }
174
+ async verifyNoCiDataVisible() {
175
+ await (0, $1UEKj$playwrighttest.expect)(this.page.getByText('LocalHost', {
176
+ exact: true
177
+ })).toHaveCount(0);
178
+ await (0, $1UEKj$playwrighttest.expect)(this.page.getByText('LocalEnv', {
179
+ exact: true
180
+ })).toHaveCount(0);
181
+ await (0, $1UEKj$playwrighttest.expect)(this.page.getByText('App', {
182
+ exact: true
183
+ })).toHaveCount(0);
184
+ }
34
185
  }
35
186
 
36
187
 
37
188
 
38
189
 
39
190
  class $c949c453826379e3$export$ecdc3cf9d1cfe143 extends (0, $ae23b16684112d0b$export$2b65d1d97338f32b) {
40
- /**
41
- * Login with username and password
42
- * @param userName
43
- * @param password
44
- */ // this should be refactored to take an object and not disable the rule!!
45
- // eslint-disable-next-line max-params
46
- async createNewUser(username, fullName, email, password, confirmPassword) {
47
- await this.page.locator("p.MuiTypography-root", {
48
- hasText: "Users"
49
- }).click();
50
- await this.page.getByRole("button", {
51
- name: "Add user"
52
- }).click();
53
- await this.page.locator('input[name="username"]').fill(username);
54
- await this.page.locator('input[name="fullName"]').fill(fullName);
191
+ async openUsersTab() {
192
+ await this.page.locator('p.MuiTypography-root', {
193
+ hasText: 'Users'
194
+ }).click();
195
+ }
196
+ async waitForToastIfPresent(timeoutVisible = 3000, timeoutHidden = 5000) {
197
+ const toast = this.page.getByRole('alertdialog');
198
+ try {
199
+ await toast.waitFor({
200
+ state: 'visible',
201
+ timeout: timeoutVisible
202
+ });
203
+ await toast.waitFor({
204
+ state: 'hidden',
205
+ timeout: timeoutHidden
206
+ });
207
+ } catch {
208
+ // Toast is transient; skip if it doesn't appear.
209
+ }
210
+ }
211
+ async waitForModalCloseIfPresent() {
212
+ try {
213
+ await this.page.locator('input[name="username"]').waitFor({
214
+ state: 'hidden',
215
+ timeout: 5000
216
+ });
217
+ await this.page.locator('body.xl-react-modal-open').waitFor({
218
+ state: 'hidden',
219
+ timeout: 5000
220
+ });
221
+ await this.page.waitForLoadState('networkidle');
222
+ } catch {
223
+ // When user already exists, modal stays open; caller handles error toast.
224
+ }
225
+ }
226
+ async openGlobalPermissions() {
227
+ await this.page.getByRole('link', {
228
+ name: 'Global permissions'
229
+ }).click();
230
+ }
231
+ async filterRoleInPermissions(roleName) {
232
+ const filter = this.page.locator('input[placeholder="Filter by roles"]:visible');
233
+ await filter.waitFor({
234
+ state: 'visible'
235
+ });
236
+ await (0, $1UEKj$playwrighttest.expect)(filter).toBeEditable();
237
+ await filter.fill('');
238
+ await filter.fill(roleName);
239
+ await (0, $1UEKj$playwrighttest.expect)(filter).toHaveValue(roleName);
240
+ await filter.press('Enter');
241
+ await this.page.getByRole('button', {
242
+ name: 'Refresh'
243
+ }).click();
244
+ await this.page.waitForLoadState('networkidle');
245
+ await this.page.locator('table tbody tr:visible').first().waitFor({
246
+ state: 'visible'
247
+ });
248
+ const escapedRoleName = roleName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
249
+ const roleCell = this.page.locator('td.permissions-pagination-table-component-role:visible').filter({
250
+ hasText: new RegExp(`^\\s*${escapedRoleName}\\s*$`)
251
+ }).first();
252
+ await (0, $1UEKj$playwrighttest.expect)(roleCell).toBeVisible();
253
+ return roleCell.locator('xpath=ancestor::tr');
254
+ }
255
+ /* Create a new user */ async createNewUser(username, email, password) {
256
+ await this.openUsersTab();
257
+ await this.page.getByRole('button', {
258
+ name: 'Add user'
259
+ }).click();
260
+ const usernameInput = this.page.locator('input[name="username"]');
261
+ await usernameInput.waitFor({
262
+ state: 'visible'
263
+ });
264
+ await usernameInput.fill(username);
265
+ await this.page.locator('input[name="fullName"]').fill(username);
55
266
  await this.page.locator('input[name="email"]').fill(email);
56
267
  const passwordInput = this.page.locator('input[name="password"]');
268
+ const confirmInput = this.page.locator('input[name="confirmPassword"]');
269
+ await passwordInput.waitFor({
270
+ state: 'visible'
271
+ });
57
272
  await passwordInput.click({
58
273
  force: true
59
274
  });
60
275
  await passwordInput.fill(password);
61
- await this.page.locator('input[name="confirmPassword"]').fill(confirmPassword);
62
- await this.page.getByRole("button", {
63
- name: "Save"
276
+ await confirmInput.fill(password);
277
+ const saveButton = this.page.getByRole('button', {
278
+ name: 'Save'
279
+ });
280
+ try {
281
+ await (0, $1UEKj$playwrighttest.expect)(saveButton).toBeEnabled({
282
+ timeout: 5000
283
+ });
284
+ } catch {
285
+ await passwordInput.fill('');
286
+ await confirmInput.fill('');
287
+ await passwordInput.type(password, {
288
+ delay: 50
289
+ });
290
+ await confirmInput.type(password, {
291
+ delay: 50
292
+ });
293
+ await (0, $1UEKj$playwrighttest.expect)(saveButton).toBeEnabled({
294
+ timeout: 5000
295
+ });
296
+ }
297
+ await saveButton.click();
298
+ await this.waitForToastIfPresent();
299
+ await this.waitForModalCloseIfPresent();
300
+ }
301
+ /* Verify success message */ async verifySuccessMessage() {
302
+ const successMessage = this.page.getByRole('alertdialog');
303
+ try {
304
+ await successMessage.waitFor({
305
+ state: 'visible',
306
+ timeout: 3000
307
+ });
308
+ } catch {
309
+ // Toast never appeared — skip silently.
310
+ return;
311
+ }
312
+ await (0, $1UEKj$playwrighttest.expect)(successMessage).toHaveText(/User ".*" created/i);
313
+ }
314
+ /* Verify user error message */ async verifyUserErrorMessage() {
315
+ const errorToast = this.page.locator('.rrt-middle-container[role="alertdialog"]').nth(1);
316
+ try {
317
+ await errorToast.waitFor({
318
+ state: 'visible',
319
+ timeout: 3000
320
+ });
321
+ } catch {
322
+ // Toast is transient; skip if it never appears.
323
+ return;
324
+ }
325
+ await (0, $1UEKj$playwrighttest.expect)(errorToast).toContainText(/already in use/i);
326
+ }
327
+ /* Delete the user and verify it is deleted */ async deleteTheUserAndVerifyItIsDeleted(username) {
328
+ await this.openUsersTab();
329
+ const filter = this.page.getByPlaceholder('Filter...');
330
+ await filter.fill(username);
331
+ await filter.press('Enter');
332
+ const escapedUsername = username.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
333
+ const userRow = this.page.getByRole('row', {
334
+ name: new RegExp(`\\b${escapedUsername}\\b`)
335
+ }).first();
336
+ await userRow.waitFor({
337
+ state: 'visible'
338
+ });
339
+ await userRow.locator('button.delete.xl-icon.table-action').click();
340
+ const confirmDialog = this.page.locator('.xl-react-widget-modal-dialog.in:visible').filter({
341
+ hasText: /Delete user/i
342
+ }).first();
343
+ await confirmDialog.waitFor({
344
+ state: 'visible'
345
+ });
346
+ await confirmDialog.getByRole('button', {
347
+ name: 'Delete',
348
+ exact: true
349
+ }).click();
350
+ await confirmDialog.waitFor({
351
+ state: 'hidden'
352
+ });
353
+ const deleteToast = this.page.getByRole('alertdialog').filter({
354
+ hasText: /deleted/i
355
+ });
356
+ await (0, $1UEKj$playwrighttest.expect)(deleteToast).toHaveText(/User ".*" deleted/i);
357
+ await deleteToast.waitFor({
358
+ state: 'hidden'
359
+ });
360
+ await userRow.waitFor({
361
+ state: 'detached'
362
+ });
363
+ }
364
+ /* Edit the user and verify the changes */ async editUserAndVerify(username, editfullname) {
365
+ await this.openUsersTab();
366
+ const filter = this.page.getByPlaceholder('Filter...');
367
+ await filter.fill(username);
368
+ await filter.press('Enter');
369
+ const escapedUsername = username.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
370
+ const userRow = this.page.getByRole('row', {
371
+ name: new RegExp(`\\b${escapedUsername}\\b`)
372
+ }).first();
373
+ await userRow.waitFor({
374
+ state: 'visible'
375
+ });
376
+ await userRow.locator('i.icon-edit.dot-i').click();
377
+ await this.page.locator('input[name="fullName"]').fill(editfullname);
378
+ const passwordInput = this.page.locator('input[name="password"]');
379
+ const confirmInput = this.page.locator('input[name="confirmPassword"]');
380
+ if (await passwordInput.count()) {
381
+ await passwordInput.click({
382
+ force: true
383
+ });
384
+ await passwordInput.press(process.platform === 'darwin' ? 'Meta+A' : 'Control+A');
385
+ await passwordInput.press('Backspace');
386
+ }
387
+ if (await confirmInput.count()) {
388
+ await confirmInput.click({
389
+ force: true
390
+ });
391
+ await confirmInput.press(process.platform === 'darwin' ? 'Meta+A' : 'Control+A');
392
+ await confirmInput.press('Backspace');
393
+ }
394
+ const saveButton = this.page.getByRole('button', {
395
+ name: 'Save',
396
+ exact: true
397
+ });
398
+ await (0, $1UEKj$playwrighttest.expect)(saveButton).toBeEnabled({
399
+ timeout: 5000
400
+ });
401
+ await saveButton.click();
402
+ const editToast = this.page.getByRole('alertdialog').filter({
403
+ hasText: 'Your change was saved'
404
+ });
405
+ await (0, $1UEKj$playwrighttest.expect)(editToast).toBeVisible();
406
+ }
407
+ /* Disable the user by toggling the login permission checkbox */ async disableUser(username) {
408
+ await this.openUsersTab();
409
+ const filter = this.page.getByPlaceholder('Filter...');
410
+ await filter.fill(username);
411
+ await filter.press('Enter');
412
+ const escapedUsername = username.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
413
+ const userRow = this.page.getByRole('row', {
414
+ name: new RegExp(`\\b${escapedUsername}\\b`)
415
+ }).first();
416
+ await userRow.waitFor({
417
+ state: 'visible'
418
+ });
419
+ const checkbox = userRow.getByRole('checkbox');
420
+ await checkbox.click();
421
+ await (0, $1UEKj$playwrighttest.expect)(checkbox).not.toBeChecked();
422
+ }
423
+ /* Revoke login permission for a role via Global permissions */ async revokeLoginPermission(roleName) {
424
+ await this.openGlobalPermissions();
425
+ const roleRow = await this.filterRoleInPermissions(roleName);
426
+ const normalizedRoleName = roleName.replace(/\s+/g, '_');
427
+ const loginCheckbox = roleRow.locator(`td.permissions-pagination-table-component-${normalizedRoleName}-4 input[type="checkbox"]`);
428
+ const saveButton = this.page.getByRole('button', {
429
+ name: 'Save'
430
+ });
431
+ if (await loginCheckbox.isVisible() && await loginCheckbox.isChecked()) {
432
+ await loginCheckbox.uncheck();
433
+ await saveButton.click();
434
+ const saveMessage = this.page.getByRole('alertdialog');
435
+ await (0, $1UEKj$playwrighttest.expect)(saveMessage).toContainText('Your change was saved');
436
+ return;
437
+ }
438
+ if (await saveButton.isVisible()) await (0, $1UEKj$playwrighttest.expect)(saveButton).toBeDisabled();
439
+ }
440
+ /* Grant only login and report view permissions for a role */ async grantOnlyLoginAndReportPermissions(roleName) {
441
+ await this.openGlobalPermissions();
442
+ const roleRow = await this.filterRoleInPermissions(roleName);
443
+ const checkboxes = roleRow.locator('input[type="checkbox"]');
444
+ const count = await checkboxes.count();
445
+ for(let i = 0; i < count; i += 1){
446
+ const checkbox = checkboxes.nth(i);
447
+ if (await checkbox.isChecked()) await checkbox.uncheck();
448
+ }
449
+ await roleRow.locator(`td.permissions-pagination-table-component-${roleName}-4 input[type="checkbox"]`).check();
450
+ await roleRow.locator(`td.permissions-pagination-table-component-${roleName}-5 input[type="checkbox"]`).check();
451
+ await this.page.getByRole('button', {
452
+ name: 'Save'
453
+ }).click();
454
+ const saveMessage = this.page.getByRole('alertdialog');
455
+ await (0, $1UEKj$playwrighttest.expect)(saveMessage).toContainText('Your change was saved');
456
+ }
457
+ /* Create a role and assign a principal to it */ async createRoleAndAssignUser(roleName, principal) {
458
+ await this.page.getByRole('link', {
459
+ name: 'Roles'
460
+ }).click();
461
+ await this.page.getByPlaceholder('Filter by roles').first().waitFor({
462
+ state: 'visible'
463
+ });
464
+ await this.page.locator('body.xl-react-modal-open').waitFor({
465
+ state: 'hidden'
466
+ });
467
+ await this.page.getByRole('button', {
468
+ name: 'Add role'
469
+ }).click();
470
+ const roleDialog = this.page.locator('.add-edit-role-component');
471
+ await roleDialog.waitFor({
472
+ state: 'visible'
473
+ });
474
+ await roleDialog.locator('input[name="name"]').fill(roleName);
475
+ const principalsInput = roleDialog.locator('input.react-tagsinput-input');
476
+ await principalsInput.fill(principal);
477
+ await principalsInput.press('Enter');
478
+ await roleDialog.getByRole('button', {
479
+ name: 'Save'
480
+ }).click();
481
+ const saveMessage = this.page.getByRole('alertdialog');
482
+ await (0, $1UEKj$playwrighttest.expect)(saveMessage).toHaveText(/Role ".*" created/);
483
+ await saveMessage.waitFor({
484
+ state: 'hidden'
485
+ });
486
+ await roleDialog.waitFor({
487
+ state: 'hidden'
488
+ });
489
+ }
490
+ /* Delete a role by name */ async deleteRole(roleName) {
491
+ await this.page.getByRole('link', {
492
+ name: 'Roles'
493
+ }).click();
494
+ const filter = this.page.getByPlaceholder('Filter by roles').first();
495
+ await filter.fill(roleName);
496
+ await filter.press('Enter');
497
+ const escapedRoleName = roleName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
498
+ const roleRow = this.page.locator('td.roles-table-name').filter({
499
+ hasText: new RegExp(`^\\s*${escapedRoleName}\\s*$`)
500
+ }).first().locator('xpath=ancestor::tr');
501
+ await roleRow.waitFor({
502
+ state: 'visible'
503
+ });
504
+ await roleRow.locator('button.delete.table-action').click();
505
+ const confirmDelete = this.page.getByRole('button', {
506
+ name: 'Delete',
507
+ exact: true
508
+ });
509
+ if (await confirmDelete.isVisible()) await confirmDelete.click();
510
+ }
511
+ }
512
+
513
+
514
+
515
+ class $647b67e126f2dbe0$export$c88e1d16f82516d extends (0, $84f8d1abd29d48fd$export$cb21416f3c2fd176) {
516
+ /* Helps to navigate to the Overthere LocalHost page */ async navigateToOverthereLocalHostPage() {
517
+ await this.openRootContextMenu('Infrastructure');
518
+ await this.page.getByRole('link', {
519
+ name: 'New'
520
+ }).click();
521
+ await this.page.getByRole('link', {
522
+ name: 'overthere'
523
+ }).click();
524
+ await this.page.getByRole('link', {
525
+ name: 'LocalHost'
526
+ }).click();
527
+ }
528
+ /* Opens the New > overthere > LocalHost form from the Infrastructure tree context menu */ async openNewLocalHost() {
529
+ await this.closeModalIfPresent();
530
+ await this.ensureExplorerTreeReady();
531
+ const infrastructureRoot = this.page.locator('span[data-id="Infrastructure"]');
532
+ await infrastructureRoot.scrollIntoViewIfNeeded();
533
+ await infrastructureRoot.click();
534
+ const contextMenu = this.page.locator('i.context-menu-button[data-id="Infrastructure"]');
535
+ await contextMenu.scrollIntoViewIfNeeded();
536
+ await contextMenu.click({
537
+ force: true
538
+ });
539
+ await this.page.getByRole('link', {
540
+ name: 'New'
541
+ }).click();
542
+ await this.page.getByRole('link', {
543
+ name: 'overthere'
544
+ }).click();
545
+ await this.page.getByRole('link', {
546
+ name: 'LocalHost'
547
+ }).click();
548
+ }
549
+ async openInfrastructureNewLocalHost() {
550
+ await this.openNewLocalHost();
551
+ }
552
+ /* Helps to create a LocalHost of type UNIX/Windows/Zos */ async createHostForGivenType(name, type) {
553
+ await this.page.locator('input[name="name"]').fill(name);
554
+ await this.page.locator('label:has-text("Operating system")').locator('xpath=following::input[1]').click();
555
+ await this.page.getByText(type, {
556
+ exact: true
557
+ }).click();
558
+ await this.page.getByRole('button', {
559
+ name: 'Save and close'
64
560
  }).click();
561
+ await this.handleSaveToastOrAlreadyExists();
65
562
  }
66
- async verifySuccessMessage() {
67
- const SuccessMessage = this.page.getByRole("alertdialog");
68
- await (0, $1UEKj$playwrighttest.expect)(SuccessMessage).toBeVisible();
69
- await (0, $1UEKj$playwrighttest.expect)(SuccessMessage).toHaveText(/User ".*" created/i);
563
+ /**
564
+ * Runs the "Check connection" control task on the given host node once.
565
+ * Expects the deployment plan page, executes it, waits for EXECUTED state, then finishes.
566
+ * @param hostDataId The full tree data-id, e.g. "Infrastructure/MyHost"
567
+ */ async runCheckConnection(hostDataId) {
568
+ // Ensure the tree node is visible by expanding Infrastructure if needed
569
+ const infraToggle = this.page.locator('div[data-id="Infrastructure"] .infinite-tree-node > a');
570
+ const hostNode = this.page.locator(`span[data-id="${hostDataId}"]`);
571
+ for(let attempt = 0; attempt < 3; attempt += 1){
572
+ if (await hostNode.count() > 0 && await hostNode.isVisible()) break;
573
+ await infraToggle.click();
574
+ await this.page.waitForTimeout(500);
575
+ }
576
+ // Hover host node to reveal context menu button
577
+ //await hostNode.scrollIntoViewIfNeeded();
578
+ await hostNode.hover();
579
+ const ctxBtn = this.page.locator(`i.context-menu-button[data-id="${hostDataId}"]`);
580
+ await ctxBtn.waitFor({
581
+ state: 'visible',
582
+ timeout: 5000
583
+ });
584
+ await ctxBtn.click({
585
+ force: true
586
+ });
587
+ // Click "Check connection" in the context menu
588
+ await this.page.getByRole('link', {
589
+ name: 'Check connection'
590
+ }).click();
591
+ // Wait for the deployment plan panel and execute
592
+ await this.page.locator('.xl-react-widget-modal-dialog button.deploy.xl-primary, button.xl-primary:has-text("Execute")').first().waitFor({
593
+ state: 'visible',
594
+ timeout: 10000
595
+ });
596
+ await this.page.locator('.xl-react-widget-modal-dialog button.deploy.xl-primary, button.xl-primary:has-text("Execute")').first().click();
597
+ // Wait for EXECUTED status
598
+ const executedLabel = this.page.locator('span.label', {
599
+ hasText: 'EXECUTED'
600
+ }).first();
601
+ await executedLabel.waitFor({
602
+ state: 'visible',
603
+ timeout: 30000
604
+ });
605
+ // Finish
606
+ const finishButton = this.page.locator('button.finish.xl-primary, button:has-text("Finish")').first();
607
+ if (await finishButton.isVisible()) await finishButton.click();
608
+ // Wait for modal/plan to close
609
+ const modal = this.page.locator('div.xl-react-widget-modal-dialog.in');
610
+ if (await modal.count() > 0) await modal.first().waitFor({
611
+ state: 'hidden',
612
+ timeout: 15000
613
+ });
70
614
  }
71
- async verifyUserErrorMessage() {
72
- await (0, $1UEKj$playwrighttest.expect)(this.page.locator('.rrt-middle-container[role="alertdialog"]').nth(1)).toContainText(/already in use/i);
615
+ }
616
+
617
+
618
+
619
+ class $f8bdd801aee9e08b$export$12530425267834a9 extends (0, $84f8d1abd29d48fd$export$cb21416f3c2fd176) {
620
+ /* Helps to navigate to the Environments page */ async navigateToEnvironmentsPage() {
621
+ await this.ensureExplorerTreeReady();
73
622
  }
74
- async deleteTheUserAndVerifyItIsDeleted(username) {
75
- await this.page.locator("p.MuiTypography-root", {
76
- hasText: "Users"
623
+ /* Helps to create a new Environment */ async createEnvironment(environmentName, hostName, dictionaryName) {
624
+ await this.openRootContextMenu('Environments');
625
+ await this.page.getByRole('link', {
626
+ name: 'New'
627
+ }).click();
628
+ await this.page.getByRole('link', {
629
+ name: 'Environment'
630
+ }).click();
631
+ await this.page.locator('input[name="name"]').fill(environmentName);
632
+ await this.page.locator('i.xl-icon.arrow-down-icon.show-all').nth(0).click();
633
+ await this.page.getByTitle(`Infrastructure/${hostName}`, {
634
+ exact: true
77
635
  }).click();
78
- await this.page.waitForTimeout(10000);
79
- await this.page.getByPlaceholder("Filter...").fill(username);
80
- await this.page.waitForTimeout(10000);
81
- await this.page.locator("td.users-table-actions button.delete.xl-icon.table-action").click();
82
- await this.page.getByRole("button", {
83
- name: "Delete",
636
+ await this.page.locator('i.xl-icon.arrow-down-icon.show-all').nth(1).click();
637
+ await this.page.getByTitle(`Environments/${dictionaryName}`, {
84
638
  exact: true
85
639
  }).click();
86
- const DeleteMessage = this.page.getByRole("alertdialog");
87
- await (0, $1UEKj$playwrighttest.expect)(DeleteMessage).toBeVisible();
88
- await (0, $1UEKj$playwrighttest.expect)(DeleteMessage).toHaveText(/User ".*" deleted/i);
640
+ await this.page.getByRole('button', {
641
+ name: 'Save and close'
642
+ }).click();
643
+ await this.handleSaveToastOrAlreadyExists();
89
644
  }
90
- async editUserAndVerify(username, editfullname) {
91
- await this.page.locator("p.MuiTypography-root", {
92
- hasText: "Users"
645
+ /* Creates a new udm.Dictionary under Environments with entry usr=admin */ async createDictionary(dictionaryName) {
646
+ await this.openRootContextMenu('Environments');
647
+ await this.page.getByRole('link', {
648
+ name: 'New'
93
649
  }).click();
94
- await this.page.waitForTimeout(10000);
95
- await this.page.getByPlaceholder("Filter...").fill(username);
96
- await this.page.waitForTimeout(10000);
97
- await this.page.locator("i.icon-edit.dot-i").first().click();
98
- await this.page.locator('input[name="fullName"]').fill(editfullname);
99
- await this.page.getByRole("button", {
100
- name: "Save",
650
+ await this.page.getByRole('link', {
651
+ name: 'Dictionary',
101
652
  exact: true
102
653
  }).click();
103
- const EditMessage = this.page.getByRole("alertdialog");
104
- await (0, $1UEKj$playwrighttest.expect)(EditMessage).toBeVisible();
105
- const editedUsername = this.page.locator("td.users-table-fullName div").textContent();
106
- // eslint-disable-next-line no-console
107
- console.log(editedUsername);
654
+ await this.page.locator('input[name="name"]').fill(dictionaryName);
655
+ // Type the key in the Entries field and press Enter to create the row
656
+ // (The hint text "Search or create a new key/value" is a div, not a real placeholder)
657
+ const entriesInput = this.page.getByRole('textbox').nth(3);
658
+ await entriesInput.fill('usr');
659
+ await entriesInput.press('Enter');
660
+ // Fill the value field that appears after the key is created
661
+ await this.page.getByRole('textbox', {
662
+ name: 'Add value...'
663
+ }).fill('admin');
664
+ await this.page.getByRole('button', {
665
+ name: 'Save and close'
666
+ }).click();
667
+ await this.handleSaveToastOrAlreadyExists();
108
668
  }
109
669
  }
110
670
 
111
671
 
112
- class $28788a34e6bbbe3d$export$b8a61e5c71402559 {
672
+
673
+
674
+ class $408106958eb335da$export$1533b625ec0c75e2 extends (0, $84f8d1abd29d48fd$export$cb21416f3c2fd176) {
113
675
  constructor(page){
114
- this.page = page;
676
+ super(page);
677
+ }
678
+ /* Helps to navigate to the Applications page */ async navigateToApplicationsPage() {
679
+ await this.openRootContextMenu('Applications');
680
+ await this.page.getByRole('link', {
681
+ name: 'New'
682
+ }).click();
683
+ await this.page.getByRole('link', {
684
+ name: 'Application'
685
+ }).click();
686
+ }
687
+ /* Creates a new application */ async createNewApplication(applicationName) {
688
+ if (await this.nodeExists(`Applications/${applicationName}`)) return;
689
+ await this.openRootContextMenu('Applications');
690
+ await this.page.getByRole('link', {
691
+ name: 'New'
692
+ }).click();
693
+ await this.page.getByRole('link', {
694
+ name: 'Application'
695
+ }).click();
696
+ await this.page.locator('input[name="name"]').waitFor({
697
+ state: 'visible'
698
+ });
699
+ await this.page.locator('input[name="name"]').click();
700
+ await this.page.locator('input[name="name"]').fill(applicationName);
701
+ await this.page.getByRole('button', {
702
+ name: 'Save and close'
703
+ }).click();
704
+ await this.page.locator('input[name="name"]').waitFor({
705
+ state: 'hidden',
706
+ timeout: 5000
707
+ });
708
+ await this.handleSaveToastOrAlreadyExists();
709
+ await this.waitForApplicationNode(applicationName);
710
+ }
711
+ /* Creates a new deployment package */ async createNewDeploymentPackage(applicationName, packageName) {
712
+ const appDataId = `Applications/${applicationName}`;
713
+ const packageDataId = `Applications/${applicationName}/${packageName}`;
714
+ if (await this.nodeExists(packageDataId)) return;
715
+ // Expand Applications
716
+ await this.ensureExplorerTreeReady();
717
+ await this.waitForApplicationNode(applicationName);
718
+ // Hover over app node
719
+ const appNode = this.page.locator(`span[data-id="${appDataId}"]`);
720
+ await (0, $1UEKj$playwrighttest.expect)(appNode).toBeVisible();
721
+ await appNode.scrollIntoViewIfNeeded();
722
+ await appNode.hover();
723
+ // Open context menu + create new deployment package
724
+ const contextMenu = this.page.locator(`i.context-menu-button[data-id="${appDataId}"]`);
725
+ await (0, $1UEKj$playwrighttest.expect)(contextMenu).toBeVisible();
726
+ await contextMenu.click();
727
+ await this.page.getByRole('link', {
728
+ name: 'New'
729
+ }).click();
730
+ await this.page.getByRole('link', {
731
+ name: 'Deployment Package'
732
+ }).click();
733
+ // Fill & save
734
+ const nameInput = this.page.locator('input[name="name"]');
735
+ await nameInput.fill(packageName);
736
+ await this.page.getByRole('button', {
737
+ name: 'Save and close'
738
+ }).click();
739
+ await nameInput.waitFor({
740
+ state: 'hidden',
741
+ timeout: 5000
742
+ });
743
+ await this.handleSaveToastOrAlreadyExists();
744
+ await this.waitForPackageNode(applicationName, packageName);
745
+ }
746
+ /* Creates a new command application */ async createNewCmdApplication(values) {
747
+ const appDataId = `Applications/${values[0]}`;
748
+ const packageDataId = `Applications/${values[0]}/${values[1]}`;
749
+ const commandDataId = `Applications/${values[0]}/${values[1]}/${values[2]}`;
750
+ if (await this.nodeExists(commandDataId)) return;
751
+ // Expand app node and click package
752
+ await this.ensureExplorerTreeReady();
753
+ await this.waitForApplicationNode(values[0]);
754
+ await this.waitForPackageNode(values[0], values[1]);
755
+ await this.page.locator(`div[data-id="${appDataId}"] .infinite-tree-node > a`).click();
756
+ await this.clickPackageNodeWithRetry(packageDataId);
757
+ // Open context menu
758
+ const packageMenu = this.page.locator(`i.context-menu-button[data-id="${packageDataId}"]`);
759
+ await (0, $1UEKj$playwrighttest.expect)(packageMenu).toBeVisible();
760
+ await packageMenu.click();
761
+ // Create Command application
762
+ await this.page.getByRole('link', {
763
+ name: 'New'
764
+ }).click();
765
+ await this.page.getByRole('link', {
766
+ name: 'cmd'
767
+ }).click();
768
+ await this.page.getByRole('link', {
769
+ name: 'Command'
770
+ }).click();
771
+ await this.page.locator('input[name="name"]').fill(values[2]);
772
+ await this.page.locator('textarea[name="commandLine"]').fill(values[3]);
773
+ // Some server configs default username to {{usr}} placeholder. Use evaluate to
774
+ // set value directly on DOM + fire React's change event, bypassing visibility checks.
775
+ await this.page.evaluate(()=>{
776
+ const inputs = Array.from(document.querySelectorAll('input[name="username"]'));
777
+ const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;
778
+ for (const input of inputs){
779
+ setter?.call(input, 'admin');
780
+ input.dispatchEvent(new Event('input', {
781
+ bubbles: true
782
+ }));
783
+ input.dispatchEvent(new Event('change', {
784
+ bubbles: true
785
+ }));
786
+ }
787
+ });
788
+ const saveBtn = this.page.locator('button', {
789
+ hasText: /^Save$/
790
+ }).first();
791
+ await (0, $1UEKj$playwrighttest.expect)(saveBtn).toBeEnabled();
792
+ await saveBtn.click();
793
+ await this.page.waitForTimeout(3000);
794
+ await this.handleSaveToastOrAlreadyExists();
795
+ // Close dialog
796
+ await this.page.locator('i.xl-icon.close-icon').first().click();
797
+ }
798
+ /*Deploy the application*/ async deployApp(values) {
799
+ const versionNode = `Applications/${values[0]}/${values[1]}`;
800
+ await this.ensureExplorerTreeReady();
801
+ await this.waitForPackageNode(values[0], values[1]);
802
+ await this.openPackageMenuWithRetry(versionNode);
803
+ await this.page.getByRole('link', {
804
+ name: 'Deploy',
805
+ exact: true
806
+ }).click();
807
+ await this.page.locator(`input[name="selections"][value="Environments/${values[2]}"]`).click();
808
+ await this.page.getByRole('button', {
809
+ name: 'Continue'
810
+ }).click();
811
+ // Wait for the deployment properties wizard to fully load and evaluate validation rules
812
+ await this.page.locator('.errors-wrapper').waitFor({
813
+ state: 'attached',
814
+ timeout: 15000
815
+ });
816
+ const errorIcon = this.page.locator('a.validation-icons-wrapper.error');
817
+ // Actively wait for the error icon to appear — isVisible() alone races against wizard rendering
818
+ let hasError = false;
819
+ try {
820
+ await errorIcon.waitFor({
821
+ state: 'visible',
822
+ timeout: 5000
823
+ });
824
+ hasError = true;
825
+ } catch {
826
+ // No validation errors detected
827
+ }
828
+ if (hasError) {
829
+ await this.page.getByRole('button', {
830
+ name: 'Deployment Properties'
831
+ }).click();
832
+ const usernameInput = this.page.locator('input[name="username"]');
833
+ await usernameInput.waitFor({
834
+ state: 'visible'
835
+ });
836
+ await usernameInput.fill(values[3]);
837
+ await this.page.getByRole('button', {
838
+ name: 'Save'
839
+ }).click();
840
+ await errorIcon.waitFor({
841
+ state: 'hidden',
842
+ timeout: 10000
843
+ });
844
+ }
845
+ await this.page.locator('button.deploy.xl-primary').waitFor({
846
+ state: 'visible'
847
+ });
848
+ await this.page.locator('button.deploy.xl-primary').click();
849
+ //Get the task id
850
+ const taskIdLocator = this.page.locator('div.task-id').first();
851
+ await (0, $1UEKj$playwrighttest.expect)(taskIdLocator).toBeVisible({
852
+ timeout: 60000
853
+ });
854
+ await (0, $1UEKj$playwrighttest.expect)(taskIdLocator).not.toHaveText('', {
855
+ timeout: 60000
856
+ });
857
+ this.taskId = (await taskIdLocator.textContent() ?? '').trim();
858
+ //Verify no failure and all are done
859
+ const labels = this.page.locator('span.label');
860
+ await (0, $1UEKj$playwrighttest.expect)(labels).toHaveCount(3);
861
+ await (0, $1UEKj$playwrighttest.expect)(labels).toHaveText([
862
+ 'EXECUTED',
863
+ 'DONE',
864
+ 'DONE'
865
+ ]);
866
+ //Get the text executed
867
+ await this.page.locator('div[data-id="0_1_1"] .infinite-tree-node > a').click();
868
+ await this.page.getByTitle('Execute Test Command').click();
869
+ this.executedText = await this.page.locator('div.step-log-body pre').nth(1).innerText();
870
+ //Verify in monitoring tab searching with the task id
871
+ await this.page.locator('div[data-id="TASK_MONITOR"] .infinite-tree-node > a').click();
872
+ await this.page.locator('span[data-id="DEPLOYMENT_TASKS"]').dblclick();
873
+ await this.page.waitForTimeout(5000);
874
+ await this.page.locator('div.deployment-task-filter-task-id input[placeholder="Search"]').first().fill(this.taskId);
875
+ const stateText = await this.page.locator('td.tasks-table-state').first().innerText();
876
+ (0, $1UEKj$playwrighttest.expect)(stateText.trim()).toBe('Executed');
877
+ //Click the row to go to deployment screen
878
+ await this.page.locator('table.table.table-striped tbody tr').first().dblclick();
879
+ await this.page.locator('button.finish.xl-primary').click();
880
+ }
881
+ /* Deploys or redeploys the application in bulk — exact copy of deployApp flow with forceRedeploy */ async deployAppBulk(values) {
882
+ const versionNode = `Applications/${values[0]}/${values[1]}`;
883
+ await this.ensureExplorerTreeReady();
884
+ await this.waitForPackageNode(values[0], values[1]);
885
+ await this.openPackageMenuWithRetry(versionNode);
886
+ await this.page.getByRole('link', {
887
+ name: 'Deploy',
888
+ exact: true
889
+ }).click();
890
+ await this.page.locator(`input[name="selections"][value="Environments/${values[2]}"]`).click();
891
+ await this.page.getByRole('button', {
892
+ name: 'Continue'
893
+ }).click();
894
+ // Wait for the deployment properties wizard to fully load and evaluate validation rules
895
+ await this.page.locator('.errors-wrapper').waitFor({
896
+ state: 'attached',
897
+ timeout: 15000
898
+ });
899
+ const errorIcon = this.page.locator('a.validation-icons-wrapper.error');
900
+ let hasError = false;
901
+ try {
902
+ await errorIcon.waitFor({
903
+ state: 'visible',
904
+ timeout: 5000
905
+ });
906
+ hasError = true;
907
+ } catch {
908
+ // No validation errors detected
909
+ }
910
+ // Always open Deployment Properties to set forceRedeploy=true
911
+ await this.page.getByRole('button', {
912
+ name: 'Deployment Properties'
913
+ }).click();
914
+ const usernameInput = this.page.locator('input[name="username"]');
915
+ if (hasError) {
916
+ await usernameInput.waitFor({
917
+ state: 'visible'
918
+ });
919
+ await usernameInput.fill(values[3]);
920
+ }
921
+ const forceRedeployInput = this.page.locator('input[name="forceRedeploy"]');
922
+ await forceRedeployInput.waitFor({
923
+ state: 'visible',
924
+ timeout: 10000
925
+ });
926
+ if (!await forceRedeployInput.isChecked()) await forceRedeployInput.check();
927
+ await this.page.getByRole('button', {
928
+ name: 'Save'
929
+ }).click();
930
+ if (hasError) await errorIcon.waitFor({
931
+ state: 'hidden',
932
+ timeout: 10000
933
+ });
934
+ await this.page.locator('button.deploy.xl-primary').waitFor({
935
+ state: 'visible'
936
+ });
937
+ await this.page.locator('button.deploy.xl-primary').click();
938
+ const labels = this.page.locator('span.label');
939
+ await (0, $1UEKj$playwrighttest.expect)(labels).toHaveCount(3, {
940
+ timeout: 120000
941
+ });
942
+ await (0, $1UEKj$playwrighttest.expect)(labels).toHaveText([
943
+ 'EXECUTED',
944
+ 'DONE',
945
+ 'DONE'
946
+ ], {
947
+ timeout: 120000
948
+ });
949
+ const finishButton = this.page.locator('button.finish.xl-primary');
950
+ if (await finishButton.isVisible()) await finishButton.click();
951
+ await this.page.locator('div.xl-react-widget-modal-dialog.in').waitFor({
952
+ state: 'hidden',
953
+ timeout: 30000
954
+ });
955
+ }
956
+ /* Redeploy the same package repeatedly — delegates to deployAppBulk (forceRedeploy already set).
957
+ async redeployAppBulk(values: string[]): Promise<void> {
958
+ await this.deployAppBulk(values);
959
+ }*/ async verifyApplicationIsVisibleToTheUser(applicationName) {
960
+ //await this.navigation.homePage.clickExplorer();
961
+ await this.assertTreeNodeVisibility('Applications', applicationName, {
962
+ shouldBeVisible: true
963
+ });
964
+ }
965
+ async verifyCiNodesVisibility(nodes, shouldBeVisible, page = this.page) {
966
+ const { applicationName: applicationName, environmentName: environmentName, infrastructureName: infrastructureName } = nodes;
967
+ await page.locator('button.dot-avatar').waitFor({
968
+ state: 'visible',
969
+ timeout: 30000
970
+ });
971
+ const currentUrl = page.url();
972
+ if (!/#\/explorer/.test(currentUrl)) await page.goto('/#/explorer');
973
+ await page.waitForURL(/#\/explorer/);
974
+ await page.locator('div[data-id="Applications"]').waitFor({
975
+ state: 'visible',
976
+ timeout: 15000
977
+ });
978
+ await this.assertTreeNodeVisibility('Applications', applicationName, {
979
+ shouldBeVisible: shouldBeVisible,
980
+ page: page
981
+ });
982
+ await this.assertTreeNodeVisibility('Environments', environmentName, {
983
+ shouldBeVisible: shouldBeVisible,
984
+ page: page
985
+ });
986
+ await this.assertTreeNodeVisibility('Infrastructure', infrastructureName, {
987
+ shouldBeVisible: shouldBeVisible,
988
+ page: page
989
+ });
990
+ }
991
+ async assertTreeNodeVisibility(rootName, childName, { page: page = this.page, shouldBeVisible: shouldBeVisible }) {
992
+ const rootToggle = page.locator(`div[data-id="${rootName}"] .infinite-tree-node > a`);
993
+ await (0, $1UEKj$playwrighttest.expect)(rootToggle).toBeVisible();
994
+ await rootToggle.click();
995
+ await page.locator(`div[data-id="${rootName}"]`).waitFor({
996
+ state: 'visible'
997
+ });
998
+ await page.waitForTimeout(1500);
999
+ const childNode = page.locator(`span[data-id="${rootName}/${childName}"]`);
1000
+ if (shouldBeVisible) {
1001
+ if (!await childNode.isVisible()) {
1002
+ await rootToggle.click();
1003
+ await page.waitForTimeout(1000);
1004
+ }
1005
+ await (0, $1UEKj$playwrighttest.expect)(childNode).toBeVisible();
1006
+ } else await (0, $1UEKj$playwrighttest.expect)(childNode).toHaveCount(0);
1007
+ }
1008
+ async refreshExplorerTree() {
1009
+ const refreshButton = this.page.getByRole('button', {
1010
+ name: 'Refresh'
1011
+ }).first();
1012
+ if (await refreshButton.isVisible()) {
1013
+ await refreshButton.click();
1014
+ await this.page.waitForLoadState('networkidle');
1015
+ }
1016
+ }
1017
+ async nodeExists(dataId) {
1018
+ await this.ensureExplorerTreeReady();
1019
+ const node = this.page.locator(`span[data-id="${dataId}"]`);
1020
+ if (await node.count() > 0) return true;
1021
+ await this.refreshExplorerTree();
1022
+ return await node.count() > 0;
1023
+ }
1024
+ async waitForApplicationNode(applicationName) {
1025
+ const appDataId = `Applications/${applicationName}`;
1026
+ const appNode = this.page.locator(`span[data-id="${appDataId}"]`);
1027
+ const rootToggle = this.page.locator('div[data-id="Applications"] .infinite-tree-node > a');
1028
+ await this.ensureExplorerTreeReady();
1029
+ await rootToggle.waitFor({
1030
+ state: 'visible'
1031
+ });
1032
+ for(let attempt = 0; attempt < 3; attempt += 1){
1033
+ if (await appNode.count() === 0 || !await appNode.isVisible()) {
1034
+ await rootToggle.click();
1035
+ await this.page.waitForTimeout(500);
1036
+ }
1037
+ try {
1038
+ await (0, $1UEKj$playwrighttest.expect)(appNode).toBeVisible({
1039
+ timeout: 5000
1040
+ });
1041
+ return;
1042
+ } catch {
1043
+ await this.refreshExplorerTree();
1044
+ }
1045
+ }
1046
+ await (0, $1UEKj$playwrighttest.expect)(appNode).toBeVisible({
1047
+ timeout: 15000
1048
+ });
1049
+ }
1050
+ async waitForPackageNode(applicationName, packageName) {
1051
+ const packageDataId = `Applications/${applicationName}/${packageName}`;
1052
+ const packageNode = this.page.locator(`span[data-id="${packageDataId}"]`);
1053
+ const appRow = this.page.locator(`div[data-id="Applications/${applicationName}"]`);
1054
+ const appToggle = appRow.locator('.infinite-tree-node > a');
1055
+ // Detects whether the app is already expanded (has any visible child span)
1056
+ const anyChildNode = this.page.locator(`span[data-id^="Applications/${applicationName}/"]`).first();
1057
+ await this.waitForApplicationNode(applicationName);
1058
+ await appRow.scrollIntoViewIfNeeded();
1059
+ await appToggle.waitFor({
1060
+ state: 'visible'
1061
+ });
1062
+ for(let attempt = 0; attempt < 5; attempt += 1){
1063
+ // Only click the toggle if the node is currently collapsed — prevents accidental collapse
1064
+ if (!await anyChildNode.isVisible()) {
1065
+ await appToggle.scrollIntoViewIfNeeded();
1066
+ await appToggle.click({
1067
+ force: true
1068
+ });
1069
+ await this.page.waitForTimeout(500);
1070
+ }
1071
+ try {
1072
+ await (0, $1UEKj$playwrighttest.expect)(packageNode).toBeVisible({
1073
+ timeout: 5000
1074
+ });
1075
+ return;
1076
+ } catch {
1077
+ // Package still not visible — refresh tree and re-expand on next attempt
1078
+ await this.refreshExplorerTree();
1079
+ await this.waitForApplicationNode(applicationName);
1080
+ await appToggle.waitFor({
1081
+ state: 'visible'
1082
+ });
1083
+ }
1084
+ }
1085
+ await (0, $1UEKj$playwrighttest.expect)(packageNode).toBeVisible({
1086
+ timeout: 15000
1087
+ });
1088
+ }
1089
+ async clickPackageNodeWithRetry(packageDataId) {
1090
+ const appName = packageDataId.split('/')[1];
1091
+ for(let attempt = 0; attempt < 3; attempt += 1){
1092
+ const appRow = this.page.locator(`div[data-id="Applications/${appName}"]`);
1093
+ const appToggle = appRow.locator('.infinite-tree-node > a');
1094
+ try {
1095
+ await appToggle.scrollIntoViewIfNeeded();
1096
+ } catch {
1097
+ // Tree can re-render between retries; re-query next loop.
1098
+ }
1099
+ await appToggle.click({
1100
+ force: true
1101
+ });
1102
+ const packageNode = this.page.locator(`span[data-id="${packageDataId}"]`).first();
1103
+ await (0, $1UEKj$playwrighttest.expect)(packageNode).toBeVisible({
1104
+ timeout: 5000
1105
+ });
1106
+ try {
1107
+ await packageNode.click();
1108
+ return;
1109
+ } catch {
1110
+ await this.page.waitForTimeout(300);
1111
+ }
1112
+ }
1113
+ await this.page.locator(`span[data-id="${packageDataId}"]`).first().click();
1114
+ }
1115
+ async openPackageMenuWithRetry(versionDataId) {
1116
+ const appName = versionDataId.split('/')[1];
1117
+ const appToggle = this.page.locator(`div[data-id="Applications/${appName}"] .infinite-tree-node > a`);
1118
+ // The context-menu button is CSS-hidden until the tree row is hovered.
1119
+ // Hover the node row first so the button becomes visible, then click it.
1120
+ const nodeRow = this.page.locator(`div[data-id="${versionDataId}"] .infinite-tree-node`);
1121
+ for(let attempt = 0; attempt < 3; attempt += 1){
1122
+ const menuButton = this.page.locator(`i.context-menu-button[data-id="${versionDataId}"]`).first();
1123
+ try {
1124
+ await nodeRow.hover({
1125
+ timeout: 5000
1126
+ });
1127
+ await (0, $1UEKj$playwrighttest.expect)(menuButton).toBeVisible({
1128
+ timeout: 5000
1129
+ });
1130
+ await menuButton.click();
1131
+ return;
1132
+ } catch {
1133
+ await appToggle.click();
1134
+ await this.page.waitForTimeout(300);
1135
+ }
1136
+ }
1137
+ await this.page.locator(`i.context-menu-button[data-id="${versionDataId}"]`).first().click({
1138
+ force: true
1139
+ });
1140
+ }
1141
+ }
1142
+
1143
+
1144
+
1145
+
1146
+ class $b2f725a937c99e2d$export$b8a443164510a1b4 extends (0, $ae23b16684112d0b$export$2b65d1d97338f32b) {
1147
+ async openTab(tabLabel, paneId) {
1148
+ const tab = this.page.locator('.app-root .dot-sidebar .dot-list p').filter({
1149
+ hasText: tabLabel
1150
+ }).first();
1151
+ await tab.scrollIntoViewIfNeeded();
1152
+ await tab.click();
1153
+ await this.page.locator(`#pane-${paneId}`).waitFor({
1154
+ state: 'visible',
1155
+ timeout: 20000
1156
+ });
1157
+ }
1158
+ async downloadReport(buttonLabel, paneId, selector) {
1159
+ const pane = this.page.locator(`#pane-${paneId}`);
1160
+ const exportButton = selector ? pane.locator(selector) : pane.getByRole('button', {
1161
+ name: buttonLabel
1162
+ });
1163
+ const fallbackButton = pane.locator('button:has-text("Export")');
1164
+ const downloadPromise = this.page.waitForEvent('download', {
1165
+ timeout: 20000
1166
+ });
1167
+ if (await exportButton.isVisible()) await exportButton.click();
1168
+ else await fallbackButton.first().click();
1169
+ return downloadPromise;
1170
+ }
1171
+ async openDashboardTab() {
1172
+ await this.openTab('Dashboard', 'DashboardTab');
1173
+ }
1174
+ async openDeploymentsTab() {
1175
+ await this.openTab('Deployments', 'DeploymentsTab');
1176
+ }
1177
+ async openControlTasksTab() {
1178
+ await this.openTab('Control tasks', 'ControlTasksTab');
1179
+ }
1180
+ async openAuditReportTab() {
1181
+ await this.openTab('Audit report', 'AuditReportTab');
1182
+ }
1183
+ async downloadDeploymentsExport() {
1184
+ return this.downloadReport('Export report', 'DeploymentsTab');
1185
+ }
1186
+ async downloadControlTasksExport() {
1187
+ return this.downloadReport('Export report', 'ControlTasksTab');
1188
+ }
1189
+ async downloadAuditReportExport() {
1190
+ return this.downloadReport('Export report', 'AuditReportTab');
1191
+ }
1192
+ /* Clicks on the Deployment Tasks section */ async clickDeploymentTasks() {
1193
+ await this.page.locator('p.MuiTypography-root', {
1194
+ hasText: 'Deployments'
1195
+ }).click();
1196
+ }
1197
+ async verifyTheDeploymentValues(values) {
1198
+ await this.page.locator('div.deployment-task-filter-task-id input[placeholder="Search"]:visible').first().fill(values[0]);
1199
+ const escapedApp = values[2].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1200
+ const escapedPkg = values[3].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1201
+ const packagePattern = new RegExp(`${escapedApp}/${escapedPkg}`, 'i');
1202
+ const row = this.page.locator('table.table-striped tbody tr').filter({
1203
+ has: this.page.locator('td.report-table-package .deployment-report-package', {
1204
+ hasText: packagePattern
1205
+ })
1206
+ }).first();
1207
+ await row.waitFor({
1208
+ state: 'visible',
1209
+ timeout: 5000
1210
+ });
1211
+ const packageCell = row.locator('td.report-table-package .deployment-report-package').first();
1212
+ await (0, $1UEKj$playwrighttest.expect)(packageCell).toContainText(packagePattern);
1213
+ await (0, $1UEKj$playwrighttest.expect)(row.locator('td.report-table-originalType .deployment-report-task-type')).toHaveText(/Initial/i);
1214
+ await (0, $1UEKj$playwrighttest.expect)(row.locator('td.report-table-user .deployment-report-user')).toHaveText('admin');
1215
+ //Click the row
1216
+ await packageCell.dblclick();
1217
+ await this.page.locator('div[data-id="0_1_1"] .infinite-tree-node > a').click();
1218
+ await this.page.getByTitle('Execute Test Command').click();
1219
+ //Get the log text
1220
+ const text = await this.page.locator('div.step-log-body pre').nth(1).innerText();
1221
+ //Verify both the log text are same
1222
+ (0, $1UEKj$playwrighttest.expect)(text).toContain(values[1]);
1223
+ }
1224
+ }
1225
+
1226
+
1227
+
1228
+
1229
+ class $1b52322f0063c4bc$export$5c1198389e139c11 extends (0, $ae23b16684112d0b$export$2b65d1d97338f32b) {
1230
+ getPane() {
1231
+ return this.page.locator('#pane-ControlTasksTab');
1232
+ }
1233
+ getPagination() {
1234
+ return this.getPane().locator('.react-bootstrap-table-pagination');
1235
+ }
1236
+ getTableBody() {
1237
+ return this.getPane().locator('.react-bootstrap-table tbody');
1238
+ }
1239
+ getLastUpdated() {
1240
+ return this.getPane().locator('.last-updated-component');
1241
+ }
1242
+ // ── Navigation ───────────────────────────────────────────────────────────
1243
+ async verifySelected() {
1244
+ const pane = this.getPane();
1245
+ await (0, $1UEKj$playwrighttest.expect)(pane).toBeVisible();
1246
+ await (0, $1UEKj$playwrighttest.expect)(this.page.locator('.dot-sidebar a[href*="controltasks"], .dot-sidebar .dot-list p', {
1247
+ hasText: 'Control tasks'
1248
+ }).first()).toBeVisible();
1249
+ }
1250
+ // ── Filters ───────────────────────────────────────────────────────────────
1251
+ async verifyFiltersVisible() {
1252
+ const pane = this.getPane();
1253
+ const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
1254
+ await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
1255
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.control-task-filter-targets .react-tagsinput-input')).toBeVisible();
1256
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.control-task-filter-task-name input[placeholder="Search"]')).toBeVisible();
1257
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.control-task-id input[placeholder="Search"]')).toBeVisible();
1258
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.control-task-filter-start-date')).toBeVisible();
1259
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.control-task-filter-end-date')).toBeVisible();
1260
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.control-task-filter-worker input[placeholder="Search"]')).toBeVisible();
1261
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.control-task-filter-state .react-tagsinput-input')).toBeVisible();
1262
+ }
1263
+ async hideFilters() {
1264
+ const pane = this.getPane();
1265
+ const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
1266
+ const hideFiltersButton = pane.getByRole('button', {
1267
+ name: 'Hide filters'
1268
+ });
1269
+ await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
1270
+ await hideFiltersButton.click();
1271
+ await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeHidden();
1272
+ }
1273
+ async showFilters() {
1274
+ const pane = this.getPane();
1275
+ const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
1276
+ const showFiltersButton = pane.getByRole('button', {
1277
+ name: 'Show filters'
1278
+ });
1279
+ if (await filtersPanel.isVisible()) return;
1280
+ for(let attempt = 0; attempt < 3; attempt += 1){
1281
+ if (await showFiltersButton.isVisible()) await showFiltersButton.click({
1282
+ force: true
1283
+ });
1284
+ if (await filtersPanel.isVisible()) return;
1285
+ await this.page.waitForTimeout(300);
1286
+ }
1287
+ await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
1288
+ }
1289
+ async ensureFiltersVisible() {
1290
+ const pane = this.getPane();
1291
+ const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
1292
+ if (await filtersPanel.isVisible()) return;
1293
+ const showFiltersButton = pane.getByRole('button', {
1294
+ name: 'Show filters'
1295
+ });
1296
+ if (await showFiltersButton.isVisible()) await showFiltersButton.click({
1297
+ force: true
1298
+ });
1299
+ await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
1300
+ }
1301
+ // ── Date filters ─────────────────────────────────────────────────────────
1302
+ async setStartDate(value) {
1303
+ const field = this.getPane().locator('.control-task-filter-start-date');
1304
+ await field.click();
1305
+ await field.fill(value);
1306
+ await this.getLastUpdated().click({
1307
+ force: true
1308
+ });
1309
+ }
1310
+ async setEndDate(value) {
1311
+ const field = this.getPane().locator('.control-task-filter-end-date');
1312
+ await field.click();
1313
+ await field.fill(value);
1314
+ await this.getLastUpdated().click({
1315
+ force: true
1316
+ });
1317
+ }
1318
+ async expectStartDateValue(value) {
1319
+ await (0, $1UEKj$playwrighttest.expect)(this.getPane().locator('.control-task-filter-start-date')).toHaveValue(value);
1320
+ }
1321
+ async expectEndDateValue(value) {
1322
+ await (0, $1UEKj$playwrighttest.expect)(this.getPane().locator('.control-task-filter-end-date')).toHaveValue(value);
1323
+ }
1324
+ async checkInvalidStartDateError() {
1325
+ const field = this.getPane().locator('.control-task-filter-start-date');
1326
+ await field.click();
1327
+ await field.press('End');
1328
+ await field.press('a');
1329
+ await this.page.keyboard.press('Escape');
1330
+ await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018').first()).toBeVisible();
1331
+ }
1332
+ async checkInvalidEndDateError() {
1333
+ const field = this.getPane().locator('.control-task-filter-end-date');
1334
+ await field.click();
1335
+ await field.press('End');
1336
+ await field.press('b');
1337
+ await this.page.keyboard.press('Escape');
1338
+ await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018').first()).toBeVisible();
1339
+ }
1340
+ async expectNoStartDateError() {
1341
+ await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018')).toHaveCount(0);
1342
+ }
1343
+ async expectNoEndDateError() {
1344
+ await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018')).toHaveCount(0);
1345
+ }
1346
+ // ── Tag filters ───────────────────────────────────────────────────────────
1347
+ async selectTargetsTagsFilter(text, optionName) {
1348
+ await this.selectTagsFilterFirstSuggestion('.control-task-filter-targets .react-tagsinput', text, optionName);
1349
+ }
1350
+ async clearTargetsTagsFirstTag() {
1351
+ const remove = this.getPane().locator('.control-task-filter-targets .react-tagsinput-tag:nth-child(1) .react-tagsinput-remove');
1352
+ if (await remove.count()) await remove.click();
1353
+ }
1354
+ async selectStatesTagsFilter(text, optionName) {
1355
+ await this.selectTagsFilterFirstSuggestion('.control-task-filter-state .react-tagsinput', text, optionName);
1356
+ }
1357
+ async clearStatesTagsFirstTag() {
1358
+ const remove = this.getPane().locator('.control-task-filter-state .react-tagsinput-tag:nth-child(1) .react-tagsinput-remove');
1359
+ if (await remove.count()) await remove.click();
1360
+ }
1361
+ // ── Text filters ──────────────────────────────────────────────────────────
1362
+ async filterByTaskName(name) {
1363
+ await this.getPane().locator('.control-task-filter-task-name input[placeholder="Search"]').fill(name);
1364
+ }
1365
+ async clearTaskName() {
1366
+ await this.filterByTaskName('');
1367
+ }
1368
+ async filterByTaskId(id) {
1369
+ await this.getPane().locator('.control-task-id input[placeholder="Search"]').fill(id);
1370
+ }
1371
+ async clearTaskId() {
1372
+ await this.filterByTaskId('');
1373
+ }
1374
+ async filterByWorker(worker) {
1375
+ await this.getPane().locator('.control-task-filter-worker input[placeholder="Search"]').fill(worker);
1376
+ }
1377
+ async clearWorkerFilter() {
1378
+ await this.filterByWorker('');
1379
+ }
1380
+ // ── Tasks toggle ──────────────────────────────────────────────────────────
1381
+ async clickMyTasks() {
1382
+ await this.getPane().getByRole('button', {
1383
+ name: 'My tasks'
1384
+ }).click();
1385
+ }
1386
+ async clickAllTasks() {
1387
+ await this.getPane().getByRole('button', {
1388
+ name: 'All tasks'
1389
+ }).click();
1390
+ }
1391
+ // ── Toolbar ───────────────────────────────────────────────────────────────
1392
+ async clickRefresh() {
1393
+ await this.getPane().getByRole('button', {
1394
+ name: 'Refresh'
1395
+ }).click();
1396
+ }
1397
+ async verifyLastUpdatedAndRefresh() {
1398
+ const lastUpdated = this.getLastUpdated();
1399
+ await (0, $1UEKj$playwrighttest.expect)(lastUpdated).toBeVisible();
1400
+ await (0, $1UEKj$playwrighttest.expect)(lastUpdated).not.toHaveText('');
1401
+ await this.getPane().getByRole('button', {
1402
+ name: 'Refresh'
1403
+ }).click();
1404
+ await (0, $1UEKj$playwrighttest.expect)(lastUpdated).toBeVisible();
1405
+ }
1406
+ // ── Table assertions ──────────────────────────────────────────────────────
1407
+ async verifyTableHasRowsOrEmpty() {
1408
+ const pane = this.getPane();
1409
+ const table = pane.locator('table.table-striped');
1410
+ await (0, $1UEKj$playwrighttest.expect)(table).toBeVisible();
1411
+ const rows = this.getTableBody().locator('tr');
1412
+ const noDataCell = this.getTableBody().locator('td.react-bs-table-no-data');
1413
+ const emptyState = pane.getByText(/No report data available/i);
1414
+ await (0, $1UEKj$playwrighttest.expect)(async ()=>{
1415
+ if (await rows.count() > 0) return true;
1416
+ if (await noDataCell.count() > 0 && await noDataCell.first().isVisible()) return true;
1417
+ if (await emptyState.count() > 0 && await emptyState.first().isVisible()) return true;
1418
+ return false;
1419
+ }).toBeTruthy();
1420
+ }
1421
+ async verifyTableColumns() {
1422
+ const headers = this.getPane().locator('.react-bootstrap-table thead th');
1423
+ await (0, $1UEKj$playwrighttest.expect)(headers.filter({
1424
+ hasText: 'Control task name'
1425
+ })).toBeVisible();
1426
+ await (0, $1UEKj$playwrighttest.expect)(headers.filter({
1427
+ hasText: 'Target CI'
1428
+ })).toBeVisible();
1429
+ await (0, $1UEKj$playwrighttest.expect)(headers.filter({
1430
+ hasText: 'Description'
1431
+ })).toBeVisible();
1432
+ await (0, $1UEKj$playwrighttest.expect)(headers.filter({
1433
+ hasText: 'User'
1434
+ })).toBeVisible();
1435
+ await (0, $1UEKj$playwrighttest.expect)(headers.filter({
1436
+ hasText: 'State'
1437
+ })).toBeVisible();
1438
+ await (0, $1UEKj$playwrighttest.expect)(headers.filter({
1439
+ hasText: 'Start date'
1440
+ })).toBeVisible();
1441
+ await (0, $1UEKj$playwrighttest.expect)(headers.filter({
1442
+ hasText: 'End date'
1443
+ })).toBeVisible();
1444
+ await (0, $1UEKj$playwrighttest.expect)(headers.filter({
1445
+ hasText: 'Worker'
1446
+ })).toBeVisible();
1447
+ }
1448
+ async waitForTableReady() {
1449
+ await (0, $1UEKj$playwrighttest.expect)(async ()=>{
1450
+ const loading = await this.getPane().getByText('Data loading').count();
1451
+ (0, $1UEKj$playwrighttest.expect)(loading).toBe(0);
1452
+ const table = this.getPane().locator('table.table-striped');
1453
+ (0, $1UEKj$playwrighttest.expect)(await table.count()).toBeGreaterThan(0);
1454
+ }).toPass({
1455
+ timeout: 20000
1456
+ });
1457
+ }
1458
+ async expectNumberOfRows(count) {
1459
+ await this.waitForTableReady();
1460
+ await (0, $1UEKj$playwrighttest.expect)(this.getTableBody().locator('tr')).toHaveCount(count);
1461
+ }
1462
+ async expectAtLeastNumberOfRows(count) {
1463
+ await this.waitForTableReady();
1464
+ await (0, $1UEKj$playwrighttest.expect)(async ()=>{
1465
+ const total = await this.getTableBody().locator('tr').count();
1466
+ (0, $1UEKj$playwrighttest.expect)(total).toBeGreaterThanOrEqual(count);
1467
+ }).toPass({
1468
+ timeout: 30000
1469
+ });
1470
+ }
1471
+ async expectTotal(total) {
1472
+ await (0, $1UEKj$playwrighttest.expect)(this.getPagination().locator('> div').filter({
1473
+ hasText: 'Total:'
1474
+ })).toContainText(`Total: ${total}`);
1475
+ }
1476
+ async expectAtLeastTotal(count) {
1477
+ await this.waitForTableReady();
1478
+ await (0, $1UEKj$playwrighttest.expect)(async ()=>{
1479
+ const text = await this.getPagination().locator('> div').filter({
1480
+ hasText: 'Total:'
1481
+ }).textContent();
1482
+ const match = text?.match(/Total:\s*(\d+)/);
1483
+ const total = match ? Number(match[1]) : 0;
1484
+ (0, $1UEKj$playwrighttest.expect)(total).toBeGreaterThanOrEqual(count);
1485
+ }).toPass();
1486
+ }
1487
+ async getTotalCount() {
1488
+ await this.waitForTableReady();
1489
+ const text = await this.getPagination().locator('> div').filter({
1490
+ hasText: 'Total:'
1491
+ }).textContent();
1492
+ const match = text?.match(/Total:\s*(\d+)/);
1493
+ return match ? Number(match[1]) : 0;
1494
+ }
1495
+ async expectValue(row, column, expected) {
1496
+ const cell = this.getTableBody().locator(`tr:nth-child(${row}) td.report-table-${column}`);
1497
+ await (0, $1UEKj$playwrighttest.expect)(cell).toHaveText(expected);
1498
+ }
1499
+ async expectValueContains(row, column, expected) {
1500
+ const cell = this.getTableBody().locator(`tr:nth-child(${row}) td.report-table-${column}`);
1501
+ await (0, $1UEKj$playwrighttest.expect)(cell).toContainText(expected);
1502
+ }
1503
+ async shouldHaveScrollBar() {
1504
+ await this.waitForTableReady();
1505
+ const container = this.getPane().locator('div.report');
1506
+ const overflowX = await container.evaluate((el)=>window.getComputedStyle(el).overflowX);
1507
+ (0, $1UEKj$playwrighttest.expect)([
1508
+ 'auto',
1509
+ 'scroll'
1510
+ ]).toContain(overflowX);
1511
+ }
1512
+ // ── Sorting ───────────────────────────────────────────────────────────────
1513
+ async sortBy(column) {
1514
+ await this.getPane().locator(`.react-bootstrap-table thead th.report-table-${column}`).click();
1515
+ }
1516
+ async verifyIsSorted(column) {
1517
+ const sortOrder = this.getPane().locator(`.react-bootstrap-table thead th.report-table-${column} .react-bootstrap-table-sort-order`);
1518
+ await (0, $1UEKj$playwrighttest.expect)(sortOrder).toBeVisible();
1519
+ }
1520
+ async getColumnValues(column) {
1521
+ await this.waitForTableReady();
1522
+ const cells = this.getTableBody().locator(`tr td.report-table-${column}`);
1523
+ const count = await cells.count();
1524
+ const values = [];
1525
+ for(let i = 0; i < count; i += 1)values.push((await cells.nth(i).textContent())?.trim() ?? '');
1526
+ return values;
1527
+ }
1528
+ // ── Pagination ────────────────────────────────────────────────────────────
1529
+ async getLastPageNumber() {
1530
+ await this.waitForTableReady();
1531
+ const links = this.getPagination().getByRole('list').getByRole('link');
1532
+ const count = await links.count();
1533
+ if (count < 2) return 1;
1534
+ const text = await links.nth(count - 2).textContent();
1535
+ const num = Number(text?.trim());
1536
+ return Number.isFinite(num) && num > 0 ? num : 1;
1537
+ }
1538
+ async goToPage(page) {
1539
+ await this.getPagination().getByRole('link', {
1540
+ name: page.toString(),
1541
+ exact: true
1542
+ }).click();
1543
+ }
1544
+ async goToFirstPage() {
1545
+ const firstLink = this.getPagination().getByRole('link', {
1546
+ name: '<<'
1547
+ });
1548
+ if (await firstLink.count()) await firstLink.click();
1549
+ else await this.getPagination().getByRole('link', {
1550
+ name: '1',
1551
+ exact: true
1552
+ }).click();
1553
+ }
1554
+ async goToLastPage() {
1555
+ const lastLink = this.getPagination().getByRole('link', {
1556
+ name: '>>'
1557
+ });
1558
+ if (await lastLink.count()) await lastLink.click();
1559
+ else {
1560
+ const links = this.getPagination().getByRole('list').getByRole('link');
1561
+ const count = await links.count();
1562
+ await links.nth(count - 2).click();
1563
+ }
1564
+ }
1565
+ async goToPreviousPage() {
1566
+ await this.getPagination().getByRole('link', {
1567
+ name: '<',
1568
+ exact: true
1569
+ }).click();
1570
+ }
1571
+ async goToNextPage() {
1572
+ await this.getPagination().getByRole('link', {
1573
+ name: '>',
1574
+ exact: true
1575
+ }).click();
1576
+ }
1577
+ async expectPage(page) {
1578
+ await (0, $1UEKj$playwrighttest.expect)(async ()=>{
1579
+ const pagination = this.getPagination();
1580
+ const activeLink = pagination.locator('li.active').getByRole('link', {
1581
+ name: page.toString(),
1582
+ exact: true
1583
+ });
1584
+ if (await activeLink.count() > 0) return;
1585
+ const ariaCurrent = pagination.locator('[aria-current="page"]').filter({
1586
+ hasText: page.toString()
1587
+ });
1588
+ if (await ariaCurrent.count() > 0) return;
1589
+ // When there is only 1 page, the pagination may not render page links at all.
1590
+ // In that case page 1 is implicitly active.
1591
+ if (page === 1) {
1592
+ const nextLink = pagination.getByRole('link', {
1593
+ name: '>',
1594
+ exact: true
1595
+ });
1596
+ const pageLinks = pagination.getByRole('link').filter({
1597
+ hasText: /^\d+$/
1598
+ });
1599
+ if (await nextLink.count() === 0 && await pageLinks.count() === 0) return;
1600
+ }
1601
+ throw new Error(`Page ${page} not active`);
1602
+ }).toPass({
1603
+ timeout: 30000
1604
+ });
1605
+ }
1606
+ async expectPageLinkVisible(pageNumber) {
1607
+ await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('link', {
1608
+ name: pageNumber.toString(),
1609
+ exact: true
1610
+ })).toBeVisible();
1611
+ }
1612
+ async expectPaginationLinksVisible() {
1613
+ await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('link', {
1614
+ name: '>',
1615
+ exact: true
1616
+ })).toBeVisible();
1617
+ }
1618
+ async expectPaginationLinksHidden() {
1619
+ await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('link', {
1620
+ name: '>',
1621
+ exact: true
1622
+ })).toHaveCount(0);
1623
+ }
1624
+ async setPageSize(size) {
1625
+ await this.openPageSizeMenu();
1626
+ await this.getPagination().getByRole('menu').getByRole('menuitem', {
1627
+ name: size.toString(),
1628
+ exact: true
1629
+ }).click();
1630
+ await this.waitForTableReady();
1631
+ }
1632
+ async expectPageSize(size) {
1633
+ await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('button', {
1634
+ name: size.toString(),
1635
+ exact: true
1636
+ })).toBeVisible();
1637
+ }
1638
+ async expectPageSizes(sizes) {
1639
+ await this.openPageSizeMenu();
1640
+ const menu = this.getPagination().getByRole('menu');
1641
+ await (0, $1UEKj$playwrighttest.expect)(menu.getByRole('menuitem')).toHaveCount(sizes.length);
1642
+ for (const size of sizes)await (0, $1UEKj$playwrighttest.expect)(menu.getByRole('menuitem', {
1643
+ name: size.toString(),
1644
+ exact: true
1645
+ })).toBeVisible();
1646
+ await this.closePageSizeMenu();
1647
+ }
1648
+ // ── Private helpers ───────────────────────────────────────────────────────
1649
+ async openPageSizeMenu() {
1650
+ const toggle = this.getPagination().locator('.dropdown-toggle');
1651
+ await toggle.click();
1652
+ await this.getPagination().getByRole('menu').waitFor({
1653
+ state: 'visible'
1654
+ });
1655
+ }
1656
+ async closePageSizeMenu() {
1657
+ const menu = this.getPagination().getByRole('menu');
1658
+ if (await menu.isVisible()) await this.getPagination().locator('.dropdown-toggle').click();
1659
+ }
1660
+ async expectTagAdded(tagTitle) {
1661
+ const tags = this.getPane().locator('.xl-react-widget-tags .react-tagsinput-tag');
1662
+ const byTitle = tags.locator(`[title='${tagTitle}']`);
1663
+ const byContainsTitle = tags.filter({
1664
+ hasText: tagTitle.split('/').pop() ?? tagTitle
1665
+ });
1666
+ if (await byTitle.count() > 0) await (0, $1UEKj$playwrighttest.expect)(byTitle.first()).toBeVisible();
1667
+ else await (0, $1UEKj$playwrighttest.expect)(byContainsTitle.first()).toBeVisible();
1668
+ }
1669
+ async selectTagsFilterFirstSuggestion(filterSelector, text, optionName) {
1670
+ const pane = this.getPane();
1671
+ const filter = pane.locator(filterSelector);
1672
+ const input = filter.locator('.xl-react-widget-autocomplete .react-tagsinput-input');
1673
+ await input.click({
1674
+ force: true
1675
+ });
1676
+ await input.fill(text);
1677
+ const suggestionList = pane.locator('.xl-react-autocomplete-suggestion-list');
1678
+ await (0, $1UEKj$playwrighttest.expect)(suggestionList).toBeVisible();
1679
+ const optionByTitle = suggestionList.locator(`.yt-option[title="${optionName}"]`);
1680
+ const optionByText = suggestionList.locator('.yt-option', {
1681
+ hasText: optionName
1682
+ });
1683
+ const gridOption = suggestionList.getByRole('rowgroup').getByText(optionName, {
1684
+ exact: false
1685
+ });
1686
+ let option;
1687
+ if (await optionByTitle.count()) option = optionByTitle.first();
1688
+ else if (await optionByText.count()) option = optionByText.first();
1689
+ else option = gridOption.first();
1690
+ await (0, $1UEKj$playwrighttest.expect)(option).toBeVisible();
1691
+ const iconCheckbox = option.locator('i.xl-icon.icon-s.checkbox');
1692
+ if (await iconCheckbox.count()) await iconCheckbox.click();
1693
+ else await option.click();
1694
+ await this.page.locator('body').click({
1695
+ position: {
1696
+ x: 10,
1697
+ y: 10
1698
+ }
1699
+ });
1700
+ await this.expectTagAdded(optionName);
1701
+ }
1702
+ }
1703
+
1704
+
1705
+
1706
+
1707
+ class $c8a7aeb49da7ed26$export$a3e02ac2d0adbb20 extends (0, $ae23b16684112d0b$export$2b65d1d97338f32b) {
1708
+ async downloadReport() {
1709
+ const pane = this.page.locator('#pane-DashboardTab');
1710
+ const exportButton = pane.locator('button.export-audit-report');
1711
+ const fallbackButton = pane.locator('button:has-text("Export")');
1712
+ const downloadPromise = this.page.waitForEvent('download', {
1713
+ timeout: 20000
1714
+ });
1715
+ if (await exportButton.isVisible()) await exportButton.click();
1716
+ else await fallbackButton.first().click();
1717
+ return downloadPromise;
1718
+ }
1719
+ async downloadDashboardExport() {
1720
+ return this.downloadReport();
1721
+ }
1722
+ async verifySummarySection() {
1723
+ const pane = this.page.locator('#pane-DashboardTab');
1724
+ const summary = pane.locator('.statistics-block');
1725
+ await (0, $1UEKj$playwrighttest.expect)(pane.getByText('Usage summary for current month', {
1726
+ exact: false
1727
+ })).toBeVisible();
1728
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('button:has-text("Export")')).toBeVisible();
1729
+ await (0, $1UEKj$playwrighttest.expect)(summary.locator('.tile-title[title="Successful deployments"]')).toBeVisible();
1730
+ await (0, $1UEKj$playwrighttest.expect)(summary.locator('.tile-title[title="Retried deployments"]')).toBeVisible();
1731
+ await (0, $1UEKj$playwrighttest.expect)(summary.locator('.tile-title[title="Rollback deployments"]')).toBeVisible();
1732
+ await (0, $1UEKj$playwrighttest.expect)(summary.locator('.tile-title[title="Aborted deployments"]')).toBeVisible();
1733
+ await (0, $1UEKj$playwrighttest.expect)(pane.getByText('Deployment status overview', {
1734
+ exact: true
1735
+ })).toBeVisible();
1736
+ }
1737
+ async verifyLast30DaysSection() {
1738
+ const pane = this.page.locator('#pane-DashboardTab');
1739
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('h1.time-period-text', {
1740
+ hasText: /Last 30 days/i
1741
+ })).toBeVisible();
1742
+ const successCard = pane.getByText('Top 10 successful deployments', {
1743
+ exact: true
1744
+ }).first().locator('..');
1745
+ const retriedCard = pane.getByText('Top 10 retried deployments', {
1746
+ exact: true
1747
+ }).first().locator('..');
1748
+ const longestCard = pane.getByText('Top 10 longest deployments', {
1749
+ exact: true
1750
+ }).first().locator('..');
1751
+ await this.expectCardHasDataOrEmpty(successCard);
1752
+ await this.expectCardHasDataOrEmpty(retriedCard);
1753
+ await this.expectCardHasDataOrEmpty(longestCard);
1754
+ }
1755
+ async verifySelected() {
1756
+ const pane = this.page.locator('#pane-DashboardTab');
1757
+ await (0, $1UEKj$playwrighttest.expect)(pane).toBeVisible();
1758
+ await (0, $1UEKj$playwrighttest.expect)(this.page.getByText('Dashboard', {
1759
+ exact: true
1760
+ })).toBeVisible();
1761
+ }
1762
+ async verifyLastUpdatedAndRefresh() {
1763
+ const pane = this.page.locator('#pane-DashboardTab');
1764
+ const lastUpdated = pane.locator('.last-updated-component');
1765
+ await (0, $1UEKj$playwrighttest.expect)(lastUpdated).toBeVisible();
1766
+ await (0, $1UEKj$playwrighttest.expect)(lastUpdated).not.toHaveText('');
1767
+ const refreshButton = pane.getByRole('button', {
1768
+ name: 'Refresh'
1769
+ });
1770
+ await refreshButton.click();
1771
+ await (0, $1UEKj$playwrighttest.expect)(lastUpdated).toBeVisible();
1772
+ }
1773
+ async verifyDateRangeDefault() {
1774
+ const pane = this.page.locator('#pane-DashboardTab');
1775
+ const dateRangeInput = pane.locator('#date-range-input');
1776
+ await (0, $1UEKj$playwrighttest.expect)(dateRangeInput).toHaveValue('Current month');
1777
+ }
1778
+ async selectDateRange(optionLabel) {
1779
+ const pane = this.page.locator('#pane-DashboardTab');
1780
+ const dateRangeInput = pane.locator('#date-range-input');
1781
+ try {
1782
+ await dateRangeInput.selectOption({
1783
+ label: optionLabel
1784
+ });
1785
+ return;
1786
+ } catch {
1787
+ // Fall back to clicking the open dropdown list.
1788
+ }
1789
+ await dateRangeInput.click();
1790
+ const optionRole = this.page.getByRole('option', {
1791
+ name: optionLabel
1792
+ });
1793
+ const menuItemRole = this.page.getByRole('menuitem', {
1794
+ name: optionLabel
1795
+ });
1796
+ const fallbackOption = this.page.locator('li', {
1797
+ hasText: optionLabel
1798
+ });
1799
+ if (await optionRole.isVisible()) {
1800
+ await optionRole.click();
1801
+ return;
1802
+ }
1803
+ if (await menuItemRole.isVisible()) {
1804
+ await menuItemRole.click();
1805
+ return;
1806
+ }
1807
+ await fallbackOption.first().click();
1808
+ }
1809
+ async verifyDateRangeHeaderContains(label) {
1810
+ const pane = this.page.locator('#pane-DashboardTab');
1811
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.title-header', {
1812
+ hasText: label
1813
+ })).toBeVisible();
1814
+ }
1815
+ async verifyOverviewChartsOrEmpty() {
1816
+ const pane = this.page.locator('#pane-DashboardTab');
1817
+ const statusOverview = pane.getByText('Deployment status overview', {
1818
+ exact: true
1819
+ }).first().locator('..');
1820
+ await this.expectWidgetHasChartOrEmpty(statusOverview);
1821
+ const numberOfDeployments = pane.getByText('Number of deployments over time', {
1822
+ exact: true
1823
+ }).first().locator('..');
1824
+ await this.expectWidgetHasChartOrEmpty(numberOfDeployments);
1825
+ const averageDuration = pane.getByText('Average deployment duration over time', {
1826
+ exact: true
1827
+ }).first().locator('..');
1828
+ await this.expectWidgetHasChartOrEmpty(averageDuration);
1829
+ }
1830
+ async verifyDeploymentStatusLegend() {
1831
+ const pane = this.page.locator('#pane-DashboardTab');
1832
+ const tile = pane.locator('.deployment-status-overview');
1833
+ await (0, $1UEKj$playwrighttest.expect)(tile).toBeVisible();
1834
+ const legendItems = tile.locator('.recharts-legend-item-text');
1835
+ if (await legendItems.count()) {
1836
+ await (0, $1UEKj$playwrighttest.expect)(legendItems.filter({
1837
+ hasText: 'Successful'
1838
+ }).first()).toBeVisible();
1839
+ await (0, $1UEKj$playwrighttest.expect)(legendItems.filter({
1840
+ hasText: 'Retried'
1841
+ }).first()).toBeVisible();
1842
+ await (0, $1UEKj$playwrighttest.expect)(legendItems.filter({
1843
+ hasText: 'Rollback'
1844
+ }).first()).toBeVisible();
1845
+ await (0, $1UEKj$playwrighttest.expect)(legendItems.filter({
1846
+ hasText: 'Aborted'
1847
+ }).first()).toBeVisible();
1848
+ return;
1849
+ }
1850
+ // No-data state: chart, canvas, or no-data widget must be visible
1851
+ const chartOrNoData = tile.locator('svg, canvas, .no-data-widget');
1852
+ await (0, $1UEKj$playwrighttest.expect)(chartOrNoData.first()).toBeVisible();
1853
+ }
1854
+ async verifyButtonsFocusable() {
1855
+ const pane = this.page.locator('#pane-DashboardTab');
1856
+ const exportButton = pane.getByRole('button', {
1857
+ name: 'Export'
1858
+ });
1859
+ const refreshButton = pane.getByRole('button', {
1860
+ name: 'Refresh'
1861
+ });
1862
+ await exportButton.focus();
1863
+ await (0, $1UEKj$playwrighttest.expect)(exportButton).toBeFocused();
1864
+ await refreshButton.focus();
1865
+ await (0, $1UEKj$playwrighttest.expect)(refreshButton).toBeFocused();
1866
+ }
1867
+ async verifyDateRangeOptions() {
1868
+ const pane = this.page.locator('#pane-DashboardTab');
1869
+ const select = pane.locator('#date-range-input');
1870
+ const expectedOptions = [
1871
+ 'Current month',
1872
+ 'Last 30 days',
1873
+ 'Last 90 days',
1874
+ 'Last 180 days',
1875
+ 'Last 360 days',
1876
+ 'Custom date range'
1877
+ ];
1878
+ for (const option of expectedOptions)await (0, $1UEKj$playwrighttest.expect)(select.locator(`option:has-text("${option}")`)).toBeAttached();
1879
+ }
1880
+ async verifyLast6MonthsSection() {
1881
+ const pane = this.page.locator('#pane-DashboardTab');
1882
+ const trendBlock = pane.locator('.time-period-section.trend-block');
1883
+ await (0, $1UEKj$playwrighttest.expect)(trendBlock).toBeVisible();
1884
+ await (0, $1UEKj$playwrighttest.expect)(trendBlock.locator('h1.time-period-text')).toContainText('Last 6 months');
1885
+ const numDeployments = trendBlock.locator('.number-of-deployments-over-time');
1886
+ const avgDuration = trendBlock.locator('.average-deployment-duration-over-time');
1887
+ await (0, $1UEKj$playwrighttest.expect)(numDeployments).toBeVisible();
1888
+ await (0, $1UEKj$playwrighttest.expect)(avgDuration).toBeVisible();
1889
+ }
1890
+ async verifyKpiTileNumbers() {
1891
+ const pane = this.page.locator('#pane-DashboardTab');
1892
+ const tiles = [
1893
+ pane.locator('.successful.text-widget'),
1894
+ pane.locator('.retried.text-widget'),
1895
+ pane.locator('.rollback.text-widget'),
1896
+ pane.locator('.aborted.text-widget')
1897
+ ];
1898
+ for (const tile of tiles){
1899
+ await (0, $1UEKj$playwrighttest.expect)(tile).toBeVisible();
1900
+ await (0, $1UEKj$playwrighttest.expect)(tile.locator('.number')).toBeVisible();
1901
+ }
1902
+ }
1903
+ async verifyCustomDateRangeInputs() {
1904
+ const pane = this.page.locator('#pane-DashboardTab');
1905
+ await pane.locator('#date-range-input').selectOption({
1906
+ label: 'Custom date range'
1907
+ });
1908
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.custom-date-range')).toBeVisible();
1909
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('#custom-range-start-date')).toBeVisible();
1910
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('#custom-range-end-date')).toBeVisible();
1911
+ // Restore default
1912
+ await pane.locator('#date-range-input').selectOption({
1913
+ label: 'Current month'
1914
+ });
1915
+ }
1916
+ async expectCardHasDataOrEmpty(card) {
1917
+ const tableRows = card.locator('table tbody tr');
1918
+ if (await tableRows.count()) {
1919
+ await (0, $1UEKj$playwrighttest.expect)(tableRows.first()).toBeVisible();
1920
+ return;
1921
+ }
1922
+ const rowData = card.locator('.row-data');
1923
+ if (await rowData.count()) {
1924
+ await (0, $1UEKj$playwrighttest.expect)(rowData.first()).toBeAttached();
1925
+ return;
1926
+ }
1927
+ await (0, $1UEKj$playwrighttest.expect)(card.getByText('No data available for the current period', {
1928
+ exact: true
1929
+ })).toBeVisible();
1930
+ }
1931
+ async expectWidgetHasChartOrEmpty(widget) {
1932
+ const chart = widget.locator('svg, canvas');
1933
+ const noData = widget.getByText('No data available for the current period', {
1934
+ exact: true
1935
+ });
1936
+ const chartVisible = await chart.first().isVisible();
1937
+ const noDataVisible = await noData.isVisible();
1938
+ (0, $1UEKj$playwrighttest.expect)(chartVisible || noDataVisible).toBeTruthy();
1939
+ }
1940
+ }
1941
+
1942
+
1943
+
1944
+
1945
+ class $50eb7c79d31afc08$export$41a993d086bafffd extends (0, $ae23b16684112d0b$export$2b65d1d97338f32b) {
1946
+ getPane() {
1947
+ return this.page.locator('#pane-DeploymentsTab');
1948
+ }
1949
+ getPagination() {
1950
+ return this.getPane().locator('.react-bootstrap-table-pagination');
1951
+ }
1952
+ getTableBody() {
1953
+ return this.getPane().locator('.react-bootstrap-table tbody');
1954
+ }
1955
+ getLastUpdated() {
1956
+ return this.getPane().locator('.last-updated-component');
1957
+ }
1958
+ async resolveDateField(containerSelector) {
1959
+ const container = this.getPane().locator(containerSelector);
1960
+ const input = container.locator('input');
1961
+ if (await input.count()) return input.first();
1962
+ return container;
1963
+ }
1964
+ async verifySelected() {
1965
+ const pane = this.getPane();
1966
+ await (0, $1UEKj$playwrighttest.expect)(pane).toBeVisible();
1967
+ await (0, $1UEKj$playwrighttest.expect)(this.page.getByText('Deployments', {
1968
+ exact: true
1969
+ })).toBeVisible();
1970
+ }
1971
+ async verifyFiltersVisible() {
1972
+ const pane = this.getPane();
1973
+ const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
1974
+ await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
1975
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-start-date')).toBeVisible();
1976
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-end-date')).toBeVisible();
1977
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-task-id input[placeholder="Search"]')).toBeVisible();
1978
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-worker input[placeholder="Search"]')).toBeVisible();
1979
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-applications .react-tagsinput-input')).toBeVisible();
1980
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-environments .react-tagsinput-input')).toBeVisible();
1981
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-state .react-tagsinput-input')).toBeVisible();
1982
+ await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-types .react-tagsinput-input')).toBeVisible();
1983
+ const tasksFilterInput = pane.locator('.deployment-task-filter-tasks .react-tagsinput-input');
1984
+ if (await tasksFilterInput.count()) await (0, $1UEKj$playwrighttest.expect)(tasksFilterInput).toBeVisible();
1985
+ }
1986
+ async verifyLastUpdatedAndRefresh() {
1987
+ const pane = this.getPane();
1988
+ const lastUpdated = this.getLastUpdated();
1989
+ await (0, $1UEKj$playwrighttest.expect)(lastUpdated).toBeVisible();
1990
+ await (0, $1UEKj$playwrighttest.expect)(lastUpdated).not.toHaveText('');
1991
+ const refreshButton = pane.getByRole('button', {
1992
+ name: 'Refresh'
1993
+ });
1994
+ await refreshButton.click();
1995
+ await (0, $1UEKj$playwrighttest.expect)(lastUpdated).toBeVisible();
1996
+ }
1997
+ async verifyTableHasRowsOrEmpty() {
1998
+ const pane = this.getPane();
1999
+ const table = pane.locator('table.table-striped');
2000
+ await (0, $1UEKj$playwrighttest.expect)(table).toBeVisible();
2001
+ const rows = this.getTableBody().locator('tr');
2002
+ const noDataCell = this.getTableBody().locator('tr td.react-bs-table-no-data');
2003
+ const emptyState = pane.getByText(/No data/i);
2004
+ await (0, $1UEKj$playwrighttest.expect)(async ()=>{
2005
+ if (await rows.count()) return true;
2006
+ if (await noDataCell.count() > 0 && await noDataCell.first().isVisible()) return true;
2007
+ if (await emptyState.count() > 0 && await emptyState.first().isVisible()) return true;
2008
+ return false;
2009
+ }).toBeTruthy();
2010
+ }
2011
+ async hideFilters() {
2012
+ const pane = this.getPane();
2013
+ const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
2014
+ const hideFiltersButton = pane.getByRole('button', {
2015
+ name: 'Hide filters'
2016
+ });
2017
+ await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
2018
+ await hideFiltersButton.click();
2019
+ await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeHidden();
2020
+ }
2021
+ async showFilters() {
2022
+ const pane = this.getPane();
2023
+ const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
2024
+ const showFiltersButton = pane.getByRole('button', {
2025
+ name: 'Show filters'
2026
+ });
2027
+ if (await filtersPanel.isVisible()) return;
2028
+ for(let attempt = 0; attempt < 3; attempt += 1){
2029
+ if (await showFiltersButton.isVisible()) await showFiltersButton.click({
2030
+ force: true
2031
+ });
2032
+ if (await filtersPanel.isVisible()) return;
2033
+ await this.page.waitForTimeout(300);
2034
+ }
2035
+ await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
2036
+ }
2037
+ async ensureFiltersVisible() {
2038
+ const pane = this.getPane();
2039
+ const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
2040
+ if (await filtersPanel.isVisible()) return;
2041
+ const showFiltersButton = pane.getByRole('button', {
2042
+ name: 'Show filters'
2043
+ });
2044
+ if (await showFiltersButton.isVisible()) await showFiltersButton.click({
2045
+ force: true
2046
+ });
2047
+ await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
2048
+ }
2049
+ async clickRefresh() {
2050
+ const pane = this.getPane();
2051
+ await pane.getByRole('button', {
2052
+ name: 'Refresh'
2053
+ }).click();
2054
+ }
2055
+ async expectTotal(total) {
2056
+ await (0, $1UEKj$playwrighttest.expect)(this.getPagination().locator('> div').filter({
2057
+ hasText: 'Total:'
2058
+ })).toContainText(`Total: ${total}`);
2059
+ }
2060
+ async expectNumberOfRows(count) {
2061
+ await this.waitForTableReady();
2062
+ await (0, $1UEKj$playwrighttest.expect)(this.getTableBody().locator('tr')).toHaveCount(count);
2063
+ }
2064
+ async expectAtLeastNumberOfRows(count) {
2065
+ await this.waitForTableReady();
2066
+ await (0, $1UEKj$playwrighttest.expect)(async ()=>{
2067
+ const total = await this.getTableBody().locator('tr').count();
2068
+ (0, $1UEKj$playwrighttest.expect)(total).toBeGreaterThanOrEqual(count);
2069
+ }).toPass({
2070
+ timeout: 30000
2071
+ });
2072
+ }
2073
+ async expectAtLeastTotal(count) {
2074
+ await this.waitForTableReady();
2075
+ await (0, $1UEKj$playwrighttest.expect)(async ()=>{
2076
+ const text = await this.getPagination().locator('> div').filter({
2077
+ hasText: 'Total:'
2078
+ }).textContent();
2079
+ const match = text?.match(/Total:\s*(\d+)/);
2080
+ const total = match ? Number(match[1]) : 0;
2081
+ (0, $1UEKj$playwrighttest.expect)(total).toBeGreaterThanOrEqual(count);
2082
+ }).toPass();
2083
+ }
2084
+ async expectValue(row, column, expected) {
2085
+ const cell = this.getTableBody().locator(`tr:nth-child(${row}) td.report-table-${column}`);
2086
+ await (0, $1UEKj$playwrighttest.expect)(cell).toHaveText(expected);
2087
+ }
2088
+ async expectValueContains(row, column, expected) {
2089
+ const cell = this.getTableBody().locator(`tr:nth-child(${row}) td.report-table-${column}`);
2090
+ await (0, $1UEKj$playwrighttest.expect)(cell).toContainText(expected);
2091
+ }
2092
+ async sortBy(column) {
2093
+ const header = this.getPane().locator(`.react-bootstrap-table thead th.report-table-${column}`);
2094
+ await header.click();
2095
+ }
2096
+ async isAscendingSorted(column) {
2097
+ const sortOrder = this.getPane().locator(`.react-bootstrap-table thead th.report-table-${column} .react-bootstrap-table-sort-order`);
2098
+ await (0, $1UEKj$playwrighttest.expect)(sortOrder).toBeVisible();
2099
+ }
2100
+ async getColumnValues(column) {
2101
+ await this.waitForTableReady();
2102
+ const cells = this.getTableBody().locator(`tr td.report-table-${column}`);
2103
+ const count = await cells.count();
2104
+ const values = [];
2105
+ for(let i = 0; i < count; i += 1)values.push((await cells.nth(i).textContent())?.trim() ?? '');
2106
+ return values;
2107
+ }
2108
+ async getLastPageNumber() {
2109
+ await this.waitForTableReady();
2110
+ const links = this.getPagination().getByRole('list').getByRole('link');
2111
+ const count = await links.count();
2112
+ if (count < 2) return 1;
2113
+ // links order on page 1: [1, 2, ..., n, >] — last numeric is nth(count-2)
2114
+ const text = await links.nth(count - 2).textContent();
2115
+ const num = Number(text?.trim());
2116
+ return Number.isFinite(num) && num > 0 ? num : 1;
2117
+ }
2118
+ async getTotalCount() {
2119
+ await this.waitForTableReady();
2120
+ const text = await this.getPagination().locator('> div').filter({
2121
+ hasText: 'Total:'
2122
+ }).textContent();
2123
+ const match = text?.match(/Total:\s*(\d+)/);
2124
+ return match ? Number(match[1]) : 0;
2125
+ }
2126
+ async expectPage(page) {
2127
+ await (0, $1UEKj$playwrighttest.expect)(async ()=>{
2128
+ const pagination = this.getPagination();
2129
+ // primary: react-bootstrap li.active with child link
2130
+ const activeLink = pagination.locator('li.active').getByRole('link', {
2131
+ name: page.toString(),
2132
+ exact: true
2133
+ });
2134
+ if (await activeLink.count() > 0) return;
2135
+ // fallback: aria-current="page"
2136
+ const ariaCurrent = pagination.locator('[aria-current="page"]').filter({
2137
+ hasText: page.toString()
2138
+ });
2139
+ if (await ariaCurrent.count() > 0) return;
2140
+ throw new Error(`Page ${page} not active`);
2141
+ }).toPass({
2142
+ timeout: 30000
2143
+ });
2144
+ }
2145
+ async goToPage(page) {
2146
+ await this.getPagination().getByRole('link', {
2147
+ name: page.toString()
2148
+ }).click();
2149
+ }
2150
+ async goToFirstPage() {
2151
+ const firstLink = this.getPagination().getByRole('link', {
2152
+ name: '<<'
2153
+ });
2154
+ if (await firstLink.count()) await firstLink.click();
2155
+ else await this.getPagination().getByRole('link', {
2156
+ name: '1',
2157
+ exact: true
2158
+ }).click();
2159
+ }
2160
+ async goToLastPage() {
2161
+ const lastLink = this.getPagination().getByRole('link', {
2162
+ name: '>>'
2163
+ });
2164
+ if (await lastLink.count()) await lastLink.click();
2165
+ else {
2166
+ const links = this.getPagination().getByRole('list').getByRole('link');
2167
+ const count = await links.count();
2168
+ // last numeric link is second-to-last (last is '>')
2169
+ await links.nth(count - 2).click();
2170
+ }
2171
+ }
2172
+ async goToPreviousPage() {
2173
+ await this.getPagination().getByRole('link', {
2174
+ name: '<',
2175
+ exact: true
2176
+ }).click();
2177
+ }
2178
+ async goToNextPage() {
2179
+ await this.getPagination().getByRole('link', {
2180
+ name: '>',
2181
+ exact: true
2182
+ }).click();
2183
+ }
2184
+ async expectPageLinkVisible(pageNumber) {
2185
+ await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('link', {
2186
+ name: pageNumber.toString(),
2187
+ exact: true
2188
+ })).toBeVisible();
2189
+ }
2190
+ async expectPaginationLinksVisible() {
2191
+ await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('link', {
2192
+ name: '>',
2193
+ exact: true
2194
+ })).toBeVisible();
2195
+ }
2196
+ async expectPaginationLinksHidden() {
2197
+ await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('link', {
2198
+ name: '>',
2199
+ exact: true
2200
+ })).toHaveCount(0);
2201
+ }
2202
+ async expectPageSize(size) {
2203
+ await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('button', {
2204
+ name: size.toString(),
2205
+ exact: true
2206
+ })).toBeVisible();
2207
+ }
2208
+ async expectPageSizes(sizes) {
2209
+ await this.openPageSizeMenu();
2210
+ const menu = this.getPagination().getByRole('menu');
2211
+ await (0, $1UEKj$playwrighttest.expect)(menu.getByRole('menuitem')).toHaveCount(sizes.length);
2212
+ for (const size of sizes)await (0, $1UEKj$playwrighttest.expect)(menu.getByRole('menuitem', {
2213
+ name: size.toString(),
2214
+ exact: true
2215
+ })).toBeVisible();
2216
+ await this.closePageSizeMenu();
2217
+ }
2218
+ async setPageSize(size) {
2219
+ await this.openPageSizeMenu();
2220
+ await this.getPagination().getByRole('menu').getByRole('menuitem', {
2221
+ name: size.toString(),
2222
+ exact: true
2223
+ }).click();
2224
+ await this.waitForTableReady();
2225
+ }
2226
+ async shouldHaveScrollBar() {
2227
+ await this.waitForTableReady();
2228
+ const container = this.getPane().locator('div.report');
2229
+ const overflowX = await container.evaluate((el)=>window.getComputedStyle(el).overflowX);
2230
+ (0, $1UEKj$playwrighttest.expect)([
2231
+ 'auto',
2232
+ 'scroll'
2233
+ ]).toContain(overflowX);
2234
+ }
2235
+ async setStartDate(value) {
2236
+ const field = await this.resolveDateField('.deployment-task-filter-start-date');
2237
+ await field.click();
2238
+ await field.fill(value);
2239
+ await this.getLastUpdated().click({
2240
+ force: true
2241
+ });
2242
+ }
2243
+ async setEndDate(value) {
2244
+ const field = await this.resolveDateField('.deployment-task-filter-end-date');
2245
+ await field.click();
2246
+ await field.fill(value);
2247
+ await this.getLastUpdated().click({
2248
+ force: true
2249
+ });
2250
+ }
2251
+ async expectStartDateValue(value) {
2252
+ await this.expectDateFieldValue('.deployment-task-filter-start-date', value);
2253
+ }
2254
+ async expectEndDateValue(value) {
2255
+ await this.expectDateFieldValue('.deployment-task-filter-end-date', value);
2256
+ }
2257
+ async checkInvalidStartDateError() {
2258
+ const field = await this.resolveDateField('.deployment-task-filter-start-date');
2259
+ await field.click(); // opens calendar
2260
+ await field.press('End');
2261
+ await field.press('a'); // append invalid char
2262
+ await this.page.keyboard.press('Escape'); // close calendar → error appears below field
2263
+ await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018').first()).toBeVisible();
2264
+ }
2265
+ async checkInvalidEndDateError() {
2266
+ const field = await this.resolveDateField('.deployment-task-filter-end-date');
2267
+ await field.click(); // opens calendar
2268
+ await field.press('End');
2269
+ await field.press('b'); // append invalid char
2270
+ await this.page.keyboard.press('Escape'); // close calendar → error appears below field
2271
+ await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018').first()).toBeVisible();
2272
+ }
2273
+ async expectNoStartDateError() {
2274
+ await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018')).toHaveCount(0);
2275
+ }
2276
+ async expectNoEndDateError() {
2277
+ await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018')).toHaveCount(0);
2278
+ }
2279
+ async selectRadioFilter(label) {
2280
+ await this.ensureFiltersVisible();
2281
+ const pane = this.getPane();
2282
+ const radioGroup = pane.locator('.xl-react-widget-radio-group');
2283
+ const labelLocator = radioGroup.locator(`label[data='${label}']`);
2284
+ if (await labelLocator.count()) {
2285
+ await labelLocator.click();
2286
+ return;
2287
+ }
2288
+ const fallbackByText = radioGroup.getByText(label, {
2289
+ exact: true
2290
+ });
2291
+ if (await fallbackByText.count()) {
2292
+ await fallbackByText.click();
2293
+ return;
2294
+ }
2295
+ const ariaRadio = pane.locator('[role="radio"]', {
2296
+ hasText: label
2297
+ });
2298
+ if (await ariaRadio.count()) await ariaRadio.first().click();
2299
+ }
2300
+ async expectApplicationsRadioSelected() {
2301
+ const radio = this.getPane().locator('.xl-react-widget-radio-group label[data="Application(s)"]');
2302
+ if (await radio.count() === 0) return;
2303
+ await (0, $1UEKj$playwrighttest.expect)(radio).toHaveAttribute('style', /background-color/);
2304
+ }
2305
+ async selectTagsFilterApplications(text, optionName) {
2306
+ await this.selectTagsFilterFirstSuggestion('.deployment-task-filter-applications .react-tagsinput', text, optionName);
2307
+ }
2308
+ async selectTagsFilterEnvironments(text, optionName) {
2309
+ await this.selectTagsFilterFirstSuggestion('.deployment-task-filter-environments .react-tagsinput', text, optionName);
2310
+ }
2311
+ async selectStatesTagsFilterFirstSuggestion(text, optionName) {
2312
+ await this.selectTagsFilterFirstSuggestion('.deployment-task-filter-state .react-tagsinput', text, optionName);
2313
+ }
2314
+ async clearStatesTagsFirstTag() {
2315
+ const remove = this.getPane().locator('.deployment-task-filter-state .react-tagsinput-tag:nth-child(1) .react-tagsinput-remove');
2316
+ if (await remove.count()) await remove.click();
2317
+ }
2318
+ async clearApplicationsTagsFirstTag() {
2319
+ const remove = this.getPane().locator('.deployment-task-filter-applications .react-tagsinput-tag:nth-child(1) .react-tagsinput-remove');
2320
+ if (await remove.count()) await remove.click();
2321
+ }
2322
+ async clearEnvironmentsTagsFirstTag() {
2323
+ const remove = this.getPane().locator('.deployment-task-filter-environments .react-tagsinput-tag:nth-child(1) .react-tagsinput-remove');
2324
+ if (await remove.count()) await remove.click();
2325
+ }
2326
+ async setTaskId(taskId) {
2327
+ const input = this.getPane().locator('.deployment-task-filter-task-id input');
2328
+ await input.fill(taskId);
2329
+ await (0, $1UEKj$playwrighttest.expect)(input).toHaveValue(taskId);
2330
+ }
2331
+ async setWorkerName(workerName) {
2332
+ const input = this.getPane().locator('.deployment-task-filter-worker input');
2333
+ await input.fill(workerName);
2334
+ await (0, $1UEKj$playwrighttest.expect)(input).toHaveValue(workerName);
2335
+ }
2336
+ async clearWorkerName() {
2337
+ await this.setWorkerName('');
2338
+ }
2339
+ async expectTagAdded(tagTitle) {
2340
+ const tags = this.getPane().locator('.xl-react-widget-tags .react-tagsinput-tag');
2341
+ const byTitle = tags.locator(`[title='${tagTitle}']`);
2342
+ const byContainsTitle = tags.filter({
2343
+ hasText: tagTitle.split('/').pop() ?? tagTitle
2344
+ });
2345
+ const count = await byTitle.count();
2346
+ if (count > 0) await (0, $1UEKj$playwrighttest.expect)(byTitle.first()).toBeVisible();
2347
+ else await (0, $1UEKj$playwrighttest.expect)(byContainsTitle.first()).toBeVisible();
2348
+ }
2349
+ async selectTagsFilterFirstSuggestion(filterSelector, text, optionName) {
2350
+ const pane = this.getPane();
2351
+ const filter = pane.locator(filterSelector);
2352
+ const input = filter.locator('.xl-react-widget-autocomplete .react-tagsinput-input');
2353
+ await input.click({
2354
+ force: true
2355
+ });
2356
+ await input.fill(text);
2357
+ const suggestionList = pane.locator('.xl-react-autocomplete-suggestion-list');
2358
+ await (0, $1UEKj$playwrighttest.expect)(suggestionList).toBeVisible();
2359
+ // Try .yt-option first (State/Type filters), then aria grid row (Application/Environment filters)
2360
+ const optionByTitle = suggestionList.locator(`.yt-option[title="${optionName}"]`);
2361
+ const optionByText = suggestionList.locator('.yt-option', {
2362
+ hasText: optionName
2363
+ });
2364
+ const gridOption = suggestionList.getByRole('rowgroup').getByText(optionName, {
2365
+ exact: false
2366
+ });
2367
+ let option;
2368
+ if (await optionByTitle.count()) option = optionByTitle.first();
2369
+ else if (await optionByText.count()) option = optionByText.first();
2370
+ else option = gridOption.first();
2371
+ await (0, $1UEKj$playwrighttest.expect)(option).toBeVisible();
2372
+ const iconCheckbox = option.locator('i.xl-icon.icon-s.checkbox');
2373
+ if (await iconCheckbox.count()) await iconCheckbox.click();
2374
+ else await option.click();
2375
+ await this.page.locator('body').click({
2376
+ position: {
2377
+ x: 10,
2378
+ y: 10
2379
+ }
2380
+ });
2381
+ await this.expectTagAdded(optionName);
2382
+ }
2383
+ async waitForTableReady() {
2384
+ // Poll until loading indicator is gone AND table content (rows or empty state) is present
2385
+ await (0, $1UEKj$playwrighttest.expect)(async ()=>{
2386
+ const loading = await this.getPane().getByText('Data loading').count();
2387
+ if (loading > 0) throw new Error('Still loading');
2388
+ const rows = await this.getTableBody().locator('tr').count();
2389
+ const noData = await this.getTableBody().locator('td.react-bs-table-no-data').count();
2390
+ if (rows === 0 && noData === 0) throw new Error('No table content yet');
2391
+ }).toPass({
2392
+ timeout: 60000
2393
+ });
2394
+ }
2395
+ async openPageSizeMenu() {
2396
+ const btn = this.getPagination().getByRole('toolbar').getByRole('button');
2397
+ const expanded = await btn.getAttribute('aria-expanded');
2398
+ if (expanded !== 'true') {
2399
+ await btn.click();
2400
+ await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('menu')).toBeVisible();
2401
+ }
2402
+ }
2403
+ async closePageSizeMenu() {
2404
+ const btn = this.getPagination().getByRole('toolbar').getByRole('button');
2405
+ const expanded = await btn.getAttribute('aria-expanded');
2406
+ if (expanded === 'true') {
2407
+ await btn.click();
2408
+ await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('menu')).toBeHidden();
2409
+ }
2410
+ }
2411
+ async expectDateFieldValue(containerSelector, value) {
2412
+ const container = this.getPane().locator(containerSelector);
2413
+ const input = container.locator('input');
2414
+ if (await input.count() > 0) {
2415
+ await (0, $1UEKj$playwrighttest.expect)(input.first()).toHaveValue(value);
2416
+ return;
2417
+ }
2418
+ await (0, $1UEKj$playwrighttest.expect)(container).toHaveValue(value);
2419
+ }
2420
+ }
2421
+
2422
+
2423
+
2424
+ class $9204952bd68f68e5$export$e37b272c81a10693 extends (0, $84f8d1abd29d48fd$export$cb21416f3c2fd176) {
2425
+ /* Navigates to the Infrastructure Edit Permissions page via the tree context menu */ async navigateToPage() {
2426
+ await this.openRootContextMenu('Infrastructure');
2427
+ await this.page.getByRole('link', {
2428
+ name: 'Edit Permissions'
2429
+ }).click();
2430
+ }
2431
+ async navigateToInfraEditPermissionPage() {
2432
+ await this.navigateToPage();
2433
+ }
2434
+ /* Gives the permissions to a role */ async givePermissionsToRole(roleName) {
2435
+ const filter = this.page.getByPlaceholder('Filter by roles').first();
2436
+ await filter.waitFor({
2437
+ state: 'visible'
2438
+ });
2439
+ await filter.scrollIntoViewIfNeeded();
2440
+ await filter.fill(roleName);
2441
+ const roleRow = this.page.locator('table.table-striped tbody tr').first();
2442
+ const checkboxes = roleRow.locator('input[type="checkbox"]');
2443
+ const count = await checkboxes.count();
2444
+ for(let i = 0; i < count; i += 1){
2445
+ const checkbox = checkboxes.nth(i);
2446
+ if (!await checkbox.isChecked()) await checkbox.check();
2447
+ }
2448
+ await this.page.getByRole('button', {
2449
+ name: 'Save and close'
2450
+ }).click();
2451
+ await this.handleSaveToastOrAlreadyExists();
2452
+ }
2453
+ }
2454
+
2455
+
2456
+
2457
+ class $05857e702fc1e333$export$c919546f4bf88f35 extends (0, $84f8d1abd29d48fd$export$cb21416f3c2fd176) {
2458
+ /* Navigates to the Environments Edit Permissions page via the tree context menu */ async navigateToPage() {
2459
+ await this.openRootContextMenu('Environments');
2460
+ await this.page.getByRole('link', {
2461
+ name: 'Edit Permissions'
2462
+ }).click();
2463
+ }
2464
+ async navigateToEnvEditPermissionPage() {
2465
+ await this.navigateToPage();
2466
+ }
2467
+ /* Gives the permissions to a role */ async givePermissionsToRole(roleName) {
2468
+ const filter = this.page.getByPlaceholder('Filter by roles').first();
2469
+ await filter.waitFor({
2470
+ state: 'visible'
2471
+ });
2472
+ await filter.scrollIntoViewIfNeeded();
2473
+ await filter.fill(roleName);
2474
+ const roleRow = this.page.locator('table.table-striped tbody tr').first();
2475
+ const labels = [
2476
+ 'deploy initial',
2477
+ 'read',
2478
+ 'repo edit'
2479
+ ];
2480
+ const headers = this.page.locator('table.table-striped thead th');
2481
+ const headerCount = await headers.count();
2482
+ const headerTexts = [];
2483
+ for(let i = 0; i < headerCount; i += 1)headerTexts.push((await headers.nth(i).innerText()).trim().toLowerCase());
2484
+ for (const label of labels){
2485
+ const colIndex = headerTexts.findIndex((text)=>text.includes(label));
2486
+ if (colIndex === -1) throw new Error(`Permission column not found: ${label}`);
2487
+ const checkbox = roleRow.locator('td').nth(colIndex).locator('input[type="checkbox"]');
2488
+ if (!await checkbox.isChecked()) await checkbox.check();
2489
+ }
2490
+ await this.page.getByRole('button', {
2491
+ name: 'Save and close'
2492
+ }).click();
2493
+ await this.handleSaveToastOrAlreadyExists();
2494
+ }
2495
+ }
2496
+
2497
+
2498
+
2499
+ class $7735c50a40aa728e$export$ecd8b3e50e92d781 extends (0, $84f8d1abd29d48fd$export$cb21416f3c2fd176) {
2500
+ /* Navigates to the Applications Edit Permissions page via the tree context menu */ async navigateToPage() {
2501
+ await this.openRootContextMenu('Applications');
2502
+ await this.page.getByRole('link', {
2503
+ name: 'Edit Permissions'
2504
+ }).click();
2505
+ }
2506
+ async navigateToAppEditPermissionPage() {
2507
+ await this.navigateToPage();
2508
+ }
2509
+ /* Gives the permissions to a role */ async givePermissionsToRole(roleName) {
2510
+ const filter = this.page.getByPlaceholder('Filter by roles').first();
2511
+ await filter.waitFor({
2512
+ state: 'visible'
2513
+ });
2514
+ await filter.scrollIntoViewIfNeeded();
2515
+ await filter.fill(roleName);
2516
+ const roleRow = this.page.locator('table.table-striped tbody tr').first();
2517
+ const labels = [
2518
+ 'read',
2519
+ 'repo edit'
2520
+ ];
2521
+ const headers = this.page.locator('table.table-striped thead th');
2522
+ const headerCount = await headers.count();
2523
+ const headerTexts = [];
2524
+ for(let i = 0; i < headerCount; i += 1)headerTexts.push((await headers.nth(i).innerText()).trim().toLowerCase());
2525
+ for (const label of labels){
2526
+ const colIndex = headerTexts.findIndex((text)=>text.includes(label));
2527
+ if (colIndex === -1) throw new Error(`Permission column not found: ${label}`);
2528
+ const checkbox = roleRow.locator('td').nth(colIndex).locator('input[type="checkbox"]');
2529
+ if (!await checkbox.isChecked()) await checkbox.check();
2530
+ }
2531
+ await this.page.getByRole('button', {
2532
+ name: 'Save and close'
2533
+ }).click();
2534
+ await this.handleSaveToastOrAlreadyExists();
2535
+ }
2536
+ }
2537
+
2538
+
2539
+ class $28788a34e6bbbe3d$export$b8a61e5c71402559 extends (0, $84f8d1abd29d48fd$export$cb21416f3c2fd176) {
2540
+ constructor(page){
2541
+ super(page);
115
2542
  this.homePage = new (0, $f280624787ceceed$export$36d69433c4f81145)(page);
116
2543
  this.userPage = new (0, $c949c453826379e3$export$ecdc3cf9d1cfe143)(page);
2544
+ this.overthereLocalHostPage = new (0, $647b67e126f2dbe0$export$c88e1d16f82516d)(page);
2545
+ this.environmentsPage = new (0, $f8bdd801aee9e08b$export$12530425267834a9)(page);
2546
+ this.applicationsPage = new (0, $408106958eb335da$export$1533b625ec0c75e2)(page);
2547
+ this.reportsPage = new (0, $b2f725a937c99e2d$export$b8a443164510a1b4)(page);
2548
+ this.reportsControlTasksPage = new (0, $1b52322f0063c4bc$export$5c1198389e139c11)(page);
2549
+ this.reportsDashboardPage = new (0, $c8a7aeb49da7ed26$export$a3e02ac2d0adbb20)(page);
2550
+ this.reportsDeploymentsPage = new (0, $50eb7c79d31afc08$export$41a993d086bafffd)(page);
2551
+ this.infrastructureEditPermissionsPage = new (0, $9204952bd68f68e5$export$e37b272c81a10693)(page);
2552
+ this.environmentEditPermissionsPage = new (0, $05857e702fc1e333$export$c919546f4bf88f35)(page);
2553
+ this.applicationEditPermissionsPage = new (0, $7735c50a40aa728e$export$ecd8b3e50e92d781)(page);
117
2554
  }
118
2555
  async navigateToHomePage() {
119
- await this.page.goto("/home");
2556
+ await this.page.goto('/home');
120
2557
  }
121
2558
  async navigateToUserPage() {
122
- await this.page.locator("p.MuiTypography-root", {
123
- hasText: "User management"
2559
+ await this.page.locator('p.MuiTypography-root', {
2560
+ hasText: 'User management'
124
2561
  }).click();
125
2562
  }
126
2563
  async clickCollapseSideView() {
127
- const collapseButton = this.page.getByTestId("back-button");
2564
+ const collapseButton = this.page.getByTestId('back-button');
128
2565
  await collapseButton.click();
129
2566
  }
130
2567
  }
@@ -133,53 +2570,71 @@ class $28788a34e6bbbe3d$export$b8a61e5c71402559 {
133
2570
 
134
2571
 
135
2572
  class $365fc3920b0d055f$export$f14c0e3f98d164c0 extends (0, $ae23b16684112d0b$export$2b65d1d97338f32b) {
136
- /**
137
- * Login with username and password
138
- * @param userName
139
- * @param password
140
- */ async login(userName, password) {
141
- await this.page.goto("/#/login");
142
- await (0, $1UEKj$playwrighttest.expect)(this.page).toHaveTitle("Digital.ai Deploy");
143
- await this.page.getByPlaceholder("user").fill(userName, {
144
- timeout: 1000
145
- });
146
- await this.page.getByPlaceholder("Password").fill(password, {
2573
+ /* Login with username and password*/ async login(userName, password, pageOverride) {
2574
+ const p = pageOverride ?? this.page;
2575
+ await p.goto('/#/login');
2576
+ await (0, $1UEKj$playwrighttest.expect)(p).toHaveTitle('Digital.ai Deploy');
2577
+ await p.getByPlaceholder('user').fill(userName, {
2578
+ timeout: 10000
2579
+ });
2580
+ await p.getByPlaceholder('Password').fill(password, {
147
2581
  timeout: 10000
148
2582
  });
149
- await this.page.getByRole("button", {
150
- name: "Log in"
2583
+ await p.getByRole('button', {
2584
+ name: 'Log in'
151
2585
  }).click({
152
2586
  timeout: 10000
153
2587
  });
154
2588
  }
155
- async verifyInvalidPasswordMessage() {
156
- const errorMessage = this.page.getByText("The username and/or password you have entered is incorrect.");
2589
+ /* Verify invalid password message */ async verifyInvalidPasswordMessage() {
2590
+ const errorMessage = this.page.getByText('The username and/or password you have entered is incorrect.');
157
2591
  await (0, $1UEKj$playwrighttest.expect)(errorMessage).toBeVisible();
158
2592
  }
159
- /**
160
- * Logout as authenticated user
161
- */ async logout() {
162
- await this.page.locator("button.dot-avatar").click();
163
- await this.page.getByText("Log out", {
2593
+ /* Verify the error message shown when a disabled user attempts to log in */ async verifyDisabledUserLoginError(pageOverride) {
2594
+ const p = pageOverride ?? this.page;
2595
+ const errorMessage = p.getByText('Your account is currently disabled. Please contact your Deploy administrator for further assistance.');
2596
+ await (0, $1UEKj$playwrighttest.expect)(errorMessage).toBeVisible();
2597
+ }
2598
+ /* Verify the error message shown when a user without login permission attempts to log in */ async verifyNoLoginPermissionError(pageOverride) {
2599
+ const p = pageOverride ?? this.page;
2600
+ const errorMessage = p.locator('div.error-message.permission-error');
2601
+ await (0, $1UEKj$playwrighttest.expect)(errorMessage).toBeVisible({
2602
+ timeout: 20000
2603
+ });
2604
+ }
2605
+ /* Logout as authenticated user */ async logout() {
2606
+ await this.page.locator('button.dot-avatar').click();
2607
+ await this.page.getByText('Log out', {
164
2608
  exact: true
165
2609
  }).click();
166
- await (0, $1UEKj$playwrighttest.expect)(this.page.getByText("Log in").first()).toBeVisible();
2610
+ await this.page.waitForURL('**/#/login', {
2611
+ timeout: 15000
2612
+ });
2613
+ await this.page.reload();
2614
+ await this.page.waitForLoadState('networkidle');
2615
+ await this.page.waitForURL('**/#/login', {
2616
+ timeout: 15000
2617
+ });
2618
+ await this.page.getByPlaceholder('user').waitFor({
2619
+ state: 'visible',
2620
+ timeout: 15000
2621
+ });
167
2622
  }
168
2623
  }
169
2624
 
170
2625
 
171
2626
  const $3352700e95ddc19e$export$e0969da9b8fb378d = (0, $1UEKj$playwrighttest.test).extend({
172
- fixtures: async ({ page: page }, use)=>{
2627
+ fixtures: async ({ page: page }, use)=>{
173
2628
  const fixtures = new $3352700e95ddc19e$var$Fixtures(page);
174
2629
  await use(fixtures);
175
2630
  return fixtures;
176
2631
  },
177
- loginPage: async ({ page: page }, use)=>{
2632
+ loginPage: async ({ page: page }, use)=>{
178
2633
  const loginPage = new (0, $365fc3920b0d055f$export$f14c0e3f98d164c0)(page);
179
2634
  await use(loginPage);
180
2635
  return loginPage;
181
2636
  },
182
- navigation: async ({ page: page }, use)=>{
2637
+ navigation: async ({ page: page }, use)=>{
183
2638
  const navigationPage = new (0, $28788a34e6bbbe3d$export$b8a61e5c71402559)(page);
184
2639
  await use(navigationPage);
185
2640
  return navigationPage;