@camunda/e2e-test-suite 0.0.1030 → 0.0.1031
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.
|
@@ -40,11 +40,17 @@ class Authorization {
|
|
|
40
40
|
name,
|
|
41
41
|
exact: true,
|
|
42
42
|
});
|
|
43
|
+
// The Owner field is no longer a Carbon ComboBox labelled "Owner".
|
|
44
|
+
// Identity replaced it with an entity-search dropdown
|
|
45
|
+
// (identity/client/src/components/form/EntitySearchSingleSelect.tsx ->
|
|
46
|
+
// EntitySearchDropdown -> DropdownSearch), which renders a bare
|
|
47
|
+
// `FormLabel` plus a Carbon `Search` input carrying only the
|
|
48
|
+
// "Search by owner ID" placeholder — nothing in the modal exposes the
|
|
49
|
+
// accessible name "Owner" any more, so the old combobox click waited out
|
|
50
|
+
// its full timeout. Anchor on the placeholder, which is what the
|
|
51
|
+
// component actually renders for every searchable owner type.
|
|
43
52
|
this.createAuthorizationOwnerComboBox =
|
|
44
|
-
this.createAuthorizationModal.
|
|
45
|
-
name: 'Owner',
|
|
46
|
-
exact: true,
|
|
47
|
-
});
|
|
53
|
+
this.createAuthorizationModal.getByPlaceholder('Search by owner ID');
|
|
48
54
|
this.createAuthorizationOwnerOption = (name) => this.createAuthorizationModal.getByRole('option', {
|
|
49
55
|
name,
|
|
50
56
|
exact: true,
|
|
@@ -90,33 +96,30 @@ class Authorization {
|
|
|
90
96
|
await (0, test_1.expect)(this.createAuthorizationModal).toBeVisible();
|
|
91
97
|
await this.createAuthorizationOwnerTypeCombobox.click();
|
|
92
98
|
await this.createAuthorizationOwnerTypeOption(authorization.ownerType).click();
|
|
93
|
-
await this.createAuthorizationOwnerComboBox.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
//
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
// 26632778543 shows the input value populated but Resource type
|
|
105
|
-
// still disabled). Scoping to the Carbon menu-item class — the same
|
|
106
|
-
// approach used in the product repo's qa-suite
|
|
107
|
-
// (camunda/camunda qa/c8-orchestration-cluster-e2e-test-suite/
|
|
108
|
-
// pages/IdentityAuthorizationsPage.ts selectAuthorizationOwner) —
|
|
109
|
-
// selects the actual Downshift menu item and fires the handler.
|
|
110
|
-
const ownerSearchInput = this.createAuthorizationModal.getByPlaceholder('Search by owner ID');
|
|
111
|
-
if (await ownerSearchInput.isVisible({ timeout: 5000 }).catch(() => false)) {
|
|
112
|
-
await ownerSearchInput.fill(authorization.ownerId);
|
|
113
|
-
}
|
|
99
|
+
await (0, test_1.expect)(this.createAuthorizationOwnerComboBox).toBeVisible({
|
|
100
|
+
timeout: 30000,
|
|
101
|
+
});
|
|
102
|
+
await this.createAuthorizationOwnerComboBox.fill(authorization.ownerId);
|
|
103
|
+
// The dropdown renders results only while the search box holds text, and
|
|
104
|
+
// the list is fetched from the server on a debounce rather than filtered
|
|
105
|
+
// client-side, so the matching entry appears a beat later. Clicking the
|
|
106
|
+
// Carbon menu item is what commits the selection: DropdownSearch wires
|
|
107
|
+
// `onSelect` to `ListBox.MenuItem`'s onClick
|
|
108
|
+
// (identity/client/src/components/form/DropdownSearch.tsx), and that is
|
|
109
|
+
// the only path that promotes an entry into the form's owner value.
|
|
114
110
|
const ownerMenuItem = this.createAuthorizationModal
|
|
115
111
|
.locator('.cds--list-box__menu-item')
|
|
116
112
|
.filter({ hasText: authorization.ownerId })
|
|
117
113
|
.first();
|
|
118
114
|
await (0, test_1.expect)(ownerMenuItem).toBeVisible({ timeout: 60000 });
|
|
119
115
|
await ownerMenuItem.click({ timeout: 20000 });
|
|
116
|
+
// A committed selection swaps the search box for a filter Tag
|
|
117
|
+
// (EntitySearchSingleSelect renders one or the other), so the box going
|
|
118
|
+
// away is how we know the owner actually landed on the form rather than
|
|
119
|
+
// the click being swallowed.
|
|
120
|
+
await (0, test_1.expect)(this.createAuthorizationOwnerComboBox).toBeHidden({
|
|
121
|
+
timeout: 15000,
|
|
122
|
+
});
|
|
120
123
|
await this.selectResourceTypeFromDialog(authorization.resourceType);
|
|
121
124
|
await this.createAuthorizationResourceIdField.fill(authorization.resourceId);
|
|
122
125
|
for (const permission of authorization.accessPermissions) {
|
|
@@ -739,8 +739,13 @@ class ModelerCreatePage {
|
|
|
739
739
|
async fillNamedInput(name) {
|
|
740
740
|
await this.nameInput.fill(name, { timeout: 60000 });
|
|
741
741
|
}
|
|
742
|
+
// 10s was too tight for the first click after the editor opens: the button
|
|
743
|
+
// is already resolved, visible and stable, and the click then waits out its
|
|
744
|
+
// whole budget on the actionability check while the bpmn-js canvas is still
|
|
745
|
+
// settling ("done scrolling" is the last line before the timeout). Every
|
|
746
|
+
// other breadcrumb interaction in this file budgets 30-60s; match them.
|
|
742
747
|
async clickDiagramBreadcrumb() {
|
|
743
|
-
await this.diagramBreadcrumb.click({ timeout:
|
|
748
|
+
await this.diagramBreadcrumb.click({ timeout: 60000 });
|
|
744
749
|
}
|
|
745
750
|
// Opening the breadcrumb menu leaves the pointer on the breadcrumb button,
|
|
746
751
|
// and hub renders that button's truncated-name tooltip on top of the menu,
|
|
@@ -105,7 +105,20 @@ class ModelerHomePage {
|
|
|
105
105
|
this.uploadFilesButton = page.getByRole('menuitem', { name: 'Upload files' });
|
|
106
106
|
this.messageBanner = page.locator('[data-test="close-top-banner"]');
|
|
107
107
|
this.dialog = page.getByRole('dialog');
|
|
108
|
-
|
|
108
|
+
// Both announcement modals hub stacks over the projects list dismiss
|
|
109
|
+
// through a "Got it…" footer button: the What's New modal's reads
|
|
110
|
+
// "Got it - Dismiss", and the migration notice added in camunda-hub
|
|
111
|
+
// #27819 ("Your projects have a new structure") reads just "Got it".
|
|
112
|
+
//
|
|
113
|
+
// Matched as a visible button rather than by role/name: the What's New
|
|
114
|
+
// modal stays mounted with `open={false}` after it is closed, so its
|
|
115
|
+
// dismiss button remains in the DOM but hidden, and an `.or(...).first()`
|
|
116
|
+
// union would resolve to that hidden button in DOM order and report
|
|
117
|
+
// "nothing to dismiss" while the migration notice is still covering the
|
|
118
|
+
// projects list.
|
|
119
|
+
this.closeButton = page
|
|
120
|
+
.locator('button:visible', { hasText: /^Got it/ })
|
|
121
|
+
.first();
|
|
109
122
|
this.optionsButton = page.getByRole('button', { name: 'Options' });
|
|
110
123
|
this.formNameBreadcrumb = (formName) => page.locator('button[data-test="breadcrumb-form"]', {
|
|
111
124
|
hasText: formName,
|
|
@@ -120,6 +133,12 @@ class ModelerHomePage {
|
|
|
120
133
|
this.renameMenuItem = page.getByRole('menuitem', { name: 'Rename' });
|
|
121
134
|
}
|
|
122
135
|
async dismissOverlays() {
|
|
136
|
+
// Before the generic pass, for the same reason
|
|
137
|
+
// clickCrossComponentProjectFolder dismisses the dialog first: the loop
|
|
138
|
+
// below closes whichever overlay it finds and moves on, so a single pass
|
|
139
|
+
// leaves a stacked announcement dialog open, and every caller that relies
|
|
140
|
+
// on dismissOverlays() to clear the page then clicks into it.
|
|
141
|
+
await this.dismissBlockingDialog();
|
|
123
142
|
for (const locator of [
|
|
124
143
|
this.closeButton,
|
|
125
144
|
this.page.getByRole('button', { name: 'Close' }).first(),
|
|
@@ -163,6 +182,12 @@ class ModelerHomePage {
|
|
|
163
182
|
if (await committedProjectName.first().isVisible()) {
|
|
164
183
|
return;
|
|
165
184
|
}
|
|
185
|
+
// The rename runs on a freshly created project, which is exactly when an
|
|
186
|
+
// announcement dialog mounts on top of it: it takes focus, the inline
|
|
187
|
+
// editor unmounts, and the probe below then reads "closed" and sends the
|
|
188
|
+
// recovery down a path that cannot reach an editor the dialog is
|
|
189
|
+
// covering.
|
|
190
|
+
await this.dismissBlockingDialog();
|
|
166
191
|
// Bounded, not instantaneous: the editor can still be mounting, and
|
|
167
192
|
// reading that as "closed" would send the recovery to the projects-list
|
|
168
193
|
// branch while the browser sits on the project page, where the row it
|
|
@@ -413,12 +438,18 @@ class ModelerHomePage {
|
|
|
413
438
|
await this.page.getByText('Home', { exact: true }).first().click();
|
|
414
439
|
}
|
|
415
440
|
}
|
|
441
|
+
// An announcement dialog covering the Modeler header makes every click on the
|
|
442
|
+
// org button bounce off the modal layer ("intercepts pointer events") until
|
|
443
|
+
// the timeout, so dismiss it before each attempt instead of waiting out a
|
|
444
|
+
// fixed sleep and hoping the header is clear.
|
|
416
445
|
async clickOpenOrganizationsButton() {
|
|
417
|
-
await (0,
|
|
418
|
-
|
|
419
|
-
|
|
446
|
+
await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.openOrganizationsButton, {
|
|
447
|
+
preAction: async () => {
|
|
448
|
+
await this.dismissBlockingDialog();
|
|
449
|
+
},
|
|
450
|
+
totalTimeout: 120000,
|
|
451
|
+
visibilityTimeout: 30000,
|
|
420
452
|
});
|
|
421
|
-
await this.openOrganizationsButton.click({ timeout: 30000 });
|
|
422
453
|
}
|
|
423
454
|
async createForm(formName) {
|
|
424
455
|
await this.clickDiagramTypeDropdown();
|
|
@@ -199,29 +199,31 @@ class ProcessApplicationPage {
|
|
|
199
199
|
// Worth the noise: an unbound cluster otherwise surfaces as a deploy dialog
|
|
200
200
|
// that silently offers nothing, which says nothing about its own cause.
|
|
201
201
|
async openStageDefinition() {
|
|
202
|
-
const opened = await this.defineStagesDialog
|
|
203
|
-
.waitFor({ state: 'visible', timeout: 15000 })
|
|
204
|
-
.then(() => true)
|
|
205
|
-
.catch(() => false);
|
|
206
|
-
if (opened) {
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
202
|
// Page-scoped .first(): hub renders one such control on this page, and a
|
|
210
203
|
// second placement must not turn every rebinding test into a strict-mode
|
|
211
204
|
// violation.
|
|
212
205
|
const reopen = this.page
|
|
213
206
|
.getByRole('button', { name: /define stages|edit stages|configure/i })
|
|
214
207
|
.first();
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
208
|
+
// Retried as a whole, because the two ways in race each other. On a slow
|
|
209
|
+
// load the deep link's own auto-open lands AFTER the first wait below has
|
|
210
|
+
// given up; the dialog then sits on top of the reopen control, so the
|
|
211
|
+
// fallback click waits out its timeout on an intercepted pointer while the
|
|
212
|
+
// dialog it was meant to open is already on screen. Re-probing for the
|
|
213
|
+
// dialog at the top of every attempt settles that: whichever route got it
|
|
214
|
+
// there, the next pass sees it and returns.
|
|
215
|
+
await (0, test_1.expect)(async () => {
|
|
216
|
+
const opened = await this.defineStagesDialog
|
|
217
|
+
.waitFor({ state: 'visible', timeout: 15000 })
|
|
218
|
+
.then(() => true)
|
|
219
|
+
.catch(() => false);
|
|
220
|
+
if (opened) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
await reopen.waitFor({ state: 'visible', timeout: 10000 });
|
|
224
|
+
await reopen.click({ timeout: 15000 });
|
|
225
|
+
await (0, test_1.expect)(this.defineStagesDialog).toBeVisible({ timeout: 15000 });
|
|
226
|
+
}).toPass({ timeout: 150000 });
|
|
225
227
|
}
|
|
226
228
|
async openByName(name) {
|
|
227
229
|
const processApplication = this.page
|