@camunda/e2e-test-suite 0.0.727 → 0.0.729

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.
@@ -2,11 +2,8 @@ import { Page, Locator } from '@playwright/test';
2
2
  declare class PlayPage {
3
3
  private page;
4
4
  readonly completeJobButton: Locator;
5
- readonly saveScenarioButton: Locator;
6
5
  readonly viewScenarioButton: Locator;
7
6
  readonly runAllScenariosButton: Locator;
8
- readonly confirmSaveScenarioButton: Locator;
9
- readonly enterScenarioNameInput: Locator;
10
7
  readonly confirmDeleteScenarioButton: Locator;
11
8
  readonly viewAllScenariosButton: Locator;
12
9
  readonly getScenarioRowByName: (scenarioName: string) => Locator;
@@ -17,7 +14,9 @@ declare class PlayPage {
17
14
  readonly configureTestPanel: Locator;
18
15
  readonly configureTestPanelStartButton: Locator;
19
16
  readonly notifications: Locator;
20
- readonly saveScenarioModal: Locator;
17
+ readonly scenarioSavedToast: Locator;
18
+ readonly scenarioDetailNameInput: Locator;
19
+ readonly scenarioDetailSaveButton: Locator;
21
20
  readonly loadingInstanceDetailsText: Locator;
22
21
  readonly retryButton: Locator;
23
22
  readonly dialog: Locator;
@@ -31,12 +30,13 @@ declare class PlayPage {
31
30
  waitForInstanceDetailsToBeLoaded(): Promise<void>;
32
31
  waitForNextElementToBeActive(historyItem: string): Promise<void>;
33
32
  waitForProcessToBeCompleted(): Promise<void>;
34
- clickSaveScenarioButton(): Promise<void>;
33
+ waitForScenarioRecordPanelReady(): Promise<void>;
35
34
  clickViewScenarioButton(): Promise<void>;
36
35
  enterScenarioName(scenarioName: string): Promise<void>;
37
36
  updateScenarioName(newScenarioName: string): Promise<void>;
38
37
  clickViewAllScenariosButton(): Promise<void>;
39
38
  confirmSaveScenario(): Promise<void>;
39
+ assertSavedScenarioNameInPanel(scenarioName: string): Promise<void>;
40
40
  getDeleteIconForScenario(scenarioName: string): Promise<Locator>;
41
41
  deleteScenario(scenarioName: string): Promise<void>;
42
42
  confirmDeleteScenario(): Promise<void>;
@@ -2,18 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PlayPage = void 0;
4
4
  const test_1 = require("@playwright/test");
5
- const sleep_1 = require("../../utils/sleep");
6
5
  const expectLocatorWithRetry_1 = require("../../utils/assertionHelpers/expectLocatorWithRetry");
7
6
  const clickLocatorWithRetry_1 = require("../../utils/assertionHelpers/clickLocatorWithRetry");
8
7
  const maxWaitTimeSeconds = 180000;
9
8
  class PlayPage {
10
9
  page;
11
10
  completeJobButton;
12
- saveScenarioButton;
13
11
  viewScenarioButton;
14
12
  runAllScenariosButton;
15
- confirmSaveScenarioButton;
16
- enterScenarioNameInput;
17
13
  confirmDeleteScenarioButton;
18
14
  viewAllScenariosButton;
19
15
  getScenarioRowByName;
@@ -24,7 +20,9 @@ class PlayPage {
24
20
  configureTestPanel;
25
21
  configureTestPanelStartButton;
26
22
  notifications;
27
- saveScenarioModal;
23
+ scenarioSavedToast;
24
+ scenarioDetailNameInput;
25
+ scenarioDetailSaveButton;
28
26
  loadingInstanceDetailsText;
29
27
  retryButton;
30
28
  dialog;
@@ -35,22 +33,23 @@ class PlayPage {
35
33
  this.completeJobButton = page
36
34
  .getByTestId('diagram')
37
35
  .getByLabel('Complete job');
38
- this.saveScenarioButton = page.getByRole('button', { name: 'Save scenario' });
39
36
  this.viewScenarioButton = page.getByRole('button', { name: 'View scenario' });
40
37
  this.runAllScenariosButton = page.getByRole('button', {
41
38
  name: 'Run all scenarios',
42
39
  });
43
- this.confirmSaveScenarioButton = page
44
- .getByTestId('save-scenario-modal')
45
- .getByRole('button', { name: 'Save' });
46
- this.enterScenarioNameInput = page
47
- .getByTestId('save-scenario-modal')
48
- .locator('input#scenario-name')
49
- .first();
50
40
  this.confirmDeleteScenarioButton = page
51
41
  .getByRole('dialog', { name: 'Delete scenario' })
52
42
  .getByRole('button', { name: 'Delete', exact: true });
53
- this.viewAllScenariosButton = page.getByText('(View all)').last();
43
+ // After saving a scenario, the Scenarios panel is in detail-view mode.
44
+ // To get back to the list (where "Run all scenarios" lives in
45
+ // ScenarioTopBar), click the "Back to scenarios" ghost button at the
46
+ // top of the detail panel — see ScenarioDetail/index.tsx Header.
47
+ // This replaces the legacy bottom-bar `(View all)` link that used to
48
+ // close the saved-scenario view.
49
+ this.viewAllScenariosButton = page.getByRole('button', {
50
+ name: 'Back to scenarios',
51
+ exact: true,
52
+ });
54
53
  this.getScenarioRowByName = (scenarioName) => page.locator('tr', { hasText: scenarioName });
55
54
  this.getScenarioRow = (scenarioName) => page.locator('tr', { hasText: scenarioName });
56
55
  this.diagram = page.getByTestId('diagram');
@@ -61,7 +60,24 @@ class PlayPage {
61
60
  this.configureTestPanel = page.getByTestId('configure-test-panel');
62
61
  this.configureTestPanelStartButton = this.configureTestPanel.getByRole('button', { name: /^Start( with .*)?$/, exact: false });
63
62
  this.notifications = page.locator('.cds--toast-notification');
64
- this.saveScenarioModal = page.getByTestId('save-scenario-modal');
63
+ // Hub PR #25327 (2026-06-22) removed the standalone "Save scenario" modal
64
+ // and moved recording into the Scenarios side panel's detail view. The
65
+ // detail view renders a TextInput with id="scenario-name" and a primary
66
+ // "Save scenario" button. A success toast with title "Scenario saved"
67
+ // is emitted via @camunda/c4-notifications — see
68
+ // ScenarioDetail/useScenarioEditor.ts.
69
+ this.scenarioDetailNameInput = page.locator('input#scenario-name');
70
+ this.scenarioDetailSaveButton = page.getByRole('button', {
71
+ name: 'Save scenario',
72
+ exact: true,
73
+ });
74
+ // c4-notifications wraps Carbon ToastNotification with role="alert" for
75
+ // non-actionable toasts (see Notification.tsx in @camunda/c4-notifications).
76
+ // Anchor on the accessible role instead of the Carbon CSS class so the
77
+ // locator is resilient to Carbon version bumps.
78
+ this.scenarioSavedToast = page
79
+ .getByRole('alert')
80
+ .filter({ hasText: 'Scenario saved' });
65
81
  this.loadingInstanceDetailsText = this.page.getByText('Loading instance details...');
66
82
  this.retryButton = this.page.getByRole('button', { name: 'Retry' });
67
83
  // New Play UI (8.10+): "Configure scenario" opens an input panel with a plain "Start" button.
@@ -145,49 +161,67 @@ class PlayPage {
145
161
  timeout: maxWaitTimeSeconds,
146
162
  });
147
163
  }
148
- async clickSaveScenarioButton() {
149
- await this.notifications
150
- .first()
151
- .waitFor({ state: 'hidden', timeout: 60000 })
152
- .catch(() => { });
153
- const isModalVisible = await this.saveScenarioModal
154
- .isVisible()
155
- .catch(() => false);
156
- if (!isModalVisible) {
157
- await (0, sleep_1.sleep)(3000);
158
- await (0, test_1.expect)(this.saveScenarioButton).toBeVisible({ timeout: 30000 });
159
- await (0, test_1.expect)(this.saveScenarioButton).toBeEnabled({ timeout: 30000 });
160
- await this.saveScenarioButton.click({ force: true, timeout: 30000 });
161
- }
164
+ async waitForScenarioRecordPanelReady() {
165
+ // Hub PR #25327 (2026-06-22) removed the standalone "Save scenario" modal.
166
+ // Starting a Play run now auto-opens the Scenarios side panel in "record"
167
+ // mode with the scenario-name input + a primary "Save scenario" button.
168
+ // This method waits for that record-mode panel to be ready for input.
169
+ //
170
+ // `state: 'hidden'` already resolves immediately when the notification is
171
+ // absent, so no try/catch is needed — letting a real timeout surface is
172
+ // intentional (it signals a stuck stale toast blocking subsequent input).
173
+ await this.notifications.first().waitFor({ state: 'hidden', timeout: 60000 });
174
+ await (0, test_1.expect)(this.scenarioDetailNameInput).toBeVisible({ timeout: 30000 });
162
175
  }
163
176
  async clickViewScenarioButton() {
164
177
  await this.viewScenarioButton.waitFor({ state: 'visible', timeout: 30000 });
165
178
  await this.viewScenarioButton.click();
166
179
  }
167
180
  async enterScenarioName(scenarioName) {
168
- await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.dialog, {
169
- postAction: async () => {
170
- await this.saveScenarioButton.click({ timeout: 30000 });
171
- },
181
+ // New flow: the name input lives directly inside the auto-opened record
182
+ // panel no modal to click open first. Fill and blur so the validation
183
+ // (canSave = isDirty && !isNameEmpty && !isNameDuplicate) commits before
184
+ // the Save scenario button is clicked.
185
+ await this.scenarioDetailNameInput.waitFor({
186
+ state: 'visible',
187
+ timeout: 30000,
172
188
  });
173
- const input = this.dialog.locator('input#scenario-name');
174
- await input.waitFor({ state: 'visible', timeout: 10000 });
175
- await input.clear();
176
- await input.fill(scenarioName);
189
+ await this.scenarioDetailNameInput.clear();
190
+ await this.scenarioDetailNameInput.fill(scenarioName);
191
+ await this.scenarioDetailNameInput.press('Tab');
177
192
  }
178
193
  async updateScenarioName(newScenarioName) {
179
- await this.enterScenarioNameInput.fill(newScenarioName);
194
+ // Mirror enterScenarioName's blur step so canSave validation commits
195
+ // before any subsequent Save click.
196
+ await this.scenarioDetailNameInput.fill(newScenarioName);
197
+ await this.scenarioDetailNameInput.press('Tab');
180
198
  }
181
199
  async clickViewAllScenariosButton() {
182
200
  await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.viewAllScenariosButton);
183
201
  await this.viewAllScenariosButton.click();
184
202
  }
185
203
  async confirmSaveScenario() {
186
- await this.confirmSaveScenarioButton.waitFor({
187
- state: 'visible',
188
- timeout: 10000,
189
- });
190
- await this.confirmSaveScenarioButton.click();
204
+ // Click the in-panel "Save scenario" primary button. It is gated by the
205
+ // canSave predicate in useScenarioEditor.ts — a non-empty, non-duplicate
206
+ // name and a completed instance are both required. enterScenarioName()
207
+ // and waitForProcessToBeCompleted() satisfy those before this is called.
208
+ await (0, test_1.expect)(this.scenarioDetailSaveButton).toBeVisible({ timeout: 30000 });
209
+ await (0, test_1.expect)(this.scenarioDetailSaveButton).toBeEnabled({ timeout: 30000 });
210
+ await this.scenarioDetailSaveButton.click({ timeout: 30000 });
211
+ // Saving fires a c4-notifications toast with title "Scenario saved".
212
+ // Wait for it as the success signal that replaces the legacy
213
+ // modal-hidden assertion.
214
+ await (0, test_1.expect)(this.scenarioSavedToast).toBeVisible({ timeout: 30000 });
215
+ }
216
+ async assertSavedScenarioNameInPanel(scenarioName) {
217
+ // After save, useScenarioEditor.saveTest() calls
218
+ // scenariosStore.stopRecording() + setSelectedScenario(newScenario) and
219
+ // processInstanceStore.reset(). The Scenarios side panel stays open in
220
+ // 'view' mode for the just-saved scenario, rendering the name as the
221
+ // TestName h4 (see ScenarioDetail/styled.tsx and index.tsx). The legacy
222
+ // instance-header scenarioName slot is cleared by the reset and is no
223
+ // longer the source of truth for the saved name.
224
+ await (0, test_1.expect)(this.page.getByRole('heading', { name: scenarioName, exact: true })).toBeVisible({ timeout: 30000 });
191
225
  }
192
226
  async getDeleteIconForScenario(scenarioName) {
193
227
  const scenarioRow = this.page
@@ -81,10 +81,9 @@ SM_8_10_1.test.describe('Deploy and run a process in Play', () => {
81
81
  await playPage.waitForCompleteJobButtonToBeAvailable();
82
82
  await playPage.clickCompleteJobButton();
83
83
  await playPage.waitForProcessToBeCompleted();
84
- await playPage.clickSaveScenarioButton();
84
+ await playPage.waitForScenarioRecordPanelReady();
85
85
  await playPage.enterScenarioName(scenarioName);
86
86
  await playPage.confirmSaveScenario();
87
- await (0, test_1.expect)(playPage.saveScenarioModal).toBeHidden({ timeout: 10000 });
88
87
  });
89
88
  await SM_8_10_1.test.step('Update the saved scenario', async () => {
90
89
  await modelerCreatePage.switchToImplementTab();
@@ -123,10 +122,10 @@ SM_8_10_1.test.describe('Deploy and run a process in Play', () => {
123
122
  await playPage.waitForCompleteJobButtonToBeAvailable();
124
123
  await playPage.clickCompleteJobButton();
125
124
  await playPage.waitForProcessToBeCompleted();
126
- await playPage.clickSaveScenarioButton();
125
+ await playPage.waitForScenarioRecordPanelReady();
127
126
  await playPage.enterScenarioName(updatedScenarioName);
128
127
  await playPage.confirmSaveScenario();
129
- await modelerCreatePage.assertScenarioNameVisible(updatedScenarioName);
128
+ await playPage.assertSavedScenarioNameInPanel(updatedScenarioName);
130
129
  });
131
130
  await SM_8_10_1.test.step('Run all scenarios', async () => {
132
131
  await playPage.clickViewAllScenariosButton();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.727",
3
+ "version": "0.0.729",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",