@camunda/e2e-test-suite 0.0.1443 → 0.0.1445
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/.github/c8_versions.json +1 -0
- package/README.md +2 -0
- package/dist/fixtures/SM-8.11.d.ts +77 -0
- package/dist/fixtures/SM-8.11.js +176 -0
- package/dist/pages/SM-8.11/Authorization.d.ts +39 -0
- package/dist/pages/SM-8.11/Authorization.js +349 -0
- package/dist/pages/SM-8.11/ClusterDetailsPage.d.ts +8 -0
- package/dist/pages/SM-8.11/ClusterDetailsPage.js +65 -0
- package/dist/pages/SM-8.11/ClusterPage.d.ts +11 -0
- package/dist/pages/SM-8.11/ClusterPage.js +37 -0
- package/dist/pages/SM-8.11/ConnectorMarketplacePage.d.ts +27 -0
- package/dist/pages/SM-8.11/ConnectorMarketplacePage.js +187 -0
- package/dist/pages/SM-8.11/ConnectorSettingsPage.d.ts +41 -0
- package/dist/pages/SM-8.11/ConnectorSettingsPage.js +153 -0
- package/dist/pages/SM-8.11/ConnectorTemplatePage.d.ts +11 -0
- package/dist/pages/SM-8.11/ConnectorTemplatePage.js +34 -0
- package/dist/pages/SM-8.11/FormJsPage.d.ts +33 -0
- package/dist/pages/SM-8.11/FormJsPage.js +109 -0
- package/dist/pages/SM-8.11/HomePage.d.ts +20 -0
- package/dist/pages/SM-8.11/HomePage.js +49 -0
- package/dist/pages/SM-8.11/IdentityTenantPage.d.ts +53 -0
- package/dist/pages/SM-8.11/IdentityTenantPage.js +264 -0
- package/dist/pages/SM-8.11/IdentityUsersPage.d.ts +92 -0
- package/dist/pages/SM-8.11/IdentityUsersPage.js +485 -0
- package/dist/pages/SM-8.11/KeycloakAdminPage.d.ts +17 -0
- package/dist/pages/SM-8.11/KeycloakAdminPage.js +78 -0
- package/dist/pages/SM-8.11/KeycloakLoginPage.d.ts +12 -0
- package/dist/pages/SM-8.11/KeycloakLoginPage.js +32 -0
- package/dist/pages/SM-8.11/KeycloakUtils.d.ts +17 -0
- package/dist/pages/SM-8.11/KeycloakUtils.js +94 -0
- package/dist/pages/SM-8.11/LoginPage.d.ts +21 -0
- package/dist/pages/SM-8.11/LoginPage.js +112 -0
- package/dist/pages/SM-8.11/ManagementIdentityPage.d.ts +44 -0
- package/dist/pages/SM-8.11/ManagementIdentityPage.js +176 -0
- package/dist/pages/SM-8.11/ModelerCreatePage.d.ts +238 -0
- package/dist/pages/SM-8.11/ModelerCreatePage.js +1950 -0
- package/dist/pages/SM-8.11/ModelerHomePage.d.ts +97 -0
- package/dist/pages/SM-8.11/ModelerHomePage.js +956 -0
- package/dist/pages/SM-8.11/NavigationPage.d.ts +58 -0
- package/dist/pages/SM-8.11/NavigationPage.js +389 -0
- package/dist/pages/SM-8.11/OCIdentityAuthorizationsPage.d.ts +10 -0
- package/dist/pages/SM-8.11/OCIdentityAuthorizationsPage.js +22 -0
- package/dist/pages/SM-8.11/OCIdentityClusterVariablesPage.d.ts +22 -0
- package/dist/pages/SM-8.11/OCIdentityClusterVariablesPage.js +120 -0
- package/dist/pages/SM-8.11/OCIdentityGroupsPage.d.ts +39 -0
- package/dist/pages/SM-8.11/OCIdentityGroupsPage.js +154 -0
- package/dist/pages/SM-8.11/OCIdentityHomePage.d.ts +23 -0
- package/dist/pages/SM-8.11/OCIdentityHomePage.js +115 -0
- package/dist/pages/SM-8.11/OCIdentityMappingRulesPage.d.ts +23 -0
- package/dist/pages/SM-8.11/OCIdentityMappingRulesPage.js +116 -0
- package/dist/pages/SM-8.11/OCIdentityRolesPage.d.ts +61 -0
- package/dist/pages/SM-8.11/OCIdentityRolesPage.js +433 -0
- package/dist/pages/SM-8.11/OCTenantPage.d.ts +73 -0
- package/dist/pages/SM-8.11/OCTenantPage.js +399 -0
- package/dist/pages/SM-8.11/OperateHomePage.d.ts +26 -0
- package/dist/pages/SM-8.11/OperateHomePage.js +139 -0
- package/dist/pages/SM-8.11/OperateProcessInstancePage.d.ts +30 -0
- package/dist/pages/SM-8.11/OperateProcessInstancePage.js +276 -0
- package/dist/pages/SM-8.11/OperateProcessesPage.d.ts +38 -0
- package/dist/pages/SM-8.11/OperateProcessesPage.js +383 -0
- package/dist/pages/SM-8.11/OptimizeCollectionsPage.d.ts +17 -0
- package/dist/pages/SM-8.11/OptimizeCollectionsPage.js +62 -0
- package/dist/pages/SM-8.11/OptimizeDashboardPage.d.ts +14 -0
- package/dist/pages/SM-8.11/OptimizeDashboardPage.js +63 -0
- package/dist/pages/SM-8.11/OptimizeHomePage.d.ts +19 -0
- package/dist/pages/SM-8.11/OptimizeHomePage.js +76 -0
- package/dist/pages/SM-8.11/OptimizeReportPage.d.ts +50 -0
- package/dist/pages/SM-8.11/OptimizeReportPage.js +209 -0
- package/dist/pages/SM-8.11/PlayPage.d.ts +47 -0
- package/dist/pages/SM-8.11/PlayPage.js +389 -0
- package/dist/pages/SM-8.11/ProcessApplicationPage.d.ts +35 -0
- package/dist/pages/SM-8.11/ProcessApplicationPage.js +340 -0
- package/dist/pages/SM-8.11/SettingsPage.d.ts +12 -0
- package/dist/pages/SM-8.11/SettingsPage.js +34 -0
- package/dist/pages/SM-8.11/TaskDetailsPage.d.ts +70 -0
- package/dist/pages/SM-8.11/TaskDetailsPage.js +273 -0
- package/dist/pages/SM-8.11/TaskPanelPage.d.ts +29 -0
- package/dist/pages/SM-8.11/TaskPanelPage.js +320 -0
- package/dist/pages/SM-8.11/TaskProcessesPage.d.ts +15 -0
- package/dist/pages/SM-8.11/TaskProcessesPage.js +77 -0
- package/dist/pages/SM-8.11/UtilitiesPage.d.ts +38 -0
- package/dist/pages/SM-8.11/UtilitiesPage.js +573 -0
- package/dist/pages/SM-8.11/optimizeReportUtils.d.ts +6 -0
- package/dist/pages/SM-8.11/optimizeReportUtils.js +77 -0
- package/dist/tests/SM-8.11/cluster-management-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/cluster-management-user-flows.spec.js +48 -0
- package/dist/tests/SM-8.11/cluster-variables.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/cluster-variables.spec.js +226 -0
- package/dist/tests/SM-8.11/connectors-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/connectors-user-flows.spec.js +307 -0
- package/dist/tests/SM-8.11/document-handling-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/document-handling-user-flows.spec.js +174 -0
- package/dist/tests/SM-8.11/hto-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/hto-user-flows.spec.js +410 -0
- package/dist/tests/SM-8.11/hub-web-modeler-smoke.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/hub-web-modeler-smoke.spec.js +24 -0
- package/dist/tests/SM-8.11/identity-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/identity-user-flows.spec.js +220 -0
- package/dist/tests/SM-8.11/license-key-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/license-key-user-flows.spec.js +461 -0
- package/dist/tests/SM-8.11/login.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/login.spec.js +24 -0
- package/dist/tests/SM-8.11/migration-path-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/migration-path-user-flows.spec.js +263 -0
- package/dist/tests/SM-8.11/mt-enabled-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/mt-enabled-user-flows.spec.js +607 -0
- package/dist/tests/SM-8.11/optimize-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/optimize-user-flows.spec.js +505 -0
- package/dist/tests/SM-8.11/play.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/play.spec.js +144 -0
- package/dist/tests/SM-8.11/rba-enabled-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/rba-enabled-user-flows.spec.js +712 -0
- package/dist/tests/SM-8.11/smoke-tests.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/smoke-tests.spec.js +167 -0
- package/dist/tests/SM-8.11/test-setup.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/test-setup.spec.js +24 -0
- package/dist/tests/SM-8.11/topology-orchestration-smoke.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/topology-orchestration-smoke.spec.js +58 -0
- package/dist/tests/SM-8.11/web-modeler-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/web-modeler-user-flows.spec.js +223 -0
- package/package.json +1 -1
- package/playwright.config.ts +21 -0
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OCTenantPage = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
const expectTextWithPagination_1 = require("../../utils/assertionHelpers/expectTextWithPagination");
|
|
6
|
+
const ROLE_ID_SEARCH_LABEL = /^(Search by role ID|searchByRoleId)$/i;
|
|
7
|
+
class OCTenantPage {
|
|
8
|
+
page;
|
|
9
|
+
createTenantMainButton;
|
|
10
|
+
enterTenantNameInput;
|
|
11
|
+
enterTenantIdInput;
|
|
12
|
+
createTenantSubButton;
|
|
13
|
+
usersTab;
|
|
14
|
+
assignUsersButton;
|
|
15
|
+
usernameInput;
|
|
16
|
+
assignUserFinalButton;
|
|
17
|
+
removeUserButton;
|
|
18
|
+
removeButton;
|
|
19
|
+
openSidebarButton;
|
|
20
|
+
logoutButton;
|
|
21
|
+
assignedApplicationsTab;
|
|
22
|
+
searchApplicationsInput;
|
|
23
|
+
assignApplicationsButtonFinal;
|
|
24
|
+
assignApplicationsButton;
|
|
25
|
+
tenantDescriptionInput;
|
|
26
|
+
modalCloseButton;
|
|
27
|
+
rolesTab;
|
|
28
|
+
assignRoleButton;
|
|
29
|
+
roleIdSearchBox;
|
|
30
|
+
roleIdSearchTrigger;
|
|
31
|
+
assignRoleSubButton;
|
|
32
|
+
assignRoleModal;
|
|
33
|
+
cancelButton;
|
|
34
|
+
gotItButton;
|
|
35
|
+
filterTableSearchbox;
|
|
36
|
+
row;
|
|
37
|
+
constructor(page) {
|
|
38
|
+
this.page = page;
|
|
39
|
+
this.createTenantMainButton = page.getByRole('button', {
|
|
40
|
+
name: 'Create Tenant',
|
|
41
|
+
});
|
|
42
|
+
this.enterTenantNameInput = page.getByPlaceholder('Enter tenant name');
|
|
43
|
+
this.enterTenantIdInput = page.getByPlaceholder('Enter tenant ID');
|
|
44
|
+
this.createTenantSubButton = page
|
|
45
|
+
.getByLabel('Create new tenant')
|
|
46
|
+
.getByRole('button', { name: 'Create tenant' });
|
|
47
|
+
this.usersTab = page.getByRole('tab', { name: 'Users' });
|
|
48
|
+
this.assignUsersButton = page.getByRole('button', { name: 'Assign user' });
|
|
49
|
+
this.usernameInput = page.getByTitle('Username');
|
|
50
|
+
this.assignUserFinalButton = page
|
|
51
|
+
.getByLabel('Assign user')
|
|
52
|
+
.getByRole('button', { name: 'Assign user' });
|
|
53
|
+
this.removeUserButton = page
|
|
54
|
+
.getByRole('dialog', { name: 'Remove user' })
|
|
55
|
+
.getByRole('button', { name: 'Remove user' });
|
|
56
|
+
this.removeButton = page.getByRole('button', { name: 'Remove', exact: true });
|
|
57
|
+
this.openSidebarButton = page.getByLabel('Open User Sidebar');
|
|
58
|
+
this.logoutButton = page.getByRole('button', { name: 'Log out' });
|
|
59
|
+
this.assignedApplicationsTab = page.getByRole('tab', {
|
|
60
|
+
name: 'Assigned applications',
|
|
61
|
+
});
|
|
62
|
+
this.searchApplicationsInput = page.getByPlaceholder('Search applications');
|
|
63
|
+
this.assignApplicationsButtonFinal = page
|
|
64
|
+
.getByLabel('Assign applications')
|
|
65
|
+
.getByRole('button', { name: 'Assign applications' });
|
|
66
|
+
this.assignApplicationsButton = page.getByRole('button', {
|
|
67
|
+
name: 'Assign applications',
|
|
68
|
+
});
|
|
69
|
+
this.tenantDescriptionInput = page.getByTitle('Description');
|
|
70
|
+
this.modalCloseButton = page
|
|
71
|
+
.getByRole('dialog')
|
|
72
|
+
.getByRole('button', { name: 'Close' });
|
|
73
|
+
this.rolesTab = page.getByRole('tab', { name: 'Roles' });
|
|
74
|
+
this.assignRoleButton = page.getByRole('button', { name: 'Assign role' });
|
|
75
|
+
// The design-system Admin shell renders the Assign role modal's search
|
|
76
|
+
// field as a MultiSelect whose trigger is a div[role="combobox"] carrying
|
|
77
|
+
// the label as a child span - it has no placeholder and is not editable,
|
|
78
|
+
// so scope the trigger to the dialog and keep the legacy searchbox match
|
|
79
|
+
// as a fallback, the same dual-shell pattern the table filter locator
|
|
80
|
+
// below uses.
|
|
81
|
+
this.roleIdSearchTrigger = page
|
|
82
|
+
.getByRole('dialog', { name: 'Assign role' })
|
|
83
|
+
.getByRole('combobox', { name: ROLE_ID_SEARCH_LABEL })
|
|
84
|
+
.or(page.getByRole('searchbox', { name: 'Search by Role ID' }))
|
|
85
|
+
.first();
|
|
86
|
+
// Clicking that trigger opens a Radix popover that portals to <body> as a
|
|
87
|
+
// sibling of the dialog, and the editable field is the cmdk <input> inside
|
|
88
|
+
// it. Nothing opened that popover before, so the placeholder-bearing input
|
|
89
|
+
// never existed and the click at it timed out. Match the popover's input,
|
|
90
|
+
// falling back to the placeholder/searchbox that pre-design-system builds
|
|
91
|
+
// render instead of the trigger/popover pair.
|
|
92
|
+
this.roleIdSearchBox = page
|
|
93
|
+
.locator('[data-slot="multi-select-content"] input[cmdk-input]')
|
|
94
|
+
.or(page.getByPlaceholder('Search by role ID'))
|
|
95
|
+
.or(page.getByRole('searchbox', { name: 'Search by Role ID' }))
|
|
96
|
+
.first();
|
|
97
|
+
this.assignRoleModal = page.getByLabel('Assign role');
|
|
98
|
+
this.assignRoleSubButton = this.assignRoleModal.getByRole('button', {
|
|
99
|
+
name: 'Assign role',
|
|
100
|
+
});
|
|
101
|
+
this.cancelButton = page
|
|
102
|
+
.getByRole('dialog')
|
|
103
|
+
.getByRole('button', { name: 'Cancel' });
|
|
104
|
+
this.gotItButton = page.getByRole('button', { name: 'Got it' });
|
|
105
|
+
// The design-system Admin shell replaced Carbon's table toolbar search
|
|
106
|
+
// (labelled "Filter table") with an input whose aria-label is its
|
|
107
|
+
// placeholder, so match the new shell first and fall back to the legacy
|
|
108
|
+
// one - the same dual-shell pattern the Admin nav locators use.
|
|
109
|
+
this.filterTableSearchbox = page
|
|
110
|
+
.getByRole('searchbox', { name: 'Search by tenant ID' })
|
|
111
|
+
.or(page.getByRole('searchbox', { name: 'Filter table' }))
|
|
112
|
+
.first();
|
|
113
|
+
this.row = (name) => this.page.getByRole('row').filter({ hasText: name }).first();
|
|
114
|
+
}
|
|
115
|
+
async clickCreateTenantMainButton() {
|
|
116
|
+
await this.createTenantMainButton.click({ timeout: 60000 });
|
|
117
|
+
}
|
|
118
|
+
async clickCreateTenantSubButton() {
|
|
119
|
+
await this.createTenantSubButton.click({ timeout: 60000 });
|
|
120
|
+
}
|
|
121
|
+
async clickEnterTenantNameInput() {
|
|
122
|
+
await this.enterTenantNameInput.click();
|
|
123
|
+
}
|
|
124
|
+
async fillEnterTenantNameInput(tenantName) {
|
|
125
|
+
await this.enterTenantNameInput.fill(tenantName);
|
|
126
|
+
}
|
|
127
|
+
async clickEnterTenantIdInput() {
|
|
128
|
+
await this.enterTenantIdInput.click();
|
|
129
|
+
}
|
|
130
|
+
async fillEnterTenantIdInput(tenantId) {
|
|
131
|
+
await this.enterTenantIdInput.fill(tenantId);
|
|
132
|
+
}
|
|
133
|
+
async clickUsersTab() {
|
|
134
|
+
await this.usersTab.click({ timeout: 60000 });
|
|
135
|
+
}
|
|
136
|
+
async clickAssignUsersButton() {
|
|
137
|
+
await this.assignUsersButton.click();
|
|
138
|
+
}
|
|
139
|
+
async clickUsernameInput() {
|
|
140
|
+
await this.usernameInput.click();
|
|
141
|
+
}
|
|
142
|
+
async fillUsernameInput(user) {
|
|
143
|
+
await this.usernameInput.fill(user);
|
|
144
|
+
}
|
|
145
|
+
async clickAssignUserFinalButton() {
|
|
146
|
+
try {
|
|
147
|
+
await this.assignUserFinalButton.click();
|
|
148
|
+
await (0, test_1.expect)(this.page.getByRole('dialog')).not.toBeVisible();
|
|
149
|
+
}
|
|
150
|
+
catch (e) {
|
|
151
|
+
if (await this.page.getByText('ALREADY_EXISTS').first().isVisible()) {
|
|
152
|
+
console.log('User is already assigned to tenant');
|
|
153
|
+
await this.clickCancelButton();
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
throw new Error('Assigning user to tenant failed' + e);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async clickRemoveUserButton() {
|
|
161
|
+
await this.removeUserButton.click();
|
|
162
|
+
}
|
|
163
|
+
async clickRemoveButton() {
|
|
164
|
+
await this.removeButton.click();
|
|
165
|
+
}
|
|
166
|
+
async clickTenantDescriptionInput() {
|
|
167
|
+
await this.tenantDescriptionInput.click();
|
|
168
|
+
}
|
|
169
|
+
async fillTenantDescriptionInput(description) {
|
|
170
|
+
await this.tenantDescriptionInput.fill(description);
|
|
171
|
+
}
|
|
172
|
+
async createTenant(tenantName) {
|
|
173
|
+
await this.clickCreateTenantMainButton();
|
|
174
|
+
await this.clickEnterTenantNameInput();
|
|
175
|
+
await this.fillEnterTenantNameInput(tenantName);
|
|
176
|
+
await this.clickEnterTenantIdInput();
|
|
177
|
+
await this.fillEnterTenantIdInput(tenantName);
|
|
178
|
+
await this.clickTenantDescriptionInput();
|
|
179
|
+
await this.fillTenantDescriptionInput('This is a test');
|
|
180
|
+
await this.clickCreateTenantSubButton();
|
|
181
|
+
await this.assertTenantCreated();
|
|
182
|
+
}
|
|
183
|
+
async assertTenantCreated() {
|
|
184
|
+
try {
|
|
185
|
+
await (0, test_1.expect)(this.page.getByText('successfully created')).toBeVisible({
|
|
186
|
+
timeout: 8000,
|
|
187
|
+
});
|
|
188
|
+
await this.clickGotItButton();
|
|
189
|
+
}
|
|
190
|
+
catch (error) {
|
|
191
|
+
if (await this.page.getByText('ALREADY_EXISTS').first().isVisible()) {
|
|
192
|
+
await this.clickCancelButton();
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
throw new Error('Creating tenant failed' + error);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
async clickCancelButton() {
|
|
200
|
+
await this.cancelButton.click({ timeout: 30000 });
|
|
201
|
+
}
|
|
202
|
+
async clickGotItButton() {
|
|
203
|
+
await this.gotItButton.click({ timeout: 30000 });
|
|
204
|
+
}
|
|
205
|
+
async filterTable(tenantName) {
|
|
206
|
+
await this.filterTableSearchbox.click({ timeout: 60000 });
|
|
207
|
+
await this.filterTableSearchbox.fill(tenantName);
|
|
208
|
+
await this.filterTableSearchbox.press('Enter');
|
|
209
|
+
}
|
|
210
|
+
async clickTenant(tenantName) {
|
|
211
|
+
const maxRetries = 10;
|
|
212
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
213
|
+
try {
|
|
214
|
+
await this.page.reload();
|
|
215
|
+
await this.page.waitForLoadState('networkidle');
|
|
216
|
+
await this.filterTable(tenantName);
|
|
217
|
+
const tenant = this.page.getByText(tenantName).first();
|
|
218
|
+
await (0, test_1.expect)(tenant).toBeVisible({ timeout: 30000 });
|
|
219
|
+
await tenant.click({ timeout: 30000 });
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
console.warn(`Attempt ${attempt} to click tenant '${tenantName}' failed. Retrying...`, error);
|
|
224
|
+
if (attempt === maxRetries) {
|
|
225
|
+
throw new Error(`Failed to click tenant '${tenantName}' after ${maxRetries} attempts. Last error: ${error}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async assignUserToTenant(user) {
|
|
231
|
+
await this.clickUsersTab();
|
|
232
|
+
await this.clickAssignUsersButton();
|
|
233
|
+
await this.clickUsernameInput();
|
|
234
|
+
await this.fillUsernameInput(user);
|
|
235
|
+
await this.clickAssignUserFinalButton();
|
|
236
|
+
}
|
|
237
|
+
async removeUserFromTenant(tenantName) {
|
|
238
|
+
await (0, expectTextWithPagination_1.expectTextWithPagination)(this.page, tenantName);
|
|
239
|
+
await this.page.getByText(tenantName).first().click();
|
|
240
|
+
await this.clickUsersTab();
|
|
241
|
+
await this.clickRemoveButton();
|
|
242
|
+
await this.clickRemoveUserButton();
|
|
243
|
+
await (0, test_1.expect)(this.page.getByText('Tenant member has been removed.')).toBeVisible({
|
|
244
|
+
timeout: 60000,
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
async clickOpenSidebarButton() {
|
|
248
|
+
await this.openSidebarButton.click({ timeout: 60000 });
|
|
249
|
+
}
|
|
250
|
+
async clickLogoutButton() {
|
|
251
|
+
await this.logoutButton.click({ timeout: 60000 });
|
|
252
|
+
}
|
|
253
|
+
async assignClientToTenant(clientName) {
|
|
254
|
+
await this.clickAssignedApplicationsTab();
|
|
255
|
+
if (await this.page.getByText(clientName, { exact: true }).isVisible()) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
await this.clickAssignApplicationsButton();
|
|
260
|
+
await this.clickSearchApplicationsInput();
|
|
261
|
+
await this.fillSearchApplicationsInput(clientName);
|
|
262
|
+
await this.page.getByText(clientName, { exact: true }).click();
|
|
263
|
+
await this.clickAssignApplicationsFinalButton();
|
|
264
|
+
await (0, test_1.expect)(this.page.getByText('Applications assigned')).toBeVisible({
|
|
265
|
+
timeout: 60000,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async clickAssignedApplicationsTab() {
|
|
270
|
+
await this.assignedApplicationsTab.click({ timeout: 60000 });
|
|
271
|
+
}
|
|
272
|
+
async clickSearchApplicationsInput() {
|
|
273
|
+
await this.searchApplicationsInput.click({ timeout: 60000 });
|
|
274
|
+
}
|
|
275
|
+
async fillSearchApplicationsInput(clientName) {
|
|
276
|
+
await this.searchApplicationsInput.fill(clientName);
|
|
277
|
+
}
|
|
278
|
+
async clickAssignApplicationsFinalButton() {
|
|
279
|
+
await this.assignApplicationsButtonFinal.click({ timeout: 60000 });
|
|
280
|
+
}
|
|
281
|
+
async clickAssignApplicationsButton() {
|
|
282
|
+
await this.assignApplicationsButton.click({ timeout: 60000 });
|
|
283
|
+
}
|
|
284
|
+
async clickRolesTab() {
|
|
285
|
+
await this.rolesTab.click();
|
|
286
|
+
}
|
|
287
|
+
async clickAssignRoleButton() {
|
|
288
|
+
await this.assignRoleButton.click();
|
|
289
|
+
}
|
|
290
|
+
async clickRoleIdSearchBox() {
|
|
291
|
+
await this.roleIdSearchTrigger.click({ timeout: 60000 });
|
|
292
|
+
await (0, test_1.expect)(this.roleIdSearchBox).toBeVisible({ timeout: 60000 });
|
|
293
|
+
}
|
|
294
|
+
// Selecting an option or losing focus dismisses the popover, which unmounts
|
|
295
|
+
// the search input, so reopen it before the next term is typed.
|
|
296
|
+
async ensureRoleIdSearchBoxOpen() {
|
|
297
|
+
const open = await this.roleIdSearchBox
|
|
298
|
+
.waitFor({ state: 'visible', timeout: 5000 })
|
|
299
|
+
.then(() => true)
|
|
300
|
+
.catch(() => false);
|
|
301
|
+
if (!open) {
|
|
302
|
+
await this.clickRoleIdSearchBox();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async fillRoleIdSearchBox(roleId, roleIdentifier) {
|
|
306
|
+
// The option row renders the ID followed by the name, so the name is the
|
|
307
|
+
// tail of `roleIdentifier`.
|
|
308
|
+
const roleName = roleIdentifier.startsWith(roleId)
|
|
309
|
+
? roleIdentifier.slice(roleId.length)
|
|
310
|
+
: '';
|
|
311
|
+
// The design-system row separates the two parts with an em dash
|
|
312
|
+
// ('connectors — Connectors') while the legacy Carbon row concatenates
|
|
313
|
+
// them ('connectorsConnectors'), so match both with an optional
|
|
314
|
+
// dash/whitespace run between the ID and the name.
|
|
315
|
+
const escapeForRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
316
|
+
const rolePattern = new RegExp(`${escapeForRegExp(roleId)}[-\\s\\u2010-\\u2015]*${escapeForRegExp(roleName)}`);
|
|
317
|
+
// The design-system shell portals the matches into a listbox rendered as
|
|
318
|
+
// a sibling of the dialog, so they cannot be scoped to the modal. The
|
|
319
|
+
// legacy Carbon list stays inside it and must remain scoped: the tenant's
|
|
320
|
+
// own roles table sits behind the modal and carries the same role text, so
|
|
321
|
+
// an unscoped match resolves to that row and its click is intercepted by
|
|
322
|
+
// the modal overlay.
|
|
323
|
+
const result = this.page
|
|
324
|
+
.getByRole('option')
|
|
325
|
+
.filter({ hasText: rolePattern })
|
|
326
|
+
.or(this.assignRoleModal.getByText(rolePattern))
|
|
327
|
+
.first();
|
|
328
|
+
// The dropdown no longer filters client-side: it queries
|
|
329
|
+
// `/v2/roles/search` with `name: {$like: '*<typed>*'}`, which is
|
|
330
|
+
// case-sensitive, so the role ID ('connectors') never matches the default
|
|
331
|
+
// role's name ('Connectors'): search the ID first for the shells that
|
|
332
|
+
// still match on it, then fall back to the name.
|
|
333
|
+
const searchTerms = [roleId];
|
|
334
|
+
if (roleName !== '' && roleName !== roleId) {
|
|
335
|
+
searchTerms.push(roleName);
|
|
336
|
+
}
|
|
337
|
+
// The dropdown only queries for a non-empty value, so clearing the box
|
|
338
|
+
// between terms resets it before the next one is typed.
|
|
339
|
+
await (0, test_1.expect)(async () => {
|
|
340
|
+
for (const searchTerm of searchTerms) {
|
|
341
|
+
await this.ensureRoleIdSearchBoxOpen();
|
|
342
|
+
await this.roleIdSearchBox.fill('');
|
|
343
|
+
await this.page.waitForLoadState('networkidle');
|
|
344
|
+
// The cmdk input only issues its server-side query on real
|
|
345
|
+
// keystrokes, so type the term in rather than setting it in one shot.
|
|
346
|
+
await this.roleIdSearchBox.pressSequentially(searchTerm);
|
|
347
|
+
const offered = await result
|
|
348
|
+
.waitFor({ state: 'visible', timeout: 15000 })
|
|
349
|
+
.then(() => true)
|
|
350
|
+
.catch(() => false);
|
|
351
|
+
if (offered) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
throw new Error(`Role ${roleIdentifier} was not offered by the assign role search for` +
|
|
356
|
+
` any of: ${searchTerms.join(', ')}`);
|
|
357
|
+
}).toPass({ timeout: 120000, intervals: [2000] });
|
|
358
|
+
await result.click();
|
|
359
|
+
}
|
|
360
|
+
async clickAssignRoleSubButton() {
|
|
361
|
+
await this.assignRoleSubButton.click();
|
|
362
|
+
}
|
|
363
|
+
async isRoleAssigned(roleId) {
|
|
364
|
+
return this.row(roleId)
|
|
365
|
+
.waitFor({ state: 'visible', timeout: 15000 })
|
|
366
|
+
.then(() => true)
|
|
367
|
+
.catch(() => false);
|
|
368
|
+
}
|
|
369
|
+
async assignRoleToTenant(roleId, roleIdentifier) {
|
|
370
|
+
await this.clickRolesTab();
|
|
371
|
+
await this.page.waitForLoadState('networkidle');
|
|
372
|
+
// The roles table renders from its own fetch, which `networkidle` can
|
|
373
|
+
// resolve ahead of, so an immediate `isVisible()` reads the table as empty
|
|
374
|
+
// and reopens the modal for a role that is already assigned. The assign
|
|
375
|
+
// dropdown never offers an already-assigned role, so that attempt can
|
|
376
|
+
// never converge.
|
|
377
|
+
if (await this.isRoleAssigned(roleId)) {
|
|
378
|
+
console.log(`Role ${roleId} is already assigned to tenant`);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
381
|
+
await this.clickAssignRoleButton();
|
|
382
|
+
await this.page.waitForLoadState('networkidle');
|
|
383
|
+
await this.clickRoleIdSearchBox();
|
|
384
|
+
await this.fillRoleIdSearchBox(roleId, roleIdentifier);
|
|
385
|
+
await this.clickAssignRoleSubButton();
|
|
386
|
+
await (0, test_1.expect)(this.page.getByLabel('Assign role')).not.toBeVisible({
|
|
387
|
+
timeout: 30000,
|
|
388
|
+
});
|
|
389
|
+
// Use retry-with-reload pattern for role visibility (MT operations need time for data replication)
|
|
390
|
+
await (0, test_1.expect)(async () => {
|
|
391
|
+
if (!(await this.row(roleId).isVisible())) {
|
|
392
|
+
await this.page.reload();
|
|
393
|
+
await this.page.waitForLoadState('networkidle');
|
|
394
|
+
}
|
|
395
|
+
await (0, test_1.expect)(this.row(roleId)).toBeVisible({ timeout: 10000 });
|
|
396
|
+
}).toPass({ timeout: 60000, intervals: [5000] });
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
exports.OCTenantPage = OCTenantPage;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
declare class OperateHomePage {
|
|
3
|
+
private page;
|
|
4
|
+
readonly operateBanner: Locator;
|
|
5
|
+
readonly processesTab: Locator;
|
|
6
|
+
readonly informationDialog: Locator;
|
|
7
|
+
readonly editVariableButton: Locator;
|
|
8
|
+
readonly variableValueInput: Locator;
|
|
9
|
+
readonly saveVariableButton: Locator;
|
|
10
|
+
readonly editVariableSpinner: Locator;
|
|
11
|
+
readonly licenseKeyTagNonProduction: Locator;
|
|
12
|
+
readonly licenseKeyTagProduction: Locator;
|
|
13
|
+
readonly processPageHeading: Locator;
|
|
14
|
+
readonly applyButton: Locator;
|
|
15
|
+
readonly openButton: Locator;
|
|
16
|
+
constructor(page: Page);
|
|
17
|
+
clickProcessesTab(): Promise<void>;
|
|
18
|
+
closeInformationDialog(): Promise<void>;
|
|
19
|
+
clickEditVariableButton(variableName: string): Promise<void>;
|
|
20
|
+
clickVariableValueInput(): Promise<void>;
|
|
21
|
+
clearVariableValueInput(): Promise<void>;
|
|
22
|
+
fillVariableValueInput(newValue: string): Promise<void>;
|
|
23
|
+
clickSaveVariableButton(): Promise<void>;
|
|
24
|
+
openProcess(processName: string): Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
export { OperateHomePage };
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OperateHomePage = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
const clickLocatorWithRetry_1 = require("../../utils/assertionHelpers/clickLocatorWithRetry");
|
|
6
|
+
const expectLocatorWithRetry_1 = require("../../utils/assertionHelpers/expectLocatorWithRetry");
|
|
7
|
+
const constants_1 = require("../../utils/constants");
|
|
8
|
+
class OperateHomePage {
|
|
9
|
+
page;
|
|
10
|
+
operateBanner;
|
|
11
|
+
processesTab;
|
|
12
|
+
informationDialog;
|
|
13
|
+
editVariableButton;
|
|
14
|
+
variableValueInput;
|
|
15
|
+
saveVariableButton;
|
|
16
|
+
editVariableSpinner;
|
|
17
|
+
licenseKeyTagNonProduction;
|
|
18
|
+
licenseKeyTagProduction;
|
|
19
|
+
processPageHeading;
|
|
20
|
+
applyButton;
|
|
21
|
+
openButton;
|
|
22
|
+
constructor(page) {
|
|
23
|
+
this.page = page;
|
|
24
|
+
// Same Design System header rollout NavigationPage.operatePageBanner
|
|
25
|
+
// covers: the home link is aria-label="Camunda Operate" once
|
|
26
|
+
// `navV2Enabled` is on, and "Camunda logo Operate" while it is off.
|
|
27
|
+
this.operateBanner = page
|
|
28
|
+
.getByRole('link', { name: 'Camunda logo Operate' })
|
|
29
|
+
.or(page.getByRole('link', { name: 'Camunda Operate', exact: true }))
|
|
30
|
+
.first();
|
|
31
|
+
this.processesTab = page.getByRole('link', {
|
|
32
|
+
name: 'Processes',
|
|
33
|
+
exact: true,
|
|
34
|
+
});
|
|
35
|
+
this.informationDialog = page.getByRole('button', {
|
|
36
|
+
name: 'Close this dialog',
|
|
37
|
+
});
|
|
38
|
+
this.editVariableButton = page.getByTestId('edit-variable-button');
|
|
39
|
+
this.variableValueInput = page.getByTestId('edit-variable-value');
|
|
40
|
+
this.saveVariableButton = page.getByLabel('Save variable');
|
|
41
|
+
this.editVariableSpinner = page
|
|
42
|
+
.getByTestId('variable-operation-spinner')
|
|
43
|
+
.locator('circle')
|
|
44
|
+
.nth(1);
|
|
45
|
+
this.licenseKeyTagNonProduction = page
|
|
46
|
+
.getByText('Non-production license')
|
|
47
|
+
.first();
|
|
48
|
+
this.licenseKeyTagProduction = page.getByText('Production license').first();
|
|
49
|
+
this.processPageHeading = page
|
|
50
|
+
.getByTestId('expanded-panel')
|
|
51
|
+
.getByRole('heading', { name: 'Process' });
|
|
52
|
+
this.applyButton = page.getByRole('button', { name: 'Apply' });
|
|
53
|
+
this.openButton = page
|
|
54
|
+
.getByTestId('variable-testVariable')
|
|
55
|
+
.getByLabel('Open');
|
|
56
|
+
}
|
|
57
|
+
async clickProcessesTab() {
|
|
58
|
+
// Called right after navigating into Operate, a Vite SPA whose nav bar can
|
|
59
|
+
// take longer than clickLocatorWithRetry's default 10s visibilityTimeout to
|
|
60
|
+
// render on a loaded cluster — so all 3 attempts could burn inside 30s and
|
|
61
|
+
// throw while the link was still on its way. Give each attempt the 30s the
|
|
62
|
+
// callers that pre-wait this same link already assume.
|
|
63
|
+
await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.processesTab, {
|
|
64
|
+
totalTimeout: 120000,
|
|
65
|
+
visibilityTimeout: 30000,
|
|
66
|
+
});
|
|
67
|
+
await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.processPageHeading, {
|
|
68
|
+
// Defaults (10s per attempt, 60s total) gave the Processes panel less
|
|
69
|
+
// budget than the tab click above already assumes it needs on a loaded
|
|
70
|
+
// cluster, so all three attempts could expire while the view was still
|
|
71
|
+
// rendering. Match the 30s/120s the click uses.
|
|
72
|
+
visibilityTimeout: 30000,
|
|
73
|
+
totalTimeout: 120000,
|
|
74
|
+
preAction: async () => {
|
|
75
|
+
await (0, test_1.expect)(this.processesTab).toBeVisible({
|
|
76
|
+
timeout: constants_1._1_SECOND_IN_MS * 4,
|
|
77
|
+
});
|
|
78
|
+
await this.processesTab.click({ timeout: constants_1._1_SECOND_IN_MS * 4 });
|
|
79
|
+
},
|
|
80
|
+
postAction: async () => {
|
|
81
|
+
// Operate is a Vite SPA: a transient module/CSS preload failure
|
|
82
|
+
// renders a "Something went wrong - Please reload the page" screen
|
|
83
|
+
// on which the Processes tab click is a no-op. Reload to fetch fresh
|
|
84
|
+
// assets so the next attempt can render the Processes view.
|
|
85
|
+
await this.page.reload({ waitUntil: 'domcontentloaded' });
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async closeInformationDialog() {
|
|
90
|
+
await this.informationDialog.click();
|
|
91
|
+
}
|
|
92
|
+
async clickEditVariableButton(variableName) {
|
|
93
|
+
const editVariableButton = 'variable-' + variableName;
|
|
94
|
+
const button = this.page.getByTestId(editVariableButton).getByLabel('Edit');
|
|
95
|
+
await (0, test_1.expect)(button).toBeVisible({ timeout: 30000 });
|
|
96
|
+
await button.click();
|
|
97
|
+
}
|
|
98
|
+
async clickVariableValueInput() {
|
|
99
|
+
await this.variableValueInput.click();
|
|
100
|
+
}
|
|
101
|
+
async clearVariableValueInput() {
|
|
102
|
+
await this.variableValueInput.clear();
|
|
103
|
+
}
|
|
104
|
+
async fillVariableValueInput(newValue) {
|
|
105
|
+
await this.openButton.click();
|
|
106
|
+
await this.page
|
|
107
|
+
.getByLabel('Edit Variable "testVariable"')
|
|
108
|
+
.getByText('"testValue"')
|
|
109
|
+
.click();
|
|
110
|
+
// Replace the whole JSON document in one shot. Typing it per-character
|
|
111
|
+
// raced the editor's re-render: after a couple of keystrokes the caret
|
|
112
|
+
// jumped outside the closing quote, leaving invalid JSON such as
|
|
113
|
+
// `"up"datedValue`. Apply then rejects it silently, the modal stays open
|
|
114
|
+
// and the old value is still on screen, so the caller's
|
|
115
|
+
// `not.toBeVisible('"testValue"')` assertion times out. insertText emits a
|
|
116
|
+
// single input event, so there is no intermediate state to race.
|
|
117
|
+
//
|
|
118
|
+
// Pass only the OPENING quote: Monaco auto-closes it. Supplying both ends
|
|
119
|
+
// yields `"updatedValue""` -- invalid JSON, and Apply rejects it exactly
|
|
120
|
+
// like the per-character version did. Same recipe as
|
|
121
|
+
// pages/c8Run-8.10/OperateHomePage.ts.
|
|
122
|
+
//
|
|
123
|
+
// Select with Home + Shift+End rather than ControlOrMeta+A: headless
|
|
124
|
+
// Chromium on macOS does not apply Meta+A as select-all inside Monaco's
|
|
125
|
+
// native edit context, so the insert landed mid-string. The value is a
|
|
126
|
+
// single-line JSON string, so selecting the line selects the document on
|
|
127
|
+
// every platform.
|
|
128
|
+
await this.page.keyboard.press('Home');
|
|
129
|
+
await this.page.keyboard.press('Shift+End');
|
|
130
|
+
await this.page.keyboard.insertText(`"${newValue}`);
|
|
131
|
+
}
|
|
132
|
+
async clickSaveVariableButton() {
|
|
133
|
+
await this.applyButton.click();
|
|
134
|
+
}
|
|
135
|
+
async openProcess(processName) {
|
|
136
|
+
await this.page.getByText(processName).click();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
exports.OperateHomePage = OperateHomePage;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
declare class OperateProcessInstancePage {
|
|
3
|
+
private page;
|
|
4
|
+
readonly diagram: Locator;
|
|
5
|
+
readonly completedIcon: Locator;
|
|
6
|
+
readonly diagramSpinner: Locator;
|
|
7
|
+
readonly activeIcon: Locator;
|
|
8
|
+
readonly incidentIcon: Locator;
|
|
9
|
+
readonly variablesList: Locator;
|
|
10
|
+
readonly messageVariable: Locator;
|
|
11
|
+
constructor(page: Page);
|
|
12
|
+
connectorResultVariableName(name: string): Promise<Locator>;
|
|
13
|
+
connectorResultVariableValue(variableName: string): Promise<Locator>;
|
|
14
|
+
completedIconAssertion(): Promise<void>;
|
|
15
|
+
activeIconAssertion(): Promise<void>;
|
|
16
|
+
activeUserTaskIconVisibleAssertion(taskName: string): Promise<void>;
|
|
17
|
+
activeUserTaskIconNotVisibleAssertion(taskName: string): Promise<void>;
|
|
18
|
+
tenantNameAssertion(name: string): Promise<void>;
|
|
19
|
+
incidentIconAssertion(): Promise<void>;
|
|
20
|
+
assertEitherIncidentOrActiveIconVisible(): Promise<string>;
|
|
21
|
+
assertEitherIncidentOrCompletedIconVisible(): Promise<string>;
|
|
22
|
+
assertProcessCompleteStatusWithRetry(timeout?: number, maxRetries?: number): Promise<void>;
|
|
23
|
+
assertProcessVariableContainsText(variableName: string, text: string): Promise<void>;
|
|
24
|
+
private captureVariableJsonValue;
|
|
25
|
+
assertResultVariableVisibleWithRetry(variableName: string): Promise<void>;
|
|
26
|
+
assertActiveTokenIsPresent(): Promise<void>;
|
|
27
|
+
assertBusinessIdVisible(businessId: string): Promise<void>;
|
|
28
|
+
assertVariablesListVisible(timeout?: number): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
export { OperateProcessInstancePage };
|