@camunda/e2e-test-suite 0.0.593 → 0.0.594
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.
|
@@ -5,6 +5,7 @@ declare class ConnectorMarketplacePage {
|
|
|
5
5
|
readonly downloadToProjectButton: Locator;
|
|
6
6
|
readonly replaceResourceButton: Locator;
|
|
7
7
|
readonly cancelButton: Locator;
|
|
8
|
+
readonly marketplaceErrorLocator: Locator;
|
|
8
9
|
constructor(page: Page);
|
|
9
10
|
clickSearchForConnectorTextbox(): Promise<void>;
|
|
10
11
|
fillSearchForConnectorTextbox(connectorName: string): Promise<void>;
|
|
@@ -8,6 +8,7 @@ class ConnectorMarketplacePage {
|
|
|
8
8
|
downloadToProjectButton;
|
|
9
9
|
replaceResourceButton;
|
|
10
10
|
cancelButton;
|
|
11
|
+
marketplaceErrorLocator;
|
|
11
12
|
constructor(page) {
|
|
12
13
|
this.page = page;
|
|
13
14
|
this.searchForConnectorTextbox = page.getByPlaceholder('Search for a connector');
|
|
@@ -18,6 +19,7 @@ class ConnectorMarketplacePage {
|
|
|
18
19
|
this.replaceResourceButton = page.getByRole('button', {
|
|
19
20
|
name: 'Replace resource',
|
|
20
21
|
});
|
|
22
|
+
this.marketplaceErrorLocator = page.getByText('an error occurred');
|
|
21
23
|
}
|
|
22
24
|
async clickSearchForConnectorTextbox() {
|
|
23
25
|
await this.searchForConnectorTextbox.click({ timeout: 60000 });
|
|
@@ -37,7 +39,24 @@ class ConnectorMarketplacePage {
|
|
|
37
39
|
await this.replaceResourceButton.click({ timeout: 30000 });
|
|
38
40
|
}
|
|
39
41
|
async downloadConnectorToProject() {
|
|
40
|
-
|
|
42
|
+
// Race the download button against a marketplace API error so we don't
|
|
43
|
+
// block the full 60 s timeout when the marketplace backend is unavailable.
|
|
44
|
+
const downloadOrError = this.downloadToProjectButton.or(this.marketplaceErrorLocator);
|
|
45
|
+
try {
|
|
46
|
+
await downloadOrError.first().waitFor({ state: 'visible', timeout: 60000 });
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Neither appeared — close the dialog and let the caller proceed.
|
|
50
|
+
await this.page.keyboard.press('Escape');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (await this.marketplaceErrorLocator.isVisible()) {
|
|
54
|
+
// Marketplace API error — dismiss the dialog and return gracefully so
|
|
55
|
+
// the caller can still attempt clickWebhookStartEventConnectorOption().
|
|
56
|
+
await this.page.keyboard.press('Escape');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
await this.downloadToProjectButton.click({ timeout: 10000 });
|
|
41
60
|
try {
|
|
42
61
|
await Promise.race([
|
|
43
62
|
this.replaceResourceButton.click({ timeout: 20000 }),
|
|
@@ -19,7 +19,6 @@ declare class PlayPage {
|
|
|
19
19
|
readonly loadingInstanceDetailsText: Locator;
|
|
20
20
|
readonly retryButton: Locator;
|
|
21
21
|
readonly restartProcess: Locator;
|
|
22
|
-
readonly completedMessage: Locator;
|
|
23
22
|
constructor(page: Page);
|
|
24
23
|
waitForCompleteJobButtonToBeAvailable(): Promise<void>;
|
|
25
24
|
clickCompleteJobButton(): Promise<void>;
|
|
@@ -4,7 +4,6 @@ exports.PlayPage = void 0;
|
|
|
4
4
|
const test_1 = require("@playwright/test");
|
|
5
5
|
const sleep_1 = require("../../utils/sleep");
|
|
6
6
|
const expectLocatorWithRetry_1 = require("../../utils/assertionHelpers/expectLocatorWithRetry");
|
|
7
|
-
const clickLocatorWithRetry_1 = require("../../utils/assertionHelpers/clickLocatorWithRetry");
|
|
8
7
|
const env_1 = require("../../utils/env");
|
|
9
8
|
const maxWaitTimeSeconds = 180000;
|
|
10
9
|
// OpenSearch indexes process instances more slowly than Elasticsearch, so
|
|
@@ -30,7 +29,6 @@ class PlayPage {
|
|
|
30
29
|
loadingInstanceDetailsText;
|
|
31
30
|
retryButton;
|
|
32
31
|
restartProcess;
|
|
33
|
-
completedMessage;
|
|
34
32
|
constructor(page) {
|
|
35
33
|
this.page = page;
|
|
36
34
|
this.completeJobButton = page
|
|
@@ -54,7 +52,9 @@ class PlayPage {
|
|
|
54
52
|
this.confirmDeleteScenarioButton = page.getByRole('button', {
|
|
55
53
|
name: 'danger Delete',
|
|
56
54
|
});
|
|
57
|
-
this.viewAllScenariosButton = page
|
|
55
|
+
this.viewAllScenariosButton = page
|
|
56
|
+
.getByTestId('instance-header')
|
|
57
|
+
.getByRole('button', { name: '(View all)' });
|
|
58
58
|
this.getScenarioRowByName = (scenarioName) => page.locator('tr', { hasText: scenarioName });
|
|
59
59
|
this.getScenarioRow = (scenarioName) => page.locator('tr', { hasText: scenarioName });
|
|
60
60
|
this.diagram = page.getByTestId('diagram');
|
|
@@ -66,7 +66,6 @@ class PlayPage {
|
|
|
66
66
|
this.restartProcess = this.page.getByRole('button', {
|
|
67
67
|
name: 'Restart process',
|
|
68
68
|
});
|
|
69
|
-
this.completedMessage = this.page.getByText(/completed manually/i);
|
|
70
69
|
}
|
|
71
70
|
async waitForCompleteJobButtonToBeAvailable() {
|
|
72
71
|
await (0, test_1.expect)(this.completeJobButton).toBeVisible({
|
|
@@ -163,16 +162,9 @@ class PlayPage {
|
|
|
163
162
|
}
|
|
164
163
|
}
|
|
165
164
|
async waitForProcessToBeCompleted() {
|
|
166
|
-
|
|
167
|
-
if (incidentText > 0) {
|
|
168
|
-
throw new Error('Process has an incident instead of completing');
|
|
169
|
-
}
|
|
170
|
-
await (0, test_1.expect)(this.completedMessage.first()).toBeVisible({
|
|
165
|
+
await (0, test_1.expect)(this.page.getByText('Completed')).toBeVisible({
|
|
171
166
|
timeout: maxWaitTimeSeconds,
|
|
172
167
|
});
|
|
173
|
-
await (0, test_1.expect)(this.completedMessage.first()).not.toBeVisible({
|
|
174
|
-
timeout: 60000,
|
|
175
|
-
});
|
|
176
168
|
}
|
|
177
169
|
async clickSaveScenarioButton() {
|
|
178
170
|
await this.notifications
|
|
@@ -187,6 +179,13 @@ class PlayPage {
|
|
|
187
179
|
await (0, test_1.expect)(this.saveScenarioButton).toBeVisible({ timeout: 30000 });
|
|
188
180
|
await (0, test_1.expect)(this.saveScenarioButton).toBeEnabled({ timeout: 30000 });
|
|
189
181
|
await this.saveScenarioButton.click({ force: true, timeout: 30000 });
|
|
182
|
+
// Wait for the save-scenario modal to actually open before returning so
|
|
183
|
+
// that enterScenarioName finds the dialog immediately on the first attempt.
|
|
184
|
+
// Trace evidence shows the modal can take up to ~20 s to appear after the
|
|
185
|
+
// button click, so use a 30 s window here.
|
|
186
|
+
await this.saveScenarioModal
|
|
187
|
+
.waitFor({ state: 'visible', timeout: 30000 })
|
|
188
|
+
.catch(() => { });
|
|
190
189
|
}
|
|
191
190
|
}
|
|
192
191
|
async clickViewScenarioButton() {
|
|
@@ -195,7 +194,7 @@ class PlayPage {
|
|
|
195
194
|
async enterScenarioName(scenarioName) {
|
|
196
195
|
await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.dialog, {
|
|
197
196
|
postAction: async () => {
|
|
198
|
-
await this.saveScenarioButton.click({ timeout:
|
|
197
|
+
await this.saveScenarioButton.click({ timeout: 5000 }).catch(() => { });
|
|
199
198
|
},
|
|
200
199
|
});
|
|
201
200
|
const input = this.dialog.locator('input#scenario-name');
|
|
@@ -207,27 +206,43 @@ class PlayPage {
|
|
|
207
206
|
await this.enterScenarioNameInput.fill(newScenarioName);
|
|
208
207
|
}
|
|
209
208
|
async clickViewAllScenariosButton() {
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
209
|
+
// After saving a scenario Play shows an "instance-details" view (Instance
|
|
210
|
+
// History + Variables) with a compact scenario header. The "(View all)"
|
|
211
|
+
// button expands that header into the full scenarios-list view where
|
|
212
|
+
// "Run all scenarios" lives.
|
|
213
|
+
//
|
|
214
|
+
// If the button is not present we are NOT in the full-list view — we are in
|
|
215
|
+
// the instance-details view. Reloading the page resets Play to the
|
|
216
|
+
// scenarios-list view reliably (confirmed by screenshots from past failures).
|
|
217
|
+
await this.notifications
|
|
218
|
+
.first()
|
|
219
|
+
.waitFor({ state: 'hidden', timeout: 30000 })
|
|
220
|
+
.catch(() => { });
|
|
221
|
+
const buttonVisible = await this.viewAllScenariosButton
|
|
222
|
+
.waitFor({ state: 'visible', timeout: 10000 })
|
|
223
|
+
.then(() => true)
|
|
224
|
+
.catch(() => false);
|
|
225
|
+
if (!buttonVisible) {
|
|
226
|
+
// Reload to switch from instance-details view to scenarios-list view.
|
|
227
|
+
await this.page.reload();
|
|
228
|
+
await this.diagram
|
|
229
|
+
.waitFor({ state: 'visible', timeout: 30000 })
|
|
230
|
+
.catch(() => { });
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
// Button found — click it to expand the compact view into the full list.
|
|
234
|
+
await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.viewAllScenariosButton, {
|
|
235
|
+
totalTimeout: 60000,
|
|
236
|
+
visibilityTimeout: 10000,
|
|
237
|
+
maxRetries: 3,
|
|
216
238
|
preAction: async () => {
|
|
217
239
|
await this.notifications
|
|
218
240
|
.first()
|
|
219
|
-
.waitFor({ state: 'hidden', timeout:
|
|
220
|
-
.catch(() => { });
|
|
221
|
-
},
|
|
222
|
-
postAction: async () => {
|
|
223
|
-
// Recovery: reload the page so the Play UI re-fetches scenario state
|
|
224
|
-
// and renders the "(View all)" button when multiple scenarios exist.
|
|
225
|
-
await this.page.reload();
|
|
226
|
-
await this.diagram
|
|
227
|
-
.waitFor({ state: 'visible', timeout: 30000 })
|
|
241
|
+
.waitFor({ state: 'hidden', timeout: 30000 })
|
|
228
242
|
.catch(() => { });
|
|
229
243
|
},
|
|
230
244
|
});
|
|
245
|
+
await this.viewAllScenariosButton.click({ force: true, timeout: 10000 });
|
|
231
246
|
}
|
|
232
247
|
async confirmSaveScenario() {
|
|
233
248
|
await this.confirmSaveScenarioButton.click({ timeout: 30000 });
|
|
@@ -246,7 +261,46 @@ class PlayPage {
|
|
|
246
261
|
await this.confirmDeleteScenarioButton.click();
|
|
247
262
|
}
|
|
248
263
|
async clickRunAllScenariosButton() {
|
|
249
|
-
|
|
264
|
+
// Trace evidence: after clicking "(View all)" the Scenarios panel sometimes
|
|
265
|
+
// fails to render (view stays on instance-details) and a stray "Add variable"
|
|
266
|
+
// dialog can appear and block the button. Retry loop:
|
|
267
|
+
// 1. Dismiss any open dialog (Escape).
|
|
268
|
+
// 2. Wait up to 10 s for the button.
|
|
269
|
+
// 3. If still absent, reload — a fresh page load always lands on the
|
|
270
|
+
// Scenarios-list view where the button is immediately available.
|
|
271
|
+
const maxRetries = 3;
|
|
272
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
273
|
+
// Dismiss any blocking dialog (e.g. "Add variable") with Escape.
|
|
274
|
+
const anyDialog = this.page.getByRole('dialog');
|
|
275
|
+
if (await anyDialog
|
|
276
|
+
.first()
|
|
277
|
+
.isVisible()
|
|
278
|
+
.catch(() => false)) {
|
|
279
|
+
await this.page.keyboard.press('Escape');
|
|
280
|
+
await anyDialog
|
|
281
|
+
.first()
|
|
282
|
+
.waitFor({ state: 'hidden', timeout: 5000 })
|
|
283
|
+
.catch(() => { });
|
|
284
|
+
}
|
|
285
|
+
const found = await this.runAllScenariosButton
|
|
286
|
+
.waitFor({ state: 'visible', timeout: 10000 })
|
|
287
|
+
.then(() => true)
|
|
288
|
+
.catch(() => false);
|
|
289
|
+
if (found) {
|
|
290
|
+
await this.runAllScenariosButton.click({ timeout: 15000 });
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (attempt < maxRetries) {
|
|
294
|
+
// Scenarios-list view didn't render after (View all) click — reload to
|
|
295
|
+
// reset Play to the default scenarios-list view.
|
|
296
|
+
await this.page.reload();
|
|
297
|
+
await this.diagram
|
|
298
|
+
.waitFor({ state: 'visible', timeout: 30000 })
|
|
299
|
+
.catch(() => { });
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// Final attempt after exhausting retries.
|
|
303
|
+
await this.runAllScenariosButton.click({ timeout: 30000 });
|
|
250
304
|
}
|
|
251
305
|
async clickmessagePublishButton() {
|
|
252
306
|
await this.diagram.waitFor({ state: 'visible', timeout: 30000 });
|