@camunda/e2e-test-suite 0.0.1389 → 0.0.1390

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.
@@ -132,7 +132,12 @@ class ClusterPage {
132
132
  // deleteCluster needs it -- while everything that means "the one cluster
133
133
  // under test" goes through this single row, so a duplicate can no longer
134
134
  // turn an ordinary visibility check into a strict-mode violation.
135
- this.clusterRow = (clusterName) => this.cluster(clusterName).first();
135
+ // createCluster's pre-clean leaves same-named rows in "Deleting" for
136
+ // minutes, and they can sort above the new cluster; that row never shows
137
+ // Creating/Healthy, so it must not be picked as the cluster under test.
138
+ this.clusterRow = (clusterName) => this.cluster(clusterName)
139
+ .filter({ hasNotText: /Deleting/ })
140
+ .first();
136
141
  this.clusterHealthiness = (clusterName) => this.clusterRow(clusterName).getByText(`Healthy`, {
137
142
  exact: true,
138
143
  });
@@ -203,35 +208,104 @@ class ClusterPage {
203
208
  }
204
209
  }
205
210
  async deleteCluster(name) {
206
- await this.page.waitForTimeout(1000);
207
- const clusterInstance = this.cluster(name).first();
208
- if ((await clusterInstance.count()) <= 0) {
211
+ // A full reload in the Hub shell bounces through /login, the identity
212
+ // provider and /login-callback (~35s) before the SPA pushes its returnUrl
213
+ // back to /clusters. Counting rows mid-bounce throws "Execution context
214
+ // was destroyed", so wait for the clusters page to have settled first.
215
+ await this.page.waitForURL((url) => /\/clusters\/?$/.test(url.pathname), {
216
+ timeout: 120000,
217
+ });
218
+ await (0, test_1.expect)(this.createNewClusterButton).toBeVisible({ timeout: 60000 });
219
+ // Hub's clusters data table renders skeleton rows (no links) while it
220
+ // fetches, and the "Create new cluster" button is visible before the data
221
+ // arrives. Counting matching rows at that point always returned 0, so the
222
+ // pre-clean was skipped and every run left one more same-named cluster
223
+ // behind until the org ran out of G3-Dev reservations (the type tile then
224
+ // renders disabled). Wait for the first loaded row before counting.
225
+ await this.clustersList
226
+ .getByRole('link')
227
+ .first()
228
+ .waitFor({ timeout: 30000 })
229
+ .catch(() => false);
230
+ // Rows whose deletion is already in progress keep the name until the
231
+ // backend removes them; exclude them so the loop does not revisit them.
232
+ const deletableRows = () => this.clustersList
233
+ .filter({
234
+ has: this.page.getByRole('link', { name, exact: true }),
235
+ })
236
+ .filter({ hasNotText: /Deleting/ });
237
+ if ((await deletableRows().count()) <= 0) {
209
238
  console.log(`No clusters found with name ${name}, skipping deletion.`);
210
239
  return;
211
240
  }
212
- await clusterInstance.waitFor({ timeout: 10000 });
213
- let deleteButtons = this.cluster(name).getByRole('button', {
214
- name: 'Delete',
215
- });
216
- while ((await deleteButtons.count()) > 0) {
217
- const deleteButton = deleteButtons.nth(0);
218
- if (await deleteButton.isVisible()) {
219
- try {
220
- await deleteButton.click({ force: true, timeout: 60000 });
221
- }
222
- catch (error) {
223
- await this.clickClusterBanner();
224
- await (0, test_1.expect)(deleteButton).toBeVisible({ timeout: 60000 });
225
- await deleteButton.click({ force: true, timeout: 60000 });
226
- }
227
- await this.confirmClusterDeletion();
228
- // Refresh the list of delete buttons
229
- deleteButtons = this.cluster(name).getByRole('button', {
230
- name: 'Delete',
241
+ // Legacy Console rendered a per-row "Delete" button; Hub's design-system
242
+ // data table instead puts it in the row's "Row actions" kebab menu.
243
+ const deleteMenuItem = this.page.getByRole('menuitem', { name: /^Delete/ });
244
+ const confirmInput = this.confirmDeleteInput.or(this.dialog.getByRole('textbox'));
245
+ const maxDeletions = 50;
246
+ for (let i = 0; i < maxDeletions; i++) {
247
+ if ((await deletableRows().count()) <= 0)
248
+ break;
249
+ const row = deletableRows().first();
250
+ const rowAction = row
251
+ .getByRole('button', { name: 'Delete' })
252
+ .or(row.getByRole('button', { name: 'Row actions', exact: true }));
253
+ try {
254
+ await (0, test_1.expect)(rowAction).toBeVisible({ timeout: 10000 });
255
+ }
256
+ catch {
257
+ // A cluster in a transient state (e.g. just created by a concurrent
258
+ // setup worker) may have its row present before the action renders.
259
+ await this.page.reload({ waitUntil: 'domcontentloaded' });
260
+ await this.clustersList.getByRole('link').first().waitFor({
261
+ timeout: 30000,
262
+ });
263
+ continue;
264
+ }
265
+ // Rows share the cluster name, so remember this row's unique link (it
266
+ // carries the cluster id) to recognise when this cluster is gone.
267
+ const clusterHref = await row
268
+ .getByRole('link', { name, exact: true })
269
+ .getAttribute('href');
270
+ await rowAction.click({ timeout: 60000 });
271
+ // isVisible() does not wait, so a kebab menu that opens a moment after
272
+ // the click was missed and the confirm dialog never appeared. Wait for
273
+ // either the menu item (Hub) or the dialog (legacy per-row button).
274
+ await (0, test_1.expect)(deleteMenuItem.or(this.dialog)).toBeVisible({
275
+ timeout: 30000,
276
+ });
277
+ if (await deleteMenuItem.isVisible()) {
278
+ await deleteMenuItem.click({ timeout: 30000 });
279
+ }
280
+ await (0, test_1.expect)(this.dialog).toBeVisible({ timeout: 60000 });
281
+ await (0, test_1.expect)(confirmInput.first()).toBeVisible({ timeout: 60000 });
282
+ await confirmInput.first().fill('DELETE', { timeout: 60000 });
283
+ await this.dangerDeleteButton.last().click({ timeout: 60000 });
284
+ // When a concurrent worker already deleted this same cluster, the modal
285
+ // stays open with "We're having trouble deleting the cluster". Close it;
286
+ // the row check below still fails if the cluster was not removed.
287
+ const deleteError = this.dialog.getByText("We're having trouble deleting the cluster");
288
+ // The alert only renders once the delete request fails, so wait for the
289
+ // modal to either close or show it rather than probing once.
290
+ await (0, test_1.expect)(async () => {
291
+ const closed = !(await this.dialog.isVisible());
292
+ (0, test_1.expect)(closed || (await deleteError.isVisible())).toBe(true);
293
+ }).toPass({ timeout: 60000 });
294
+ if (await deleteError.isVisible()) {
295
+ await this.dialog.getByRole('button', { name: 'Cancel' }).click();
296
+ await this.page.reload({ waitUntil: 'domcontentloaded' });
297
+ await this.clustersList.getByRole('link').first().waitFor({
298
+ timeout: 120000,
231
299
  });
232
300
  }
301
+ await (0, test_1.expect)(this.dialog).not.toBeVisible({ timeout: 60000 });
302
+ // Wait until the deleted row is gone or flagged as deleting. The table
303
+ // is paginated, so the row count alone does not drop: the next page's
304
+ // rows slide in to fill the gap.
305
+ await (0, test_1.expect)(this.clustersList
306
+ .filter({ has: this.page.locator(`a[href="${clusterHref}"]`) })
307
+ .filter({ hasNotText: /Deleting/ })).toHaveCount(0, { timeout: 60000 });
233
308
  }
234
- await this.page.waitForTimeout(500);
235
309
  }
236
310
  async clickClusterTab() {
237
311
  await Promise.race([
@@ -27,7 +27,14 @@ class TaskPanelPage {
27
27
  // was plainly listed. The `<section aria-label="Tasks side panel">` that
28
28
  // wraps the panel is present on both shells -- it is what the product's own
29
29
  // qa/c8-orchestration-cluster-e2e-test-suite and pages/SM-8.10 now anchor on.
30
- this.availableTasks = page.getByRole('region', { name: 'Tasks side panel' });
30
+ // Both are matched because the rollout is per app and not yet everywhere:
31
+ // a run still served the pre-#65074 shell (an older SaaS cluster, or a
32
+ // version that has not flipped) only exposes the title wrapper. `.first()`
33
+ // keeps the locator strict-mode safe on a shell that renders both.
34
+ this.availableTasks = page
35
+ .getByRole('region', { name: 'Tasks side panel' })
36
+ .or(page.getByTitle('Available tasks'))
37
+ .first();
31
38
  // 8.10 still serves Tasklist on the Carbon shell, so filtering lives in
32
39
  // the collapsible filter panel: the collapsed rail carries the expand
33
40
  // control (aria-controls="task-nav-bar") and the expanded nav lists each
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.1389",
3
+ "version": "0.0.1390",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",