@camunda/e2e-test-suite 0.0.376 → 0.0.377

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.
@@ -130,8 +130,23 @@ class IdentityPage {
130
130
  }
131
131
  }
132
132
  async clickAssignedRolesTab() {
133
- await (0, test_1.expect)(this.assignedRolesTab).toBeVisible({ timeout: 60000 });
134
- await this.assignedRolesTab.click();
133
+ const maxRetries = 3;
134
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
135
+ try {
136
+ await (0, test_1.expect)(this.assignedRolesTab).toBeVisible({ timeout: 60000 });
137
+ await this.assignedRolesTab.click();
138
+ return;
139
+ }
140
+ catch (error) {
141
+ if (attempt === maxRetries) {
142
+ throw error;
143
+ }
144
+ console.warn(`clickAssignedRolesTab attempt ${attempt} failed: ${error}. Retrying...`);
145
+ await this.page.reload().catch((reloadErr) => {
146
+ console.warn(`clickAssignedRolesTab: page.reload error (ignored): ${reloadErr}`);
147
+ });
148
+ }
149
+ }
135
150
  }
136
151
  async clickDemoUser() {
137
152
  await (0, test_1.expect)(this.demoUser).toBeVisible({ timeout: 60000 });
@@ -22,22 +22,35 @@ class KeycloakAdminPage {
22
22
  await (0, test_1.expect)(this.keycloakBanner).toBeVisible({ timeout: 10000 });
23
23
  }
24
24
  async switchToCamundaPlatform() {
25
- const realmSelector = this.page.getByTestId('nav-item-realms');
26
- await realmSelector.click();
27
- const searchField = this.page.getByTestId('selectRealminput');
28
- await (0, test_1.expect)(searchField).toBeVisible({ timeout: 30000 });
29
- await searchField.click();
30
- await this.page
31
- .getByPlaceholder('Search')
32
- .fill(process.env.KEYCLOAK_REALM || 'camunda-platform');
33
- await this.page
34
- .locator('[data-ouia-component-id="OUIA-Generated-Button-control-1"]')
35
- .click();
36
- await this.page
37
- .getByText(process.env.KEYCLOAK_REALM || 'camunda-platform')
38
- .click();
39
- const mainLocator = this.page.locator('#kc-main-content-page-container');
40
- await (0, test_1.expect)(mainLocator).toBeVisible();
25
+ const maxRetries = 3;
26
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
27
+ try {
28
+ const realmSelector = this.page.getByTestId('nav-item-realms');
29
+ await realmSelector.click();
30
+ const searchField = this.page.getByTestId('selectRealminput');
31
+ await (0, test_1.expect)(searchField).toBeVisible({ timeout: 30000 });
32
+ await searchField.click();
33
+ await this.page
34
+ .getByPlaceholder('Search')
35
+ .fill(process.env.KEYCLOAK_REALM || 'camunda-platform');
36
+ await this.page
37
+ .locator('[data-ouia-component-id="OUIA-Generated-Button-control-1"]')
38
+ .click();
39
+ await this.page
40
+ .getByText(process.env.KEYCLOAK_REALM || 'camunda-platform')
41
+ .click();
42
+ const mainLocator = this.page.locator('#kc-main-content-page-container');
43
+ await (0, test_1.expect)(mainLocator).toBeVisible();
44
+ return;
45
+ }
46
+ catch (error) {
47
+ if (attempt === maxRetries) {
48
+ throw new Error(`switchToCamundaPlatform failed after ${maxRetries} attempts: ${error}`);
49
+ }
50
+ console.warn(`switchToCamundaPlatform attempt ${attempt} failed: ${error}. Retrying...`);
51
+ await (0, sleep_1.sleep)(3000);
52
+ }
53
+ }
41
54
  }
42
55
  async clickUsersTab() {
43
56
  await this.page.click('[id="nav-item-users"]');
@@ -57,7 +70,7 @@ class KeycloakAdminPage {
57
70
  async fillPassword(password) {
58
71
  await (0, test_1.expect)(this.credentialsTab).toBeVisible();
59
72
  await this.credentialsTab.click();
60
- await (0, test_1.expect)(this.noCredentialsEmptyAction).toBeVisible({ timeout: 30000 });
73
+ await (0, test_1.expect)(this.noCredentialsEmptyAction).toBeVisible({ timeout: 60000 });
61
74
  await this.noCredentialsEmptyAction.click();
62
75
  await (0, test_1.expect)(this.passwordField).toBeVisible();
63
76
  await this.passwordField.fill(password);
@@ -468,20 +468,34 @@ class ModelerCreatePage {
468
468
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
469
469
  try {
470
470
  if (attempt > 1) {
471
- await this.page.reload();
471
+ await this.page
472
+ .reload({ waitUntil: 'domcontentloaded' })
473
+ .catch((reloadErr) => {
474
+ console.warn(`clickStartInstanceMainButton: page.reload error (ignored): ${reloadErr}`);
475
+ });
476
+ }
477
+ if (this.page.isClosed()) {
478
+ throw new Error('Target page, context or browser has been closed');
472
479
  }
473
- if (await this.startInstanceMainButton.isVisible({ timeout: 3000 })) {
474
- await this.startInstanceMainButton.click({ timeout: 3000 });
480
+ if (await this.startInstanceMainButton.isVisible({ timeout: 30000 })) {
481
+ await this.startInstanceMainButton.click({ timeout: 30000 });
475
482
  }
476
483
  else {
477
- await this.deployAndRunMainButton.click({ timeout: 3000 }); //will be available from alpha7
484
+ await this.deployAndRunMainButton.click({ timeout: 30000 }); //will be available from alpha7
478
485
  }
479
486
  return;
480
487
  }
481
488
  catch (error) {
489
+ const errorMsg = String(error);
490
+ if (errorMsg.includes('Target page, context or browser has been closed') ||
491
+ errorMsg.includes('page has been closed') ||
492
+ errorMsg.includes('Target closed')) {
493
+ throw error;
494
+ }
482
495
  if (attempt === maxRetries) {
483
496
  throw new Error(`Failed to click start process after ${maxRetries} attempts: ${error}`);
484
497
  }
498
+ console.warn(`clickStartInstanceMainButton attempt ${attempt} failed: ${error}`);
485
499
  }
486
500
  }
487
501
  }
@@ -151,7 +151,11 @@ class OperateProcessesPage {
151
151
  if (attempt === MAX_ATTEMPTS) {
152
152
  throw new Error(`Failed to open instance "${processName}" after ${MAX_ATTEMPTS} attempts. Last error: ${String(err)}`);
153
153
  }
154
- await this.page.reload({ waitUntil: 'domcontentloaded' });
154
+ await this.page
155
+ .reload({ waitUntil: 'domcontentloaded' })
156
+ .catch((reloadErr) => {
157
+ console.warn(`clickProcessInstanceLink: page.reload error (ignored): ${reloadErr}`);
158
+ });
155
159
  await (0, sleep_1.sleep)(5000);
156
160
  }
157
161
  }
@@ -38,25 +38,27 @@ class PlayPage {
38
38
  name: 'Run all scenarios',
39
39
  });
40
40
  this.dialog = page.getByRole('dialog');
41
- this.confirmSaveScenarioButton = page
42
- .getByTestId('save-scenario-modal')
43
- .getByRole('button', { name: 'Save' });
41
+ this.confirmSaveScenarioButton = page.getByRole('button', {
42
+ name: 'Save',
43
+ exact: true,
44
+ });
44
45
  this.enterScenarioNameInput = page
45
46
  .getByTestId('save-scenario-modal')
47
+ .first()
46
48
  .locator('input#scenario-name')
47
49
  .first();
48
50
  this.confirmDeleteScenarioButton = page.getByRole('button', {
49
51
  name: 'danger Delete',
50
52
  });
51
- this.viewAllScenariosButton = page.getByRole('button', {
52
- name: '(View all)',
53
- });
53
+ this.viewAllScenariosButton = page
54
+ .getByRole('button', { name: '(View all)' })
55
+ .first();
54
56
  this.getScenarioRowByName = (scenarioName) => page.locator('tr', { hasText: scenarioName });
55
57
  this.getScenarioRow = (scenarioName) => page.locator('tr', { hasText: scenarioName });
56
58
  this.diagram = page.getByTestId('diagram');
57
59
  this.startInstanceButton = page.getByTestId('undefined-Start instance');
58
60
  this.notifications = page.locator('.cds--toast-notification');
59
- this.saveScenarioModal = page.getByTestId('save-scenario-modal');
61
+ this.saveScenarioModal = page.getByTestId('save-scenario-modal').first();
60
62
  this.loadingInstanceDetailsText = this.page.getByText('Loading instance details...');
61
63
  this.retryButton = this.page.getByRole('button', { name: 'Retry' });
62
64
  this.restartProcess = this.page.getByRole('button', {
@@ -201,10 +203,14 @@ class PlayPage {
201
203
  await this.enterScenarioNameInput.fill(newScenarioName);
202
204
  }
203
205
  async clickViewAllScenariosButton() {
204
- await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.viewAllScenariosButton);
206
+ await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.viewAllScenariosButton, {
207
+ totalTimeout: 60000,
208
+ visibilityTimeout: 15000,
209
+ maxRetries: 5,
210
+ });
205
211
  }
206
212
  async confirmSaveScenario() {
207
- await this.confirmSaveScenarioButton.click();
213
+ await this.confirmSaveScenarioButton.click({ timeout: 30000 });
208
214
  }
209
215
  async getDeleteIconForScenario(scenarioName) {
210
216
  const scenarioRow = this.page
@@ -5,4 +5,5 @@ export declare const clickLocatorWithRetry: (page: Page, locator: Locator, optio
5
5
  postAction?: () => Promise<void>;
6
6
  maxRetries?: number;
7
7
  preAction?: () => Promise<void>;
8
+ retryDelayMs?: number;
8
9
  }) => Promise<void>;
@@ -1,29 +1,47 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.clickLocatorWithRetry = void 0;
4
+ const RETRY_DELAY_MS = 2000;
5
+ const isPageClosedError = (err) => {
6
+ if (!(err instanceof Error))
7
+ return false;
8
+ const msg = err.message;
9
+ return (msg.includes('Target page, context or browser has been closed') ||
10
+ msg.includes('page has been closed') ||
11
+ msg.includes('Target closed') ||
12
+ msg.includes('frame was detached') ||
13
+ msg.includes('Frame was detached'));
14
+ };
4
15
  const clickLocatorWithRetry = async (page, locator, options) => {
5
- const { totalTimeout = 60000, visibilityTimeout = 10000, maxRetries = 3, } = options || {};
16
+ const { totalTimeout = 60000, visibilityTimeout = 10000, maxRetries = 3, retryDelayMs = RETRY_DELAY_MS, } = options || {};
6
17
  let attempt = 0;
7
18
  const startTime = Date.now();
8
19
  while (Date.now() - startTime < totalTimeout && attempt < maxRetries) {
9
20
  attempt++;
10
21
  try {
22
+ if (page.isClosed()) {
23
+ throw new Error('clickLocatorWithRetry: page is closed, aborting retries');
24
+ }
11
25
  if (options?.preAction) {
12
26
  await options?.preAction();
13
27
  }
14
- await Promise.race([
15
- locator.waitFor({ state: 'visible', timeout: visibilityTimeout }),
16
- new Promise((_, reject) => setTimeout(() => reject(new Error('Visibility timeout')), visibilityTimeout)),
17
- ]);
18
- await locator.click();
28
+ await locator.waitFor({ state: 'visible', timeout: visibilityTimeout });
29
+ await locator.scrollIntoViewIfNeeded({ timeout: visibilityTimeout });
30
+ await locator.waitFor({ state: 'attached', timeout: visibilityTimeout });
31
+ // Trial click to verify the element is clickable before the real click
32
+ await locator.click({ trial: true, timeout: visibilityTimeout });
33
+ await locator.click({ timeout: visibilityTimeout });
19
34
  return;
20
35
  }
21
36
  catch (err) {
22
- console.log(`Attempt ${attempt} failed for ${locator}, retrying...`);
37
+ if (isPageClosedError(err)) {
38
+ throw err;
39
+ }
40
+ console.log(`Attempt ${attempt} failed for ${locator}, retrying... Error: ${err}`);
23
41
  if (options?.postAction) {
24
42
  await options.postAction();
25
43
  }
26
- await page.waitForTimeout(500);
44
+ await page.waitForTimeout(retryDelayMs);
27
45
  }
28
46
  }
29
47
  throw new Error(`Failed to click locator ${locator} after ${attempt} attempts.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.376",
3
+ "version": "0.0.377",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",