@camunda/e2e-test-suite 0.0.1443 → 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.
- package/.github/c8_versions.json +1 -0
- package/README.md +2 -0
- package/dist/fixtures/SM-8.11.d.ts +77 -0
- package/dist/fixtures/SM-8.11.js +176 -0
- package/dist/pages/SM-8.11/Authorization.d.ts +39 -0
- package/dist/pages/SM-8.11/Authorization.js +349 -0
- package/dist/pages/SM-8.11/ClusterDetailsPage.d.ts +8 -0
- package/dist/pages/SM-8.11/ClusterDetailsPage.js +65 -0
- package/dist/pages/SM-8.11/ClusterPage.d.ts +11 -0
- package/dist/pages/SM-8.11/ClusterPage.js +37 -0
- package/dist/pages/SM-8.11/ConnectorMarketplacePage.d.ts +27 -0
- package/dist/pages/SM-8.11/ConnectorMarketplacePage.js +187 -0
- package/dist/pages/SM-8.11/ConnectorSettingsPage.d.ts +41 -0
- package/dist/pages/SM-8.11/ConnectorSettingsPage.js +153 -0
- package/dist/pages/SM-8.11/ConnectorTemplatePage.d.ts +11 -0
- package/dist/pages/SM-8.11/ConnectorTemplatePage.js +34 -0
- package/dist/pages/SM-8.11/FormJsPage.d.ts +33 -0
- package/dist/pages/SM-8.11/FormJsPage.js +109 -0
- package/dist/pages/SM-8.11/HomePage.d.ts +20 -0
- package/dist/pages/SM-8.11/HomePage.js +49 -0
- package/dist/pages/SM-8.11/IdentityTenantPage.d.ts +53 -0
- package/dist/pages/SM-8.11/IdentityTenantPage.js +264 -0
- package/dist/pages/SM-8.11/IdentityUsersPage.d.ts +92 -0
- package/dist/pages/SM-8.11/IdentityUsersPage.js +485 -0
- package/dist/pages/SM-8.11/KeycloakAdminPage.d.ts +17 -0
- package/dist/pages/SM-8.11/KeycloakAdminPage.js +78 -0
- package/dist/pages/SM-8.11/KeycloakLoginPage.d.ts +12 -0
- package/dist/pages/SM-8.11/KeycloakLoginPage.js +32 -0
- package/dist/pages/SM-8.11/KeycloakUtils.d.ts +17 -0
- package/dist/pages/SM-8.11/KeycloakUtils.js +94 -0
- package/dist/pages/SM-8.11/LoginPage.d.ts +21 -0
- package/dist/pages/SM-8.11/LoginPage.js +112 -0
- package/dist/pages/SM-8.11/ManagementIdentityPage.d.ts +44 -0
- package/dist/pages/SM-8.11/ManagementIdentityPage.js +176 -0
- package/dist/pages/SM-8.11/ModelerCreatePage.d.ts +238 -0
- package/dist/pages/SM-8.11/ModelerCreatePage.js +1950 -0
- package/dist/pages/SM-8.11/ModelerHomePage.d.ts +97 -0
- package/dist/pages/SM-8.11/ModelerHomePage.js +956 -0
- package/dist/pages/SM-8.11/NavigationPage.d.ts +58 -0
- package/dist/pages/SM-8.11/NavigationPage.js +389 -0
- package/dist/pages/SM-8.11/OCIdentityAuthorizationsPage.d.ts +10 -0
- package/dist/pages/SM-8.11/OCIdentityAuthorizationsPage.js +22 -0
- package/dist/pages/SM-8.11/OCIdentityClusterVariablesPage.d.ts +22 -0
- package/dist/pages/SM-8.11/OCIdentityClusterVariablesPage.js +120 -0
- package/dist/pages/SM-8.11/OCIdentityGroupsPage.d.ts +39 -0
- package/dist/pages/SM-8.11/OCIdentityGroupsPage.js +154 -0
- package/dist/pages/SM-8.11/OCIdentityHomePage.d.ts +23 -0
- package/dist/pages/SM-8.11/OCIdentityHomePage.js +115 -0
- package/dist/pages/SM-8.11/OCIdentityMappingRulesPage.d.ts +23 -0
- package/dist/pages/SM-8.11/OCIdentityMappingRulesPage.js +116 -0
- package/dist/pages/SM-8.11/OCIdentityRolesPage.d.ts +61 -0
- package/dist/pages/SM-8.11/OCIdentityRolesPage.js +433 -0
- package/dist/pages/SM-8.11/OCTenantPage.d.ts +73 -0
- package/dist/pages/SM-8.11/OCTenantPage.js +399 -0
- package/dist/pages/SM-8.11/OperateHomePage.d.ts +26 -0
- package/dist/pages/SM-8.11/OperateHomePage.js +139 -0
- package/dist/pages/SM-8.11/OperateProcessInstancePage.d.ts +30 -0
- package/dist/pages/SM-8.11/OperateProcessInstancePage.js +276 -0
- package/dist/pages/SM-8.11/OperateProcessesPage.d.ts +38 -0
- package/dist/pages/SM-8.11/OperateProcessesPage.js +383 -0
- package/dist/pages/SM-8.11/OptimizeCollectionsPage.d.ts +17 -0
- package/dist/pages/SM-8.11/OptimizeCollectionsPage.js +62 -0
- package/dist/pages/SM-8.11/OptimizeDashboardPage.d.ts +14 -0
- package/dist/pages/SM-8.11/OptimizeDashboardPage.js +63 -0
- package/dist/pages/SM-8.11/OptimizeHomePage.d.ts +19 -0
- package/dist/pages/SM-8.11/OptimizeHomePage.js +76 -0
- package/dist/pages/SM-8.11/OptimizeReportPage.d.ts +50 -0
- package/dist/pages/SM-8.11/OptimizeReportPage.js +209 -0
- package/dist/pages/SM-8.11/PlayPage.d.ts +47 -0
- package/dist/pages/SM-8.11/PlayPage.js +389 -0
- package/dist/pages/SM-8.11/ProcessApplicationPage.d.ts +35 -0
- package/dist/pages/SM-8.11/ProcessApplicationPage.js +340 -0
- package/dist/pages/SM-8.11/SettingsPage.d.ts +12 -0
- package/dist/pages/SM-8.11/SettingsPage.js +34 -0
- package/dist/pages/SM-8.11/TaskDetailsPage.d.ts +70 -0
- package/dist/pages/SM-8.11/TaskDetailsPage.js +273 -0
- package/dist/pages/SM-8.11/TaskPanelPage.d.ts +29 -0
- package/dist/pages/SM-8.11/TaskPanelPage.js +320 -0
- package/dist/pages/SM-8.11/TaskProcessesPage.d.ts +15 -0
- package/dist/pages/SM-8.11/TaskProcessesPage.js +77 -0
- package/dist/pages/SM-8.11/UtilitiesPage.d.ts +38 -0
- package/dist/pages/SM-8.11/UtilitiesPage.js +573 -0
- package/dist/pages/SM-8.11/optimizeReportUtils.d.ts +6 -0
- package/dist/pages/SM-8.11/optimizeReportUtils.js +77 -0
- package/dist/tests/SM-8.11/cluster-management-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/cluster-management-user-flows.spec.js +48 -0
- package/dist/tests/SM-8.11/cluster-variables.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/cluster-variables.spec.js +226 -0
- package/dist/tests/SM-8.11/connectors-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/connectors-user-flows.spec.js +307 -0
- package/dist/tests/SM-8.11/document-handling-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/document-handling-user-flows.spec.js +174 -0
- package/dist/tests/SM-8.11/hto-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/hto-user-flows.spec.js +410 -0
- package/dist/tests/SM-8.11/hub-web-modeler-smoke.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/hub-web-modeler-smoke.spec.js +24 -0
- package/dist/tests/SM-8.11/identity-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/identity-user-flows.spec.js +220 -0
- package/dist/tests/SM-8.11/license-key-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/license-key-user-flows.spec.js +461 -0
- package/dist/tests/SM-8.11/login.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/login.spec.js +24 -0
- package/dist/tests/SM-8.11/migration-path-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/migration-path-user-flows.spec.js +263 -0
- package/dist/tests/SM-8.11/mt-enabled-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/mt-enabled-user-flows.spec.js +607 -0
- package/dist/tests/SM-8.11/optimize-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/optimize-user-flows.spec.js +505 -0
- package/dist/tests/SM-8.11/play.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/play.spec.js +144 -0
- package/dist/tests/SM-8.11/rba-enabled-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/rba-enabled-user-flows.spec.js +712 -0
- package/dist/tests/SM-8.11/smoke-tests.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/smoke-tests.spec.js +167 -0
- package/dist/tests/SM-8.11/test-setup.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/test-setup.spec.js +24 -0
- package/dist/tests/SM-8.11/topology-orchestration-smoke.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/topology-orchestration-smoke.spec.js +58 -0
- package/dist/tests/SM-8.11/web-modeler-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/web-modeler-user-flows.spec.js +223 -0
- package/package.json +1 -1
- 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 };
|