@camunda/e2e-test-suite 0.0.624 → 0.0.626
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/dist/pages/8.10/AppsPage.js +26 -2
- package/dist/pages/8.10/LoginPage.js +17 -5
- package/dist/pages/8.10/OperateHomePage.js +5 -0
- package/dist/pages/8.10/PlayPage.d.ts +3 -0
- package/dist/pages/8.10/PlayPage.js +42 -9
- package/dist/pages/8.10/UtilitiesPage.js +14 -1
- package/dist/pages/SM-8.7/IdentityPage.js +33 -1
- package/dist/pages/SM-8.7/UtlitiesPage.js +0 -1
- package/dist/tests/8.10/aws-cluster-user-flows.spec.js +8 -2
- package/dist/tests/8.10/connectors-user-flows.spec.js +7 -2
- package/dist/tests/8.10/hto-user-flows.spec.js +10 -2
- package/dist/tests/SM-8.7/connectors-user-flows.spec.js +1 -0
- package/dist/tests/SM-8.7/optimize-user-flows.spec.js +1 -0
- package/dist/tests/SM-8.7/play.spec.js +1 -0
- package/package.json +1 -1
|
@@ -68,7 +68,11 @@ class AppsPage {
|
|
|
68
68
|
await cluster.click();
|
|
69
69
|
}
|
|
70
70
|
async clickModeler() {
|
|
71
|
-
const maxRetries =
|
|
71
|
+
const maxRetries = 5;
|
|
72
|
+
const modelerErrorPage = this.page
|
|
73
|
+
.getByText(/local_rate_limited/i)
|
|
74
|
+
.or(this.page.getByText(/Well, that's awkward/i))
|
|
75
|
+
.first();
|
|
72
76
|
for (let retries = 0; retries < maxRetries; retries++) {
|
|
73
77
|
try {
|
|
74
78
|
if (retries === 0) {
|
|
@@ -80,11 +84,31 @@ class AppsPage {
|
|
|
80
84
|
await (0, test_1.expect)(this.modelerLink).toBeVisible({ timeout: 30000 });
|
|
81
85
|
await this.modelerLink.click();
|
|
82
86
|
}
|
|
87
|
+
// After clicking, the browser is redirected through
|
|
88
|
+
// modeler.ultrawombat.com/login which can return 429
|
|
89
|
+
// (`local_rate_limited`) or a generic "Well, that's awkward" error
|
|
90
|
+
// page when the Modeler backend is being rate-limited by SaaS infra.
|
|
91
|
+
// Detect those pages and trigger a clean retry instead of letting
|
|
92
|
+
// the caller's banner-visibility assertion burn its timeout.
|
|
93
|
+
if (await modelerErrorPage.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
94
|
+
throw new Error('MODELER_RATE_LIMIT: navigation landed on a rate-limit / error page');
|
|
95
|
+
}
|
|
83
96
|
return;
|
|
84
97
|
}
|
|
85
98
|
catch (error) {
|
|
99
|
+
const rateLimited = String(error).includes('MODELER_RATE_LIMIT');
|
|
86
100
|
console.warn(`Click attempt ${retries + 1} failed: ${error}`);
|
|
87
|
-
|
|
101
|
+
if (rateLimited) {
|
|
102
|
+
// Modeler login throttling windows commonly need 30-60s to clear.
|
|
103
|
+
await this.page.context().clearCookies();
|
|
104
|
+
await new Promise((resolve) => setTimeout(resolve, 45000));
|
|
105
|
+
await this.page
|
|
106
|
+
.goto('/', { waitUntil: 'domcontentloaded', timeout: 60000 })
|
|
107
|
+
.catch(() => { });
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
await new Promise((resolve) => setTimeout(resolve, 10000));
|
|
111
|
+
}
|
|
88
112
|
}
|
|
89
113
|
}
|
|
90
114
|
throw new Error(`Failed to click the modeler link after ${maxRetries} attempts.`);
|
|
@@ -22,10 +22,15 @@ class LoginPage {
|
|
|
22
22
|
constructor(page) {
|
|
23
23
|
this.page = page;
|
|
24
24
|
this.usernameInput = {
|
|
25
|
-
locator: page
|
|
25
|
+
locator: page
|
|
26
|
+
.getByLabel('Email address')
|
|
27
|
+
.and(page.locator(':not([id="c4-invite-email"])')),
|
|
26
28
|
schema: userNameInputValidInputSchema,
|
|
27
29
|
};
|
|
28
|
-
this.passwordInput = page
|
|
30
|
+
this.passwordInput = page
|
|
31
|
+
.getByLabel(/^Password\s*\*?$/i)
|
|
32
|
+
.or(page.locator('input[type="password"]'))
|
|
33
|
+
.first();
|
|
29
34
|
this.continueButton = page.getByRole('button', {
|
|
30
35
|
name: 'Continue',
|
|
31
36
|
exact: true,
|
|
@@ -47,6 +52,7 @@ class LoginPage {
|
|
|
47
52
|
await this.continueButton.click({ timeout: 30000 });
|
|
48
53
|
}
|
|
49
54
|
async fillPassword(password) {
|
|
55
|
+
await this.passwordInput.waitFor({ state: 'visible', timeout: 120000 });
|
|
50
56
|
await this.passwordInput.fill(password);
|
|
51
57
|
}
|
|
52
58
|
async clickLoginButton() {
|
|
@@ -72,9 +78,15 @@ class LoginPage {
|
|
|
72
78
|
await this.page
|
|
73
79
|
.waitForURL((url) => !url.hostname.includes('console'), { timeout: 60000 })
|
|
74
80
|
.catch(() => { });
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
const onPasswordStep = (await this.passwordHeading
|
|
82
|
+
.isVisible({ timeout: 5000 })
|
|
83
|
+
.catch(() => false)) ||
|
|
84
|
+
(await this.passwordInput.isVisible({ timeout: 5000 }).catch(() => false));
|
|
85
|
+
if (!onPasswordStep) {
|
|
86
|
+
await (0, test_1.expect)(this.usernameInput.locator).toBeVisible({ timeout: 120000 });
|
|
87
|
+
await this.fillUsername(username);
|
|
88
|
+
await this.clickContinueButton();
|
|
89
|
+
}
|
|
78
90
|
await this.fillPassword(password);
|
|
79
91
|
await (0, test_1.expect)(this.loginButton).toBeVisible({ timeout: 120000 });
|
|
80
92
|
await this.clickLoginButton();
|
|
@@ -58,12 +58,17 @@ class OperateHomePage {
|
|
|
58
58
|
preAction: async () => {
|
|
59
59
|
await this.clickMessageBanner();
|
|
60
60
|
},
|
|
61
|
+
maxRetries: 5,
|
|
62
|
+
totalTimeout: 120000,
|
|
63
|
+
visibilityTimeout: 30000,
|
|
61
64
|
});
|
|
62
65
|
await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.processPageHeading, {
|
|
63
66
|
preAction: async () => {
|
|
64
67
|
await (0, test_1.expect)(this.processesTab).toBeVisible({ timeout: 10000 });
|
|
65
68
|
await this.processesTab.click({ timeout: 10000 });
|
|
66
69
|
},
|
|
70
|
+
maxRetries: 5,
|
|
71
|
+
totalTimeout: 120000,
|
|
67
72
|
});
|
|
68
73
|
}
|
|
69
74
|
async closeInformationDialog() {
|
|
@@ -2,6 +2,9 @@ import { Page, Locator } from '@playwright/test';
|
|
|
2
2
|
declare class PlayPage {
|
|
3
3
|
private page;
|
|
4
4
|
readonly completeJobButton: Locator;
|
|
5
|
+
readonly configureTestPanel: Locator;
|
|
6
|
+
readonly configureTestPanelStartButton: Locator;
|
|
7
|
+
readonly startInstanceOverlayButton: Locator;
|
|
5
8
|
constructor(page: Page);
|
|
6
9
|
waitForCompleteJobButtonToBeAvailable(): Promise<void>;
|
|
7
10
|
clickCompleteJobButton(): Promise<void>;
|
|
@@ -2,15 +2,35 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PlayPage = void 0;
|
|
4
4
|
const test_1 = require("@playwright/test");
|
|
5
|
+
const clickLocatorWithRetry_1 = require("../../utils/assertionHelpers/clickLocatorWithRetry");
|
|
5
6
|
const maxWaitTimeSeconds = 120000;
|
|
6
7
|
class PlayPage {
|
|
7
8
|
page;
|
|
8
9
|
completeJobButton;
|
|
10
|
+
configureTestPanel;
|
|
11
|
+
configureTestPanelStartButton;
|
|
12
|
+
startInstanceOverlayButton;
|
|
9
13
|
constructor(page) {
|
|
10
14
|
this.page = page;
|
|
11
15
|
this.completeJobButton = page
|
|
12
16
|
.getByTestId('diagram')
|
|
13
17
|
.getByLabel('Complete job');
|
|
18
|
+
// Newer Modeler Play UI: the "Configure Scenario" floating card hosts a
|
|
19
|
+
// primary "Start" Carbon button (variants: "Start", "Start with
|
|
20
|
+
// variables", "Start with a form"). Source:
|
|
21
|
+
// camunda-hub frontend/packages/modeler/play/src/Definition/
|
|
22
|
+
// ConfigureTestPanel/index.tsx — renders <FloatingCard
|
|
23
|
+
// data-testid="configure-test-panel"> with a <Button> whose visible
|
|
24
|
+
// text starts with "Start".
|
|
25
|
+
this.configureTestPanel = page.getByTestId('configure-test-panel');
|
|
26
|
+
this.configureTestPanelStartButton = this.configureTestPanel.getByRole('button', { name: /^Start( with .*)?$/, exact: false });
|
|
27
|
+
// Legacy bpmn-js canvas overlay button (still emitted by older Modeler
|
|
28
|
+
// builds, also relabeled with cached/example-data suffixes). Kept as a
|
|
29
|
+
// fallback for environments that haven't shipped the ConfigureTestPanel
|
|
30
|
+
// yet.
|
|
31
|
+
this.startInstanceOverlayButton = page
|
|
32
|
+
.getByTestId('diagram')
|
|
33
|
+
.getByLabel('Start instance', { exact: false });
|
|
14
34
|
}
|
|
15
35
|
async waitForCompleteJobButtonToBeAvailable() {
|
|
16
36
|
await (0, test_1.expect)(this.completeJobButton).toBeVisible({
|
|
@@ -21,17 +41,30 @@ class PlayPage {
|
|
|
21
41
|
await this.completeJobButton.click();
|
|
22
42
|
}
|
|
23
43
|
async clickStartInstanceButton() {
|
|
24
|
-
|
|
25
|
-
.
|
|
26
|
-
.
|
|
27
|
-
|
|
44
|
+
const startTrigger = this.configureTestPanelStartButton
|
|
45
|
+
.or(this.startInstanceOverlayButton)
|
|
46
|
+
.first();
|
|
47
|
+
await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, startTrigger, {
|
|
48
|
+
totalTimeout: maxWaitTimeSeconds,
|
|
49
|
+
visibilityTimeout: 30000,
|
|
50
|
+
maxRetries: 5,
|
|
51
|
+
});
|
|
28
52
|
}
|
|
29
53
|
async dismissStartModal() {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
54
|
+
// The intro dialog cycles labels across Modeler versions/states.
|
|
55
|
+
const buttonVariations = [
|
|
56
|
+
'Start a process instance',
|
|
57
|
+
'Start another instance',
|
|
58
|
+
'Start new instance',
|
|
59
|
+
'Start instance',
|
|
60
|
+
];
|
|
61
|
+
for (const buttonName of buttonVariations) {
|
|
62
|
+
const button = this.page.getByRole('button', { name: buttonName });
|
|
63
|
+
if ((await button.count()) > 0) {
|
|
64
|
+
await button.first().click();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
35
68
|
}
|
|
36
69
|
async waitForInstanceDetailsToBeLoaded() {
|
|
37
70
|
const maxRetries = 2;
|
|
@@ -39,7 +39,20 @@ async function loginWithRetry(page, loginPage, testUser, timeout, maxRetries = 3
|
|
|
39
39
|
// Without this check the locator for 'Email address' can match a hidden
|
|
40
40
|
// invite-user input on the Console page when the logout redirect has not
|
|
41
41
|
// yet completed, causing a 200 s wait before failing.
|
|
42
|
-
await
|
|
42
|
+
await test_1.expect
|
|
43
|
+
.poll(async () => {
|
|
44
|
+
const [loginMessageVisible, passwordHeadingVisible, usernameVisible, passwordVisible,] = await Promise.all([
|
|
45
|
+
loginPage.loginMessage.isVisible().catch(() => false),
|
|
46
|
+
loginPage.passwordHeading.isVisible().catch(() => false),
|
|
47
|
+
loginPage.usernameInput.locator.isVisible().catch(() => false),
|
|
48
|
+
loginPage.passwordInput.isVisible().catch(() => false),
|
|
49
|
+
]);
|
|
50
|
+
return (loginMessageVisible ||
|
|
51
|
+
passwordHeadingVisible ||
|
|
52
|
+
usernameVisible ||
|
|
53
|
+
passwordVisible);
|
|
54
|
+
}, { timeout: 60000 })
|
|
55
|
+
.toBe(true);
|
|
43
56
|
await loginPage.loginWithTestUser(testUser);
|
|
44
57
|
return;
|
|
45
58
|
}
|
|
@@ -179,7 +179,39 @@ class IdentityPage {
|
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
async clickAssignRolesButton() {
|
|
182
|
-
|
|
182
|
+
const rolesLoadError = this.page.getByText('The list of roles could not be loaded.');
|
|
183
|
+
const retryLink = this.page.getByText('Retry', { exact: true });
|
|
184
|
+
const maxRetries = 3;
|
|
185
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
186
|
+
await this.assignRolesButton.click();
|
|
187
|
+
try {
|
|
188
|
+
await this.operateCheckbox.waitFor({ state: 'visible', timeout: 10000 });
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
if (await rolesLoadError.isVisible()) {
|
|
193
|
+
console.warn(`clickAssignRolesButton attempt ${attempt}: roles list failed to load, clicking Retry`);
|
|
194
|
+
await retryLink.click();
|
|
195
|
+
try {
|
|
196
|
+
await this.operateCheckbox.waitFor({
|
|
197
|
+
state: 'visible',
|
|
198
|
+
timeout: 10000,
|
|
199
|
+
});
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
await this.page
|
|
204
|
+
.getByRole('button', { name: 'Cancel' })
|
|
205
|
+
.click()
|
|
206
|
+
.catch(() => { });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (attempt === maxRetries) {
|
|
210
|
+
throw new Error(`Roles list failed to load after ${maxRetries} retries`);
|
|
211
|
+
}
|
|
212
|
+
await this.page.waitForTimeout(1000);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
183
215
|
}
|
|
184
216
|
async clickAddButton() {
|
|
185
217
|
await this.addButton.click();
|
|
@@ -230,7 +230,6 @@ async function modelWebhookConnector(modelerCreatePage, connectorMarketplacePage
|
|
|
230
230
|
timeout: 30000,
|
|
231
231
|
});
|
|
232
232
|
await connectorMarketplacePage.downloadConnectorToProject();
|
|
233
|
-
await modelerCreatePage.clickChangeTypeButton();
|
|
234
233
|
}
|
|
235
234
|
await modelerCreatePage.clickWebhookStartEventConnectorOption();
|
|
236
235
|
await modelerCreatePage.clickWebhookIdInput();
|
|
@@ -8,6 +8,7 @@ const sleep_1 = require("../../utils/sleep");
|
|
|
8
8
|
const fileUpload_1 = require("../../utils/fileUpload");
|
|
9
9
|
const UtilitiesPage_2 = require("../../pages/8.10/UtilitiesPage");
|
|
10
10
|
const users_1 = require("../../utils/users");
|
|
11
|
+
const expectLocatorWithRetry_1 = require("../../utils/assertionHelpers/expectLocatorWithRetry");
|
|
11
12
|
const testUser = (0, users_1.getTestUser)('fifteenthUser');
|
|
12
13
|
_8_10_1.test.describe.configure({ mode: 'parallel' });
|
|
13
14
|
_8_10_1.test.describe('AWS Cluster User Flows Test', () => {
|
|
@@ -154,8 +155,13 @@ _8_10_1.test.describe('AWS Cluster User Flows Test', () => {
|
|
|
154
155
|
await _8_10_1.test.step('View Process Instance in Operate, assert it completes and assert variable values are correct', async () => {
|
|
155
156
|
await appsPage.clickCamundaApps();
|
|
156
157
|
await appsPage.clickOperate(clusterName);
|
|
157
|
-
await (0,
|
|
158
|
-
|
|
158
|
+
await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(page, operateHomePage.operateBanner, {
|
|
159
|
+
totalTimeout: 120000,
|
|
160
|
+
visibilityTimeout: 30000,
|
|
161
|
+
maxRetries: 5,
|
|
162
|
+
postAction: async () => {
|
|
163
|
+
await page.reload();
|
|
164
|
+
},
|
|
159
165
|
});
|
|
160
166
|
await operateHomePage.clickProcessesTab();
|
|
161
167
|
await operateProcessesPage.clickProcessCompletedCheckbox();
|
|
@@ -241,8 +241,13 @@ _8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
241
241
|
await _8_10_1.test.step('View Process Instance in Operate, assert it completes and assert result expression', async () => {
|
|
242
242
|
await appsPage.clickCamundaApps();
|
|
243
243
|
await appsPage.clickOperate(awsCluster);
|
|
244
|
-
await (0,
|
|
245
|
-
|
|
244
|
+
await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(page, operateHomePage.operateBanner, {
|
|
245
|
+
totalTimeout: 120000,
|
|
246
|
+
visibilityTimeout: 30000,
|
|
247
|
+
maxRetries: 5,
|
|
248
|
+
postAction: async () => {
|
|
249
|
+
await page.reload();
|
|
250
|
+
},
|
|
246
251
|
});
|
|
247
252
|
await operateHomePage.clickProcessesTab();
|
|
248
253
|
await operateProcessesPage.clickProcessCompletedCheckbox();
|
|
@@ -135,7 +135,11 @@ _8_10_1.test.describe('HTO User Flow Tests', () => {
|
|
|
135
135
|
await (0, test_1.expect)(taskPanelPage.taskListPageBanner).toBeVisible({
|
|
136
136
|
timeout: 20000,
|
|
137
137
|
});
|
|
138
|
-
await (0, expectTextWithRetry_1.expectTextWithRetry)(page, processName
|
|
138
|
+
await (0, expectTextWithRetry_1.expectTextWithRetry)(page, processName, {
|
|
139
|
+
totalTimeout: 120000,
|
|
140
|
+
visibilityTimeout: 30000,
|
|
141
|
+
maxRetries: 5,
|
|
142
|
+
});
|
|
139
143
|
await taskPanelPage.openTask(processName);
|
|
140
144
|
await (0, test_1.expect)(page.getByText('testVariable')).toBeVisible({
|
|
141
145
|
timeout: 60000,
|
|
@@ -218,7 +222,11 @@ _8_10_1.test.describe('HTO User Flow Tests', () => {
|
|
|
218
222
|
await (0, test_1.expect)(taskPanelPage.taskListPageBanner).toBeVisible({
|
|
219
223
|
timeout: 15000,
|
|
220
224
|
});
|
|
221
|
-
await (0, expectTextWithRetry_1.expectTextWithRetry)(page, processName
|
|
225
|
+
await (0, expectTextWithRetry_1.expectTextWithRetry)(page, processName, {
|
|
226
|
+
totalTimeout: 120000,
|
|
227
|
+
visibilityTimeout: 30000,
|
|
228
|
+
maxRetries: 5,
|
|
229
|
+
});
|
|
222
230
|
await (0, UtilitiesPage_1.completeTaskWithRetry)(taskPanelPage, taskDetailsPage, `${taskName}4`, 'critical');
|
|
223
231
|
await (0, UtilitiesPage_1.completeTaskWithRetry)(taskPanelPage, taskDetailsPage, `${taskName}3`, 'high');
|
|
224
232
|
await (0, UtilitiesPage_1.completeTaskWithRetry)(taskPanelPage, taskDetailsPage, `${taskName}2`, 'medium');
|
|
@@ -82,6 +82,7 @@ SM_8_7_1.test.describe.parallel('Connectors User Flow Tests', () => {
|
|
|
82
82
|
});
|
|
83
83
|
});
|
|
84
84
|
(0, SM_8_7_1.test)('Message Start Event Webhook Connector No Auth User Flow', async ({ modelerHomePage, navigationPage, modelerCreatePage, request, operateHomePage, operateProcessInstancePage, operateProcessesPage, connectorMarketplacePage, context, }) => {
|
|
85
|
+
SM_8_7_1.test.slow();
|
|
85
86
|
const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
|
|
86
87
|
const processName = 'Start_Event_Webhook_Connector_No_Auth_Process' + randomString;
|
|
87
88
|
await SM_8_7_1.test.step('Open Cross Component Test Project and Create a BPMN Diagram Template', async () => {
|
|
@@ -52,6 +52,7 @@ SM_8_7_1.test.describe.parallel('Optimize User Flow Tests', () => {
|
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
54
|
(0, SM_8_7_1.test)('Job Worker User Task User Flow', async ({ page, context, modelerHomePage, navigationPage, modelerCreatePage, operateHomePage, operateProcessesPage, operateProcessInstancePage, optimizeHomePage, optimizeCollectionsPage, optimizeReportPage, }) => {
|
|
55
|
+
SM_8_7_1.test.slow();
|
|
55
56
|
const reportName = await (0, _setup_1.generateRandomStringAsync)(5);
|
|
56
57
|
const processName = 'Optimize_Job_Worker_User_Task_Diagram' + reportName;
|
|
57
58
|
await SM_8_7_1.test.step('Open Cross Component Test Project and Create a BPMN Diagram Template', async () => {
|
|
@@ -18,6 +18,7 @@ SM_8_7_1.test.describe('Deploy and run a process in Play', () => {
|
|
|
18
18
|
(0, loggingUtils_1.cleanupTestLogging)();
|
|
19
19
|
});
|
|
20
20
|
(0, SM_8_7_1.test)('User Tasks and Service Task', async ({ page, modelerHomePage, modelerCreatePage, playPage, context, navigationPage, }) => {
|
|
21
|
+
SM_8_7_1.test.slow();
|
|
21
22
|
const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
|
|
22
23
|
const processName = 'Play_Test_Process' + randomString;
|
|
23
24
|
await SM_8_7_1.test.step('Open Cross Component Test Project', async () => {
|