@camunda/e2e-test-suite 0.0.1229 → 0.0.1231

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.
@@ -87,9 +87,21 @@ class ClusterDetailsPage {
87
87
  name: 'Create new Client',
88
88
  });
89
89
  this.settingsTab = page.getByRole('tab', { name: 'Settings' });
90
- this.rbaEnableButton = page.getByRole('button', { name: 'Enable' });
91
- this.rbaDisableButton = page.getByRole('button', { name: 'Disable' });
92
90
  this.dialog = page.getByRole('dialog');
91
+ // Scope the confirmation buttons to the open modal and match exactly. The
92
+ // authorizations toggle behind the modal is a button too, and its
93
+ // accessible name flips to "Authorizations enabled"/"Authorizations
94
+ // disabled" the moment it is clicked — a substring match on
95
+ // "Enable"/"Disable" then resolves to two elements (strict-mode violation),
96
+ // or to the toggle alone, whose click the modal backdrop intercepts.
97
+ this.rbaEnableButton = this.dialog.getByRole('button', {
98
+ name: 'Enable',
99
+ exact: true,
100
+ });
101
+ this.rbaDisableButton = this.dialog.getByRole('button', {
102
+ name: 'Disable',
103
+ exact: true,
104
+ });
93
105
  this.deleteButton = page.getByRole('button', { name: 'Delete' });
94
106
  // The danger button in Console's confirmation modals is now named just
95
107
  // "Delete" (it used to expose "danger Delete"), so scope to the open
@@ -375,7 +375,15 @@ class ClusterPage {
375
375
  }
376
376
  else {
377
377
  await this.createCluster(clusterName, region);
378
- await this.assertClusterHealthyStatusWithRetry(clusterName, 120000, 600000);
378
+ // The Healthy badge is only re-read after a reload, and
379
+ // expectLocatorWithRetry reloads once per attempt -- so `maxRetries` is
380
+ // what sets the polling resolution, not `totalTimeout`. With the old
381
+ // 120s/600s/5 the loop reloaded 5 times and gave up after ~570s, short
382
+ // of its own 600s budget. Polling every 15s instead reloads ~45 times,
383
+ // and 900s gives a cluster provisioning alongside the other parallel
384
+ // workers' clusters the room it actually needs (still well inside the
385
+ // 36-minute test.slow() budget).
386
+ await this.assertClusterHealthyStatusWithRetry(clusterName, 15000, 900000, 60);
379
387
  }
380
388
  }
381
389
  async assertClusterHealthyStatusWithRetry(name, visibilityTimeout = 100000, totalTimeout = 500000, maxRetries) {
@@ -17,6 +17,7 @@ declare class HomePage {
17
17
  readonly buttonSkipCustomization: Locator;
18
18
  constructor(page: Page);
19
19
  clickClusters(): Promise<void>;
20
+ clickClustersWithReloadRecovery(): Promise<void>;
20
21
  clickDashboard(): Promise<void>;
21
22
  clickOrganization(): Promise<void>;
22
23
  closeInformationDialog(): Promise<void>;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HomePage = void 0;
4
4
  const test_1 = require("@playwright/test");
5
+ const clickLocatorWithRetry_1 = require("../../utils/assertionHelpers/clickLocatorWithRetry");
5
6
  class HomePage {
6
7
  page;
7
8
  dashboardTab;
@@ -52,6 +53,24 @@ class HomePage {
52
53
  await (0, test_1.expect)(this.clusterTab).toBeVisible({ timeout: 30000 });
53
54
  await this.clusterTab.click({ timeout: 15000 });
54
55
  }
56
+ // Same navigation as clickClusters(), but recovers from a Console SPA crash
57
+ // by remounting the app. Toggling authorizations restarts the cluster, and
58
+ // while it restarts the cluster-settings page's own API calls fail; Console
59
+ // has been seen to fall into its error boundary ("An error has occurred")
60
+ // there, which unmounts the banner along with the Clusters link. Waiting
61
+ // longer cannot recover a crashed route — only a reload can — so use this
62
+ // after an action that restarts the cluster. A route that crashes on every
63
+ // reload still fails, so a real Console regression is not hidden.
64
+ async clickClustersWithReloadRecovery() {
65
+ await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.clusterTab, {
66
+ visibilityTimeout: 30000,
67
+ totalTimeout: 180000,
68
+ maxRetries: 3,
69
+ postAction: async () => {
70
+ await this.page.reload();
71
+ },
72
+ });
73
+ }
55
74
  async clickDashboard() {
56
75
  await this.dashboardTab.click({ timeout: 90000 });
57
76
  }
@@ -297,7 +297,9 @@ async function disableRBA(clusterName, homePage, clusterPage, clusterDetailsPage
297
297
  });
298
298
  await clusterDetailsPage.clickSettingsTab();
299
299
  await clusterDetailsPage.disableAuthorizations();
300
- await homePage.clickClusters();
300
+ // The toggle restarts the cluster; Console can crash out of the settings
301
+ // route while it does, taking the Clusters nav link with it.
302
+ await homePage.clickClustersWithReloadRecovery();
301
303
  await clusterPage.assertClusterHealthyStatusWithRetry(clusterName);
302
304
  await clusterPage.clickClusterLink(clusterName);
303
305
  await (0, test_1.expect)(clusterDetailsPage.rbaEnabledMessage.first()).not.toBeVisible({
@@ -317,7 +319,9 @@ async function enableAuthorizations(clusterName, homePage, clusterPage, clusterD
317
319
  });
318
320
  await clusterDetailsPage.clickSettingsTab();
319
321
  await clusterDetailsPage.enableAuthorizations();
320
- await homePage.clickClusters();
322
+ // The toggle restarts the cluster; Console can crash out of the settings
323
+ // route while it does, taking the Clusters nav link with it.
324
+ await homePage.clickClustersWithReloadRecovery();
321
325
  await clusterPage.assertClusterHealthyStatusWithRetry(clusterName);
322
326
  await clusterPage.clickClusterLink(clusterName);
323
327
  await clusterDetailsPage.assertComponentsHealth();
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OCIdentityRolesPage = void 0;
4
4
  const test_1 = require("@playwright/test");
5
5
  const UtilitiesPage_1 = require("../SM-8.9/UtilitiesPage");
6
+ const expectLocatorWithPagination_1 = require("../../utils/assertionHelpers/expectLocatorWithPagination");
7
+ const findLocatorWithPagination_1 = require("../../utils/assertionHelpers/findLocatorWithPagination");
6
8
  class OCIdentityRolesPage {
7
9
  page;
8
10
  selectResourceTypeTab;
@@ -100,6 +102,12 @@ class OCIdentityRolesPage {
100
102
  await this.selectResourceTypeTab('Mapping Rules').click();
101
103
  }
102
104
  async clickRole(roleID) {
105
+ // The roles table paginates at 10 rows and the cluster already ships six
106
+ // built-in roles, so a role this suite created is regularly on page 2 and
107
+ // is never in the DOM on page 1 - walk the pages before asserting.
108
+ await (0, findLocatorWithPagination_1.findLocatorWithPagination)(this.page, this.roleCell(roleID).first(), {
109
+ timeout: 60000,
110
+ });
103
111
  await (0, test_1.expect)(this.roleCell(roleID).first()).toBeVisible({ timeout: 60000 });
104
112
  await this.roleCell(roleID).first().click();
105
113
  }
@@ -122,15 +130,26 @@ class OCIdentityRolesPage {
122
130
  await this.createNameField.fill(role.name);
123
131
  await this.createRoleModalCreateButton.click();
124
132
  await (0, test_1.expect)(this.createRoleModal).not.toBeVisible();
125
- await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(this.page, this.rolesList
133
+ // A freshly created role sorts to the end of a table that paginates at 10
134
+ // rows, so on a cluster that already holds ten roles it lands on page 2.
135
+ // Polling page 1 can never see it - page through instead.
136
+ await (0, expectLocatorWithPagination_1.expectLocatorWithPagination)(this.page, this.rolesList
126
137
  .getByRole('cell', {
127
138
  name: role.name,
128
139
  })
129
- .first(), role.id, 10000, false, 10);
140
+ .first(), { maxRetries: 10 });
130
141
  }
131
142
  async deleteRole(name, options) {
132
143
  const { throwError = true } = options || {};
133
144
  try {
145
+ // Same pagination trap as createRole: the row - and therefore its
146
+ // Delete button - is regularly on page 2 by the time cleanup runs. With
147
+ // throwError:false the miss is swallowed, which is how stale roles pile
148
+ // up on the cluster and push later roles off page 1 in the first place.
149
+ const found = await (0, findLocatorWithPagination_1.findLocatorWithPagination)(this.page, this.deleteRoleButton(name), { timeout: 30000 });
150
+ if (!found) {
151
+ throw new Error(`Role "${name}" was not found on any page of the roles table`);
152
+ }
134
153
  await this.deleteRoleButton(name).click();
135
154
  await (0, test_1.expect)(this.deleteRoleModal).toBeVisible();
136
155
  await this.deleteRoleModalDeleteButton.click();
@@ -24,7 +24,7 @@ export declare function modelRestConnector(modelerCreatePage: ModelerCreatePage,
24
24
  username: string;
25
25
  password: string;
26
26
  }): Promise<void>;
27
- export declare function assertLocatorVisibleWithPaginated(page: Page, locator: Locator, text: string): Promise<void>;
27
+ export declare function assertLocatorVisibleWithPaginated(page: Page, locator: Locator, text: string, timeout?: number): Promise<void>;
28
28
  export declare function assertLocatorVisibleWithRetry(page: Page, locator: Locator, text: string, timeout?: number, notVisible?: boolean, maxRetries?: number, clickLocator?: Locator): Promise<void>;
29
29
  export declare function assertPageTextWithRetry(page: Page, text: string, notVisible?: boolean, timeout?: number, maxRetries?: number): Promise<void>;
30
30
  export declare function modelAndRunConnectorsTimerEventDiagram(modelerCreatePage: ModelerCreatePage, modelerHomePage: ModelerHomePage, processName: string, page: Page, connectorMarketplacePage: ConnectorMarketplacePage, connectorTemplatePage: ConnectorTemplatePage, tenant?: string): Promise<void>;
@@ -48,10 +48,13 @@ async function assignMappingToRole(page, ocIdentityHomePage, ocIdentityRolesPage
48
48
  });
49
49
  }
50
50
  catch (error) {
51
- await ocIdentityRolesPage.createRole({ id: role, name: role });
51
+ // Register the id before creating so a create that fails part-way still
52
+ // leaves the role on the afterEach cleanup list; a leaked role stays on
53
+ // the cluster forever and pushes later roles onto page 2 of the table.
52
54
  if (testMappings && role.toLowerCase() != 'admin') {
53
55
  testMappings.roleIds.push(role);
54
56
  }
57
+ await ocIdentityRolesPage.createRole({ id: role, name: role });
55
58
  }
56
59
  await ocIdentityRolesPage.assignMappingRuleToRole(role, mappingRuleId);
57
60
  await assertLocatorVisibleWithPaginated(page, ocIdentityRolesPage.mappingRuleRow(mappingRuleId).first(), `${mappingRuleId} Mapping Rule`);
@@ -231,7 +234,7 @@ async function modelRestConnector(modelerCreatePage, connectorSettingsPage, conn
231
234
  await (0, sleep_1.sleep)(10000);
232
235
  }
233
236
  exports.modelRestConnector = modelRestConnector;
234
- async function assertLocatorVisibleWithPaginated(page, locator, text) {
237
+ async function assertLocatorVisibleWithPaginated(page, locator, text, timeout = 20000) {
235
238
  const maxRetries = 6;
236
239
  for (let attempt = 0; attempt < maxRetries; attempt++) {
237
240
  try {
@@ -240,7 +243,10 @@ async function assertLocatorVisibleWithPaginated(page, locator, text) {
240
243
  if (attempt > 2) {
241
244
  await (0, findLocatorInPaginatedList_1.findLocatorInPaginatedList)(page, locator);
242
245
  }
243
- await (0, test_1.expect)(locator).toBeVisible();
246
+ // Without an explicit budget this inherits the 5s expect default, so
247
+ // six attempts only poll for ~60s in total - not enough for a freshly
248
+ // assigned mapping rule to show up on the role detail page.
249
+ await (0, test_1.expect)(locator).toBeVisible({ timeout });
244
250
  return;
245
251
  }
246
252
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.1229",
3
+ "version": "0.0.1231",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",