@camunda/e2e-test-suite 0.0.1094 → 0.0.1096
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pages/8.10/Authorization.d.ts +2 -0
- package/dist/pages/8.10/Authorization.js +49 -21
- package/dist/pages/SM-8.10/Authorization.d.ts +1 -1
- package/dist/pages/SM-8.10/Authorization.js +51 -23
- package/dist/pages/SM-8.10/ModelerCreatePage.js +18 -2
- package/dist/pages/SM-8.10/OCIdentityRolesPage.js +10 -1
- package/dist/pages/SM-8.10/OCTenantPage.js +30 -7
- package/package.json +1 -1
|
@@ -7,6 +7,8 @@ declare class Authorization {
|
|
|
7
7
|
protected readonly createAuthorizationOwnerTypeCombobox: Locator;
|
|
8
8
|
protected readonly createAuthorizationOwnerTypeOption: (name: string) => Locator;
|
|
9
9
|
protected readonly createAuthorizationOwnerComboBox: Locator;
|
|
10
|
+
protected readonly createAuthorizationOwnerSearchInput: Locator;
|
|
11
|
+
protected readonly createAuthorizationOwnerLegacyComboBox: Locator;
|
|
10
12
|
protected readonly createAuthorizationOwnerOption: (name: string) => Locator;
|
|
11
13
|
protected readonly createAuthorizationResourceTypeDropdown: Locator;
|
|
12
14
|
protected readonly createAuthorizationResourceTypeOption: (name: string) => Locator;
|
|
@@ -12,6 +12,8 @@ class Authorization {
|
|
|
12
12
|
createAuthorizationOwnerTypeCombobox;
|
|
13
13
|
createAuthorizationOwnerTypeOption;
|
|
14
14
|
createAuthorizationOwnerComboBox;
|
|
15
|
+
createAuthorizationOwnerSearchInput;
|
|
16
|
+
createAuthorizationOwnerLegacyComboBox;
|
|
15
17
|
createAuthorizationOwnerOption;
|
|
16
18
|
createAuthorizationResourceTypeDropdown;
|
|
17
19
|
createAuthorizationResourceTypeOption;
|
|
@@ -40,17 +42,30 @@ class Authorization {
|
|
|
40
42
|
name,
|
|
41
43
|
exact: true,
|
|
42
44
|
});
|
|
43
|
-
// The Owner field
|
|
44
|
-
//
|
|
45
|
+
// The Owner field has two shapes depending on the build under test.
|
|
46
|
+
//
|
|
47
|
+
// Since camunda/camunda#57931 Identity renders an entity-search dropdown
|
|
45
48
|
// (identity/client/src/components/form/EntitySearchSingleSelect.tsx ->
|
|
46
|
-
// EntitySearchDropdown -> DropdownSearch)
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
|
|
49
|
+
// EntitySearchDropdown -> DropdownSearch): a bare `FormLabel` plus a
|
|
50
|
+
// Carbon `Search` input carrying only the "Search by owner ID"
|
|
51
|
+
// placeholder, with nothing exposing the accessible name "Owner".
|
|
52
|
+
//
|
|
53
|
+
// Builds that predate that change — notably released alpha generations
|
|
54
|
+
// such as 8.10.0-alpha5, tagged 2026-08-31 before the stable/8.10
|
|
55
|
+
// backport (#61570) landed on 2026-09-01 — still render the older Carbon
|
|
56
|
+
// `ComboBox` (owner-selection/OwnerSelection.tsx) whose `titleText` is
|
|
57
|
+
// "Owner". Match either so the suite runs against both.
|
|
58
|
+
this.createAuthorizationOwnerSearchInput =
|
|
53
59
|
this.createAuthorizationModal.getByPlaceholder('Search by owner ID');
|
|
60
|
+
this.createAuthorizationOwnerLegacyComboBox =
|
|
61
|
+
this.createAuthorizationModal.getByRole('combobox', {
|
|
62
|
+
name: 'Owner',
|
|
63
|
+
exact: true,
|
|
64
|
+
});
|
|
65
|
+
this.createAuthorizationOwnerComboBox =
|
|
66
|
+
this.createAuthorizationOwnerSearchInput
|
|
67
|
+
.or(this.createAuthorizationOwnerLegacyComboBox)
|
|
68
|
+
.first();
|
|
54
69
|
this.createAuthorizationOwnerOption = (name) => this.createAuthorizationModal.getByRole('option', {
|
|
55
70
|
name,
|
|
56
71
|
exact: true,
|
|
@@ -96,14 +111,23 @@ class Authorization {
|
|
|
96
111
|
await (0, test_1.expect)(this.createAuthorizationModal).toBeVisible();
|
|
97
112
|
await this.createAuthorizationOwnerTypeCombobox.click();
|
|
98
113
|
await this.createAuthorizationOwnerTypeOption(authorization.ownerType).click();
|
|
99
|
-
// Owner type selection re-renders the owner field
|
|
100
|
-
//
|
|
101
|
-
// product team's own e2e suite
|
|
102
|
-
// analogous wait from 20s to 60s
|
|
114
|
+
// Owner type selection re-renders the owner field, and on the
|
|
115
|
+
// entity-search build that swap is query-driven, so it can lag well past
|
|
116
|
+
// 30s under CI load — the product team's own e2e suite raised an
|
|
117
|
+
// analogous wait on the same widget from 20s to 60s (camunda/camunda#61353).
|
|
103
118
|
await (0, test_1.expect)(this.createAuthorizationOwnerComboBox).toBeVisible({
|
|
104
119
|
timeout: 60000,
|
|
105
120
|
});
|
|
106
|
-
await this.
|
|
121
|
+
const usesEntitySearch = await this.createAuthorizationOwnerSearchInput.isVisible();
|
|
122
|
+
const ownerField = usesEntitySearch
|
|
123
|
+
? this.createAuthorizationOwnerSearchInput
|
|
124
|
+
: this.createAuthorizationOwnerLegacyComboBox;
|
|
125
|
+
if (!usesEntitySearch) {
|
|
126
|
+
// The legacy Carbon ComboBox renders its menu only once opened; the
|
|
127
|
+
// entity-search input renders results off the typed value alone.
|
|
128
|
+
await ownerField.click();
|
|
129
|
+
}
|
|
130
|
+
await ownerField.fill(authorization.ownerId);
|
|
107
131
|
// The dropdown renders results only while the search box holds text, and
|
|
108
132
|
// the list is fetched from the server on a debounce rather than filtered
|
|
109
133
|
// client-side, so the matching entry appears a beat later. Clicking the
|
|
@@ -117,13 +141,17 @@ class Authorization {
|
|
|
117
141
|
.first();
|
|
118
142
|
await (0, test_1.expect)(ownerMenuItem).toBeVisible({ timeout: 60000 });
|
|
119
143
|
await ownerMenuItem.click({ timeout: 20000 });
|
|
120
|
-
//
|
|
121
|
-
// (EntitySearchSingleSelect renders one or the other), so
|
|
122
|
-
// away is how we know the owner actually landed on the form
|
|
123
|
-
// the click being swallowed.
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
144
|
+
// On the entity-search build a committed selection swaps the search box
|
|
145
|
+
// for a filter Tag (EntitySearchSingleSelect renders one or the other), so
|
|
146
|
+
// the box going away is how we know the owner actually landed on the form
|
|
147
|
+
// rather than the click being swallowed. The legacy ComboBox keeps its
|
|
148
|
+
// input and shows the selection as the input value, so this check only
|
|
149
|
+
// applies to the entity-search shape.
|
|
150
|
+
if (usesEntitySearch) {
|
|
151
|
+
await (0, test_1.expect)(this.createAuthorizationOwnerSearchInput).toBeHidden({
|
|
152
|
+
timeout: 15000,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
127
155
|
await this.selectResourceTypeFromDialog(authorization.resourceType);
|
|
128
156
|
await this.createAuthorizationResourceIdField.fill(authorization.resourceId);
|
|
129
157
|
for (const permission of authorization.accessPermissions) {
|
|
@@ -21,7 +21,7 @@ declare class Authorization {
|
|
|
21
21
|
protected readonly deleteAuthorizationModalDeleteButton: Locator;
|
|
22
22
|
protected readonly tableFilter: Locator;
|
|
23
23
|
constructor(page: Page);
|
|
24
|
-
protected selectResourceType(resourceType: string): Promise<void>;
|
|
24
|
+
protected selectResourceType(resourceType: string, timeout?: number): Promise<void>;
|
|
25
25
|
private selectAuthorizationOwner;
|
|
26
26
|
createAuthorization(authorization: {
|
|
27
27
|
ownerType: string;
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Authorization = void 0;
|
|
4
4
|
const test_1 = require("@playwright/test");
|
|
5
|
-
const UtilitiesPage_1 = require("../SM-8.10/UtilitiesPage");
|
|
6
5
|
const findLocatorWithPagination_1 = require("../../utils/assertionHelpers/findLocatorWithPagination");
|
|
7
6
|
const submitModalWithRetry_1 = require("../../utils/assertionHelpers/submitModalWithRetry");
|
|
8
7
|
const sleep_1 = require("../../utils/sleep");
|
|
@@ -41,10 +40,17 @@ class Authorization {
|
|
|
41
40
|
this.createAuthorizationModal.getByRole('combobox', {
|
|
42
41
|
name: 'Owner type',
|
|
43
42
|
});
|
|
44
|
-
|
|
43
|
+
// The design-system Select portals its listbox to the end of <body>, so
|
|
44
|
+
// the options are siblings of the dialog rather than descendants, and
|
|
45
|
+
// while the listbox is open Radix marks everything outside it aria-hidden
|
|
46
|
+
// - which takes the dialog itself out of the accessibility tree. Query the
|
|
47
|
+
// options from the page root so both placements resolve.
|
|
48
|
+
this.createAuthorizationOwnerTypeOption = (name) => this.page
|
|
49
|
+
.getByRole('option', {
|
|
45
50
|
name,
|
|
46
51
|
exact: true,
|
|
47
|
-
})
|
|
52
|
+
})
|
|
53
|
+
.first();
|
|
48
54
|
this.createAuthorizationOwnerComboBox =
|
|
49
55
|
this.createAuthorizationModal.getByRole('combobox', {
|
|
50
56
|
name: 'Owner',
|
|
@@ -57,32 +63,38 @@ class Authorization {
|
|
|
57
63
|
// Every owner type now renders a server-backed search dropdown instead of
|
|
58
64
|
// the combobox that listed all owners up front. The dropdown is a Carbon
|
|
59
65
|
// Search inside a ListBox, so it has no combobox role; its results are
|
|
60
|
-
// list-box menu items that carry the owner ID as their title.
|
|
66
|
+
// list-box menu items that carry the owner ID as their title. The
|
|
67
|
+
// design-system build renders the same field as a command palette whose
|
|
68
|
+
// results are role=option items in a popover portalled out of the dialog.
|
|
61
69
|
this.createAuthorizationOwnerSearchInput =
|
|
62
70
|
this.createAuthorizationModal.getByPlaceholder('Search by owner ID');
|
|
63
|
-
this.createAuthorizationOwnerSearchOption = (name) => this.
|
|
64
|
-
.locator('.cds--list-box__menu-item')
|
|
71
|
+
this.createAuthorizationOwnerSearchOption = (name) => this.page
|
|
72
|
+
.locator('.cds--list-box__menu-item, [role="option"]')
|
|
65
73
|
.filter({ hasText: name })
|
|
66
74
|
.first();
|
|
67
75
|
this.createAuthorizationResourceTypeDropdown =
|
|
68
76
|
this.createAuthorizationModal.getByRole('combobox', {
|
|
69
77
|
name: 'Resource type',
|
|
70
78
|
});
|
|
71
|
-
this.createAuthorizationResourceTypeOption = (name) => this.
|
|
79
|
+
this.createAuthorizationResourceTypeOption = (name) => this.page
|
|
80
|
+
.getByRole('option', {
|
|
72
81
|
name,
|
|
73
82
|
exact: true,
|
|
74
|
-
})
|
|
83
|
+
})
|
|
84
|
+
.first();
|
|
75
85
|
this.createAuthorizationResourceIdField =
|
|
76
86
|
this.createAuthorizationModal.getByRole('textbox', {
|
|
77
87
|
name: 'Resource ID',
|
|
78
88
|
});
|
|
79
|
-
this.createAuthorizationAccessPermission = (name) => this.createAuthorizationModal.locator(
|
|
89
|
+
this.createAuthorizationAccessPermission = (name) => this.createAuthorizationModal.locator(`label[for="${name}"]`);
|
|
80
90
|
this.createAuthorizationSubmitButton =
|
|
81
91
|
this.createAuthorizationModal.getByRole('button', {
|
|
82
92
|
name: 'Create authorization',
|
|
83
93
|
});
|
|
84
94
|
this.authorizationsList = page.getByRole('table');
|
|
85
|
-
this.deleteAuthorizationButton = (name) => this.authorizationsList
|
|
95
|
+
this.deleteAuthorizationButton = (name) => this.authorizationsList
|
|
96
|
+
.getByRole('row', { name })
|
|
97
|
+
.getByRole('button', { name: 'Delete' });
|
|
86
98
|
this.deleteAuthorizationModal = page.getByRole('dialog', {
|
|
87
99
|
name: /delete authorization/i,
|
|
88
100
|
});
|
|
@@ -103,12 +115,12 @@ class Authorization {
|
|
|
103
115
|
// "Authorization type" filter combobox (identity/client ListV2.tsx, gated on
|
|
104
116
|
// isNewDesignSystemEnabled). Select the resource type through whichever this
|
|
105
117
|
// build renders: click the tab, or open the combobox and pick the option.
|
|
106
|
-
async selectResourceType(resourceType) {
|
|
118
|
+
async selectResourceType(resourceType, timeout = 60000) {
|
|
107
119
|
const tab = this.selectResourceTypeTab(resourceType);
|
|
108
120
|
const filterCombobox = this.page.getByRole('combobox', {
|
|
109
121
|
name: 'Authorization type',
|
|
110
122
|
});
|
|
111
|
-
await (0, test_1.expect)(tab.or(filterCombobox)).toBeVisible({ timeout
|
|
123
|
+
await (0, test_1.expect)(tab.or(filterCombobox)).toBeVisible({ timeout });
|
|
112
124
|
if (await tab.isVisible()) {
|
|
113
125
|
await tab.click();
|
|
114
126
|
return;
|
|
@@ -116,7 +128,7 @@ class Authorization {
|
|
|
116
128
|
await filterCombobox.click();
|
|
117
129
|
await this.page
|
|
118
130
|
.getByRole('option', { name: resourceType, exact: true })
|
|
119
|
-
.click({ timeout: 30000 });
|
|
131
|
+
.click({ timeout: Math.min(timeout, 30000) });
|
|
120
132
|
}
|
|
121
133
|
// Released 8.10 alpha images still render the Carbon combobox that lists
|
|
122
134
|
// every owner, so accept whichever owner field this build shows.
|
|
@@ -164,7 +176,7 @@ class Authorization {
|
|
|
164
176
|
for (const permission of authorization.accessPermissions) {
|
|
165
177
|
await (0, sleep_1.sleep)(200);
|
|
166
178
|
const permissionLabel = this.createAuthorizationAccessPermission(permission);
|
|
167
|
-
const permissionInput = this.createAuthorizationModal.locator(`input[id="${permission}"]`);
|
|
179
|
+
const permissionInput = this.createAuthorizationModal.locator(`input[id="${permission}"], [role="checkbox"][id="${permission}"]`);
|
|
168
180
|
await (0, test_1.expect)(permissionLabel).toBeVisible();
|
|
169
181
|
if (!(await permissionInput.isChecked())) {
|
|
170
182
|
await permissionLabel.scrollIntoViewIfNeeded();
|
|
@@ -188,7 +200,7 @@ class Authorization {
|
|
|
188
200
|
}
|
|
189
201
|
}
|
|
190
202
|
await (0, submitModalWithRetry_1.submitModalWithRetry)(this.createAuthorizationSubmitButton, this.createAuthorizationModal);
|
|
191
|
-
await this.
|
|
203
|
+
await this.selectResourceType(authorization.resourceType);
|
|
192
204
|
const item = this.page
|
|
193
205
|
.getByRole('row')
|
|
194
206
|
.filter({
|
|
@@ -215,9 +227,7 @@ class Authorization {
|
|
|
215
227
|
break;
|
|
216
228
|
}
|
|
217
229
|
try {
|
|
218
|
-
|
|
219
|
-
await (0, test_1.expect)(resourceTab).toBeVisible({ timeout: budget(30000) });
|
|
220
|
-
await resourceTab.click({ timeout: budget(30000) });
|
|
230
|
+
await this.selectResourceType(authorization.resourceType, budget(30000));
|
|
221
231
|
for (const filterValue of [authorization.ownerId, '']) {
|
|
222
232
|
await this.filterAuthorizationsByOwner(filterValue);
|
|
223
233
|
const found = await (0, findLocatorWithPagination_1.findLocatorWithPagination)(this.page, item, {
|
|
@@ -268,11 +278,29 @@ class Authorization {
|
|
|
268
278
|
await (0, test_1.expect)(this.deleteAuthorizationModal).toBeVisible();
|
|
269
279
|
await this.deleteAuthorizationModalDeleteButton.click();
|
|
270
280
|
await (0, test_1.expect)(this.deleteAuthorizationModal).not.toBeVisible();
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
.
|
|
281
|
+
// Re-selecting the resource type takes two interactions on the
|
|
282
|
+
// design-system build, so this cannot go through the shared
|
|
283
|
+
// reload-click-assert helper, which only accepts a single click locator.
|
|
284
|
+
const deletedCell = this.authorizationsList
|
|
285
|
+
.getByRole('cell', { name: resourceId })
|
|
286
|
+
.first();
|
|
287
|
+
let lastError;
|
|
288
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
289
|
+
await this.page.reload();
|
|
290
|
+
await this.page.waitForLoadState('domcontentloaded');
|
|
291
|
+
await this.selectResourceType(resourceType);
|
|
292
|
+
try {
|
|
293
|
+
await (0, test_1.expect)(deletedCell).not.toBeVisible({ timeout: 5000 });
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
lastError = error;
|
|
298
|
+
console.warn(`Attempt ${attempt + 1} still listed the ${resourceType} ` +
|
|
299
|
+
`authorization for "${resourceId}". Retrying...`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
throw new Error(`Authorization for resource "${resourceId}" is still listed: ` +
|
|
303
|
+
String(lastError));
|
|
276
304
|
}
|
|
277
305
|
}
|
|
278
306
|
exports.Authorization = Authorization;
|
|
@@ -1138,13 +1138,29 @@ class ModelerCreatePage {
|
|
|
1138
1138
|
// processApplicationId is defined, which it now is, so re-entering
|
|
1139
1139
|
// Play on an already-deployed process application legitimately
|
|
1140
1140
|
// has nothing to deploy and renders no Deploy button at all.
|
|
1141
|
+
// The "Configure test case" accordion header is always rendered once
|
|
1142
|
+
// the overlay is up -- it's only enabled once deploy has completed --
|
|
1143
|
+
// so isVisible() alone is always true here and this check must gate on
|
|
1144
|
+
// isEnabled() instead, or Step 2 is skipped even when deploy is still
|
|
1145
|
+
// pending and Step 3 spins forever on a button that never unlocks.
|
|
1141
1146
|
const alreadyDeployed = await configureScenarioButton
|
|
1142
|
-
.
|
|
1147
|
+
.isEnabled({ timeout: 3000 })
|
|
1143
1148
|
.catch(() => false);
|
|
1144
1149
|
if (!alreadyDeployed) {
|
|
1145
1150
|
// Step 2: deploy
|
|
1146
1151
|
await setupDeployButton.click({ timeout });
|
|
1147
|
-
|
|
1152
|
+
// Test Studio dropped the "Process has been successfully deployed"
|
|
1153
|
+
// toast: DeployStepContent now swaps the Deploy button for an inline
|
|
1154
|
+
// `deploy-success-badge` ("Successfully deployed"), and
|
|
1155
|
+
// TestConfigurationPanel's isDeployStepComplete effect calls
|
|
1156
|
+
// hideConfigurationOverlay() as soon as deploy lands -- which
|
|
1157
|
+
// collapses the Deploy step and opens "Configure test case" on its
|
|
1158
|
+
// own. Accept either signal; a failed deploy renders a "Deployment
|
|
1159
|
+
// failed" badge instead and matches neither.
|
|
1160
|
+
await (0, test_1.expect)(this.page
|
|
1161
|
+
.getByTestId('deploy-success-badge')
|
|
1162
|
+
.or(configureTestPanel)
|
|
1163
|
+
.first()).toBeVisible({ timeout: 90000 });
|
|
1148
1164
|
}
|
|
1149
1165
|
// Step 3: leave the overlay, and confirm we actually left it. The
|
|
1150
1166
|
// button only calls hideConfigurationOverlay(); the overlay's own
|
|
@@ -71,7 +71,16 @@ class OCIdentityRolesPage {
|
|
|
71
71
|
// options are not inside assignMappingModal's DOM subtree - query them from
|
|
72
72
|
// the page. Each option repeats the id as both title and subtitle, so match
|
|
73
73
|
// on contained text rather than the accessible name.
|
|
74
|
-
|
|
74
|
+
//
|
|
75
|
+
// Builds predating the design-system migration (released alpha
|
|
76
|
+
// generations such as 8.10.0-alpha5) still render the Carbon
|
|
77
|
+
// `DropdownSearch` list inside the dialog as a role="listbox", with the
|
|
78
|
+
// mapping rule id as the item title - accept either shape.
|
|
79
|
+
this.assignMappingRuleOption = (name) => page
|
|
80
|
+
.getByRole('option')
|
|
81
|
+
.filter({ hasText: name })
|
|
82
|
+
.or(this.assignMappingModal.getByRole('listbox').getByText(name))
|
|
83
|
+
.first();
|
|
75
84
|
this.assignMappingModalAssignRuleButton = this.assignMappingModal.getByRole('button', {
|
|
76
85
|
name: 'Assign mapping rule',
|
|
77
86
|
});
|
|
@@ -287,14 +287,37 @@ class OCTenantPage {
|
|
|
287
287
|
.filter({ hasText: roleIdentifier })
|
|
288
288
|
.or(this.assignRoleModal.getByText(roleIdentifier))
|
|
289
289
|
.first();
|
|
290
|
-
// The dropdown
|
|
291
|
-
//
|
|
290
|
+
// The dropdown no longer filters client-side: it queries
|
|
291
|
+
// `/v2/roles/search` with `name: {$like: '*<typed>*'}`, which is
|
|
292
|
+
// case-sensitive, so the role ID ('connectors') never matches the default
|
|
293
|
+
// role's name ('Connectors'). The option row renders the ID followed by
|
|
294
|
+
// the name, so the name is the tail of `roleIdentifier`: search the ID
|
|
295
|
+
// first for the shells that still match on it, then fall back to the name.
|
|
296
|
+
const searchTerms = [roleId];
|
|
297
|
+
const roleName = roleIdentifier.startsWith(roleId)
|
|
298
|
+
? roleIdentifier.slice(roleId.length)
|
|
299
|
+
: '';
|
|
300
|
+
if (roleName !== '' && roleName !== roleId) {
|
|
301
|
+
searchTerms.push(roleName);
|
|
302
|
+
}
|
|
303
|
+
// The dropdown only queries for a non-empty value, so clearing the box
|
|
304
|
+
// between terms resets it before the next one is typed.
|
|
292
305
|
await (0, test_1.expect)(async () => {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
306
|
+
for (const searchTerm of searchTerms) {
|
|
307
|
+
await this.roleIdSearchBox.fill('');
|
|
308
|
+
await this.page.waitForLoadState('networkidle');
|
|
309
|
+
await this.roleIdSearchBox.fill(searchTerm);
|
|
310
|
+
const offered = await result
|
|
311
|
+
.waitFor({ state: 'visible', timeout: 15000 })
|
|
312
|
+
.then(() => true)
|
|
313
|
+
.catch(() => false);
|
|
314
|
+
if (offered) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
throw new Error(`Role ${roleIdentifier} was not offered by the assign role search for` +
|
|
319
|
+
` any of: ${searchTerms.join(', ')}`);
|
|
320
|
+
}).toPass({ timeout: 120000, intervals: [2000] });
|
|
298
321
|
await result.click();
|
|
299
322
|
}
|
|
300
323
|
async clickAssignRoleSubButton() {
|