@camunda/e2e-test-suite 0.0.1442 → 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.
Files changed (123) 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/8.7/ModelerCreatePage.js +21 -0
  6. package/dist/pages/SM-8.11/Authorization.d.ts +39 -0
  7. package/dist/pages/SM-8.11/Authorization.js +349 -0
  8. package/dist/pages/SM-8.11/ClusterDetailsPage.d.ts +8 -0
  9. package/dist/pages/SM-8.11/ClusterDetailsPage.js +65 -0
  10. package/dist/pages/SM-8.11/ClusterPage.d.ts +11 -0
  11. package/dist/pages/SM-8.11/ClusterPage.js +37 -0
  12. package/dist/pages/SM-8.11/ConnectorMarketplacePage.d.ts +27 -0
  13. package/dist/pages/SM-8.11/ConnectorMarketplacePage.js +187 -0
  14. package/dist/pages/SM-8.11/ConnectorSettingsPage.d.ts +41 -0
  15. package/dist/pages/SM-8.11/ConnectorSettingsPage.js +153 -0
  16. package/dist/pages/SM-8.11/ConnectorTemplatePage.d.ts +11 -0
  17. package/dist/pages/SM-8.11/ConnectorTemplatePage.js +34 -0
  18. package/dist/pages/SM-8.11/FormJsPage.d.ts +33 -0
  19. package/dist/pages/SM-8.11/FormJsPage.js +109 -0
  20. package/dist/pages/SM-8.11/HomePage.d.ts +20 -0
  21. package/dist/pages/SM-8.11/HomePage.js +49 -0
  22. package/dist/pages/SM-8.11/IdentityTenantPage.d.ts +53 -0
  23. package/dist/pages/SM-8.11/IdentityTenantPage.js +264 -0
  24. package/dist/pages/SM-8.11/IdentityUsersPage.d.ts +92 -0
  25. package/dist/pages/SM-8.11/IdentityUsersPage.js +485 -0
  26. package/dist/pages/SM-8.11/KeycloakAdminPage.d.ts +17 -0
  27. package/dist/pages/SM-8.11/KeycloakAdminPage.js +78 -0
  28. package/dist/pages/SM-8.11/KeycloakLoginPage.d.ts +12 -0
  29. package/dist/pages/SM-8.11/KeycloakLoginPage.js +32 -0
  30. package/dist/pages/SM-8.11/KeycloakUtils.d.ts +17 -0
  31. package/dist/pages/SM-8.11/KeycloakUtils.js +94 -0
  32. package/dist/pages/SM-8.11/LoginPage.d.ts +21 -0
  33. package/dist/pages/SM-8.11/LoginPage.js +112 -0
  34. package/dist/pages/SM-8.11/ManagementIdentityPage.d.ts +44 -0
  35. package/dist/pages/SM-8.11/ManagementIdentityPage.js +176 -0
  36. package/dist/pages/SM-8.11/ModelerCreatePage.d.ts +238 -0
  37. package/dist/pages/SM-8.11/ModelerCreatePage.js +1950 -0
  38. package/dist/pages/SM-8.11/ModelerHomePage.d.ts +97 -0
  39. package/dist/pages/SM-8.11/ModelerHomePage.js +956 -0
  40. package/dist/pages/SM-8.11/NavigationPage.d.ts +58 -0
  41. package/dist/pages/SM-8.11/NavigationPage.js +389 -0
  42. package/dist/pages/SM-8.11/OCIdentityAuthorizationsPage.d.ts +10 -0
  43. package/dist/pages/SM-8.11/OCIdentityAuthorizationsPage.js +22 -0
  44. package/dist/pages/SM-8.11/OCIdentityClusterVariablesPage.d.ts +22 -0
  45. package/dist/pages/SM-8.11/OCIdentityClusterVariablesPage.js +120 -0
  46. package/dist/pages/SM-8.11/OCIdentityGroupsPage.d.ts +39 -0
  47. package/dist/pages/SM-8.11/OCIdentityGroupsPage.js +154 -0
  48. package/dist/pages/SM-8.11/OCIdentityHomePage.d.ts +23 -0
  49. package/dist/pages/SM-8.11/OCIdentityHomePage.js +115 -0
  50. package/dist/pages/SM-8.11/OCIdentityMappingRulesPage.d.ts +23 -0
  51. package/dist/pages/SM-8.11/OCIdentityMappingRulesPage.js +116 -0
  52. package/dist/pages/SM-8.11/OCIdentityRolesPage.d.ts +61 -0
  53. package/dist/pages/SM-8.11/OCIdentityRolesPage.js +433 -0
  54. package/dist/pages/SM-8.11/OCTenantPage.d.ts +73 -0
  55. package/dist/pages/SM-8.11/OCTenantPage.js +399 -0
  56. package/dist/pages/SM-8.11/OperateHomePage.d.ts +26 -0
  57. package/dist/pages/SM-8.11/OperateHomePage.js +139 -0
  58. package/dist/pages/SM-8.11/OperateProcessInstancePage.d.ts +30 -0
  59. package/dist/pages/SM-8.11/OperateProcessInstancePage.js +276 -0
  60. package/dist/pages/SM-8.11/OperateProcessesPage.d.ts +38 -0
  61. package/dist/pages/SM-8.11/OperateProcessesPage.js +383 -0
  62. package/dist/pages/SM-8.11/OptimizeCollectionsPage.d.ts +17 -0
  63. package/dist/pages/SM-8.11/OptimizeCollectionsPage.js +62 -0
  64. package/dist/pages/SM-8.11/OptimizeDashboardPage.d.ts +14 -0
  65. package/dist/pages/SM-8.11/OptimizeDashboardPage.js +63 -0
  66. package/dist/pages/SM-8.11/OptimizeHomePage.d.ts +19 -0
  67. package/dist/pages/SM-8.11/OptimizeHomePage.js +76 -0
  68. package/dist/pages/SM-8.11/OptimizeReportPage.d.ts +50 -0
  69. package/dist/pages/SM-8.11/OptimizeReportPage.js +209 -0
  70. package/dist/pages/SM-8.11/PlayPage.d.ts +47 -0
  71. package/dist/pages/SM-8.11/PlayPage.js +389 -0
  72. package/dist/pages/SM-8.11/ProcessApplicationPage.d.ts +35 -0
  73. package/dist/pages/SM-8.11/ProcessApplicationPage.js +340 -0
  74. package/dist/pages/SM-8.11/SettingsPage.d.ts +12 -0
  75. package/dist/pages/SM-8.11/SettingsPage.js +34 -0
  76. package/dist/pages/SM-8.11/TaskDetailsPage.d.ts +70 -0
  77. package/dist/pages/SM-8.11/TaskDetailsPage.js +273 -0
  78. package/dist/pages/SM-8.11/TaskPanelPage.d.ts +29 -0
  79. package/dist/pages/SM-8.11/TaskPanelPage.js +320 -0
  80. package/dist/pages/SM-8.11/TaskProcessesPage.d.ts +15 -0
  81. package/dist/pages/SM-8.11/TaskProcessesPage.js +77 -0
  82. package/dist/pages/SM-8.11/UtilitiesPage.d.ts +38 -0
  83. package/dist/pages/SM-8.11/UtilitiesPage.js +573 -0
  84. package/dist/pages/SM-8.11/optimizeReportUtils.d.ts +6 -0
  85. package/dist/pages/SM-8.11/optimizeReportUtils.js +77 -0
  86. package/dist/tests/SM-8.11/cluster-management-user-flows.spec.d.ts +1 -0
  87. package/dist/tests/SM-8.11/cluster-management-user-flows.spec.js +48 -0
  88. package/dist/tests/SM-8.11/cluster-variables.spec.d.ts +1 -0
  89. package/dist/tests/SM-8.11/cluster-variables.spec.js +226 -0
  90. package/dist/tests/SM-8.11/connectors-user-flows.spec.d.ts +1 -0
  91. package/dist/tests/SM-8.11/connectors-user-flows.spec.js +307 -0
  92. package/dist/tests/SM-8.11/document-handling-user-flows.spec.d.ts +1 -0
  93. package/dist/tests/SM-8.11/document-handling-user-flows.spec.js +174 -0
  94. package/dist/tests/SM-8.11/hto-user-flows.spec.d.ts +1 -0
  95. package/dist/tests/SM-8.11/hto-user-flows.spec.js +410 -0
  96. package/dist/tests/SM-8.11/hub-web-modeler-smoke.spec.d.ts +1 -0
  97. package/dist/tests/SM-8.11/hub-web-modeler-smoke.spec.js +24 -0
  98. package/dist/tests/SM-8.11/identity-user-flows.spec.d.ts +1 -0
  99. package/dist/tests/SM-8.11/identity-user-flows.spec.js +220 -0
  100. package/dist/tests/SM-8.11/license-key-user-flows.spec.d.ts +1 -0
  101. package/dist/tests/SM-8.11/license-key-user-flows.spec.js +461 -0
  102. package/dist/tests/SM-8.11/login.spec.d.ts +1 -0
  103. package/dist/tests/SM-8.11/login.spec.js +24 -0
  104. package/dist/tests/SM-8.11/migration-path-user-flows.spec.d.ts +1 -0
  105. package/dist/tests/SM-8.11/migration-path-user-flows.spec.js +263 -0
  106. package/dist/tests/SM-8.11/mt-enabled-user-flows.spec.d.ts +1 -0
  107. package/dist/tests/SM-8.11/mt-enabled-user-flows.spec.js +607 -0
  108. package/dist/tests/SM-8.11/optimize-user-flows.spec.d.ts +1 -0
  109. package/dist/tests/SM-8.11/optimize-user-flows.spec.js +505 -0
  110. package/dist/tests/SM-8.11/play.spec.d.ts +1 -0
  111. package/dist/tests/SM-8.11/play.spec.js +144 -0
  112. package/dist/tests/SM-8.11/rba-enabled-user-flows.spec.d.ts +1 -0
  113. package/dist/tests/SM-8.11/rba-enabled-user-flows.spec.js +712 -0
  114. package/dist/tests/SM-8.11/smoke-tests.spec.d.ts +1 -0
  115. package/dist/tests/SM-8.11/smoke-tests.spec.js +167 -0
  116. package/dist/tests/SM-8.11/test-setup.spec.d.ts +1 -0
  117. package/dist/tests/SM-8.11/test-setup.spec.js +24 -0
  118. package/dist/tests/SM-8.11/topology-orchestration-smoke.spec.d.ts +1 -0
  119. package/dist/tests/SM-8.11/topology-orchestration-smoke.spec.js +58 -0
  120. package/dist/tests/SM-8.11/web-modeler-user-flows.spec.d.ts +1 -0
  121. package/dist/tests/SM-8.11/web-modeler-user-flows.spec.js +223 -0
  122. package/package.json +1 -1
  123. package/playwright.config.ts +21 -0
@@ -0,0 +1,349 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Authorization = void 0;
4
+ const test_1 = require("@playwright/test");
5
+ const findLocatorWithPagination_1 = require("../../utils/assertionHelpers/findLocatorWithPagination");
6
+ const submitModalWithRetry_1 = require("../../utils/assertionHelpers/submitModalWithRetry");
7
+ const sleep_1 = require("../../utils/sleep");
8
+ class Authorization {
9
+ page;
10
+ selectResourceTypeTab;
11
+ createAuthorizationModal;
12
+ createAuthorizationButton;
13
+ createAuthorizationOwnerTypeCombobox;
14
+ createAuthorizationOwnerTypeOption;
15
+ createAuthorizationOwnerComboBox;
16
+ createAuthorizationOwnerOption;
17
+ createAuthorizationOwnerSearchInput;
18
+ createAuthorizationOwnerSearchOption;
19
+ createAuthorizationOwnerCommandInput;
20
+ createAuthorizationOwnerCommandOptions;
21
+ createAuthorizationResourceTypeDropdown;
22
+ createAuthorizationResourceTypeOption;
23
+ createAuthorizationResourceIdField;
24
+ createAuthorizationAccessPermission;
25
+ createAuthorizationSubmitButton;
26
+ authorizationsList;
27
+ deleteAuthorizationButton;
28
+ deleteAuthorizationModal;
29
+ deleteAuthorizationModalDeleteButton;
30
+ tableFilter;
31
+ constructor(page) {
32
+ this.page = page;
33
+ this.selectResourceTypeTab = (resourceType) => page.getByRole('tab', { name: resourceType });
34
+ this.authorizationsList = page.getByRole('table');
35
+ this.createAuthorizationButton = page.getByRole('button', {
36
+ name: /create authorization/i,
37
+ });
38
+ this.createAuthorizationModal = page.getByRole('dialog', {
39
+ name: 'Create authorization',
40
+ });
41
+ this.createAuthorizationOwnerTypeCombobox =
42
+ this.createAuthorizationModal.getByRole('combobox', {
43
+ name: 'Owner type',
44
+ });
45
+ // The design-system Select portals its listbox to the end of <body>, so
46
+ // the options are siblings of the dialog rather than descendants, and
47
+ // while the listbox is open Radix marks everything outside it aria-hidden
48
+ // - which takes the dialog itself out of the accessibility tree. Query the
49
+ // options from the page root so both placements resolve.
50
+ this.createAuthorizationOwnerTypeOption = (name) => this.page
51
+ .getByRole('option', {
52
+ name,
53
+ exact: true,
54
+ })
55
+ .first();
56
+ this.createAuthorizationOwnerComboBox =
57
+ this.createAuthorizationModal.getByRole('combobox', {
58
+ name: 'Owner',
59
+ exact: true,
60
+ });
61
+ this.createAuthorizationOwnerOption = (name) => this.createAuthorizationModal.getByRole('option', {
62
+ name,
63
+ exact: true,
64
+ });
65
+ // Every owner type now renders a server-backed search dropdown instead of
66
+ // the combobox that listed all owners up front. The dropdown is a Carbon
67
+ // Search inside a ListBox, so it has no combobox role; its results are
68
+ // list-box menu items that carry the owner ID as their title. The
69
+ // design-system build renders the same field as a command palette whose
70
+ // results are role=option items in a popover portalled out of the dialog.
71
+ this.createAuthorizationOwnerSearchInput =
72
+ this.createAuthorizationModal.getByPlaceholder('Search by owner ID');
73
+ this.createAuthorizationOwnerSearchOption = (name) => this.page
74
+ .locator('.cds--list-box__menu-item, [role="option"]')
75
+ .filter({ hasText: name })
76
+ .first();
77
+ // In the design-system build the closed Owner control is a combobox-role
78
+ // trigger that paints "Search by owner ID" as static text (no placeholder
79
+ // attribute), and the cmdk search box plus its result listbox only mount
80
+ // in a popover once the trigger is opened. That popover is portalled out
81
+ // of the dialog, and while it is open Radix marks the dialog aria-hidden,
82
+ // so both must be queried from the page root.
83
+ this.createAuthorizationOwnerCommandInput = this.page
84
+ .locator('[data-slot="command-input"]')
85
+ .first();
86
+ this.createAuthorizationOwnerCommandOptions = this.page
87
+ .getByRole('listbox')
88
+ .getByRole('option');
89
+ this.createAuthorizationResourceTypeDropdown =
90
+ this.createAuthorizationModal.getByRole('combobox', {
91
+ name: 'Resource type',
92
+ });
93
+ this.createAuthorizationResourceTypeOption = (name) => this.page
94
+ .getByRole('option', {
95
+ name,
96
+ exact: true,
97
+ })
98
+ .first();
99
+ this.createAuthorizationResourceIdField =
100
+ this.createAuthorizationModal.getByRole('textbox', {
101
+ name: 'Resource ID',
102
+ });
103
+ this.createAuthorizationAccessPermission = (name) => this.createAuthorizationModal.locator(`label[for="${name}"]`);
104
+ this.createAuthorizationSubmitButton =
105
+ this.createAuthorizationModal.getByRole('button', {
106
+ name: 'Create authorization',
107
+ });
108
+ this.authorizationsList = page.getByRole('table');
109
+ this.deleteAuthorizationButton = (name) => this.authorizationsList
110
+ .getByRole('row', { name })
111
+ .getByRole('button', { name: 'Delete' });
112
+ this.deleteAuthorizationModal = page.getByRole('dialog', {
113
+ name: /delete authorization/i,
114
+ });
115
+ this.deleteAuthorizationModalDeleteButton =
116
+ this.deleteAuthorizationModal.getByRole('button', {
117
+ name: /delete authorization/i,
118
+ });
119
+ // Carbon labels the table toolbar search "Filter table"; the
120
+ // design-system shell the Admin is migrating onto exposes the placeholder
121
+ // as the accessible name instead. Match either.
122
+ this.tableFilter = page
123
+ .getByRole('searchbox', { name: 'Filter table' })
124
+ .or(page.getByRole('searchbox', { name: 'Search by owner ID' }))
125
+ .first();
126
+ }
127
+ // The Admin's Authorizations page is migrating onto the new design-system
128
+ // shell, which replaces the per-resource-type tabs with a single filter
129
+ // combobox (identity/client ListV2.tsx). That combobox's accessible name was
130
+ // renamed from "Authorization type" to "Resource type" in camunda/camunda
131
+ // commit 377b0e3; match either so both image versions resolve. Select the
132
+ // resource type through whichever this build renders: click the tab, or open
133
+ // the combobox and pick the option.
134
+ async selectResourceType(resourceType, timeout = 60000) {
135
+ const tab = this.selectResourceTypeTab(resourceType);
136
+ const filterCombobox = this.page
137
+ .getByRole('combobox', { name: 'Resource type' })
138
+ .or(this.page.getByRole('combobox', { name: 'Authorization type' }));
139
+ await (0, test_1.expect)(tab.or(filterCombobox)).toBeVisible({ timeout });
140
+ if (await tab.isVisible()) {
141
+ await tab.click();
142
+ return;
143
+ }
144
+ await filterCombobox.click();
145
+ await this.page
146
+ .getByRole('option', { name: resourceType, exact: true })
147
+ .click({ timeout: Math.min(timeout, 30000) });
148
+ }
149
+ // Released 8.10 alpha images still render the Carbon combobox that lists
150
+ // every owner, so accept whichever of the three owner fields this build
151
+ // shows: the Carbon search dropdown, the Carbon combobox that lists every
152
+ // owner inline, or the design-system combobox whose cmdk popover only loads
153
+ // results once the owner ID is typed.
154
+ async selectAuthorizationOwner(ownerId) {
155
+ await (0, test_1.expect)(this.createAuthorizationOwnerSearchInput.or(this.createAuthorizationOwnerComboBox)).toBeVisible({ timeout: 30000 });
156
+ if (await this.createAuthorizationOwnerSearchInput.isVisible()) {
157
+ await this.createAuthorizationOwnerSearchInput.fill(ownerId);
158
+ const ownerOption = this.createAuthorizationOwnerSearchOption(ownerId);
159
+ // The search is debounced and served by the API, so a just-created
160
+ // owner can take a while to surface.
161
+ await (0, test_1.expect)(ownerOption).toBeVisible({ timeout: 60000 });
162
+ await ownerOption.click({ timeout: 30000 });
163
+ // Picking an owner replaces the search input with a tag holding the
164
+ // selection, which is how we know the choice was committed.
165
+ await (0, test_1.expect)(this.createAuthorizationOwnerSearchInput).toBeHidden({
166
+ timeout: 30000,
167
+ });
168
+ return;
169
+ }
170
+ await this.createAuthorizationOwnerComboBox.click();
171
+ const commandInput = this.createAuthorizationOwnerCommandInput;
172
+ const inlineOwnerOption = this.createAuthorizationOwnerOption(ownerId);
173
+ await (0, test_1.expect)(commandInput.or(inlineOwnerOption).first()).toBeVisible({
174
+ timeout: 30000,
175
+ });
176
+ if (await commandInput.isVisible()) {
177
+ await commandInput.fill(ownerId);
178
+ // The result list is served by the API and debounced, and each option is
179
+ // labelled "<owner id> — <name>" rather than the bare ID, so narrow the
180
+ // search to the single row this unique owner ID resolves to instead of
181
+ // matching on the label text.
182
+ const ownerOptions = this.createAuthorizationOwnerCommandOptions;
183
+ await (0, test_1.expect)(ownerOptions).toHaveCount(1, { timeout: 60000 });
184
+ await ownerOptions.first().click({ timeout: 30000, force: true });
185
+ // Committing the choice closes the popover, which is what hands the
186
+ // dialog back to the accessibility tree for the fields that follow.
187
+ await (0, test_1.expect)(commandInput).toBeHidden({ timeout: 30000 });
188
+ return;
189
+ }
190
+ await (0, test_1.expect)(inlineOwnerOption).toBeVisible({
191
+ timeout: 30000,
192
+ });
193
+ await inlineOwnerOption.click({
194
+ timeout: 30000,
195
+ });
196
+ }
197
+ async createAuthorization(authorization) {
198
+ await this.createAuthorizationButton.click();
199
+ await (0, test_1.expect)(this.createAuthorizationModal).toBeVisible();
200
+ await this.createAuthorizationOwnerTypeCombobox.click();
201
+ await this.createAuthorizationOwnerTypeOption(authorization.ownerType).click();
202
+ await this.selectAuthorizationOwner(authorization.ownerId);
203
+ // The Resource type is pre-selected and disabled when the dialog is
204
+ // opened from a specific resource-type tab. Only pick it from the dropdown
205
+ // when the field is still enabled (editable).
206
+ await (0, test_1.expect)(this.createAuthorizationResourceTypeDropdown).toBeVisible({
207
+ timeout: 30000,
208
+ });
209
+ if (await this.createAuthorizationResourceTypeDropdown.isEnabled()) {
210
+ await this.createAuthorizationResourceTypeDropdown.click();
211
+ await this.createAuthorizationResourceTypeOption(authorization.resourceType).click({ timeout: 30000 });
212
+ }
213
+ await this.createAuthorizationResourceIdField.fill(authorization.resourceId);
214
+ for (const permission of authorization.accessPermissions) {
215
+ await (0, sleep_1.sleep)(200);
216
+ const permissionLabel = this.createAuthorizationAccessPermission(permission);
217
+ const permissionInput = this.createAuthorizationModal.locator(`input[id="${permission}"], [role="checkbox"][id="${permission}"]`);
218
+ await (0, test_1.expect)(permissionLabel).toBeVisible();
219
+ if (!(await permissionInput.isChecked())) {
220
+ await permissionLabel.scrollIntoViewIfNeeded();
221
+ await (0, sleep_1.sleep)(300);
222
+ let clickAttempts = 0;
223
+ const maxAttempts = 3;
224
+ while (!(await permissionInput.isChecked()) &&
225
+ clickAttempts < maxAttempts) {
226
+ clickAttempts++;
227
+ await permissionLabel.click({ force: true });
228
+ await (0, sleep_1.sleep)(500);
229
+ if (await permissionInput.isChecked()) {
230
+ break;
231
+ }
232
+ if (clickAttempts < maxAttempts) {
233
+ await permissionLabel.click();
234
+ await (0, sleep_1.sleep)(300);
235
+ }
236
+ }
237
+ await (0, test_1.expect)(permissionInput).toBeChecked({ timeout: 3000 });
238
+ }
239
+ }
240
+ await (0, submitModalWithRetry_1.submitModalWithRetry)(this.createAuthorizationSubmitButton, this.createAuthorizationModal);
241
+ await this.selectResourceType(authorization.resourceType);
242
+ const item = this.page
243
+ .getByRole('row')
244
+ .filter({
245
+ hasText: `${authorization.ownerId}${authorization.resourceId}`,
246
+ })
247
+ .first();
248
+ await this.expectAuthorizationListed(authorization, item);
249
+ }
250
+ // The toolbar search filters by owner ID, so filling it with the resource
251
+ // type narrowed nothing, and the retry helper it fed reloads before it
252
+ // asserts, which dropped the filter entirely. That left the new row to be
253
+ // found on page 1 of the unfiltered table - and the authorizations table
254
+ // paginates, so with the RBA specs creating owners in parallel it is
255
+ // regularly on a later page. Filter by owner, then page through what is
256
+ // left; fall back to the unfiltered table for builds whose search has not
257
+ // caught up with a just-created owner yet.
258
+ async expectAuthorizationListed(authorization, item) {
259
+ const deadline = Date.now() + 180000;
260
+ const budget = (max) => Math.max(5000, Math.min(max, deadline - Date.now()));
261
+ const maxAttempts = 5;
262
+ let lastError;
263
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
264
+ if (attempt > 0 && Date.now() >= deadline) {
265
+ break;
266
+ }
267
+ try {
268
+ await this.selectResourceType(authorization.resourceType, budget(30000));
269
+ for (const filterValue of [authorization.ownerId, '']) {
270
+ await this.filterAuthorizationsByOwner(filterValue);
271
+ const found = await (0, findLocatorWithPagination_1.findLocatorWithPagination)(this.page, item, {
272
+ timeout: budget(filterValue === '' ? 45000 : 15000),
273
+ });
274
+ if (found) {
275
+ await (0, test_1.expect)(item).toBeVisible({ timeout: 5000 });
276
+ return;
277
+ }
278
+ }
279
+ throw new Error(`No row for owner "${authorization.ownerId}" and resource ` +
280
+ `"${authorization.resourceId}" in the ` +
281
+ `${authorization.resourceType} table`);
282
+ }
283
+ catch (error) {
284
+ lastError = error;
285
+ if (attempt < maxAttempts - 1) {
286
+ console.warn(`Attempt ${attempt + 1} did not list the ` +
287
+ `${authorization.resourceType} authorization for ` +
288
+ `"${authorization.ownerId}". Retrying...`);
289
+ // A refused/reset connection while the gateway restarts must feed
290
+ // the next attempt, not abort the retry loop.
291
+ await this.page
292
+ .reload({ waitUntil: 'domcontentloaded' })
293
+ .catch((reloadError) => {
294
+ lastError = reloadError;
295
+ });
296
+ }
297
+ }
298
+ }
299
+ throw new Error(`Authorization for "${authorization.ownerId}" on ` +
300
+ `"${authorization.resourceId}" was not listed: ${String(lastError)}`);
301
+ }
302
+ async filterAuthorizationsByOwner(ownerId) {
303
+ const hasFilter = await this.tableFilter
304
+ .isVisible({ timeout: 5000 })
305
+ .catch(() => false);
306
+ if (!hasFilter) {
307
+ return;
308
+ }
309
+ await this.tableFilter.fill(ownerId);
310
+ await (0, sleep_1.sleep)(1000);
311
+ }
312
+ async deleteAuthorization(resourceId, resourceType) {
313
+ // Same paginated table as createAuthorization: the row being deleted is
314
+ // not necessarily on page 1, and the toolbar search cannot narrow by
315
+ // resource ID.
316
+ const found = await (0, findLocatorWithPagination_1.findLocatorWithPagination)(this.page, this.deleteAuthorizationButton(resourceId), { timeout: 60000 });
317
+ if (!found) {
318
+ throw new Error(`No ${resourceType} authorization row for resource "${resourceId}"`);
319
+ }
320
+ await this.deleteAuthorizationButton(resourceId).click();
321
+ await (0, test_1.expect)(this.deleteAuthorizationModal).toBeVisible();
322
+ await this.deleteAuthorizationModalDeleteButton.click();
323
+ await (0, test_1.expect)(this.deleteAuthorizationModal).not.toBeVisible();
324
+ // Re-selecting the resource type takes two interactions on the
325
+ // design-system build, so this cannot go through the shared
326
+ // reload-click-assert helper, which only accepts a single click locator.
327
+ const deletedCell = this.authorizationsList
328
+ .getByRole('cell', { name: resourceId })
329
+ .first();
330
+ let lastError;
331
+ for (let attempt = 0; attempt < 3; attempt++) {
332
+ await this.page.reload();
333
+ await this.page.waitForLoadState('domcontentloaded');
334
+ await this.selectResourceType(resourceType);
335
+ try {
336
+ await (0, test_1.expect)(deletedCell).not.toBeVisible({ timeout: 5000 });
337
+ return;
338
+ }
339
+ catch (error) {
340
+ lastError = error;
341
+ console.warn(`Attempt ${attempt + 1} still listed the ${resourceType} ` +
342
+ `authorization for "${resourceId}". Retrying...`);
343
+ }
344
+ }
345
+ throw new Error(`Authorization for resource "${resourceId}" is still listed: ` +
346
+ String(lastError));
347
+ }
348
+ }
349
+ exports.Authorization = Authorization;
@@ -0,0 +1,8 @@
1
+ import { Page, Locator } from '@playwright/test';
2
+ declare class ClusterDetailsPage {
3
+ private page;
4
+ readonly clusterDetailsHeading: Locator;
5
+ constructor(page: Page);
6
+ assertCustomPropertyIsNavigated(linkTitle: string, linkName: string, link: string | RegExp): Promise<void>;
7
+ }
8
+ export { ClusterDetailsPage };
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClusterDetailsPage = void 0;
4
+ const test_1 = require("@playwright/test");
5
+ class ClusterDetailsPage {
6
+ page;
7
+ // Custom properties no longer have a heading/section of their own — the
8
+ // Environments/Clusters redesign folds them into the "Cluster details"
9
+ // card as rows (camunda-hub's ClusterDetailFields.sm.tsx), so this is the
10
+ // heading that confirms that card — and the properties inside it — rendered.
11
+ clusterDetailsHeading;
12
+ constructor(page) {
13
+ this.page = page;
14
+ this.clusterDetailsHeading = this.page.getByRole('heading', {
15
+ name: 'Cluster details',
16
+ });
17
+ }
18
+ async assertCustomPropertyIsNavigated(linkTitle, linkName, link) {
19
+ const context = this.page.context();
20
+ const matchesLink = (url) => typeof link === 'string'
21
+ ? url.href.startsWith(link)
22
+ : link.test(url.href);
23
+ // Serve a stub document only when the external site itself is
24
+ // unreachable, so the popup still commits the URL Console navigated to.
25
+ const handler = async (route) => {
26
+ if (route.request().resourceType() !== 'document') {
27
+ await route.continue();
28
+ return;
29
+ }
30
+ try {
31
+ const response = await route.fetch({ timeout: 30000 });
32
+ await route.fulfill({ response });
33
+ }
34
+ catch (error) {
35
+ const url = route.request().url();
36
+ console.warn(`External link ${url} unreachable, stubbing: ${error}`);
37
+ await route.fulfill({
38
+ status: 200,
39
+ contentType: 'text/html',
40
+ body: '<html><body></body></html>',
41
+ });
42
+ }
43
+ };
44
+ await context.route(matchesLink, handler);
45
+ try {
46
+ const pageTabPromise = this.page.waitForEvent('popup', {
47
+ timeout: 60000,
48
+ });
49
+ // Each custom property is a <dt>/<dd> row (direct child of the details
50
+ // card's <dl>), keyed by the property's own description — not by a
51
+ // generic "Link 1"/"Link 2" row title, which this DOM never had.
52
+ await this.page
53
+ .locator('dl > div')
54
+ .filter({ has: this.page.locator('dt', { hasText: linkTitle }) })
55
+ .getByRole('link', { name: linkName, exact: true })
56
+ .click({ timeout: 60000 });
57
+ const camundaPage = await pageTabPromise;
58
+ await (0, test_1.expect)(camundaPage).toHaveURL(link, { timeout: 60000 });
59
+ }
60
+ finally {
61
+ await context.unroute(matchesLink, handler);
62
+ }
63
+ }
64
+ }
65
+ exports.ClusterDetailsPage = ClusterDetailsPage;
@@ -0,0 +1,11 @@
1
+ import { Page, Locator } from '@playwright/test';
2
+ declare class ClusterPage {
3
+ private page;
4
+ readonly clustersList: Locator;
5
+ readonly defaultClusterName: string;
6
+ readonly cluster: Locator;
7
+ constructor(page: Page);
8
+ clickClusterLink(): Promise<void>;
9
+ assertCustomTagsAreVisible(tags: string[]): Promise<void>;
10
+ }
11
+ export { ClusterPage };
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ClusterPage = void 0;
4
+ const test_1 = require("@playwright/test");
5
+ class ClusterPage {
6
+ page;
7
+ clustersList;
8
+ defaultClusterName = process.env.CLUSTER_NAME || 'integration-zeebe';
9
+ cluster;
10
+ constructor(page) {
11
+ this.page = page;
12
+ // The Clusters tab defaults to the card grid (`[data-test="cluster-card"]`),
13
+ // not a table — there is no `tbody` in that view.
14
+ this.clustersList = page.locator('[data-test="cluster-card"]');
15
+ this.cluster = this.clustersList.getByText(this.defaultClusterName).first();
16
+ }
17
+ async clickClusterLink() {
18
+ await (0, test_1.expect)(this.cluster).toBeVisible({ timeout: 60000 });
19
+ await this.cluster.click({ timeout: 30000 });
20
+ await (0, test_1.expect)(this.page.getByText('Cluster details').first()).toBeVisible({
21
+ timeout: 10000,
22
+ });
23
+ }
24
+ async assertCustomTagsAreVisible(tags) {
25
+ // The previous implementation called page.waitForLoadState('networkidle')
26
+ // here. The Console clusters page emits continuous polling/SSE traffic,
27
+ // so networkidle never fires — the test would hang for 12 min then
28
+ // retry, costing ~24 min total. The toBeVisible auto-waits below
29
+ // already cover the readiness window we actually need.
30
+ for (const tag of tags) {
31
+ await (0, test_1.expect)(this.page.getByText(tag, { exact: true }).first()).toBeVisible({
32
+ timeout: 60000,
33
+ });
34
+ }
35
+ }
36
+ }
37
+ exports.ClusterPage = ClusterPage;
@@ -0,0 +1,27 @@
1
+ import { Page, Locator } from '@playwright/test';
2
+ declare class ConnectorMarketplacePage {
3
+ private page;
4
+ private browseAllConnectorMarketplacePage;
5
+ private connectorName?;
6
+ private legacyMarketplaceDialog;
7
+ readonly searchForConnectorTextbox: Locator;
8
+ readonly downloadToProjectButton: Locator;
9
+ readonly addToProjectButton: Locator;
10
+ readonly replaceResourceButton: Locator;
11
+ readonly saveAsCopyButton: Locator;
12
+ readonly cancelButton: Locator;
13
+ readonly marketplaceErrorMessage: Locator;
14
+ readonly noResultsMessage: Locator;
15
+ constructor(page: Page);
16
+ clickSearchForConnectorTextbox(): Promise<void>;
17
+ fillSearchForConnectorTextbox(connectorName: string): Promise<void>;
18
+ clickDownloadToProjectButton(): Promise<void>;
19
+ clickCancelButton(): Promise<void>;
20
+ clickReplaceResourceButton(): Promise<void>;
21
+ waitForConnectorSearchResults(): Promise<void>;
22
+ downloadConnectorToProject(): Promise<void>;
23
+ private confirmImport;
24
+ private waitForImportToSettle;
25
+ private getLegacyDownloadButton;
26
+ }
27
+ export { ConnectorMarketplacePage };