@camunda/e2e-test-suite 0.0.1132 → 0.0.1133

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.
@@ -1316,7 +1316,14 @@ class ModelerCreatePage {
1316
1316
  }
1317
1317
  async switchToPlay() {
1318
1318
  await this.suppressTestModeAnnouncement();
1319
- await this.playTab.click();
1319
+ // completePlayConfiguration re-enters Play after ensureStageCluster's
1320
+ // detour, which comes back through goto(..., 'domcontentloaded'). The
1321
+ // modeler mounts its mode tablist well after that event -- measurably
1322
+ // longer than the 10s default actionTimeout this click would otherwise
1323
+ // inherit, which failed the re-entry outright on every run of the
1324
+ // dispatched test. Wait for the tab to exist before clicking it.
1325
+ await (0, test_1.expect)(this.playTab).toBeVisible({ timeout: 90000 });
1326
+ await this.playTab.click({ timeout: 30000 });
1320
1327
  }
1321
1328
  async completePlayConfiguration(clusterName) {
1322
1329
  // Play is a tab on the diagram page, not its own URL, so the stage-cluster
@@ -1421,15 +1428,34 @@ class ModelerCreatePage {
1421
1428
  // keying on it would fall through to the legacy Continue flow.
1422
1429
  const isNewFlow = await configurationOverlay.isVisible();
1423
1430
  if (isNewFlow) {
1424
- // Step 1: connect cluster if not yet connected (modal with cluster dropdown)
1431
+ // Step 1: connect cluster if not yet connected (modal with cluster
1432
+ // dropdown). camunda-hub's ClusterStepContent renders "Connect cluster"
1433
+ // only while NO cluster is bound; once one is, the very same modal is
1434
+ // reached through "Change", and an unusable cluster shows the
1435
+ // "Cluster unavailable. Choose a healthy cluster." error instead. That
1436
+ // second state is not a slow state: DeployStepContent hardcodes
1437
+ // `disabled={!isClusterStepComplete}`, so an unhealthy cluster keeps the
1438
+ // Deploy button disabled forever -- which is exactly how the dispatched
1439
+ // nightly failed, burning the whole 60s toBeEnabled budget. Treat it as
1440
+ // "step 1 not done" and drive the same modal through "Change".
1425
1441
  const connectClusterButton = this.page.getByRole('button', {
1426
1442
  name: 'Connect cluster',
1427
1443
  });
1428
- const needsConnect = await connectClusterButton
1429
- .isVisible({ timeout: 3000 })
1444
+ const changeClusterButton = this.page
1445
+ .getByRole('button', { name: 'Change', exact: true })
1446
+ .first();
1447
+ const clusterHealthyIcon = this.page.locator('[aria-label="Cluster healthy"]');
1448
+ const clusterUnavailableIcon = this.page.locator('[aria-label="Cluster unavailable"]');
1449
+ const hasConnectButton = await connectClusterButton
1450
+ .isVisible()
1430
1451
  .catch(() => false);
1452
+ const needsConnect = hasConnectButton ||
1453
+ (await clusterUnavailableIcon.isVisible().catch(() => false));
1431
1454
  if (needsConnect) {
1432
- await connectClusterButton.click({ timeout });
1455
+ const openClusterModalButton = hasConnectButton
1456
+ ? connectClusterButton
1457
+ : changeClusterButton;
1458
+ await openClusterModalButton.click({ timeout });
1433
1459
  const configureEnvDialog = this.page
1434
1460
  .getByRole('dialog')
1435
1461
  .filter({ hasText: 'Configure environment' });
@@ -1458,6 +1484,15 @@ class ModelerCreatePage {
1458
1484
  .getByRole('button', { name: 'Save' })
1459
1485
  .click({ timeout });
1460
1486
  }
1487
+ // Gate the deploy step on the cluster step actually having completed.
1488
+ // ClusterStepIcon renders this label only for
1489
+ // `!isVerifyingClusterAvailability && hasCluster && isClusterHealthy` --
1490
+ // the exact condition DeployStepContent gates its Deploy button on --
1491
+ // and it lives in the accordion *heading*, so it stays readable while
1492
+ // the item collapses. Without this the flow raced ahead into step 2 and
1493
+ // reported "Deploy button never enabled", which says nothing about the
1494
+ // cluster being the thing that was wrong.
1495
+ await (0, test_1.expect)(clusterHealthyIcon).toBeVisible({ timeout: 90000 });
1461
1496
  // Step 2: deploy, unless the overlay already opened with the
1462
1497
  // deploy step complete. checkExistingDeployment only runs when
1463
1498
  // processApplicationId is defined, which it now is, so re-entering
@@ -1493,12 +1528,23 @@ class ModelerCreatePage {
1493
1528
  // which expands itself once the cluster step completes. Nudge the
1494
1529
  // heading if it is still collapsed, otherwise the button is present
1495
1530
  // but hidden and the click below would time out.
1531
+ //
1532
+ // This probe HAS to wait. `locator.isVisible({timeout})` never does --
1533
+ // it answers for the DOM as it is right now and ignores the timeout --
1534
+ // so it reported "not shown" in the gap before the item auto-expanded,
1535
+ // and the nudge below then hit an item that was already open. That
1536
+ // click is not idempotent: TestConfigurationPanel's
1537
+ // handleDeployHeadingClick sets manualSetupStep to 'none' when the
1538
+ // item was open, collapsing BOTH setup steps, and the retry loop that
1539
+ // follows only ever clicked a *visible* button -- so the deploy was
1540
+ // never triggered and the loop timed out with the step collapsed.
1496
1541
  const deployButtonShown = await setupDeployButton
1497
- .isVisible({ timeout: 30000 })
1542
+ .waitFor({ state: 'visible', timeout: 30000 })
1543
+ .then(() => true)
1498
1544
  .catch(() => false);
1499
1545
  if (!deployButtonShown) {
1500
1546
  const canExpandDeployStep = await deployStepHeading
1501
- .isEnabled({ timeout: 5000 })
1547
+ .isEnabled()
1502
1548
  .catch(() => false);
1503
1549
  if (canExpandDeployStep) {
1504
1550
  await deployStepHeading.click({ timeout });
@@ -1520,9 +1566,21 @@ class ModelerCreatePage {
1520
1566
  if (await isDeployComplete()) {
1521
1567
  return;
1522
1568
  }
1523
- const canClick = (await setupDeployButton.isVisible().catch(() => false)) &&
1524
- (await setupDeployButton.isEnabled().catch(() => false));
1525
- if (canClick) {
1569
+ const isButtonShown = await setupDeployButton
1570
+ .isVisible()
1571
+ .catch(() => false);
1572
+ if (!isButtonShown) {
1573
+ // Collapsed, not gone: re-open the step instead of waiting the
1574
+ // budget out on a button that can never become visible on its own
1575
+ // once manualSetupStep is 'none'.
1576
+ const canExpand = await deployStepHeading
1577
+ .isEnabled()
1578
+ .catch(() => false);
1579
+ if (canExpand) {
1580
+ await deployStepHeading.click({ timeout });
1581
+ }
1582
+ }
1583
+ else if (await setupDeployButton.isEnabled().catch(() => false)) {
1526
1584
  await setupDeployButton.click({ timeout });
1527
1585
  }
1528
1586
  (0, test_1.expect)(await isDeployComplete()).toBe(true);
@@ -145,7 +145,6 @@ class ProcessApplicationPage {
145
145
  // taken -- callers that care assert the name in the deploy dialog anyway.
146
146
  async defineDevelopmentStage(clusterName) {
147
147
  await (0, test_1.expect)(this.defineStagesDialog).toBeVisible({ timeout: 60000 });
148
- await this.developmentClusterSelect.click({ timeout: 30000 });
149
148
  // Escape before interpolating: a cluster name can carry regex
150
149
  // metacharacters -- "Test Cluster (v2)" is the common case -- which would
151
150
  // otherwise mis-match or throw. Same treatment as
@@ -159,9 +158,52 @@ class ProcessApplicationPage {
159
158
  .getByRole('option')
160
159
  .filter({ hasNotText: 'Select a development cluster' })
161
160
  .first();
162
- await option.click({ timeout: 30000 });
161
+ // The dropdown is fully controlled and DefineStagesModal resets its local
162
+ // state from processApplication.default*ClusterId in an effect. The modal
163
+ // itself triggers a cluster/application refresh when it opens, so a
164
+ // selection made before that refresh lands is silently reverted to
165
+ // whatever the application already pointed at -- and Save then persists
166
+ // THAT cluster. A failing nightly trace shows exactly this: the option
167
+ // click committed "Test Cluster", and ~0.5s later, with the Save click
168
+ // already in flight, the field had snapped back to "First RBA V2 Cluster".
169
+ // The application bound the wrong cluster, and Play's "Choose where to
170
+ // run" step then reported "Cluster unavailable" for it, which leaves the
171
+ // Deploy button hardcoded-disabled and fails the test 60s later, far from
172
+ // the cause. Re-select until the field still reads the requested cluster.
173
+ const selectedCluster = this.developmentClusterSelect.getByRole('combobox');
174
+ const isRequestedCluster = async () => {
175
+ const label = (await selectedCluster.textContent()) ?? '';
176
+ return clusterName
177
+ ? new RegExp(`^${escapedClusterName}`).test(label)
178
+ : !label.startsWith('[Select a');
179
+ };
180
+ await (0, test_1.expect)(async () => {
181
+ if (!(await isRequestedCluster())) {
182
+ await this.developmentClusterSelect.click({ timeout: 30000 });
183
+ await option.click({ timeout: 30000 });
184
+ }
185
+ (0, test_1.expect)(await isRequestedCluster()).toBe(true);
186
+ }).toPass({ timeout: 90000, intervals: [1000, 2000, 5000] });
163
187
  await this.defineStagesSaveButton.click({ timeout: 30000 });
164
188
  await (0, test_1.expect)(this.defineStagesDialog).toBeHidden({ timeout: 60000 });
189
+ // Verify what was actually persisted, not what was typed into the modal:
190
+ // the revert above can also land between the last check and the Save
191
+ // click. The Connected clusters card renders one row per bound stage with
192
+ // the cluster's full "<name> <generation>" as its title, so it is the
193
+ // cheapest read of the saved binding. Failing here re-runs the whole bind
194
+ // through ensureStageCluster's retry, which re-navigates first.
195
+ if (clusterName) {
196
+ const developmentRow = this.page
197
+ .getByRole('listitem')
198
+ .filter({
199
+ has: this.page.getByRole('button', {
200
+ name: 'Development',
201
+ exact: true,
202
+ }),
203
+ })
204
+ .first();
205
+ await (0, test_1.expect)(developmentRow.getByTitle(new RegExp(`^${escapedClusterName}( |$)`))).toBeVisible({ timeout: 60000 });
206
+ }
165
207
  }
166
208
  rememberResourceContainer(url) {
167
209
  resourceContainers.set(this.page, { url });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.1132",
3
+ "version": "0.0.1133",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",