@camunda/e2e-test-suite 0.0.938 → 0.0.940
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.
|
@@ -121,11 +121,20 @@ class TaskDetailsPage {
|
|
|
121
121
|
async clickUnassignButton() {
|
|
122
122
|
await this.unassignButton.click();
|
|
123
123
|
}
|
|
124
|
+
// The "Task completed" toast is a transient confirmation: the v2 webapp's
|
|
125
|
+
// notification store auto-removes it after 5s, and it is never rendered at
|
|
126
|
+
// all when the refreshed task list deselects the completed task before the
|
|
127
|
+
// completion state machine reaches CompletionSucceeded (which is what the
|
|
128
|
+
// nightly trace showed - the task was completed, but no toast ever entered
|
|
129
|
+
// the DOM). Accept the task's own completed state as well: the details panel
|
|
130
|
+
// only renders "Completion date" once the task has one, so this confirms the
|
|
131
|
+
// completion rather than weakening the check.
|
|
124
132
|
async clickCompleteTaskButton() {
|
|
125
133
|
await this.completeTaskButton.click({ timeout: 60000 });
|
|
126
|
-
await (0, test_1.expect)(this.page
|
|
127
|
-
|
|
128
|
-
|
|
134
|
+
await (0, test_1.expect)(this.page
|
|
135
|
+
.getByText('Task completed')
|
|
136
|
+
.or(this.detailsPanel.getByText('Completion date'))
|
|
137
|
+
.first()).toBeVisible({ timeout: 60000 });
|
|
129
138
|
await this.page.reload();
|
|
130
139
|
}
|
|
131
140
|
async clickAddVariableButton() {
|
|
@@ -249,7 +249,11 @@ async function assertPageTextWithRetry(page, text, notVisible = false, timeout =
|
|
|
249
249
|
}
|
|
250
250
|
exports.assertPageTextWithRetry = assertPageTextWithRetry;
|
|
251
251
|
async function completeTaskWithRetry(taskPanelPage, taskDetailsPage, taskName, taskPriority, maxRetries = 3) {
|
|
252
|
+
const openTask = taskPanelPage.availableTasks
|
|
253
|
+
.getByText(taskName, { exact: true })
|
|
254
|
+
.first();
|
|
252
255
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
256
|
+
let completionRequested = false;
|
|
253
257
|
try {
|
|
254
258
|
await taskPanelPage.openTask(taskName);
|
|
255
259
|
await (0, test_1.expect)(taskDetailsPage.detailsInfo.getByText(taskName, { exact: true })).toBeVisible();
|
|
@@ -257,11 +261,27 @@ async function completeTaskWithRetry(taskPanelPage, taskDetailsPage, taskName, t
|
|
|
257
261
|
await taskDetailsPage.clickAssignToMeButton();
|
|
258
262
|
}
|
|
259
263
|
await (0, test_1.expect)(taskDetailsPage.detailsPanel.getByText(taskPriority)).toBeVisible();
|
|
264
|
+
completionRequested = true;
|
|
260
265
|
await taskDetailsPage.clickCompleteTaskButton();
|
|
261
|
-
await (0, test_1.expect)(
|
|
266
|
+
await (0, test_1.expect)(openTask).not.toBeVisible({ timeout: 30000 });
|
|
262
267
|
return;
|
|
263
268
|
}
|
|
264
269
|
catch (error) {
|
|
270
|
+
// Once the Complete Task click has gone out, the task may well have been
|
|
271
|
+
// completed even though its confirmation was missed. Retrying blindly
|
|
272
|
+
// re-enters openTask, which waits 120s per attempt for a task that no
|
|
273
|
+
// longer exists and exhausts the 12 min test timeout, so first check the
|
|
274
|
+
// durable signal: the task leaving the open task list.
|
|
275
|
+
if (completionRequested) {
|
|
276
|
+
const taskGone = await openTask
|
|
277
|
+
.waitFor({ state: 'hidden', timeout: 30000 })
|
|
278
|
+
.then(() => true)
|
|
279
|
+
.catch(() => false);
|
|
280
|
+
if (taskGone) {
|
|
281
|
+
console.warn(`Confirmation for completing task ${taskName} was missed, but the task left the open task list - treating it as completed.`);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
265
285
|
if (attempt < maxRetries - 1) {
|
|
266
286
|
console.warn(`Attempt ${attempt + 1} failed for completing task ${taskName}. Retrying...`);
|
|
267
287
|
await (0, sleep_1.sleep)(2000);
|
|
@@ -74,6 +74,8 @@ declare class ClusterDetailsPage {
|
|
|
74
74
|
selectEmailRadioButton(): Promise<void>;
|
|
75
75
|
clickCreateAlertButton(): Promise<void>;
|
|
76
76
|
deleteAlerts(): Promise<void>;
|
|
77
|
+
private confirmDelete;
|
|
78
|
+
private dismissDeleteDialog;
|
|
77
79
|
private doDelete;
|
|
78
80
|
assertComponentsHealth(components?: string[]): Promise<void>;
|
|
79
81
|
createAPIClientAndReturnVariables(name: string, setEnvVariables?: boolean): Promise<{
|
|
@@ -404,6 +404,45 @@ class ClusterDetailsPage {
|
|
|
404
404
|
async deleteAlerts() {
|
|
405
405
|
await this.doDelete(this.dialog, 'Alerts');
|
|
406
406
|
}
|
|
407
|
+
// The confirmation modal only closes once the DELETE request resolves. When
|
|
408
|
+
// that call fails the modal stays open on an inline "We're having trouble
|
|
409
|
+
// deleting the alert / fetch error" banner, and Carbon's modal overlay then
|
|
410
|
+
// intercepts pointer events for the whole page — so the next row's Delete
|
|
411
|
+
// click can never land (the nightly trace shows
|
|
412
|
+
// `DELETE /api/orgs/<org>/clusters/<id>/alerts/<uuid>` ending in
|
|
413
|
+
// net::ERR_FAILED, followed by a 60s click timeout on the row button). Retry
|
|
414
|
+
// the confirmation while the modal is up, and guarantee it is gone before the
|
|
415
|
+
// caller touches the list again.
|
|
416
|
+
async confirmDelete(dialog) {
|
|
417
|
+
const maxAttempts = 3;
|
|
418
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
419
|
+
await this.deleteSubButton.click({ timeout: 60000 });
|
|
420
|
+
await (0, test_1.expect)(this.page.getByText('Deleting...')).not.toBeVisible({
|
|
421
|
+
timeout: 60000,
|
|
422
|
+
});
|
|
423
|
+
const closed = await dialog
|
|
424
|
+
.waitFor({ state: 'hidden', timeout: 30000 })
|
|
425
|
+
.then(() => true)
|
|
426
|
+
.catch(() => false);
|
|
427
|
+
if (closed) {
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
console.warn(`Confirmation dialog still open after delete attempt ${attempt} of ${maxAttempts}`);
|
|
431
|
+
}
|
|
432
|
+
await this.dismissDeleteDialog(dialog);
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
async dismissDeleteDialog(dialog) {
|
|
436
|
+
const cancelButton = dialog.getByRole('button', { name: 'Cancel' });
|
|
437
|
+
const hasCancel = await cancelButton.isVisible().catch(() => false);
|
|
438
|
+
if (hasCancel) {
|
|
439
|
+
await cancelButton.click({ timeout: 30000 });
|
|
440
|
+
}
|
|
441
|
+
else {
|
|
442
|
+
await this.page.keyboard.press('Escape');
|
|
443
|
+
}
|
|
444
|
+
await dialog.waitFor({ state: 'hidden', timeout: 30000 });
|
|
445
|
+
}
|
|
407
446
|
async doDelete(dialog, text) {
|
|
408
447
|
try {
|
|
409
448
|
await (0, test_1.expect)(this.deleteButton.first()).toBeVisible({
|
|
@@ -414,9 +453,12 @@ class ClusterDetailsPage {
|
|
|
414
453
|
return; //No items found in the list
|
|
415
454
|
}
|
|
416
455
|
try {
|
|
417
|
-
let
|
|
418
|
-
|
|
419
|
-
|
|
456
|
+
let remaining = await this.deleteButton.count();
|
|
457
|
+
// A failed DELETE leaves the row in place; retry it from a clean state
|
|
458
|
+
// instead of giving up on the first stalled round.
|
|
459
|
+
let stalledRounds = 0;
|
|
460
|
+
while (remaining > 0 && stalledRounds < 3) {
|
|
461
|
+
const deleteButton = this.deleteButton.first();
|
|
420
462
|
if (await deleteButton.isVisible()) {
|
|
421
463
|
await deleteButton.click({ timeout: 60000 });
|
|
422
464
|
// Handle confirmation dialog if it appears; some UI states delete without one
|
|
@@ -424,17 +466,18 @@ class ClusterDetailsPage {
|
|
|
424
466
|
.isVisible({ timeout: 5000 })
|
|
425
467
|
.catch(() => false);
|
|
426
468
|
if (dialogVisible) {
|
|
427
|
-
await this.
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
});
|
|
469
|
+
if (!(await this.confirmDelete(dialog))) {
|
|
470
|
+
console.warn(`Retrying ${text} deletion after a failed request`);
|
|
471
|
+
}
|
|
431
472
|
}
|
|
432
473
|
else {
|
|
433
474
|
await (0, sleep_1.sleep)(2000);
|
|
434
475
|
}
|
|
435
476
|
}
|
|
436
477
|
await (0, sleep_1.sleep)(3000);
|
|
437
|
-
|
|
478
|
+
const left = await this.deleteButton.count();
|
|
479
|
+
stalledRounds = left < remaining ? 0 : stalledRounds + 1;
|
|
480
|
+
remaining = left;
|
|
438
481
|
}
|
|
439
482
|
await (0, test_1.expect)(this.deleteButton).not.toBeVisible({
|
|
440
483
|
timeout: 30000,
|
|
@@ -152,17 +152,23 @@ _8_8_1.test.describe('Console User Flow Tests @tasklistV2', () => {
|
|
|
152
152
|
await homePage.clickClusters();
|
|
153
153
|
await clusterPage.clickClusterLink(alertFlowClusterName);
|
|
154
154
|
});
|
|
155
|
+
// Console's alerts panel renders its "Alerts keep you informed" empty state
|
|
156
|
+
// whenever the cluster-detail fetch fails (the nightly trace shows
|
|
157
|
+
// `/api/orgs/<org>/clusters/<id>*` calls ending in net::ERR_FAILED), so the
|
|
158
|
+
// alert rows can be missing for a while even though the alert exists and has
|
|
159
|
+
// already triggered. A reload re-issues those fetches, so give the recovery
|
|
160
|
+
// enough reload cycles instead of only three.
|
|
155
161
|
await _8_8_1.test.step('Verify Alert Count Increases to 1 After Trigger', async () => {
|
|
156
162
|
await clusterDetailsPage.clickAlertsTab();
|
|
157
163
|
await (0, test_1.expect)(async () => {
|
|
158
164
|
await page.reload();
|
|
159
165
|
await (0, test_1.expect)(clusterDetailsPage.alertsList.first()).toBeVisible({
|
|
160
|
-
timeout:
|
|
166
|
+
timeout: 20000,
|
|
161
167
|
});
|
|
162
168
|
(0, test_1.expect)(await clusterDetailsPage.alertsList.count()).toBeGreaterThan(1);
|
|
163
169
|
await (0, test_1.expect)(clusterDetailsPage.alertsList.nth(0)).toContainText(`Email`);
|
|
164
170
|
await clusterDetailsPage.assertAlertText('Amount triggered1');
|
|
165
|
-
}).toPass({ timeout:
|
|
171
|
+
}).toPass({ timeout: 240000 });
|
|
166
172
|
});
|
|
167
173
|
await _8_8_1.test.step('Verify Alerts Received via Email for Incident', async () => {
|
|
168
174
|
await (0, sleep_1.sleep)(60000);
|
|
@@ -191,16 +197,17 @@ _8_8_1.test.describe('Console User Flow Tests @tasklistV2', () => {
|
|
|
191
197
|
await homePage.clickClusters();
|
|
192
198
|
await clusterPage.clickClusterLink(alertFlowClusterName);
|
|
193
199
|
});
|
|
200
|
+
// Same empty-state recovery as the first count assertion above.
|
|
194
201
|
await _8_8_1.test.step('Verify Alert Count Increases to 3 After Trigger', async () => {
|
|
195
202
|
await clusterDetailsPage.clickAlertsTab();
|
|
196
203
|
await (0, test_1.expect)(async () => {
|
|
197
204
|
await page.reload();
|
|
198
205
|
await (0, test_1.expect)(clusterDetailsPage.alertsList.first()).toBeVisible({
|
|
199
|
-
timeout:
|
|
206
|
+
timeout: 20000,
|
|
200
207
|
});
|
|
201
208
|
(0, test_1.expect)(await clusterDetailsPage.alertsList.count()).toBeGreaterThan(1);
|
|
202
209
|
await clusterDetailsPage.assertAlertText('Amount triggered3');
|
|
203
|
-
}).toPass({ timeout:
|
|
210
|
+
}).toPass({ timeout: 240000 });
|
|
204
211
|
});
|
|
205
212
|
await _8_8_1.test.step('Verify Alerts Received via Email for Incident', async () => {
|
|
206
213
|
await (0, sleep_1.sleep)(60000);
|