@camunda/e2e-test-suite 0.0.1611 → 0.0.1613
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.10/HomePage.d.ts +1 -1
- package/dist/pages/8.10/HomePage.js +2 -4
- package/dist/pages/8.10/UtilitiesPage.d.ts +4 -1
- package/dist/pages/8.10/UtilitiesPage.js +5 -5
- package/dist/pages/8.7/ProcessApplicationPage.d.ts +1 -0
- package/dist/pages/8.7/ProcessApplicationPage.js +36 -10
- package/dist/tests/8.10/console-cluster-duplication.spec.js +5 -1
- package/dist/tests/8.10/console-user-flows.spec.js +4 -2
- package/dist/tests/8.10/test-setup.spec.js +3 -1
- package/package.json +1 -1
|
@@ -24,6 +24,6 @@ declare class HomePage {
|
|
|
24
24
|
closeInformationDialog(): Promise<void>;
|
|
25
25
|
organizationUuid(): string;
|
|
26
26
|
clickOpenOrganizationButton(): Promise<void>;
|
|
27
|
-
clickSkipCustomization(): Promise<void>;
|
|
27
|
+
clickSkipCustomization(timeout?: number): Promise<void>;
|
|
28
28
|
}
|
|
29
29
|
export { HomePage };
|
|
@@ -155,11 +155,9 @@ class HomePage {
|
|
|
155
155
|
async clickOpenOrganizationButton() {
|
|
156
156
|
await this.openOrganizationButton.click({ timeout: 60000 });
|
|
157
157
|
}
|
|
158
|
-
async clickSkipCustomization() {
|
|
158
|
+
async clickSkipCustomization(timeout = 30000) {
|
|
159
159
|
try {
|
|
160
|
-
await (0, test_1.expect)(this.buttonSkipCustomization).toBeVisible({
|
|
161
|
-
timeout: 30000,
|
|
162
|
-
});
|
|
160
|
+
await (0, test_1.expect)(this.buttonSkipCustomization).toBeVisible({ timeout });
|
|
163
161
|
// Best-effort: dismiss any Carbon modal that has a named close button.
|
|
164
162
|
await this.closeInformationDialog();
|
|
165
163
|
// force: true bypasses the "pointer events intercepted by modal backdrop"
|
|
@@ -38,7 +38,10 @@ export declare function runMultipleProcesses(clusterName: string, page: Page, mo
|
|
|
38
38
|
processName?: string;
|
|
39
39
|
uploadFromFile?: string;
|
|
40
40
|
}): Promise<void>;
|
|
41
|
-
export declare function consoleSessionRecovery(page: Page, clusterUrl: string
|
|
41
|
+
export declare function consoleSessionRecovery(page: Page, clusterUrl: string, user?: {
|
|
42
|
+
username: string;
|
|
43
|
+
password: string;
|
|
44
|
+
}): () => Promise<void>;
|
|
42
45
|
export declare function goBackWithRetry(page: Page, maxRetries?: number): Promise<void>;
|
|
43
46
|
export declare function disableRBA(clusterName: string, homePage: HomePage, clusterPage: ClusterPage, clusterDetailsPage: ClusterDetailsPage, appsPage: AppsPage): Promise<void>;
|
|
44
47
|
export declare function enableAuthorizations(clusterName: string, homePage: HomePage, clusterPage: ClusterPage, clusterDetailsPage: ClusterDetailsPage, appsPage: AppsPage): Promise<void>;
|
|
@@ -649,13 +649,13 @@ exports.runMultipleProcesses = runMultipleProcesses;
|
|
|
649
649
|
// component-health poll below, after which reloading only ever re-renders the
|
|
650
650
|
// login form. Re-authenticate as the console user and return to the page that
|
|
651
651
|
// was being polled, so the health assertion carries on unchanged.
|
|
652
|
-
function consoleSessionRecovery(page, clusterUrl
|
|
652
|
+
function consoleSessionRecovery(page, clusterUrl, user = {
|
|
653
|
+
username: process.env.C8_USERNAME,
|
|
654
|
+
password: process.env.C8_PASSWORD,
|
|
655
|
+
}) {
|
|
653
656
|
return async () => {
|
|
654
657
|
console.warn(`Console session lost while polling component health on ${clusterUrl}; re-authenticating.`);
|
|
655
|
-
await loginWithRetry(page, new LoginPage_1.LoginPage(page),
|
|
656
|
-
username: process.env.C8_USERNAME,
|
|
657
|
-
password: process.env.C8_PASSWORD,
|
|
658
|
-
}, 0, 3);
|
|
658
|
+
await loginWithRetry(page, new LoginPage_1.LoginPage(page), user, 0, 3);
|
|
659
659
|
await page.goto(clusterUrl, {
|
|
660
660
|
waitUntil: 'domcontentloaded',
|
|
661
661
|
timeout: 60000,
|
|
@@ -9,6 +9,7 @@ declare class ProcessApplicationPage {
|
|
|
9
9
|
readonly cancelButton: Locator;
|
|
10
10
|
readonly breadcrumb: Locator;
|
|
11
11
|
readonly folderExplorer: Locator;
|
|
12
|
+
readonly loadErrorState: Locator;
|
|
12
13
|
readonly createNewDropdown: Locator;
|
|
13
14
|
readonly inlineNameInput: Locator;
|
|
14
15
|
readonly defineStagesDialog: Locator;
|
|
@@ -33,6 +33,7 @@ class ProcessApplicationPage {
|
|
|
33
33
|
cancelButton;
|
|
34
34
|
breadcrumb;
|
|
35
35
|
folderExplorer;
|
|
36
|
+
loadErrorState;
|
|
36
37
|
createNewDropdown;
|
|
37
38
|
inlineNameInput;
|
|
38
39
|
// "Define stages" dialog, reached from the process application's "Connected
|
|
@@ -85,6 +86,11 @@ class ProcessApplicationPage {
|
|
|
85
86
|
});
|
|
86
87
|
this.breadcrumb = page.locator('[data-test="breadcrumb-process-application-menu"]');
|
|
87
88
|
this.folderExplorer = page.getByRole('region', { name: 'Folder explorer' });
|
|
89
|
+
// camunda-hub app/components/page/Page.tsx ErrorState: the design-system
|
|
90
|
+
// EmptyState that replaces the page body when its fetches fail.
|
|
91
|
+
this.loadErrorState = page.getByRole('heading', {
|
|
92
|
+
name: /^(Something went wrong|Not found)$/,
|
|
93
|
+
});
|
|
88
94
|
// The process application page renders more than one diagram-dropdown
|
|
89
95
|
// (hub's own unit tests reach for it with getAllByTestId), so the dropdown
|
|
90
96
|
// has to be resolved inside the folder explorer rather than page-wide.
|
|
@@ -133,30 +139,50 @@ class ProcessApplicationPage {
|
|
|
133
139
|
// absolute deadline: reload() otherwise inherits the 120s navigationTimeout
|
|
134
140
|
// and two slow reloads would eat the test budget during an outage. The last
|
|
135
141
|
// attempt is a plain assertion so a persistent outage still fails loudly.
|
|
142
|
+
//
|
|
143
|
+
// Reload only when it can help: as soon as Hub has rendered its error view
|
|
144
|
+
// (Page.tsx ErrorState -- "Something went wrong" or a "Not found" that a
|
|
145
|
+
// just-created application can briefly answer), or when one load has sat
|
|
146
|
+
// on the spinner for `stallAfter`. A fixed per-attempt cut-off used to
|
|
147
|
+
// reload pages that were still loading, so under load a ~40s render was
|
|
148
|
+
// restarted twice and the last attempt got ~37s -- not enough for a third
|
|
149
|
+
// render from scratch.
|
|
136
150
|
async waitWithReload(target, timeout, attempts = 3) {
|
|
137
151
|
const deadline = Date.now() + timeout;
|
|
138
|
-
const
|
|
152
|
+
const stallAfter = Math.ceil(timeout / 2);
|
|
139
153
|
const remaining = () => Math.max(deadline - Date.now(), 1000);
|
|
140
|
-
|
|
154
|
+
const settled = target.or(this.loadErrorState).first();
|
|
155
|
+
let reloadsLeft = attempts - 1;
|
|
156
|
+
let loadStartedAt = Date.now();
|
|
157
|
+
while (reloadsLeft > 0 && deadline - Date.now() > 1000) {
|
|
141
158
|
// Hub can drop its session on a navigation and bounce through
|
|
142
159
|
// /login-callback to the identity provider, where neither reloading nor
|
|
143
160
|
// waiting renders the content -- re-authenticate first.
|
|
144
|
-
await (0, sessionRecovery_1.recoverStuckModelerLogin)(this.page);
|
|
145
|
-
await (0, sessionRecovery_1.restoreSessionIfLoggedOut)(this.page);
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
161
|
+
const recoveredLogin = await (0, sessionRecovery_1.recoverStuckModelerLogin)(this.page);
|
|
162
|
+
const restoredSession = await (0, sessionRecovery_1.restoreSessionIfLoggedOut)(this.page);
|
|
163
|
+
if (recoveredLogin || restoredSession) {
|
|
164
|
+
loadStartedAt = Date.now();
|
|
165
|
+
}
|
|
166
|
+
// Short slices so a session dropped mid-wait is still recovered above.
|
|
167
|
+
await settled
|
|
168
|
+
.waitFor({ state: 'visible', timeout: Math.min(15000, remaining()) })
|
|
169
|
+
.catch(() => undefined);
|
|
170
|
+
if (await target.isVisible()) {
|
|
151
171
|
return;
|
|
152
172
|
}
|
|
153
|
-
|
|
173
|
+
const errored = await this.loadErrorState.isVisible();
|
|
174
|
+
if (!errored && Date.now() - loadStartedAt < stallAfter) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
reloadsLeft--;
|
|
178
|
+
console.warn(`Process application content did not render (${errored ? 'Hub showed its error view' : 'still loading'}); reloading, ${reloadsLeft} reload(s) left.`);
|
|
154
179
|
// 'domcontentloaded': a reload that bounces through /login-callback
|
|
155
180
|
// never reaches "load", and recovery above needs the page back.
|
|
156
181
|
await this.page.reload({
|
|
157
182
|
waitUntil: 'domcontentloaded',
|
|
158
183
|
timeout: remaining(),
|
|
159
184
|
});
|
|
185
|
+
loadStartedAt = Date.now();
|
|
160
186
|
}
|
|
161
187
|
await (0, sessionRecovery_1.recoverStuckModelerLogin)(this.page);
|
|
162
188
|
await (0, sessionRecovery_1.restoreSessionIfLoggedOut)(this.page);
|
|
@@ -31,8 +31,12 @@ const OVERRIDE_REGION_LABEL = 'Finland, Europe (europe-north1)';
|
|
|
31
31
|
* Isolation: every case creates its own source cluster with a randomised name and
|
|
32
32
|
* deletes every cluster it created in teardown - a leaked cluster consumes a
|
|
33
33
|
* reservation and would make the reservation case pass for the wrong reason.
|
|
34
|
+
*
|
|
35
|
+
* Parallel: no case reads state another writes (randomised names, per-test
|
|
36
|
+
* teardown), and each spends 10-14 minutes on cluster health polls, so serial
|
|
37
|
+
* mode stacked them into the longest chain of the full-suite pass.
|
|
34
38
|
*/
|
|
35
|
-
_8_10_1.test.describe.configure({ mode: '
|
|
39
|
+
_8_10_1.test.describe.configure({ mode: 'parallel' });
|
|
36
40
|
_8_10_1.test.describe('Console cluster duplication', () => {
|
|
37
41
|
// Everything this spec creates is tracked here so teardown runs even on failure.
|
|
38
42
|
const createdClusterNames = [];
|
|
@@ -71,7 +71,7 @@ _8_10_1.test.describe('Console User Flow Tests @tasklistV2', () => {
|
|
|
71
71
|
await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTabProcessInstancePage, operateTabProcessInstancePage.completedIcon, 'completed icon in Operate', 60000);
|
|
72
72
|
});
|
|
73
73
|
});
|
|
74
|
-
(0, _8_10_1.test)('Create Cluster', async ({ homePage, clusterPage }) => {
|
|
74
|
+
(0, _8_10_1.test)('Create Cluster', async ({ page, homePage, clusterPage }) => {
|
|
75
75
|
_8_10_1.test.slow();
|
|
76
76
|
// createCluster pre-deletes every cluster carrying this name, so a fixed
|
|
77
77
|
// name let concurrent runs of this test (--repeat-each, retries racing a
|
|
@@ -83,7 +83,9 @@ _8_10_1.test.describe('Console User Flow Tests @tasklistV2', () => {
|
|
|
83
83
|
await clusterPage.createCluster(newClusterName);
|
|
84
84
|
});
|
|
85
85
|
await _8_10_1.test.step('Assert cluster is healthy', async () => {
|
|
86
|
-
|
|
86
|
+
// Without recovery a dropped Hub session left the poll reloading the
|
|
87
|
+
// login form for its whole 25-minute budget.
|
|
88
|
+
await clusterPage.assertClusterHealthyStatusWithRetry(newClusterName, 300000, 1500000, (0, UtilitiesPage_1.consoleSessionRecovery)(page, page.url(), testUser));
|
|
87
89
|
});
|
|
88
90
|
await _8_10_1.test.step('Delete the created cluster', async () => {
|
|
89
91
|
await homePage.clickClusters();
|
|
@@ -227,7 +227,9 @@ _8_10_1.test.describe('Cluster Setup Tests', () => {
|
|
|
227
227
|
});
|
|
228
228
|
await _8_10_1.test.step('Create Cross Component Project Folder', async () => {
|
|
229
229
|
await (0, UtilitiesPage_1.withSessionRecovery)(page, loginPage, appsPage, modelerHomePage.modelerPageBanner, user, Math.min((testInfo.workerIndex + 1) * 5000, 60000), async () => {
|
|
230
|
-
|
|
230
|
+
// The Modeler banner is already visible here, so a customization
|
|
231
|
+
// prompt would have rendered with it; Hub-only orgs never show one.
|
|
232
|
+
await homePage.clickSkipCustomization(3000);
|
|
231
233
|
// Reverted the "only User 1 creates" leader-election attempt: it made
|
|
232
234
|
// things worse, not better. Run 34191243820 showed followers still
|
|
233
235
|
// timing out waiting for the folder even well after User 1 (2.9min)
|