@digital-ai/devops-page-object-deploy 0.0.8 → 0.0.10
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/CHANGELOG.md +14 -0
- package/dist/main.js +2504 -205
- package/dist/main.js.map +1 -1
- package/dist/module.js +2504 -206
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +212 -12
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
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,411 +15,2709 @@ 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(
|
|
20
|
-
hasText:
|
|
125
|
+
await this.page.locator('p.MuiTypography-root', {
|
|
126
|
+
hasText: 'User management'
|
|
21
127
|
}).click();
|
|
22
|
-
await this.page.getByRole(
|
|
23
|
-
name:
|
|
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(
|
|
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(
|
|
142
|
+
const title = this.page.getByTitle('digital.ai');
|
|
32
143
|
await (0, $1UEKj$playwrighttest.expect)(title).toBeVisible();
|
|
33
144
|
}
|
|
34
145
|
async clickReports() {
|
|
35
|
-
await this.page.locator(
|
|
36
|
-
hasText:
|
|
146
|
+
await this.page.locator('p.MuiTypography-root', {
|
|
147
|
+
hasText: 'Reports'
|
|
37
148
|
}).click();
|
|
38
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
|
+
}
|
|
39
185
|
}
|
|
40
186
|
|
|
41
187
|
|
|
42
188
|
|
|
43
189
|
|
|
44
190
|
class $c949c453826379e3$export$ecdc3cf9d1cfe143 extends (0, $ae23b16684112d0b$export$2b65d1d97338f32b) {
|
|
45
|
-
|
|
46
|
-
await this.page.locator(
|
|
47
|
-
hasText:
|
|
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'
|
|
48
243
|
}).click();
|
|
49
|
-
await this.page.
|
|
50
|
-
|
|
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'
|
|
51
259
|
}).click();
|
|
52
|
-
|
|
260
|
+
const usernameInput = this.page.locator('input[name="username"]');
|
|
261
|
+
await usernameInput.waitFor({
|
|
262
|
+
state: 'visible'
|
|
263
|
+
});
|
|
264
|
+
await usernameInput.fill(username);
|
|
53
265
|
await this.page.locator('input[name="fullName"]').fill(username);
|
|
54
266
|
await this.page.locator('input[name="email"]').fill(email);
|
|
55
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
|
+
});
|
|
56
272
|
await passwordInput.click({
|
|
57
273
|
force: true
|
|
58
274
|
});
|
|
59
275
|
await passwordInput.fill(password);
|
|
60
|
-
await
|
|
61
|
-
|
|
62
|
-
name:
|
|
63
|
-
})
|
|
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();
|
|
64
300
|
}
|
|
65
301
|
/* Verify success message */ async verifySuccessMessage() {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
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);
|
|
69
313
|
}
|
|
70
314
|
/* Verify user error message */ async verifyUserErrorMessage() {
|
|
71
|
-
|
|
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);
|
|
72
326
|
}
|
|
73
327
|
/* Delete the user and verify it is deleted */ async deleteTheUserAndVerifyItIsDeleted(username) {
|
|
74
|
-
await this.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
await
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
name:
|
|
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',
|
|
81
348
|
exact: true
|
|
82
349
|
}).click();
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
+
});
|
|
86
363
|
}
|
|
87
364
|
/* Edit the user and verify the changes */ async editUserAndVerify(username, editfullname) {
|
|
88
|
-
await this.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
await
|
|
92
|
-
|
|
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();
|
|
93
377
|
await this.page.locator('input[name="fullName"]').fill(editfullname);
|
|
94
|
-
|
|
95
|
-
|
|
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',
|
|
96
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'
|
|
97
493
|
}).click();
|
|
98
|
-
const
|
|
99
|
-
await
|
|
100
|
-
await
|
|
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();
|
|
101
510
|
}
|
|
102
511
|
}
|
|
103
512
|
|
|
104
513
|
|
|
105
514
|
|
|
106
|
-
|
|
107
|
-
|
|
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
|
+
}
|
|
108
552
|
/* Helps to create a LocalHost of type UNIX/Windows/Zos */ async createHostForGivenType(name, type) {
|
|
109
553
|
await this.page.locator('input[name="name"]').fill(name);
|
|
110
|
-
await this.page.locator('label:has-text("Operating system")').locator(
|
|
554
|
+
await this.page.locator('label:has-text("Operating system")').locator('xpath=following::input[1]').click();
|
|
111
555
|
await this.page.getByText(type, {
|
|
112
556
|
exact: true
|
|
113
557
|
}).click();
|
|
114
|
-
await this.page.getByRole(
|
|
115
|
-
name:
|
|
558
|
+
await this.page.getByRole('button', {
|
|
559
|
+
name: 'Save and close'
|
|
560
|
+
}).click();
|
|
561
|
+
await this.handleSaveToastOrAlreadyExists();
|
|
562
|
+
}
|
|
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'
|
|
116
590
|
}).click();
|
|
117
|
-
|
|
118
|
-
await (
|
|
119
|
-
|
|
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
|
|
120
613
|
});
|
|
121
614
|
}
|
|
122
615
|
}
|
|
123
616
|
|
|
124
617
|
|
|
125
618
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
619
|
+
class $f8bdd801aee9e08b$export$12530425267834a9 extends (0, $84f8d1abd29d48fd$export$cb21416f3c2fd176) {
|
|
620
|
+
/* Helps to navigate to the Environments page */ async navigateToEnvironmentsPage() {
|
|
621
|
+
await this.ensureExplorerTreeReady();
|
|
622
|
+
}
|
|
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();
|
|
129
631
|
await this.page.locator('input[name="name"]').fill(environmentName);
|
|
130
|
-
await this.page.locator(
|
|
632
|
+
await this.page.locator('i.xl-icon.arrow-down-icon.show-all').nth(0).click();
|
|
131
633
|
await this.page.getByTitle(`Infrastructure/${hostName}`, {
|
|
132
634
|
exact: true
|
|
133
635
|
}).click();
|
|
134
|
-
await this.page.
|
|
135
|
-
|
|
636
|
+
await this.page.locator('i.xl-icon.arrow-down-icon.show-all').nth(1).click();
|
|
637
|
+
await this.page.getByTitle(`Environments/${dictionaryName}`, {
|
|
638
|
+
exact: true
|
|
639
|
+
}).click();
|
|
640
|
+
await this.page.getByRole('button', {
|
|
641
|
+
name: 'Save and close'
|
|
136
642
|
}).click();
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
643
|
+
await this.handleSaveToastOrAlreadyExists();
|
|
644
|
+
}
|
|
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'
|
|
649
|
+
}).click();
|
|
650
|
+
await this.page.getByRole('link', {
|
|
651
|
+
name: 'Dictionary',
|
|
652
|
+
exact: true
|
|
653
|
+
}).click();
|
|
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();
|
|
141
668
|
}
|
|
142
669
|
}
|
|
143
670
|
|
|
144
671
|
|
|
145
672
|
|
|
146
673
|
|
|
147
|
-
class $408106958eb335da$export$1533b625ec0c75e2 extends (0, $
|
|
148
|
-
constructor(page
|
|
674
|
+
class $408106958eb335da$export$1533b625ec0c75e2 extends (0, $84f8d1abd29d48fd$export$cb21416f3c2fd176) {
|
|
675
|
+
constructor(page){
|
|
149
676
|
super(page);
|
|
150
|
-
|
|
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();
|
|
151
686
|
}
|
|
152
687
|
/* Creates a new application */ async createNewApplication(applicationName) {
|
|
153
|
-
await this.
|
|
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();
|
|
154
696
|
await this.page.locator('input[name="name"]').waitFor({
|
|
155
|
-
state:
|
|
697
|
+
state: 'visible'
|
|
156
698
|
});
|
|
157
699
|
await this.page.locator('input[name="name"]').click();
|
|
158
700
|
await this.page.locator('input[name="name"]').fill(applicationName);
|
|
159
|
-
await this.page.getByRole(
|
|
160
|
-
name:
|
|
701
|
+
await this.page.getByRole('button', {
|
|
702
|
+
name: 'Save and close'
|
|
161
703
|
}).click();
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
timeout:
|
|
704
|
+
await this.page.locator('input[name="name"]').waitFor({
|
|
705
|
+
state: 'hidden',
|
|
706
|
+
timeout: 5000
|
|
165
707
|
});
|
|
708
|
+
await this.handleSaveToastOrAlreadyExists();
|
|
709
|
+
await this.waitForApplicationNode(applicationName);
|
|
166
710
|
}
|
|
167
711
|
/* Creates a new deployment package */ async createNewDeploymentPackage(applicationName, packageName) {
|
|
168
712
|
const appDataId = `Applications/${applicationName}`;
|
|
713
|
+
const packageDataId = `Applications/${applicationName}/${packageName}`;
|
|
714
|
+
if (await this.nodeExists(packageDataId)) return;
|
|
169
715
|
// Expand Applications
|
|
170
|
-
|
|
171
|
-
await
|
|
716
|
+
await this.ensureExplorerTreeReady();
|
|
717
|
+
await this.waitForApplicationNode(applicationName);
|
|
172
718
|
// Hover over app node
|
|
173
719
|
const appNode = this.page.locator(`span[data-id="${appDataId}"]`);
|
|
174
720
|
await (0, $1UEKj$playwrighttest.expect)(appNode).toBeVisible();
|
|
721
|
+
await appNode.scrollIntoViewIfNeeded();
|
|
175
722
|
await appNode.hover();
|
|
176
723
|
// Open context menu + create new deployment package
|
|
177
724
|
const contextMenu = this.page.locator(`i.context-menu-button[data-id="${appDataId}"]`);
|
|
178
725
|
await (0, $1UEKj$playwrighttest.expect)(contextMenu).toBeVisible();
|
|
179
726
|
await contextMenu.click();
|
|
180
|
-
await this.page.getByRole(
|
|
181
|
-
name:
|
|
727
|
+
await this.page.getByRole('link', {
|
|
728
|
+
name: 'New'
|
|
182
729
|
}).click();
|
|
183
|
-
await this.page.getByRole(
|
|
184
|
-
name:
|
|
730
|
+
await this.page.getByRole('link', {
|
|
731
|
+
name: 'Deployment Package'
|
|
185
732
|
}).click();
|
|
186
733
|
// Fill & save
|
|
187
734
|
const nameInput = this.page.locator('input[name="name"]');
|
|
188
735
|
await nameInput.fill(packageName);
|
|
189
|
-
await this.page.getByRole(
|
|
190
|
-
name:
|
|
736
|
+
await this.page.getByRole('button', {
|
|
737
|
+
name: 'Save and close'
|
|
191
738
|
}).click();
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
timeout:
|
|
739
|
+
await nameInput.waitFor({
|
|
740
|
+
state: 'hidden',
|
|
741
|
+
timeout: 5000
|
|
195
742
|
});
|
|
743
|
+
await this.handleSaveToastOrAlreadyExists();
|
|
744
|
+
await this.waitForPackageNode(applicationName, packageName);
|
|
196
745
|
}
|
|
197
746
|
/* Creates a new command application */ async createNewCmdApplication(values) {
|
|
198
747
|
const appDataId = `Applications/${values[0]}`;
|
|
199
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;
|
|
200
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]);
|
|
201
755
|
await this.page.locator(`div[data-id="${appDataId}"] .infinite-tree-node > a`).click();
|
|
202
|
-
|
|
203
|
-
await (0, $1UEKj$playwrighttest.expect)(packageNode).toBeVisible();
|
|
204
|
-
await packageNode.click();
|
|
756
|
+
await this.clickPackageNodeWithRetry(packageDataId);
|
|
205
757
|
// Open context menu
|
|
206
758
|
const packageMenu = this.page.locator(`i.context-menu-button[data-id="${packageDataId}"]`);
|
|
207
759
|
await (0, $1UEKj$playwrighttest.expect)(packageMenu).toBeVisible();
|
|
208
760
|
await packageMenu.click();
|
|
209
761
|
// Create Command application
|
|
210
|
-
await this.page.getByRole(
|
|
211
|
-
name:
|
|
762
|
+
await this.page.getByRole('link', {
|
|
763
|
+
name: 'New'
|
|
212
764
|
}).click();
|
|
213
|
-
await this.page.getByRole(
|
|
214
|
-
name:
|
|
765
|
+
await this.page.getByRole('link', {
|
|
766
|
+
name: 'cmd'
|
|
215
767
|
}).click();
|
|
216
|
-
await this.page.getByRole(
|
|
217
|
-
name:
|
|
768
|
+
await this.page.getByRole('link', {
|
|
769
|
+
name: 'Command'
|
|
218
770
|
}).click();
|
|
219
771
|
await this.page.locator('input[name="name"]').fill(values[2]);
|
|
220
772
|
await this.page.locator('textarea[name="commandLine"]').fill(values[3]);
|
|
221
|
-
|
|
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', {
|
|
222
789
|
hasText: /^Save$/
|
|
223
790
|
}).first();
|
|
224
791
|
await (0, $1UEKj$playwrighttest.expect)(saveBtn).toBeEnabled();
|
|
225
792
|
await saveBtn.click();
|
|
226
793
|
await this.page.waitForTimeout(3000);
|
|
227
|
-
|
|
228
|
-
await (0, $1UEKj$playwrighttest.expect)(toastMessage).toHaveText("Your change was saved.", {
|
|
229
|
-
timeout: 2000
|
|
230
|
-
});
|
|
794
|
+
await this.handleSaveToastOrAlreadyExists();
|
|
231
795
|
// Close dialog
|
|
232
|
-
await this.page.locator(
|
|
796
|
+
await this.page.locator('i.xl-icon.close-icon').first().click();
|
|
233
797
|
}
|
|
234
798
|
/*Deploy the application*/ async deployApp(values) {
|
|
235
799
|
const versionNode = `Applications/${values[0]}/${values[1]}`;
|
|
236
|
-
await this.
|
|
237
|
-
await this.
|
|
238
|
-
|
|
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',
|
|
239
805
|
exact: true
|
|
240
806
|
}).click();
|
|
241
807
|
await this.page.locator(`input[name="selections"][value="Environments/${values[2]}"]`).click();
|
|
242
|
-
await this.page.getByRole(
|
|
243
|
-
name:
|
|
808
|
+
await this.page.getByRole('button', {
|
|
809
|
+
name: 'Continue'
|
|
244
810
|
}).click();
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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
|
|
248
815
|
});
|
|
249
|
-
const errorIcon = this.page.locator(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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'
|
|
253
831
|
}).click();
|
|
254
|
-
|
|
255
|
-
await
|
|
256
|
-
|
|
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'
|
|
257
839
|
}).click();
|
|
840
|
+
await errorIcon.waitFor({
|
|
841
|
+
state: 'hidden',
|
|
842
|
+
timeout: 10000
|
|
843
|
+
});
|
|
258
844
|
}
|
|
259
|
-
await this.page.locator(
|
|
845
|
+
await this.page.locator('button.deploy.xl-primary').waitFor({
|
|
846
|
+
state: 'visible'
|
|
847
|
+
});
|
|
848
|
+
await this.page.locator('button.deploy.xl-primary').click();
|
|
260
849
|
//Get the task id
|
|
261
|
-
|
|
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();
|
|
262
858
|
//Verify no failure and all are done
|
|
263
|
-
const labels = this.page.locator(
|
|
859
|
+
const labels = this.page.locator('span.label');
|
|
264
860
|
await (0, $1UEKj$playwrighttest.expect)(labels).toHaveCount(3);
|
|
265
861
|
await (0, $1UEKj$playwrighttest.expect)(labels).toHaveText([
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
862
|
+
'EXECUTED',
|
|
863
|
+
'DONE',
|
|
864
|
+
'DONE'
|
|
269
865
|
]);
|
|
270
866
|
//Get the text executed
|
|
271
867
|
await this.page.locator('div[data-id="0_1_1"] .infinite-tree-node > a').click();
|
|
272
|
-
await this.page.getByTitle(
|
|
273
|
-
this.executedText = await this.page.locator(
|
|
868
|
+
await this.page.getByTitle('Execute Test Command').click();
|
|
869
|
+
this.executedText = await this.page.locator('div.step-log-body pre').nth(1).innerText();
|
|
274
870
|
//Verify in monitoring tab searching with the task id
|
|
275
871
|
await this.page.locator('div[data-id="TASK_MONITOR"] .infinite-tree-node > a').click();
|
|
276
872
|
await this.page.locator('span[data-id="DEPLOYMENT_TASKS"]').dblclick();
|
|
277
873
|
await this.page.waitForTimeout(5000);
|
|
278
874
|
await this.page.locator('div.deployment-task-filter-task-id input[placeholder="Search"]').first().fill(this.taskId);
|
|
279
|
-
const stateText = await this.page.locator(
|
|
280
|
-
(0, $1UEKj$playwrighttest.expect)(stateText.trim()).toBe(
|
|
875
|
+
const stateText = await this.page.locator('td.tasks-table-state').first().innerText();
|
|
876
|
+
(0, $1UEKj$playwrighttest.expect)(stateText.trim()).toBe('Executed');
|
|
281
877
|
//Click the row to go to deployment screen
|
|
282
|
-
await this.page.locator(
|
|
283
|
-
await this.page.locator(
|
|
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
|
+
class $179e7dbfee839d39$export$95a11cbd2815051e extends (0, $84f8d1abd29d48fd$export$cb21416f3c2fd176) {
|
|
1146
|
+
/* Helps to navigate to the Gitops Configuration page */ async navigateToGitopsConfigurationPage() {
|
|
1147
|
+
await this.openRootContextMenu('Configuration');
|
|
1148
|
+
await this.page.getByRole('link', {
|
|
1149
|
+
name: 'New'
|
|
1150
|
+
}).click();
|
|
1151
|
+
await this.page.locator('a[title="git"]').click();
|
|
1152
|
+
await this.page.getByRole('link', {
|
|
1153
|
+
name: 'GitSource'
|
|
1154
|
+
}).click();
|
|
1155
|
+
await this.ensureExplorerTreeReady();
|
|
1156
|
+
}
|
|
1157
|
+
async openNewgitops() {
|
|
1158
|
+
await this.closeModalIfPresent();
|
|
1159
|
+
await this.ensureExplorerTreeReady();
|
|
1160
|
+
const infrastructureRoot = this.page.locator('span[data-id="Configuration"]');
|
|
1161
|
+
await infrastructureRoot.scrollIntoViewIfNeeded();
|
|
1162
|
+
await infrastructureRoot.click();
|
|
1163
|
+
const contextMenu = this.page.locator('i.context-menu-button[data-id="Configuration"]');
|
|
1164
|
+
await contextMenu.scrollIntoViewIfNeeded();
|
|
1165
|
+
await contextMenu.click({
|
|
1166
|
+
force: true
|
|
1167
|
+
});
|
|
1168
|
+
await this.page.getByRole('link', {
|
|
1169
|
+
name: 'New'
|
|
1170
|
+
}).click();
|
|
1171
|
+
await this.page.locator('a[title="git"]').click();
|
|
1172
|
+
await this.page.getByRole('link', {
|
|
1173
|
+
name: 'GitSource'
|
|
1174
|
+
}).click();
|
|
1175
|
+
}
|
|
1176
|
+
async openConfigurationNewgit() {
|
|
1177
|
+
await this.openNewgitops();
|
|
1178
|
+
}
|
|
1179
|
+
/* Helps to create a gitops ci*/ async createGitopsCI(ciName, gitData) {
|
|
1180
|
+
await this.page.locator('input[name="name"]').fill(ciName);
|
|
1181
|
+
await this.page.locator('input[name="username"]').fill(gitData[0]);
|
|
1182
|
+
await this.page.locator('input[name="password"]').fill(gitData[1]);
|
|
1183
|
+
await this.page.locator('input[name="url"]').fill(gitData[2]);
|
|
1184
|
+
await this.page.getByRole('button', {
|
|
1185
|
+
name: /^Save and close$/i
|
|
1186
|
+
}).click();
|
|
1187
|
+
await this.handleSaveToastOrAlreadyExists();
|
|
284
1188
|
}
|
|
285
1189
|
}
|
|
286
1190
|
|
|
287
1191
|
|
|
288
1192
|
|
|
289
1193
|
|
|
290
|
-
class $
|
|
1194
|
+
class $b2f725a937c99e2d$export$b8a443164510a1b4 extends (0, $ae23b16684112d0b$export$2b65d1d97338f32b) {
|
|
1195
|
+
async openTab(tabLabel, paneId) {
|
|
1196
|
+
const tab = this.page.locator('.app-root .dot-sidebar .dot-list p').filter({
|
|
1197
|
+
hasText: tabLabel
|
|
1198
|
+
}).first();
|
|
1199
|
+
await tab.scrollIntoViewIfNeeded();
|
|
1200
|
+
await tab.click();
|
|
1201
|
+
await this.page.locator(`#pane-${paneId}`).waitFor({
|
|
1202
|
+
state: 'visible',
|
|
1203
|
+
timeout: 20000
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
async downloadReport(buttonLabel, paneId, selector) {
|
|
1207
|
+
const pane = this.page.locator(`#pane-${paneId}`);
|
|
1208
|
+
const exportButton = selector ? pane.locator(selector) : pane.getByRole('button', {
|
|
1209
|
+
name: buttonLabel
|
|
1210
|
+
});
|
|
1211
|
+
const fallbackButton = pane.locator('button:has-text("Export")');
|
|
1212
|
+
const downloadPromise = this.page.waitForEvent('download', {
|
|
1213
|
+
timeout: 20000
|
|
1214
|
+
});
|
|
1215
|
+
if (await exportButton.isVisible()) await exportButton.click();
|
|
1216
|
+
else await fallbackButton.first().click();
|
|
1217
|
+
return downloadPromise;
|
|
1218
|
+
}
|
|
1219
|
+
async openDashboardTab() {
|
|
1220
|
+
await this.openTab('Dashboard', 'DashboardTab');
|
|
1221
|
+
}
|
|
1222
|
+
async openDeploymentsTab() {
|
|
1223
|
+
await this.openTab('Deployments', 'DeploymentsTab');
|
|
1224
|
+
}
|
|
1225
|
+
async openControlTasksTab() {
|
|
1226
|
+
await this.openTab('Control tasks', 'ControlTasksTab');
|
|
1227
|
+
}
|
|
1228
|
+
async openAuditReportTab() {
|
|
1229
|
+
await this.openTab('Audit report', 'AuditReportTab');
|
|
1230
|
+
}
|
|
1231
|
+
async downloadDeploymentsExport() {
|
|
1232
|
+
return this.downloadReport('Export report', 'DeploymentsTab');
|
|
1233
|
+
}
|
|
1234
|
+
async downloadControlTasksExport() {
|
|
1235
|
+
return this.downloadReport('Export report', 'ControlTasksTab');
|
|
1236
|
+
}
|
|
1237
|
+
async downloadAuditReportExport() {
|
|
1238
|
+
return this.downloadReport('Export report', 'AuditReportTab');
|
|
1239
|
+
}
|
|
291
1240
|
/* Clicks on the Deployment Tasks section */ async clickDeploymentTasks() {
|
|
292
|
-
await this.page.locator(
|
|
293
|
-
hasText:
|
|
1241
|
+
await this.page.locator('p.MuiTypography-root', {
|
|
1242
|
+
hasText: 'Deployments'
|
|
294
1243
|
}).click();
|
|
295
1244
|
}
|
|
296
1245
|
async verifyTheDeploymentValues(values) {
|
|
297
1246
|
await this.page.locator('div.deployment-task-filter-task-id input[placeholder="Search"]:visible').first().fill(values[0]);
|
|
298
|
-
|
|
1247
|
+
const escapedApp = values[2].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1248
|
+
const escapedPkg = values[3].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1249
|
+
const packagePattern = new RegExp(`${escapedApp}/${escapedPkg}`, 'i');
|
|
1250
|
+
const row = this.page.locator('table.table-striped tbody tr').filter({
|
|
1251
|
+
has: this.page.locator('td.report-table-package .deployment-report-package', {
|
|
1252
|
+
hasText: packagePattern
|
|
1253
|
+
})
|
|
1254
|
+
}).first();
|
|
1255
|
+
await row.waitFor({
|
|
1256
|
+
state: 'visible',
|
|
299
1257
|
timeout: 5000
|
|
300
1258
|
});
|
|
301
|
-
|
|
302
|
-
await (0, $1UEKj$playwrighttest.expect)(
|
|
303
|
-
await (0, $1UEKj$playwrighttest.expect)(
|
|
1259
|
+
const packageCell = row.locator('td.report-table-package .deployment-report-package').first();
|
|
1260
|
+
await (0, $1UEKj$playwrighttest.expect)(packageCell).toContainText(packagePattern);
|
|
1261
|
+
await (0, $1UEKj$playwrighttest.expect)(row.locator('td.report-table-originalType .deployment-report-task-type')).toHaveText(/Initial/i);
|
|
1262
|
+
await (0, $1UEKj$playwrighttest.expect)(row.locator('td.report-table-user .deployment-report-user')).toHaveText('admin');
|
|
304
1263
|
//Click the row
|
|
305
|
-
await
|
|
1264
|
+
await packageCell.dblclick();
|
|
306
1265
|
await this.page.locator('div[data-id="0_1_1"] .infinite-tree-node > a').click();
|
|
307
|
-
await this.page.getByTitle(
|
|
1266
|
+
await this.page.getByTitle('Execute Test Command').click();
|
|
308
1267
|
//Get the log text
|
|
309
|
-
const text = await this.page.locator(
|
|
1268
|
+
const text = await this.page.locator('div.step-log-body pre').nth(1).innerText();
|
|
310
1269
|
//Verify both the log text are same
|
|
311
1270
|
(0, $1UEKj$playwrighttest.expect)(text).toContain(values[1]);
|
|
312
1271
|
}
|
|
313
1272
|
}
|
|
314
1273
|
|
|
315
1274
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
this.
|
|
321
|
-
this.overthereLocalHostPage = new (0, $647b67e126f2dbe0$export$c88e1d16f82516d)(page);
|
|
322
|
-
this.environmentsPage = new (0, $64bc587a017c1e89$export$12530425267834a9)(page);
|
|
323
|
-
this.applicationsPage = new (0, $408106958eb335da$export$1533b625ec0c75e2)(page, this);
|
|
324
|
-
this.reportsPage = new (0, $fea68740c371df96$export$b8a443164510a1b4)(page);
|
|
1275
|
+
|
|
1276
|
+
|
|
1277
|
+
class $1b52322f0063c4bc$export$5c1198389e139c11 extends (0, $ae23b16684112d0b$export$2b65d1d97338f32b) {
|
|
1278
|
+
getPane() {
|
|
1279
|
+
return this.page.locator('#pane-ControlTasksTab');
|
|
325
1280
|
}
|
|
326
|
-
|
|
327
|
-
|
|
1281
|
+
getPagination() {
|
|
1282
|
+
return this.getPane().locator('.react-bootstrap-table-pagination');
|
|
328
1283
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
hasText: "User management"
|
|
332
|
-
}).click();
|
|
1284
|
+
getTableBody() {
|
|
1285
|
+
return this.getPane().locator('.react-bootstrap-table tbody');
|
|
333
1286
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
await collapseButton.click();
|
|
1287
|
+
getLastUpdated() {
|
|
1288
|
+
return this.getPane().locator('.last-updated-component');
|
|
337
1289
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
await
|
|
342
|
-
await this.page.
|
|
343
|
-
|
|
344
|
-
}).
|
|
345
|
-
await this.page.getByRole("link", {
|
|
346
|
-
name: "Application"
|
|
347
|
-
}).click();
|
|
1290
|
+
// ── Navigation ───────────────────────────────────────────────────────────
|
|
1291
|
+
async verifySelected() {
|
|
1292
|
+
const pane = this.getPane();
|
|
1293
|
+
await (0, $1UEKj$playwrighttest.expect)(pane).toBeVisible();
|
|
1294
|
+
await (0, $1UEKj$playwrighttest.expect)(this.page.locator('.dot-sidebar a[href*="controltasks"], .dot-sidebar .dot-list p', {
|
|
1295
|
+
hasText: 'Control tasks'
|
|
1296
|
+
}).first()).toBeVisible();
|
|
348
1297
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
await
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
await
|
|
357
|
-
|
|
358
|
-
|
|
1298
|
+
// ── Filters ───────────────────────────────────────────────────────────────
|
|
1299
|
+
async verifyFiltersVisible() {
|
|
1300
|
+
const pane = this.getPane();
|
|
1301
|
+
const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
|
|
1302
|
+
await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
|
|
1303
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.control-task-filter-targets .react-tagsinput-input')).toBeVisible();
|
|
1304
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.control-task-filter-task-name input[placeholder="Search"]')).toBeVisible();
|
|
1305
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.control-task-id input[placeholder="Search"]')).toBeVisible();
|
|
1306
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.control-task-filter-start-date')).toBeVisible();
|
|
1307
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.control-task-filter-end-date')).toBeVisible();
|
|
1308
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.control-task-filter-worker input[placeholder="Search"]')).toBeVisible();
|
|
1309
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.control-task-filter-state .react-tagsinput-input')).toBeVisible();
|
|
359
1310
|
}
|
|
360
|
-
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
await
|
|
369
|
-
name: "overthere"
|
|
370
|
-
}).click();
|
|
371
|
-
await this.page.getByRole("link", {
|
|
372
|
-
name: "LocalHost"
|
|
373
|
-
}).click();
|
|
1311
|
+
async hideFilters() {
|
|
1312
|
+
const pane = this.getPane();
|
|
1313
|
+
const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
|
|
1314
|
+
const hideFiltersButton = pane.getByRole('button', {
|
|
1315
|
+
name: 'Hide filters'
|
|
1316
|
+
});
|
|
1317
|
+
await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
|
|
1318
|
+
await hideFiltersButton.click();
|
|
1319
|
+
await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeHidden();
|
|
374
1320
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
1321
|
+
async showFilters() {
|
|
1322
|
+
const pane = this.getPane();
|
|
1323
|
+
const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
|
|
1324
|
+
const showFiltersButton = pane.getByRole('button', {
|
|
1325
|
+
name: 'Show filters'
|
|
1326
|
+
});
|
|
1327
|
+
if (await filtersPanel.isVisible()) return;
|
|
1328
|
+
for(let attempt = 0; attempt < 3; attempt += 1){
|
|
1329
|
+
if (await showFiltersButton.isVisible()) await showFiltersButton.click({
|
|
1330
|
+
force: true
|
|
1331
|
+
});
|
|
1332
|
+
if (await filtersPanel.isVisible()) return;
|
|
1333
|
+
await this.page.waitForTimeout(300);
|
|
1334
|
+
}
|
|
1335
|
+
await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
|
|
1336
|
+
}
|
|
1337
|
+
async ensureFiltersVisible() {
|
|
1338
|
+
const pane = this.getPane();
|
|
1339
|
+
const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
|
|
1340
|
+
if (await filtersPanel.isVisible()) return;
|
|
1341
|
+
const showFiltersButton = pane.getByRole('button', {
|
|
1342
|
+
name: 'Show filters'
|
|
1343
|
+
});
|
|
1344
|
+
if (await showFiltersButton.isVisible()) await showFiltersButton.click({
|
|
1345
|
+
force: true
|
|
1346
|
+
});
|
|
1347
|
+
await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
|
|
1348
|
+
}
|
|
1349
|
+
// ── Date filters ─────────────────────────────────────────────────────────
|
|
1350
|
+
async setStartDate(value) {
|
|
1351
|
+
const field = this.getPane().locator('.control-task-filter-start-date');
|
|
1352
|
+
await field.click();
|
|
1353
|
+
await field.fill(value);
|
|
1354
|
+
await this.getLastUpdated().click({
|
|
1355
|
+
force: true
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
async setEndDate(value) {
|
|
1359
|
+
const field = this.getPane().locator('.control-task-filter-end-date');
|
|
1360
|
+
await field.click();
|
|
1361
|
+
await field.fill(value);
|
|
1362
|
+
await this.getLastUpdated().click({
|
|
1363
|
+
force: true
|
|
1364
|
+
});
|
|
1365
|
+
}
|
|
1366
|
+
async expectStartDateValue(value) {
|
|
1367
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPane().locator('.control-task-filter-start-date')).toHaveValue(value);
|
|
1368
|
+
}
|
|
1369
|
+
async expectEndDateValue(value) {
|
|
1370
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPane().locator('.control-task-filter-end-date')).toHaveValue(value);
|
|
1371
|
+
}
|
|
1372
|
+
async checkInvalidStartDateError() {
|
|
1373
|
+
const field = this.getPane().locator('.control-task-filter-start-date');
|
|
1374
|
+
await field.click();
|
|
1375
|
+
await field.press('End');
|
|
1376
|
+
await field.press('a');
|
|
1377
|
+
await this.page.keyboard.press('Escape');
|
|
1378
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018').first()).toBeVisible();
|
|
1379
|
+
}
|
|
1380
|
+
async checkInvalidEndDateError() {
|
|
1381
|
+
const field = this.getPane().locator('.control-task-filter-end-date');
|
|
1382
|
+
await field.click();
|
|
1383
|
+
await field.press('End');
|
|
1384
|
+
await field.press('b');
|
|
1385
|
+
await this.page.keyboard.press('Escape');
|
|
1386
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018').first()).toBeVisible();
|
|
1387
|
+
}
|
|
1388
|
+
async expectNoStartDateError() {
|
|
1389
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018')).toHaveCount(0);
|
|
1390
|
+
}
|
|
1391
|
+
async expectNoEndDateError() {
|
|
1392
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018')).toHaveCount(0);
|
|
1393
|
+
}
|
|
1394
|
+
// ── Tag filters ───────────────────────────────────────────────────────────
|
|
1395
|
+
async selectTargetsTagsFilter(text, optionName) {
|
|
1396
|
+
await this.selectTagsFilterFirstSuggestion('.control-task-filter-targets .react-tagsinput', text, optionName);
|
|
1397
|
+
}
|
|
1398
|
+
async clearTargetsTagsFirstTag() {
|
|
1399
|
+
const remove = this.getPane().locator('.control-task-filter-targets .react-tagsinput-tag:nth-child(1) .react-tagsinput-remove');
|
|
1400
|
+
if (await remove.count()) await remove.click();
|
|
1401
|
+
}
|
|
1402
|
+
async selectStatesTagsFilter(text, optionName) {
|
|
1403
|
+
await this.selectTagsFilterFirstSuggestion('.control-task-filter-state .react-tagsinput', text, optionName);
|
|
1404
|
+
}
|
|
1405
|
+
async clearStatesTagsFirstTag() {
|
|
1406
|
+
const remove = this.getPane().locator('.control-task-filter-state .react-tagsinput-tag:nth-child(1) .react-tagsinput-remove');
|
|
1407
|
+
if (await remove.count()) await remove.click();
|
|
1408
|
+
}
|
|
1409
|
+
// ── Text filters ──────────────────────────────────────────────────────────
|
|
1410
|
+
async filterByTaskName(name) {
|
|
1411
|
+
await this.getPane().locator('.control-task-filter-task-name input[placeholder="Search"]').fill(name);
|
|
1412
|
+
}
|
|
1413
|
+
async clearTaskName() {
|
|
1414
|
+
await this.filterByTaskName('');
|
|
1415
|
+
}
|
|
1416
|
+
async filterByTaskId(id) {
|
|
1417
|
+
await this.getPane().locator('.control-task-id input[placeholder="Search"]').fill(id);
|
|
1418
|
+
}
|
|
1419
|
+
async clearTaskId() {
|
|
1420
|
+
await this.filterByTaskId('');
|
|
1421
|
+
}
|
|
1422
|
+
async filterByWorker(worker) {
|
|
1423
|
+
await this.getPane().locator('.control-task-filter-worker input[placeholder="Search"]').fill(worker);
|
|
1424
|
+
}
|
|
1425
|
+
async clearWorkerFilter() {
|
|
1426
|
+
await this.filterByWorker('');
|
|
1427
|
+
}
|
|
1428
|
+
// ── Tasks toggle ──────────────────────────────────────────────────────────
|
|
1429
|
+
async clickMyTasks() {
|
|
1430
|
+
await this.getPane().getByRole('button', {
|
|
1431
|
+
name: 'My tasks'
|
|
1432
|
+
}).click();
|
|
1433
|
+
}
|
|
1434
|
+
async clickAllTasks() {
|
|
1435
|
+
await this.getPane().getByRole('button', {
|
|
1436
|
+
name: 'All tasks'
|
|
1437
|
+
}).click();
|
|
1438
|
+
}
|
|
1439
|
+
// ── Toolbar ───────────────────────────────────────────────────────────────
|
|
1440
|
+
async clickRefresh() {
|
|
1441
|
+
await this.getPane().getByRole('button', {
|
|
1442
|
+
name: 'Refresh'
|
|
1443
|
+
}).click();
|
|
1444
|
+
}
|
|
1445
|
+
async verifyLastUpdatedAndRefresh() {
|
|
1446
|
+
const lastUpdated = this.getLastUpdated();
|
|
1447
|
+
await (0, $1UEKj$playwrighttest.expect)(lastUpdated).toBeVisible();
|
|
1448
|
+
await (0, $1UEKj$playwrighttest.expect)(lastUpdated).not.toHaveText('');
|
|
1449
|
+
await this.getPane().getByRole('button', {
|
|
1450
|
+
name: 'Refresh'
|
|
1451
|
+
}).click();
|
|
1452
|
+
await (0, $1UEKj$playwrighttest.expect)(lastUpdated).toBeVisible();
|
|
1453
|
+
}
|
|
1454
|
+
// ── Table assertions ──────────────────────────────────────────────────────
|
|
1455
|
+
async verifyTableHasRowsOrEmpty() {
|
|
1456
|
+
const pane = this.getPane();
|
|
1457
|
+
const table = pane.locator('table.table-striped');
|
|
1458
|
+
await (0, $1UEKj$playwrighttest.expect)(table).toBeVisible();
|
|
1459
|
+
const rows = this.getTableBody().locator('tr');
|
|
1460
|
+
const noDataCell = this.getTableBody().locator('td.react-bs-table-no-data');
|
|
1461
|
+
const emptyState = pane.getByText(/No report data available/i);
|
|
1462
|
+
await (0, $1UEKj$playwrighttest.expect)(async ()=>{
|
|
1463
|
+
if (await rows.count() > 0) return true;
|
|
1464
|
+
if (await noDataCell.count() > 0 && await noDataCell.first().isVisible()) return true;
|
|
1465
|
+
if (await emptyState.count() > 0 && await emptyState.first().isVisible()) return true;
|
|
1466
|
+
return false;
|
|
1467
|
+
}).toBeTruthy();
|
|
1468
|
+
}
|
|
1469
|
+
async verifyTableColumns() {
|
|
1470
|
+
const headers = this.getPane().locator('.react-bootstrap-table thead th');
|
|
1471
|
+
await (0, $1UEKj$playwrighttest.expect)(headers.filter({
|
|
1472
|
+
hasText: 'Control task name'
|
|
1473
|
+
})).toBeVisible();
|
|
1474
|
+
await (0, $1UEKj$playwrighttest.expect)(headers.filter({
|
|
1475
|
+
hasText: 'Target CI'
|
|
1476
|
+
})).toBeVisible();
|
|
1477
|
+
await (0, $1UEKj$playwrighttest.expect)(headers.filter({
|
|
1478
|
+
hasText: 'Description'
|
|
1479
|
+
})).toBeVisible();
|
|
1480
|
+
await (0, $1UEKj$playwrighttest.expect)(headers.filter({
|
|
1481
|
+
hasText: 'User'
|
|
1482
|
+
})).toBeVisible();
|
|
1483
|
+
await (0, $1UEKj$playwrighttest.expect)(headers.filter({
|
|
1484
|
+
hasText: 'State'
|
|
1485
|
+
})).toBeVisible();
|
|
1486
|
+
await (0, $1UEKj$playwrighttest.expect)(headers.filter({
|
|
1487
|
+
hasText: 'Start date'
|
|
1488
|
+
})).toBeVisible();
|
|
1489
|
+
await (0, $1UEKj$playwrighttest.expect)(headers.filter({
|
|
1490
|
+
hasText: 'End date'
|
|
1491
|
+
})).toBeVisible();
|
|
1492
|
+
await (0, $1UEKj$playwrighttest.expect)(headers.filter({
|
|
1493
|
+
hasText: 'Worker'
|
|
1494
|
+
})).toBeVisible();
|
|
1495
|
+
}
|
|
1496
|
+
async waitForTableReady() {
|
|
1497
|
+
await (0, $1UEKj$playwrighttest.expect)(async ()=>{
|
|
1498
|
+
const loading = await this.getPane().getByText('Data loading').count();
|
|
1499
|
+
(0, $1UEKj$playwrighttest.expect)(loading).toBe(0);
|
|
1500
|
+
const table = this.getPane().locator('table.table-striped');
|
|
1501
|
+
(0, $1UEKj$playwrighttest.expect)(await table.count()).toBeGreaterThan(0);
|
|
1502
|
+
}).toPass({
|
|
1503
|
+
timeout: 20000
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
async expectNumberOfRows(count) {
|
|
1507
|
+
await this.waitForTableReady();
|
|
1508
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getTableBody().locator('tr')).toHaveCount(count);
|
|
1509
|
+
}
|
|
1510
|
+
async expectAtLeastNumberOfRows(count) {
|
|
1511
|
+
await this.waitForTableReady();
|
|
1512
|
+
await (0, $1UEKj$playwrighttest.expect)(async ()=>{
|
|
1513
|
+
const total = await this.getTableBody().locator('tr').count();
|
|
1514
|
+
(0, $1UEKj$playwrighttest.expect)(total).toBeGreaterThanOrEqual(count);
|
|
1515
|
+
}).toPass({
|
|
1516
|
+
timeout: 30000
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
async expectTotal(total) {
|
|
1520
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPagination().locator('> div').filter({
|
|
1521
|
+
hasText: 'Total:'
|
|
1522
|
+
})).toContainText(`Total: ${total}`);
|
|
1523
|
+
}
|
|
1524
|
+
async expectAtLeastTotal(count) {
|
|
1525
|
+
await this.waitForTableReady();
|
|
1526
|
+
await (0, $1UEKj$playwrighttest.expect)(async ()=>{
|
|
1527
|
+
const text = await this.getPagination().locator('> div').filter({
|
|
1528
|
+
hasText: 'Total:'
|
|
1529
|
+
}).textContent();
|
|
1530
|
+
const match = text?.match(/Total:\s*(\d+)/);
|
|
1531
|
+
const total = match ? Number(match[1]) : 0;
|
|
1532
|
+
(0, $1UEKj$playwrighttest.expect)(total).toBeGreaterThanOrEqual(count);
|
|
1533
|
+
}).toPass();
|
|
1534
|
+
}
|
|
1535
|
+
async getTotalCount() {
|
|
1536
|
+
await this.waitForTableReady();
|
|
1537
|
+
const text = await this.getPagination().locator('> div').filter({
|
|
1538
|
+
hasText: 'Total:'
|
|
1539
|
+
}).textContent();
|
|
1540
|
+
const match = text?.match(/Total:\s*(\d+)/);
|
|
1541
|
+
return match ? Number(match[1]) : 0;
|
|
1542
|
+
}
|
|
1543
|
+
async expectValue(row, column, expected) {
|
|
1544
|
+
const cell = this.getTableBody().locator(`tr:nth-child(${row}) td.report-table-${column}`);
|
|
1545
|
+
await (0, $1UEKj$playwrighttest.expect)(cell).toHaveText(expected);
|
|
1546
|
+
}
|
|
1547
|
+
async expectValueContains(row, column, expected) {
|
|
1548
|
+
const cell = this.getTableBody().locator(`tr:nth-child(${row}) td.report-table-${column}`);
|
|
1549
|
+
await (0, $1UEKj$playwrighttest.expect)(cell).toContainText(expected);
|
|
1550
|
+
}
|
|
1551
|
+
async shouldHaveScrollBar() {
|
|
1552
|
+
await this.waitForTableReady();
|
|
1553
|
+
const container = this.getPane().locator('div.report');
|
|
1554
|
+
const overflowX = await container.evaluate((el)=>window.getComputedStyle(el).overflowX);
|
|
1555
|
+
(0, $1UEKj$playwrighttest.expect)([
|
|
1556
|
+
'auto',
|
|
1557
|
+
'scroll'
|
|
1558
|
+
]).toContain(overflowX);
|
|
1559
|
+
}
|
|
1560
|
+
// ── Sorting ───────────────────────────────────────────────────────────────
|
|
1561
|
+
async sortBy(column) {
|
|
1562
|
+
await this.getPane().locator(`.react-bootstrap-table thead th.report-table-${column}`).click();
|
|
1563
|
+
}
|
|
1564
|
+
async verifyIsSorted(column) {
|
|
1565
|
+
const sortOrder = this.getPane().locator(`.react-bootstrap-table thead th.report-table-${column} .react-bootstrap-table-sort-order`);
|
|
1566
|
+
await (0, $1UEKj$playwrighttest.expect)(sortOrder).toBeVisible();
|
|
1567
|
+
}
|
|
1568
|
+
async getColumnValues(column) {
|
|
1569
|
+
await this.waitForTableReady();
|
|
1570
|
+
const cells = this.getTableBody().locator(`tr td.report-table-${column}`);
|
|
1571
|
+
const count = await cells.count();
|
|
1572
|
+
const values = [];
|
|
1573
|
+
for(let i = 0; i < count; i += 1)values.push((await cells.nth(i).textContent())?.trim() ?? '');
|
|
1574
|
+
return values;
|
|
1575
|
+
}
|
|
1576
|
+
// ── Pagination ────────────────────────────────────────────────────────────
|
|
1577
|
+
async getLastPageNumber() {
|
|
1578
|
+
await this.waitForTableReady();
|
|
1579
|
+
const links = this.getPagination().getByRole('list').getByRole('link');
|
|
1580
|
+
const count = await links.count();
|
|
1581
|
+
if (count < 2) return 1;
|
|
1582
|
+
const text = await links.nth(count - 2).textContent();
|
|
1583
|
+
const num = Number(text?.trim());
|
|
1584
|
+
return Number.isFinite(num) && num > 0 ? num : 1;
|
|
1585
|
+
}
|
|
1586
|
+
async goToPage(page) {
|
|
1587
|
+
await this.getPagination().getByRole('link', {
|
|
1588
|
+
name: page.toString(),
|
|
1589
|
+
exact: true
|
|
1590
|
+
}).click();
|
|
1591
|
+
}
|
|
1592
|
+
async goToFirstPage() {
|
|
1593
|
+
const firstLink = this.getPagination().getByRole('link', {
|
|
1594
|
+
name: '<<'
|
|
1595
|
+
});
|
|
1596
|
+
if (await firstLink.count()) await firstLink.click();
|
|
1597
|
+
else await this.getPagination().getByRole('link', {
|
|
1598
|
+
name: '1',
|
|
1599
|
+
exact: true
|
|
1600
|
+
}).click();
|
|
1601
|
+
}
|
|
1602
|
+
async goToLastPage() {
|
|
1603
|
+
const lastLink = this.getPagination().getByRole('link', {
|
|
1604
|
+
name: '>>'
|
|
1605
|
+
});
|
|
1606
|
+
if (await lastLink.count()) await lastLink.click();
|
|
1607
|
+
else {
|
|
1608
|
+
const links = this.getPagination().getByRole('list').getByRole('link');
|
|
1609
|
+
const count = await links.count();
|
|
1610
|
+
await links.nth(count - 2).click();
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
async goToPreviousPage() {
|
|
1614
|
+
await this.getPagination().getByRole('link', {
|
|
1615
|
+
name: '<',
|
|
1616
|
+
exact: true
|
|
1617
|
+
}).click();
|
|
1618
|
+
}
|
|
1619
|
+
async goToNextPage() {
|
|
1620
|
+
await this.getPagination().getByRole('link', {
|
|
1621
|
+
name: '>',
|
|
1622
|
+
exact: true
|
|
1623
|
+
}).click();
|
|
1624
|
+
}
|
|
1625
|
+
async expectPage(page) {
|
|
1626
|
+
await (0, $1UEKj$playwrighttest.expect)(async ()=>{
|
|
1627
|
+
const pagination = this.getPagination();
|
|
1628
|
+
const activeLink = pagination.locator('li.active').getByRole('link', {
|
|
1629
|
+
name: page.toString(),
|
|
1630
|
+
exact: true
|
|
1631
|
+
});
|
|
1632
|
+
if (await activeLink.count() > 0) return;
|
|
1633
|
+
const ariaCurrent = pagination.locator('[aria-current="page"]').filter({
|
|
1634
|
+
hasText: page.toString()
|
|
1635
|
+
});
|
|
1636
|
+
if (await ariaCurrent.count() > 0) return;
|
|
1637
|
+
// When there is only 1 page, the pagination may not render page links at all.
|
|
1638
|
+
// In that case page 1 is implicitly active.
|
|
1639
|
+
if (page === 1) {
|
|
1640
|
+
const nextLink = pagination.getByRole('link', {
|
|
1641
|
+
name: '>',
|
|
1642
|
+
exact: true
|
|
1643
|
+
});
|
|
1644
|
+
const pageLinks = pagination.getByRole('link').filter({
|
|
1645
|
+
hasText: /^\d+$/
|
|
1646
|
+
});
|
|
1647
|
+
if (await nextLink.count() === 0 && await pageLinks.count() === 0) return;
|
|
1648
|
+
}
|
|
1649
|
+
throw new Error(`Page ${page} not active`);
|
|
1650
|
+
}).toPass({
|
|
1651
|
+
timeout: 30000
|
|
1652
|
+
});
|
|
1653
|
+
}
|
|
1654
|
+
async expectPageLinkVisible(pageNumber) {
|
|
1655
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('link', {
|
|
1656
|
+
name: pageNumber.toString(),
|
|
1657
|
+
exact: true
|
|
1658
|
+
})).toBeVisible();
|
|
1659
|
+
}
|
|
1660
|
+
async expectPaginationLinksVisible() {
|
|
1661
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('link', {
|
|
1662
|
+
name: '>',
|
|
1663
|
+
exact: true
|
|
1664
|
+
})).toBeVisible();
|
|
1665
|
+
}
|
|
1666
|
+
async expectPaginationLinksHidden() {
|
|
1667
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('link', {
|
|
1668
|
+
name: '>',
|
|
1669
|
+
exact: true
|
|
1670
|
+
})).toHaveCount(0);
|
|
1671
|
+
}
|
|
1672
|
+
async setPageSize(size) {
|
|
1673
|
+
await this.openPageSizeMenu();
|
|
1674
|
+
await this.getPagination().getByRole('menu').getByRole('menuitem', {
|
|
1675
|
+
name: size.toString(),
|
|
1676
|
+
exact: true
|
|
1677
|
+
}).click();
|
|
1678
|
+
await this.waitForTableReady();
|
|
1679
|
+
}
|
|
1680
|
+
async expectPageSize(size) {
|
|
1681
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('button', {
|
|
1682
|
+
name: size.toString(),
|
|
1683
|
+
exact: true
|
|
1684
|
+
})).toBeVisible();
|
|
1685
|
+
}
|
|
1686
|
+
async expectPageSizes(sizes) {
|
|
1687
|
+
await this.openPageSizeMenu();
|
|
1688
|
+
const menu = this.getPagination().getByRole('menu');
|
|
1689
|
+
await (0, $1UEKj$playwrighttest.expect)(menu.getByRole('menuitem')).toHaveCount(sizes.length);
|
|
1690
|
+
for (const size of sizes)await (0, $1UEKj$playwrighttest.expect)(menu.getByRole('menuitem', {
|
|
1691
|
+
name: size.toString(),
|
|
1692
|
+
exact: true
|
|
1693
|
+
})).toBeVisible();
|
|
1694
|
+
await this.closePageSizeMenu();
|
|
1695
|
+
}
|
|
1696
|
+
// ── Private helpers ───────────────────────────────────────────────────────
|
|
1697
|
+
async openPageSizeMenu() {
|
|
1698
|
+
const toggle = this.getPagination().locator('.dropdown-toggle');
|
|
1699
|
+
await toggle.click();
|
|
1700
|
+
await this.getPagination().getByRole('menu').waitFor({
|
|
1701
|
+
state: 'visible'
|
|
1702
|
+
});
|
|
1703
|
+
}
|
|
1704
|
+
async closePageSizeMenu() {
|
|
1705
|
+
const menu = this.getPagination().getByRole('menu');
|
|
1706
|
+
if (await menu.isVisible()) await this.getPagination().locator('.dropdown-toggle').click();
|
|
1707
|
+
}
|
|
1708
|
+
async expectTagAdded(tagTitle) {
|
|
1709
|
+
const tags = this.getPane().locator('.xl-react-widget-tags .react-tagsinput-tag');
|
|
1710
|
+
const byTitle = tags.locator(`[title='${tagTitle}']`);
|
|
1711
|
+
const byContainsTitle = tags.filter({
|
|
1712
|
+
hasText: tagTitle.split('/').pop() ?? tagTitle
|
|
1713
|
+
});
|
|
1714
|
+
if (await byTitle.count() > 0) await (0, $1UEKj$playwrighttest.expect)(byTitle.first()).toBeVisible();
|
|
1715
|
+
else await (0, $1UEKj$playwrighttest.expect)(byContainsTitle.first()).toBeVisible();
|
|
1716
|
+
}
|
|
1717
|
+
async selectTagsFilterFirstSuggestion(filterSelector, text, optionName) {
|
|
1718
|
+
const pane = this.getPane();
|
|
1719
|
+
const filter = pane.locator(filterSelector);
|
|
1720
|
+
const input = filter.locator('.xl-react-widget-autocomplete .react-tagsinput-input');
|
|
1721
|
+
await input.click({
|
|
1722
|
+
force: true
|
|
1723
|
+
});
|
|
1724
|
+
await input.fill(text);
|
|
1725
|
+
const suggestionList = pane.locator('.xl-react-autocomplete-suggestion-list');
|
|
1726
|
+
await (0, $1UEKj$playwrighttest.expect)(suggestionList).toBeVisible();
|
|
1727
|
+
const optionByTitle = suggestionList.locator(`.yt-option[title="${optionName}"]`);
|
|
1728
|
+
const optionByText = suggestionList.locator('.yt-option', {
|
|
1729
|
+
hasText: optionName
|
|
1730
|
+
});
|
|
1731
|
+
const gridOption = suggestionList.getByRole('rowgroup').getByText(optionName, {
|
|
1732
|
+
exact: false
|
|
1733
|
+
});
|
|
1734
|
+
let option;
|
|
1735
|
+
if (await optionByTitle.count()) option = optionByTitle.first();
|
|
1736
|
+
else if (await optionByText.count()) option = optionByText.first();
|
|
1737
|
+
else option = gridOption.first();
|
|
1738
|
+
await (0, $1UEKj$playwrighttest.expect)(option).toBeVisible();
|
|
1739
|
+
const iconCheckbox = option.locator('i.xl-icon.icon-s.checkbox');
|
|
1740
|
+
if (await iconCheckbox.count()) await iconCheckbox.click();
|
|
1741
|
+
else await option.click();
|
|
1742
|
+
await this.page.locator('body').click({
|
|
1743
|
+
position: {
|
|
1744
|
+
x: 10,
|
|
1745
|
+
y: 10
|
|
1746
|
+
}
|
|
1747
|
+
});
|
|
1748
|
+
await this.expectTagAdded(optionName);
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
|
|
1753
|
+
|
|
1754
|
+
|
|
1755
|
+
class $c8a7aeb49da7ed26$export$a3e02ac2d0adbb20 extends (0, $ae23b16684112d0b$export$2b65d1d97338f32b) {
|
|
1756
|
+
async downloadReport() {
|
|
1757
|
+
const pane = this.page.locator('#pane-DashboardTab');
|
|
1758
|
+
const exportButton = pane.locator('button.export-audit-report');
|
|
1759
|
+
const fallbackButton = pane.locator('button:has-text("Export")');
|
|
1760
|
+
const downloadPromise = this.page.waitForEvent('download', {
|
|
1761
|
+
timeout: 20000
|
|
1762
|
+
});
|
|
1763
|
+
if (await exportButton.isVisible()) await exportButton.click();
|
|
1764
|
+
else await fallbackButton.first().click();
|
|
1765
|
+
return downloadPromise;
|
|
1766
|
+
}
|
|
1767
|
+
async downloadDashboardExport() {
|
|
1768
|
+
return this.downloadReport();
|
|
1769
|
+
}
|
|
1770
|
+
async verifySummarySection() {
|
|
1771
|
+
const pane = this.page.locator('#pane-DashboardTab');
|
|
1772
|
+
const summary = pane.locator('.statistics-block');
|
|
1773
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.getByText('Usage summary for current month', {
|
|
1774
|
+
exact: false
|
|
1775
|
+
})).toBeVisible();
|
|
1776
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('button:has-text("Export")')).toBeVisible();
|
|
1777
|
+
await (0, $1UEKj$playwrighttest.expect)(summary.locator('.tile-title[title="Successful deployments"]')).toBeVisible();
|
|
1778
|
+
await (0, $1UEKj$playwrighttest.expect)(summary.locator('.tile-title[title="Retried deployments"]')).toBeVisible();
|
|
1779
|
+
await (0, $1UEKj$playwrighttest.expect)(summary.locator('.tile-title[title="Rollback deployments"]')).toBeVisible();
|
|
1780
|
+
await (0, $1UEKj$playwrighttest.expect)(summary.locator('.tile-title[title="Aborted deployments"]')).toBeVisible();
|
|
1781
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.getByText('Deployment status overview', {
|
|
1782
|
+
exact: true
|
|
1783
|
+
})).toBeVisible();
|
|
1784
|
+
}
|
|
1785
|
+
async verifyLast30DaysSection() {
|
|
1786
|
+
const pane = this.page.locator('#pane-DashboardTab');
|
|
1787
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('h1.time-period-text', {
|
|
1788
|
+
hasText: /Last 30 days/i
|
|
1789
|
+
})).toBeVisible();
|
|
1790
|
+
const successCard = pane.getByText('Top 10 successful deployments', {
|
|
1791
|
+
exact: true
|
|
1792
|
+
}).first().locator('..');
|
|
1793
|
+
const retriedCard = pane.getByText('Top 10 retried deployments', {
|
|
1794
|
+
exact: true
|
|
1795
|
+
}).first().locator('..');
|
|
1796
|
+
const longestCard = pane.getByText('Top 10 longest deployments', {
|
|
1797
|
+
exact: true
|
|
1798
|
+
}).first().locator('..');
|
|
1799
|
+
await this.expectCardHasDataOrEmpty(successCard);
|
|
1800
|
+
await this.expectCardHasDataOrEmpty(retriedCard);
|
|
1801
|
+
await this.expectCardHasDataOrEmpty(longestCard);
|
|
1802
|
+
}
|
|
1803
|
+
async verifySelected() {
|
|
1804
|
+
const pane = this.page.locator('#pane-DashboardTab');
|
|
1805
|
+
await (0, $1UEKj$playwrighttest.expect)(pane).toBeVisible();
|
|
1806
|
+
await (0, $1UEKj$playwrighttest.expect)(this.page.getByText('Dashboard', {
|
|
1807
|
+
exact: true
|
|
1808
|
+
})).toBeVisible();
|
|
1809
|
+
}
|
|
1810
|
+
async verifyLastUpdatedAndRefresh() {
|
|
1811
|
+
const pane = this.page.locator('#pane-DashboardTab');
|
|
1812
|
+
const lastUpdated = pane.locator('.last-updated-component');
|
|
1813
|
+
await (0, $1UEKj$playwrighttest.expect)(lastUpdated).toBeVisible();
|
|
1814
|
+
await (0, $1UEKj$playwrighttest.expect)(lastUpdated).not.toHaveText('');
|
|
1815
|
+
const refreshButton = pane.getByRole('button', {
|
|
1816
|
+
name: 'Refresh'
|
|
1817
|
+
});
|
|
1818
|
+
await refreshButton.click();
|
|
1819
|
+
await (0, $1UEKj$playwrighttest.expect)(lastUpdated).toBeVisible();
|
|
1820
|
+
}
|
|
1821
|
+
async verifyDateRangeDefault() {
|
|
1822
|
+
const pane = this.page.locator('#pane-DashboardTab');
|
|
1823
|
+
const dateRangeInput = pane.locator('#date-range-input');
|
|
1824
|
+
await (0, $1UEKj$playwrighttest.expect)(dateRangeInput).toHaveValue('Current month');
|
|
1825
|
+
}
|
|
1826
|
+
async selectDateRange(optionLabel) {
|
|
1827
|
+
const pane = this.page.locator('#pane-DashboardTab');
|
|
1828
|
+
const dateRangeInput = pane.locator('#date-range-input');
|
|
1829
|
+
try {
|
|
1830
|
+
await dateRangeInput.selectOption({
|
|
1831
|
+
label: optionLabel
|
|
1832
|
+
});
|
|
1833
|
+
return;
|
|
1834
|
+
} catch {
|
|
1835
|
+
// Fall back to clicking the open dropdown list.
|
|
1836
|
+
}
|
|
1837
|
+
await dateRangeInput.click();
|
|
1838
|
+
const optionRole = this.page.getByRole('option', {
|
|
1839
|
+
name: optionLabel
|
|
1840
|
+
});
|
|
1841
|
+
const menuItemRole = this.page.getByRole('menuitem', {
|
|
1842
|
+
name: optionLabel
|
|
1843
|
+
});
|
|
1844
|
+
const fallbackOption = this.page.locator('li', {
|
|
1845
|
+
hasText: optionLabel
|
|
1846
|
+
});
|
|
1847
|
+
if (await optionRole.isVisible()) {
|
|
1848
|
+
await optionRole.click();
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
if (await menuItemRole.isVisible()) {
|
|
1852
|
+
await menuItemRole.click();
|
|
1853
|
+
return;
|
|
1854
|
+
}
|
|
1855
|
+
await fallbackOption.first().click();
|
|
1856
|
+
}
|
|
1857
|
+
async verifyDateRangeHeaderContains(label) {
|
|
1858
|
+
const pane = this.page.locator('#pane-DashboardTab');
|
|
1859
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.title-header', {
|
|
1860
|
+
hasText: label
|
|
1861
|
+
})).toBeVisible();
|
|
1862
|
+
}
|
|
1863
|
+
async verifyOverviewChartsOrEmpty() {
|
|
1864
|
+
const pane = this.page.locator('#pane-DashboardTab');
|
|
1865
|
+
const statusOverview = pane.getByText('Deployment status overview', {
|
|
1866
|
+
exact: true
|
|
1867
|
+
}).first().locator('..');
|
|
1868
|
+
await this.expectWidgetHasChartOrEmpty(statusOverview);
|
|
1869
|
+
const numberOfDeployments = pane.getByText('Number of deployments over time', {
|
|
1870
|
+
exact: true
|
|
1871
|
+
}).first().locator('..');
|
|
1872
|
+
await this.expectWidgetHasChartOrEmpty(numberOfDeployments);
|
|
1873
|
+
const averageDuration = pane.getByText('Average deployment duration over time', {
|
|
1874
|
+
exact: true
|
|
1875
|
+
}).first().locator('..');
|
|
1876
|
+
await this.expectWidgetHasChartOrEmpty(averageDuration);
|
|
1877
|
+
}
|
|
1878
|
+
async verifyDeploymentStatusLegend() {
|
|
1879
|
+
const pane = this.page.locator('#pane-DashboardTab');
|
|
1880
|
+
const tile = pane.locator('.deployment-status-overview');
|
|
1881
|
+
await (0, $1UEKj$playwrighttest.expect)(tile).toBeVisible();
|
|
1882
|
+
const legendItems = tile.locator('.recharts-legend-item-text');
|
|
1883
|
+
if (await legendItems.count()) {
|
|
1884
|
+
await (0, $1UEKj$playwrighttest.expect)(legendItems.filter({
|
|
1885
|
+
hasText: 'Successful'
|
|
1886
|
+
}).first()).toBeVisible();
|
|
1887
|
+
await (0, $1UEKj$playwrighttest.expect)(legendItems.filter({
|
|
1888
|
+
hasText: 'Retried'
|
|
1889
|
+
}).first()).toBeVisible();
|
|
1890
|
+
await (0, $1UEKj$playwrighttest.expect)(legendItems.filter({
|
|
1891
|
+
hasText: 'Rollback'
|
|
1892
|
+
}).first()).toBeVisible();
|
|
1893
|
+
await (0, $1UEKj$playwrighttest.expect)(legendItems.filter({
|
|
1894
|
+
hasText: 'Aborted'
|
|
1895
|
+
}).first()).toBeVisible();
|
|
1896
|
+
return;
|
|
1897
|
+
}
|
|
1898
|
+
// No-data state: chart, canvas, or no-data widget must be visible
|
|
1899
|
+
const chartOrNoData = tile.locator('svg, canvas, .no-data-widget');
|
|
1900
|
+
await (0, $1UEKj$playwrighttest.expect)(chartOrNoData.first()).toBeVisible();
|
|
1901
|
+
}
|
|
1902
|
+
async verifyButtonsFocusable() {
|
|
1903
|
+
const pane = this.page.locator('#pane-DashboardTab');
|
|
1904
|
+
const exportButton = pane.getByRole('button', {
|
|
1905
|
+
name: 'Export'
|
|
1906
|
+
});
|
|
1907
|
+
const refreshButton = pane.getByRole('button', {
|
|
1908
|
+
name: 'Refresh'
|
|
1909
|
+
});
|
|
1910
|
+
await exportButton.focus();
|
|
1911
|
+
await (0, $1UEKj$playwrighttest.expect)(exportButton).toBeFocused();
|
|
1912
|
+
await refreshButton.focus();
|
|
1913
|
+
await (0, $1UEKj$playwrighttest.expect)(refreshButton).toBeFocused();
|
|
1914
|
+
}
|
|
1915
|
+
async verifyDateRangeOptions() {
|
|
1916
|
+
const pane = this.page.locator('#pane-DashboardTab');
|
|
1917
|
+
const select = pane.locator('#date-range-input');
|
|
1918
|
+
const expectedOptions = [
|
|
1919
|
+
'Current month',
|
|
1920
|
+
'Last 30 days',
|
|
1921
|
+
'Last 90 days',
|
|
1922
|
+
'Last 180 days',
|
|
1923
|
+
'Last 360 days',
|
|
1924
|
+
'Custom date range'
|
|
1925
|
+
];
|
|
1926
|
+
for (const option of expectedOptions)await (0, $1UEKj$playwrighttest.expect)(select.locator(`option:has-text("${option}")`)).toBeAttached();
|
|
1927
|
+
}
|
|
1928
|
+
async verifyLast6MonthsSection() {
|
|
1929
|
+
const pane = this.page.locator('#pane-DashboardTab');
|
|
1930
|
+
const trendBlock = pane.locator('.time-period-section.trend-block');
|
|
1931
|
+
await (0, $1UEKj$playwrighttest.expect)(trendBlock).toBeVisible();
|
|
1932
|
+
await (0, $1UEKj$playwrighttest.expect)(trendBlock.locator('h1.time-period-text')).toContainText('Last 6 months');
|
|
1933
|
+
const numDeployments = trendBlock.locator('.number-of-deployments-over-time');
|
|
1934
|
+
const avgDuration = trendBlock.locator('.average-deployment-duration-over-time');
|
|
1935
|
+
await (0, $1UEKj$playwrighttest.expect)(numDeployments).toBeVisible();
|
|
1936
|
+
await (0, $1UEKj$playwrighttest.expect)(avgDuration).toBeVisible();
|
|
1937
|
+
}
|
|
1938
|
+
async verifyKpiTileNumbers() {
|
|
1939
|
+
const pane = this.page.locator('#pane-DashboardTab');
|
|
1940
|
+
const tiles = [
|
|
1941
|
+
pane.locator('.successful.text-widget'),
|
|
1942
|
+
pane.locator('.retried.text-widget'),
|
|
1943
|
+
pane.locator('.rollback.text-widget'),
|
|
1944
|
+
pane.locator('.aborted.text-widget')
|
|
1945
|
+
];
|
|
1946
|
+
for (const tile of tiles){
|
|
1947
|
+
await (0, $1UEKj$playwrighttest.expect)(tile).toBeVisible();
|
|
1948
|
+
await (0, $1UEKj$playwrighttest.expect)(tile.locator('.number')).toBeVisible();
|
|
1949
|
+
}
|
|
1950
|
+
}
|
|
1951
|
+
async verifyCustomDateRangeInputs() {
|
|
1952
|
+
const pane = this.page.locator('#pane-DashboardTab');
|
|
1953
|
+
await pane.locator('#date-range-input').selectOption({
|
|
1954
|
+
label: 'Custom date range'
|
|
1955
|
+
});
|
|
1956
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.custom-date-range')).toBeVisible();
|
|
1957
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('#custom-range-start-date')).toBeVisible();
|
|
1958
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('#custom-range-end-date')).toBeVisible();
|
|
1959
|
+
// Restore default
|
|
1960
|
+
await pane.locator('#date-range-input').selectOption({
|
|
1961
|
+
label: 'Current month'
|
|
1962
|
+
});
|
|
1963
|
+
}
|
|
1964
|
+
async expectCardHasDataOrEmpty(card) {
|
|
1965
|
+
const tableRows = card.locator('table tbody tr');
|
|
1966
|
+
if (await tableRows.count()) {
|
|
1967
|
+
await (0, $1UEKj$playwrighttest.expect)(tableRows.first()).toBeVisible();
|
|
1968
|
+
return;
|
|
1969
|
+
}
|
|
1970
|
+
const rowData = card.locator('.row-data');
|
|
1971
|
+
if (await rowData.count()) {
|
|
1972
|
+
await (0, $1UEKj$playwrighttest.expect)(rowData.first()).toBeAttached();
|
|
1973
|
+
return;
|
|
1974
|
+
}
|
|
1975
|
+
await (0, $1UEKj$playwrighttest.expect)(card.getByText('No data available for the current period', {
|
|
1976
|
+
exact: true
|
|
1977
|
+
})).toBeVisible();
|
|
1978
|
+
}
|
|
1979
|
+
async expectWidgetHasChartOrEmpty(widget) {
|
|
1980
|
+
const chart = widget.locator('svg, canvas');
|
|
1981
|
+
const noData = widget.getByText('No data available for the current period', {
|
|
1982
|
+
exact: true
|
|
1983
|
+
});
|
|
1984
|
+
const chartVisible = await chart.first().isVisible();
|
|
1985
|
+
const noDataVisible = await noData.isVisible();
|
|
1986
|
+
(0, $1UEKj$playwrighttest.expect)(chartVisible || noDataVisible).toBeTruthy();
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
|
|
1991
|
+
|
|
1992
|
+
|
|
1993
|
+
class $50eb7c79d31afc08$export$41a993d086bafffd extends (0, $ae23b16684112d0b$export$2b65d1d97338f32b) {
|
|
1994
|
+
getPane() {
|
|
1995
|
+
return this.page.locator('#pane-DeploymentsTab');
|
|
1996
|
+
}
|
|
1997
|
+
getPagination() {
|
|
1998
|
+
return this.getPane().locator('.react-bootstrap-table-pagination');
|
|
1999
|
+
}
|
|
2000
|
+
getTableBody() {
|
|
2001
|
+
return this.getPane().locator('.react-bootstrap-table tbody');
|
|
2002
|
+
}
|
|
2003
|
+
getLastUpdated() {
|
|
2004
|
+
return this.getPane().locator('.last-updated-component');
|
|
2005
|
+
}
|
|
2006
|
+
async resolveDateField(containerSelector) {
|
|
2007
|
+
const container = this.getPane().locator(containerSelector);
|
|
2008
|
+
const input = container.locator('input');
|
|
2009
|
+
if (await input.count()) return input.first();
|
|
2010
|
+
return container;
|
|
2011
|
+
}
|
|
2012
|
+
async verifySelected() {
|
|
2013
|
+
const pane = this.getPane();
|
|
2014
|
+
await (0, $1UEKj$playwrighttest.expect)(pane).toBeVisible();
|
|
2015
|
+
await (0, $1UEKj$playwrighttest.expect)(this.page.getByText('Deployments', {
|
|
2016
|
+
exact: true
|
|
2017
|
+
})).toBeVisible();
|
|
2018
|
+
}
|
|
2019
|
+
async verifyFiltersVisible() {
|
|
2020
|
+
const pane = this.getPane();
|
|
2021
|
+
const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
|
|
2022
|
+
await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
|
|
2023
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-start-date')).toBeVisible();
|
|
2024
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-end-date')).toBeVisible();
|
|
2025
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-task-id input[placeholder="Search"]')).toBeVisible();
|
|
2026
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-worker input[placeholder="Search"]')).toBeVisible();
|
|
2027
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-applications .react-tagsinput-input')).toBeVisible();
|
|
2028
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-environments .react-tagsinput-input')).toBeVisible();
|
|
2029
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-state .react-tagsinput-input')).toBeVisible();
|
|
2030
|
+
await (0, $1UEKj$playwrighttest.expect)(pane.locator('.deployment-task-filter-types .react-tagsinput-input')).toBeVisible();
|
|
2031
|
+
const tasksFilterInput = pane.locator('.deployment-task-filter-tasks .react-tagsinput-input');
|
|
2032
|
+
if (await tasksFilterInput.count()) await (0, $1UEKj$playwrighttest.expect)(tasksFilterInput).toBeVisible();
|
|
2033
|
+
}
|
|
2034
|
+
async verifyLastUpdatedAndRefresh() {
|
|
2035
|
+
const pane = this.getPane();
|
|
2036
|
+
const lastUpdated = this.getLastUpdated();
|
|
2037
|
+
await (0, $1UEKj$playwrighttest.expect)(lastUpdated).toBeVisible();
|
|
2038
|
+
await (0, $1UEKj$playwrighttest.expect)(lastUpdated).not.toHaveText('');
|
|
2039
|
+
const refreshButton = pane.getByRole('button', {
|
|
2040
|
+
name: 'Refresh'
|
|
2041
|
+
});
|
|
2042
|
+
await refreshButton.click();
|
|
2043
|
+
await (0, $1UEKj$playwrighttest.expect)(lastUpdated).toBeVisible();
|
|
2044
|
+
}
|
|
2045
|
+
async verifyTableHasRowsOrEmpty() {
|
|
2046
|
+
const pane = this.getPane();
|
|
2047
|
+
const table = pane.locator('table.table-striped');
|
|
2048
|
+
await (0, $1UEKj$playwrighttest.expect)(table).toBeVisible();
|
|
2049
|
+
const rows = this.getTableBody().locator('tr');
|
|
2050
|
+
const noDataCell = this.getTableBody().locator('tr td.react-bs-table-no-data');
|
|
2051
|
+
const emptyState = pane.getByText(/No data/i);
|
|
2052
|
+
await (0, $1UEKj$playwrighttest.expect)(async ()=>{
|
|
2053
|
+
if (await rows.count()) return true;
|
|
2054
|
+
if (await noDataCell.count() > 0 && await noDataCell.first().isVisible()) return true;
|
|
2055
|
+
if (await emptyState.count() > 0 && await emptyState.first().isVisible()) return true;
|
|
2056
|
+
return false;
|
|
2057
|
+
}).toBeTruthy();
|
|
2058
|
+
}
|
|
2059
|
+
async hideFilters() {
|
|
2060
|
+
const pane = this.getPane();
|
|
2061
|
+
const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
|
|
2062
|
+
const hideFiltersButton = pane.getByRole('button', {
|
|
2063
|
+
name: 'Hide filters'
|
|
2064
|
+
});
|
|
2065
|
+
await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
|
|
2066
|
+
await hideFiltersButton.click();
|
|
2067
|
+
await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeHidden();
|
|
2068
|
+
}
|
|
2069
|
+
async showFilters() {
|
|
2070
|
+
const pane = this.getPane();
|
|
2071
|
+
const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
|
|
2072
|
+
const showFiltersButton = pane.getByRole('button', {
|
|
2073
|
+
name: 'Show filters'
|
|
2074
|
+
});
|
|
2075
|
+
if (await filtersPanel.isVisible()) return;
|
|
2076
|
+
for(let attempt = 0; attempt < 3; attempt += 1){
|
|
2077
|
+
if (await showFiltersButton.isVisible()) await showFiltersButton.click({
|
|
2078
|
+
force: true
|
|
2079
|
+
});
|
|
2080
|
+
if (await filtersPanel.isVisible()) return;
|
|
2081
|
+
await this.page.waitForTimeout(300);
|
|
2082
|
+
}
|
|
2083
|
+
await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
|
|
2084
|
+
}
|
|
2085
|
+
async ensureFiltersVisible() {
|
|
2086
|
+
const pane = this.getPane();
|
|
2087
|
+
const filtersPanel = pane.locator('.xl-react-redux-table-filters-panel');
|
|
2088
|
+
if (await filtersPanel.isVisible()) return;
|
|
2089
|
+
const showFiltersButton = pane.getByRole('button', {
|
|
2090
|
+
name: 'Show filters'
|
|
2091
|
+
});
|
|
2092
|
+
if (await showFiltersButton.isVisible()) await showFiltersButton.click({
|
|
2093
|
+
force: true
|
|
2094
|
+
});
|
|
2095
|
+
await (0, $1UEKj$playwrighttest.expect)(filtersPanel).toBeVisible();
|
|
2096
|
+
}
|
|
2097
|
+
async clickRefresh() {
|
|
2098
|
+
const pane = this.getPane();
|
|
2099
|
+
await pane.getByRole('button', {
|
|
2100
|
+
name: 'Refresh'
|
|
2101
|
+
}).click();
|
|
2102
|
+
}
|
|
2103
|
+
async expectTotal(total) {
|
|
2104
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPagination().locator('> div').filter({
|
|
2105
|
+
hasText: 'Total:'
|
|
2106
|
+
})).toContainText(`Total: ${total}`);
|
|
2107
|
+
}
|
|
2108
|
+
async expectNumberOfRows(count) {
|
|
2109
|
+
await this.waitForTableReady();
|
|
2110
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getTableBody().locator('tr')).toHaveCount(count);
|
|
2111
|
+
}
|
|
2112
|
+
async expectAtLeastNumberOfRows(count) {
|
|
2113
|
+
await this.waitForTableReady();
|
|
2114
|
+
await (0, $1UEKj$playwrighttest.expect)(async ()=>{
|
|
2115
|
+
const total = await this.getTableBody().locator('tr').count();
|
|
2116
|
+
(0, $1UEKj$playwrighttest.expect)(total).toBeGreaterThanOrEqual(count);
|
|
2117
|
+
}).toPass({
|
|
2118
|
+
timeout: 30000
|
|
2119
|
+
});
|
|
2120
|
+
}
|
|
2121
|
+
async expectAtLeastTotal(count) {
|
|
2122
|
+
await this.waitForTableReady();
|
|
2123
|
+
await (0, $1UEKj$playwrighttest.expect)(async ()=>{
|
|
2124
|
+
const text = await this.getPagination().locator('> div').filter({
|
|
2125
|
+
hasText: 'Total:'
|
|
2126
|
+
}).textContent();
|
|
2127
|
+
const match = text?.match(/Total:\s*(\d+)/);
|
|
2128
|
+
const total = match ? Number(match[1]) : 0;
|
|
2129
|
+
(0, $1UEKj$playwrighttest.expect)(total).toBeGreaterThanOrEqual(count);
|
|
2130
|
+
}).toPass();
|
|
2131
|
+
}
|
|
2132
|
+
async expectValue(row, column, expected) {
|
|
2133
|
+
const cell = this.getTableBody().locator(`tr:nth-child(${row}) td.report-table-${column}`);
|
|
2134
|
+
await (0, $1UEKj$playwrighttest.expect)(cell).toHaveText(expected);
|
|
2135
|
+
}
|
|
2136
|
+
async expectValueContains(row, column, expected) {
|
|
2137
|
+
const cell = this.getTableBody().locator(`tr:nth-child(${row}) td.report-table-${column}`);
|
|
2138
|
+
await (0, $1UEKj$playwrighttest.expect)(cell).toContainText(expected);
|
|
2139
|
+
}
|
|
2140
|
+
async sortBy(column) {
|
|
2141
|
+
const header = this.getPane().locator(`.react-bootstrap-table thead th.report-table-${column}`);
|
|
2142
|
+
await header.click();
|
|
2143
|
+
}
|
|
2144
|
+
async isAscendingSorted(column) {
|
|
2145
|
+
const sortOrder = this.getPane().locator(`.react-bootstrap-table thead th.report-table-${column} .react-bootstrap-table-sort-order`);
|
|
2146
|
+
await (0, $1UEKj$playwrighttest.expect)(sortOrder).toBeVisible();
|
|
2147
|
+
}
|
|
2148
|
+
async getColumnValues(column) {
|
|
2149
|
+
await this.waitForTableReady();
|
|
2150
|
+
const cells = this.getTableBody().locator(`tr td.report-table-${column}`);
|
|
2151
|
+
const count = await cells.count();
|
|
2152
|
+
const values = [];
|
|
2153
|
+
for(let i = 0; i < count; i += 1)values.push((await cells.nth(i).textContent())?.trim() ?? '');
|
|
2154
|
+
return values;
|
|
2155
|
+
}
|
|
2156
|
+
async getLastPageNumber() {
|
|
2157
|
+
await this.waitForTableReady();
|
|
2158
|
+
const links = this.getPagination().getByRole('list').getByRole('link');
|
|
2159
|
+
const count = await links.count();
|
|
2160
|
+
if (count < 2) return 1;
|
|
2161
|
+
// links order on page 1: [1, 2, ..., n, >] — last numeric is nth(count-2)
|
|
2162
|
+
const text = await links.nth(count - 2).textContent();
|
|
2163
|
+
const num = Number(text?.trim());
|
|
2164
|
+
return Number.isFinite(num) && num > 0 ? num : 1;
|
|
2165
|
+
}
|
|
2166
|
+
async getTotalCount() {
|
|
2167
|
+
await this.waitForTableReady();
|
|
2168
|
+
const text = await this.getPagination().locator('> div').filter({
|
|
2169
|
+
hasText: 'Total:'
|
|
2170
|
+
}).textContent();
|
|
2171
|
+
const match = text?.match(/Total:\s*(\d+)/);
|
|
2172
|
+
return match ? Number(match[1]) : 0;
|
|
2173
|
+
}
|
|
2174
|
+
async expectPage(page) {
|
|
2175
|
+
await (0, $1UEKj$playwrighttest.expect)(async ()=>{
|
|
2176
|
+
const pagination = this.getPagination();
|
|
2177
|
+
// primary: react-bootstrap li.active with child link
|
|
2178
|
+
const activeLink = pagination.locator('li.active').getByRole('link', {
|
|
2179
|
+
name: page.toString(),
|
|
2180
|
+
exact: true
|
|
2181
|
+
});
|
|
2182
|
+
if (await activeLink.count() > 0) return;
|
|
2183
|
+
// fallback: aria-current="page"
|
|
2184
|
+
const ariaCurrent = pagination.locator('[aria-current="page"]').filter({
|
|
2185
|
+
hasText: page.toString()
|
|
2186
|
+
});
|
|
2187
|
+
if (await ariaCurrent.count() > 0) return;
|
|
2188
|
+
throw new Error(`Page ${page} not active`);
|
|
2189
|
+
}).toPass({
|
|
2190
|
+
timeout: 30000
|
|
2191
|
+
});
|
|
2192
|
+
}
|
|
2193
|
+
async goToPage(page) {
|
|
2194
|
+
await this.getPagination().getByRole('link', {
|
|
2195
|
+
name: page.toString()
|
|
2196
|
+
}).click();
|
|
2197
|
+
}
|
|
2198
|
+
async goToFirstPage() {
|
|
2199
|
+
const firstLink = this.getPagination().getByRole('link', {
|
|
2200
|
+
name: '<<'
|
|
2201
|
+
});
|
|
2202
|
+
if (await firstLink.count()) await firstLink.click();
|
|
2203
|
+
else await this.getPagination().getByRole('link', {
|
|
2204
|
+
name: '1',
|
|
2205
|
+
exact: true
|
|
2206
|
+
}).click();
|
|
2207
|
+
}
|
|
2208
|
+
async goToLastPage() {
|
|
2209
|
+
const lastLink = this.getPagination().getByRole('link', {
|
|
2210
|
+
name: '>>'
|
|
2211
|
+
});
|
|
2212
|
+
if (await lastLink.count()) await lastLink.click();
|
|
2213
|
+
else {
|
|
2214
|
+
const links = this.getPagination().getByRole('list').getByRole('link');
|
|
2215
|
+
const count = await links.count();
|
|
2216
|
+
// last numeric link is second-to-last (last is '>')
|
|
2217
|
+
await links.nth(count - 2).click();
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
async goToPreviousPage() {
|
|
2221
|
+
await this.getPagination().getByRole('link', {
|
|
2222
|
+
name: '<',
|
|
2223
|
+
exact: true
|
|
2224
|
+
}).click();
|
|
2225
|
+
}
|
|
2226
|
+
async goToNextPage() {
|
|
2227
|
+
await this.getPagination().getByRole('link', {
|
|
2228
|
+
name: '>',
|
|
2229
|
+
exact: true
|
|
2230
|
+
}).click();
|
|
2231
|
+
}
|
|
2232
|
+
async expectPageLinkVisible(pageNumber) {
|
|
2233
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('link', {
|
|
2234
|
+
name: pageNumber.toString(),
|
|
2235
|
+
exact: true
|
|
2236
|
+
})).toBeVisible();
|
|
2237
|
+
}
|
|
2238
|
+
async expectPaginationLinksVisible() {
|
|
2239
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('link', {
|
|
2240
|
+
name: '>',
|
|
2241
|
+
exact: true
|
|
2242
|
+
})).toBeVisible();
|
|
2243
|
+
}
|
|
2244
|
+
async expectPaginationLinksHidden() {
|
|
2245
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('link', {
|
|
2246
|
+
name: '>',
|
|
2247
|
+
exact: true
|
|
2248
|
+
})).toHaveCount(0);
|
|
2249
|
+
}
|
|
2250
|
+
async expectPageSize(size) {
|
|
2251
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('button', {
|
|
2252
|
+
name: size.toString(),
|
|
2253
|
+
exact: true
|
|
2254
|
+
})).toBeVisible();
|
|
2255
|
+
}
|
|
2256
|
+
async expectPageSizes(sizes) {
|
|
2257
|
+
await this.openPageSizeMenu();
|
|
2258
|
+
const menu = this.getPagination().getByRole('menu');
|
|
2259
|
+
await (0, $1UEKj$playwrighttest.expect)(menu.getByRole('menuitem')).toHaveCount(sizes.length);
|
|
2260
|
+
for (const size of sizes)await (0, $1UEKj$playwrighttest.expect)(menu.getByRole('menuitem', {
|
|
2261
|
+
name: size.toString(),
|
|
2262
|
+
exact: true
|
|
2263
|
+
})).toBeVisible();
|
|
2264
|
+
await this.closePageSizeMenu();
|
|
2265
|
+
}
|
|
2266
|
+
async setPageSize(size) {
|
|
2267
|
+
await this.openPageSizeMenu();
|
|
2268
|
+
await this.getPagination().getByRole('menu').getByRole('menuitem', {
|
|
2269
|
+
name: size.toString(),
|
|
2270
|
+
exact: true
|
|
2271
|
+
}).click();
|
|
2272
|
+
await this.waitForTableReady();
|
|
2273
|
+
}
|
|
2274
|
+
async shouldHaveScrollBar() {
|
|
2275
|
+
await this.waitForTableReady();
|
|
2276
|
+
const container = this.getPane().locator('div.report');
|
|
2277
|
+
const overflowX = await container.evaluate((el)=>window.getComputedStyle(el).overflowX);
|
|
2278
|
+
(0, $1UEKj$playwrighttest.expect)([
|
|
2279
|
+
'auto',
|
|
2280
|
+
'scroll'
|
|
2281
|
+
]).toContain(overflowX);
|
|
2282
|
+
}
|
|
2283
|
+
async setStartDate(value) {
|
|
2284
|
+
const field = await this.resolveDateField('.deployment-task-filter-start-date');
|
|
2285
|
+
await field.click();
|
|
2286
|
+
await field.fill(value);
|
|
2287
|
+
await this.getLastUpdated().click({
|
|
2288
|
+
force: true
|
|
2289
|
+
});
|
|
2290
|
+
}
|
|
2291
|
+
async setEndDate(value) {
|
|
2292
|
+
const field = await this.resolveDateField('.deployment-task-filter-end-date');
|
|
2293
|
+
await field.click();
|
|
2294
|
+
await field.fill(value);
|
|
2295
|
+
await this.getLastUpdated().click({
|
|
2296
|
+
force: true
|
|
2297
|
+
});
|
|
2298
|
+
}
|
|
2299
|
+
async expectStartDateValue(value) {
|
|
2300
|
+
await this.expectDateFieldValue('.deployment-task-filter-start-date', value);
|
|
2301
|
+
}
|
|
2302
|
+
async expectEndDateValue(value) {
|
|
2303
|
+
await this.expectDateFieldValue('.deployment-task-filter-end-date', value);
|
|
2304
|
+
}
|
|
2305
|
+
async checkInvalidStartDateError() {
|
|
2306
|
+
const field = await this.resolveDateField('.deployment-task-filter-start-date');
|
|
2307
|
+
await field.click(); // opens calendar
|
|
2308
|
+
await field.press('End');
|
|
2309
|
+
await field.press('a'); // append invalid char
|
|
2310
|
+
await this.page.keyboard.press('Escape'); // close calendar → error appears below field
|
|
2311
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018').first()).toBeVisible();
|
|
2312
|
+
}
|
|
2313
|
+
async checkInvalidEndDateError() {
|
|
2314
|
+
const field = await this.resolveDateField('.deployment-task-filter-end-date');
|
|
2315
|
+
await field.click(); // opens calendar
|
|
2316
|
+
await field.press('End');
|
|
2317
|
+
await field.press('b'); // append invalid char
|
|
2318
|
+
await this.page.keyboard.press('Escape'); // close calendar → error appears below field
|
|
2319
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018').first()).toBeVisible();
|
|
2320
|
+
}
|
|
2321
|
+
async expectNoStartDateError() {
|
|
2322
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018')).toHaveCount(0);
|
|
2323
|
+
}
|
|
2324
|
+
async expectNoEndDateError() {
|
|
2325
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPane().getByText('Example: Dec 1, 2018')).toHaveCount(0);
|
|
2326
|
+
}
|
|
2327
|
+
async selectRadioFilter(label) {
|
|
2328
|
+
await this.ensureFiltersVisible();
|
|
2329
|
+
const pane = this.getPane();
|
|
2330
|
+
const radioGroup = pane.locator('.xl-react-widget-radio-group');
|
|
2331
|
+
const labelLocator = radioGroup.locator(`label[data='${label}']`);
|
|
2332
|
+
if (await labelLocator.count()) {
|
|
2333
|
+
await labelLocator.click();
|
|
2334
|
+
return;
|
|
2335
|
+
}
|
|
2336
|
+
const fallbackByText = radioGroup.getByText(label, {
|
|
2337
|
+
exact: true
|
|
2338
|
+
});
|
|
2339
|
+
if (await fallbackByText.count()) {
|
|
2340
|
+
await fallbackByText.click();
|
|
2341
|
+
return;
|
|
2342
|
+
}
|
|
2343
|
+
const ariaRadio = pane.locator('[role="radio"]', {
|
|
2344
|
+
hasText: label
|
|
2345
|
+
});
|
|
2346
|
+
if (await ariaRadio.count()) await ariaRadio.first().click();
|
|
2347
|
+
}
|
|
2348
|
+
async expectApplicationsRadioSelected() {
|
|
2349
|
+
const radio = this.getPane().locator('.xl-react-widget-radio-group label[data="Application(s)"]');
|
|
2350
|
+
if (await radio.count() === 0) return;
|
|
2351
|
+
await (0, $1UEKj$playwrighttest.expect)(radio).toHaveAttribute('style', /background-color/);
|
|
2352
|
+
}
|
|
2353
|
+
async selectTagsFilterApplications(text, optionName) {
|
|
2354
|
+
await this.selectTagsFilterFirstSuggestion('.deployment-task-filter-applications .react-tagsinput', text, optionName);
|
|
2355
|
+
}
|
|
2356
|
+
async selectTagsFilterEnvironments(text, optionName) {
|
|
2357
|
+
await this.selectTagsFilterFirstSuggestion('.deployment-task-filter-environments .react-tagsinput', text, optionName);
|
|
2358
|
+
}
|
|
2359
|
+
async selectStatesTagsFilterFirstSuggestion(text, optionName) {
|
|
2360
|
+
await this.selectTagsFilterFirstSuggestion('.deployment-task-filter-state .react-tagsinput', text, optionName);
|
|
2361
|
+
}
|
|
2362
|
+
async clearStatesTagsFirstTag() {
|
|
2363
|
+
const remove = this.getPane().locator('.deployment-task-filter-state .react-tagsinput-tag:nth-child(1) .react-tagsinput-remove');
|
|
2364
|
+
if (await remove.count()) await remove.click();
|
|
2365
|
+
}
|
|
2366
|
+
async clearApplicationsTagsFirstTag() {
|
|
2367
|
+
const remove = this.getPane().locator('.deployment-task-filter-applications .react-tagsinput-tag:nth-child(1) .react-tagsinput-remove');
|
|
2368
|
+
if (await remove.count()) await remove.click();
|
|
2369
|
+
}
|
|
2370
|
+
async clearEnvironmentsTagsFirstTag() {
|
|
2371
|
+
const remove = this.getPane().locator('.deployment-task-filter-environments .react-tagsinput-tag:nth-child(1) .react-tagsinput-remove');
|
|
2372
|
+
if (await remove.count()) await remove.click();
|
|
2373
|
+
}
|
|
2374
|
+
async setTaskId(taskId) {
|
|
2375
|
+
const input = this.getPane().locator('.deployment-task-filter-task-id input');
|
|
2376
|
+
await input.fill(taskId);
|
|
2377
|
+
await (0, $1UEKj$playwrighttest.expect)(input).toHaveValue(taskId);
|
|
2378
|
+
}
|
|
2379
|
+
async setWorkerName(workerName) {
|
|
2380
|
+
const input = this.getPane().locator('.deployment-task-filter-worker input');
|
|
2381
|
+
await input.fill(workerName);
|
|
2382
|
+
await (0, $1UEKj$playwrighttest.expect)(input).toHaveValue(workerName);
|
|
2383
|
+
}
|
|
2384
|
+
async clearWorkerName() {
|
|
2385
|
+
await this.setWorkerName('');
|
|
2386
|
+
}
|
|
2387
|
+
async expectTagAdded(tagTitle) {
|
|
2388
|
+
const tags = this.getPane().locator('.xl-react-widget-tags .react-tagsinput-tag');
|
|
2389
|
+
const byTitle = tags.locator(`[title='${tagTitle}']`);
|
|
2390
|
+
const byContainsTitle = tags.filter({
|
|
2391
|
+
hasText: tagTitle.split('/').pop() ?? tagTitle
|
|
2392
|
+
});
|
|
2393
|
+
const count = await byTitle.count();
|
|
2394
|
+
if (count > 0) await (0, $1UEKj$playwrighttest.expect)(byTitle.first()).toBeVisible();
|
|
2395
|
+
else await (0, $1UEKj$playwrighttest.expect)(byContainsTitle.first()).toBeVisible();
|
|
2396
|
+
}
|
|
2397
|
+
async selectTagsFilterFirstSuggestion(filterSelector, text, optionName) {
|
|
2398
|
+
const pane = this.getPane();
|
|
2399
|
+
const filter = pane.locator(filterSelector);
|
|
2400
|
+
const input = filter.locator('.xl-react-widget-autocomplete .react-tagsinput-input');
|
|
2401
|
+
await input.click({
|
|
2402
|
+
force: true
|
|
2403
|
+
});
|
|
2404
|
+
await input.fill(text);
|
|
2405
|
+
const suggestionList = pane.locator('.xl-react-autocomplete-suggestion-list');
|
|
2406
|
+
await (0, $1UEKj$playwrighttest.expect)(suggestionList).toBeVisible();
|
|
2407
|
+
// Try .yt-option first (State/Type filters), then aria grid row (Application/Environment filters)
|
|
2408
|
+
const optionByTitle = suggestionList.locator(`.yt-option[title="${optionName}"]`);
|
|
2409
|
+
const optionByText = suggestionList.locator('.yt-option', {
|
|
2410
|
+
hasText: optionName
|
|
2411
|
+
});
|
|
2412
|
+
const gridOption = suggestionList.getByRole('rowgroup').getByText(optionName, {
|
|
2413
|
+
exact: false
|
|
2414
|
+
});
|
|
2415
|
+
let option;
|
|
2416
|
+
if (await optionByTitle.count()) option = optionByTitle.first();
|
|
2417
|
+
else if (await optionByText.count()) option = optionByText.first();
|
|
2418
|
+
else option = gridOption.first();
|
|
2419
|
+
await (0, $1UEKj$playwrighttest.expect)(option).toBeVisible();
|
|
2420
|
+
const iconCheckbox = option.locator('i.xl-icon.icon-s.checkbox');
|
|
2421
|
+
if (await iconCheckbox.count()) await iconCheckbox.click();
|
|
2422
|
+
else await option.click();
|
|
2423
|
+
await this.page.locator('body').click({
|
|
2424
|
+
position: {
|
|
2425
|
+
x: 10,
|
|
2426
|
+
y: 10
|
|
2427
|
+
}
|
|
2428
|
+
});
|
|
2429
|
+
await this.expectTagAdded(optionName);
|
|
2430
|
+
}
|
|
2431
|
+
async waitForTableReady() {
|
|
2432
|
+
// Poll until loading indicator is gone AND table content (rows or empty state) is present
|
|
2433
|
+
await (0, $1UEKj$playwrighttest.expect)(async ()=>{
|
|
2434
|
+
const loading = await this.getPane().getByText('Data loading').count();
|
|
2435
|
+
if (loading > 0) throw new Error('Still loading');
|
|
2436
|
+
const rows = await this.getTableBody().locator('tr').count();
|
|
2437
|
+
const noData = await this.getTableBody().locator('td.react-bs-table-no-data').count();
|
|
2438
|
+
if (rows === 0 && noData === 0) throw new Error('No table content yet');
|
|
2439
|
+
}).toPass({
|
|
2440
|
+
timeout: 60000
|
|
2441
|
+
});
|
|
2442
|
+
}
|
|
2443
|
+
async openPageSizeMenu() {
|
|
2444
|
+
const btn = this.getPagination().getByRole('toolbar').getByRole('button');
|
|
2445
|
+
const expanded = await btn.getAttribute('aria-expanded');
|
|
2446
|
+
if (expanded !== 'true') {
|
|
2447
|
+
await btn.click();
|
|
2448
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('menu')).toBeVisible();
|
|
2449
|
+
}
|
|
2450
|
+
}
|
|
2451
|
+
async closePageSizeMenu() {
|
|
2452
|
+
const btn = this.getPagination().getByRole('toolbar').getByRole('button');
|
|
2453
|
+
const expanded = await btn.getAttribute('aria-expanded');
|
|
2454
|
+
if (expanded === 'true') {
|
|
2455
|
+
await btn.click();
|
|
2456
|
+
await (0, $1UEKj$playwrighttest.expect)(this.getPagination().getByRole('menu')).toBeHidden();
|
|
2457
|
+
}
|
|
2458
|
+
}
|
|
2459
|
+
async expectDateFieldValue(containerSelector, value) {
|
|
2460
|
+
const container = this.getPane().locator(containerSelector);
|
|
2461
|
+
const input = container.locator('input');
|
|
2462
|
+
if (await input.count() > 0) {
|
|
2463
|
+
await (0, $1UEKj$playwrighttest.expect)(input.first()).toHaveValue(value);
|
|
2464
|
+
return;
|
|
2465
|
+
}
|
|
2466
|
+
await (0, $1UEKj$playwrighttest.expect)(container).toHaveValue(value);
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2470
|
+
|
|
2471
|
+
|
|
2472
|
+
class $9204952bd68f68e5$export$e37b272c81a10693 extends (0, $84f8d1abd29d48fd$export$cb21416f3c2fd176) {
|
|
2473
|
+
/* Navigates to the Infrastructure Edit Permissions page via the tree context menu */ async navigateToPage() {
|
|
2474
|
+
await this.openRootContextMenu('Infrastructure');
|
|
2475
|
+
await this.page.getByRole('link', {
|
|
2476
|
+
name: 'Edit Permissions'
|
|
2477
|
+
}).click();
|
|
2478
|
+
}
|
|
2479
|
+
async navigateToInfraEditPermissionPage() {
|
|
2480
|
+
await this.navigateToPage();
|
|
2481
|
+
}
|
|
2482
|
+
/* Gives the permissions to a role */ async givePermissionsToRole(roleName) {
|
|
2483
|
+
const filter = this.page.getByPlaceholder('Filter by roles').first();
|
|
2484
|
+
await filter.waitFor({
|
|
2485
|
+
state: 'visible'
|
|
2486
|
+
});
|
|
2487
|
+
await filter.scrollIntoViewIfNeeded();
|
|
2488
|
+
await filter.fill(roleName);
|
|
2489
|
+
const roleRow = this.page.locator('table.table-striped tbody tr').first();
|
|
2490
|
+
const checkboxes = roleRow.locator('input[type="checkbox"]');
|
|
2491
|
+
const count = await checkboxes.count();
|
|
2492
|
+
for(let i = 0; i < count; i += 1){
|
|
2493
|
+
const checkbox = checkboxes.nth(i);
|
|
2494
|
+
if (!await checkbox.isChecked()) await checkbox.check();
|
|
2495
|
+
}
|
|
2496
|
+
await this.page.getByRole('button', {
|
|
2497
|
+
name: 'Save and close'
|
|
2498
|
+
}).click();
|
|
2499
|
+
await this.handleSaveToastOrAlreadyExists();
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
|
|
2503
|
+
|
|
2504
|
+
|
|
2505
|
+
class $05857e702fc1e333$export$c919546f4bf88f35 extends (0, $84f8d1abd29d48fd$export$cb21416f3c2fd176) {
|
|
2506
|
+
/* Navigates to the Environments Edit Permissions page via the tree context menu */ async navigateToPage() {
|
|
2507
|
+
await this.openRootContextMenu('Environments');
|
|
2508
|
+
await this.page.getByRole('link', {
|
|
2509
|
+
name: 'Edit Permissions'
|
|
2510
|
+
}).click();
|
|
2511
|
+
}
|
|
2512
|
+
async navigateToEnvEditPermissionPage() {
|
|
2513
|
+
await this.navigateToPage();
|
|
2514
|
+
}
|
|
2515
|
+
/* Gives the permissions to a role */ async givePermissionsToRole(roleName) {
|
|
2516
|
+
const filter = this.page.getByPlaceholder('Filter by roles').first();
|
|
2517
|
+
await filter.waitFor({
|
|
2518
|
+
state: 'visible'
|
|
2519
|
+
});
|
|
2520
|
+
await filter.scrollIntoViewIfNeeded();
|
|
2521
|
+
await filter.fill(roleName);
|
|
2522
|
+
const roleRow = this.page.locator('table.table-striped tbody tr').first();
|
|
2523
|
+
const labels = [
|
|
2524
|
+
'deploy initial',
|
|
2525
|
+
'read',
|
|
2526
|
+
'repo edit'
|
|
2527
|
+
];
|
|
2528
|
+
const headers = this.page.locator('table.table-striped thead th');
|
|
2529
|
+
const headerCount = await headers.count();
|
|
2530
|
+
const headerTexts = [];
|
|
2531
|
+
for(let i = 0; i < headerCount; i += 1)headerTexts.push((await headers.nth(i).innerText()).trim().toLowerCase());
|
|
2532
|
+
for (const label of labels){
|
|
2533
|
+
const colIndex = headerTexts.findIndex((text)=>text.includes(label));
|
|
2534
|
+
if (colIndex === -1) throw new Error(`Permission column not found: ${label}`);
|
|
2535
|
+
const checkbox = roleRow.locator('td').nth(colIndex).locator('input[type="checkbox"]');
|
|
2536
|
+
if (!await checkbox.isChecked()) await checkbox.check();
|
|
2537
|
+
}
|
|
2538
|
+
await this.page.getByRole('button', {
|
|
2539
|
+
name: 'Save and close'
|
|
2540
|
+
}).click();
|
|
2541
|
+
await this.handleSaveToastOrAlreadyExists();
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2544
|
+
|
|
2545
|
+
|
|
2546
|
+
|
|
2547
|
+
class $7735c50a40aa728e$export$ecd8b3e50e92d781 extends (0, $84f8d1abd29d48fd$export$cb21416f3c2fd176) {
|
|
2548
|
+
/* Navigates to the Applications Edit Permissions page via the tree context menu */ async navigateToPage() {
|
|
2549
|
+
await this.openRootContextMenu('Applications');
|
|
2550
|
+
await this.page.getByRole('link', {
|
|
2551
|
+
name: 'Edit Permissions'
|
|
2552
|
+
}).click();
|
|
2553
|
+
}
|
|
2554
|
+
async navigateToAppEditPermissionPage() {
|
|
2555
|
+
await this.navigateToPage();
|
|
2556
|
+
}
|
|
2557
|
+
/* Gives the permissions to a role */ async givePermissionsToRole(roleName) {
|
|
2558
|
+
const filter = this.page.getByPlaceholder('Filter by roles').first();
|
|
2559
|
+
await filter.waitFor({
|
|
2560
|
+
state: 'visible'
|
|
2561
|
+
});
|
|
2562
|
+
await filter.scrollIntoViewIfNeeded();
|
|
2563
|
+
await filter.fill(roleName);
|
|
2564
|
+
const roleRow = this.page.locator('table.table-striped tbody tr').first();
|
|
2565
|
+
const labels = [
|
|
2566
|
+
'read',
|
|
2567
|
+
'repo edit'
|
|
2568
|
+
];
|
|
2569
|
+
const headers = this.page.locator('table.table-striped thead th');
|
|
2570
|
+
const headerCount = await headers.count();
|
|
2571
|
+
const headerTexts = [];
|
|
2572
|
+
for(let i = 0; i < headerCount; i += 1)headerTexts.push((await headers.nth(i).innerText()).trim().toLowerCase());
|
|
2573
|
+
for (const label of labels){
|
|
2574
|
+
const colIndex = headerTexts.findIndex((text)=>text.includes(label));
|
|
2575
|
+
if (colIndex === -1) throw new Error(`Permission column not found: ${label}`);
|
|
2576
|
+
const checkbox = roleRow.locator('td').nth(colIndex).locator('input[type="checkbox"]');
|
|
2577
|
+
if (!await checkbox.isChecked()) await checkbox.check();
|
|
2578
|
+
}
|
|
2579
|
+
await this.page.getByRole('button', {
|
|
2580
|
+
name: 'Save and close'
|
|
2581
|
+
}).click();
|
|
2582
|
+
await this.handleSaveToastOrAlreadyExists();
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2586
|
+
|
|
2587
|
+
|
|
2588
|
+
class $51cef27488199b02$export$170f2cb42585788e extends (0, $84f8d1abd29d48fd$export$cb21416f3c2fd176) {
|
|
2589
|
+
/* Navigates to the Configuration Edit Permissions page via the tree context menu */ async navigateToPage() {
|
|
2590
|
+
await this.openRootContextMenu('Configuration');
|
|
2591
|
+
await this.page.getByRole('link', {
|
|
2592
|
+
name: 'Edit Permissions'
|
|
2593
|
+
}).click();
|
|
2594
|
+
}
|
|
2595
|
+
async navigateToConfigurationEditPermissionsPage() {
|
|
2596
|
+
await this.navigateToPage();
|
|
2597
|
+
}
|
|
2598
|
+
/* Gives the permissions to a role */ async givePermissionsToRole(roleName) {
|
|
2599
|
+
const filter = this.page.getByPlaceholder('Filter by roles').first();
|
|
2600
|
+
await filter.waitFor({
|
|
2601
|
+
state: 'visible'
|
|
2602
|
+
});
|
|
2603
|
+
await filter.scrollIntoViewIfNeeded();
|
|
2604
|
+
await filter.fill(roleName);
|
|
2605
|
+
const roleRow = this.page.locator('table.table-striped tbody tr').first();
|
|
2606
|
+
const checkboxes = roleRow.locator('input[type="checkbox"]');
|
|
2607
|
+
const count = await checkboxes.count();
|
|
2608
|
+
for(let i = 0; i < count; i += 1){
|
|
2609
|
+
const checkbox = checkboxes.nth(i);
|
|
2610
|
+
if (!await checkbox.isChecked()) await checkbox.check();
|
|
2611
|
+
}
|
|
2612
|
+
await this.page.getByRole('button', {
|
|
2613
|
+
name: 'Save and close'
|
|
2614
|
+
}).click();
|
|
2615
|
+
await this.handleSaveToastOrAlreadyExists();
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
|
|
2619
|
+
|
|
2620
|
+
class $28788a34e6bbbe3d$export$b8a61e5c71402559 extends (0, $84f8d1abd29d48fd$export$cb21416f3c2fd176) {
|
|
2621
|
+
constructor(page){
|
|
2622
|
+
super(page);
|
|
2623
|
+
this.homePage = new (0, $f280624787ceceed$export$36d69433c4f81145)(page);
|
|
2624
|
+
this.userPage = new (0, $c949c453826379e3$export$ecdc3cf9d1cfe143)(page);
|
|
2625
|
+
this.overthereLocalHostPage = new (0, $647b67e126f2dbe0$export$c88e1d16f82516d)(page);
|
|
2626
|
+
this.environmentsPage = new (0, $f8bdd801aee9e08b$export$12530425267834a9)(page);
|
|
2627
|
+
this.applicationsPage = new (0, $408106958eb335da$export$1533b625ec0c75e2)(page);
|
|
2628
|
+
this.reportsPage = new (0, $b2f725a937c99e2d$export$b8a443164510a1b4)(page);
|
|
2629
|
+
this.reportsControlTasksPage = new (0, $1b52322f0063c4bc$export$5c1198389e139c11)(page);
|
|
2630
|
+
this.reportsDashboardPage = new (0, $c8a7aeb49da7ed26$export$a3e02ac2d0adbb20)(page);
|
|
2631
|
+
this.reportsDeploymentsPage = new (0, $50eb7c79d31afc08$export$41a993d086bafffd)(page);
|
|
2632
|
+
this.infrastructureEditPermissionsPage = new (0, $9204952bd68f68e5$export$e37b272c81a10693)(page);
|
|
2633
|
+
this.environmentEditPermissionsPage = new (0, $05857e702fc1e333$export$c919546f4bf88f35)(page);
|
|
2634
|
+
this.applicationEditPermissionsPage = new (0, $7735c50a40aa728e$export$ecd8b3e50e92d781)(page);
|
|
2635
|
+
this.configurationEditPermissionsPage = new (0, $51cef27488199b02$export$170f2cb42585788e)(page);
|
|
2636
|
+
this.gitopsConfigurationPage = new (0, $179e7dbfee839d39$export$95a11cbd2815051e)(page);
|
|
2637
|
+
}
|
|
2638
|
+
async navigateToHomePage() {
|
|
2639
|
+
await this.page.goto('/home');
|
|
2640
|
+
}
|
|
2641
|
+
async navigateToUserPage() {
|
|
2642
|
+
await this.page.locator('p.MuiTypography-root', {
|
|
2643
|
+
hasText: 'User management'
|
|
2644
|
+
}).click();
|
|
2645
|
+
}
|
|
2646
|
+
async clickCollapseSideView() {
|
|
2647
|
+
const collapseButton = this.page.getByTestId('back-button');
|
|
2648
|
+
await collapseButton.click();
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
|
|
2653
|
+
|
|
2654
|
+
|
|
380
2655
|
class $365fc3920b0d055f$export$f14c0e3f98d164c0 extends (0, $ae23b16684112d0b$export$2b65d1d97338f32b) {
|
|
381
|
-
/* Login with username and password*/ async login(userName, password) {
|
|
382
|
-
|
|
383
|
-
await
|
|
384
|
-
await
|
|
385
|
-
|
|
2656
|
+
/* Login with username and password*/ async login(userName, password, pageOverride) {
|
|
2657
|
+
const p = pageOverride ?? this.page;
|
|
2658
|
+
await p.goto('/#/login');
|
|
2659
|
+
await (0, $1UEKj$playwrighttest.expect)(p).toHaveTitle('Digital.ai Deploy');
|
|
2660
|
+
await p.getByPlaceholder('user').fill(userName, {
|
|
2661
|
+
timeout: 10000
|
|
386
2662
|
});
|
|
387
|
-
await
|
|
2663
|
+
await p.getByPlaceholder('Password').fill(password, {
|
|
388
2664
|
timeout: 10000
|
|
389
2665
|
});
|
|
390
|
-
await
|
|
391
|
-
name:
|
|
2666
|
+
await p.getByRole('button', {
|
|
2667
|
+
name: 'Log in'
|
|
392
2668
|
}).click({
|
|
393
2669
|
timeout: 10000
|
|
394
2670
|
});
|
|
395
2671
|
}
|
|
396
2672
|
/* Verify invalid password message */ async verifyInvalidPasswordMessage() {
|
|
397
|
-
const errorMessage = this.page.getByText(
|
|
2673
|
+
const errorMessage = this.page.getByText('The username and/or password you have entered is incorrect.');
|
|
2674
|
+
await (0, $1UEKj$playwrighttest.expect)(errorMessage).toBeVisible();
|
|
2675
|
+
}
|
|
2676
|
+
/* Verify the error message shown when a disabled user attempts to log in */ async verifyDisabledUserLoginError(pageOverride) {
|
|
2677
|
+
const p = pageOverride ?? this.page;
|
|
2678
|
+
const errorMessage = p.getByText('Your account is currently disabled. Please contact your Deploy administrator for further assistance.');
|
|
398
2679
|
await (0, $1UEKj$playwrighttest.expect)(errorMessage).toBeVisible();
|
|
399
2680
|
}
|
|
2681
|
+
/* Verify the error message shown when a user without login permission attempts to log in */ async verifyNoLoginPermissionError(pageOverride) {
|
|
2682
|
+
const p = pageOverride ?? this.page;
|
|
2683
|
+
const errorMessage = p.locator('div.error-message.permission-error');
|
|
2684
|
+
await (0, $1UEKj$playwrighttest.expect)(errorMessage).toBeVisible({
|
|
2685
|
+
timeout: 20000
|
|
2686
|
+
});
|
|
2687
|
+
}
|
|
400
2688
|
/* Logout as authenticated user */ async logout() {
|
|
401
|
-
await this.page.locator(
|
|
402
|
-
await this.page.getByText(
|
|
2689
|
+
await this.page.locator('button.dot-avatar').click();
|
|
2690
|
+
await this.page.getByText('Log out', {
|
|
403
2691
|
exact: true
|
|
404
2692
|
}).click();
|
|
405
|
-
await
|
|
2693
|
+
await this.page.waitForURL('**/#/login', {
|
|
2694
|
+
timeout: 15000
|
|
2695
|
+
});
|
|
2696
|
+
await this.page.reload();
|
|
2697
|
+
await this.page.waitForLoadState('networkidle');
|
|
2698
|
+
await this.page.waitForURL('**/#/login', {
|
|
2699
|
+
timeout: 15000
|
|
2700
|
+
});
|
|
2701
|
+
await this.page.getByPlaceholder('user').waitFor({
|
|
2702
|
+
state: 'visible',
|
|
2703
|
+
timeout: 15000
|
|
2704
|
+
});
|
|
406
2705
|
}
|
|
407
2706
|
}
|
|
408
2707
|
|
|
409
2708
|
|
|
410
2709
|
const $3352700e95ddc19e$export$e0969da9b8fb378d = (0, $1UEKj$playwrighttest.test).extend({
|
|
411
|
-
fixtures: async ({ page: page
|
|
2710
|
+
fixtures: async ({ page: page }, use)=>{
|
|
412
2711
|
const fixtures = new $3352700e95ddc19e$var$Fixtures(page);
|
|
413
2712
|
await use(fixtures);
|
|
414
2713
|
return fixtures;
|
|
415
2714
|
},
|
|
416
|
-
loginPage: async ({ page: page
|
|
2715
|
+
loginPage: async ({ page: page }, use)=>{
|
|
417
2716
|
const loginPage = new (0, $365fc3920b0d055f$export$f14c0e3f98d164c0)(page);
|
|
418
2717
|
await use(loginPage);
|
|
419
2718
|
return loginPage;
|
|
420
2719
|
},
|
|
421
|
-
navigation: async ({ page: page
|
|
2720
|
+
navigation: async ({ page: page }, use)=>{
|
|
422
2721
|
const navigationPage = new (0, $28788a34e6bbbe3d$export$b8a61e5c71402559)(page);
|
|
423
2722
|
await use(navigationPage);
|
|
424
2723
|
return navigationPage;
|