@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.
Files changed (122) hide show
  1. package/.github/c8_versions.json +1 -0
  2. package/README.md +2 -0
  3. package/dist/fixtures/SM-8.11.d.ts +77 -0
  4. package/dist/fixtures/SM-8.11.js +176 -0
  5. package/dist/pages/SM-8.11/Authorization.d.ts +39 -0
  6. package/dist/pages/SM-8.11/Authorization.js +349 -0
  7. package/dist/pages/SM-8.11/ClusterDetailsPage.d.ts +8 -0
  8. package/dist/pages/SM-8.11/ClusterDetailsPage.js +65 -0
  9. package/dist/pages/SM-8.11/ClusterPage.d.ts +11 -0
  10. package/dist/pages/SM-8.11/ClusterPage.js +37 -0
  11. package/dist/pages/SM-8.11/ConnectorMarketplacePage.d.ts +27 -0
  12. package/dist/pages/SM-8.11/ConnectorMarketplacePage.js +187 -0
  13. package/dist/pages/SM-8.11/ConnectorSettingsPage.d.ts +41 -0
  14. package/dist/pages/SM-8.11/ConnectorSettingsPage.js +153 -0
  15. package/dist/pages/SM-8.11/ConnectorTemplatePage.d.ts +11 -0
  16. package/dist/pages/SM-8.11/ConnectorTemplatePage.js +34 -0
  17. package/dist/pages/SM-8.11/FormJsPage.d.ts +33 -0
  18. package/dist/pages/SM-8.11/FormJsPage.js +109 -0
  19. package/dist/pages/SM-8.11/HomePage.d.ts +20 -0
  20. package/dist/pages/SM-8.11/HomePage.js +49 -0
  21. package/dist/pages/SM-8.11/IdentityTenantPage.d.ts +53 -0
  22. package/dist/pages/SM-8.11/IdentityTenantPage.js +264 -0
  23. package/dist/pages/SM-8.11/IdentityUsersPage.d.ts +92 -0
  24. package/dist/pages/SM-8.11/IdentityUsersPage.js +485 -0
  25. package/dist/pages/SM-8.11/KeycloakAdminPage.d.ts +17 -0
  26. package/dist/pages/SM-8.11/KeycloakAdminPage.js +78 -0
  27. package/dist/pages/SM-8.11/KeycloakLoginPage.d.ts +12 -0
  28. package/dist/pages/SM-8.11/KeycloakLoginPage.js +32 -0
  29. package/dist/pages/SM-8.11/KeycloakUtils.d.ts +17 -0
  30. package/dist/pages/SM-8.11/KeycloakUtils.js +94 -0
  31. package/dist/pages/SM-8.11/LoginPage.d.ts +21 -0
  32. package/dist/pages/SM-8.11/LoginPage.js +112 -0
  33. package/dist/pages/SM-8.11/ManagementIdentityPage.d.ts +44 -0
  34. package/dist/pages/SM-8.11/ManagementIdentityPage.js +176 -0
  35. package/dist/pages/SM-8.11/ModelerCreatePage.d.ts +238 -0
  36. package/dist/pages/SM-8.11/ModelerCreatePage.js +1950 -0
  37. package/dist/pages/SM-8.11/ModelerHomePage.d.ts +97 -0
  38. package/dist/pages/SM-8.11/ModelerHomePage.js +956 -0
  39. package/dist/pages/SM-8.11/NavigationPage.d.ts +58 -0
  40. package/dist/pages/SM-8.11/NavigationPage.js +389 -0
  41. package/dist/pages/SM-8.11/OCIdentityAuthorizationsPage.d.ts +10 -0
  42. package/dist/pages/SM-8.11/OCIdentityAuthorizationsPage.js +22 -0
  43. package/dist/pages/SM-8.11/OCIdentityClusterVariablesPage.d.ts +22 -0
  44. package/dist/pages/SM-8.11/OCIdentityClusterVariablesPage.js +120 -0
  45. package/dist/pages/SM-8.11/OCIdentityGroupsPage.d.ts +39 -0
  46. package/dist/pages/SM-8.11/OCIdentityGroupsPage.js +154 -0
  47. package/dist/pages/SM-8.11/OCIdentityHomePage.d.ts +23 -0
  48. package/dist/pages/SM-8.11/OCIdentityHomePage.js +115 -0
  49. package/dist/pages/SM-8.11/OCIdentityMappingRulesPage.d.ts +23 -0
  50. package/dist/pages/SM-8.11/OCIdentityMappingRulesPage.js +116 -0
  51. package/dist/pages/SM-8.11/OCIdentityRolesPage.d.ts +61 -0
  52. package/dist/pages/SM-8.11/OCIdentityRolesPage.js +433 -0
  53. package/dist/pages/SM-8.11/OCTenantPage.d.ts +73 -0
  54. package/dist/pages/SM-8.11/OCTenantPage.js +399 -0
  55. package/dist/pages/SM-8.11/OperateHomePage.d.ts +26 -0
  56. package/dist/pages/SM-8.11/OperateHomePage.js +139 -0
  57. package/dist/pages/SM-8.11/OperateProcessInstancePage.d.ts +30 -0
  58. package/dist/pages/SM-8.11/OperateProcessInstancePage.js +276 -0
  59. package/dist/pages/SM-8.11/OperateProcessesPage.d.ts +38 -0
  60. package/dist/pages/SM-8.11/OperateProcessesPage.js +383 -0
  61. package/dist/pages/SM-8.11/OptimizeCollectionsPage.d.ts +17 -0
  62. package/dist/pages/SM-8.11/OptimizeCollectionsPage.js +62 -0
  63. package/dist/pages/SM-8.11/OptimizeDashboardPage.d.ts +14 -0
  64. package/dist/pages/SM-8.11/OptimizeDashboardPage.js +63 -0
  65. package/dist/pages/SM-8.11/OptimizeHomePage.d.ts +19 -0
  66. package/dist/pages/SM-8.11/OptimizeHomePage.js +76 -0
  67. package/dist/pages/SM-8.11/OptimizeReportPage.d.ts +50 -0
  68. package/dist/pages/SM-8.11/OptimizeReportPage.js +209 -0
  69. package/dist/pages/SM-8.11/PlayPage.d.ts +47 -0
  70. package/dist/pages/SM-8.11/PlayPage.js +389 -0
  71. package/dist/pages/SM-8.11/ProcessApplicationPage.d.ts +35 -0
  72. package/dist/pages/SM-8.11/ProcessApplicationPage.js +340 -0
  73. package/dist/pages/SM-8.11/SettingsPage.d.ts +12 -0
  74. package/dist/pages/SM-8.11/SettingsPage.js +34 -0
  75. package/dist/pages/SM-8.11/TaskDetailsPage.d.ts +70 -0
  76. package/dist/pages/SM-8.11/TaskDetailsPage.js +273 -0
  77. package/dist/pages/SM-8.11/TaskPanelPage.d.ts +29 -0
  78. package/dist/pages/SM-8.11/TaskPanelPage.js +320 -0
  79. package/dist/pages/SM-8.11/TaskProcessesPage.d.ts +15 -0
  80. package/dist/pages/SM-8.11/TaskProcessesPage.js +77 -0
  81. package/dist/pages/SM-8.11/UtilitiesPage.d.ts +38 -0
  82. package/dist/pages/SM-8.11/UtilitiesPage.js +573 -0
  83. package/dist/pages/SM-8.11/optimizeReportUtils.d.ts +6 -0
  84. package/dist/pages/SM-8.11/optimizeReportUtils.js +77 -0
  85. package/dist/tests/SM-8.11/cluster-management-user-flows.spec.d.ts +1 -0
  86. package/dist/tests/SM-8.11/cluster-management-user-flows.spec.js +48 -0
  87. package/dist/tests/SM-8.11/cluster-variables.spec.d.ts +1 -0
  88. package/dist/tests/SM-8.11/cluster-variables.spec.js +226 -0
  89. package/dist/tests/SM-8.11/connectors-user-flows.spec.d.ts +1 -0
  90. package/dist/tests/SM-8.11/connectors-user-flows.spec.js +307 -0
  91. package/dist/tests/SM-8.11/document-handling-user-flows.spec.d.ts +1 -0
  92. package/dist/tests/SM-8.11/document-handling-user-flows.spec.js +174 -0
  93. package/dist/tests/SM-8.11/hto-user-flows.spec.d.ts +1 -0
  94. package/dist/tests/SM-8.11/hto-user-flows.spec.js +410 -0
  95. package/dist/tests/SM-8.11/hub-web-modeler-smoke.spec.d.ts +1 -0
  96. package/dist/tests/SM-8.11/hub-web-modeler-smoke.spec.js +24 -0
  97. package/dist/tests/SM-8.11/identity-user-flows.spec.d.ts +1 -0
  98. package/dist/tests/SM-8.11/identity-user-flows.spec.js +220 -0
  99. package/dist/tests/SM-8.11/license-key-user-flows.spec.d.ts +1 -0
  100. package/dist/tests/SM-8.11/license-key-user-flows.spec.js +461 -0
  101. package/dist/tests/SM-8.11/login.spec.d.ts +1 -0
  102. package/dist/tests/SM-8.11/login.spec.js +24 -0
  103. package/dist/tests/SM-8.11/migration-path-user-flows.spec.d.ts +1 -0
  104. package/dist/tests/SM-8.11/migration-path-user-flows.spec.js +263 -0
  105. package/dist/tests/SM-8.11/mt-enabled-user-flows.spec.d.ts +1 -0
  106. package/dist/tests/SM-8.11/mt-enabled-user-flows.spec.js +607 -0
  107. package/dist/tests/SM-8.11/optimize-user-flows.spec.d.ts +1 -0
  108. package/dist/tests/SM-8.11/optimize-user-flows.spec.js +505 -0
  109. package/dist/tests/SM-8.11/play.spec.d.ts +1 -0
  110. package/dist/tests/SM-8.11/play.spec.js +144 -0
  111. package/dist/tests/SM-8.11/rba-enabled-user-flows.spec.d.ts +1 -0
  112. package/dist/tests/SM-8.11/rba-enabled-user-flows.spec.js +712 -0
  113. package/dist/tests/SM-8.11/smoke-tests.spec.d.ts +1 -0
  114. package/dist/tests/SM-8.11/smoke-tests.spec.js +167 -0
  115. package/dist/tests/SM-8.11/test-setup.spec.d.ts +1 -0
  116. package/dist/tests/SM-8.11/test-setup.spec.js +24 -0
  117. package/dist/tests/SM-8.11/topology-orchestration-smoke.spec.d.ts +1 -0
  118. package/dist/tests/SM-8.11/topology-orchestration-smoke.spec.js +58 -0
  119. package/dist/tests/SM-8.11/web-modeler-user-flows.spec.d.ts +1 -0
  120. package/dist/tests/SM-8.11/web-modeler-user-flows.spec.js +223 -0
  121. package/package.json +1 -1
  122. 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;