@camunda/e2e-test-suite 0.0.1217 → 0.0.1219
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.
|
@@ -22,7 +22,7 @@ export declare function isSignedOut(page: Page): boolean;
|
|
|
22
22
|
export declare function withSessionRecovery(page: Page, loginPage: LoginPage, appsPage: AppsPage, modelerBanner: Locator, testUser: {
|
|
23
23
|
username: string;
|
|
24
24
|
password: string;
|
|
25
|
-
}, loginStagger: number, action: () => Promise<void
|
|
25
|
+
}, loginStagger: number, action: () => Promise<void>, maxRecoveries?: number): Promise<void>;
|
|
26
26
|
export declare function modelDiagramFromFile(page: Page, modelerHomePage: ModelerHomePage, modelerCreatePage: ModelerCreatePage, processName: string, fileName?: string, nrOfRenamedUserTasks?: number, taskName?: string): Promise<void>;
|
|
27
27
|
export declare function modelRestConnector(modelerCreatePage: ModelerCreatePage, connectorSettingsPage: ConnectorSettingsPage, processName: string, url: string, auth: string, resultExpression: string, resultVariable?: string, basicAuthCredentials?: {
|
|
28
28
|
username: string;
|
|
@@ -235,7 +235,7 @@ function isSignedOut(page) {
|
|
|
235
235
|
}
|
|
236
236
|
exports.isSignedOut = isSignedOut;
|
|
237
237
|
// Runs `action`, and if it fails because the session was dropped mid-flow,
|
|
238
|
-
// signs back in, returns to the Modeler projects list and runs it
|
|
238
|
+
// signs back in, returns to the Modeler projects list and runs it again.
|
|
239
239
|
//
|
|
240
240
|
// Without this the caller's own locator retries keep hunting for app controls
|
|
241
241
|
// on the Auth0 form and expire against a page that is not the app at all —
|
|
@@ -243,21 +243,32 @@ exports.isSignedOut = isSignedOut;
|
|
|
243
243
|
// than as the sign-out it actually was. Only a confirmed sign-out is retried;
|
|
244
244
|
// any other failure propagates untouched, so a genuinely broken control still
|
|
245
245
|
// fails the test.
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
246
|
+
//
|
|
247
|
+
// Recovers up to `maxRecoveries` times rather than once: under a concurrent
|
|
248
|
+
// login burst the SaaS `/api/internal/login` hand-off can 500 and bounce the
|
|
249
|
+
// tab back to Auth0 (see AppsPage.openModelerWithRetry) more than once in a
|
|
250
|
+
// row, so the lowest-staggered workers get dropped, recovered, then dropped
|
|
251
|
+
// again — spending the single recovery before landing a stable session. A
|
|
252
|
+
// bounded re-login loop (the same shape as loginWithRetry's own retries) lets
|
|
253
|
+
// those tail sessions settle; a session that never stabilises still exhausts
|
|
254
|
+
// the loop and fails, so a genuinely broken login is not masked.
|
|
255
|
+
async function withSessionRecovery(page, loginPage, appsPage, modelerBanner, testUser, loginStagger, action, maxRecoveries = 2) {
|
|
256
|
+
for (let recovery = 0; recovery <= maxRecoveries; recovery++) {
|
|
257
|
+
try {
|
|
258
|
+
await action();
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
if (!isSignedOut(page) || recovery === maxRecoveries) {
|
|
263
|
+
throw error;
|
|
264
|
+
}
|
|
265
|
+
console.warn(`Session was dropped mid-flow (tab landed on ${page.url()}); ` +
|
|
266
|
+
`signing back in and retrying (recovery ${recovery + 1} of ` +
|
|
267
|
+
`${maxRecoveries}).`);
|
|
254
268
|
}
|
|
255
|
-
|
|
256
|
-
|
|
269
|
+
await loginWithRetry(page, loginPage, testUser, loginStagger);
|
|
270
|
+
await appsPage.openModelerWithRetry(modelerBanner);
|
|
257
271
|
}
|
|
258
|
-
await loginWithRetry(page, loginPage, testUser, loginStagger);
|
|
259
|
-
await appsPage.openModelerWithRetry(modelerBanner);
|
|
260
|
-
await action();
|
|
261
272
|
}
|
|
262
273
|
exports.withSessionRecovery = withSessionRecovery;
|
|
263
274
|
async function modelDiagramFromFile(page, modelerHomePage, modelerCreatePage, processName, fileName = '', nrOfRenamedUserTasks = 0, taskName = '') {
|
|
@@ -12,6 +12,7 @@ const apiHelpers_1 = require("../../utils/apiHelpers");
|
|
|
12
12
|
const expectLocatorWithRetry_1 = require("../../utils/assertionHelpers/expectLocatorWithRetry");
|
|
13
13
|
const users_1 = require("../../utils/users");
|
|
14
14
|
const connectorSecrets_1 = require("../../utils/connectorSecrets");
|
|
15
|
+
const constants_1 = require("../../utils/constants");
|
|
15
16
|
const testUser = (0, users_1.getTestUser)('seventeenthUser');
|
|
16
17
|
_8_10_1.test.describe.configure({ mode: 'parallel' });
|
|
17
18
|
_8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
@@ -271,19 +272,21 @@ _8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
271
272
|
await operateHomePage.clickProcessesTab();
|
|
272
273
|
await operateProcessesPage.clickProcessCompletedCheckbox();
|
|
273
274
|
await operateProcessesPage.clickProcessInstanceLink(processName);
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
275
|
+
// Poll the instance deep link rather than reloading whatever page the
|
|
276
|
+
// tab is left on, the same way the identical processes-list flow in
|
|
277
|
+
// smoke-tests.spec.ts does. The completed filter also lists still-ACTIVE
|
|
278
|
+
// instances, so the link is regularly opened before this flow's REST
|
|
279
|
+
// connector has finished round-tripping to a real AWS endpoint, and
|
|
280
|
+
// expectLocatorWithRetry is blind to both that and to Operate's 404
|
|
281
|
+
// bounce: it reported only "Failed to assert visibility locator ...
|
|
282
|
+
// after 4 attempts" (run 35595548687) without saying which state the
|
|
283
|
+
// header had reached. waitForInstanceLocator re-asks Operate every few
|
|
284
|
+
// seconds within the same budget the other import gates use, names the
|
|
285
|
+
// rendered state on timeout, and fails immediately with the incident
|
|
286
|
+
// text when the connector job raised one -- COMPLETED is unreachable
|
|
287
|
+
// then, so waiting out the budget only hides the cause.
|
|
288
|
+
await operateProcessInstancePage.rememberInstanceUrl(constants_1.TIMEOUT.navigation);
|
|
289
|
+
await operateProcessInstancePage.waitForInstanceLocator(operateProcessInstancePage.completedIcon, 'Completed icon', constants_1.TIMEOUT.processImport);
|
|
287
290
|
await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(page, operateProcessInstancePage.variablesList, {
|
|
288
291
|
postAction: async () => {
|
|
289
292
|
await page.reload();
|
|
@@ -177,11 +177,12 @@ _8_10_1.test.describe('Optimize Sharing & Export UI Tests @optimize-sharing-aler
|
|
|
177
177
|
});
|
|
178
178
|
(0, _8_10_1.test)('Enable dashboard sharing and expose a public share link', async ({ appsPage, optimizeHomePage, optimizeSharePage, }) => {
|
|
179
179
|
await _8_10_1.test.step('Open Optimize from the Camunda apps launcher', async () => {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
// Same retrying opener as the two tests above, for the same reason: a
|
|
181
|
+
// /sso-callback that answers 401 leaves the tab on a blank page that
|
|
182
|
+
// never renders the banner, and polling it can only watch that dead
|
|
183
|
+
// page time out (`expect(received).toBe(expected)` after 120s). Only
|
|
184
|
+
// re-entering through the apps menu starts a fresh hand-off.
|
|
185
|
+
await appsPage.openOptimizeWithRetry(optimizeHomePage.optimizeBanner, clusterName);
|
|
185
186
|
});
|
|
186
187
|
await _8_10_1.test.step('Navigate to the seeded dashboard', async () => {
|
|
187
188
|
await optimizeSharePage.openEntityById(baseUrl, 'dashboard', dashboardId);
|
package/dist/utils/apiHelpers.js
CHANGED
|
@@ -740,6 +740,28 @@ async function probeOptimizeSession(page) {
|
|
|
740
740
|
}
|
|
741
741
|
return token;
|
|
742
742
|
}
|
|
743
|
+
// Starts a fresh OIDC code exchange with Optimize without touching the
|
|
744
|
+
// identity provider session. The 401 that probeOptimizeSession catches is the
|
|
745
|
+
// cluster rejecting one exchange, not the IdP rejecting the user: the Auth0
|
|
746
|
+
// session minted moments earlier is still valid, so navigating to Optimize
|
|
747
|
+
// again sends the browser back through the IdP, which answers from that
|
|
748
|
+
// session and returns a brand new code. That costs seconds and no Auth0
|
|
749
|
+
// credential round trip, where a full re-login costs another interactive
|
|
750
|
+
// sign-in plus the minutes of backoff that precede it -- and there are only
|
|
751
|
+
// four of those before the caller's beforeAll fails outright with
|
|
752
|
+
// OPTIMIZE_SESSION_UNAUTHENTICATED.
|
|
753
|
+
async function redoOptimizeSsoHandshake(page) {
|
|
754
|
+
const optimizeUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
|
|
755
|
+
await page.goto(optimizeUrl, { waitUntil: 'domcontentloaded', timeout: 60000 });
|
|
756
|
+
// The redirect chain has to land back on Optimize for the exchange to have
|
|
757
|
+
// completed. A chain that stops anywhere else (the IdP asking for
|
|
758
|
+
// credentials again, another rejected callback) throws a waitForURL
|
|
759
|
+
// timeout, which the caller's catch already classifies and backs off for.
|
|
760
|
+
await page.waitForURL((url) => url.toString().startsWith(optimizeUrl), {
|
|
761
|
+
timeout: 60000,
|
|
762
|
+
waitUntil: 'commit',
|
|
763
|
+
});
|
|
764
|
+
}
|
|
743
765
|
async function performOptimizeInteractiveLogin(page, callerLabel, verifySession) {
|
|
744
766
|
const optimizeUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
|
|
745
767
|
const maxLoginAttempts = 4;
|
|
@@ -764,7 +786,15 @@ async function performOptimizeInteractiveLogin(page, callerLabel, verifySession)
|
|
|
764
786
|
await page.context().clearCookies();
|
|
765
787
|
await (0, utils_1.loginToApp)(optimizeUrl, page);
|
|
766
788
|
if (verifySession && !(await verifySession(page))) {
|
|
767
|
-
|
|
789
|
+
// Redo the hand-off once before writing the attempt off: the rejected
|
|
790
|
+
// exchange is what has to be replaced, and re-navigating replaces it
|
|
791
|
+
// for seconds (see redoOptimizeSsoHandshake). Only when a second,
|
|
792
|
+
// freshly minted exchange is also unauthenticated is the login itself
|
|
793
|
+
// worth repeating.
|
|
794
|
+
await redoOptimizeSsoHandshake(page);
|
|
795
|
+
if (!(await verifySession(page))) {
|
|
796
|
+
throw new Error(OPTIMIZE_SESSION_UNAUTHENTICATED);
|
|
797
|
+
}
|
|
768
798
|
}
|
|
769
799
|
return;
|
|
770
800
|
}
|