@camunda/e2e-test-suite 0.0.1442 → 0.0.1444

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.
Files changed (123) hide show
  1. package/.github/c8_versions.json +1 -0
  2. package/README.md +2 -0
  3. package/dist/fixtures/SM-8.11.d.ts +77 -0
  4. package/dist/fixtures/SM-8.11.js +176 -0
  5. package/dist/pages/8.7/ModelerCreatePage.js +21 -0
  6. package/dist/pages/SM-8.11/Authorization.d.ts +39 -0
  7. package/dist/pages/SM-8.11/Authorization.js +349 -0
  8. package/dist/pages/SM-8.11/ClusterDetailsPage.d.ts +8 -0
  9. package/dist/pages/SM-8.11/ClusterDetailsPage.js +65 -0
  10. package/dist/pages/SM-8.11/ClusterPage.d.ts +11 -0
  11. package/dist/pages/SM-8.11/ClusterPage.js +37 -0
  12. package/dist/pages/SM-8.11/ConnectorMarketplacePage.d.ts +27 -0
  13. package/dist/pages/SM-8.11/ConnectorMarketplacePage.js +187 -0
  14. package/dist/pages/SM-8.11/ConnectorSettingsPage.d.ts +41 -0
  15. package/dist/pages/SM-8.11/ConnectorSettingsPage.js +153 -0
  16. package/dist/pages/SM-8.11/ConnectorTemplatePage.d.ts +11 -0
  17. package/dist/pages/SM-8.11/ConnectorTemplatePage.js +34 -0
  18. package/dist/pages/SM-8.11/FormJsPage.d.ts +33 -0
  19. package/dist/pages/SM-8.11/FormJsPage.js +109 -0
  20. package/dist/pages/SM-8.11/HomePage.d.ts +20 -0
  21. package/dist/pages/SM-8.11/HomePage.js +49 -0
  22. package/dist/pages/SM-8.11/IdentityTenantPage.d.ts +53 -0
  23. package/dist/pages/SM-8.11/IdentityTenantPage.js +264 -0
  24. package/dist/pages/SM-8.11/IdentityUsersPage.d.ts +92 -0
  25. package/dist/pages/SM-8.11/IdentityUsersPage.js +485 -0
  26. package/dist/pages/SM-8.11/KeycloakAdminPage.d.ts +17 -0
  27. package/dist/pages/SM-8.11/KeycloakAdminPage.js +78 -0
  28. package/dist/pages/SM-8.11/KeycloakLoginPage.d.ts +12 -0
  29. package/dist/pages/SM-8.11/KeycloakLoginPage.js +32 -0
  30. package/dist/pages/SM-8.11/KeycloakUtils.d.ts +17 -0
  31. package/dist/pages/SM-8.11/KeycloakUtils.js +94 -0
  32. package/dist/pages/SM-8.11/LoginPage.d.ts +21 -0
  33. package/dist/pages/SM-8.11/LoginPage.js +112 -0
  34. package/dist/pages/SM-8.11/ManagementIdentityPage.d.ts +44 -0
  35. package/dist/pages/SM-8.11/ManagementIdentityPage.js +176 -0
  36. package/dist/pages/SM-8.11/ModelerCreatePage.d.ts +238 -0
  37. package/dist/pages/SM-8.11/ModelerCreatePage.js +1950 -0
  38. package/dist/pages/SM-8.11/ModelerHomePage.d.ts +97 -0
  39. package/dist/pages/SM-8.11/ModelerHomePage.js +956 -0
  40. package/dist/pages/SM-8.11/NavigationPage.d.ts +58 -0
  41. package/dist/pages/SM-8.11/NavigationPage.js +389 -0
  42. package/dist/pages/SM-8.11/OCIdentityAuthorizationsPage.d.ts +10 -0
  43. package/dist/pages/SM-8.11/OCIdentityAuthorizationsPage.js +22 -0
  44. package/dist/pages/SM-8.11/OCIdentityClusterVariablesPage.d.ts +22 -0
  45. package/dist/pages/SM-8.11/OCIdentityClusterVariablesPage.js +120 -0
  46. package/dist/pages/SM-8.11/OCIdentityGroupsPage.d.ts +39 -0
  47. package/dist/pages/SM-8.11/OCIdentityGroupsPage.js +154 -0
  48. package/dist/pages/SM-8.11/OCIdentityHomePage.d.ts +23 -0
  49. package/dist/pages/SM-8.11/OCIdentityHomePage.js +115 -0
  50. package/dist/pages/SM-8.11/OCIdentityMappingRulesPage.d.ts +23 -0
  51. package/dist/pages/SM-8.11/OCIdentityMappingRulesPage.js +116 -0
  52. package/dist/pages/SM-8.11/OCIdentityRolesPage.d.ts +61 -0
  53. package/dist/pages/SM-8.11/OCIdentityRolesPage.js +433 -0
  54. package/dist/pages/SM-8.11/OCTenantPage.d.ts +73 -0
  55. package/dist/pages/SM-8.11/OCTenantPage.js +399 -0
  56. package/dist/pages/SM-8.11/OperateHomePage.d.ts +26 -0
  57. package/dist/pages/SM-8.11/OperateHomePage.js +139 -0
  58. package/dist/pages/SM-8.11/OperateProcessInstancePage.d.ts +30 -0
  59. package/dist/pages/SM-8.11/OperateProcessInstancePage.js +276 -0
  60. package/dist/pages/SM-8.11/OperateProcessesPage.d.ts +38 -0
  61. package/dist/pages/SM-8.11/OperateProcessesPage.js +383 -0
  62. package/dist/pages/SM-8.11/OptimizeCollectionsPage.d.ts +17 -0
  63. package/dist/pages/SM-8.11/OptimizeCollectionsPage.js +62 -0
  64. package/dist/pages/SM-8.11/OptimizeDashboardPage.d.ts +14 -0
  65. package/dist/pages/SM-8.11/OptimizeDashboardPage.js +63 -0
  66. package/dist/pages/SM-8.11/OptimizeHomePage.d.ts +19 -0
  67. package/dist/pages/SM-8.11/OptimizeHomePage.js +76 -0
  68. package/dist/pages/SM-8.11/OptimizeReportPage.d.ts +50 -0
  69. package/dist/pages/SM-8.11/OptimizeReportPage.js +209 -0
  70. package/dist/pages/SM-8.11/PlayPage.d.ts +47 -0
  71. package/dist/pages/SM-8.11/PlayPage.js +389 -0
  72. package/dist/pages/SM-8.11/ProcessApplicationPage.d.ts +35 -0
  73. package/dist/pages/SM-8.11/ProcessApplicationPage.js +340 -0
  74. package/dist/pages/SM-8.11/SettingsPage.d.ts +12 -0
  75. package/dist/pages/SM-8.11/SettingsPage.js +34 -0
  76. package/dist/pages/SM-8.11/TaskDetailsPage.d.ts +70 -0
  77. package/dist/pages/SM-8.11/TaskDetailsPage.js +273 -0
  78. package/dist/pages/SM-8.11/TaskPanelPage.d.ts +29 -0
  79. package/dist/pages/SM-8.11/TaskPanelPage.js +320 -0
  80. package/dist/pages/SM-8.11/TaskProcessesPage.d.ts +15 -0
  81. package/dist/pages/SM-8.11/TaskProcessesPage.js +77 -0
  82. package/dist/pages/SM-8.11/UtilitiesPage.d.ts +38 -0
  83. package/dist/pages/SM-8.11/UtilitiesPage.js +573 -0
  84. package/dist/pages/SM-8.11/optimizeReportUtils.d.ts +6 -0
  85. package/dist/pages/SM-8.11/optimizeReportUtils.js +77 -0
  86. package/dist/tests/SM-8.11/cluster-management-user-flows.spec.d.ts +1 -0
  87. package/dist/tests/SM-8.11/cluster-management-user-flows.spec.js +48 -0
  88. package/dist/tests/SM-8.11/cluster-variables.spec.d.ts +1 -0
  89. package/dist/tests/SM-8.11/cluster-variables.spec.js +226 -0
  90. package/dist/tests/SM-8.11/connectors-user-flows.spec.d.ts +1 -0
  91. package/dist/tests/SM-8.11/connectors-user-flows.spec.js +307 -0
  92. package/dist/tests/SM-8.11/document-handling-user-flows.spec.d.ts +1 -0
  93. package/dist/tests/SM-8.11/document-handling-user-flows.spec.js +174 -0
  94. package/dist/tests/SM-8.11/hto-user-flows.spec.d.ts +1 -0
  95. package/dist/tests/SM-8.11/hto-user-flows.spec.js +410 -0
  96. package/dist/tests/SM-8.11/hub-web-modeler-smoke.spec.d.ts +1 -0
  97. package/dist/tests/SM-8.11/hub-web-modeler-smoke.spec.js +24 -0
  98. package/dist/tests/SM-8.11/identity-user-flows.spec.d.ts +1 -0
  99. package/dist/tests/SM-8.11/identity-user-flows.spec.js +220 -0
  100. package/dist/tests/SM-8.11/license-key-user-flows.spec.d.ts +1 -0
  101. package/dist/tests/SM-8.11/license-key-user-flows.spec.js +461 -0
  102. package/dist/tests/SM-8.11/login.spec.d.ts +1 -0
  103. package/dist/tests/SM-8.11/login.spec.js +24 -0
  104. package/dist/tests/SM-8.11/migration-path-user-flows.spec.d.ts +1 -0
  105. package/dist/tests/SM-8.11/migration-path-user-flows.spec.js +263 -0
  106. package/dist/tests/SM-8.11/mt-enabled-user-flows.spec.d.ts +1 -0
  107. package/dist/tests/SM-8.11/mt-enabled-user-flows.spec.js +607 -0
  108. package/dist/tests/SM-8.11/optimize-user-flows.spec.d.ts +1 -0
  109. package/dist/tests/SM-8.11/optimize-user-flows.spec.js +505 -0
  110. package/dist/tests/SM-8.11/play.spec.d.ts +1 -0
  111. package/dist/tests/SM-8.11/play.spec.js +144 -0
  112. package/dist/tests/SM-8.11/rba-enabled-user-flows.spec.d.ts +1 -0
  113. package/dist/tests/SM-8.11/rba-enabled-user-flows.spec.js +712 -0
  114. package/dist/tests/SM-8.11/smoke-tests.spec.d.ts +1 -0
  115. package/dist/tests/SM-8.11/smoke-tests.spec.js +167 -0
  116. package/dist/tests/SM-8.11/test-setup.spec.d.ts +1 -0
  117. package/dist/tests/SM-8.11/test-setup.spec.js +24 -0
  118. package/dist/tests/SM-8.11/topology-orchestration-smoke.spec.d.ts +1 -0
  119. package/dist/tests/SM-8.11/topology-orchestration-smoke.spec.js +58 -0
  120. package/dist/tests/SM-8.11/web-modeler-user-flows.spec.d.ts +1 -0
  121. package/dist/tests/SM-8.11/web-modeler-user-flows.spec.js +223 -0
  122. package/package.json +1 -1
  123. package/playwright.config.ts +21 -0
@@ -0,0 +1,389 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PlayPage = void 0;
4
+ const test_1 = require("@playwright/test");
5
+ const expectLocatorWithRetry_1 = require("../../utils/assertionHelpers/expectLocatorWithRetry");
6
+ const clickLocatorWithRetry_1 = require("../../utils/assertionHelpers/clickLocatorWithRetry");
7
+ const maxWaitTimeSeconds = 180000;
8
+ class PlayPage {
9
+ page;
10
+ completeJobButton;
11
+ viewScenarioButton;
12
+ runAllScenariosButton;
13
+ confirmDeleteScenarioButton;
14
+ viewAllScenariosButton;
15
+ getScenarioRowByName;
16
+ getScenarioRow;
17
+ diagram;
18
+ startInstanceButton;
19
+ startInstanceWithCachedButton;
20
+ configureTestPanel;
21
+ configureTestPanelStartButton;
22
+ notifications;
23
+ scenarioSavedToast;
24
+ scenarioDetailNameInput;
25
+ scenarioDetailSaveButton;
26
+ loadingInstanceDetailsText;
27
+ retryButton;
28
+ dialog;
29
+ inputPanelStartButton;
30
+ constructor(page) {
31
+ this.page = page;
32
+ this.dialog = page.getByRole('dialog');
33
+ // Test mode labels this button with a <span class="sr-only">Complete job</span>
34
+ // rather than an aria-label, so getByLabel cannot see it -- its data-testid
35
+ // is element-scoped ("<elementId>-Complete job"). getByRole reads the
36
+ // accessible name, which the sr-only text and the legacy aria-label both
37
+ // supply, so this resolves on either shell.
38
+ this.completeJobButton = page
39
+ .getByTestId('diagram')
40
+ .getByRole('button', { name: 'Complete job' })
41
+ .first();
42
+ // Test mode renamed "scenario" to "test case" across this panel
43
+ // (camunda-hub a90fd06). Each label below accepts both wordings so the
44
+ // page object works either side of the rename.
45
+ this.viewScenarioButton = page.getByRole('button', {
46
+ name: /^View (scenario|test case)$/i,
47
+ });
48
+ this.runAllScenariosButton = page.getByRole('button', {
49
+ name: /^Run all (scenarios|test cases)$/i,
50
+ });
51
+ // The Design System confirmation is a Radix AlertDialog, so its role is
52
+ // "alertdialog" -- a distinct ARIA role that getByRole('dialog') does not
53
+ // match. Accept either role and identify it by its heading rather than an
54
+ // accessible name, which the DS dialog does not always expose.
55
+ this.confirmDeleteScenarioButton = page
56
+ .getByRole('alertdialog')
57
+ .or(page.getByRole('dialog'))
58
+ .filter({
59
+ has: page.getByRole('heading', {
60
+ name: /^Delete (scenario|test case)$/i,
61
+ }),
62
+ })
63
+ .getByRole('button', { name: 'Delete', exact: true });
64
+ // After saving a scenario, the Scenarios panel is in detail-view mode.
65
+ // To get back to the list (where "Run all test cases" lives in
66
+ // TestCaseTopBar), click the ghost button at the top of the detail
67
+ // panel — see TestCaseDetail.tsx Header.
68
+ // This replaces the legacy bottom-bar `(View all)` link that used to
69
+ // close the saved-scenario view.
70
+ // camunda-hub cfaea03 (#28259, "rename play package to test-studio")
71
+ // relabelled it "Back to all test cases"; the anchored regex below must
72
+ // keep the optional "all " or it matches nothing on current builds.
73
+ this.viewAllScenariosButton = page.getByRole('button', {
74
+ name: /^Back to (all )?(scenarios|test cases)$/i,
75
+ });
76
+ this.getScenarioRowByName = (scenarioName) => page.locator('tr', { hasText: scenarioName });
77
+ this.getScenarioRow = (scenarioName) => page.locator('tr', { hasText: scenarioName });
78
+ this.diagram = page.getByTestId('diagram');
79
+ this.startInstanceButton = this.diagram.getByLabel('Start instance', {
80
+ exact: true,
81
+ });
82
+ this.startInstanceWithCachedButton = page.getByLabel('Start instance with cached');
83
+ this.configureTestPanel = page.getByTestId('configure-test-panel');
84
+ this.configureTestPanelStartButton = this.configureTestPanel.getByRole('button', { name: /^Start( with .*)?$/, exact: false });
85
+ this.notifications = page.locator('.cds--toast-notification');
86
+ // Hub PR #25327 (2026-06-22) removed the standalone "Save scenario" modal
87
+ // and moved recording into the side panel's detail view. The detail view
88
+ // renders a TextInput plus a primary "Save test case" button and emits a
89
+ // "Test case saved" toast via @camunda/c4-notifications — see
90
+ // TestCasesPanel/TestCases/TestCaseDetail/{index.tsx,use-test-case-editor.ts}.
91
+ // camunda-hub a90fd06 renamed the input id from "scenario-name" to
92
+ // "test-case-name"; accept the pre-rename id too.
93
+ this.scenarioDetailNameInput = page
94
+ .locator('input#test-case-name')
95
+ .or(page.locator('input#scenario-name'))
96
+ .first();
97
+ this.scenarioDetailSaveButton = page.getByRole('button', {
98
+ name: /^Save (scenario|test case)$/i,
99
+ });
100
+ // c4-notifications wraps Carbon ToastNotification with role="alert" for
101
+ // non-actionable toasts (see Notification.tsx in @camunda/c4-notifications).
102
+ // Anchor on the accessible role instead of the Carbon CSS class so the
103
+ // locator is resilient to Carbon version bumps.
104
+ // The Design System toast stack is an <ol> of plain <li> items with no
105
+ // role="alert" (the only alert role in Test mode belongs to the Copilot
106
+ // panel), so match the text as well as the legacy alert role.
107
+ this.scenarioSavedToast = page
108
+ .getByRole('alert')
109
+ .filter({ hasText: /(Scenario|Test case) saved/i })
110
+ .or(page.getByText(/(Scenario|Test case) saved/i))
111
+ .first();
112
+ this.loadingInstanceDetailsText = this.page.getByText('Loading instance details...');
113
+ this.retryButton = this.page.getByRole('button', { name: 'Retry' });
114
+ // New Play UI (8.10+): "Configure scenario" opens an input panel with a plain "Start" button.
115
+ this.inputPanelStartButton = page.getByRole('button', {
116
+ name: 'Start',
117
+ exact: true,
118
+ });
119
+ }
120
+ async waitForCompleteJobButtonToBeAvailable() {
121
+ await (0, test_1.expect)(this.completeJobButton).toBeVisible({
122
+ timeout: maxWaitTimeSeconds,
123
+ });
124
+ }
125
+ async clickCompleteJobButton() {
126
+ await this.completeJobButton.waitFor({ state: 'visible', timeout: 30000 });
127
+ await this.completeJobButton.click();
128
+ }
129
+ async clickStartInstanceButton() {
130
+ const scenariosRegion = this.page.getByRole('region', {
131
+ name: /^(Scenarios|Test cases)$/i,
132
+ });
133
+ const backToScenariosButton = scenariosRegion.getByRole('button', {
134
+ name: /^Back to (all )?(scenarios|test cases)$/i,
135
+ });
136
+ // TestPanel.tsx labels this `Close ${title} panel`, so match on the
137
+ // substring rather than the exact accessible name.
138
+ const closeScenariosButton = scenariosRegion
139
+ .getByRole('button', {
140
+ name: 'Close',
141
+ })
142
+ .first();
143
+ // When "Configure test case" is clicked with a prior test case already
144
+ // saved, the Test Studio UI opens test-case DETAILS (with "Back to all test
145
+ // cases") instead of the input panel. Close the Test cases panel to reveal
146
+ // the canvas start controls.
147
+ const isDetailsView = await backToScenariosButton
148
+ .waitFor({ state: 'visible', timeout: 3000 })
149
+ .then(() => true)
150
+ .catch(() => false);
151
+ if (isDetailsView) {
152
+ await closeScenariosButton.click();
153
+ }
154
+ const startTrigger = this.configureTestPanelStartButton
155
+ .or(this.inputPanelStartButton)
156
+ .or(this.startInstanceButton)
157
+ .or(this.startInstanceWithCachedButton)
158
+ .first();
159
+ // Test Studio's "Setup environment" is an inline accordion whose step 3
160
+ // ("Configure test case") holds ConfigureTestPanel and its Start button.
161
+ // The section can collapse again after completePlayConfiguration() returns
162
+ // (a cluster-change effect re-shows the overlay), in which case the Start
163
+ // trigger disappears. Re-open the "Configure test case" accordion heading
164
+ // before an attempt, but only when the panel is not already visible --
165
+ // clicking the heading while it is open re-shows the overlay and collapses
166
+ // the panel, so gating on the always-present "Setup environment" heading
167
+ // would defeat every retry.
168
+ const configureTestCaseButton = this.page
169
+ .getByTestId('configure-test-case-button')
170
+ .or(this.page.getByTestId('play-configuration-configure-scenario-button'))
171
+ .or(this.page.getByRole('button', {
172
+ name: /^Configure (test case|scenario)$/,
173
+ }))
174
+ .first();
175
+ await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, startTrigger, {
176
+ totalTimeout: maxWaitTimeSeconds,
177
+ visibilityTimeout: 30000,
178
+ maxRetries: 5,
179
+ preAction: async () => {
180
+ const panelVisible = await this.configureTestPanel
181
+ .isVisible()
182
+ .catch(() => false);
183
+ if (!panelVisible) {
184
+ await configureTestCaseButton.click({ timeout: 30000 });
185
+ }
186
+ },
187
+ });
188
+ }
189
+ async dismissStartModal() {
190
+ const buttonVariations = [
191
+ 'Start a process instance',
192
+ 'Start another instance',
193
+ 'Start new instance',
194
+ 'Start instance',
195
+ ];
196
+ // Wait for the page to settle before checking for modal buttons
197
+ await this.page.waitForLoadState('domcontentloaded');
198
+ const maxAttempts = 3;
199
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
200
+ for (const buttonName of buttonVariations) {
201
+ const button = this.page.getByRole('button', { name: buttonName });
202
+ const count = await button.count();
203
+ if (count > 0) {
204
+ await button.waitFor({ state: 'visible', timeout: 10000 });
205
+ await button.click();
206
+ return;
207
+ }
208
+ }
209
+ if (attempt < maxAttempts - 1) {
210
+ await this.page.waitForLoadState('load');
211
+ }
212
+ }
213
+ // No modal present — new Play UI skips the modal entirely (Configure scenario
214
+ // opens the input panel directly without a modal step).
215
+ }
216
+ async clickStartInstanceWithCachedButton() {
217
+ await this.diagram.waitFor({ state: 'visible', timeout: 30000 });
218
+ await this.startInstanceWithCachedButton.click({ timeout: 30000 });
219
+ }
220
+ async waitForInstanceDetailsToBeLoaded() {
221
+ // Do NOT use page.reload() — it destroys Play's embedded context.
222
+ await (0, test_1.expect)(this.page.getByText(/process instance key/i)).toBeVisible({
223
+ timeout: maxWaitTimeSeconds,
224
+ });
225
+ await (0, test_1.expect)(this.page.getByText(/This process instance has no variables/i)).toBeVisible({
226
+ timeout: maxWaitTimeSeconds,
227
+ });
228
+ }
229
+ async waitForNextElementToBeActive(historyItem) {
230
+ const locator = this.page.getByText(new RegExp(`^${historyItem}`, 'i'));
231
+ // Do NOT use page.reload() here — Play is embedded in the modeler and
232
+ // reloading destroys the Play instance context, making the diagram
233
+ // permanently invisible. Use a long timeout instead, consistent with
234
+ // waitForCompleteJobButtonToBeAvailable.
235
+ await (0, test_1.expect)(locator.first()).toBeVisible({ timeout: maxWaitTimeSeconds });
236
+ }
237
+ async waitForProcessToBeCompleted() {
238
+ // Do NOT use page.reload() — it destroys Play's embedded context.
239
+ await (0, test_1.expect)(this.page.getByText('Completed')).toBeVisible({
240
+ timeout: maxWaitTimeSeconds,
241
+ });
242
+ }
243
+ async waitForScenarioRecordPanelReady() {
244
+ // Hub PR #25327 (2026-06-22) removed the standalone "Save scenario" modal.
245
+ // Starting a Play run now auto-opens the Scenarios side panel in "record"
246
+ // mode with the scenario-name input + a primary "Save scenario" button.
247
+ // This method waits for that record-mode panel to be ready for input.
248
+ //
249
+ // `state: 'hidden'` already resolves immediately when the notification is
250
+ // absent, so no try/catch is needed — letting a real timeout surface is
251
+ // intentional (it signals a stuck stale toast blocking subsequent input).
252
+ await this.notifications.first().waitFor({ state: 'hidden', timeout: 60000 });
253
+ await (0, test_1.expect)(this.scenarioDetailNameInput).toBeVisible({ timeout: 30000 });
254
+ }
255
+ async clickViewScenarioButton() {
256
+ await this.viewScenarioButton.waitFor({ state: 'visible', timeout: 30000 });
257
+ await this.viewScenarioButton.click();
258
+ }
259
+ async enterScenarioName(scenarioName) {
260
+ // New flow: the name input lives directly inside the auto-opened record
261
+ // panel — no modal to click open first. Fill and blur so the validation
262
+ // (canSave = isDirty && !isNameEmpty && !isNameDuplicate) commits before
263
+ // the Save scenario button is clicked.
264
+ await this.scenarioDetailNameInput.waitFor({
265
+ state: 'visible',
266
+ timeout: 30000,
267
+ });
268
+ await this.scenarioDetailNameInput.clear();
269
+ await this.scenarioDetailNameInput.fill(scenarioName);
270
+ await this.scenarioDetailNameInput.press('Tab');
271
+ }
272
+ async updateScenarioName(newScenarioName) {
273
+ // Mirror enterScenarioName's blur step so canSave validation commits
274
+ // before any subsequent Save click.
275
+ await this.scenarioDetailNameInput.fill(newScenarioName);
276
+ await this.scenarioDetailNameInput.press('Tab');
277
+ }
278
+ async clickViewAllScenariosButton() {
279
+ // This method's job is to reach the scenario LIST. Saving can already leave
280
+ // Play there (see assertSavedScenarioNameInPanel), and the "Back to all
281
+ // test cases" control exists only inside a scenario's detail view -- so on the
282
+ // list it never renders and waiting for it just burns the retry budget.
283
+ // "Run all test cases" lives on the list and is what the caller needs next,
284
+ // so treat it as proof the destination is already reached.
285
+ if (await this.runAllScenariosButton
286
+ .isVisible({ timeout: 3000 })
287
+ .catch(() => false)) {
288
+ return;
289
+ }
290
+ await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.viewAllScenariosButton);
291
+ await this.viewAllScenariosButton.click();
292
+ }
293
+ async confirmSaveScenario() {
294
+ // Click the in-panel "Save scenario" primary button. It is gated by the
295
+ // canSave predicate in useScenarioEditor.ts — a non-empty, non-duplicate
296
+ // name and a completed instance are both required. enterScenarioName()
297
+ // and waitForProcessToBeCompleted() satisfy those before this is called.
298
+ await (0, test_1.expect)(this.scenarioDetailSaveButton).toBeVisible({ timeout: 30000 });
299
+ await (0, test_1.expect)(this.scenarioDetailSaveButton).toBeEnabled({ timeout: 30000 });
300
+ await this.scenarioDetailSaveButton.click({ timeout: 30000 });
301
+ // Saving fires a c4-notifications toast with title "Scenario saved".
302
+ // Wait for it as the success signal that replaces the legacy
303
+ // modal-hidden assertion.
304
+ await (0, test_1.expect)(this.scenarioSavedToast).toBeVisible({ timeout: 30000 });
305
+ }
306
+ async assertSavedScenarioNameInPanel(scenarioName) {
307
+ // A successful save tears the detail view down: use-test-case-editor.ts
308
+ // saveTest() calls resetForm() + testCasesStore.resetTestCaseDetail(),
309
+ // which clears selectedTestCase and stops recording, so the store's
310
+ // isTestCaseDetailVisible turns false and the panel falls back to the
311
+ // saved test-case list. The name therefore lands on one of three
312
+ // surfaces: a list row, the read-only TestName heading, or (pre-reset
313
+ // hub builds) the still-editable Name field.
314
+ const savedName = this.getScenarioRow(scenarioName)
315
+ .or(this.page.getByRole('heading', { name: scenarioName, exact: true }))
316
+ .first();
317
+ const onListOrHeading = await savedName
318
+ .waitFor({ state: 'visible', timeout: 30000 })
319
+ .then(() => true)
320
+ .catch(() => false);
321
+ if (onListOrHeading) {
322
+ return;
323
+ }
324
+ // Updating an existing test case does NOT always leave the detail view
325
+ // open: Play can close it and fall back to the "Test cases" list, which is
326
+ // what it did in run 32105225796 -- the screenshot shows the list with the
327
+ // updated name present while neither the heading nor the Name field
328
+ // existed, so the old two-surface check failed on a save that had actually
329
+ // succeeded (the "Scenario saved" toast had already been asserted). The row
330
+ // carrying the exact name is the same proof, on the surface Play chose.
331
+ const listRow = this.page
332
+ .locator('tr')
333
+ .filter({ has: this.page.getByText(scenarioName, { exact: true }) })
334
+ .first();
335
+ if (await listRow.isVisible({ timeout: 3000 }).catch(() => false)) {
336
+ return;
337
+ }
338
+ await (0, test_1.expect)(this.scenarioDetailNameInput).toHaveValue(scenarioName, {
339
+ timeout: 30000,
340
+ });
341
+ }
342
+ async getDeleteIconForScenario(scenarioName) {
343
+ const scenarioRow = this.page
344
+ .locator('tr')
345
+ .filter({ has: this.page.getByText(scenarioName, { exact: true }) });
346
+ return scenarioRow.getByLabel(/^Delete (scenario|test case)$/i);
347
+ }
348
+ async deleteScenario(scenarioName) {
349
+ const deleteIcon = await this.getDeleteIconForScenario(scenarioName);
350
+ await deleteIcon.waitFor({ state: 'visible', timeout: 10000 });
351
+ await deleteIcon.click();
352
+ }
353
+ async confirmDeleteScenario() {
354
+ await this.confirmDeleteScenarioButton.waitFor({
355
+ state: 'visible',
356
+ timeout: 10000,
357
+ });
358
+ await this.confirmDeleteScenarioButton.click();
359
+ }
360
+ async clickRunAllScenariosButton() {
361
+ await this.runAllScenariosButton.waitFor({
362
+ state: 'visible',
363
+ timeout: 30000,
364
+ });
365
+ await this.runAllScenariosButton.click();
366
+ }
367
+ async clickmessagePublishButton() {
368
+ await this.diagram.waitFor({ state: 'visible', timeout: 30000 });
369
+ const publishIcon = this.diagram
370
+ .locator('div.djs-overlays')
371
+ .getByLabel('Publish message', { exact: true });
372
+ await publishIcon.click({ timeout: 30000 });
373
+ }
374
+ async assertScenarioCompleted(scenarioName) {
375
+ await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.page
376
+ .locator('tr', { hasText: scenarioName })
377
+ .locator('text=Completed'), {
378
+ totalTimeout: 120000,
379
+ visibilityTimeout: 30000,
380
+ maxRetries: 5,
381
+ postAction: async () => {
382
+ if (await this.retryButton.isVisible()) {
383
+ await this.retryButton.click();
384
+ }
385
+ },
386
+ });
387
+ }
388
+ }
389
+ exports.PlayPage = PlayPage;
@@ -0,0 +1,35 @@
1
+ import { Locator, Page } from '@playwright/test';
2
+ declare class ProcessApplicationPage {
3
+ private page;
4
+ readonly createProcessApplicationButton: Locator;
5
+ readonly createProcessApplicationOption: Locator;
6
+ readonly creationDialog: Locator;
7
+ readonly nameInput: Locator;
8
+ readonly submitButton: Locator;
9
+ readonly cancelButton: Locator;
10
+ readonly breadcrumb: Locator;
11
+ readonly folderExplorer: Locator;
12
+ readonly titleBar: Locator;
13
+ readonly createNewDropdown: Locator;
14
+ readonly inlineNameInput: Locator;
15
+ readonly manageEnvironmentsHeading: Locator;
16
+ readonly assignedEnvironmentCards: Locator;
17
+ readonly editEnvironmentsButton: Locator;
18
+ readonly environmentEditorRegion: Locator;
19
+ readonly saveEnvironmentsButton: Locator;
20
+ constructor(page: Page);
21
+ private pageReady;
22
+ waitForProcessApplication(timeout?: number): Promise<void>;
23
+ clickCreateProcessApplicationButton(): Promise<void>;
24
+ clickCreateProcessApplicationOption(): Promise<void>;
25
+ nameNewProcessApplication(name: string): Promise<void>;
26
+ rememberResourceContainer(url: string): void;
27
+ private workspaceEnvironmentsUrl;
28
+ ensureStageCluster(clusterName?: string): Promise<void>;
29
+ assignEnvironmentToWorkspace(workspaceUrl: string, clusterName?: string): Promise<void>;
30
+ markWorkspaceEnvironmentAssignedByAdmin(workspaceUrl: string): void;
31
+ private workspaceId;
32
+ private assignWorkspaceEnvironment;
33
+ openByName(name: string): Promise<void>;
34
+ }
35
+ export { ProcessApplicationPage };