@camunda/e2e-test-suite 0.0.1221 → 0.0.1223
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/ModelerHomePage.js +9 -2
- package/dist/pages/8.7/UtilitiesPage.d.ts +5 -0
- package/dist/pages/8.7/UtilitiesPage.js +60 -1
- package/dist/pages/SM-8.10/NavigationPage.js +10 -3
- package/dist/pages/SM-8.10/OptimizeDashboardPage.js +7 -1
- package/dist/pages/SM-8.10/OptimizeHomePage.js +16 -5
- package/dist/tests/8.7/smoke-tests.spec.js +13 -4
- package/package.json +1 -1
|
@@ -469,9 +469,16 @@ class ModelerHomePage {
|
|
|
469
469
|
});
|
|
470
470
|
}
|
|
471
471
|
async createCrossComponentProjectFolder() {
|
|
472
|
-
await (0, sleep_1.sleep)(15000);
|
|
473
472
|
await this.dismissOverlays();
|
|
474
|
-
|
|
473
|
+
// The project list can take well over a minute to render after a session
|
|
474
|
+
// recovery (observed spinners up to ~70s), so wait for the tile itself
|
|
475
|
+
// rather than a fixed sleep -- a short sleep misclassifies a slow-loading
|
|
476
|
+
// list as missing and creates a duplicate folder.
|
|
477
|
+
const folderExists = await this.crossComponentProjectFolder
|
|
478
|
+
.waitFor({ state: 'visible', timeout: 90000 })
|
|
479
|
+
.then(() => true)
|
|
480
|
+
.catch(() => false);
|
|
481
|
+
if (folderExists) {
|
|
475
482
|
console.log('Cross Component Project folder already exists. Clicking into it');
|
|
476
483
|
await this.clickCrossComponentProjectFolder();
|
|
477
484
|
return;
|
|
@@ -18,6 +18,11 @@ export declare function loginWithRetry(page: Page, loginPage: LoginPage, testUse
|
|
|
18
18
|
username: string;
|
|
19
19
|
password: string;
|
|
20
20
|
}, timeout: number, maxRetries?: number, skipOrgAssertion?: boolean): Promise<void>;
|
|
21
|
+
export declare function isSignedOut(page: Page): boolean;
|
|
22
|
+
export declare function withSessionRecovery(page: Page, loginPage: LoginPage, appsPage: AppsPage, modelerBanner: Locator, testUser: {
|
|
23
|
+
username: string;
|
|
24
|
+
password: string;
|
|
25
|
+
}, loginStagger: number, action: () => Promise<void>): Promise<void>;
|
|
21
26
|
export declare function modelRestConnector(modelerCreatePage: ModelerCreatePage, connectorSettingsPage: ConnectorSettingsPage, processName: string, url: string, auth: string, resultExpression: string, resultVariable?: string, basicAuthCredentials?: {
|
|
22
27
|
username: string;
|
|
23
28
|
password: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.modelAndRunConnectorsTimerEventDiagram = exports.modelAndRunConnectorsDocHandlingDiagram = exports.deleteCluster = exports.deleteAllUserGroups = exports.assertTestUsesCorrectOrganization = exports.clickInvitationLinkInEmail = exports.enableRBA = exports.disableRBA = exports.modelDiagramFromFile = exports.runProcess = exports.runMultipleProcesses = exports.completeTaskWithRetry = exports.assertPageTextWithRetry = exports.assertLocatorVisibleWithRetry = exports.modelRestConnector = exports.loginWithRetry = void 0;
|
|
3
|
+
exports.modelAndRunConnectorsTimerEventDiagram = exports.modelAndRunConnectorsDocHandlingDiagram = exports.deleteCluster = exports.deleteAllUserGroups = exports.assertTestUsesCorrectOrganization = exports.clickInvitationLinkInEmail = exports.enableRBA = exports.disableRBA = exports.modelDiagramFromFile = exports.runProcess = exports.runMultipleProcesses = exports.completeTaskWithRetry = exports.assertPageTextWithRetry = exports.assertLocatorVisibleWithRetry = exports.modelRestConnector = exports.withSessionRecovery = exports.isSignedOut = exports.loginWithRetry = void 0;
|
|
4
4
|
const test_1 = require("@playwright/test");
|
|
5
5
|
const ModelerHomePage_1 = require("./ModelerHomePage");
|
|
6
6
|
const HomePage_1 = require("./HomePage");
|
|
@@ -104,6 +104,65 @@ async function loginWithRetry(page, loginPage, testUser, timeout, maxRetries = 5
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
exports.loginWithRetry = loginWithRetry;
|
|
107
|
+
// True when the tab is sitting on the SaaS sign-in page: either the Auth0
|
|
108
|
+
// tenant (weblogin.<host>) or the Modeler's own /login route.
|
|
109
|
+
function isSignedOut(page) {
|
|
110
|
+
try {
|
|
111
|
+
const { hostname, pathname } = new URL(page.url());
|
|
112
|
+
return (hostname.startsWith('weblogin.') ||
|
|
113
|
+
(hostname.startsWith('modeler.') && /^\/login\/?$/.test(pathname)));
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
// Not a parsable URL yet (e.g. a tab still on about:blank).
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
exports.isSignedOut = isSignedOut;
|
|
121
|
+
// A mid-flow sign-out redirects to the IdP asynchronously, so at the instant
|
|
122
|
+
// `action()` throws the tab can still report the Modeler URL. Give the
|
|
123
|
+
// redirect a bounded window to commit before deciding, so a genuinely dropped
|
|
124
|
+
// session is recovered rather than mistaken for "still signed in".
|
|
125
|
+
async function landedOnLoginPage(page, timeout) {
|
|
126
|
+
if (isSignedOut(page)) {
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
return page
|
|
130
|
+
.waitForURL((url) => url.hostname.startsWith('weblogin.') ||
|
|
131
|
+
(url.hostname.startsWith('modeler.') &&
|
|
132
|
+
/^\/login\/?$/.test(url.pathname)), { timeout })
|
|
133
|
+
.then(() => true)
|
|
134
|
+
.catch((error) => {
|
|
135
|
+
if (error instanceof test_1.errors.TimeoutError) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
throw error;
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
// Runs a Modeler-home action and, if it fails because the session was dropped
|
|
142
|
+
// mid-flow (a SaaS provisioning-burst artifact -- the tab lands on the Auth0
|
|
143
|
+
// login page and the projects list never renders), signs back in, re-enters
|
|
144
|
+
// Modeler and retries once. Any failure that does NOT land on the login page
|
|
145
|
+
// is re-thrown unchanged, so a genuine product or selector break is never
|
|
146
|
+
// masked.
|
|
147
|
+
async function withSessionRecovery(page, loginPage, appsPage, modelerBanner, testUser, loginStagger, action) {
|
|
148
|
+
try {
|
|
149
|
+
await action();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
if (!(await landedOnLoginPage(page, 15000))) {
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
console.warn(`Session was dropped mid-flow (tab landed on ${page.url()}); ` +
|
|
157
|
+
'signing back in and retrying once.');
|
|
158
|
+
}
|
|
159
|
+
await loginWithRetry(page, loginPage, testUser, loginStagger);
|
|
160
|
+
await appsPage.clickCamundaApps();
|
|
161
|
+
await appsPage.clickModeler();
|
|
162
|
+
await (0, test_1.expect)(modelerBanner).toBeVisible({ timeout: 60000 });
|
|
163
|
+
await action();
|
|
164
|
+
}
|
|
165
|
+
exports.withSessionRecovery = withSessionRecovery;
|
|
107
166
|
async function modelRestConnector(modelerCreatePage, connectorSettingsPage, processName, url, auth, resultExpression, resultVariable = '', basicAuthCredentials = {
|
|
108
167
|
username: '',
|
|
109
168
|
password: '',
|
|
@@ -67,9 +67,16 @@ class NavigationPage {
|
|
|
67
67
|
.and(page.locator(`[href$="${NORMALIZED_ORCHESTRATION_CONTEXT_PATH}/tasklist"]`)))
|
|
68
68
|
.or(page.getByRole('link', { name: 'Camunda logo Tasklist' }))
|
|
69
69
|
.first();
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
// Optimize renders the Design System header (HeaderV2) whenever the nav
|
|
71
|
+
// V2 client config is on -- its home link is aria-label="Camunda
|
|
72
|
+
// Optimize" wrapping an aria-hidden logo svg, so the legacy shell's
|
|
73
|
+
// "Camunda logo Optimize" text no longer exists. Match either so the
|
|
74
|
+
// banner resolves with the flag on or off (mirrors operatePageBanner and
|
|
75
|
+
// pages/8.10/OptimizeHomePage.optimizeBanner).
|
|
76
|
+
this.optimizePageBanner = page
|
|
77
|
+
.getByRole('link', { name: 'Camunda logo Optimize' })
|
|
78
|
+
.or(page.getByRole('link', { name: 'Camunda Optimize', exact: true }))
|
|
79
|
+
.first();
|
|
73
80
|
// TODO: Drop support for Identity once we fully rename it to Admin
|
|
74
81
|
this.identityPageBanner = page
|
|
75
82
|
.getByRole('link', { name: 'Camunda Admin', exact: true })
|
|
@@ -7,7 +7,13 @@ class OptimizeDashboardPage {
|
|
|
7
7
|
filterTable;
|
|
8
8
|
constructor(page) {
|
|
9
9
|
this.page = page;
|
|
10
|
-
|
|
10
|
+
// Optimize's nav V2 shell renames the process-overview search input's
|
|
11
|
+
// placeholder from "Filter table" to "Search for name...", so the legacy
|
|
12
|
+
// placeholder resolves nothing there. Match either.
|
|
13
|
+
this.filterTable = page
|
|
14
|
+
.getByPlaceholder('Filter table')
|
|
15
|
+
.or(page.getByPlaceholder('Search for name'))
|
|
16
|
+
.first();
|
|
11
17
|
}
|
|
12
18
|
async clickFilterTable() {
|
|
13
19
|
await this.filterTable.click({ timeout: 180000 });
|
|
@@ -14,14 +14,25 @@ class OptimizeHomePage {
|
|
|
14
14
|
licenseKeyTagProduction;
|
|
15
15
|
constructor(page) {
|
|
16
16
|
this.page = page;
|
|
17
|
+
// Nav V2 also moved the app links out of the header and into a sidebar
|
|
18
|
+
// (`<nav aria-label="Main navigation">`), so scoping to role=banner
|
|
19
|
+
// resolves nothing there. Match the sidebar and keep the legacy
|
|
20
|
+
// header-scoped lookup as a fallback so both shells resolve.
|
|
17
21
|
this.collectionsLink = page
|
|
18
|
-
.getByRole('
|
|
19
|
-
.getByRole('link', { name: 'Collections' })
|
|
22
|
+
.getByRole('navigation', { name: 'Main navigation' })
|
|
23
|
+
.getByRole('link', { name: 'Collections' })
|
|
24
|
+
.or(page.getByRole('banner').getByRole('link', { name: 'Collections' }))
|
|
25
|
+
.first();
|
|
20
26
|
this.dashboardLink = page.getByRole('link', { name: 'Dashboard' });
|
|
21
27
|
this.modalCloseButton = page.getByText('Close', { exact: true });
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
28
|
+
// Same nav V2 header drift as NavigationPage.optimizePageBanner: the
|
|
29
|
+
// Design System logo link is aria-label="Camunda Optimize", so the legacy
|
|
30
|
+
// shell's combined "Camunda logo Optimize" name no longer exists. Match
|
|
31
|
+
// either shell.
|
|
32
|
+
this.optimizeBanner = page
|
|
33
|
+
.getByRole('link', { name: 'Camunda logo Optimize' })
|
|
34
|
+
.or(page.getByRole('link', { name: 'Camunda Optimize', exact: true }))
|
|
35
|
+
.first();
|
|
25
36
|
this.openUserSettingsButton = page.getByLabel('Open User');
|
|
26
37
|
this.logoutButton = page.getByRole('button', { name: 'Logout' });
|
|
27
38
|
this.licenseKeyTagNonProduction = page
|
|
@@ -37,7 +37,7 @@ _8_7_1.test.describe('Smoke Tests', () => {
|
|
|
37
37
|
await (0, _setup_1.captureScreenshot)(page, testInfo);
|
|
38
38
|
await (0, _setup_1.captureFailureVideo)(page, testInfo);
|
|
39
39
|
});
|
|
40
|
-
(0, _8_7_1.test)('Most Common Flow User Flow With All Apps', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, formJsPage, }) => {
|
|
40
|
+
(0, _8_7_1.test)('Most Common Flow User Flow With All Apps', async ({ page, loginPage, homePage, modelerHomePage, appsPage, modelerCreatePage, formJsPage, }, testInfo) => {
|
|
41
41
|
_8_7_1.test.slow();
|
|
42
42
|
const randomString = useFixedNames
|
|
43
43
|
? 'UpgradeTest'
|
|
@@ -60,7 +60,13 @@ _8_7_1.test.describe('Smoke Tests', () => {
|
|
|
60
60
|
});
|
|
61
61
|
});
|
|
62
62
|
await _8_7_1.test.step('Create form to be linked for Zeebe user task', async () => {
|
|
63
|
-
|
|
63
|
+
// The shared test-setup project creates this folder, but under the
|
|
64
|
+
// SaaS provisioning burst the session can drop mid-flow (tab lands on
|
|
65
|
+
// login, projects list never renders) or the per-user setup can miss
|
|
66
|
+
// the folder. createCrossComponentProjectFolder clicks into it when
|
|
67
|
+
// present and creates it only when genuinely missing;
|
|
68
|
+
// withSessionRecovery re-signs-in and retries once on a dropped session.
|
|
69
|
+
await (0, UtilitiesPage_1.withSessionRecovery)(page, loginPage, appsPage, modelerHomePage.modelerPageBanner, testUser, (testInfo.workerIndex + 1) * 1000, () => modelerHomePage.createCrossComponentProjectFolder());
|
|
64
70
|
await modelerHomePage.createForm(formName);
|
|
65
71
|
await formJsPage.deployForm(CLUSTER_NAME);
|
|
66
72
|
});
|
|
@@ -182,7 +188,7 @@ _8_7_1.test.describe('Smoke Tests', () => {
|
|
|
182
188
|
});
|
|
183
189
|
});
|
|
184
190
|
});
|
|
185
|
-
(0, _8_7_1.test)('Most Common REST Connector User Flow', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, connectorSettingsPage, }) => {
|
|
191
|
+
(0, _8_7_1.test)('Most Common REST Connector User Flow', async ({ page, loginPage, homePage, modelerHomePage, appsPage, modelerCreatePage, connectorSettingsPage, }, testInfo) => {
|
|
186
192
|
_8_7_1.test.slow();
|
|
187
193
|
const randomString = useFixedNames
|
|
188
194
|
? 'UpgradeTest'
|
|
@@ -199,7 +205,10 @@ _8_7_1.test.describe('Smoke Tests', () => {
|
|
|
199
205
|
});
|
|
200
206
|
});
|
|
201
207
|
await _8_7_1.test.step('Open Cross Component Test Project and create a BPMN diagram template', async () => {
|
|
202
|
-
|
|
208
|
+
// See the note in the flow above: ensure the per-user project exists
|
|
209
|
+
// rather than hanging on a click when the shared setup missed it, and
|
|
210
|
+
// recover a session dropped by the SaaS provisioning burst.
|
|
211
|
+
await (0, UtilitiesPage_1.withSessionRecovery)(page, loginPage, appsPage, modelerHomePage.modelerPageBanner, testUser, (testInfo.workerIndex + 1) * 1000, () => modelerHomePage.createCrossComponentProjectFolder());
|
|
203
212
|
await modelerHomePage.clickDiagramTypeDropdown();
|
|
204
213
|
await modelerHomePage.clickBpmnTemplateOption();
|
|
205
214
|
});
|