@camunda/e2e-test-suite 0.0.641 → 0.0.643

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.
@@ -6,6 +6,7 @@ declare class ConnectorMarketplacePage {
6
6
  readonly replaceResourceButton: Locator;
7
7
  readonly cancelButton: Locator;
8
8
  readonly marketplaceErrorMessage: Locator;
9
+ readonly noResultsMessage: Locator;
9
10
  constructor(page: Page);
10
11
  clickSearchForConnectorTextbox(): Promise<void>;
11
12
  fillSearchForConnectorTextbox(connectorName: string): Promise<void>;
@@ -10,6 +10,7 @@ class ConnectorMarketplacePage {
10
10
  replaceResourceButton;
11
11
  cancelButton;
12
12
  marketplaceErrorMessage;
13
+ noResultsMessage;
13
14
  constructor(page) {
14
15
  this.page = page;
15
16
  this.searchForConnectorTextbox = page.getByPlaceholder('Search for a connector');
@@ -21,6 +22,7 @@ class ConnectorMarketplacePage {
21
22
  name: 'Replace resource',
22
23
  });
23
24
  this.marketplaceErrorMessage = page.getByText('an error occurred, please try again later');
25
+ this.noResultsMessage = page.getByText('find a match for your search phrase');
24
26
  }
25
27
  async clickSearchForConnectorTextbox() {
26
28
  await this.searchForConnectorTextbox.click({ timeout: 60000 });
@@ -42,11 +44,13 @@ class ConnectorMarketplacePage {
42
44
  async waitForConnectorSearchResults() {
43
45
  const MAX_RETRIES = 3;
44
46
  for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
45
- await (0, test_1.expect)(this.downloadToProjectButton.or(this.marketplaceErrorMessage)).toBeVisible({ timeout: 60000 });
47
+ await (0, test_1.expect)(this.downloadToProjectButton
48
+ .or(this.marketplaceErrorMessage)
49
+ .or(this.noResultsMessage)).toBeVisible({ timeout: 60000 });
46
50
  if (await this.downloadToProjectButton.isVisible()) {
47
51
  return;
48
52
  }
49
- console.log(`Marketplace search attempt ${attempt} of ${MAX_RETRIES} returned an error. Retrying search...`);
53
+ console.log(`Marketplace search attempt ${attempt} of ${MAX_RETRIES} returned an error or no results. Retrying search...`);
50
54
  if (attempt < MAX_RETRIES) {
51
55
  const searchText = await this.searchForConnectorTextbox.inputValue();
52
56
  await this.searchForConnectorTextbox.clear();
@@ -55,7 +59,7 @@ class ConnectorMarketplacePage {
55
59
  await (0, sleep_1.sleep)(2000);
56
60
  }
57
61
  }
58
- throw new Error('Marketplace connector search failed after retries due to persistent error');
62
+ throw new Error('Marketplace connector search failed after retries due to persistent error or no matching results');
59
63
  }
60
64
  async downloadConnectorToProject() {
61
65
  await this.clickDownloadToProjectButton();
@@ -710,6 +710,57 @@ class ModelerCreatePage {
710
710
  }
711
711
  async completePlayConfiguration() {
712
712
  const timeout = 30000;
713
+ // New flow (8.10+): "Setup environment" panel with three steps:
714
+ // 1. Connect cluster → "Configure environment" modal → select cluster → Save
715
+ // 2. Deploy process → wait for success banner
716
+ // 3. Configure scenario
717
+ const setupDeployButton = this.page
718
+ .getByText('Deploy process')
719
+ .locator('..')
720
+ .getByRole('button', { name: 'Deploy' });
721
+ const configureScenarioButton = this.page.getByRole('button', {
722
+ name: 'Configure scenario',
723
+ });
724
+ // Wait up to 15s for EITHER the new-flow setup panel OR the legacy Continue
725
+ // button to appear. Using .or() avoids separate sequential timeouts that would
726
+ // both expire before the page finishes loading on a slow cluster.
727
+ const setupOrContinue = setupDeployButton.or(this.continueToPlayButton);
728
+ const panelAppeared = await setupOrContinue
729
+ .first()
730
+ .isVisible({ timeout: 15000 })
731
+ .catch(() => false);
732
+ if (!panelAppeared) {
733
+ // Neither appeared — Play is already configured and ready.
734
+ return;
735
+ }
736
+ const isNewFlow = await setupDeployButton.isVisible().catch(() => false);
737
+ if (isNewFlow) {
738
+ // Step 1: connect cluster if not yet connected (modal with cluster dropdown)
739
+ const connectClusterButton = this.page.getByRole('button', {
740
+ name: 'Connect cluster',
741
+ });
742
+ const needsConnect = await connectClusterButton
743
+ .isVisible({ timeout: 3000 })
744
+ .catch(() => false);
745
+ if (needsConnect) {
746
+ await connectClusterButton.click({ timeout });
747
+ const configureEnvDialog = this.page
748
+ .getByRole('dialog')
749
+ .filter({ hasText: 'Configure environment' });
750
+ await configureEnvDialog.getByRole('combobox').click({ timeout });
751
+ await this.page.getByRole('option').first().click({ timeout });
752
+ await configureEnvDialog
753
+ .getByRole('button', { name: 'Save' })
754
+ .click({ timeout });
755
+ }
756
+ // Step 2: deploy
757
+ await setupDeployButton.click({ timeout });
758
+ await (0, test_1.expect)(this.page.getByText('Process has been successfully deployed')).toBeVisible({ timeout: 90000 });
759
+ // Step 3: configure scenario
760
+ await configureScenarioButton.click({ timeout });
761
+ return;
762
+ }
763
+ // Legacy flow: "Continue" button → "Play environment is ready" confirmation.
713
764
  let attempts = 0;
714
765
  const maxRetries = 2;
715
766
  while (attempts < maxRetries) {
@@ -49,7 +49,18 @@ class OperateProcessesPage {
49
49
  async checkCheckbox(checkbox) {
50
50
  await checkbox.waitFor({ state: 'attached', timeout: constants_1._1_SECOND_IN_MS * 10 });
51
51
  if (!(await checkbox.isChecked())) {
52
- await checkbox.check({ force: true, timeout: constants_1._1_SECOND_IN_MS * 30 });
52
+ // Carbon DS checkboxes are hidden inputs click the label to trigger React's
53
+ // synthetic event instead of force-checking the hidden input directly, which
54
+ // would be overridden by React's controlled state on the next render.
55
+ const checkboxId = await checkbox.getAttribute('id');
56
+ if (checkboxId) {
57
+ await this.page
58
+ .locator(`label[for="${checkboxId}"]`)
59
+ .click({ timeout: constants_1._1_SECOND_IN_MS * 30 });
60
+ }
61
+ else {
62
+ await checkbox.check({ force: true, timeout: constants_1._1_SECOND_IN_MS * 30 });
63
+ }
53
64
  await (0, test_1.expect)(checkbox).toBeChecked({
54
65
  checked: true,
55
66
  timeout: constants_1._1_SECOND_IN_MS * 30,
@@ -59,7 +70,15 @@ class OperateProcessesPage {
59
70
  async uncheckCheckbox(checkbox) {
60
71
  await checkbox.waitFor({ state: 'attached', timeout: constants_1._1_SECOND_IN_MS * 10 });
61
72
  if (await checkbox.isChecked()) {
62
- await checkbox.uncheck({ force: true, timeout: constants_1._1_SECOND_IN_MS * 30 });
73
+ const checkboxId = await checkbox.getAttribute('id');
74
+ if (checkboxId) {
75
+ await this.page
76
+ .locator(`label[for="${checkboxId}"]`)
77
+ .click({ timeout: constants_1._1_SECOND_IN_MS * 30 });
78
+ }
79
+ else {
80
+ await checkbox.uncheck({ force: true, timeout: constants_1._1_SECOND_IN_MS * 30 });
81
+ }
63
82
  await (0, test_1.expect)(checkbox).toBeChecked({
64
83
  checked: false,
65
84
  timeout: constants_1._1_SECOND_IN_MS * 30,
@@ -21,6 +21,7 @@ declare class PlayPage {
21
21
  readonly loadingInstanceDetailsText: Locator;
22
22
  readonly retryButton: Locator;
23
23
  readonly dialog: Locator;
24
+ readonly inputPanelStartButton: Locator;
24
25
  constructor(page: Page);
25
26
  waitForCompleteJobButtonToBeAvailable(): Promise<void>;
26
27
  clickCompleteJobButton(): Promise<void>;
@@ -28,6 +28,7 @@ class PlayPage {
28
28
  loadingInstanceDetailsText;
29
29
  retryButton;
30
30
  dialog;
31
+ inputPanelStartButton;
31
32
  constructor(page) {
32
33
  this.page = page;
33
34
  this.dialog = page.getByRole('dialog');
@@ -63,6 +64,11 @@ class PlayPage {
63
64
  this.saveScenarioModal = page.getByTestId('save-scenario-modal');
64
65
  this.loadingInstanceDetailsText = this.page.getByText('Loading instance details...');
65
66
  this.retryButton = this.page.getByRole('button', { name: 'Retry' });
67
+ // New Play UI (8.10+): "Configure scenario" opens an input panel with a plain "Start" button.
68
+ this.inputPanelStartButton = page.getByRole('button', {
69
+ name: 'Start',
70
+ exact: true,
71
+ });
66
72
  }
67
73
  async waitForCompleteJobButtonToBeAvailable() {
68
74
  await (0, test_1.expect)(this.completeJobButton).toBeVisible({
@@ -75,6 +81,7 @@ class PlayPage {
75
81
  }
76
82
  async clickStartInstanceButton() {
77
83
  const startTrigger = this.configureTestPanelStartButton
84
+ .or(this.inputPanelStartButton)
78
85
  .or(this.startInstanceButton)
79
86
  .or(this.startInstanceWithCachedButton)
80
87
  .first();
@@ -108,7 +115,8 @@ class PlayPage {
108
115
  await this.page.waitForLoadState('load');
109
116
  }
110
117
  }
111
- throw new Error('Could not find start instance button with any expected variation');
118
+ // No modal present new Play UI skips the modal entirely (Configure scenario
119
+ // opens the input panel directly without a modal step).
112
120
  }
113
121
  async clickStartInstanceWithCachedButton() {
114
122
  await this.diagram.waitFor({ state: 'visible', timeout: 30000 });
@@ -174,6 +174,13 @@ async function modelRestConnector(modelerCreatePage, connectorSettingsPage, conn
174
174
  await connectorMarketplacePage.fillSearchForConnectorTextbox('REST Connector');
175
175
  await connectorMarketplacePage.waitForConnectorSearchResults();
176
176
  await connectorMarketplacePage.downloadConnectorToProject();
177
+ // Marketplace modal closes the change-type dropdown. The task may remain in
178
+ // a selected state (class includes "djs-selected") which breaks the exact
179
+ // class-attribute match used by secondElement. Click the canvas background
180
+ // first to deselect, then re-select the task and reopen the dropdown.
181
+ await modelerCreatePage.clickCanvas();
182
+ await modelerCreatePage.secondElement.click({ timeout: 30000 });
183
+ await modelerCreatePage.clickChangeTypeButton();
177
184
  }
178
185
  await modelerCreatePage.clickRestConnectorOption(recoverSession);
179
186
  await connectorSettingsPage.clickAuthenticationTab();
@@ -91,21 +91,19 @@ class IdentityHomePage {
91
91
  }
92
92
  async selectOptionFromDropdown(option) {
93
93
  // The Identity "Assign permissions to application" dialog renders the
94
- // API list as a Carbon Dropdown rather than a native <select>. Using
95
- // Locator.selectOption() on the underlying `select` updates the form
96
- // value but does NOT trigger the Carbon component's React state, so
97
- // the dropdown stays open and the write/read permission checkboxes
98
- // are never rendered (observed in nightly run 26612986585 the
99
- // dropdown popup was still expanded when the test timed out waiting
100
- // for the `write:*` label).
94
+ // API list as a Carbon Select, which wraps a NATIVE <select> with native
95
+ // <option class="cds--select-option"> children. Native <option> elements
96
+ // are never "visible" to Playwright (the browser draws the option list as
97
+ // an OS-level widget, not as visible DOM), so clicking one via
98
+ // getByRole('option').click() always times out waiting for visibility
99
+ // observed in nightly run 26731057113, where the option resolved but the
100
+ // click never succeeded ("element is not visible").
101
101
  //
102
- // Open the dropdown and click the option from the listbox so the
103
- // Carbon handler fires, the popup closes, and the form re-renders
104
- // with the permission rows.
105
- await this.select.click();
106
- await this.page
107
- .getByRole('option', { name: option, exact: true })
108
- .click({ timeout: 10000 });
102
+ // Locator.selectOption() is the correct primitive for a native <select>:
103
+ // it sets the value and dispatches the `input`/`change` events that
104
+ // Carbon's onChange handler listens for, closing the picker and
105
+ // re-rendering the form with the write/read permission rows.
106
+ await this.select.selectOption({ label: option });
109
107
  await (0, sleep_1.sleep)(1000);
110
108
  }
111
109
  }
@@ -92,6 +92,18 @@ SM_8_10_1.test.describe('Deploy and run a process in Play', () => {
92
92
  timeout: 30000,
93
93
  });
94
94
  await modelerCreatePage.clickFirstPlacedGateway();
95
+ // On slow clusters clickFirstPlacedGateway() may trigger page.reload();
96
+ // the canvas needs settling before the context-pad JavaScript handler
97
+ // attaches. Wait up to 20 s for the context pad, then re-click the
98
+ // gateway directly (canvas is settled by then) if it still isn't open.
99
+ if (!(await modelerCreatePage.appendElementButton
100
+ .isVisible({ timeout: 20000 })
101
+ .catch(() => false))) {
102
+ await modelerCreatePage.firstPlacedGateway.click({ timeout: 10000 });
103
+ }
104
+ await (0, test_1.expect)(modelerCreatePage.appendElementButton).toBeVisible({
105
+ timeout: 15000,
106
+ });
95
107
  await modelerCreatePage.clickAppendElementButton();
96
108
  await modelerCreatePage.clickAppendTaskButton();
97
109
  await modelerCreatePage.clickConnectToOtherElementButton();
@@ -102,15 +114,10 @@ SM_8_10_1.test.describe('Deploy and run a process in Play', () => {
102
114
  // Wait for modeler background auto-save to complete
103
115
  await (0, sleep_1.sleep)(2000);
104
116
  await modelerCreatePage.switchToPlay();
105
- // After diagram modification, Play may show a "Continue" button
106
- // to redeploy, or it may show the diagram directly. Try clicking
107
- // Continue if present, then wait for the diagram to be ready.
108
- try {
109
- await modelerCreatePage.clickContinueToPlay();
110
- }
111
- catch {
112
- // Continue button not present; Play loaded directly
113
- }
117
+ // After diagram modification, Play shows the setup panel again (new flow)
118
+ // or a Continue button (legacy flow), or loads directly.
119
+ await modelerCreatePage.completePlayConfiguration();
120
+ await playPage.dismissStartModal();
114
121
  await playPage.clickStartInstanceButton();
115
122
  await playPage.waitForNextElementToBeActive('zeebe-user-task' + randomString + '1');
116
123
  await playPage.waitForCompleteJobButtonToBeAvailable();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.641",
3
+ "version": "0.0.643",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",