@camunda/e2e-test-suite 0.0.930 → 0.0.932

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.
@@ -3,8 +3,8 @@ declare class OptimizeDashboardPage {
3
3
  private page;
4
4
  readonly filterTable: Locator;
5
5
  constructor(page: Page);
6
- clickFilterTable(): Promise<void>;
7
- fillFilterTable(processName: string): Promise<void>;
6
+ clickFilterTable(timeout?: number): Promise<void>;
7
+ fillFilterTable(processName: string, timeout?: number): Promise<void>;
8
8
  processLinkAssertion(processName: string, maxRetries: number, retryDelay?: number): Promise<void>;
9
9
  processOwnerNameAssertion(processName: string, userEmail: string): Promise<void>;
10
10
  }
@@ -9,11 +9,11 @@ class OptimizeDashboardPage {
9
9
  this.page = page;
10
10
  this.filterTable = page.getByPlaceholder('Filter table');
11
11
  }
12
- async clickFilterTable() {
13
- await this.filterTable.click({ timeout: 120000 });
12
+ async clickFilterTable(timeout = 120000) {
13
+ await this.filterTable.click({ timeout });
14
14
  }
15
- async fillFilterTable(processName) {
16
- await this.filterTable.fill(processName, { timeout: 60000 });
15
+ async fillFilterTable(processName, timeout = 60000) {
16
+ await this.filterTable.fill(processName, { timeout });
17
17
  await this.filterTable.press('Enter');
18
18
  }
19
19
  async processLinkAssertion(processName, maxRetries, retryDelay = 60000) {
@@ -39,26 +39,40 @@ class OptimizeDashboardPage {
39
39
  async processOwnerNameAssertion(processName, userEmail) {
40
40
  // Optimize's Process Overview only refreshes its owner data on a page
41
41
  // reload (it does not poll in the background), so a long per-attempt
42
- // assertion timeout just idles against a stale DOM. Poll more frequently
43
- // instead: a short assertion timeout (enough for the table to render after
44
- // reload) plus a shorter delay lets us reload roughly every ~55s and catch
45
- // a slow owner import sooner. The total budget (~23 min) stays safely under
46
- // the test timeout so a genuine failure surfaces as this clean error rather
47
- // than an opaque "page closed" test-timeout.
48
- const maxRetries = 25;
42
+ // assertion timeout just idles against a stale DOM. Poll frequently
43
+ // instead: a short assertion timeout (enough for the table to render) plus
44
+ // a short delay lets us reload roughly every ~45s and catch a slow owner
45
+ // import sooner.
46
+ //
47
+ // The reload also clears the "Filter table" search that
48
+ // processLinkAssertion applied, so every attempt re-applies it — otherwise
49
+ // attempts 2..n search the full, paginated org-wide process list and the
50
+ // row can be off-screen entirely. Filter waits are bounded per attempt so
51
+ // one slow render cannot consume the whole budget.
52
+ //
53
+ // Owner assignment is asynchronous (Optimize assigns the deploying Web
54
+ // Modeler user once the deployment is imported) and on a loaded nightly
55
+ // org it can lag well past 20 minutes, so the loop keeps polling until a
56
+ // wall-clock deadline. The deadline (not just the attempt count) is what
57
+ // keeps the total under the test.slow() timeout, so a genuine failure
58
+ // surfaces as this clean error rather than an opaque test-timeout.
59
+ const maxRetries = 32;
49
60
  const retryDelay = 25000;
61
+ const deadline = Date.now() + 25 * 60 * 1000;
50
62
  const localPart = userEmail.split('@')[0];
51
63
  const uuid = localPart.replace('qa-user-', '');
52
64
  const result = !uuid ? 'QA Camunda' : `QA User ${uuid}`;
53
65
  for (let attempt = 0; attempt < maxRetries; attempt++) {
54
66
  try {
67
+ await this.clickFilterTable(30000);
68
+ await this.fillFilterTable(processName, 15000);
55
69
  await (0, test_1.expect)(this.page.getByRole('row', {
56
70
  name: `${processName}\nProcess\n\t${result}`,
57
71
  })).toBeVisible({ timeout: 20000 });
58
72
  return;
59
73
  }
60
74
  catch (error) {
61
- if (attempt < maxRetries - 1) {
75
+ if (attempt < maxRetries - 1 && Date.now() < deadline) {
62
76
  console.warn(`Attempt ${attempt + 1} failed for asserting owner name ${result} assertion for ${processName} in Optimize.. Retrying...`);
63
77
  try {
64
78
  await this.page.reload();
@@ -69,7 +83,7 @@ class OptimizeDashboardPage {
69
83
  await new Promise((resolve) => setTimeout(resolve, retryDelay));
70
84
  }
71
85
  else {
72
- throw new Error(`Owner name ${result} assertion for ${processName} failed after ${maxRetries} attempts`);
86
+ throw new Error(`Owner name ${result} assertion for ${processName} failed after ${attempt + 1} attempts`);
73
87
  }
74
88
  }
75
89
  }
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createKeycloakUser = exports.createUser = exports.setupKeycloakUser = exports.keycloakAdminCredentials = void 0;
4
4
  const test_1 = require("@playwright/test");
5
+ const sleep_1 = require("../../utils/sleep");
5
6
  const keycloakAdminCredentials = () => {
6
7
  return {
7
8
  username: 'admin',
@@ -33,13 +34,35 @@ async function createUser(navigationPage, identityPage, keycloakLoginPage, keycl
33
34
  await createIdentityUser(navigationPage, identityPage, username);
34
35
  }
35
36
  exports.createUser = createUser;
37
+ // Keycloak occasionally serves "We are sorry... Unexpected error when
38
+ // handling authentication request to identity provider" instead of
39
+ // completing the admin login (the same transient 5xx NavigationPage.goTo's
40
+ // progressive backoff already guards against for app logins). This flow logs
41
+ // into the Keycloak admin console directly rather than through goTo, so it
42
+ // needs the same retry of the whole login sequence instead of failing the
43
+ // test outright on the first hit.
44
+ async function loginToKeycloakAdmin(navigationPage, keycloakLoginPage, keycloakAdminPage, maxRetries = 5) {
45
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
46
+ try {
47
+ await navigationPage.goToKeycloak();
48
+ await keycloakLoginPage.toBeVisible();
49
+ await keycloakLoginPage.fillUsername((0, exports.keycloakAdminCredentials)().username);
50
+ await keycloakLoginPage.fillPassword((0, exports.keycloakAdminCredentials)().password);
51
+ await keycloakLoginPage.clickLogin();
52
+ await keycloakAdminPage.toBeVisible();
53
+ return;
54
+ }
55
+ catch (error) {
56
+ if (attempt >= maxRetries - 1) {
57
+ throw error;
58
+ }
59
+ console.warn(`[KeycloakUtils] Keycloak admin login attempt ${attempt + 1} failed, retrying: ${error instanceof Error ? error.message : error}`);
60
+ await (0, sleep_1.sleep)(2000 * (attempt + 1));
61
+ }
62
+ }
63
+ }
36
64
  async function createKeycloakUser(navigationPage, keycloakLoginPage, keycloakAdminPage, username, password) {
37
- await navigationPage.goToKeycloak();
38
- await keycloakLoginPage.toBeVisible();
39
- await keycloakLoginPage.fillUsername((0, exports.keycloakAdminCredentials)().username);
40
- await keycloakLoginPage.fillPassword((0, exports.keycloakAdminCredentials)().password);
41
- await keycloakLoginPage.clickLogin();
42
- await keycloakAdminPage.toBeVisible();
65
+ await loginToKeycloakAdmin(navigationPage, keycloakLoginPage, keycloakAdminPage);
43
66
  await keycloakAdminPage.switchToCamundaPlatform();
44
67
  await keycloakAdminPage.clickUsersTab();
45
68
  await keycloakAdminPage.clickAddUser();
@@ -30,6 +30,10 @@ declare class NavigationPage {
30
30
  username: string;
31
31
  password: string;
32
32
  }): Promise<void>;
33
+ goToOptimizeWithoutAccess({ username, password, }?: {
34
+ username?: string;
35
+ password?: string;
36
+ }): Promise<void>;
33
37
  goToKeycloak(): Promise<void>;
34
38
  goToOCIdentity(sleepTimeout?: number, credentials?: {
35
39
  username: string;
@@ -43,6 +47,10 @@ declare class NavigationPage {
43
47
  username: string;
44
48
  password: string;
45
49
  }): Promise<void>;
50
+ goToConsoleWithoutAccess({ username, password, }?: {
51
+ username?: string;
52
+ password?: string;
53
+ }): Promise<void>;
46
54
  goToManagementIdentity(sleepTimeout?: number, credentials?: {
47
55
  username: string;
48
56
  password: string;
@@ -118,6 +118,19 @@ class NavigationPage {
118
118
  async goToOptimize(sleepTimeout, credentials) {
119
119
  await this.goTo('/optimize', this.optimizePageBanner, sleepTimeout, credentials);
120
120
  }
121
+ // `goTo` ends by asserting the app banner, so it cannot be used to reach
122
+ // Optimize as a user without the Optimize role: that request ends on a
123
+ // `text/plain` 403 page carrying neither the banner nor a login form, and
124
+ // every retry burns a full 60s timeout. Navigate and log in only; the
125
+ // caller asserts what the denied page shows.
126
+ async goToOptimizeWithoutAccess({ username = 'demo', password = process.env.DISTRO_QA_E2E_TESTS_IDENTITY_FIRSTUSER_PASSWORD ??
127
+ 'demo', } = {}) {
128
+ await this.page.goto('/optimize', {
129
+ timeout: 60000,
130
+ waitUntil: 'domcontentloaded',
131
+ });
132
+ await new LoginPage_1.LoginPage(this.page).login(username, password);
133
+ }
121
134
  async goToKeycloak() {
122
135
  const keycloakUrl = process.env.KEYCLOAK_URL
123
136
  ? `${process.env.KEYCLOAK_URL}/auth`
@@ -134,6 +147,19 @@ class NavigationPage {
134
147
  async goToConsole(sleepTimeout, credentials) {
135
148
  await this.goTo('/console', this.consolePageBanner, sleepTimeout, credentials);
136
149
  }
150
+ // `goTo` ends by asserting the app banner, so it cannot be used to reach
151
+ // Console as a user without the Console role: that request renders
152
+ // Console's own "Something went wrong" error page, which has neither the
153
+ // banner nor a login form, and every retry burns a full 60s timeout.
154
+ // Navigate and log in only; the caller asserts what the denied page shows.
155
+ async goToConsoleWithoutAccess({ username = 'demo', password = process.env.DISTRO_QA_E2E_TESTS_IDENTITY_FIRSTUSER_PASSWORD ??
156
+ 'demo', } = {}) {
157
+ await this.page.goto('/console', {
158
+ timeout: 60000,
159
+ waitUntil: 'domcontentloaded',
160
+ });
161
+ await new LoginPage_1.LoginPage(this.page).login(username, password);
162
+ }
137
163
  async goToManagementIdentity(sleepTimeout, credentials) {
138
164
  await this.goTo('/identity', this.managementIdentityPageBanner, sleepTimeout, credentials);
139
165
  }
@@ -71,7 +71,7 @@ class OperateProcessesPage {
71
71
  await (0, sleep_1.sleep)(1000);
72
72
  await this.page
73
73
  .getByRole('row')
74
- .filter({ hasText: new RegExp(`^Select row${processName}\\d+.*$`) })
74
+ .filter({ hasText: new RegExp(`^(?:Select row)?${processName}\\d+`) })
75
75
  .first()
76
76
  .getByRole('link')
77
77
  .click({ timeout: 30000 });
@@ -43,6 +43,8 @@ async function completeTaskWithRetry(taskPanelPage, taskDetailsPage, taskName, t
43
43
  for (let attempt = 0; attempt < maxRetries; attempt++) {
44
44
  try {
45
45
  await taskPanelPage.openTask(taskName, userTaskName);
46
+ const openTasksWithName = taskPanelPage.availableTasks.getByText(taskName, { exact: true });
47
+ const openTaskCount = await openTasksWithName.count();
46
48
  await (0, sleep_1.sleep)(500);
47
49
  if (!(await taskDetailsPage.assignedToMeText.isVisible())) {
48
50
  await taskDetailsPage.clickAssignToMeButton();
@@ -54,7 +56,9 @@ async function completeTaskWithRetry(taskPanelPage, taskDetailsPage, taskName, t
54
56
  }
55
57
  await taskDetailsPage.taskCompletedBanner.waitFor({ state: 'hidden' });
56
58
  await taskDetailsPage.clickCompleteTaskButton();
57
- await (0, test_1.expect)(taskPanelPage.availableTasks.getByText(taskName, { exact: true }).first()).not.toBeVisible({ timeout: 15000 });
59
+ await (0, test_1.expect)(openTasksWithName).toHaveCount(openTaskCount - 1, {
60
+ timeout: 60000,
61
+ });
58
62
  return;
59
63
  }
60
64
  catch (error) {
@@ -17,8 +17,7 @@ SM_8_8_1.test.describe('Identity User Flow Tests', () => {
17
17
  await (0, _setup_1.captureScreenshot)(page, testInfo);
18
18
  await (0, _setup_1.captureFailureVideo)(page, testInfo);
19
19
  });
20
- //Skipped due to bug 35735: https://github.com/camunda/camunda/issues/35735
21
- SM_8_8_1.test.skip('User Roles User Flow on Management Identity @tasklistV2', async ({ page, navigationPage, optimizeHomePage, managementIdentityPage, keycloakLoginPage, keycloakAdminPage, modelerHomePage, consoleHomePage, settingsPage, browser, }) => {
20
+ (0, SM_8_8_1.test)('User Roles User Flow on Management Identity @tasklistV2', async ({ page, navigationPage, optimizeHomePage, managementIdentityPage, keycloakLoginPage, keycloakAdminPage, modelerHomePage, consoleHomePage, settingsPage, browser, }) => {
22
21
  if (node_process_1.default.env.IS_RBA === 'true' ||
23
22
  node_process_1.default.env.IS_MT === 'true' ||
24
23
  node_process_1.default.env.IS_LICENSE_KEY === 'true') {
@@ -66,7 +65,7 @@ SM_8_8_1.test.describe('Identity User Flow Tests', () => {
66
65
  await (0, sleep_1.sleep)(40000);
67
66
  });
68
67
  await SM_8_8_1.test.step('Ensure Console is not Accessible', async () => {
69
- await navigationPage.goToConsole(undefined, credentials);
68
+ await navigationPage.goToConsoleWithoutAccess(credentials);
70
69
  await (0, test_1.expect)(page.getByText('Something went wrong')).toBeVisible({
71
70
  timeout: 60000,
72
71
  });
@@ -82,11 +81,11 @@ SM_8_8_1.test.describe('Identity User Flow Tests', () => {
82
81
  });
83
82
  if (node_process_1.default.env.IS_OPTIMIZE !== 'false') {
84
83
  await SM_8_8_1.test.step('Ensure Optimize is not Accessible', async () => {
85
- await navigationPage.goToOptimize(undefined, credentials);
86
- await (0, test_1.expect)(optimizeHomePage.optimizeBanner).not.toBeVisible({
84
+ await navigationPage.goToOptimizeWithoutAccess(credentials);
85
+ await (0, test_1.expect)(page.getByText('User has no authorization to access Optimize. Please check your Identity configuration')).toBeVisible({
87
86
  timeout: 60000,
88
87
  });
89
- await (0, test_1.expect)(page.getByText('User has no authorization to access Optimize. Please check your Identity configuration')).toBeVisible({
88
+ await (0, test_1.expect)(optimizeHomePage.optimizeBanner).not.toBeVisible({
90
89
  timeout: 60000,
91
90
  });
92
91
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.930",
3
+ "version": "0.0.932",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",