@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.
- 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/8.7/ModelerCreatePage.js +21 -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,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 };
|