@camunda/e2e-test-suite 0.0.1233 → 0.0.1235
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/OCIdentityHomePage.d.ts +1 -0
- package/dist/pages/8.10/OCIdentityHomePage.js +22 -8
- package/dist/pages/8.10/OCIdentityRolesPage.d.ts +2 -0
- package/dist/pages/8.10/OCIdentityRolesPage.js +39 -3
- package/dist/pages/8.10/TaskPanelPage.d.ts +1 -1
- package/dist/pages/8.10/TaskPanelPage.js +31 -3
- package/dist/pages/c8Run-8.10/OperateProcessInstancePage.d.ts +1 -0
- package/dist/pages/c8Run-8.10/OperateProcessInstancePage.js +32 -30
- package/dist/pages/c8Run-8.10/OperateProcessesPage.js +2 -3
- package/dist/tests/8.10/rba-enabled-v2-user-flows.spec.js +7 -16
- package/dist/utils/users.d.ts +4 -0
- package/dist/utils/users.js +35 -1
- package/package.json +1 -1
|
@@ -12,6 +12,7 @@ declare class OCIdentityHomePage {
|
|
|
12
12
|
readonly licenseKeyTagCommercial: Locator;
|
|
13
13
|
readonly noPermissionsMessage: Locator;
|
|
14
14
|
constructor(page: Page);
|
|
15
|
+
private openTab;
|
|
15
16
|
clickAuthorizationsTab(): Promise<void>;
|
|
16
17
|
clickRolesTab(): Promise<void>;
|
|
17
18
|
clickGroupsTab(): Promise<void>;
|
|
@@ -73,21 +73,35 @@ class OCIdentityHomePage {
|
|
|
73
73
|
this.licenseKeyTagCommercial = page.getByText('Commercial license');
|
|
74
74
|
this.noPermissionsMessage = page.getByText('You don’t have access to this component');
|
|
75
75
|
}
|
|
76
|
+
// Every caller arrives here from somewhere else in Admin -- a role detail
|
|
77
|
+
// page that was just reloaded, a freshly dismissed modal -- and the default
|
|
78
|
+
// retry gives the sidebar three 10s looks with no recovery between them.
|
|
79
|
+
// That is the whole budget a hand-off has if the SPA is still re-rendering
|
|
80
|
+
// its shell, and when it runs out the run reports "Failed to assert
|
|
81
|
+
// visibility locator ... 'Authorizations' ... after 3 attempts" for a nav
|
|
82
|
+
// that is permanently part of the page. Look longer, and reload between
|
|
83
|
+
// attempts so a shell that never finished painting gets another chance
|
|
84
|
+
// instead of being polled in the same broken state three times.
|
|
85
|
+
async openTab(tab) {
|
|
86
|
+
await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, tab, {
|
|
87
|
+
visibilityTimeout: 20000,
|
|
88
|
+
postAction: async () => {
|
|
89
|
+
await this.page.reload();
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
await tab.click({ timeout: 30000 });
|
|
93
|
+
}
|
|
76
94
|
async clickAuthorizationsTab() {
|
|
77
|
-
await
|
|
78
|
-
await this.authorizationsTab.click({ timeout: 30000 });
|
|
95
|
+
await this.openTab(this.authorizationsTab);
|
|
79
96
|
}
|
|
80
97
|
async clickRolesTab() {
|
|
81
|
-
await
|
|
82
|
-
await this.rolesTab.click({ timeout: 30000 });
|
|
98
|
+
await this.openTab(this.rolesTab);
|
|
83
99
|
}
|
|
84
100
|
async clickGroupsTab() {
|
|
85
|
-
await
|
|
86
|
-
await this.groupsTab.click({ timeout: 30000 });
|
|
101
|
+
await this.openTab(this.groupsTab);
|
|
87
102
|
}
|
|
88
103
|
async clickClusterVariablesTab() {
|
|
89
|
-
await
|
|
90
|
-
await this.clusterVariablesTab.click({ timeout: 30000 });
|
|
104
|
+
await this.openTab(this.clusterVariablesTab);
|
|
91
105
|
}
|
|
92
106
|
}
|
|
93
107
|
exports.OCIdentityHomePage = OCIdentityHomePage;
|
|
@@ -25,7 +25,9 @@ declare class OCIdentityRolesPage {
|
|
|
25
25
|
readonly assignUserModal: Locator;
|
|
26
26
|
readonly assignUserModalInputField: Locator;
|
|
27
27
|
readonly assignUserModalAssignButton: Locator;
|
|
28
|
+
readonly roleSearchBox: Locator;
|
|
28
29
|
constructor(page: Page);
|
|
30
|
+
private searchForRole;
|
|
29
31
|
clickMappingRulesTab(): Promise<void>;
|
|
30
32
|
clickRole(roleID: string): Promise<void>;
|
|
31
33
|
assignMappingRuleToRole(roleId: string, mappingId: string): Promise<void>;
|
|
@@ -30,6 +30,7 @@ class OCIdentityRolesPage {
|
|
|
30
30
|
assignUserModal;
|
|
31
31
|
assignUserModalInputField;
|
|
32
32
|
assignUserModalAssignButton;
|
|
33
|
+
roleSearchBox;
|
|
33
34
|
constructor(page) {
|
|
34
35
|
this.page = page;
|
|
35
36
|
this.rolesList = page.getByRole('table');
|
|
@@ -105,6 +106,25 @@ class OCIdentityRolesPage {
|
|
|
105
106
|
this.assignUserModalAssignButton = this.assignUserModal.getByRole('button', {
|
|
106
107
|
name: /assign user/i,
|
|
107
108
|
});
|
|
109
|
+
// Both role-list shapes render this filter with the same placeholder:
|
|
110
|
+
// camunda/camunda identity/client/src/pages/roles/List.tsx (Carbon
|
|
111
|
+
// TableToolbarSearch) and ListV2.tsx (design-system SearchInput) each pass
|
|
112
|
+
// `searchPlaceholder={t("searchByRoleId")}`. Only V2 mirrors it into an
|
|
113
|
+
// aria-label, so match on the placeholder to cover both.
|
|
114
|
+
this.roleSearchBox = page.getByPlaceholder('Search by role ID');
|
|
115
|
+
}
|
|
116
|
+
// The roles list is server-side paginated at 10 rows per page, and these
|
|
117
|
+
// long-lived RBA clusters accumulate a role per test run -- the org that
|
|
118
|
+
// failed was already at "Page 6 of 6". A freshly created role sorts to the
|
|
119
|
+
// end, so `findLocatorWithPagination` has to walk every page one request at
|
|
120
|
+
// a time and exhausts its 30s budget before it arrives, reporting a role
|
|
121
|
+
// that is plainly on screen as missing. Both list shapes pass
|
|
122
|
+
// `searchKey="roleId"` with the default `eq` operator, so filtering queries
|
|
123
|
+
// the backend for that exact role and puts it on page 1.
|
|
124
|
+
async searchForRole(roleId) {
|
|
125
|
+
await (0, test_1.expect)(this.roleSearchBox).toBeVisible({ timeout: 60000 });
|
|
126
|
+
await this.roleSearchBox.fill(roleId, { timeout: 30000 });
|
|
127
|
+
await (0, test_1.expect)(this.roleSearchBox).toHaveValue(roleId, { timeout: 30000 });
|
|
108
128
|
}
|
|
109
129
|
async clickMappingRulesTab() {
|
|
110
130
|
await (0, test_1.expect)(this.selectResourceTypeTab('Mapping Rules')).toBeVisible({
|
|
@@ -113,6 +133,7 @@ class OCIdentityRolesPage {
|
|
|
113
133
|
await this.selectResourceTypeTab('Mapping Rules').click();
|
|
114
134
|
}
|
|
115
135
|
async clickRole(roleID) {
|
|
136
|
+
await this.searchForRole(roleID);
|
|
116
137
|
await (0, test_1.expect)(this.roleCell(roleID).first()).toBeVisible({ timeout: 60000 });
|
|
117
138
|
await this.roleCell(roleID).first().click();
|
|
118
139
|
}
|
|
@@ -149,11 +170,21 @@ class OCIdentityRolesPage {
|
|
|
149
170
|
await this.createNameField.fill(role.name);
|
|
150
171
|
await this.createRoleModalCreateButton.click();
|
|
151
172
|
await (0, test_1.expect)(this.createRoleModal).not.toBeVisible({ timeout: 30000 });
|
|
152
|
-
await (
|
|
173
|
+
await this.searchForRole(role.id);
|
|
174
|
+
await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.rolesList
|
|
153
175
|
.getByRole('cell', {
|
|
154
176
|
name: role.name,
|
|
155
177
|
})
|
|
156
|
-
.first()
|
|
178
|
+
.first(), {
|
|
179
|
+
visibilityTimeout: 20000,
|
|
180
|
+
postAction: async () => {
|
|
181
|
+
// The filter is component state, not a URL param, so a reload drops
|
|
182
|
+
// it -- re-apply it or the retry falls back to the page walk this
|
|
183
|
+
// whole approach exists to avoid.
|
|
184
|
+
await this.page.reload();
|
|
185
|
+
await this.searchForRole(role.id);
|
|
186
|
+
},
|
|
187
|
+
});
|
|
157
188
|
}
|
|
158
189
|
async assignMappingToRole(role, mappingRuleId) {
|
|
159
190
|
try {
|
|
@@ -169,8 +200,12 @@ class OCIdentityRolesPage {
|
|
|
169
200
|
await (0, test_1.expect)(this.rowValue(mappingRuleId)).toContainText(`${mappingRuleId}`);
|
|
170
201
|
}
|
|
171
202
|
async assignUserToRole(role, email) {
|
|
203
|
+
// Filter before probing for the role: unfiltered, this only ever sees the
|
|
204
|
+
// ten rows of page 1, so an existing role further down the list reads as
|
|
205
|
+
// missing and we try to create it a second time.
|
|
206
|
+
await this.searchForRole(role);
|
|
172
207
|
try {
|
|
173
|
-
await (0, test_1.expect)(this.
|
|
208
|
+
await (0, test_1.expect)(this.roleCell(role).first()).toBeVisible({
|
|
174
209
|
timeout: 10000,
|
|
175
210
|
});
|
|
176
211
|
}
|
|
@@ -186,6 +221,7 @@ class OCIdentityRolesPage {
|
|
|
186
221
|
});
|
|
187
222
|
}
|
|
188
223
|
async deleteRole(name) {
|
|
224
|
+
await this.searchForRole(name);
|
|
189
225
|
const locator = this.deleteRoleButton(name);
|
|
190
226
|
await (0, expectLocatorWithPagination_1.expectLocatorWithPagination)(this.page, locator);
|
|
191
227
|
await locator.click();
|
|
@@ -17,7 +17,7 @@ declare class TaskPanelPage {
|
|
|
17
17
|
private openFilterControls;
|
|
18
18
|
private collapseFilterPanel;
|
|
19
19
|
private static appliedFilter;
|
|
20
|
-
taskCount(name: string): Promise<number>;
|
|
20
|
+
taskCount(name: string, expectedCount?: number): Promise<number>;
|
|
21
21
|
clickTasksTab(): Promise<void>;
|
|
22
22
|
scrollToLastTask(name: string): Promise<void>;
|
|
23
23
|
scrollToFirstTask(name: string): Promise<void>;
|
|
@@ -190,9 +190,37 @@ class TaskPanelPage {
|
|
|
190
190
|
const param = filterParam[option] ?? null;
|
|
191
191
|
return (url) => url.searchParams.get('filter') === param;
|
|
192
192
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
193
|
+
// `expectedCount` turns the single fixed-delay sample into a poll. Tasklist
|
|
194
|
+
// only lists a task once the exporter has indexed the instance, and on these
|
|
195
|
+
// long-lived SaaS RBA clusters that routinely lags past the 20s wait -- the
|
|
196
|
+
// count then comes back one short and the caller's `toBe(before + 1)` fails
|
|
197
|
+
// on a task that does arrive moments later. Callers that have no expectation
|
|
198
|
+
// yet (the baseline read taken before starting a process) still get the
|
|
199
|
+
// fixed wait. The count returned is always the real one, so a task that
|
|
200
|
+
// genuinely never appears still fails the caller's assertion.
|
|
201
|
+
async taskCount(name, expectedCount) {
|
|
202
|
+
const tasks = this.availableTasks.getByText(name, { exact: true });
|
|
203
|
+
if (expectedCount === undefined) {
|
|
204
|
+
await (0, sleep_1.sleep)(20000);
|
|
205
|
+
return tasks.count();
|
|
206
|
+
}
|
|
207
|
+
// Re-reading the same rendered list only finds the new task if the panel
|
|
208
|
+
// refetches while we wait, and the sample that failed sat on 1 of an
|
|
209
|
+
// expected 2 for the whole budget -- the instance was started, the
|
|
210
|
+
// exporter published its task afterwards, and the view never asked again.
|
|
211
|
+
// Reload between samples so each one is an actual query rather than a
|
|
212
|
+
// re-count of the DOM we already had. The count returned is still the
|
|
213
|
+
// real one, so a task that genuinely never arrives still fails the
|
|
214
|
+
// caller's assertion.
|
|
215
|
+
const deadline = Date.now() + 180000;
|
|
216
|
+
let count = await tasks.count();
|
|
217
|
+
while (count !== expectedCount && Date.now() < deadline) {
|
|
218
|
+
await (0, sleep_1.sleep)(5000);
|
|
219
|
+
await this.page.reload();
|
|
220
|
+
await (0, test_1.expect)(this.availableTasks).toBeVisible({ timeout: 60000 });
|
|
221
|
+
count = await tasks.count();
|
|
222
|
+
}
|
|
223
|
+
return count;
|
|
196
224
|
}
|
|
197
225
|
async clickTasksTab() {
|
|
198
226
|
await (0, test_1.expect)(this.tasksTab).toBeVisible({ timeout: 60000 });
|
|
@@ -11,6 +11,7 @@ declare class OperateProcessInstancePage {
|
|
|
11
11
|
constructor(page: Page);
|
|
12
12
|
connectorResultVariableName(name: string): Promise<Locator>;
|
|
13
13
|
connectorResultVariableValue(variableName: string): Promise<Locator>;
|
|
14
|
+
private assertInstanceStatusIcon;
|
|
14
15
|
completedIconAssertion(): Promise<void>;
|
|
15
16
|
activeIconAssertion(): Promise<void>;
|
|
16
17
|
getProcessVariableValue(variableName: string): Promise<Locator>;
|
|
@@ -32,45 +32,47 @@ class OperateProcessInstancePage {
|
|
|
32
32
|
async connectorResultVariableValue(variableName) {
|
|
33
33
|
return await this.page.getByTestId(variableName).locator('td').last();
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
// The instance header renders the status the instance had when the page was
|
|
36
|
+
// loaded, so a status that flips afterwards -- ACTIVE once the instance is
|
|
37
|
+
// exported, COMPLETED once the last task finishes -- only becomes visible on
|
|
38
|
+
// a reload. Sitting out a 60-90s per-attempt timeout before reloading
|
|
39
|
+
// therefore buys nothing: the page under the wait can no longer change. Poll
|
|
40
|
+
// on a short cadence and reload between polls instead, so the status is
|
|
41
|
+
// picked up shortly after it lands rather than at the next minute boundary.
|
|
42
|
+
//
|
|
43
|
+
// The per-attempt timeout escalates rather than staying at its opening
|
|
44
|
+
// value: on a loaded c8Run runner the instance page itself can take longer
|
|
45
|
+
// than the first interval to render, and a fixed short interval would keep
|
|
46
|
+
// reloading a page that was about to show the icon. `deadlineMs` bounds the
|
|
47
|
+
// whole assertion, so a status that never arrives still fails after at least
|
|
48
|
+
// as long as the retry loop this replaces waited.
|
|
49
|
+
async assertInstanceStatusIcon(icon, status, deadlineMs) {
|
|
50
|
+
const attemptTimeouts = [15000, 15000, 30000, 30000, 60000];
|
|
51
|
+
const deadline = Date.now() + deadlineMs;
|
|
52
|
+
let attempt = 0;
|
|
53
|
+
let lastError;
|
|
54
|
+
while (Date.now() < deadline) {
|
|
39
55
|
try {
|
|
40
|
-
await (0, test_1.expect)(
|
|
41
|
-
timeout:
|
|
56
|
+
await (0, test_1.expect)(icon).toBeVisible({
|
|
57
|
+
timeout: attemptTimeouts[Math.min(attempt, attemptTimeouts.length - 1)],
|
|
42
58
|
});
|
|
43
|
-
return;
|
|
59
|
+
return;
|
|
44
60
|
}
|
|
45
61
|
catch (error) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
console.log(
|
|
62
|
+
lastError = error;
|
|
63
|
+
attempt++;
|
|
64
|
+
console.log(`${status} icon not visible yet (attempt ${attempt}). Reloading...`);
|
|
49
65
|
await this.page.reload();
|
|
50
|
-
await (0, sleep_1.sleep)(10000);
|
|
51
66
|
}
|
|
52
67
|
}
|
|
53
|
-
throw new Error(
|
|
68
|
+
throw new Error(`${status} icon not visible after ${deadlineMs}ms and ${attempt} ` +
|
|
69
|
+
`reload(s): ${String(lastError)}`);
|
|
70
|
+
}
|
|
71
|
+
async completedIconAssertion() {
|
|
72
|
+
await this.assertInstanceStatusIcon(this.completedIcon, 'COMPLETED', 420000);
|
|
54
73
|
}
|
|
55
74
|
async activeIconAssertion() {
|
|
56
|
-
|
|
57
|
-
const maxRetries = 3;
|
|
58
|
-
while (retryCount < maxRetries) {
|
|
59
|
-
try {
|
|
60
|
-
await (0, test_1.expect)(this.activeIcon).toBeVisible({
|
|
61
|
-
timeout: 90000,
|
|
62
|
-
});
|
|
63
|
-
return; // Exit the function if the expectation is met
|
|
64
|
-
}
|
|
65
|
-
catch (error) {
|
|
66
|
-
// If the active icon isn't found, reload the page and try again
|
|
67
|
-
retryCount++;
|
|
68
|
-
console.log(`Attempt ${retryCount} failed. Retrying...`);
|
|
69
|
-
await this.page.reload();
|
|
70
|
-
await (0, sleep_1.sleep)(10000);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
throw new Error(`Active icon not visible after ${maxRetries} attempts.`);
|
|
75
|
+
await this.assertInstanceStatusIcon(this.activeIcon, 'ACTIVE', 300000);
|
|
74
76
|
}
|
|
75
77
|
async getProcessVariableValue(variableName) {
|
|
76
78
|
return await this.page
|
|
@@ -97,11 +97,10 @@ class OperateProcessesPage {
|
|
|
97
97
|
return; // Exit the function if the click is successful
|
|
98
98
|
}
|
|
99
99
|
catch (error) {
|
|
100
|
-
//
|
|
100
|
+
// The next iteration opens with its own reload, so reloading and
|
|
101
|
+
// sleeping here only parks the worker for 10s on top of it.
|
|
101
102
|
retryCount++;
|
|
102
103
|
console.log(`Attempt ${retryCount} failed. Retrying...`);
|
|
103
|
-
await this.page.reload();
|
|
104
|
-
await (0, sleep_1.sleep)(10000);
|
|
105
104
|
}
|
|
106
105
|
}
|
|
107
106
|
throw new Error(`Failed to click on process instance link after ${maxRetries} attempts.`);
|
|
@@ -39,10 +39,7 @@ _8_10_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV2', () =>
|
|
|
39
39
|
const process1 = processName + '1';
|
|
40
40
|
const process2 = processName + '2';
|
|
41
41
|
const role = `rbaNoPermission${randomString}`;
|
|
42
|
-
const testUser =
|
|
43
|
-
username: process.env.C8_USERNAME_TEST_4,
|
|
44
|
-
password: process.env.C8_PASSWORD_TEST_4,
|
|
45
|
-
};
|
|
42
|
+
const testUser = (0, users_1.requireTestUser)('C8_USERNAME_TEST_4');
|
|
46
43
|
await _8_10_1.test.step('Navigate to OC Identity and Assign User To A Role', async () => {
|
|
47
44
|
await appsPage.clickCamundaApps();
|
|
48
45
|
await appsPage.clickAdmin(clusterName);
|
|
@@ -149,10 +146,7 @@ _8_10_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV2', () =>
|
|
|
149
146
|
const process2 = processName + '2';
|
|
150
147
|
const role1 = `rbaOnePerm${randomString}1`;
|
|
151
148
|
const role2 = `rbaOnePerm${randomString}2`;
|
|
152
|
-
const testUser =
|
|
153
|
-
username: process.env.C8_USERNAME_TEST_6,
|
|
154
|
-
password: process.env.C8_PASSWORD_TEST_6,
|
|
155
|
-
};
|
|
149
|
+
const testUser = (0, users_1.requireTestUser)('C8_USERNAME_TEST_6');
|
|
156
150
|
await _8_10_1.test.step('Navigate to OC Identity and Assign User To Two Roles', async () => {
|
|
157
151
|
await appsPage.clickCamundaApps();
|
|
158
152
|
await appsPage.clickAdmin(clusterName);
|
|
@@ -365,7 +359,7 @@ _8_10_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV2', () =>
|
|
|
365
359
|
const firstProcessTaskCount = await taskPanelPage.taskCount(process1);
|
|
366
360
|
await taskPanelPage.clickProcessesTab();
|
|
367
361
|
await taskProcessesPage.startProcess(process1);
|
|
368
|
-
const updatedFirstProcessTaskCount = await taskPanelPage.taskCount(process1);
|
|
362
|
+
const updatedFirstProcessTaskCount = await taskPanelPage.taskCount(process1, firstProcessTaskCount + 1);
|
|
369
363
|
(0, test_1.expect)(updatedFirstProcessTaskCount).toBe(firstProcessTaskCount + 1);
|
|
370
364
|
});
|
|
371
365
|
await _8_10_1.test.step('Assert Second Process Can Be Started From Tasklist', async () => {
|
|
@@ -373,7 +367,7 @@ _8_10_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV2', () =>
|
|
|
373
367
|
const secondProcessTaskCount = await taskPanelPage.taskCount(process2);
|
|
374
368
|
await taskPanelPage.clickProcessesTab();
|
|
375
369
|
await taskProcessesPage.startProcess(process2);
|
|
376
|
-
const updatedSecondProcessTaskCount = await taskPanelPage.taskCount(process2);
|
|
370
|
+
const updatedSecondProcessTaskCount = await taskPanelPage.taskCount(process2, secondProcessTaskCount + 1);
|
|
377
371
|
(0, test_1.expect)(updatedSecondProcessTaskCount).toBe(secondProcessTaskCount + 1);
|
|
378
372
|
});
|
|
379
373
|
await _8_10_1.test.step('Navigate to Operate and Assert Both Processes Are Accessible', async () => {
|
|
@@ -395,10 +389,7 @@ _8_10_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV2', () =>
|
|
|
395
389
|
const role = `rbaSelectedProcessesPermission${randomString}`;
|
|
396
390
|
const process1 = processName + '1';
|
|
397
391
|
const process2 = processName + '2';
|
|
398
|
-
const testUser =
|
|
399
|
-
username: process.env.C8_USERNAME_TEST_5,
|
|
400
|
-
password: process.env.C8_PASSWORD_TEST_5,
|
|
401
|
-
};
|
|
392
|
+
const testUser = (0, users_1.requireTestUser)('C8_USERNAME_TEST_5');
|
|
402
393
|
await _8_10_1.test.step('Navigate to OC Identity and Assign User To A Role', async () => {
|
|
403
394
|
await appsPage.clickCamundaApps();
|
|
404
395
|
await appsPage.clickAdmin(clusterName);
|
|
@@ -470,7 +461,7 @@ _8_10_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV2', () =>
|
|
|
470
461
|
const firstProcessTaskCount = await taskPanelPage.taskCount(process1);
|
|
471
462
|
await taskPanelPage.clickProcessesTab();
|
|
472
463
|
await taskProcessesPage.startProcess(process1);
|
|
473
|
-
const updatedFirstProcessTaskCount = await taskPanelPage.taskCount(process1);
|
|
464
|
+
const updatedFirstProcessTaskCount = await taskPanelPage.taskCount(process1, firstProcessTaskCount + 1);
|
|
474
465
|
(0, test_1.expect)(updatedFirstProcessTaskCount).toBe(firstProcessTaskCount + 1);
|
|
475
466
|
});
|
|
476
467
|
await _8_10_1.test.step('Assert Second Process Can Be Started From Tasklist', async () => {
|
|
@@ -478,7 +469,7 @@ _8_10_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV2', () =>
|
|
|
478
469
|
const secondProcessTaskCount = await taskPanelPage.taskCount(process2);
|
|
479
470
|
await taskPanelPage.clickProcessesTab();
|
|
480
471
|
await taskProcessesPage.startProcess(process2);
|
|
481
|
-
const updatedSecondProcessTaskCount = await taskPanelPage.taskCount(process2);
|
|
472
|
+
const updatedSecondProcessTaskCount = await taskPanelPage.taskCount(process2, secondProcessTaskCount + 1);
|
|
482
473
|
(0, test_1.expect)(updatedSecondProcessTaskCount).toBe(secondProcessTaskCount + 1);
|
|
483
474
|
});
|
|
484
475
|
await _8_10_1.test.step('Navigate to Operate and Assert Both Processes Are Accessible', async () => {
|
package/dist/utils/users.d.ts
CHANGED
package/dist/utils/users.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getTestUsers = exports.getTestUser = exports.smokeTestUsers = exports.testUsers = void 0;
|
|
3
|
+
exports.requireTestUser = exports.getTestUsers = exports.getTestUser = exports.smokeTestUsers = exports.testUsers = void 0;
|
|
4
4
|
exports.testUsers = {
|
|
5
5
|
mainUser: {
|
|
6
6
|
username: process.env.C8_USERNAME,
|
|
@@ -115,3 +115,37 @@ function getTestUsers() {
|
|
|
115
115
|
return [exports.testUsers.mainUser];
|
|
116
116
|
}
|
|
117
117
|
exports.getTestUsers = getTestUsers;
|
|
118
|
+
// Specs that exercise a *restricted* user reach for a numbered credential
|
|
119
|
+
// slot directly rather than through getTestUser. The SaaS organisation a run
|
|
120
|
+
// gets is sized by grepping the spec for the slots it names
|
|
121
|
+
// (scripts/flakiness-saas-create-org.sh), so when a slot is missed the
|
|
122
|
+
// variable is simply absent -- and `process.env.C8_USERNAME_TEST_4!` then
|
|
123
|
+
// hands `undefined` to whatever consumes it next. Run 35494241466 spent 150
|
|
124
|
+
// of its 152 repeats that way: roughly twenty minutes each to reach the
|
|
125
|
+
// assign-user modal and die on "locator.fill: value: expected string, got
|
|
126
|
+
// undefined", an error that names neither the slot nor the org that lacks it.
|
|
127
|
+
//
|
|
128
|
+
// Resolve the pair here instead, so a slot the org was never given is
|
|
129
|
+
// reported in one second and by name. Callers pass the username variable as
|
|
130
|
+
// a literal so the org-sizing grep above still sees it in the spec.
|
|
131
|
+
//
|
|
132
|
+
// There is deliberately no fallback to mainUser of the kind getTestUser has:
|
|
133
|
+
// mainUser is the organisation owner, and an RBA flow that runs its
|
|
134
|
+
// "restricted" user as the owner would assert nothing.
|
|
135
|
+
function requireTestUser(usernameVar) {
|
|
136
|
+
const passwordVar = usernameVar.replace('USERNAME', 'PASSWORD');
|
|
137
|
+
const username = process.env[usernameVar];
|
|
138
|
+
const password = process.env[passwordVar];
|
|
139
|
+
const missing = [
|
|
140
|
+
username ? null : usernameVar,
|
|
141
|
+
password ? null : passwordVar,
|
|
142
|
+
].filter(Boolean);
|
|
143
|
+
if (missing.length > 0) {
|
|
144
|
+
throw new Error(`Test user credentials are not set: ${missing.join(', ')}. The ` +
|
|
145
|
+
'organisation for this run was created without that user slot -- ' +
|
|
146
|
+
'see scripts/flakiness-saas-create-org.sh, which sizes the org from ' +
|
|
147
|
+
'the slots the spec names.');
|
|
148
|
+
}
|
|
149
|
+
return { username: username, password: password };
|
|
150
|
+
}
|
|
151
|
+
exports.requireTestUser = requireTestUser;
|