@camunda/e2e-test-suite 0.0.1275 → 0.0.1277

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.
@@ -280,7 +280,14 @@ class ClusterPage {
280
280
  await this.clickClusterNameInput();
281
281
  await this.fillClusterNameInput(name);
282
282
  await this.clickClusterType();
283
- await this.page.getByRole('tab', { name: region }).click();
283
+ // Same design-system role="radio" fallback as the choice()-wrapped
284
+ // locators above -- `region` is a runtime parameter ('GCP'/'AWS') so
285
+ // this can't reuse a named locator directly. Ported from
286
+ // pages/8.9/ClusterPage.ts.
287
+ await this.page
288
+ .getByRole('tab', { name: region })
289
+ .or(this.page.getByRole('radio', { name: region, exact: true }))
290
+ .click();
284
291
  await this.selectSubRegion(region);
285
292
  await this.clickClusterOption();
286
293
  await this.determineTasklistAPI(tasklistV1Api);
@@ -118,9 +118,16 @@ class ConsoleOrganizationPage {
118
118
  name: 'danger',
119
119
  })
120
120
  .last();
121
- this.filterTableSearchbox = page.getByRole('searchbox', {
122
- name: 'Filter table',
123
- });
121
+ // Camunda Hub's design-system Organization Management page renamed the
122
+ // Carbon TableToolbarSearch's default "Filter table" label to plain
123
+ // "Search" (same rename already ported elsewhere for the secrets
124
+ // panels). Confirmed against camunda-hub's own
125
+ // e2e-playwright/pages/Console/ConsoleOrgManagementPage.ts, which already
126
+ // uses `getByRole('searchbox', {name: 'Search'})` for this exact users
127
+ // list. Match either.
128
+ this.filterTableSearchbox = page
129
+ .getByRole('searchbox', { name: 'Filter table' })
130
+ .or(page.getByRole('searchbox', { name: 'Search' }));
124
131
  }
125
132
  async filterTable(names) {
126
133
  for (const name of names) {
@@ -48,21 +48,49 @@ class ProcessApplicationPage {
48
48
  // .first() defensively only: hub renders exactly one of these today, in the
49
49
  // workspace-root empty state. A future second placement should not turn
50
50
  // every PA-creating test into a strict-mode violation.
51
+ //
52
+ // hubTerm('project') (utils/hub-terminology.ts) renames "process
53
+ // application" to "project" in copy once an org has workspacesEnabled --
54
+ // confirmed from a nightly trace screenshot on hub.ultrawombat.com: the
55
+ // workspace-root empty state renders "Create project" (not "Create
56
+ // process application"), and EmptyStateActionButtons.tsx's source
57
+ // confirms the button text is `Create {hubTerm('project')}`. Accept both
58
+ // so this keeps working on orgs at either rollout stage.
59
+ // exact:true on both -- without it, "Create project" substring-matches
60
+ // the split button's own "Create project options" chevron too, and
61
+ // .first() then picks whichever the DOM happens to order first. Same
62
+ // pattern already fixed for the same chevron in
63
+ // ModelerHomePage.ts's createNewProjectButton/createWorkspaceButton.
51
64
  this.createProcessApplicationButton = page
52
- .getByRole('button', { name: 'Create process application' })
65
+ .getByRole('button', { name: 'Create process application', exact: true })
66
+ .or(page.getByRole('button', { name: 'Create project', exact: true }))
53
67
  .first();
54
68
  this.createProcessApplicationOption = page.locator('[data-test="create-process-application"]');
55
- this.creationDialog = page.getByRole('dialog', {
56
- name: 'Create a process application',
57
- });
69
+ // Same hubTerm('project') rename as the button above --
70
+ // CreationModalDs.common.tsx's dialog title is `Create a
71
+ // {hubTerm('project')}`. A confirmed nightly trace showed a THIRD shape,
72
+ // live on this SaaS org: `app/pages/workspace-projects/features/
73
+ // create-project/CreateProjectModal.tsx` -- a newer, unrelated component
74
+ // (built on `api/projects`' useCreateProject, not processApplicationStore)
75
+ // titled plainly "Create project" (no "a"), with its field labelled
76
+ // "Project name" (not "Name") and its submit button reading "Create
77
+ // project" (not "Create"). Accept all three shapes.
78
+ this.creationDialog = page
79
+ .getByRole('dialog', { name: 'Create a process application' })
80
+ .or(page.getByRole('dialog', { name: 'Create a project' }))
81
+ .or(page.getByRole('dialog', { name: 'Create project', exact: true }));
58
82
  // Deliberately scoped to the dialog: ModelerCreatePage owns an unscoped
59
83
  // getByLabel('Name', {exact: true}) for the properties panel, which would
60
84
  // resolve to two elements while the creation modal is open.
61
- this.nameInput = this.creationDialog.getByLabel('Name', { exact: true });
62
- this.submitButton = this.creationDialog.getByRole('button', {
63
- name: 'Create',
85
+ this.nameInput = this.creationDialog
86
+ .getByLabel('Name', { exact: true })
87
+ .or(this.creationDialog.getByLabel('Project name', { exact: true }));
88
+ this.submitButton = this.creationDialog
89
+ .getByRole('button', { name: 'Create', exact: true })
90
+ .or(this.creationDialog.getByRole('button', {
91
+ name: 'Create project',
64
92
  exact: true,
65
- });
93
+ }));
66
94
  this.cancelButton = this.creationDialog.getByRole('button', {
67
95
  name: 'Cancel',
68
96
  exact: true,
@@ -7,8 +7,18 @@ class SettingsPage {
7
7
  logoutButton;
8
8
  constructor(page) {
9
9
  this.page = page;
10
- this.openSettingsButton = page.getByLabel('Open Settings');
11
- this.logoutButton = page.getByRole('button', { name: 'Log out' });
10
+ // Camunda Hub's design-system shell renders a `UserMenu` button named
11
+ // "User menu, <full name>" in place of the legacy Carbon "Open Settings"
12
+ // icon button, and its logout control is a menuitem inside that menu
13
+ // rather than a plain button. Both shells still ship -- match either.
14
+ // Ported from camunda-hub's own e2e-playwright/pages/Console/ConsoleBasePage.ts
15
+ // (settingsButton / dsLogoutMenuItem / carbonLogoutButton).
16
+ this.openSettingsButton = page
17
+ .getByRole('button', { name: /^User menu,/ })
18
+ .or(page.getByLabel('Open Settings'));
19
+ this.logoutButton = page
20
+ .getByRole('menuitem', { name: 'Log out', exact: true })
21
+ .or(page.getByRole('button', { name: 'Log out' }));
12
22
  }
13
23
  async clickOpenSettingsButton(retries = 3) {
14
24
  for (let i = 0; i < retries; i++) {
@@ -21,8 +21,13 @@ declare class AppsPage {
21
21
  readonly hubSidebar: Locator;
22
22
  constructor(page: Page);
23
23
  clickCluster(component: Locator, name: string): Promise<void>;
24
+ private expandHubClusterGroup;
25
+ private expandHubSidebar;
26
+ private showAllWorkspaces;
24
27
  private clickHubClusterApp;
28
+ private isOnHub;
25
29
  clickModeler(): Promise<void>;
30
+ private clickHubModelerHome;
26
31
  /**
27
32
  * Waits for the Modeler app to actually render, and recovers the one landing
28
33
  * state a hostname check cannot see: Modeler's OAuth `/login-callback`
@@ -4,6 +4,9 @@ exports.AppsPage = void 0;
4
4
  const test_1 = require("@playwright/test");
5
5
  const sleep_1 = require("../../utils/sleep");
6
6
  const UtilitiesPage_1 = require("../8.8/UtilitiesPage");
7
+ function escapeRegExp(value) {
8
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
9
+ }
7
10
  class AppsPage {
8
11
  page;
9
12
  modelerLink;
@@ -85,6 +88,187 @@ class AppsPage {
85
88
  // up the app link sidebar-wide rather than scoped to a "group" container
86
89
  // that doesn't reliably exist. Ported from pages/8.9/AppsPage.ts, where
87
90
  // this shape is already nightly-proven.
91
+ async expandHubClusterGroup(clusterName) {
92
+ const toggle = this.hubSidebar
93
+ .getByRole('button', { name: clusterName })
94
+ .first();
95
+ // The diagram editor (and other full-screen views reached from it, e.g.
96
+ // right after runProcessInstance/deployDiagram) has no "Main navigation"
97
+ // sidebar at all -- confirmed from a nightly trace's DOM snapshot: the
98
+ // page's only <nav> is aria-label="Context navigation" (the breadcrumb),
99
+ // and neither "Operate"/"Tasklist"/"Optimize" nor any app-switcher text
100
+ // appears anywhere on it. clickCamundaApps() is a no-op on Hub (there is
101
+ // no switcher panel to open), so a caller reaching clickOperate/
102
+ // clickTasklist/clickOptimize straight from a diagram was left on a page
103
+ // this sidebar could never resolve on, no matter how the toggle itself
104
+ // is matched. Navigate home first, the same way clickModeler() does,
105
+ // whenever the sidebar isn't already there.
106
+ //
107
+ // A freshly re-authenticated page can also land on a workspace-agnostic
108
+ // route (e.g. bare /workspaces right after loginWithTestUser(), which
109
+ // clickCamundaApps() does nothing to fix -- it is a no-op on Hub) whose
110
+ // sidebar renders but has not resolved this cluster's own group yet.
111
+ // Confirmed from a nightly trace: both the getAttribute and click below
112
+ // timed out (10s each) against this exact toggle while the URL sat on
113
+ // /workspaces, right after a mid-test re-login. Route through the same
114
+ // clickModeler() recovery whenever the toggle itself hasn't shown up
115
+ // either, not only when the whole sidebar is missing.
116
+ // Before deciding whether the group is reachable: a collapsed rail hides
117
+ // every cluster trigger, so the probe below would report "not ready" and
118
+ // the recovery would then wait out its own timeout on the same hidden
119
+ // element.
120
+ await this.expandHubSidebar();
121
+ const sidebarReady = (await this.hubSidebar.isVisible({ timeout: 5000 }).catch(() => false)) &&
122
+ (await toggle
123
+ .waitFor({ state: 'visible', timeout: 10000 })
124
+ .then(() => true)
125
+ .catch(() => false));
126
+ if (!sidebarReady) {
127
+ await this.clickModeler();
128
+ await (0, test_1.expect)(this.hubSidebar).toBeVisible({ timeout: 30000 });
129
+ // Since camunda-hub #28956 the Workspaces overview defaults to "My
130
+ // workspaces" -- only the ones the current user is an EXPLICIT member
131
+ // of -- and resets to it on every load. The shared workspace is created
132
+ // by whichever setup user won the race, so a role-flow user lands on
133
+ // "No workspaces match this filter / You don't have explicit membership
134
+ // in any workspace in this organization" (confirmed live) and the
135
+ // sidebar never renders this cluster's group. Widening the scope is
136
+ // what makes it appear; without it the wait below can only time out.
137
+ // ModelerHomePage.showAllWorkspaces() covers the project-folder paths,
138
+ // but nothing covered the app-switcher path until here.
139
+ await this.showAllWorkspaces(5000);
140
+ await this.expandHubSidebar();
141
+ await (0, test_1.expect)(toggle).toBeVisible({ timeout: 30000 });
142
+ }
143
+ // camunda-hub's own nav-shell-ds.spec.saas.tsx documents the actual
144
+ // shape: "the design system splits the group row into a label control
145
+ // and a chevron control, both labelled by the cluster name; either one
146
+ // opens it" -- there is no separate "Toggle <name>" control, and the
147
+ // child links do not exist in the DOM at all until expanded (not merely
148
+ // hidden), nor are they nested under any container matchable by cluster
149
+ // name once expanded. So: find the toggle by the cluster's own name
150
+ // (`.first()` because both controls share that name), click it, then
151
+ // look up the app link sidebar-wide rather than scoped to a "group"
152
+ // container that doesn't reliably exist.
153
+ // Check THIS cluster's own toggle state, not "is any app link with this
154
+ // name visible anywhere in the sidebar" -- with more than one cluster in
155
+ // the org, a DIFFERENT cluster's group can already be expanded (left over
156
+ // from an earlier action on the same page), and that cluster's own
157
+ // Operate/Tasklist/Optimize link would satisfy an unscoped visibility
158
+ // check, skipping expansion of the requested cluster entirely and then
159
+ // clicking the wrong cluster's link. The child links are not nested under
160
+ // any container matchable by cluster name once expanded (confirmed
161
+ // against camunda-hub's own nav-shell-ds.spec.saas.tsx), so the toggle's
162
+ // own aria-expanded is the only reliable per-cluster signal available.
163
+ // Not exact: the same suite asserts this row with a substring/regex
164
+ // match (`{name: /Production/}`), not an exact string -- and nightly
165
+ // traces confirm why: the rendered toggle's accessible name is not
166
+ // byte-for-byte the bare cluster name (it carries additional content,
167
+ // e.g. a trailing tag summary), so an exact-match lookup here finds
168
+ // nothing and the click below times out waiting on a locator that can
169
+ // never resolve.
170
+ const alreadyExpanded = (await toggle.getAttribute('aria-expanded').catch(() => null)) === 'true';
171
+ if (!alreadyExpanded) {
172
+ // This is the only expansion action in the Hub path -- swallowing its
173
+ // failure made a broken expansion look successful and left the caller
174
+ // to burn the full 30s below waiting on an app link that could never
175
+ // appear, hiding the actionable error (e.g. a toggle that doesn't
176
+ // exist or isn't clickable) behind an unrelated timeout.
177
+ await toggle.click({ timeout: 10000 });
178
+ }
179
+ // An app link resolved after this is sidebar-wide (see callers below), so
180
+ // if an EARLIER call in the same test left a different cluster's group
181
+ // expanded too, two "Operate" (say) links now exist and that becomes a
182
+ // strict-mode violation. Collapse every other expanded group before
183
+ // resolving it -- there is no container to scope the link to instead,
184
+ // since the child links aren't nested under anything matchable by
185
+ // cluster name once expanded.
186
+ // hasNotText filters DOM text content, not the accessible name -- the
187
+ // expanded chevron can be labelled via aria-label with no text content
188
+ // of its own, in which case hasNotText would fail to exclude the toggle
189
+ // we just expanded and this loop would immediately collapse it again.
190
+ // Match by accessible name (same algorithm getByRole's own name option
191
+ // uses) instead, excluding any name CONTAINING clusterName -- not just
192
+ // one exactly equal to it, since (per the toggle locator above) the
193
+ // current cluster's own toggle name is not exactly clusterName either,
194
+ // and an exact-equality exclusion would wrongly treat it as "other" and
195
+ // immediately re-collapse the group this method just expanded.
196
+ const otherExpandedToggles = this.hubSidebar.getByRole('button', {
197
+ expanded: true,
198
+ name: new RegExp(`^(?!.*${escapeRegExp(clusterName)}).+$`),
199
+ });
200
+ for (let i = 0; i < 5 && (await otherExpandedToggles.count()) > 0; i++) {
201
+ // Let a failed collapse throw rather than swallowing it: the app link
202
+ // is intentionally sidebar-wide, so a group left expanded here produces
203
+ // a strict-mode violation (or the wrong cluster's link) below anyway --
204
+ // failing at the actual cause is more useful than at that symptom.
205
+ await otherExpandedToggles.first().click({ timeout: 10000 });
206
+ }
207
+ }
208
+ // Widens Hub's Workspaces scope filter to "All workspaces". Duplicated from
209
+ // ModelerHomePage.showAllWorkspaces() rather than imported: ModelerHomePage
210
+ // constructs page objects of its own, and pulling it in here would make the
211
+ // Apps -> Modeler navigation path circular. The control is absent outside
212
+ // Hub and for non-elevated users, where a membership-scoped list is already
213
+ // correct, so a missing control is a no-op.
214
+ // Opens Hub's collapsed left rail. While collapsed it shows only the
215
+ // top-level nav icons: the per-cluster group triggers are in the DOM
216
+ // (data-slot="app-sidebar-group-trigger", aria-label="<cluster>") but never
217
+ // visible, so a toBeVisible() wait on one can only time out. A failing run's
218
+ // trace carries "Expand sidebar" seven times and "Collapse sidebar" not
219
+ // once, i.e. the rail stayed shut for its whole duration. A no-op when the
220
+ // rail is already open, since the toggle is then labelled "Collapse sidebar".
221
+ async expandHubSidebar() {
222
+ const expandToggle = this.page
223
+ .getByRole('button', { name: 'Expand sidebar' })
224
+ .or(this.page
225
+ .locator('[data-slot="app-sidebar-collapse-toggle"]')
226
+ .and(this.page.getByLabel('Expand sidebar')))
227
+ .first();
228
+ if (!(await expandToggle.isVisible().catch(() => false))) {
229
+ return;
230
+ }
231
+ await expandToggle.click({ timeout: 10000 });
232
+ await (0, test_1.expect)(expandToggle).toBeHidden({ timeout: 10000 });
233
+ }
234
+ async showAllWorkspaces(probeTimeout) {
235
+ // The empty state offers an explicit "Switch to All workspaces" button
236
+ // next to "You don't have explicit membership in any workspace in this
237
+ // organization" -- prefer it. Driving the combobox alone was not enough:
238
+ // when its accessible name does not match, the probe below simply
239
+ // returns and the caller waits out its whole timeout on a page that is
240
+ // still scoped to "My workspaces" (observed live, twice).
241
+ const switchButton = this.page.getByRole('button', {
242
+ name: 'Switch to All workspaces',
243
+ });
244
+ const scopeFilter = this.page.getByRole('combobox', {
245
+ name: 'Filter workspaces',
246
+ });
247
+ const appeared = await switchButton
248
+ .or(scopeFilter)
249
+ .first()
250
+ .waitFor({ state: 'visible', timeout: probeTimeout })
251
+ .then(() => true)
252
+ .catch(() => false);
253
+ if (!appeared) {
254
+ return;
255
+ }
256
+ if (await switchButton.isVisible().catch(() => false)) {
257
+ await switchButton.click({ timeout: 10000 });
258
+ await (0, test_1.expect)(switchButton).toBeHidden({ timeout: 10000 });
259
+ return;
260
+ }
261
+ if ((await scopeFilter.innerText()).includes('All workspaces')) {
262
+ return;
263
+ }
264
+ await scopeFilter.click({ timeout: 10000 });
265
+ await this.page
266
+ .getByRole('option', { name: 'All workspaces' })
267
+ .click({ timeout: 10000 });
268
+ await (0, test_1.expect)(scopeFilter).toContainText('All workspaces', {
269
+ timeout: 10000,
270
+ });
271
+ }
88
272
  async clickHubClusterApp(clusterName, appName) {
89
273
  // When the sidebar is collapsed (the default on a fresh session), the
90
274
  // group toggle opens a Radix popover -- a role="dialog" labelled with the
@@ -104,12 +288,12 @@ class AppsPage {
104
288
  // multi-cluster org another cluster's group can already be expanded with
105
289
  // the same app name, and the click would then fire on the wrong
106
290
  // cluster's link.
107
- const collapsedToggle = this.hubSidebar
108
- .getByRole('button', { name: clusterName, expanded: false })
109
- .first();
110
- if (await collapsedToggle.isVisible({ timeout: 5000 }).catch(() => false)) {
111
- await collapsedToggle.click({ timeout: 10000 });
112
- }
291
+ // Expansion, sidebar recovery and collapsing every OTHER expanded group
292
+ // all live in the ported helper: with more than one cluster in the org
293
+ // (this suite creates Test Cluster and AWS Cluster), a sidebar-wide app
294
+ // link can otherwise resolve to a different cluster's row, or become a
295
+ // strict-mode violation once two groups are open.
296
+ await this.expandHubClusterGroup(clusterName);
113
297
  await (0, test_1.expect)(appLink).toBeVisible({ timeout: 30000 });
114
298
  // Hub's cluster-app links carry target="_blank", so clicking them opens
115
299
  // the app in a new tab while every caller keeps asserting on this.page.
@@ -122,6 +306,11 @@ class AppsPage {
122
306
  }
123
307
  await appLink.click({ timeout: 10000 });
124
308
  }
309
+ // True once the tab is on a Camunda Hub host, where Modeler home is Hub's
310
+ // own home rather than a modeler.* hostname.
311
+ isOnHub() {
312
+ return new URL(this.page.url()).hostname.startsWith('hub.');
313
+ }
125
314
  async clickModeler() {
126
315
  // Camunda Hub (hub.*) replaced Console + Web Modeler on SaaS and has no
127
316
  // app switcher. Its home is the Modeler/Workspaces home, but a hostname
@@ -132,22 +321,7 @@ class AppsPage {
132
321
  // Navigate home through the header logo link instead, same as
133
322
  // ModelerHomePage.clickHomeBreadcrumb -- clicking it when already on the
134
323
  // Modeler home is a harmless in-SPA no-op.
135
- if (new URL(this.page.url()).hostname.startsWith('hub.')) {
136
- const header = this.page.locator('header[data-slot="app-header"]');
137
- // The href stays literally "/login" until the client's auth state
138
- // catches up just after login (camunda-hub NavShellDs.common.tsx) --
139
- // clicking it in that window would log the test out instead of
140
- // navigating home, so exclude that href and wait for the authenticated
141
- // one rather than racing it.
142
- const homeLink = header
143
- .getByRole('link', { name: 'Camunda Modeler home' })
144
- .and(this.page.locator(':not([href$="/login"])'))
145
- .or(header
146
- .getByRole('link', { name: 'Camunda Hub home' })
147
- .and(this.page.locator(':not([href$="/login"])')))
148
- .first();
149
- await (0, test_1.expect)(homeLink).toBeVisible({ timeout: 30000 });
150
- await homeLink.click({ timeout: 30000 });
324
+ if (await this.clickHubModelerHome()) {
151
325
  return;
152
326
  }
153
327
  const maxRetries = 5;
@@ -160,6 +334,17 @@ class AppsPage {
160
334
  }
161
335
  for (let retries = 0; retries < maxRetries; retries++) {
162
336
  try {
337
+ // Re-checked every iteration, not once before the loop. A caller can
338
+ // reach here from a cluster app (Operate/Tasklist/Identity), which is
339
+ // NOT a hub.* host, so the pre-loop check above takes the legacy
340
+ // branch -- but the reload in the catch below lands back on Hub, and
341
+ // the loop's own success signal (a modeler.* hostname) can never come
342
+ // true there. A nightly trace shows exactly that: every retry
343
+ // snapshot sits on hub.ultrawombat.com/workspaces, which IS Modeler
344
+ // home, while the loop kept failing until it ran out of attempts.
345
+ if (await this.clickHubModelerHome()) {
346
+ return;
347
+ }
163
348
  await this.clickCamundaApps();
164
349
  await (0, test_1.expect)(this.modelerLink).toBeVisible({ timeout: 30000 });
165
350
  await this.modelerLink.click({ timeout: 30000 });
@@ -194,6 +379,30 @@ class AppsPage {
194
379
  }
195
380
  throw new Error(`Failed to click the modeler link after ${maxRetries} attempts.`);
196
381
  }
382
+ // Navigates to Hub's Modeler home through the header logo link. Returns
383
+ // false when this tab is not on a Hub host, so the caller falls through to
384
+ // the legacy Console app-switcher flow.
385
+ async clickHubModelerHome() {
386
+ if (!this.isOnHub()) {
387
+ return false;
388
+ }
389
+ const header = this.page.locator('header[data-slot="app-header"]');
390
+ // The href stays literally "/login" until the client's auth state
391
+ // catches up just after login (camunda-hub NavShellDs.common.tsx) --
392
+ // clicking it in that window would log the test out instead of
393
+ // navigating home, so exclude that href and wait for the authenticated
394
+ // one rather than racing it.
395
+ const homeLink = header
396
+ .getByRole('link', { name: 'Camunda Modeler home' })
397
+ .and(this.page.locator(':not([href$="/login"])'))
398
+ .or(header
399
+ .getByRole('link', { name: 'Camunda Hub home' })
400
+ .and(this.page.locator(':not([href$="/login"])')))
401
+ .first();
402
+ await (0, test_1.expect)(homeLink).toBeVisible({ timeout: 30000 });
403
+ await homeLink.click({ timeout: 30000 });
404
+ return true;
405
+ }
197
406
  /**
198
407
  * Waits for the Modeler app to actually render, and recovers the one landing
199
408
  * state a hostname check cannot see: Modeler's OAuth `/login-callback`
@@ -22,8 +22,13 @@ declare class ClusterDetailsPage {
22
22
  readonly clientCredentialsDialog: Locator;
23
23
  readonly clustersLink: Locator;
24
24
  readonly alertsTab: Locator;
25
+ readonly alertsPanel: Locator;
25
26
  readonly activeTabPanel: Locator;
26
27
  readonly alertsList: Locator;
28
+ readonly alertExpandRowButtons: Locator;
29
+ readonly alertRowActionsButtons: Locator;
30
+ readonly deleteAlertMenuItem: Locator;
31
+ readonly deleteAlertConfirmButton: Locator;
27
32
  readonly createFirstAlertButton: Locator;
28
33
  readonly createNewAlertButton: Locator;
29
34
  readonly emailRadioButton: Locator;
@@ -37,6 +42,7 @@ declare class ClusterDetailsPage {
37
42
  readonly expandButton: Locator;
38
43
  readonly rbaEnabledMessage: Locator;
39
44
  readonly authorizationsHeading: Locator;
45
+ readonly authorizationsSwitch: Locator;
40
46
  readonly orchestrationClusterCheckbox: Locator;
41
47
  readonly clientCredentialsLink: (clientCredentials: string) => Locator;
42
48
  readonly clientRow: (name: string) => Locator;
@@ -64,6 +70,7 @@ declare class ClusterDetailsPage {
64
70
  clickCloseModalButton(): Promise<void>;
65
71
  clickSettingsTab(): Promise<void>;
66
72
  private waitForAuthorizationsSection;
73
+ private setAuthorizationsViaSwitch;
67
74
  enableAuthorizations(): Promise<void>;
68
75
  disableAuthorizations(): Promise<void>;
69
76
  searchAndClickClientCredentialsLink(name: string): Promise<ClientCredentialsDetailsPage>;
@@ -76,6 +83,8 @@ declare class ClusterDetailsPage {
76
83
  selectEmailRadioButton(): Promise<void>;
77
84
  clickCreateAlertButton(): Promise<void>;
78
85
  deleteAlerts(): Promise<void>;
86
+ ensureAlertsTabSelected(): Promise<void>;
87
+ expandAlerts(): Promise<void>;
79
88
  private confirmDelete;
80
89
  private dismissDeleteDialog;
81
90
  private doDelete;