@camunda/e2e-test-suite 0.0.982 → 0.0.983
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/SM-8.7/ModelerCreatePage.d.ts +3 -3
- package/dist/pages/SM-8.7/ModelerCreatePage.js +26 -8
- package/dist/pages/SM-8.7/TaskPanelPage.js +5 -1
- package/dist/pages/SM-8.7/UtlitiesPage.js +6 -1
- package/dist/tests/SM-8.10/mt-enabled-user-flows.spec.js +10 -5
- package/dist/utils/gatewayErrors.d.ts +3 -0
- package/dist/utils/gatewayErrors.js +33 -0
- package/package.json +1 -1
|
@@ -101,9 +101,9 @@ declare class ModelerCreatePage {
|
|
|
101
101
|
clickSecondPlacedGateway(): Promise<void>;
|
|
102
102
|
private reloadAndWaitForCanvas;
|
|
103
103
|
/**
|
|
104
|
-
* Detects if the page has been redirected to
|
|
105
|
-
*
|
|
106
|
-
*
|
|
104
|
+
* Detects if the page has been redirected to a login page (session expiry)
|
|
105
|
+
* or is in the middle of an OIDC redirect, and performs a re-login if
|
|
106
|
+
* necessary.
|
|
107
107
|
*/
|
|
108
108
|
private handleSessionExpiry;
|
|
109
109
|
clickStartInstanceMainButton(): Promise<void>;
|
|
@@ -483,9 +483,9 @@ class ModelerCreatePage {
|
|
|
483
483
|
await element.waitFor({ state: 'visible', timeout: 120000 });
|
|
484
484
|
}
|
|
485
485
|
/**
|
|
486
|
-
* Detects if the page has been redirected to
|
|
487
|
-
*
|
|
488
|
-
*
|
|
486
|
+
* Detects if the page has been redirected to a login page (session expiry)
|
|
487
|
+
* or is in the middle of an OIDC redirect, and performs a re-login if
|
|
488
|
+
* necessary.
|
|
489
489
|
*/
|
|
490
490
|
async handleSessionExpiry() {
|
|
491
491
|
const currentUrl = this.page.url();
|
|
@@ -501,12 +501,30 @@ class ModelerCreatePage {
|
|
|
501
501
|
const loginPage = new LoginPage_1.LoginPage(this.page);
|
|
502
502
|
const keycloakBanner = this.page.locator('body#keycloak-bg[data-page-id="login-login"]');
|
|
503
503
|
const usernameField = this.page.locator('#username');
|
|
504
|
-
//
|
|
505
|
-
|
|
504
|
+
// `loginPage.usernameInput` covers the Camunda Identity form, which carries
|
|
505
|
+
// neither the Keycloak body id nor a `#username` input, so neither of the
|
|
506
|
+
// two locators above matches it.
|
|
507
|
+
const loginForm = keycloakBanner
|
|
506
508
|
.or(usernameField)
|
|
507
|
-
.
|
|
508
|
-
.
|
|
509
|
-
|
|
509
|
+
.or(loginPage.usernameInput)
|
|
510
|
+
.first();
|
|
511
|
+
// `Run` and the login form are mutually exclusive, so race them rather than
|
|
512
|
+
// sampling one: an expired session redirects through the OIDC callback
|
|
513
|
+
// after the reload has already resolved, so a point-in-time visibility
|
|
514
|
+
// check still sees the Modeler document and misses the login form that
|
|
515
|
+
// replaces it a moment later. The race also costs nothing on the happy
|
|
516
|
+
// path — it returns as soon as `Run` renders.
|
|
517
|
+
const landedOn = await Promise.race([
|
|
518
|
+
loginForm
|
|
519
|
+
.waitFor({ state: 'visible', timeout: 15000 })
|
|
520
|
+
.then(() => 'login')
|
|
521
|
+
.catch(() => 'unknown'),
|
|
522
|
+
this.startInstanceMainButton
|
|
523
|
+
.waitFor({ state: 'visible', timeout: 15000 })
|
|
524
|
+
.then(() => 'app')
|
|
525
|
+
.catch(() => 'unknown'),
|
|
526
|
+
]);
|
|
527
|
+
if (landedOn !== 'login') {
|
|
510
528
|
return;
|
|
511
529
|
}
|
|
512
530
|
console.warn('clickStartInstanceMainButton: session expired — performing re-login');
|
|
@@ -4,6 +4,7 @@ exports.TaskPanelPage = void 0;
|
|
|
4
4
|
const test_1 = require("@playwright/test");
|
|
5
5
|
const constants_1 = require("../../utils/constants");
|
|
6
6
|
const sleep_1 = require("../../utils/sleep");
|
|
7
|
+
const gatewayErrors_1 = require("../../utils/gatewayErrors");
|
|
7
8
|
class TaskPanelPage {
|
|
8
9
|
page;
|
|
9
10
|
availableTasks;
|
|
@@ -39,6 +40,9 @@ class TaskPanelPage {
|
|
|
39
40
|
}
|
|
40
41
|
async openTask(name, assignButton = true) {
|
|
41
42
|
const maxAttempts = 10;
|
|
43
|
+
// Shared by every attempt so that a sustained Tasklist outage still fails
|
|
44
|
+
// this call rather than stretching it by ten times this budget.
|
|
45
|
+
const gatewayRecoveryDeadline = Date.now() + constants_1._1_SECOND_IN_MS * 60;
|
|
42
46
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
43
47
|
try {
|
|
44
48
|
await this.page.waitForLoadState('domcontentloaded');
|
|
@@ -56,7 +60,7 @@ class TaskPanelPage {
|
|
|
56
60
|
catch (error) {
|
|
57
61
|
if (attempt < maxAttempts) {
|
|
58
62
|
console.warn(`Attempt ${attempt} failed. Reloading page...`);
|
|
59
|
-
await this.page
|
|
63
|
+
await (0, gatewayErrors_1.reloadPastGatewayError)(this.page, gatewayRecoveryDeadline);
|
|
60
64
|
await this.page.waitForLoadState('domcontentloaded');
|
|
61
65
|
}
|
|
62
66
|
else {
|
|
@@ -4,6 +4,8 @@ exports.modelDiagramFromFile = exports.modelAndRunConnectorsTimerEventDiagram =
|
|
|
4
4
|
const test_1 = require("@playwright/test");
|
|
5
5
|
const fileUpload_1 = require("../../utils/fileUpload");
|
|
6
6
|
const sleep_1 = require("../../utils/sleep");
|
|
7
|
+
const gatewayErrors_1 = require("../../utils/gatewayErrors");
|
|
8
|
+
const constants_1 = require("../../utils/constants");
|
|
7
9
|
// Tasklist renders "No permission for Tasklist" as a terminal page: it reads the
|
|
8
10
|
// permissions off the token minted at login and never re-evaluates them, so a
|
|
9
11
|
// user whose Identity role assignment has not propagated yet stays stuck there.
|
|
@@ -61,6 +63,9 @@ async function createAndRunProcess(modelerHomePage, modelerCreatePage, processNa
|
|
|
61
63
|
}
|
|
62
64
|
exports.createAndRunProcess = createAndRunProcess;
|
|
63
65
|
async function completeTaskWithRetry(page, taskPanelPage, taskDetailsPage, taskName, taskPriority, maxRetries = 5) {
|
|
66
|
+
// Shared by every attempt so that a sustained Tasklist outage still fails
|
|
67
|
+
// this call rather than stretching it by five times this budget.
|
|
68
|
+
const gatewayRecoveryDeadline = Date.now() + constants_1._1_SECOND_IN_MS * 60;
|
|
64
69
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
65
70
|
try {
|
|
66
71
|
await page.waitForLoadState('domcontentloaded');
|
|
@@ -98,7 +103,7 @@ async function completeTaskWithRetry(page, taskPanelPage, taskDetailsPage, taskN
|
|
|
98
103
|
catch (error) {
|
|
99
104
|
console.warn(`Attempt ${attempt + 1} failed for completing task ${taskName}:`, error instanceof Error ? error.message : error);
|
|
100
105
|
if (attempt < maxRetries - 1) {
|
|
101
|
-
await
|
|
106
|
+
await (0, gatewayErrors_1.reloadPastGatewayError)(page, gatewayRecoveryDeadline);
|
|
102
107
|
await page.waitForLoadState('domcontentloaded');
|
|
103
108
|
await (0, sleep_1.sleep)(1000);
|
|
104
109
|
}
|
|
@@ -19,7 +19,8 @@ if (process.env.IS_MT === 'true') {
|
|
|
19
19
|
await (0, _setup_1.captureScreenshot)(page, testInfo);
|
|
20
20
|
await (0, _setup_1.captureFailureVideo)(page, testInfo);
|
|
21
21
|
});
|
|
22
|
-
|
|
22
|
+
// Skipped due to bug #60836: https://github.com/camunda/camunda/issues/60836
|
|
23
|
+
SM_8_10_1.test.skip('Main User Can Create & Access Tenant & User Without Access Cannot - User Task Flow', async ({ page, modelerHomePage, modelerCreatePage, taskPanelPage, operateProcessInstancePage, operateProcessesPage, taskDetailsPage, operateHomePage, navigationPage, ocTenantPage, optimizeHomePage, optimizeDashboardPage, browser, ocIdentityHomePage, managementIdentityTenantPage, managementIdentityPage, keycloakAdminPage, keycloakLoginPage, ocIdentityMappingRulesPage, ocIdentityRolesPage, }) => {
|
|
23
24
|
SM_8_10_1.test.slow();
|
|
24
25
|
const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
|
|
25
26
|
const processName = 'Main_User_Task_Flow_' + randomString;
|
|
@@ -127,7 +128,8 @@ if (process.env.IS_MT === 'true') {
|
|
|
127
128
|
}
|
|
128
129
|
});
|
|
129
130
|
});
|
|
130
|
-
|
|
131
|
+
// Skipped due to bug #60836: https://github.com/camunda/camunda/issues/60836
|
|
132
|
+
SM_8_10_1.test.skip('Main User Can Create & Access Tenant - Connectors Flow @tasklistV2', async ({ page, modelerHomePage, modelerCreatePage, operateProcessInstancePage, operateProcessesPage, operateHomePage, navigationPage, managementIdentityTenantPage, connectorSettingsPage, connectorMarketplacePage, optimizeHomePage, optimizeDashboardPage, ocIdentityHomePage, ocTenantPage, }) => {
|
|
131
133
|
SM_8_10_1.test.slow();
|
|
132
134
|
const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
|
|
133
135
|
const processName = 'Main_User_Connectors_Flow_' + randomString;
|
|
@@ -259,7 +261,8 @@ if (process.env.IS_MT === 'true') {
|
|
|
259
261
|
});
|
|
260
262
|
}
|
|
261
263
|
});
|
|
262
|
-
|
|
264
|
+
// Skipped due to bug #60836: https://github.com/camunda/camunda/issues/60836
|
|
265
|
+
SM_8_10_1.test.skip('Second User Can Access Tenant Created By Main User - Connectors Flow @tasklistV2', async ({ page, modelerHomePage, modelerCreatePage, operateProcessInstancePage, operateProcessesPage, operateHomePage, navigationPage, managementIdentityTenantPage, connectorSettingsPage, connectorMarketplacePage, optimizeHomePage, optimizeDashboardPage, ocIdentityHomePage, ocTenantPage, managementIdentityPage, keycloakAdminPage, keycloakLoginPage, ocIdentityRolesPage, ocIdentityMappingRulesPage, browser, }) => {
|
|
263
266
|
SM_8_10_1.test.slow();
|
|
264
267
|
const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
|
|
265
268
|
const processName = 'Second_User_Connectors_Flow_' + randomString;
|
|
@@ -330,7 +333,8 @@ if (process.env.IS_MT === 'true') {
|
|
|
330
333
|
});
|
|
331
334
|
}
|
|
332
335
|
});
|
|
333
|
-
|
|
336
|
+
// Skipped due to bug #60836: https://github.com/camunda/camunda/issues/60836
|
|
337
|
+
SM_8_10_1.test.skip('Default Tenant Is Available And Used When Tenant Not Set In Deployment', async ({ page, modelerHomePage, modelerCreatePage, taskPanelPage, operateProcessInstancePage, operateProcessesPage, taskDetailsPage, operateHomePage, navigationPage, managementIdentityTenantPage, optimizeHomePage, optimizeDashboardPage, ocIdentityHomePage, ocTenantPage, }) => {
|
|
334
338
|
SM_8_10_1.test.slow();
|
|
335
339
|
const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
|
|
336
340
|
const processName = 'Default_Tenant_Flow_' + randomString;
|
|
@@ -466,7 +470,8 @@ if (process.env.IS_MT === 'true') {
|
|
|
466
470
|
await operateProcessInstancePage.tenantNameAssertion(tenantName2);
|
|
467
471
|
});
|
|
468
472
|
});
|
|
469
|
-
|
|
473
|
+
// Skipped due to bug #60836: https://github.com/camunda/camunda/issues/60836
|
|
474
|
+
SM_8_10_1.test.skip('Revoking Tenant Access User Flow', async ({ page, modelerHomePage, modelerCreatePage, taskPanelPage, operateProcessInstancePage, operateProcessesPage, taskDetailsPage, operateHomePage, navigationPage, managementIdentityTenantPage, optimizeHomePage, optimizeDashboardPage, ocIdentityHomePage, ocTenantPage, }) => {
|
|
470
475
|
SM_8_10_1.test.slow();
|
|
471
476
|
const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
|
|
472
477
|
const processName1 = 'Revoke_Tenant_Flow_1' + randomString;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reloadPastGatewayError = exports.isGatewayErrorPage = void 0;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
const GATEWAY_ERROR_HEADING = /50[234]\s+(Bad Gateway|Service Temporarily Unavailable|Gateway Time-?out)/i;
|
|
6
|
+
async function isGatewayErrorPage(page) {
|
|
7
|
+
return page
|
|
8
|
+
.getByRole('heading', { name: GATEWAY_ERROR_HEADING })
|
|
9
|
+
.first()
|
|
10
|
+
.isVisible()
|
|
11
|
+
.catch(() => false);
|
|
12
|
+
}
|
|
13
|
+
exports.isGatewayErrorPage = isGatewayErrorPage;
|
|
14
|
+
// While a component's pod restarts, its ingress answers with an nginx 50x page
|
|
15
|
+
// that carries none of the app. A 5xx does not reject `page.reload`, so a retry
|
|
16
|
+
// loop that reloads after every failed attempt keeps re-rendering that error
|
|
17
|
+
// page and spends its whole attempt budget in seconds, long before the
|
|
18
|
+
// component is back. Reload until the component answers again, bounded by
|
|
19
|
+
// `deadline` so a sustained outage still fails the caller.
|
|
20
|
+
async function reloadPastGatewayError(page, deadline) {
|
|
21
|
+
for (let attempt = 1;; attempt++) {
|
|
22
|
+
const response = await page.reload({ waitUntil: 'domcontentloaded' });
|
|
23
|
+
const status = response?.status();
|
|
24
|
+
const unavailable = (status !== undefined && status >= 500) ||
|
|
25
|
+
(await isGatewayErrorPage(page));
|
|
26
|
+
if (!unavailable || Date.now() >= deadline) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
console.warn(`reloadPastGatewayError: ${page.url()} answered ${status} — waiting for the component to come back (attempt ${attempt})`);
|
|
30
|
+
await page.waitForTimeout(Math.min(constants_1._1_SECOND_IN_MS * 5 * attempt, deadline - Date.now()));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.reloadPastGatewayError = reloadPastGatewayError;
|