@camunda/e2e-test-suite 0.0.1278 → 0.0.1279
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/dist/pages/8.7/AppsPage.js +15 -7
- package/dist/pages/8.7/ClusterDetailsPage.d.ts +1 -0
- package/dist/pages/8.7/ClusterDetailsPage.js +15 -0
- package/dist/pages/8.7/ClusterSecretsPage.js +9 -2
- package/dist/pages/8.7/ConnectorMarketplacePage.d.ts +1 -0
- package/dist/pages/8.7/ConnectorMarketplacePage.js +27 -3
- package/dist/pages/8.7/LoginPage.js +26 -4
- package/dist/pages/8.7/UtilitiesPage.js +6 -1
- package/dist/tests/8.7/connectors-user-flows.spec.js +3 -1
- package/package.json +1 -1
|
@@ -83,8 +83,14 @@ class AppsPage {
|
|
|
83
83
|
// (see clickHubClusterApp and isHubClusterAppPresent below) gets at most
|
|
84
84
|
// one match, scoped to this cluster.
|
|
85
85
|
async expandHubClusterGroup(clusterName) {
|
|
86
|
+
// Anchored at the start: an unanchored substring match on "Test Cluster"
|
|
87
|
+
// also matches "UI Test Cluster" (created by console-user-flows in the
|
|
88
|
+
// same org), and .first() then opened that cluster's popover instead --
|
|
89
|
+
// a nightly trace shows the "UI Test Cluster" group open while the
|
|
90
|
+
// "Tasklist"/"Operate" lookup for "Test Cluster" timed out.
|
|
91
|
+
const clusterNamePattern = new RegExp(`^${escapeRegExp(clusterName)}\\b`);
|
|
86
92
|
const toggle = this.hubSidebar
|
|
87
|
-
.getByRole('button', { name:
|
|
93
|
+
.getByRole('button', { name: clusterNamePattern })
|
|
88
94
|
.first();
|
|
89
95
|
// The diagram editor (and other full-screen views reached from it, e.g.
|
|
90
96
|
// right after runProcessInstance/deployDiagram) has no "Main navigation"
|
|
@@ -165,14 +171,16 @@ class AppsPage {
|
|
|
165
171
|
// of its own, in which case hasNotText would fail to exclude the toggle
|
|
166
172
|
// we just expanded and this loop would immediately collapse it again.
|
|
167
173
|
// Match by accessible name (same algorithm getByRole's own name option
|
|
168
|
-
// uses) instead, excluding any name
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
//
|
|
174
|
+
// uses) instead, excluding any name STARTING WITH clusterName (the same
|
|
175
|
+
// anchoring as the toggle locator, so "UI Test Cluster" still counts as
|
|
176
|
+
// another group) -- not just one exactly equal to it, since (per the
|
|
177
|
+
// toggle locator above) the current cluster's own toggle name is not
|
|
178
|
+
// exactly clusterName either, and an exact-equality exclusion would
|
|
179
|
+
// wrongly treat it as "other" and immediately re-collapse the group this
|
|
180
|
+
// method just expanded.
|
|
173
181
|
const otherExpandedToggles = this.hubSidebar.getByRole('button', {
|
|
174
182
|
expanded: true,
|
|
175
|
-
name: new RegExp(`^(
|
|
183
|
+
name: new RegExp(`^(?!${escapeRegExp(clusterName)}\\b).+$`),
|
|
176
184
|
});
|
|
177
185
|
for (let i = 0; i < 5 && (await otherExpandedToggles.count()) > 0; i++) {
|
|
178
186
|
// Let a failed collapse throw rather than swallowing it: the app link
|
|
@@ -62,6 +62,7 @@ declare class ClusterDetailsPage {
|
|
|
62
62
|
clickCreateButton(): Promise<void>;
|
|
63
63
|
clickCloseModalButton(): Promise<void>;
|
|
64
64
|
clickSettingsTab(): Promise<void>;
|
|
65
|
+
private isRBAToggleInState;
|
|
65
66
|
enableRBA(): Promise<void>;
|
|
66
67
|
disableRBA(): Promise<void>;
|
|
67
68
|
searchAndClickClientCredentialsLink(name: string): Promise<ClientCredentialsDetailsPage>;
|
|
@@ -234,9 +234,21 @@ class ClusterDetailsPage {
|
|
|
234
234
|
async clickSettingsTab() {
|
|
235
235
|
await this.settingsTab.click({ timeout: 60000 });
|
|
236
236
|
}
|
|
237
|
+
// The switch reports its state through aria-checked. Clicking it
|
|
238
|
+
// unconditionally opened the opposite confirmation dialog whenever the
|
|
239
|
+
// cluster was already in the requested state (e.g. "Ensure RBA is
|
|
240
|
+
// Disabled" on a cluster that never had it on), so the Enable/Disable
|
|
241
|
+
// dialog wait below timed out. Returns false when the state is unknown.
|
|
242
|
+
async isRBAToggleInState(enabled) {
|
|
243
|
+
const checked = await this.rbaToggle.getAttribute('aria-checked');
|
|
244
|
+
return checked !== null && (checked === 'true') === enabled;
|
|
245
|
+
}
|
|
237
246
|
async enableRBA() {
|
|
238
247
|
await (0, test_1.expect)(this.rbaHeading).toBeVisible({ timeout: 60000 });
|
|
239
248
|
await (0, test_1.expect)(this.rbaToggle).toBeVisible({ timeout: 30000 });
|
|
249
|
+
if (await this.isRBAToggleInState(true)) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
240
252
|
await this.rbaToggle.click();
|
|
241
253
|
await (0, test_1.expect)(this.enableRBADialog).toBeVisible({ timeout: 60000 });
|
|
242
254
|
await this.rbaEnableButton.click();
|
|
@@ -247,6 +259,9 @@ class ClusterDetailsPage {
|
|
|
247
259
|
async disableRBA() {
|
|
248
260
|
await (0, test_1.expect)(this.rbaHeading).toBeVisible({ timeout: 60000 });
|
|
249
261
|
await (0, test_1.expect)(this.rbaToggle).toBeVisible({ timeout: 30000 });
|
|
262
|
+
if (await this.isRBAToggleInState(false)) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
250
265
|
await this.rbaToggle.click();
|
|
251
266
|
await (0, test_1.expect)(this.disableRBADialog).toBeVisible({ timeout: 60000 });
|
|
252
267
|
await this.rbaDisableButton.click();
|
|
@@ -31,9 +31,16 @@ class ClusterSecretsPage {
|
|
|
31
31
|
this.connectorSecretsTable = page.getByRole('table', {
|
|
32
32
|
name: 'Connector secrets',
|
|
33
33
|
});
|
|
34
|
+
// Legacy Carbon table: an "Options" cell holding the kebab. Hub's
|
|
35
|
+
// design-system data table instead labels each row's kebab
|
|
36
|
+
// "Row actions" (data-slot="data-table-actions-trigger"), with no
|
|
37
|
+
// "Options" cell -- so the old locator found nothing, the pre-clean
|
|
38
|
+
// returned early, and re-creating an existing secret failed with
|
|
39
|
+
// "Secret names must be unique".
|
|
34
40
|
this.optionsButton = page
|
|
35
41
|
.getByRole('cell', { name: 'Options' })
|
|
36
|
-
.getByRole('button')
|
|
42
|
+
.getByRole('button')
|
|
43
|
+
.or(page.getByRole('button', { name: 'Row actions', exact: true }));
|
|
37
44
|
this.deleteConnectorSecretButton = page.getByRole('menuitem', {
|
|
38
45
|
name: 'Delete',
|
|
39
46
|
exact: true,
|
|
@@ -124,7 +131,7 @@ class ClusterSecretsPage {
|
|
|
124
131
|
// that nothing *deletable* is left behind.
|
|
125
132
|
const deletableSecretRows = this.page
|
|
126
133
|
.getByRole('row')
|
|
127
|
-
.filter({ has: this.
|
|
134
|
+
.filter({ has: this.optionsButton })
|
|
128
135
|
.filter({ hasNotText: 'CAMUNDA_PROVIDED_' });
|
|
129
136
|
await (0, test_1.expect)(deletableSecretRows).toHaveCount(0, { timeout: 30000 });
|
|
130
137
|
}
|
|
@@ -7,6 +7,7 @@ declare class ConnectorMarketplacePage {
|
|
|
7
7
|
readonly replaceResourceButton: Locator;
|
|
8
8
|
readonly addToProjectButton: Locator;
|
|
9
9
|
readonly cancelButton: Locator;
|
|
10
|
+
readonly tryAgainButton: Locator;
|
|
10
11
|
readonly closeButton: Locator;
|
|
11
12
|
readonly browseAllMarketplaceDetailsButton: Locator;
|
|
12
13
|
constructor(page: Page);
|
|
@@ -10,6 +10,7 @@ class ConnectorMarketplacePage {
|
|
|
10
10
|
replaceResourceButton;
|
|
11
11
|
addToProjectButton;
|
|
12
12
|
cancelButton;
|
|
13
|
+
tryAgainButton;
|
|
13
14
|
closeButton;
|
|
14
15
|
browseAllMarketplaceDetailsButton;
|
|
15
16
|
constructor(page) {
|
|
@@ -38,6 +39,7 @@ class ConnectorMarketplacePage {
|
|
|
38
39
|
name: 'Add to project',
|
|
39
40
|
});
|
|
40
41
|
this.cancelButton = page.getByRole('button', { name: 'Cancel' });
|
|
42
|
+
this.tryAgainButton = page.getByRole('button', { name: 'Try again' });
|
|
41
43
|
this.closeButton = page.getByRole('button', { name: 'Close' });
|
|
42
44
|
}
|
|
43
45
|
async clickSearchForConnectorTextbox() {
|
|
@@ -72,11 +74,33 @@ class ConnectorMarketplacePage {
|
|
|
72
74
|
// genuine timeout here throw rather than silently falling through to the
|
|
73
75
|
// immediate isVisible() probes below, which would just repeat the same
|
|
74
76
|
// race this wait exists to close.
|
|
75
|
-
|
|
77
|
+
//
|
|
78
|
+
// The Browse all review step first "checks this project" (camunda-hub
|
|
79
|
+
// marketplace-connector-detail-state.ts), which waits on the project's
|
|
80
|
+
// element-template metadata request -- nightly and live traces show that
|
|
81
|
+
// request still pending 30s in. When the check fails the footer offers
|
|
82
|
+
// "Try again" (RETRY_INSPECTION) instead of "Add to project", so give the
|
|
83
|
+
// check longer and re-run it on failure rather than timing out.
|
|
84
|
+
const outcome = this.addToProjectButton
|
|
76
85
|
.or(this.replaceResourceButton)
|
|
77
86
|
.or(this.cancelButton)
|
|
78
|
-
.first()
|
|
79
|
-
|
|
87
|
+
.first();
|
|
88
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
89
|
+
await outcome
|
|
90
|
+
.or(this.tryAgainButton)
|
|
91
|
+
.first()
|
|
92
|
+
.waitFor({ state: 'visible', timeout: 120000 });
|
|
93
|
+
if (await outcome.isVisible()) {
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
// The retrying check keeps "Try again" on screen (as a loading button),
|
|
97
|
+
// so wait for its result here instead of re-polling the union above.
|
|
98
|
+
await this.tryAgainButton.click({ timeout: 10000 });
|
|
99
|
+
await outcome
|
|
100
|
+
.waitFor({ state: 'visible', timeout: 120000 })
|
|
101
|
+
.catch(() => { });
|
|
102
|
+
}
|
|
103
|
+
await outcome.waitFor({ state: 'visible', timeout: 10000 });
|
|
80
104
|
let imported = false;
|
|
81
105
|
// A fresh download shows "Add to project"; an already-present resource
|
|
82
106
|
// shows "Replace resource". Pick whichever the ImportModal rendered —
|
|
@@ -77,8 +77,20 @@ class LoginPage {
|
|
|
77
77
|
// before) must clear it too, or getUserToken() can return the previous
|
|
78
78
|
// user's cached token.
|
|
79
79
|
(0, consoleApiHelpers_1.resetHubBearerToken)(this.page);
|
|
80
|
-
// Navigate to app root to start the auth flow.
|
|
81
|
-
|
|
80
|
+
// Navigate to app root to start the auth flow. Callers usually arrive
|
|
81
|
+
// here straight from a "Log out" click, whose own redirect chain (Hub's
|
|
82
|
+
// /login?returnUrl=/logout -> Auth0 logout) can still be in flight and
|
|
83
|
+
// interrupt this goto. Let that navigation land, then start over once.
|
|
84
|
+
try {
|
|
85
|
+
await this.page.goto('/');
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
if (!String(error).includes('is interrupted by another navigation')) {
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
await this.page.waitForLoadState('domcontentloaded', { timeout: 60000 });
|
|
92
|
+
await this.page.goto('/');
|
|
93
|
+
}
|
|
82
94
|
await this.page
|
|
83
95
|
.waitForURL((url) => !url.hostname.includes('console'), { timeout: 60000 })
|
|
84
96
|
.catch(() => { });
|
|
@@ -86,8 +98,18 @@ class LoginPage {
|
|
|
86
98
|
// logout/session-reset, keeping the page on Console instead of redirecting
|
|
87
99
|
// to the Auth0 login form. Detect Console (settings button visible) and
|
|
88
100
|
// force logout until the Auth0 email input appears.
|
|
89
|
-
|
|
90
|
-
|
|
101
|
+
// Hub's design-system shell has no "Open Settings" button: its user menu
|
|
102
|
+
// is a "User menu, <full name>" button whose "Log out" is a menuitem (see
|
|
103
|
+
// SettingsPage). Without these the loop never noticed it had been
|
|
104
|
+
// silently re-authenticated on Hub and waited out the username input.
|
|
105
|
+
const settingsBtn = this.page
|
|
106
|
+
.getByLabel('Open Settings')
|
|
107
|
+
.or(this.page.getByRole('button', { name: /^User menu,/ }))
|
|
108
|
+
.first();
|
|
109
|
+
const logoutBtn = this.page
|
|
110
|
+
.getByRole('button', { name: 'Log out' })
|
|
111
|
+
.or(this.page.getByRole('menuitem', { name: 'Log out' }))
|
|
112
|
+
.first();
|
|
91
113
|
for (let i = 0; i < 3; i++) {
|
|
92
114
|
const onAuth0 = await this.usernameInput
|
|
93
115
|
.waitFor({ state: 'visible', timeout: 15000 })
|
|
@@ -8,6 +8,7 @@ const sleep_1 = require("../../utils/sleep");
|
|
|
8
8
|
const fileUpload_1 = require("../../utils/fileUpload");
|
|
9
9
|
const mailSlurpClient_1 = require("../../utils/mailSlurpClient");
|
|
10
10
|
const consoleApiHelpers_1 = require("../../utils/consoleApiHelpers");
|
|
11
|
+
const sessionRecovery_1 = require("../../utils/sessionRecovery");
|
|
11
12
|
// Tracks which worker-derived `timeout` values have already paid the
|
|
12
13
|
// pre-flight stagger below, so a worker only pays it once per process
|
|
13
14
|
// lifetime rather than on every call -- see the comment at the call sites.
|
|
@@ -601,7 +602,11 @@ async function modelAndRunConnectorsDocHandlingDiagram(modelerCreatePage, modele
|
|
|
601
602
|
});
|
|
602
603
|
await connectorMarketplacePage.downloadConnectorToProject();
|
|
603
604
|
await (0, sleep_1.sleep)(5000);
|
|
604
|
-
|
|
605
|
+
// A live run showed this reload bounce through Hub's /login?returnUrl=...
|
|
606
|
+
// and never reach "load" -- the stuck-session case recoverStuckModelerLogin
|
|
607
|
+
// exists for. Wait for the document only, then recover if it is stuck.
|
|
608
|
+
await page.reload({ waitUntil: 'domcontentloaded' });
|
|
609
|
+
await (0, sessionRecovery_1.recoverStuckModelerLogin)(page);
|
|
605
610
|
await (0, sleep_1.sleep)(5000);
|
|
606
611
|
await modelerCreatePage.fillStartEventVariablesForDocHandling(zeebeUrl, zeebeClientId, zeebeClientSecret);
|
|
607
612
|
await modelerCreatePage.runProcessInstance(clusterName);
|
|
@@ -182,7 +182,9 @@ _8_7_1.test.describe('Connectors User Flow Tests', () => {
|
|
|
182
182
|
timeout: 20000,
|
|
183
183
|
});
|
|
184
184
|
await appsPage.clickCamundaApps();
|
|
185
|
-
|
|
185
|
+
// No separate modelerLink check: Hub has no app-switcher "Modeler" link
|
|
186
|
+
// (clickModeler() goes through its home link instead), and on the
|
|
187
|
+
// legacy shell clickModeler() asserts modelerLink itself.
|
|
186
188
|
await appsPage.clickModeler();
|
|
187
189
|
await (0, test_1.expect)(modelerHomePage.modelerPageBanner).toBeVisible({
|
|
188
190
|
timeout: 120000,
|