@camunda/e2e-test-suite 0.0.1443 → 0.0.1444
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,383 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OperateProcessesPage = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
const sleep_1 = require("../../utils/sleep");
|
|
6
|
+
const constants_1 = require("../../utils/constants");
|
|
7
|
+
const env_1 = require("../../utils/env");
|
|
8
|
+
class OperateProcessesPage {
|
|
9
|
+
page;
|
|
10
|
+
processResultCount;
|
|
11
|
+
processActiveCheckbox;
|
|
12
|
+
processCompletedCheckbox;
|
|
13
|
+
processRunningInstancesCheckbox;
|
|
14
|
+
processIncidentsCheckbox;
|
|
15
|
+
processPageHeading;
|
|
16
|
+
noMatchingInstancesMessage;
|
|
17
|
+
processFinishedInstancesCheckbox;
|
|
18
|
+
moreFiltersButton;
|
|
19
|
+
processInstanceKeyTextBox;
|
|
20
|
+
whatsNewPopUp;
|
|
21
|
+
gotItButton;
|
|
22
|
+
processesTabLink;
|
|
23
|
+
constructor(page) {
|
|
24
|
+
this.page = page;
|
|
25
|
+
this.processResultCount = page.getByTestId('result-count');
|
|
26
|
+
this.processesTabLink = page.getByRole('link', { name: 'Processes' });
|
|
27
|
+
this.processActiveCheckbox = page.locator('input#active');
|
|
28
|
+
this.processCompletedCheckbox = page.locator('input#completed');
|
|
29
|
+
this.processRunningInstancesCheckbox = page
|
|
30
|
+
.getByTestId('filter-running-instances')
|
|
31
|
+
.getByRole('checkbox');
|
|
32
|
+
this.processIncidentsCheckbox = page.locator('input#incidents');
|
|
33
|
+
this.processPageHeading = page
|
|
34
|
+
.getByTestId('expanded-panel')
|
|
35
|
+
.getByRole('heading', { name: 'Process' });
|
|
36
|
+
this.noMatchingInstancesMessage = page.getByText('There are no Instances matching this filter set');
|
|
37
|
+
this.processFinishedInstancesCheckbox = page
|
|
38
|
+
.getByTestId('filter-finished-instances')
|
|
39
|
+
.getByRole('checkbox');
|
|
40
|
+
this.moreFiltersButton = page.getByRole('button', { name: 'More filters' });
|
|
41
|
+
this.processInstanceKeyTextBox = page
|
|
42
|
+
.locator('#processInstanceKey')
|
|
43
|
+
.or(page.locator('#ids'));
|
|
44
|
+
this.whatsNewPopUp = page.getByRole('heading', {
|
|
45
|
+
name: "Here's what moved in Operate",
|
|
46
|
+
});
|
|
47
|
+
this.gotItButton = page.getByRole('button', { name: 'Got it' });
|
|
48
|
+
}
|
|
49
|
+
// Carbon DS checkboxes are hidden inputs — click the label to trigger React's
|
|
50
|
+
// synthetic event. Operate's filter sidebar is URL-bound and the URL→state
|
|
51
|
+
// hydration that runs on mount can revert a click that races with it, so the
|
|
52
|
+
// setter re-clicks (bounded) when the post-click assertion fails.
|
|
53
|
+
async setCheckboxState(checkbox, desired) {
|
|
54
|
+
await checkbox.waitFor({ state: 'attached', timeout: constants_1._1_SECOND_IN_MS * 10 });
|
|
55
|
+
const checkboxId = await checkbox.getAttribute('id');
|
|
56
|
+
const MAX_ATTEMPTS = 4;
|
|
57
|
+
let lastError;
|
|
58
|
+
for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt++) {
|
|
59
|
+
if ((await checkbox.isChecked()) !== desired) {
|
|
60
|
+
if (checkboxId) {
|
|
61
|
+
await this.page
|
|
62
|
+
.locator(`label[for="${checkboxId}"]`)
|
|
63
|
+
.click({ timeout: constants_1._1_SECOND_IN_MS * 10 });
|
|
64
|
+
}
|
|
65
|
+
else if (desired) {
|
|
66
|
+
await checkbox.check({ force: true, timeout: constants_1._1_SECOND_IN_MS * 10 });
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
await checkbox.uncheck({ force: true, timeout: constants_1._1_SECOND_IN_MS * 10 });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
await (0, test_1.expect)(checkbox).toBeChecked({
|
|
74
|
+
checked: desired,
|
|
75
|
+
timeout: constants_1._1_SECOND_IN_MS * 5,
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
lastError = err;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
throw lastError instanceof Error
|
|
84
|
+
? lastError
|
|
85
|
+
: new Error(`Checkbox did not settle to checked=${desired} after ${MAX_ATTEMPTS} attempts`);
|
|
86
|
+
}
|
|
87
|
+
async checkCheckbox(checkbox) {
|
|
88
|
+
await this.setCheckboxState(checkbox, true);
|
|
89
|
+
}
|
|
90
|
+
async uncheckCheckbox(checkbox) {
|
|
91
|
+
await this.setCheckboxState(checkbox, false);
|
|
92
|
+
}
|
|
93
|
+
async uncheckCompletedCheckbox() {
|
|
94
|
+
await this.uncheckCheckbox(this.processCompletedCheckbox);
|
|
95
|
+
}
|
|
96
|
+
async uncheckActiveCheckbox() {
|
|
97
|
+
await this.uncheckCheckbox(this.processActiveCheckbox);
|
|
98
|
+
}
|
|
99
|
+
async toggleActiveCheckboxOn() {
|
|
100
|
+
await this.uncheckCompletedCheckbox();
|
|
101
|
+
// Set — never toggle — the Incidents filter: applyProcessStateFilter is
|
|
102
|
+
// re-applied after every reload in clickProcessInstanceLink, so a blind
|
|
103
|
+
// flip oscillates the filter between passes and the lookup never converges.
|
|
104
|
+
await this.checkCheckbox(this.processIncidentsCheckbox);
|
|
105
|
+
await this.processActiveCheckbox.waitFor({
|
|
106
|
+
state: 'attached',
|
|
107
|
+
timeout: constants_1._1_SECOND_IN_MS * 10,
|
|
108
|
+
});
|
|
109
|
+
if (await this.processActiveCheckbox.isChecked({ timeout: constants_1._1_SECOND_IN_MS })) {
|
|
110
|
+
await this.uncheckCheckbox(this.processActiveCheckbox);
|
|
111
|
+
await (0, sleep_1.sleep)(200); // Required to skip the debounce protection on the frontend
|
|
112
|
+
await this.checkCheckbox(this.processActiveCheckbox);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
await this.checkCheckbox(this.processActiveCheckbox);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async toggleCompletedCheckbox() {
|
|
119
|
+
await this.uncheckActiveCheckbox();
|
|
120
|
+
await this.checkCheckbox(this.processIncidentsCheckbox);
|
|
121
|
+
await this.processCompletedCheckbox.waitFor({
|
|
122
|
+
state: 'attached',
|
|
123
|
+
timeout: constants_1._1_SECOND_IN_MS * 10,
|
|
124
|
+
});
|
|
125
|
+
if (await this.processCompletedCheckbox.isChecked({ timeout: constants_1._1_SECOND_IN_MS })) {
|
|
126
|
+
await this.uncheckCheckbox(this.processCompletedCheckbox);
|
|
127
|
+
await (0, sleep_1.sleep)(200); // Required to skip the debounce protection on the frontend
|
|
128
|
+
await this.checkCheckbox(this.processCompletedCheckbox);
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
await this.checkCheckbox(this.processCompletedCheckbox);
|
|
132
|
+
await (0, sleep_1.sleep)(200);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async applyProcessStateFilter(type) {
|
|
136
|
+
if (type === 'active') {
|
|
137
|
+
await this.toggleActiveCheckboxOn();
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
await this.toggleCompletedCheckbox();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// Operate raises the "Here's what moved in Operate" modal on a first page
|
|
144
|
+
// load, and every test here logs in as a freshly created user, so it is up on
|
|
145
|
+
// the Processes view. Its Carbon overlay swallows clicks on the Processes tab
|
|
146
|
+
// link and on the filter checkbox labels, which is why a reload-recovery pass
|
|
147
|
+
// could make no progress at all. Dismiss it before interacting with the view,
|
|
148
|
+
// not only after an instance page has opened.
|
|
149
|
+
async dismissWhatsNewPopUp() {
|
|
150
|
+
// isVisible() returns immediately — Playwright ignores its `timeout`
|
|
151
|
+
// option — so this probe got none of the 2s grace it reads as having. Right
|
|
152
|
+
// after a reload Operate has usually not mounted the modal yet, the probe
|
|
153
|
+
// reported "not open", and the Carbon overlay then intercepted every click
|
|
154
|
+
// in the recovery loop below until the 720s test timeout. waitFor spends
|
|
155
|
+
// the 2s the probe was written to spend.
|
|
156
|
+
const isOpen = await this.whatsNewPopUp
|
|
157
|
+
.waitFor({ state: 'visible', timeout: constants_1._1_SECOND_IN_MS * 2 })
|
|
158
|
+
.then(() => true)
|
|
159
|
+
.catch(() => false);
|
|
160
|
+
if (!isOpen) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
await this.gotItButton.click({ timeout: constants_1._1_SECOND_IN_MS * 10 });
|
|
164
|
+
await (0, test_1.expect)(this.whatsNewPopUp).toBeHidden({
|
|
165
|
+
timeout: constants_1._1_SECOND_IN_MS * 10,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
async checkTableForProcess(processName) {
|
|
169
|
+
try {
|
|
170
|
+
const processTable = this.page.locator('[aria-label="Process Instances Panel"]');
|
|
171
|
+
await (0, test_1.expect)(processTable).toBeVisible({ timeout: constants_1._1_SECOND_IN_MS * 4 });
|
|
172
|
+
const timeout = env_1.isOpenSearch ? constants_1._1_MINUTE_IN_MS : constants_1._1_SECOND_IN_MS * 20;
|
|
173
|
+
const row = this.page.locator(`tr:has-text("${processName}")`).first();
|
|
174
|
+
await (0, test_1.expect)(row).toBeVisible({ timeout });
|
|
175
|
+
const link = row.locator('a').first();
|
|
176
|
+
await (0, test_1.expect)(link).toBeVisible({ timeout: constants_1._1_SECOND_IN_MS * 2 });
|
|
177
|
+
await link.scrollIntoViewIfNeeded();
|
|
178
|
+
await link.click({ timeout: constants_1._1_SECOND_IN_MS * 2 });
|
|
179
|
+
await (0, test_1.expect)(this.page.getByText('Instance History').first()).toBeVisible({ timeout: constants_1._1_SECOND_IN_MS * 10 });
|
|
180
|
+
await this.dismissWhatsNewPopUp();
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async clickProcessActiveCheckbox() {
|
|
188
|
+
await this.processActiveCheckbox.waitFor({
|
|
189
|
+
state: 'attached',
|
|
190
|
+
timeout: constants_1._1_SECOND_IN_MS * 10,
|
|
191
|
+
});
|
|
192
|
+
if (!(await this.processActiveCheckbox.isChecked())) {
|
|
193
|
+
await this.processActiveCheckbox.check({ force: true, timeout: 120000 });
|
|
194
|
+
await (0, test_1.expect)(this.processActiveCheckbox).toBeChecked({
|
|
195
|
+
checked: true,
|
|
196
|
+
timeout: constants_1._1_SECOND_IN_MS * 5,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
async clickProcessCompletedCheckbox() {
|
|
201
|
+
await this.processCompletedCheckbox.waitFor({
|
|
202
|
+
state: 'attached',
|
|
203
|
+
timeout: constants_1._1_SECOND_IN_MS * 10,
|
|
204
|
+
});
|
|
205
|
+
if (!(await this.processCompletedCheckbox.isChecked())) {
|
|
206
|
+
await this.processCompletedCheckbox.check({ force: true, timeout: 120000 });
|
|
207
|
+
await (0, test_1.expect)(this.processCompletedCheckbox).toBeChecked({
|
|
208
|
+
checked: true,
|
|
209
|
+
timeout: constants_1._1_SECOND_IN_MS * 5,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async clickProcessIncidentsCheckbox() {
|
|
214
|
+
await this.processIncidentsCheckbox.waitFor({
|
|
215
|
+
state: 'attached',
|
|
216
|
+
timeout: constants_1._1_SECOND_IN_MS * 10,
|
|
217
|
+
});
|
|
218
|
+
const wasChecked = await this.processIncidentsCheckbox.isChecked();
|
|
219
|
+
await this.setCheckboxState(this.processIncidentsCheckbox, !wasChecked);
|
|
220
|
+
}
|
|
221
|
+
async clickRunningProcessInstancesCheckbox() {
|
|
222
|
+
await this.processRunningInstancesCheckbox.waitFor({
|
|
223
|
+
state: 'attached',
|
|
224
|
+
timeout: constants_1._1_SECOND_IN_MS * 10,
|
|
225
|
+
});
|
|
226
|
+
const wasChecked = await this.processRunningInstancesCheckbox.isChecked();
|
|
227
|
+
if (wasChecked) {
|
|
228
|
+
await this.processRunningInstancesCheckbox.uncheck({
|
|
229
|
+
force: true,
|
|
230
|
+
timeout: 90000,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
await this.processRunningInstancesCheckbox.check({
|
|
235
|
+
force: true,
|
|
236
|
+
timeout: 90000,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
await (0, test_1.expect)(this.processRunningInstancesCheckbox).toBeChecked({
|
|
240
|
+
checked: !wasChecked,
|
|
241
|
+
timeout: constants_1._1_SECOND_IN_MS * 5,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
async clickFinishedProcessInstancesCheckbox() {
|
|
245
|
+
await this.processFinishedInstancesCheckbox.waitFor({
|
|
246
|
+
state: 'visible',
|
|
247
|
+
timeout: constants_1._1_SECOND_IN_MS * 10,
|
|
248
|
+
});
|
|
249
|
+
const wasChecked = await this.processFinishedInstancesCheckbox.isChecked();
|
|
250
|
+
await this.processFinishedInstancesCheckbox.click({ timeout: 90000 });
|
|
251
|
+
await (0, test_1.expect)(this.processFinishedInstancesCheckbox).toBeChecked({
|
|
252
|
+
checked: !wasChecked,
|
|
253
|
+
timeout: constants_1._1_SECOND_IN_MS * 5,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
async clickProcessInstanceLink(processName, type = 'active') {
|
|
257
|
+
await this.dismissWhatsNewPopUp();
|
|
258
|
+
await this.applyProcessStateFilter(type);
|
|
259
|
+
if (await this.checkTableForProcess(processName)) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
const MAX_ATTEMPTS = 30;
|
|
263
|
+
const TOTAL_TIMEOUT_MS = constants_1._1_MINUTE_IN_MS * 10;
|
|
264
|
+
let attempt = 0;
|
|
265
|
+
let lastError;
|
|
266
|
+
const startTime = Date.now();
|
|
267
|
+
const operateUrl = this.page.url();
|
|
268
|
+
while (Date.now() - startTime < TOTAL_TIMEOUT_MS &&
|
|
269
|
+
attempt < MAX_ATTEMPTS) {
|
|
270
|
+
attempt++;
|
|
271
|
+
try {
|
|
272
|
+
if (await this.checkTableForProcess(processName)) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
throw new Error(`Instance "${processName}" not found in table (attempt ${attempt}).`);
|
|
276
|
+
}
|
|
277
|
+
catch (err) {
|
|
278
|
+
lastError = err instanceof Error ? err.message : err;
|
|
279
|
+
console.log(`clickProcessInstanceLink attempt ${attempt}: ${String(lastError)}`);
|
|
280
|
+
await (0, sleep_1.sleep)(constants_1._1_SECOND_IN_MS * 3);
|
|
281
|
+
await this.page
|
|
282
|
+
.reload({ timeout: 30000 })
|
|
283
|
+
.catch(() => console.log('Page reload timed out, continuing...'));
|
|
284
|
+
await this.page
|
|
285
|
+
.waitForLoadState('networkidle', { timeout: 30000 })
|
|
286
|
+
.catch(() => console.log('waitForLoadState networkidle timed out, continuing...'));
|
|
287
|
+
// After reload, the page may redirect away from Operate
|
|
288
|
+
if (!this.page.url().includes('/operate')) {
|
|
289
|
+
await this.page
|
|
290
|
+
.goto(operateUrl, { timeout: 30000 })
|
|
291
|
+
.catch(() => console.log('Navigation to Operate timed out, continuing...'));
|
|
292
|
+
await this.page
|
|
293
|
+
.waitForLoadState('networkidle', { timeout: 30000 })
|
|
294
|
+
.catch(() => console.log('waitForLoadState after Operate navigation timed out, continuing...'));
|
|
295
|
+
}
|
|
296
|
+
// The reload re-raises the what's-new modal, and its overlay would
|
|
297
|
+
// otherwise intercept both the Processes tab click and the checkbox
|
|
298
|
+
// labels below, so no attempt in this loop could ever converge.
|
|
299
|
+
await this.dismissWhatsNewPopUp();
|
|
300
|
+
// Operate may land on the Dashboard after reload even when the captured
|
|
301
|
+
// URL pointed at the Processes view. The filter checkboxes only exist on
|
|
302
|
+
// the Processes tab, so navigate there explicitly before applying filters.
|
|
303
|
+
try {
|
|
304
|
+
await this.processesTabLink.waitFor({
|
|
305
|
+
state: 'visible',
|
|
306
|
+
timeout: constants_1._1_SECOND_IN_MS * 10,
|
|
307
|
+
});
|
|
308
|
+
await this.processesTabLink.click({ timeout: constants_1._1_SECOND_IN_MS * 10 });
|
|
309
|
+
await this.page
|
|
310
|
+
.waitForLoadState('networkidle', { timeout: 30000 })
|
|
311
|
+
.catch(() => { });
|
|
312
|
+
}
|
|
313
|
+
catch (navErr) {
|
|
314
|
+
console.log(`Processes tab click failed after reload: ${String(navErr)}`);
|
|
315
|
+
}
|
|
316
|
+
// Re-apply checkbox filter lost after reload
|
|
317
|
+
try {
|
|
318
|
+
await this.applyProcessStateFilter(type);
|
|
319
|
+
}
|
|
320
|
+
catch (toggleErr) {
|
|
321
|
+
console.log(`Checkbox toggle failed after reload: ${String(toggleErr)}`);
|
|
322
|
+
}
|
|
323
|
+
// Re-fill Process Instance Key filter if it was previously applied
|
|
324
|
+
// but its value was cleared by the reload.
|
|
325
|
+
try {
|
|
326
|
+
const isFilterVisible = await this.processInstanceKeyTextBox.isVisible();
|
|
327
|
+
if (isFilterVisible) {
|
|
328
|
+
const currentValue = await this.processInstanceKeyTextBox.inputValue();
|
|
329
|
+
if (!currentValue) {
|
|
330
|
+
await this.processInstanceKeyTextBox.fill(processName);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
// Filter input not available, skip
|
|
336
|
+
}
|
|
337
|
+
await (0, sleep_1.sleep)(2000);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
throw new Error(`Failed to open instance "${processName}" after ${attempt} attempts (${Math.round((Date.now() - startTime) / 1000)}s elapsed). Last error: ${String(lastError)}`);
|
|
341
|
+
}
|
|
342
|
+
async clickProcessInstanceLinkWithPartialNameMatch(processName) {
|
|
343
|
+
let retryCount = 0;
|
|
344
|
+
const maxRetries = 5;
|
|
345
|
+
while (retryCount < maxRetries) {
|
|
346
|
+
try {
|
|
347
|
+
const process = this.page
|
|
348
|
+
.locator(`td:right-of(:has-text("${processName}"))`)
|
|
349
|
+
.first();
|
|
350
|
+
await (0, test_1.expect)(process).toBeVisible({ timeout: 60000 });
|
|
351
|
+
await process.click({ timeout: 60000 });
|
|
352
|
+
await (0, test_1.expect)(this.page.getByText('Instance History').first()).toBeVisible({
|
|
353
|
+
timeout: 30000,
|
|
354
|
+
});
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
retryCount++;
|
|
359
|
+
console.log(`Attempt ${retryCount} failed. Retrying...`);
|
|
360
|
+
await this.page.reload({ waitUntil: 'domcontentloaded' });
|
|
361
|
+
await this.page.waitForTimeout(10000);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
throw new Error(`Failed to click on process instance link containing "${processName}" after ${maxRetries} attempts.`);
|
|
365
|
+
}
|
|
366
|
+
getMoreFilterValueLocator(filter) {
|
|
367
|
+
switch (filter.trim()) {
|
|
368
|
+
case 'Process Instance Key(s)':
|
|
369
|
+
return this.processInstanceKeyTextBox;
|
|
370
|
+
default:
|
|
371
|
+
throw new Error(`Unsupported filter: "${filter}". Add a mapping in getMoreFilterValueLocator().`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
async applyMoreFilters(filter, value) {
|
|
375
|
+
await this.moreFiltersButton.click();
|
|
376
|
+
await this.page.getByRole('menuitem', { name: filter }).click();
|
|
377
|
+
const valueInput = this.getMoreFilterValueLocator(filter);
|
|
378
|
+
await (0, test_1.expect)(valueInput).toBeVisible({ timeout: 10000 });
|
|
379
|
+
await valueInput.fill(value);
|
|
380
|
+
await (0, sleep_1.sleep)(2000);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
exports.OperateProcessesPage = OperateProcessesPage;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
declare class OptimizeCollectionsPage {
|
|
3
|
+
private page;
|
|
4
|
+
readonly collectionsPageHeader: Locator;
|
|
5
|
+
readonly createNewButton: Locator;
|
|
6
|
+
readonly reportOption: Locator;
|
|
7
|
+
readonly modalCloseButton: Locator;
|
|
8
|
+
readonly processReport: Locator;
|
|
9
|
+
readonly collectionsLink: Locator;
|
|
10
|
+
readonly reportAuthor: Locator;
|
|
11
|
+
constructor(page: Page);
|
|
12
|
+
clickCreateNewButton(): Promise<void>;
|
|
13
|
+
clickReportOption(): Promise<void>;
|
|
14
|
+
clickMostRecentProcessReport(reportName: string): Promise<void>;
|
|
15
|
+
clickFirstProcessReport(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export { OptimizeCollectionsPage };
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OptimizeCollectionsPage = void 0;
|
|
4
|
+
class OptimizeCollectionsPage {
|
|
5
|
+
page;
|
|
6
|
+
collectionsPageHeader;
|
|
7
|
+
createNewButton;
|
|
8
|
+
reportOption;
|
|
9
|
+
modalCloseButton;
|
|
10
|
+
processReport;
|
|
11
|
+
collectionsLink;
|
|
12
|
+
reportAuthor;
|
|
13
|
+
constructor(page) {
|
|
14
|
+
this.page = page;
|
|
15
|
+
this.collectionsPageHeader = page.getByRole('heading', {
|
|
16
|
+
name: 'Collections, Dashboards, and Reports',
|
|
17
|
+
});
|
|
18
|
+
this.createNewButton = page.getByRole('button', {
|
|
19
|
+
name: 'Create new',
|
|
20
|
+
exact: true,
|
|
21
|
+
});
|
|
22
|
+
this.reportOption = page.getByText('Report', { exact: true });
|
|
23
|
+
this.modalCloseButton = page.getByText('Close', { exact: true });
|
|
24
|
+
this.processReport = page.getByText('Process Report').first();
|
|
25
|
+
this.collectionsLink = page.getByRole('link', { name: 'Collections' });
|
|
26
|
+
this.reportAuthor = page.getByRole('cell', { name: 'Demo User' }).first();
|
|
27
|
+
}
|
|
28
|
+
async clickCreateNewButton() {
|
|
29
|
+
if (await this.modalCloseButton.isVisible({ timeout: 2000 })) {
|
|
30
|
+
await this.modalCloseButton.click();
|
|
31
|
+
}
|
|
32
|
+
await this.collectionsLink.click({ timeout: 60000 });
|
|
33
|
+
await this.createNewButton.click({ timeout: 90000 });
|
|
34
|
+
}
|
|
35
|
+
async clickReportOption() {
|
|
36
|
+
await this.reportOption.click();
|
|
37
|
+
}
|
|
38
|
+
async clickMostRecentProcessReport(reportName) {
|
|
39
|
+
await this.page.getByText(reportName).click({ timeout: 60000 });
|
|
40
|
+
}
|
|
41
|
+
async clickFirstProcessReport() {
|
|
42
|
+
const cells = await this.page
|
|
43
|
+
.getByRole('cell', { name: /Process report/ })
|
|
44
|
+
.all();
|
|
45
|
+
if (cells.length === 0) {
|
|
46
|
+
throw new Error('No process report cells found');
|
|
47
|
+
}
|
|
48
|
+
const cell = cells[0];
|
|
49
|
+
const text = (await cell.textContent())?.trim();
|
|
50
|
+
if (!text) {
|
|
51
|
+
throw new Error('Could not read cell text');
|
|
52
|
+
}
|
|
53
|
+
const match = text.match(/^(\S+)\s*Process report$/i);
|
|
54
|
+
if (!match) {
|
|
55
|
+
throw new Error(`Unexpected text format: ${text}`);
|
|
56
|
+
}
|
|
57
|
+
const dynamicValue = match[1];
|
|
58
|
+
await cell.click();
|
|
59
|
+
await this.page.getByRole('link', { name: dynamicValue }).click();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.OptimizeCollectionsPage = OptimizeCollectionsPage;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
declare class OptimizeDashboardPage {
|
|
3
|
+
private page;
|
|
4
|
+
readonly filterTable: Locator;
|
|
5
|
+
constructor(page: Page);
|
|
6
|
+
clickFilterTable(): Promise<void>;
|
|
7
|
+
fillFilterTable(processName: string): Promise<void>;
|
|
8
|
+
processOwnerNameAssertion(processName: string): Promise<void>;
|
|
9
|
+
processAssertion(processName: string, { timeout, not }?: {
|
|
10
|
+
timeout?: number;
|
|
11
|
+
not?: boolean;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export { OptimizeDashboardPage };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OptimizeDashboardPage = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
class OptimizeDashboardPage {
|
|
6
|
+
page;
|
|
7
|
+
filterTable;
|
|
8
|
+
constructor(page) {
|
|
9
|
+
this.page = page;
|
|
10
|
+
// Optimize's Design System EntityList replaced the Carbon table toolbar
|
|
11
|
+
// search (placeholder "Filter table") with a plain input whose placeholder
|
|
12
|
+
// is the `home.search.name` translation, "Search for name...". Match
|
|
13
|
+
// either so the filter resolves on both shells.
|
|
14
|
+
this.filterTable = page
|
|
15
|
+
.getByPlaceholder(/Search for name/i)
|
|
16
|
+
.or(page.getByPlaceholder('Filter table'))
|
|
17
|
+
.first();
|
|
18
|
+
}
|
|
19
|
+
async clickFilterTable() {
|
|
20
|
+
await this.filterTable.click({ timeout: 180000 });
|
|
21
|
+
}
|
|
22
|
+
async fillFilterTable(processName) {
|
|
23
|
+
await this.filterTable.fill(processName, { timeout: 60000 });
|
|
24
|
+
await this.filterTable.press('Enter');
|
|
25
|
+
}
|
|
26
|
+
async processOwnerNameAssertion(processName) {
|
|
27
|
+
try {
|
|
28
|
+
await this.page.reload();
|
|
29
|
+
await (0, test_1.expect)(this.page.getByRole('row', {
|
|
30
|
+
name: processName + ' Process QA Camunda',
|
|
31
|
+
})).toBeVisible({ timeout: 180000 });
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
await this.page.reload();
|
|
35
|
+
await (0, test_1.expect)(this.page.getByRole('row', {
|
|
36
|
+
name: processName + ' Process QA Camunda',
|
|
37
|
+
})).toBeVisible({ timeout: 180000 });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async processAssertion(processName, { timeout = 180000, not = false } = {}) {
|
|
41
|
+
const interval = 45000;
|
|
42
|
+
const start = Date.now();
|
|
43
|
+
while (Date.now() - start < timeout) {
|
|
44
|
+
const locator = this.page.getByTitle(processName).first();
|
|
45
|
+
try {
|
|
46
|
+
if (not) {
|
|
47
|
+
await (0, test_1.expect)(locator).not.toBeVisible({ timeout: interval });
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
await (0, test_1.expect)(locator).toBeVisible({ timeout: interval });
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
if (Date.now() - start + interval >= timeout) {
|
|
56
|
+
throw e;
|
|
57
|
+
}
|
|
58
|
+
await this.page.reload();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.OptimizeDashboardPage = OptimizeDashboardPage;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
declare class OptimizeHomePage {
|
|
3
|
+
private page;
|
|
4
|
+
readonly collectionsLink: Locator;
|
|
5
|
+
readonly dashboardLink: Locator;
|
|
6
|
+
readonly modalCloseButton: Locator;
|
|
7
|
+
readonly optimizeBanner: Locator;
|
|
8
|
+
readonly openUserSettingsButton: Locator;
|
|
9
|
+
readonly logoutButton: Locator;
|
|
10
|
+
readonly licenseKeyTagNonProduction: Locator;
|
|
11
|
+
readonly licenseKeyTagProduction: Locator;
|
|
12
|
+
constructor(page: Page);
|
|
13
|
+
clickCollectionsLink(): Promise<void>;
|
|
14
|
+
clickDashboardLink(): Promise<void>;
|
|
15
|
+
assertProcessHasBeenImported(processId: string): Promise<void>;
|
|
16
|
+
clickOpenUserSettingsButton(): Promise<void>;
|
|
17
|
+
clickLogoutButton(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
export { OptimizeHomePage };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OptimizeHomePage = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
class OptimizeHomePage {
|
|
6
|
+
page;
|
|
7
|
+
collectionsLink;
|
|
8
|
+
dashboardLink;
|
|
9
|
+
modalCloseButton;
|
|
10
|
+
optimizeBanner;
|
|
11
|
+
openUserSettingsButton;
|
|
12
|
+
logoutButton;
|
|
13
|
+
licenseKeyTagNonProduction;
|
|
14
|
+
licenseKeyTagProduction;
|
|
15
|
+
constructor(page) {
|
|
16
|
+
this.page = page;
|
|
17
|
+
// Optimize's Design System navigation moved Dashboards/Collections out of
|
|
18
|
+
// the top banner into a left sidebar (`<nav aria-label="Main navigation">`),
|
|
19
|
+
// so a banner-scoped link can no longer resolve. Match either shell.
|
|
20
|
+
this.collectionsLink = page
|
|
21
|
+
.getByRole('navigation', { name: 'Main navigation' })
|
|
22
|
+
.getByRole('link', { name: 'Collections' })
|
|
23
|
+
.or(page.getByRole('banner').getByRole('link', { name: 'Collections' }))
|
|
24
|
+
.first();
|
|
25
|
+
this.dashboardLink = page.getByRole('link', { name: 'Dashboard' });
|
|
26
|
+
this.modalCloseButton = page.getByText('Close', { exact: true });
|
|
27
|
+
// See NavigationPage.optimizePageBanner: the Design System header renders
|
|
28
|
+
// the logo as its own anchor labelled "Camunda Optimize", so the legacy
|
|
29
|
+
// combined "Camunda logo Optimize" name is gone. Match either shell.
|
|
30
|
+
this.optimizeBanner = page
|
|
31
|
+
.getByRole('link', { name: 'Camunda logo Optimize' })
|
|
32
|
+
.or(page.getByRole('link', { name: 'Camunda Optimize', exact: true }))
|
|
33
|
+
.first();
|
|
34
|
+
this.openUserSettingsButton = page.getByLabel('Open User');
|
|
35
|
+
this.logoutButton = page.getByRole('button', { name: 'Logout' });
|
|
36
|
+
this.licenseKeyTagNonProduction = page
|
|
37
|
+
.getByText('Non-production license')
|
|
38
|
+
.first();
|
|
39
|
+
this.licenseKeyTagProduction = page.getByText('Production license').first();
|
|
40
|
+
}
|
|
41
|
+
async clickCollectionsLink() {
|
|
42
|
+
if (await this.modalCloseButton.isVisible({ timeout: 2000 })) {
|
|
43
|
+
await this.modalCloseButton.click();
|
|
44
|
+
}
|
|
45
|
+
await this.collectionsLink.click();
|
|
46
|
+
}
|
|
47
|
+
async clickDashboardLink() {
|
|
48
|
+
if (await this.modalCloseButton.isVisible({ timeout: 2000 })) {
|
|
49
|
+
await this.modalCloseButton.click();
|
|
50
|
+
}
|
|
51
|
+
await this.dashboardLink.click();
|
|
52
|
+
}
|
|
53
|
+
async assertProcessHasBeenImported(processId) {
|
|
54
|
+
await this.clickDashboardLink();
|
|
55
|
+
// The process list is paginated, so on a cluster with more than one page
|
|
56
|
+
// of processes the new one is not rendered until the list is filtered by
|
|
57
|
+
// name. Import is asynchronous, so reload and re-filter until it appears.
|
|
58
|
+
const search = this.page
|
|
59
|
+
.getByPlaceholder(/Search for name/i)
|
|
60
|
+
.or(this.page.getByPlaceholder('Filter table'))
|
|
61
|
+
.first();
|
|
62
|
+
const processLink = this.page.getByRole('link', { name: processId });
|
|
63
|
+
await (0, test_1.expect)(async () => {
|
|
64
|
+
await this.page.reload();
|
|
65
|
+
await search.fill(processId, { timeout: 30000 });
|
|
66
|
+
await (0, test_1.expect)(processLink).toBeVisible({ timeout: 15000 });
|
|
67
|
+
}).toPass({ timeout: 180000, intervals: [5000, 10000, 15000] });
|
|
68
|
+
}
|
|
69
|
+
async clickOpenUserSettingsButton() {
|
|
70
|
+
await this.openUserSettingsButton.click();
|
|
71
|
+
}
|
|
72
|
+
async clickLogoutButton() {
|
|
73
|
+
await this.logoutButton.click();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.OptimizeHomePage = OptimizeHomePage;
|