@camunda/e2e-test-suite 0.0.1220 → 0.0.1222
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/ModelerHomePage.js +17 -3
- package/dist/pages/8.10/UtilitiesPage.js +19 -0
- 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/tests/8.7/smoke-tests.spec.js +13 -4
- package/package.json +1 -1
|
@@ -264,9 +264,23 @@ class ModelerHomePage {
|
|
|
264
264
|
// into it, so the breadcrumb editor was never mounted at all. Rename the
|
|
265
265
|
// row in place rather than clicking "New project" again, which would leave
|
|
266
266
|
// a second, permanently unnamed project behind in the org.
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
267
|
+
//
|
|
268
|
+
// Routed through clickLocatorWithRetry with a dialog-dismiss preAction --
|
|
269
|
+
// the same guard clickCreateNewProjectButton and the folder tile already
|
|
270
|
+
// use, and the one row control in this flow that was still a raw click.
|
|
271
|
+
// Creating a project mounts an announcement dialog over the projects list
|
|
272
|
+
// (see enterNewProjectName); it re-appears during the waits above and
|
|
273
|
+
// intercepts this context-dropdown, so the bare click timed out for its
|
|
274
|
+
// whole budget every toPass iteration. Dismissing right before each
|
|
275
|
+
// attempt clears the overlay the caller's earlier dismiss no longer
|
|
276
|
+
// covers.
|
|
277
|
+
await (0, clickLocatorWithRetry_1.clickLocatorWithRetry)(this.page, this.defaultNamedProjectRow.locator('[data-test="entity-context-dropdown"]'), {
|
|
278
|
+
totalTimeout: 90000,
|
|
279
|
+
visibilityTimeout: 15000,
|
|
280
|
+
preAction: async () => {
|
|
281
|
+
await this.dismissBlockingDialog();
|
|
282
|
+
},
|
|
283
|
+
});
|
|
270
284
|
await this.renameMenuItem.click({ timeout: 10000 });
|
|
271
285
|
}
|
|
272
286
|
async enterIdpApplicationName(name) {
|
|
@@ -259,6 +259,25 @@ async function withSessionRecovery(page, loginPage, appsPage, modelerBanner, tes
|
|
|
259
259
|
return;
|
|
260
260
|
}
|
|
261
261
|
catch (error) {
|
|
262
|
+
// A mid-flow sign-out redirects to the IdP asynchronously: at the instant
|
|
263
|
+
// action() throws, the tab can still report the Modeler URL for a beat
|
|
264
|
+
// before it commits to weblogin.*, so a single synchronous isSignedOut()
|
|
265
|
+
// check reads "still signed in" and the recovery below never fires -- the
|
|
266
|
+
// error then propagates while the tab drifts on to the login page, which
|
|
267
|
+
// is exactly how a create-button-never-visible timeout presented on the
|
|
268
|
+
// isolated provisioning burst. Give the redirect a bounded window to land
|
|
269
|
+
// before deciding whether this was a dropped session.
|
|
270
|
+
if (!isSignedOut(page)) {
|
|
271
|
+
try {
|
|
272
|
+
await page.waitForURL((url) => url.hostname.startsWith('weblogin.') ||
|
|
273
|
+
(url.hostname.startsWith('modeler.') &&
|
|
274
|
+
/^\/login\/?$/.test(url.pathname)), { timeout: 15000 });
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
// Redirect never landed within the window: this was not a dropped
|
|
278
|
+
// session, so fall through and let the original error propagate.
|
|
279
|
+
}
|
|
280
|
+
}
|
|
262
281
|
if (!isSignedOut(page) || recovery === maxRecoveries) {
|
|
263
282
|
throw error;
|
|
264
283
|
}
|
|
@@ -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: '',
|
|
@@ -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
|
});
|