@camunda/e2e-test-suite 0.0.1425 → 0.0.1426

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.
@@ -20,6 +20,7 @@ declare class ClusterPage {
20
20
  readonly dangerDeleteButton: Locator;
21
21
  readonly testClusterLink: Locator;
22
22
  readonly connectorSecretsTab: Locator;
23
+ readonly connectorSecretsTabSelected: Locator;
23
24
  readonly createAClusterButton: Locator;
24
25
  readonly clusterVersionOption: Locator;
25
26
  readonly generationSearchInput: Locator;
@@ -25,6 +25,7 @@ class ClusterPage {
25
25
  dangerDeleteButton;
26
26
  testClusterLink;
27
27
  connectorSecretsTab;
28
+ connectorSecretsTabSelected;
28
29
  createAClusterButton;
29
30
  clusterVersionOption;
30
31
  generationSearchInput;
@@ -80,6 +81,13 @@ class ClusterPage {
80
81
  .or(page.getByRole('tab', {
81
82
  name: 'Connector Secrets',
82
83
  }));
84
+ // Same tab, but only when it is the active one. Hub renders the cluster
85
+ // tabs as Radix triggers whose active tab is controlled React state, so a
86
+ // visually successful click is not proof the panel switched -- see
87
+ // clickConnectorSecretsTab.
88
+ this.connectorSecretsTabSelected = page
89
+ .getByRole('tab', { name: 'Cluster secrets', selected: true })
90
+ .or(page.getByRole('tab', { name: 'Connector Secrets', selected: true }));
83
91
  // Camunda Hub's create-cluster form replaced the Carbon tiles (a
84
92
  // <label> per option) and tabs with design-system choice cards and toggle
85
93
  // groups, all exposed as role="radio" named by their title. Each locator
@@ -441,12 +449,41 @@ class ClusterPage {
441
449
  });
442
450
  }
443
451
  async clickConnectorSecretsTab(clusterName) {
444
- await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.connectorSecretsTab, {
445
- postAction: async () => {
446
- await this.clickClusterBanner();
447
- await this.clickClusterLink(clusterName);
448
- },
452
+ // Hub's cluster tabs are Radix triggers whose active tab is controlled
453
+ // React state. A background refetch landing right after the click can
454
+ // reset that state back to the default Overview tab, so a single
455
+ // successful click sometimes leaves the panel on Overview and the secrets
456
+ // Import button never mounts (AlwaysGreen run 38259225730: the tab was
457
+ // clicked on a visible/enabled/stable element yet Overview stayed
458
+ // selected for the full 30s Import-button wait). Click, confirm the
459
+ // secrets tab actually became selected, and re-click until it does.
460
+ const maxAttempts = 3;
461
+ const importButton = this.page.getByRole('button', {
462
+ name: 'Import',
463
+ exact: true,
464
+ });
465
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
466
+ await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.connectorSecretsTab, {
467
+ postAction: async () => {
468
+ await this.clickClusterBanner();
469
+ await this.clickClusterLink(clusterName);
470
+ },
471
+ });
472
+ const activated = await importButton
473
+ .waitFor({ state: 'visible', timeout: 10000 })
474
+ .then(() => this.connectorSecretsTabSelected.isVisible())
475
+ .catch(() => false);
476
+ if (activated) {
477
+ return;
478
+ }
479
+ }
480
+ // Still on Overview after re-clicking: fail loudly rather than proceeding
481
+ // to a secrets panel that never mounted -- this keeps a genuinely broken
482
+ // panel red instead of masking it.
483
+ await (0, test_1.expect)(this.connectorSecretsTabSelected).toBeVisible({
484
+ timeout: 10000,
449
485
  });
486
+ await (0, test_1.expect)(importButton).toBeVisible({ timeout: 10000 });
450
487
  }
451
488
  async clickLatestStableForMinorVersion() {
452
489
  const minorVersion = process.env.MINOR_VERSION;
@@ -19,6 +19,7 @@ declare class ClusterSecretsPage {
19
19
  readonly clusterBanner: Locator;
20
20
  readonly clusterLink: (name: string) => Locator;
21
21
  readonly clusterSecretTab: Locator;
22
+ readonly secretsFetchErrorReloadButton: Locator;
22
23
  readonly confirmDeleteInput: Locator;
23
24
  constructor(page: Page);
24
25
  deleteConnectorSecretsIfExist(): Promise<void>;
@@ -44,5 +45,6 @@ declare class ClusterSecretsPage {
44
45
  name: string;
45
46
  value: string;
46
47
  }[]): Promise<void>;
48
+ private restoreClusterPageIfSignedOut;
47
49
  }
48
50
  export { ClusterSecretsPage };
@@ -4,6 +4,7 @@ exports.ClusterSecretsPage = void 0;
4
4
  const test_1 = require("@playwright/test");
5
5
  const sleep_1 = require("../../utils/sleep");
6
6
  const expectLocatorWithRetry_1 = require("../../utils/assertionHelpers/expectLocatorWithRetry");
7
+ const sessionRecovery_1 = require("../../utils/sessionRecovery");
7
8
  class ClusterSecretsPage {
8
9
  page;
9
10
  createNewSecretButton;
@@ -24,6 +25,7 @@ class ClusterSecretsPage {
24
25
  clusterBanner;
25
26
  clusterLink;
26
27
  clusterSecretTab;
28
+ secretsFetchErrorReloadButton;
27
29
  confirmDeleteInput;
28
30
  constructor(page) {
29
31
  this.page = page;
@@ -112,6 +114,11 @@ class ClusterSecretsPage {
112
114
  this.clusterSecretTab = this.page
113
115
  .getByRole('tab', { name: 'Cluster secrets' })
114
116
  .or(this.page.getByRole('tab', { name: 'Connector Secrets' }));
117
+ // c4's secrets tab replaces the list with an "Oops ... something went
118
+ // wrong." card when its GET of the secrets fails; this button refetches.
119
+ this.secretsFetchErrorReloadButton = page
120
+ .getByRole('tabpanel', { name: /^(Cluster secrets|Connector Secrets)$/ })
121
+ .getByRole('button', { name: 'Reload', exact: true });
115
122
  this.confirmDeleteInput = page.getByRole('textbox', {
116
123
  name: 'Type the word DELETE to confirm',
117
124
  });
@@ -390,6 +397,7 @@ class ClusterSecretsPage {
390
397
  await (0, test_1.expect)(this.createNewSecretButton).toBeVisible({
391
398
  timeout: 60000,
392
399
  });
400
+ const clusterUrl = this.page.url();
393
401
  await this.page.reload();
394
402
  // Hub keeps the selected cluster tab out of the URL, so the reload lands
395
403
  // on Overview, where assertSecretsOrCreate finds no search box and tries
@@ -397,10 +405,32 @@ class ClusterSecretsPage {
397
405
  // The cluster page can still be hydrating after the reload and snap back
398
406
  // to Overview after the click lands (CI trace: click succeeded, the
399
407
  // screenshot still showed Overview), so re-click until the panel renders.
408
+ // Re-clicking the already selected tab does not refetch a failed secrets
409
+ // list, so use the error card's own Reload button for that case.
400
410
  await (0, test_1.expect)(async () => {
401
- await this.clusterSecretTab.first().click({ timeout: 15000 });
411
+ await this.restoreClusterPageIfSignedOut(clusterUrl);
412
+ if (await this.secretsFetchErrorReloadButton.isVisible()) {
413
+ await this.secretsFetchErrorReloadButton.click({ timeout: 15000 });
414
+ }
415
+ else {
416
+ await this.clusterSecretTab.first().click({ timeout: 15000 });
417
+ }
402
418
  await (0, test_1.expect)(this.createNewSecretButton).toBeVisible({ timeout: 10000 });
403
- }).toPass({ timeout: 90000 });
419
+ }).toPass({ timeout: 180000 });
420
+ }
421
+ // A reload is a full Hub navigation through Auth0 and /login-callback. When
422
+ // Hub's session hand-off fails it calls Auth0 logout, which parks the tab on
423
+ // the identity provider's login form (PR run 38354316061). Sign back in as
424
+ // the same user, then open the cluster with a fresh goto: the callback code
425
+ // is single use, so that URL cannot be reloaded.
426
+ async restoreClusterPageIfSignedOut(clusterUrl) {
427
+ if (!(0, sessionRecovery_1.isAtIdentityProvider)(this.page) ||
428
+ !(await (0, sessionRecovery_1.restoreSessionIfLoggedOut)(this.page))) {
429
+ return;
430
+ }
431
+ await this.page.goto(clusterUrl, { waitUntil: 'domcontentloaded' });
432
+ await this.page.waitForURL((url) => url.host === new URL(clusterUrl).host &&
433
+ !/^\/login(-callback)?\/?$/.test(url.pathname), { timeout: 120000 });
404
434
  }
405
435
  }
406
436
  exports.ClusterSecretsPage = ClusterSecretsPage;
@@ -10,7 +10,7 @@ const resetSession_1 = require("../../utils/resetSession");
10
10
  const KeycloakUtils_1 = require("../../pages/SM-8.10/KeycloakUtils");
11
11
  const constants_1 = require("../../utils/constants");
12
12
  SM_8_10_1.test.describe.parallel('Smoke Tests', () => {
13
- SM_8_10_1.test.beforeEach(async ({ navigationPage, managementIdentityPage, managementIdentityTenantPage, keycloakLoginPage, keycloakAdminPage, page, browser, loginPage, ocIdentityHomePage, ocIdentityRolesPage, }, testInfo) => {
13
+ SM_8_10_1.test.beforeEach(async ({ navigationPage, managementIdentityPage, managementIdentityTenantPage, keycloakLoginPage, keycloakAdminPage, page, browser, ocIdentityHomePage, ocIdentityRolesPage, }, testInfo) => {
14
14
  if (process.env.IS_MIGRATION === 'true') {
15
15
  await navigationPage.goToOCAdmin((testInfo.workerIndex + 1) * 1000);
16
16
  }
@@ -20,7 +20,14 @@ SM_8_10_1.test.describe.parallel('Smoke Tests', () => {
20
20
  const { testUsername, testPassword } = await (0, KeycloakUtils_1.setupKeycloakUser)(navigationPage, managementIdentityPage, keycloakLoginPage, keycloakAdminPage, ocIdentityHomePage, ocIdentityRolesPage, process.env.IS_MT === 'true', managementIdentityTenantPage);
21
21
  console.log(`BeforeEach end: timeout = ${testInfo.timeout}`);
22
22
  await (0, resetSession_1.resetSession)(browser, page);
23
- await loginPage.login(testUsername, testPassword);
23
+ // Not loginPage.login() on the page resetSession leaves us on: where
24
+ // "/" serves Console it never redirects to Keycloak, so login()
25
+ // returned false unnoticed and later goTo() calls fell back to the
26
+ // first user (an org admin) instead of this test user.
27
+ await navigationPage.goToManagementIdentity(undefined, {
28
+ username: testUsername,
29
+ password: testPassword,
30
+ });
24
31
  }
25
32
  });
26
33
  SM_8_10_1.test.afterEach(async ({ page }, testInfo) => {
@@ -64,7 +71,7 @@ SM_8_10_1.test.describe.parallel('Smoke Tests', () => {
64
71
  }
65
72
  console.log(`[${testInfo.title}] Test end: timeout = ${testInfo.timeout}`);
66
73
  });
67
- (0, SM_8_10_1.test)('Most Common Flow User Flow With All Apps', async ({ page, modelerHomePage, navigationPage, modelerCreatePage, operateHomePage, operateProcessesPage, operateProcessInstancePage, optimizeHomePage, optimizeCollectionsPage, optimizeReportPage, taskDetailsPage, taskPanelPage, }, testInfo) => {
74
+ (0, SM_8_10_1.test)('Most Common Flow User Flow With All Apps', async ({ page, browser, modelerHomePage, navigationPage, modelerCreatePage, operateHomePage, operateProcessesPage, operateProcessInstancePage, optimizeHomePage, optimizeCollectionsPage, optimizeReportPage, taskDetailsPage, taskPanelPage, }, testInfo) => {
68
75
  console.log(`[${testInfo.title}] Test start: timeout = ${testInfo.timeout}`);
69
76
  const randomName = await (0, _setup_1.generateRandomStringAsync)(3);
70
77
  const processName = 'Zeebe_User_Task_Process' + randomName;
@@ -72,6 +79,7 @@ SM_8_10_1.test.describe.parallel('Smoke Tests', () => {
72
79
  await SM_8_10_1.test.step('Open Cross Component Test Project and Create a BPMN Diagram Template', async () => {
73
80
  await navigationPage.goToModeler();
74
81
  await modelerHomePage.createCrossComponentProjectFolder();
82
+ await modelerHomePage.assignWorkspaceEnvironmentAsOrgAdmin(browser);
75
83
  await modelerHomePage.clickDiagramTypeDropdown();
76
84
  await modelerHomePage.clickBpmnTemplateOption();
77
85
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.1425",
3
+ "version": "0.0.1426",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",