@camunda/e2e-test-suite 0.0.1031 → 0.0.1032
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.
|
@@ -32,6 +32,7 @@ declare class ModelerHomePage {
|
|
|
32
32
|
readonly formNameBreadcrumb: (formName: string) => Locator;
|
|
33
33
|
readonly buttonSkipCustomization: Locator;
|
|
34
34
|
constructor(page: Page);
|
|
35
|
+
dismissBlockingDialog(): Promise<void>;
|
|
35
36
|
dismissOverlays(): Promise<void>;
|
|
36
37
|
clickCreateNewProjectButton(): Promise<void>;
|
|
37
38
|
enterNewProjectName(name: string): Promise<void>;
|
|
@@ -96,7 +96,19 @@ class ModelerHomePage {
|
|
|
96
96
|
this.uploadFilesButton = page.getByRole('menuitem', { name: 'Upload files' });
|
|
97
97
|
this.messageBanner = page.locator('[data-test="close-top-banner"]');
|
|
98
98
|
this.dialog = page.getByRole('dialog');
|
|
99
|
-
|
|
99
|
+
// Both announcement modals hub stacks over the projects list dismiss
|
|
100
|
+
// through a "Got it…" footer button: the What's New modal's reads
|
|
101
|
+
// "Got it - Dismiss", and the migration notice ("Your projects have a new
|
|
102
|
+
// structure") reads just "Got it".
|
|
103
|
+
//
|
|
104
|
+
// Matched as a visible button rather than by role/name: the What's New
|
|
105
|
+
// modal stays mounted with `open={false}` after it is closed, so its
|
|
106
|
+
// dismiss button remains in the DOM but hidden, and a role/name match
|
|
107
|
+
// would resolve both and fail strict mode -- reporting "nothing to
|
|
108
|
+
// dismiss" while the migration notice is still covering the projects list.
|
|
109
|
+
this.closeButton = page
|
|
110
|
+
.locator('button:visible', { hasText: /^Got it/ })
|
|
111
|
+
.first();
|
|
100
112
|
this.optionsButton = page.getByRole('button', { name: 'Options' });
|
|
101
113
|
this.formNameBreadcrumb = (formName) => page.locator('button[data-test="breadcrumb-form"]', {
|
|
102
114
|
hasText: formName,
|
|
@@ -105,7 +117,39 @@ class ModelerHomePage {
|
|
|
105
117
|
name: 'Skip customization',
|
|
106
118
|
});
|
|
107
119
|
}
|
|
120
|
+
// An announcement dialog renders over the projects list and swallows every
|
|
121
|
+
// click on it: Playwright reports the project link as visible, enabled and
|
|
122
|
+
// stable, then times out with the modal overlay "intercepts pointer events".
|
|
123
|
+
// Pressing Escape blindly does not help -- Carbon only closes the dialog when
|
|
124
|
+
// focus is inside it, and focus sits on the body after a page load -- so
|
|
125
|
+
// close it via its own dismiss button and confirm it is gone.
|
|
126
|
+
//
|
|
127
|
+
// The dialog is mounted a beat after the projects list, so let the page
|
|
128
|
+
// settle before looking for it, and re-probe after each close: hub can stack
|
|
129
|
+
// the What's New modal and the migration notice, and closing one reveals the
|
|
130
|
+
// next.
|
|
131
|
+
async dismissBlockingDialog() {
|
|
132
|
+
await this.page
|
|
133
|
+
.waitForLoadState('networkidle', { timeout: 15000 })
|
|
134
|
+
.catch(() => false);
|
|
135
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
136
|
+
const isOpen = await this.closeButton
|
|
137
|
+
.waitFor({ state: 'visible', timeout: 10000 })
|
|
138
|
+
.then(() => true)
|
|
139
|
+
.catch(() => false);
|
|
140
|
+
if (!isOpen)
|
|
141
|
+
return;
|
|
142
|
+
await this.closeButton.click({ timeout: 5000 }).catch(() => { });
|
|
143
|
+
await this.closeButton
|
|
144
|
+
.waitFor({ state: 'hidden', timeout: 5000 })
|
|
145
|
+
.catch(() => false);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
108
148
|
async dismissOverlays() {
|
|
149
|
+
// Before the generic pass: clickMessageBanner and the loop below resolve
|
|
150
|
+
// `.or(...).first()` unions in DOM order, where the top banner comes first,
|
|
151
|
+
// so on their own they close the banner and leave the dialog open.
|
|
152
|
+
await this.dismissBlockingDialog();
|
|
109
153
|
for (const locator of [
|
|
110
154
|
this.closeButton,
|
|
111
155
|
this.page.getByRole('button', { name: 'Close' }).first(),
|
|
@@ -147,6 +191,11 @@ class ModelerHomePage {
|
|
|
147
191
|
if (await committedProjectName.first().isVisible()) {
|
|
148
192
|
return;
|
|
149
193
|
}
|
|
194
|
+
// The rename runs on a freshly created project, which is exactly when an
|
|
195
|
+
// announcement dialog mounts on top of it: it takes focus, the inline
|
|
196
|
+
// editor unmounts, and the click below then waits out its whole timeout
|
|
197
|
+
// on an element that never comes back.
|
|
198
|
+
await this.dismissOverlays();
|
|
150
199
|
await this.projectNameInput.first().click({ timeout: 15000 });
|
|
151
200
|
await this.projectNameInput.first().fill(name);
|
|
152
201
|
// Confirm the value actually landed in the edit field before committing;
|
|
@@ -397,12 +446,17 @@ class ModelerHomePage {
|
|
|
397
446
|
.catch(() => { });
|
|
398
447
|
}
|
|
399
448
|
}
|
|
449
|
+
// An announcement dialog covering the Modeler header makes every click on the
|
|
450
|
+
// org button bounce off the modal layer ("intercepts pointer events") until
|
|
451
|
+
// the timeout, so dismiss overlays before each attempt.
|
|
400
452
|
async clickOpenOrganizationsButton() {
|
|
401
|
-
await (0,
|
|
402
|
-
|
|
403
|
-
|
|
453
|
+
await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.openOrganizationsButton, {
|
|
454
|
+
preAction: async () => {
|
|
455
|
+
await this.dismissOverlays();
|
|
456
|
+
},
|
|
457
|
+
totalTimeout: 120000,
|
|
458
|
+
visibilityTimeout: 30000,
|
|
404
459
|
});
|
|
405
|
-
await this.openOrganizationsButton.click({ timeout: 30000 });
|
|
406
460
|
}
|
|
407
461
|
async createForm(formName) {
|
|
408
462
|
const maxRetries = 3;
|
|
@@ -429,24 +483,19 @@ class ModelerHomePage {
|
|
|
429
483
|
async clickCrossComponentProjectFolder() {
|
|
430
484
|
await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.crossComponentProjectFolder, {
|
|
431
485
|
visibilityTimeout: 60000,
|
|
432
|
-
|
|
486
|
+
// Widened from 180s: dismissing the announcement dialog costs up to ~25s
|
|
487
|
+
// of settle-and-probe per attempt, which at 180s left room for barely one
|
|
488
|
+
// retry after an intercepted click had already burnt its 60s.
|
|
489
|
+
totalTimeout: 240000,
|
|
433
490
|
maxRetries: 5,
|
|
434
491
|
preAction: async () => {
|
|
435
|
-
//
|
|
436
|
-
//
|
|
437
|
-
//
|
|
438
|
-
//
|
|
439
|
-
//
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
if ((await blockingModal.count()) > 0) {
|
|
443
|
-
await this.page.keyboard.press('Escape');
|
|
444
|
-
await (0, sleep_1.sleep)(1000);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
catch {
|
|
448
|
-
// No blocking modal to dismiss
|
|
449
|
-
}
|
|
492
|
+
// Dismiss the dialog BEFORE the banner: the Modeler home page can
|
|
493
|
+
// stack both at once, and clickMessageBanner resolves an
|
|
494
|
+
// `.or(...).first()` union in DOM order — the top banner comes first,
|
|
495
|
+
// so on its own it closes the banner and leaves the dialog open. The
|
|
496
|
+
// previous press-Escape-on-any-visible-modal handling could not close
|
|
497
|
+
// it at all: the migration notice ignores Escape.
|
|
498
|
+
await this.dismissBlockingDialog();
|
|
450
499
|
await this.clickMessageBanner();
|
|
451
500
|
},
|
|
452
501
|
postAction: async () => {
|
|
@@ -31,6 +31,7 @@ declare class ModelerHomePage {
|
|
|
31
31
|
readonly optionsButton: Locator;
|
|
32
32
|
readonly formNameBreadcrumb: (formName: string) => Locator;
|
|
33
33
|
constructor(page: Page);
|
|
34
|
+
dismissBlockingDialog(): Promise<void>;
|
|
34
35
|
dismissOverlays(): Promise<void>;
|
|
35
36
|
clickCreateNewProjectButton(): Promise<void>;
|
|
36
37
|
enterNewProjectName(name: string): Promise<void>;
|
|
@@ -100,13 +100,58 @@ class ModelerHomePage {
|
|
|
100
100
|
this.uploadFilesButton = page.getByRole('menuitem', { name: 'Upload files' });
|
|
101
101
|
this.messageBanner = page.locator('[data-test="close-top-banner"]');
|
|
102
102
|
this.dialog = page.getByRole('dialog');
|
|
103
|
-
|
|
103
|
+
// Both announcement modals hub stacks over the projects list dismiss
|
|
104
|
+
// through a "Got it…" footer button: the What's New modal's reads
|
|
105
|
+
// "Got it - Dismiss", and the migration notice ("Your projects have a new
|
|
106
|
+
// structure") reads just "Got it".
|
|
107
|
+
//
|
|
108
|
+
// Matched as a visible button rather than by role/name: the What's New
|
|
109
|
+
// modal stays mounted with `open={false}` after it is closed, so its
|
|
110
|
+
// dismiss button remains in the DOM but hidden, and a role/name match
|
|
111
|
+
// would resolve both and fail strict mode -- reporting "nothing to
|
|
112
|
+
// dismiss" while the migration notice is still covering the projects list.
|
|
113
|
+
this.closeButton = page
|
|
114
|
+
.locator('button:visible', { hasText: /^Got it/ })
|
|
115
|
+
.first();
|
|
104
116
|
this.optionsButton = page.getByRole('button', { name: 'Options' });
|
|
105
117
|
this.formNameBreadcrumb = (formName) => page.locator('button[data-test="breadcrumb-form"]', {
|
|
106
118
|
hasText: formName,
|
|
107
119
|
});
|
|
108
120
|
}
|
|
121
|
+
// An announcement dialog renders over the projects list and swallows every
|
|
122
|
+
// click on it: Playwright reports the project link as visible, enabled and
|
|
123
|
+
// stable, then times out with the modal overlay "intercepts pointer events".
|
|
124
|
+
// Pressing Escape blindly does not help -- Carbon only closes the dialog when
|
|
125
|
+
// focus is inside it, and focus sits on the body after a page load -- so
|
|
126
|
+
// close it via its own dismiss button and confirm it is gone.
|
|
127
|
+
//
|
|
128
|
+
// The dialog is mounted a beat after the projects list, so let the page
|
|
129
|
+
// settle before looking for it, and re-probe after each close: hub can stack
|
|
130
|
+
// the What's New modal and the migration notice, and closing one reveals the
|
|
131
|
+
// next.
|
|
132
|
+
async dismissBlockingDialog() {
|
|
133
|
+
await this.page
|
|
134
|
+
.waitForLoadState('networkidle', { timeout: 15000 })
|
|
135
|
+
.catch(() => false);
|
|
136
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
137
|
+
const isOpen = await this.closeButton
|
|
138
|
+
.waitFor({ state: 'visible', timeout: 10000 })
|
|
139
|
+
.then(() => true)
|
|
140
|
+
.catch(() => false);
|
|
141
|
+
if (!isOpen)
|
|
142
|
+
return;
|
|
143
|
+
await this.closeButton.click({ timeout: 5000 }).catch(() => { });
|
|
144
|
+
await this.closeButton
|
|
145
|
+
.waitFor({ state: 'hidden', timeout: 5000 })
|
|
146
|
+
.catch(() => false);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
109
149
|
async dismissOverlays() {
|
|
150
|
+
// Before the generic pass: the loop below closes whichever overlay it finds
|
|
151
|
+
// and moves on, so a single pass leaves a stacked announcement dialog open,
|
|
152
|
+
// and every caller that relies on dismissOverlays() to clear the page then
|
|
153
|
+
// clicks into it.
|
|
154
|
+
await this.dismissBlockingDialog();
|
|
110
155
|
for (const locator of [
|
|
111
156
|
this.closeButton,
|
|
112
157
|
this.page.getByRole('button', { name: 'Close' }).first(),
|
|
@@ -141,6 +186,11 @@ class ModelerHomePage {
|
|
|
141
186
|
if (await this.page.getByTitle(name, { exact: true }).isVisible()) {
|
|
142
187
|
return;
|
|
143
188
|
}
|
|
189
|
+
// The rename runs on a freshly created project, which is exactly when an
|
|
190
|
+
// announcement dialog mounts on top of it: it takes focus, the inline
|
|
191
|
+
// editor unmounts, and the click below then waits out its whole timeout
|
|
192
|
+
// on an element that never comes back.
|
|
193
|
+
await this.dismissBlockingDialog();
|
|
144
194
|
await this.projectNameInput.click({ timeout: 60000 });
|
|
145
195
|
await this.projectNameInput.fill(name);
|
|
146
196
|
await this.projectNameInput.press('Enter');
|
|
@@ -342,12 +392,18 @@ class ModelerHomePage {
|
|
|
342
392
|
await this.page.getByText('Home', { exact: true }).first().click();
|
|
343
393
|
}
|
|
344
394
|
}
|
|
395
|
+
// An announcement dialog covering the Modeler header makes every click on the
|
|
396
|
+
// org button bounce off the modal layer ("intercepts pointer events") until
|
|
397
|
+
// the timeout, so dismiss it before each attempt instead of waiting out a
|
|
398
|
+
// fixed sleep and hoping the header is clear.
|
|
345
399
|
async clickOpenOrganizationsButton() {
|
|
346
|
-
await (0,
|
|
347
|
-
|
|
348
|
-
|
|
400
|
+
await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.openOrganizationsButton, {
|
|
401
|
+
preAction: async () => {
|
|
402
|
+
await this.dismissBlockingDialog();
|
|
403
|
+
},
|
|
404
|
+
totalTimeout: 120000,
|
|
405
|
+
visibilityTimeout: 30000,
|
|
349
406
|
});
|
|
350
|
-
await this.openOrganizationsButton.click({ timeout: 30000 });
|
|
351
407
|
}
|
|
352
408
|
async createForm(formName) {
|
|
353
409
|
await this.clickDiagramTypeDropdown();
|
|
@@ -361,26 +417,19 @@ class ModelerHomePage {
|
|
|
361
417
|
async clickCrossComponentProjectFolder() {
|
|
362
418
|
await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.crossComponentProjectFolder, {
|
|
363
419
|
visibilityTimeout: 60000,
|
|
364
|
-
|
|
420
|
+
// Widened from 180s: dismissing the announcement dialog costs up to ~25s
|
|
421
|
+
// of settle-and-probe per attempt, which at 180s left room for barely one
|
|
422
|
+
// retry after an intercepted click had already burnt its 60s.
|
|
423
|
+
totalTimeout: 240000,
|
|
365
424
|
maxRetries: 5,
|
|
366
425
|
preAction: async () => {
|
|
367
|
-
// A Carbon modal (`.cds--modal.is-visible`) sometimes covers the
|
|
368
|
-
// project folder and intercepts pointer events. Escape it explicitly
|
|
369
|
-
// first — same pattern 8.7 already uses for the same symptom.
|
|
370
|
-
try {
|
|
371
|
-
const blockingModal = this.page.locator('.cds--modal.is-visible');
|
|
372
|
-
if ((await blockingModal.count()) > 0) {
|
|
373
|
-
await this.page.keyboard.press('Escape');
|
|
374
|
-
await (0, sleep_1.sleep)(1000);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
catch {
|
|
378
|
-
// No blocking modal to dismiss
|
|
379
|
-
}
|
|
380
426
|
// dismissOverlays, not clickMessageBanner: the latter resolves
|
|
381
427
|
// `messageBanner.or(closeButton).first()`, so with a top banner on
|
|
382
428
|
// screen it always picks the banner and leaves any dialog in place.
|
|
383
|
-
// dismissOverlays
|
|
429
|
+
// dismissOverlays closes the announcement dialog first, then walks
|
|
430
|
+
// every other overlay control in turn. The previous
|
|
431
|
+
// press-Escape-on-any-visible-modal handling could not close the
|
|
432
|
+
// migration notice at all: it ignores Escape.
|
|
384
433
|
await this.dismissOverlays();
|
|
385
434
|
},
|
|
386
435
|
postAction: async () => {
|