@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,573 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.modelAndRunConnectorsDocHandlingDiagram = exports.expectTextWithPagination = exports.modelDiagramFromFile = exports.findTextWithPagination = exports.modelAndRunConnectorsTimerEventDiagram = exports.assertPageTextWithRetry = exports.assertLocatorVisibleWithRetry = exports.assertLocatorVisibleWithPaginated = exports.modelRestConnector = exports.completeTaskWithRetry = exports.createAndRunProcess = exports.assignMappingToRole = exports.createUserAndAssignToRole = exports.runMultipleProcesses = exports.deleteAllUserGroups = void 0;
|
|
4
|
+
const sleep_1 = require("../../utils/sleep");
|
|
5
|
+
const test_1 = require("@playwright/test");
|
|
6
|
+
const fileUpload_1 = require("../../utils/fileUpload");
|
|
7
|
+
const findLocatorInPaginatedList_1 = require("../../utils/findLocatorInPaginatedList");
|
|
8
|
+
const KeycloakUtils_1 = require("../SM-8.11/KeycloakUtils");
|
|
9
|
+
const expectLocatorWithPagination_1 = require("../../utils/assertionHelpers/expectLocatorWithPagination");
|
|
10
|
+
// This step will be skipped until new Identity expected flow is confirmed
|
|
11
|
+
async function deleteAllUserGroups() {
|
|
12
|
+
// await navigationPage.goToOCAdmin();
|
|
13
|
+
// await identityPage.clickGroupsTab();
|
|
14
|
+
// await identityPage.deleteAllGroups();
|
|
15
|
+
}
|
|
16
|
+
exports.deleteAllUserGroups = deleteAllUserGroups;
|
|
17
|
+
async function runMultipleProcesses(page, modelerHomePage, modelerCreatePage, numberOfProcesses, processName, processId = '', tenant = '') {
|
|
18
|
+
for (let i = 1; i <= numberOfProcesses; i++) {
|
|
19
|
+
await createAndRunProcess(page, modelerHomePage, modelerCreatePage, processName + i, processId.length > 0 ? processId + i : processId, tenant);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.runMultipleProcesses = runMultipleProcesses;
|
|
23
|
+
async function createUserAndAssignToRole(page, navigationPage, managementIdentityPage, keycloakLoginPage, keycloakAdminPage, ocIdentityHomePage, identityMappingRulesPage, identityRolesPage, username, password, role = 'admin', testMappings) {
|
|
24
|
+
const mappingRuleId = `${username}-user-mapping`;
|
|
25
|
+
const claimName = 'preferred_username';
|
|
26
|
+
await (0, KeycloakUtils_1.createUser)(navigationPage, managementIdentityPage, keycloakLoginPage, keycloakAdminPage, username, password);
|
|
27
|
+
await navigationPage.goToOCAdmin();
|
|
28
|
+
await ocIdentityHomePage.clickMappingRulesTab();
|
|
29
|
+
await identityMappingRulesPage.createMappingRule(mappingRuleId, mappingRuleId, claimName, username);
|
|
30
|
+
await (0, expectLocatorWithPagination_1.expectLocatorWithPagination)(page, identityMappingRulesPage.mappingRuleRow(mappingRuleId).first());
|
|
31
|
+
if (testMappings && mappingRuleId.toLowerCase() != 'demo-user-mapping') {
|
|
32
|
+
testMappings.mappingIds.push(mappingRuleId);
|
|
33
|
+
}
|
|
34
|
+
await assignMappingToRole(page, ocIdentityHomePage, identityRolesPage, role, mappingRuleId, testMappings);
|
|
35
|
+
await (0, expectLocatorWithPagination_1.expectLocatorWithPagination)(page, identityMappingRulesPage
|
|
36
|
+
.mappingRuleRow(mappingRuleId)
|
|
37
|
+
.filter({
|
|
38
|
+
hasText: `${mappingRuleId}${mappingRuleId}${claimName}${username}`,
|
|
39
|
+
})
|
|
40
|
+
.first());
|
|
41
|
+
}
|
|
42
|
+
exports.createUserAndAssignToRole = createUserAndAssignToRole;
|
|
43
|
+
async function assignMappingToRole(page, ocIdentityHomePage, ocIdentityRolesPage, role, mappingRuleId, testMappings) {
|
|
44
|
+
await ocIdentityHomePage.clickRolesTab();
|
|
45
|
+
try {
|
|
46
|
+
await (0, test_1.expect)(ocIdentityRolesPage.roleCell(role).first()).toBeVisible({
|
|
47
|
+
timeout: 20000,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
await ocIdentityRolesPage.createRole({ id: role, name: role });
|
|
52
|
+
if (testMappings && role.toLowerCase() != 'admin') {
|
|
53
|
+
testMappings.roleIds.push(role);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
await ocIdentityRolesPage.assignMappingRuleToRole(role, mappingRuleId);
|
|
57
|
+
await assertLocatorVisibleWithPaginated(page, ocIdentityRolesPage.mappingRuleRow(mappingRuleId).first(), `${mappingRuleId} Mapping Rule`);
|
|
58
|
+
}
|
|
59
|
+
exports.assignMappingToRole = assignMappingToRole;
|
|
60
|
+
async function createAndRunProcess(page, modelerHomePage, modelerCreatePage, processName, processId = '', tenant = '') {
|
|
61
|
+
await modelerHomePage.clickDiagramTypeDropdown();
|
|
62
|
+
await modelerHomePage.clickBpmnTemplateOption();
|
|
63
|
+
await modelerCreatePage.modelCamundaUserTaskDiagram(processName, processId);
|
|
64
|
+
await modelerCreatePage.runProcessInstance('', tenant);
|
|
65
|
+
await modelerHomePage.clickProjectBreadcrumb();
|
|
66
|
+
}
|
|
67
|
+
exports.createAndRunProcess = createAndRunProcess;
|
|
68
|
+
async function completeTaskWithRetry(page, taskPanelPage, taskDetailsPage, taskName, taskPriority, maxRetries = 7) {
|
|
69
|
+
// Dismiss any lingering "Task completed" toast from a previous completion
|
|
70
|
+
// to avoid strict mode violations and stale UI state
|
|
71
|
+
try {
|
|
72
|
+
const toastCloseButton = page
|
|
73
|
+
.locator('.cds--toast-notification')
|
|
74
|
+
.getByRole('button', { name: /close/i })
|
|
75
|
+
.first();
|
|
76
|
+
if (await toastCloseButton.isVisible({ timeout: 2000 })) {
|
|
77
|
+
await toastCloseButton.click();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// No toast to dismiss, continue
|
|
82
|
+
}
|
|
83
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
84
|
+
try {
|
|
85
|
+
// Tasklist keeps polling for task updates, so 'networkidle' never
|
|
86
|
+
// settles here. This wait carried no timeout, so it consumed whatever
|
|
87
|
+
// was left of the 12-minute test budget and the run died inside
|
|
88
|
+
// waitForLoadState. The task panel rendering is the precondition this
|
|
89
|
+
// step actually needs, so wait for that instead.
|
|
90
|
+
await (0, test_1.expect)(taskPanelPage.availableTasks).toBeVisible({ timeout: 60000 });
|
|
91
|
+
const taskLocator = taskPanelPage.availableTasks
|
|
92
|
+
.getByText(taskName, { exact: true })
|
|
93
|
+
.first();
|
|
94
|
+
try {
|
|
95
|
+
await taskLocator.waitFor({ state: 'visible', timeout: 10000 });
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
throw new Error(`Task ${taskName} not found in available tasks`);
|
|
99
|
+
}
|
|
100
|
+
// Click the task directly instead of using openTask's 30-retry loop
|
|
101
|
+
// (this outer loop already provides retry logic)
|
|
102
|
+
await taskLocator.click({ timeout: 10000 });
|
|
103
|
+
await (0, test_1.expect)(taskDetailsPage.detailsPanel).toBeVisible({ timeout: 30000 });
|
|
104
|
+
// Verify the correct task is loaded by checking the task name appears
|
|
105
|
+
// in the details-info section (the task name heading is outside the
|
|
106
|
+
// complementary panel, so we use detailsInfo instead of detailsPanel)
|
|
107
|
+
await (0, test_1.expect)(taskDetailsPage.detailsInfo.getByText(taskName)).toBeVisible({ timeout: 30000 });
|
|
108
|
+
if (!(await taskDetailsPage.assignedToMeText.isVisible({ timeout: 5000 }))) {
|
|
109
|
+
await taskDetailsPage.clickAssignToMeButton();
|
|
110
|
+
}
|
|
111
|
+
await (0, test_1.expect)(taskDetailsPage.detailsPanel.getByText(taskPriority)).toBeVisible({ timeout: 30000 });
|
|
112
|
+
await taskDetailsPage.completeTaskButton.click({ timeout: 30000 });
|
|
113
|
+
await (0, test_1.expect)(taskDetailsPage.taskCompletedBanner).toBeVisible({
|
|
114
|
+
timeout: 30000,
|
|
115
|
+
});
|
|
116
|
+
await (0, test_1.expect)(taskPanelPage.availableTasks.getByText(taskName, { exact: true }).first()).not.toBeVisible({ timeout: 15000 });
|
|
117
|
+
console.log(`Successfully completed task: ${taskName}`);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
console.warn(`Attempt ${attempt + 1} failed for completing task ${taskName}:`, error instanceof Error ? error.message : error);
|
|
122
|
+
if (attempt < maxRetries - 1) {
|
|
123
|
+
// Dismiss any toast before reloading
|
|
124
|
+
try {
|
|
125
|
+
const toastCloseButton = page
|
|
126
|
+
.locator('.cds--toast-notification')
|
|
127
|
+
.getByRole('button', { name: /close/i })
|
|
128
|
+
.first();
|
|
129
|
+
if (await toastCloseButton.isVisible({ timeout: 1000 })) {
|
|
130
|
+
await toastCloseButton.click();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// No toast to dismiss
|
|
135
|
+
}
|
|
136
|
+
// A transient backend 500 while opening the task can leave the page on
|
|
137
|
+
// the raw Tasklist API URL (the JSON error renders as the top-level
|
|
138
|
+
// document). A bare reload then re-requests that broken URL and never
|
|
139
|
+
// restores the SPA, so every remaining attempt is wasted. Re-navigate
|
|
140
|
+
// to the Tasklist list — which uses a different endpoint than the
|
|
141
|
+
// single-task fetch that 500s — so the app re-mounts and the task can
|
|
142
|
+
// be re-opened once the blip clears. Progressive backoff gives the
|
|
143
|
+
// backend time to recover between attempts.
|
|
144
|
+
await (0, sleep_1.sleep)(2000 * (attempt + 1));
|
|
145
|
+
const orchestrationContextPath = process.env.ORCHESTRATION_CONTEXT_PATH || '/orchestration';
|
|
146
|
+
const normalizedOrchestrationPath = orchestrationContextPath.startsWith('/')
|
|
147
|
+
? orchestrationContextPath
|
|
148
|
+
: `/${orchestrationContextPath}`;
|
|
149
|
+
try {
|
|
150
|
+
await page.goto(`${normalizedOrchestrationPath}/tasklist`, {
|
|
151
|
+
timeout: 60000,
|
|
152
|
+
waitUntil: 'domcontentloaded',
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
await page.reload();
|
|
157
|
+
}
|
|
158
|
+
// No settle wait here either — the goto already waited for
|
|
159
|
+
// domcontentloaded and the next attempt waits for the task panel.
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
console.error(`All ${maxRetries} attempts failed for task ${taskName}`);
|
|
163
|
+
throw new Error(`Completion of task ${taskName} failed after ${maxRetries} attempts`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
exports.completeTaskWithRetry = completeTaskWithRetry;
|
|
169
|
+
async function modelRestConnector(modelerCreatePage, connectorSettingsPage, connectorMarketplacePage, processName, url, auth, resultExpression, { resultVariable = '', basicAuthCredentials = { username: '', password: '' }, recoverSession, } = {}) {
|
|
170
|
+
await (0, test_1.expect)(modelerCreatePage.generalPanel).toBeVisible({
|
|
171
|
+
timeout: 120000,
|
|
172
|
+
});
|
|
173
|
+
await modelerCreatePage.enterDiagramName(processName);
|
|
174
|
+
await (0, sleep_1.sleep)(1000);
|
|
175
|
+
await modelerCreatePage.clickCanvas();
|
|
176
|
+
await modelerCreatePage.clickGeneralPropertiesPanel();
|
|
177
|
+
await modelerCreatePage.clickIdInput();
|
|
178
|
+
await modelerCreatePage.fillIdInput(processName);
|
|
179
|
+
await modelerCreatePage.assertNameInput(processName);
|
|
180
|
+
await (0, sleep_1.sleep)(1000);
|
|
181
|
+
await modelerCreatePage.clickStartEventElement();
|
|
182
|
+
try {
|
|
183
|
+
await (0, test_1.expect)(modelerCreatePage.appendElementButton).toBeVisible({
|
|
184
|
+
timeout: 1000,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
await modelerCreatePage.clickStartEventElement();
|
|
189
|
+
}
|
|
190
|
+
await modelerCreatePage.clickAppendElementButton();
|
|
191
|
+
await modelerCreatePage.clickAppendTaskButton();
|
|
192
|
+
// The REST connector template requires engines.camunda ^8.10 (since connector v15).
|
|
193
|
+
// New diagrams default to "Check problems against: Camunda 8.9", which moves the
|
|
194
|
+
// connector into the "Requires newer Camunda version" section where clicking it
|
|
195
|
+
// has no effect. Set the diagram version to 8.10 before opening the picker.
|
|
196
|
+
await modelerCreatePage.setEngineVersion('8.10');
|
|
197
|
+
await modelerCreatePage.clickChangeTypeButton();
|
|
198
|
+
try {
|
|
199
|
+
// The change-element popup virtualizes its list: connector templates render
|
|
200
|
+
// below the built-in BPMN tasks and are not in the queryable DOM slice until
|
|
201
|
+
// the list is filtered. Type the template name into the popup search box so
|
|
202
|
+
// the bundled REST template (compatible with the 8.10 engine set above)
|
|
203
|
+
// renders and is selected directly. Without this filter the off-screen entry
|
|
204
|
+
// is missed and the flow falls back to the marketplace unnecessarily,
|
|
205
|
+
// exposing it to transient marketplace-API errors even though the connector
|
|
206
|
+
// is already bundled in the modeler.
|
|
207
|
+
await (0, test_1.expect)(modelerCreatePage.changeElementSearchInput).toBeVisible({
|
|
208
|
+
timeout: 30000,
|
|
209
|
+
});
|
|
210
|
+
// Connector templates are listed under the popup's "Reusable assets" tab;
|
|
211
|
+
// revealChangeElementTemplate selects it before falling back to search.
|
|
212
|
+
const revealed = await modelerCreatePage.revealChangeElementTemplate(modelerCreatePage.restConnectorOption, 'Send REST Request');
|
|
213
|
+
if (!revealed) {
|
|
214
|
+
throw new Error('Bundled REST connector template not listed');
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
await modelerCreatePage.clickMarketPlaceButton();
|
|
219
|
+
await connectorMarketplacePage.clickSearchForConnectorTextbox();
|
|
220
|
+
await connectorMarketplacePage.fillSearchForConnectorTextbox('REST Connector');
|
|
221
|
+
await connectorMarketplacePage.waitForConnectorSearchResults();
|
|
222
|
+
await connectorMarketplacePage.downloadConnectorToProject();
|
|
223
|
+
// Marketplace modal closes the change-type dropdown. The task may remain in
|
|
224
|
+
// a selected state (class includes "djs-selected") which breaks the exact
|
|
225
|
+
// class-attribute match used by secondElement. Click the canvas background
|
|
226
|
+
// first to deselect, then re-select the task and reopen the dropdown.
|
|
227
|
+
await modelerCreatePage.clickCanvas();
|
|
228
|
+
await modelerCreatePage.secondElement.click({ timeout: 30000 });
|
|
229
|
+
await modelerCreatePage.clickChangeTypeButton();
|
|
230
|
+
}
|
|
231
|
+
await modelerCreatePage.clickRestConnectorOption(recoverSession);
|
|
232
|
+
// Guard: wait for the connector settings panel to render before proceeding.
|
|
233
|
+
// On slow clusters the properties panel can take several seconds to update
|
|
234
|
+
// after the change-type click; if it doesn't appear, re-select the element
|
|
235
|
+
// and try once more before letting the tab click time out.
|
|
236
|
+
try {
|
|
237
|
+
await (0, test_1.expect)(connectorSettingsPage.authenticationTab).toBeVisible({
|
|
238
|
+
timeout: 30000,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
await modelerCreatePage.secondElement
|
|
243
|
+
.click({ timeout: 30000 })
|
|
244
|
+
.catch(() => { });
|
|
245
|
+
await modelerCreatePage.clickChangeTypeButton();
|
|
246
|
+
await modelerCreatePage.clickRestConnectorOption(recoverSession);
|
|
247
|
+
}
|
|
248
|
+
await connectorSettingsPage.clickAuthenticationTab();
|
|
249
|
+
await connectorSettingsPage.clickHTTPEndpointTab();
|
|
250
|
+
if (auth == 'bearer') {
|
|
251
|
+
await connectorSettingsPage.selectAuthenticationType('bearer');
|
|
252
|
+
await (0, test_1.expect)(connectorSettingsPage.bearerTokenInput).toBeVisible({
|
|
253
|
+
timeout: 60000,
|
|
254
|
+
});
|
|
255
|
+
await connectorSettingsPage.clickBearerTokenInput();
|
|
256
|
+
await connectorSettingsPage.fillBearerTokenInput('thisisabearertoken');
|
|
257
|
+
await (0, test_1.expect)(connectorSettingsPage.methodTypeDropdown).toBeVisible({
|
|
258
|
+
timeout: 30000,
|
|
259
|
+
});
|
|
260
|
+
await connectorSettingsPage.selectMethodType('POST');
|
|
261
|
+
}
|
|
262
|
+
else if (auth == 'basic') {
|
|
263
|
+
await connectorSettingsPage.selectAuthenticationType('basic');
|
|
264
|
+
await (0, test_1.expect)(connectorSettingsPage.usernameInput).toBeVisible({
|
|
265
|
+
timeout: 60000,
|
|
266
|
+
});
|
|
267
|
+
await connectorSettingsPage.clickUsernameInput();
|
|
268
|
+
(0, test_1.expect)(basicAuthCredentials['username'].length).toBeGreaterThan(0);
|
|
269
|
+
(0, test_1.expect)(basicAuthCredentials['password'].length).toBeGreaterThan(0);
|
|
270
|
+
await connectorSettingsPage.fillUsernameInput(basicAuthCredentials['username']);
|
|
271
|
+
await (0, test_1.expect)(connectorSettingsPage.passwordInput).toBeVisible({
|
|
272
|
+
timeout: 30000,
|
|
273
|
+
});
|
|
274
|
+
await connectorSettingsPage.clickPasswordInput();
|
|
275
|
+
await connectorSettingsPage.fillPasswordInput(basicAuthCredentials['password']);
|
|
276
|
+
await (0, test_1.expect)(connectorSettingsPage.methodTypeDropdown).toBeVisible({
|
|
277
|
+
timeout: 30000,
|
|
278
|
+
});
|
|
279
|
+
await connectorSettingsPage.selectMethodType('POST');
|
|
280
|
+
}
|
|
281
|
+
await connectorSettingsPage.clickUrlInput();
|
|
282
|
+
await connectorSettingsPage.fillUrlInput(url);
|
|
283
|
+
await (0, sleep_1.sleep)(3000);
|
|
284
|
+
await connectorSettingsPage.clickOutputMappingTab();
|
|
285
|
+
if (resultVariable.length > 0) {
|
|
286
|
+
await (0, test_1.expect)(connectorSettingsPage.resultVariableInput).toBeVisible({
|
|
287
|
+
timeout: 60000,
|
|
288
|
+
});
|
|
289
|
+
await connectorSettingsPage.clickResultVariableInput();
|
|
290
|
+
await connectorSettingsPage.fillResultVariableInput('result');
|
|
291
|
+
await (0, sleep_1.sleep)(5000);
|
|
292
|
+
}
|
|
293
|
+
await connectorSettingsPage.clickResultExpressionInput();
|
|
294
|
+
await connectorSettingsPage.fillResultExpressionInput(resultExpression);
|
|
295
|
+
await (0, sleep_1.sleep)(5000);
|
|
296
|
+
await modelerCreatePage.clickAppendElementButton();
|
|
297
|
+
await modelerCreatePage.clickAppendEndEventButton();
|
|
298
|
+
await (0, sleep_1.sleep)(10000);
|
|
299
|
+
}
|
|
300
|
+
exports.modelRestConnector = modelRestConnector;
|
|
301
|
+
async function assertLocatorVisibleWithPaginated(page, locator, text) {
|
|
302
|
+
const maxRetries = 6;
|
|
303
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
304
|
+
try {
|
|
305
|
+
await page.reload();
|
|
306
|
+
await (0, sleep_1.sleep)(5000);
|
|
307
|
+
if (attempt > 2) {
|
|
308
|
+
await (0, findLocatorInPaginatedList_1.findLocatorInPaginatedList)(page, locator);
|
|
309
|
+
}
|
|
310
|
+
await (0, test_1.expect)(locator).toBeVisible();
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
if (attempt < maxRetries - 1) {
|
|
315
|
+
console.warn(`Attempt ${attempt + 1} failed for asserting ${text}. Retrying...`);
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
throw new Error(`Assertion ${text} failed after ${maxRetries} attempts`);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
exports.assertLocatorVisibleWithPaginated = assertLocatorVisibleWithPaginated;
|
|
324
|
+
async function assertLocatorVisibleWithRetry(page, locator, text, timeout = 30000, notVisible, maxRetries = 3, clickLocator) {
|
|
325
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
326
|
+
try {
|
|
327
|
+
await page.reload();
|
|
328
|
+
await page.waitForLoadState('domcontentloaded');
|
|
329
|
+
if (clickLocator) {
|
|
330
|
+
await (0, test_1.expect)(clickLocator).toBeVisible({
|
|
331
|
+
timeout: 30000,
|
|
332
|
+
});
|
|
333
|
+
await clickLocator.click();
|
|
334
|
+
}
|
|
335
|
+
if (notVisible == true) {
|
|
336
|
+
await (0, test_1.expect)(locator.first()).not.toBeVisible({
|
|
337
|
+
timeout: timeout,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
await (0, test_1.expect)(locator).toBeVisible({
|
|
342
|
+
timeout: timeout,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
catch (error) {
|
|
348
|
+
if (attempt < maxRetries - 1) {
|
|
349
|
+
console.warn(`Attempt ${attempt + 1} failed for asserting ${text}. Retrying...`);
|
|
350
|
+
}
|
|
351
|
+
else {
|
|
352
|
+
// Keep the cause. Callers branch on the original error to tell a real
|
|
353
|
+
// failure apart from the test timeout aborting an in-flight wait (see
|
|
354
|
+
// assertReportWithRefreshes), and a bare attempt count told them
|
|
355
|
+
// nothing — so they retried into an already-dead test budget and
|
|
356
|
+
// reported their own generic "after N attempts" instead.
|
|
357
|
+
throw new Error(`Assertion failed after ${maxRetries} attempts: ${String(error)}`);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
exports.assertLocatorVisibleWithRetry = assertLocatorVisibleWithRetry;
|
|
363
|
+
async function assertPageTextWithRetry(page, text, notVisible, timeout = 30000, maxRetries = 3) {
|
|
364
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
365
|
+
try {
|
|
366
|
+
await page.reload();
|
|
367
|
+
await page.waitForLoadState('domcontentloaded');
|
|
368
|
+
if (notVisible == true) {
|
|
369
|
+
await (0, test_1.expect)(page.getByText(text, { exact: true }).first()).not.toBeVisible({
|
|
370
|
+
timeout: timeout,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
await (0, test_1.expect)(page.getByText(text, { exact: true }).first()).toBeVisible({
|
|
375
|
+
timeout: timeout,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
if (attempt < maxRetries - 1) {
|
|
382
|
+
console.warn(`Attempt ${attempt + 1} failed for asserting ${text}. Retrying...`);
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
throw new Error(`Assertion failed after ${maxRetries} attempts: ${String(error)}`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
exports.assertPageTextWithRetry = assertPageTextWithRetry;
|
|
391
|
+
async function modelAndRunConnectorsTimerEventDiagram(modelerCreatePage, modelerHomePage, processName, page, connectorMarketplacePage, connectorTemplatePage, tenant = '') {
|
|
392
|
+
await modelerHomePage.clickCrossComponentProjectFolder();
|
|
393
|
+
await modelerHomePage.clickDiagramTypeDropdown();
|
|
394
|
+
await modelerHomePage.clickUploadFilesButton();
|
|
395
|
+
await (0, fileUpload_1.uploadFile)(page, 'Rest Connector Timer Event.bpmn');
|
|
396
|
+
await modelerHomePage.clickProcessDiagram('Rest Connector Timer Event');
|
|
397
|
+
await modelerCreatePage.clickDiagramBreadcrumb();
|
|
398
|
+
await modelerCreatePage.clickEditDiagramNameButton();
|
|
399
|
+
await modelerCreatePage.enterDiagramName(processName);
|
|
400
|
+
await modelerCreatePage.clickStartEventElement();
|
|
401
|
+
await modelerCreatePage.clickGeneralPropertiesPanel();
|
|
402
|
+
await modelerCreatePage.clickIdInput();
|
|
403
|
+
await modelerCreatePage.fillIdInput(processName);
|
|
404
|
+
await (0, sleep_1.sleep)(2000);
|
|
405
|
+
await page.reload();
|
|
406
|
+
await modelerCreatePage.clickEighthPlacedElement();
|
|
407
|
+
await modelerCreatePage.clickChangeTypeButton();
|
|
408
|
+
await (0, test_1.expect)(modelerCreatePage.marketPlaceButton).toBeVisible({
|
|
409
|
+
timeout: 120000,
|
|
410
|
+
});
|
|
411
|
+
await modelerCreatePage.clickMarketPlaceButton();
|
|
412
|
+
await connectorMarketplacePage.clickSearchForConnectorTextbox();
|
|
413
|
+
await connectorMarketplacePage.fillSearchForConnectorTextbox('REST Connector');
|
|
414
|
+
await connectorMarketplacePage.waitForConnectorSearchResults();
|
|
415
|
+
await connectorMarketplacePage.downloadConnectorToProject();
|
|
416
|
+
// Both lookups are container-relative: clickProjectBreadcrumb() returns to
|
|
417
|
+
// this test's process application, which is where the uploaded diagram
|
|
418
|
+
// lives. The downloaded element template is the open question -- hub
|
|
419
|
+
// publishes element templates to the workspace, but the marketplace
|
|
420
|
+
// download is initiated from inside the container, so it should land there.
|
|
421
|
+
// If a trace shows the template at workspace root instead, the first of
|
|
422
|
+
// these two calls becomes clickWorkspaceBreadcrumb() followed by
|
|
423
|
+
// returnToResourceContainer() before the diagram is reopened.
|
|
424
|
+
await modelerHomePage.clickProjectBreadcrumb();
|
|
425
|
+
await modelerHomePage.clickProcessDiagram('REST Outbound Connector');
|
|
426
|
+
await connectorTemplatePage.publishConnector();
|
|
427
|
+
await modelerHomePage.clickProjectBreadcrumb();
|
|
428
|
+
await modelerHomePage.clickProcessDiagram(processName);
|
|
429
|
+
await (0, sleep_1.sleep)(5000);
|
|
430
|
+
await page.reload();
|
|
431
|
+
await (0, sleep_1.sleep)(5000);
|
|
432
|
+
await modelerCreatePage.runProcessInstance('', tenant);
|
|
433
|
+
}
|
|
434
|
+
exports.modelAndRunConnectorsTimerEventDiagram = modelAndRunConnectorsTimerEventDiagram;
|
|
435
|
+
async function findTextWithPagination(page, text, timeout = 30000, action) {
|
|
436
|
+
const maxPages = 10;
|
|
437
|
+
const startTime = Date.now();
|
|
438
|
+
for (let pageNum = 0; pageNum < maxPages; pageNum++) {
|
|
439
|
+
if (Date.now() - startTime > timeout) {
|
|
440
|
+
console.log(`Overall timeout of ${timeout}ms exceeded`);
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
const locator = page.getByText(text).last();
|
|
444
|
+
const textExists = await locator
|
|
445
|
+
.isVisible({ timeout: 2000 })
|
|
446
|
+
.catch(() => false);
|
|
447
|
+
if (textExists) {
|
|
448
|
+
console.log(`Found text "${text}" on page ${pageNum + 1}`);
|
|
449
|
+
if (action) {
|
|
450
|
+
await action(locator);
|
|
451
|
+
}
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
454
|
+
const loadMoreButton = page
|
|
455
|
+
.getByRole('button', { name: 'Load more', exact: true })
|
|
456
|
+
.last();
|
|
457
|
+
const isVisible = await loadMoreButton
|
|
458
|
+
.isVisible({ timeout: 1000 })
|
|
459
|
+
.catch(() => false);
|
|
460
|
+
const isEnabled = isVisible
|
|
461
|
+
? await loadMoreButton.isEnabled().catch(() => false)
|
|
462
|
+
: false;
|
|
463
|
+
if (!isVisible || !isEnabled) {
|
|
464
|
+
console.log(`No more pages available after page ${pageNum + 1} (visible: ${isVisible}, enabled: ${isEnabled})`);
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
console.log(`Text "${text}" not found on page ${pageNum + 1}, going to next page...`);
|
|
468
|
+
await loadMoreButton.click();
|
|
469
|
+
await page.waitForTimeout(1000);
|
|
470
|
+
}
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
exports.findTextWithPagination = findTextWithPagination;
|
|
474
|
+
async function modelDiagramFromFile(page, modelerHomePage, modelerCreatePage, processName, fileName = '', nrOfRenamedUserTasks = 0, taskName = '') {
|
|
475
|
+
await modelerHomePage.clickDiagramTypeDropdown();
|
|
476
|
+
await modelerHomePage.clickUploadFilesButton();
|
|
477
|
+
await (0, fileUpload_1.uploadFile)(page, fileName + '.bpmn');
|
|
478
|
+
await modelerHomePage.clickProcessDiagram(fileName);
|
|
479
|
+
await modelerCreatePage.clickDiagramBreadcrumb();
|
|
480
|
+
await modelerCreatePage.clickEditDiagramNameButton();
|
|
481
|
+
await modelerCreatePage.enterDiagramName(processName);
|
|
482
|
+
await (0, sleep_1.sleep)(1000);
|
|
483
|
+
await modelerCreatePage.clickGeneralPropertiesPanel();
|
|
484
|
+
await modelerCreatePage.clickNameInput();
|
|
485
|
+
await modelerCreatePage.fillNameInput(processName);
|
|
486
|
+
await modelerCreatePage.clickIdInput();
|
|
487
|
+
await modelerCreatePage.fillIdInput(processName);
|
|
488
|
+
if (nrOfRenamedUserTasks > 0) {
|
|
489
|
+
for (let i = 1; i <= nrOfRenamedUserTasks; i++) {
|
|
490
|
+
await modelerCreatePage.clickUserTask('process' + i);
|
|
491
|
+
await modelerCreatePage.clickNameInput();
|
|
492
|
+
await modelerCreatePage.fillNameInput(taskName + i);
|
|
493
|
+
await modelerCreatePage.clickIdInput();
|
|
494
|
+
await modelerCreatePage.fillIdInput(taskName + i);
|
|
495
|
+
await modelerCreatePage.clickCanvas();
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
await (0, sleep_1.sleep)(10000);
|
|
499
|
+
}
|
|
500
|
+
exports.modelDiagramFromFile = modelDiagramFromFile;
|
|
501
|
+
async function expectTextWithPagination(page, text, shouldExist = true, timeout = 30000) {
|
|
502
|
+
const found = await findTextWithPagination(page, text, timeout);
|
|
503
|
+
if (shouldExist && !found) {
|
|
504
|
+
throw new Error(`Text "${text}" not found on any page within ${timeout}ms`);
|
|
505
|
+
}
|
|
506
|
+
if (!shouldExist && found) {
|
|
507
|
+
throw new Error(`Text "${text}" should not exist but was found`);
|
|
508
|
+
}
|
|
509
|
+
if (shouldExist && found) {
|
|
510
|
+
await (0, test_1.expect)(page.getByText(text).last()).toBeVisible({ timeout: 5000 });
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
exports.expectTextWithPagination = expectTextWithPagination;
|
|
514
|
+
async function modelAndRunConnectorsDocHandlingDiagram(modelerCreatePage, modelerHomePage, processName, page, connectorMarketplacePage, zeebeUrl, zeebeClientId, zeebeClientSecret, endpoint, tenant = '') {
|
|
515
|
+
await modelerHomePage.clickCrossComponentProjectFolder();
|
|
516
|
+
await modelerHomePage.clickDiagramTypeDropdown();
|
|
517
|
+
await modelerHomePage.clickUploadFilesButton();
|
|
518
|
+
// 8.10-scoped copy of the fixture: it pins the REST connector element
|
|
519
|
+
// template version Web Modeler offers an 8.10 project (v18, engines ^8.10)
|
|
520
|
+
// and declares Camunda 8.10 as the execution platform. SM-8.8/SM-8.9 keep the
|
|
521
|
+
// fixture at the resources root, whose v15 pin still matches the template
|
|
522
|
+
// line their clusters accept. The version lives in the directory, not the
|
|
523
|
+
// basename: Web Modeler renders the basename in the diagram breadcrumb and
|
|
524
|
+
// a longer one widens the overflow tooltip until it covers the Rename item.
|
|
525
|
+
await (0, fileUpload_1.uploadFile)(page, 'SM-8.10/SM_Document_Handling_Connectors_User_Flow.bpmn');
|
|
526
|
+
await modelerHomePage.clickProcessDiagram('SM_Document_Handling_Connectors_User_Flow');
|
|
527
|
+
await modelerCreatePage.clickDiagramBreadcrumb();
|
|
528
|
+
await modelerCreatePage.clickEditDiagramNameButton();
|
|
529
|
+
await modelerCreatePage.enterDiagramName(processName);
|
|
530
|
+
await modelerCreatePage.clickGeneralPropertiesPanel();
|
|
531
|
+
await modelerCreatePage.clickNameInput();
|
|
532
|
+
await modelerCreatePage.fillNameInput(processName);
|
|
533
|
+
await modelerCreatePage.clickIdInput();
|
|
534
|
+
await modelerCreatePage.fillIdInput(processName);
|
|
535
|
+
await (0, sleep_1.sleep)(10000);
|
|
536
|
+
await modelerCreatePage.clickFirstPlaceRESTConnector();
|
|
537
|
+
await modelerCreatePage.clickChangeTypeButton();
|
|
538
|
+
await modelerCreatePage.clickMarketPlaceButton();
|
|
539
|
+
await connectorMarketplacePage.clickSearchForConnectorTextbox();
|
|
540
|
+
await connectorMarketplacePage.fillSearchForConnectorTextbox('REST Connector');
|
|
541
|
+
await connectorMarketplacePage.waitForConnectorSearchResults();
|
|
542
|
+
await connectorMarketplacePage.downloadConnectorToProject();
|
|
543
|
+
await page.reload();
|
|
544
|
+
await page.waitForLoadState('networkidle');
|
|
545
|
+
await modelerCreatePage.fillStartEventVariablesForDocHandling(zeebeUrl, zeebeClientId, zeebeClientSecret);
|
|
546
|
+
// bpmn-js reorders the canvas `<g>` groups as elements render and get
|
|
547
|
+
// selected, so `g:nth-child(N)` does not keep pointing at the task it was
|
|
548
|
+
// written for. Pin each shape by its BPMN id from the fixture instead: these
|
|
549
|
+
// are exactly the five REST tasks the fixture configures with
|
|
550
|
+
// `authentication.type = oauth-client-credentials-flow`, i.e. the ones that
|
|
551
|
+
// render an "OAuth 2.0 token endpoint" field. The sixth task
|
|
552
|
+
// (Activity_0jxgggi, "Download & store Image as 'doc' storage") is `noAuth`
|
|
553
|
+
// and has no such field.
|
|
554
|
+
const restTaskHit = (elementId) => page
|
|
555
|
+
.locator(`.djs-element[data-element-id="${elementId}"] .djs-hit`)
|
|
556
|
+
.first();
|
|
557
|
+
await modelerCreatePage.setOAuthTokenEndpoint(restTaskHit('Activity_0k0vloc'), endpoint);
|
|
558
|
+
await (0, sleep_1.sleep)(2000);
|
|
559
|
+
await modelerCreatePage.setOAuthTokenEndpoint(restTaskHit('Activity_02hwevk'), endpoint);
|
|
560
|
+
await (0, sleep_1.sleep)(2000);
|
|
561
|
+
await modelerCreatePage.setOAuthTokenEndpoint(restTaskHit('Activity_1oi90zl'), endpoint);
|
|
562
|
+
// The two parallel-branch tasks sit under the Details panel, so it has to be
|
|
563
|
+
// closed before their shape can be clicked and reopened afterwards.
|
|
564
|
+
await (0, sleep_1.sleep)(2000);
|
|
565
|
+
await modelerCreatePage.clickCloseDetailsPanel();
|
|
566
|
+
await modelerCreatePage.setOAuthTokenEndpoint(restTaskHit('Activity_0iyt4gv'), endpoint, { openPanel: true, elementId: 'Activity_0iyt4gv' });
|
|
567
|
+
await (0, sleep_1.sleep)(2000);
|
|
568
|
+
await modelerCreatePage.clickCloseDetailsPanel();
|
|
569
|
+
await modelerCreatePage.setOAuthTokenEndpoint(restTaskHit('Activity_1oiegam'), endpoint, { openPanel: true, elementId: 'Activity_1oiegam' });
|
|
570
|
+
await (0, sleep_1.sleep)(2000);
|
|
571
|
+
await modelerCreatePage.runProcessInstance('', tenant);
|
|
572
|
+
}
|
|
573
|
+
exports.modelAndRunConnectorsDocHandlingDiagram = modelAndRunConnectorsDocHandlingDiagram;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Locator, Page } from '@playwright/test';
|
|
2
|
+
import { OptimizeCollectionsPage } from '../SM-8.11/OptimizeCollectionsPage';
|
|
3
|
+
import { OptimizeReportPage } from '../SM-8.11/OptimizeReportPage';
|
|
4
|
+
import { OptimizeHomePage } from '../SM-8.11/OptimizeHomePage';
|
|
5
|
+
export declare const createReportForProcess: (optimizeCollectionsPage: OptimizeCollectionsPage, optimizeReportPage: OptimizeReportPage, processName: string) => Promise<void>;
|
|
6
|
+
export declare const assertReportWithRefreshes: (page: Page, optimizeHomePage: OptimizeHomePage, optimizeCollectionsPage: OptimizeCollectionsPage, optimizeReportPage: OptimizeReportPage, reportName: string, locator: Locator, text: string) => Promise<void>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertReportWithRefreshes = exports.createReportForProcess = void 0;
|
|
4
|
+
const UtilitiesPage_1 = require("../SM-8.11/UtilitiesPage");
|
|
5
|
+
const UtilitiesPage_2 = require("../SM-8.11/UtilitiesPage");
|
|
6
|
+
const constants_1 = require("../../utils/constants");
|
|
7
|
+
const createReportForProcess = async (optimizeCollectionsPage, optimizeReportPage, processName) => {
|
|
8
|
+
const maxRetries = 30;
|
|
9
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
10
|
+
try {
|
|
11
|
+
await optimizeCollectionsPage.clickCreateNewButton();
|
|
12
|
+
await optimizeCollectionsPage.clickReportOption();
|
|
13
|
+
await optimizeReportPage.clickProcessSelectionButton();
|
|
14
|
+
await optimizeReportPage.waitUntilUserTaskProcessIsVisible(processName);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
try {
|
|
19
|
+
await optimizeReportPage.clickCancelButton();
|
|
20
|
+
}
|
|
21
|
+
catch (cancelError) {
|
|
22
|
+
// Page may have been closed due to test timeout, ignore
|
|
23
|
+
}
|
|
24
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
throw new Error(`Failed to create report for process ${processName} after ${maxRetries} attempts`);
|
|
28
|
+
};
|
|
29
|
+
exports.createReportForProcess = createReportForProcess;
|
|
30
|
+
const assertReportWithRefreshes = async (page, optimizeHomePage, optimizeCollectionsPage, optimizeReportPage, reportName, locator, text) => {
|
|
31
|
+
// Was 30. Each attempt does 4 nested retries (each ~30s), so 30
|
|
32
|
+
// worst-case = 15 min on the longest-pole shard. Test that's
|
|
33
|
+
// genuinely failing at 30 attempts will also fail at 10 — same
|
|
34
|
+
// outcome but 3× faster fail. Test that needs more than 10
|
|
35
|
+
// refresh cycles for Optimize to catch up has a bigger underlying
|
|
36
|
+
// problem worth surfacing.
|
|
37
|
+
const maxRetries = 10;
|
|
38
|
+
// Playwright treats a 0 timeout as "no timeout", so passing 0 here made the
|
|
39
|
+
// visibility wait poll one already-rendered report view until the 720s test
|
|
40
|
+
// timeout killed the test. Optimize report views do not live-refresh, so that
|
|
41
|
+
// single wait could never observe the batch import landing, and it consumed
|
|
42
|
+
// every cycle this loop exists to spread out — the reason the failure surfaces
|
|
43
|
+
// as a bare "Test timeout of 720000ms exceeded". Bound each cycle instead, so
|
|
44
|
+
// the reload between cycles actually refetches. 60s (rather than the 90s the
|
|
45
|
+
// identical assertion gets in optimize-user-flows.spec.ts) keeps several
|
|
46
|
+
// cycles inside the smoke test's shared budget.
|
|
47
|
+
const attemptTimeout = constants_1._1_SECOND_IN_MS * 60;
|
|
48
|
+
// Stop starting cycles in time to throw the error below, instead of being
|
|
49
|
+
// killed mid-cycle by the test timeout, which reports no cause at all.
|
|
50
|
+
const deadline = Date.now() + constants_1._1_MINUTE_IN_MS * 6;
|
|
51
|
+
let attempts = 0;
|
|
52
|
+
let lastError;
|
|
53
|
+
while (attempts < maxRetries && Date.now() < deadline) {
|
|
54
|
+
attempts++;
|
|
55
|
+
try {
|
|
56
|
+
await optimizeHomePage.clickCollectionsLink();
|
|
57
|
+
await (0, UtilitiesPage_1.assertPageTextWithRetry)(page, reportName, false);
|
|
58
|
+
await optimizeCollectionsPage.clickMostRecentProcessReport(reportName);
|
|
59
|
+
await (0, UtilitiesPage_2.assertLocatorVisibleWithRetry)(page, locator, text, attemptTimeout);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
lastError = error;
|
|
64
|
+
// Once the test budget is gone (or the page is closed) every remaining
|
|
65
|
+
// attempt rejects instantly, so this loop used to spin out and report
|
|
66
|
+
// "after 10 attempts" for a failure that happened somewhere else
|
|
67
|
+
// entirely. Surface the real error instead of retrying into it.
|
|
68
|
+
if (page.isClosed() || String(error).includes('Test timeout')) {
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
// otherwise nothing to do. Just loop back to the collections
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
throw new Error(`Failed to assert report ${reportName} after ${attempts} attempts: ` +
|
|
75
|
+
String(lastError));
|
|
76
|
+
};
|
|
77
|
+
exports.assertReportWithRefreshes = assertReportWithRefreshes;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|