@camunda/e2e-test-suite 0.0.1052 → 0.0.1054
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/ClusterDetailsPage.d.ts +5 -1
- package/dist/pages/8.10/ClusterDetailsPage.js +79 -19
- package/dist/pages/8.10/ProcessApplicationPage.js +25 -2
- package/dist/pages/8.10/UtilitiesPage.d.ts +1 -0
- package/dist/pages/8.10/UtilitiesPage.js +58 -3
- package/dist/tests/8.10/access-user-role-management.spec.js +3 -3
- package/dist/tests/8.10/connectors-user-flows.spec.js +30 -9
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import { McpCredentials } from '../../utils/mcpSecrets';
|
|
|
3
3
|
import { ClientCredentialsDetailsPage } from './ClientCredentialsDetailsPage';
|
|
4
4
|
declare class ClusterDetailsPage {
|
|
5
5
|
private page;
|
|
6
|
+
private pendingClientScopeCheck;
|
|
6
7
|
readonly apiTab: Locator;
|
|
7
8
|
readonly createFirstClientButton: Locator;
|
|
8
9
|
readonly clientNameTextbox: Locator;
|
|
@@ -62,6 +63,8 @@ declare class ClusterDetailsPage {
|
|
|
62
63
|
clickOptimizeCheckbox(): Promise<void>;
|
|
63
64
|
clickCreateButton(): Promise<void>;
|
|
64
65
|
clickCloseModalButton(): Promise<void>;
|
|
66
|
+
assertAPIClientScopes(name: string): Promise<void>;
|
|
67
|
+
filterClientsList(name: string): Promise<void>;
|
|
65
68
|
clickSettingsTab(): Promise<void>;
|
|
66
69
|
enableAuthorizations(): Promise<void>;
|
|
67
70
|
disableAuthorizations(): Promise<void>;
|
|
@@ -76,7 +79,8 @@ declare class ClusterDetailsPage {
|
|
|
76
79
|
clickCreateAlertButton(): Promise<void>;
|
|
77
80
|
deleteAlerts(): Promise<void>;
|
|
78
81
|
private doDelete;
|
|
79
|
-
assertComponentsHealth(components?: string[]): Promise<void>;
|
|
82
|
+
assertComponentsHealth(components?: string[], recoverSession?: () => Promise<void>): Promise<void>;
|
|
83
|
+
private isSessionLost;
|
|
80
84
|
createAPIClientAndReturnVariables(name: string, setEnvVariables?: boolean, clusterType?: string): Promise<{
|
|
81
85
|
[key: string]: string;
|
|
82
86
|
}>;
|
|
@@ -8,6 +8,9 @@ const expectLocatorWithRetry_1 = require("../../utils/assertionHelpers/expectLoc
|
|
|
8
8
|
const ClientCredentialsDetailsPage_1 = require("./ClientCredentialsDetailsPage");
|
|
9
9
|
class ClusterDetailsPage {
|
|
10
10
|
page;
|
|
11
|
+
// Name of a client whose scopes still have to be checked against the
|
|
12
|
+
// clients list — see createAPIClient() / clickCloseModalButton().
|
|
13
|
+
pendingClientScopeCheck = null;
|
|
11
14
|
apiTab;
|
|
12
15
|
createFirstClientButton;
|
|
13
16
|
clientNameTextbox;
|
|
@@ -235,6 +238,47 @@ class ClusterDetailsPage {
|
|
|
235
238
|
}
|
|
236
239
|
async clickCloseModalButton() {
|
|
237
240
|
await this.closeModalButton.click({ timeout: 60000 });
|
|
241
|
+
const pendingClient = this.pendingClientScopeCheck;
|
|
242
|
+
if (pendingClient === null) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
this.pendingClientScopeCheck = null;
|
|
246
|
+
await this.assertAPIClientScopes(pendingClient);
|
|
247
|
+
}
|
|
248
|
+
// Asserts the scopes an API client was created with, read from its row in
|
|
249
|
+
// the clients list. Only meaningful once the credentials modal is closed:
|
|
250
|
+
// while it is open the panel behind it can still be showing its pre-create
|
|
251
|
+
// state and lists no row for the new client at all.
|
|
252
|
+
async assertAPIClientScopes(name) {
|
|
253
|
+
const row = this.clientsList.filter({ hasText: name });
|
|
254
|
+
const scopes = /(?=.*Orchestration)(?=.*Optimize)(?=.*Secrets)/;
|
|
255
|
+
await this.filterClientsList(name);
|
|
256
|
+
try {
|
|
257
|
+
await (0, test_1.expect)(row).toContainText(scopes, { timeout: 30000 });
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
// A panel that rendered before the client existed — or that failed into
|
|
261
|
+
// the "Oops ... something went wrong." state — only picks the new client
|
|
262
|
+
// up on a refetch, which is what reloading the API tab forces.
|
|
263
|
+
console.warn(`assertAPIClientScopes: "${name}" is not listed yet; reloading the API tab and retrying. ${error}`);
|
|
264
|
+
await this.dismissOopsIfPresent();
|
|
265
|
+
await this.page.reload({ waitUntil: 'domcontentloaded' });
|
|
266
|
+
await this.clickAPITab();
|
|
267
|
+
await this.filterClientsList(name);
|
|
268
|
+
await (0, test_1.expect)(row).toContainText(scopes, { timeout: 60000 });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// Narrows the clients table to one client through the table's own filter.
|
|
272
|
+
// The table paginates at ten rows and is not ordered by creation time, so on
|
|
273
|
+
// a cluster that already has clients a newly created one is usually on a
|
|
274
|
+
// page other than the one being displayed — matching a row without filtering
|
|
275
|
+
// first reports it as missing.
|
|
276
|
+
async filterClientsList(name) {
|
|
277
|
+
const filterInput = this.page.getByRole('searchbox', {
|
|
278
|
+
name: 'Filter table',
|
|
279
|
+
});
|
|
280
|
+
await (0, test_1.expect)(filterInput).toBeVisible({ timeout: 60000 });
|
|
281
|
+
await filterInput.fill(name);
|
|
238
282
|
}
|
|
239
283
|
async clickSettingsTab() {
|
|
240
284
|
await this.settingsTab.click({ timeout: 60000 });
|
|
@@ -411,24 +455,48 @@ class ClusterDetailsPage {
|
|
|
411
455
|
console.error(`Error during ${text} deletion: `, error);
|
|
412
456
|
}
|
|
413
457
|
}
|
|
458
|
+
// This poll reloads the cluster-details page for minutes on end, which is a
|
|
459
|
+
// prime window for the Console SPA to drop its own session: auth0-spa-js
|
|
460
|
+
// calls `/v2/logout` when a silent token renewal fails, and the tab then
|
|
461
|
+
// lands on the identity provider's login form. From that point on every
|
|
462
|
+
// reload only re-renders that form, so the component status can never
|
|
463
|
+
// appear again and all 7 attempts are spent on a login page.
|
|
464
|
+
// `recoverSession` is supplied by the caller (which knows the credentials);
|
|
465
|
+
// it re-authenticates and returns the tab to this cluster's details page so
|
|
466
|
+
// the health assertion itself is retried unchanged.
|
|
414
467
|
async assertComponentsHealth(components = [
|
|
415
468
|
'Zeebe',
|
|
416
469
|
'Tasklist',
|
|
417
470
|
'Operate',
|
|
418
471
|
'Optimize',
|
|
419
472
|
'Admin',
|
|
420
|
-
]) {
|
|
473
|
+
], recoverSession) {
|
|
421
474
|
for (const component of components) {
|
|
422
475
|
await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.page.getByText(`${component}Healthy`, { exact: true }), {
|
|
423
476
|
visibilityTimeout: 60000,
|
|
424
477
|
totalTimeout: 400000,
|
|
425
478
|
maxRetries: 7,
|
|
426
479
|
preAction: async () => {
|
|
480
|
+
if (recoverSession && this.isSessionLost()) {
|
|
481
|
+
await recoverSession();
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
427
484
|
await this.page.reload();
|
|
428
485
|
},
|
|
429
486
|
});
|
|
430
487
|
}
|
|
431
488
|
}
|
|
489
|
+
// True when the tab has been bounced to the identity provider
|
|
490
|
+
// (`weblogin.` / `login.` / `auth.` hosts), i.e. the Console session is
|
|
491
|
+
// gone and no amount of reloading will bring the cluster page back.
|
|
492
|
+
isSessionLost() {
|
|
493
|
+
try {
|
|
494
|
+
return /^(weblogin|login|auth)\./.test(new URL(this.page.url()).host);
|
|
495
|
+
}
|
|
496
|
+
catch {
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
432
500
|
async createAPIClientAndReturnVariables(name, setEnvVariables = true, clusterType) {
|
|
433
501
|
await this.createAPIClient(name);
|
|
434
502
|
await this.clickEnvVarsButton();
|
|
@@ -515,24 +583,16 @@ class ClusterDetailsPage {
|
|
|
515
583
|
await (0, test_1.expect)(this.clientCredentialsDialog
|
|
516
584
|
.getByText('The Client Secret will not be shown again.')
|
|
517
585
|
.first()).toBeVisible();
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
const oopsVisible = await this.page
|
|
529
|
-
.getByRole('heading', { name: /Oops/i })
|
|
530
|
-
.isVisible({ timeout: 1000 })
|
|
531
|
-
.catch(() => false);
|
|
532
|
-
if (!oopsVisible)
|
|
533
|
-
throw err;
|
|
534
|
-
console.warn(`createAPIClient: clients list panel is in Oops state; skipping row scope assertion for "${attemptName}".`);
|
|
535
|
-
}
|
|
586
|
+
// The client now exists — the modal above is proof — so the scopes it
|
|
587
|
+
// was created with are checked in clickCloseModalButton() rather than
|
|
588
|
+
// here. The clients list behind the open modal is not a reliable
|
|
589
|
+
// source for that check: it can still be rendering its pre-create
|
|
590
|
+
// state (no row for this client) or have failed into "Oops ...
|
|
591
|
+
// something went wrong.", and the modal blocks the pointer events a
|
|
592
|
+
// reload would need. Asserting it here instead sent a created client
|
|
593
|
+
// round the retry loop, which both left orphan clients behind and
|
|
594
|
+
// discarded the credentials the caller has yet to read.
|
|
595
|
+
this.pendingClientScopeCheck = attemptName;
|
|
536
596
|
return;
|
|
537
597
|
}
|
|
538
598
|
catch (error) {
|
|
@@ -152,7 +152,6 @@ class ProcessApplicationPage {
|
|
|
152
152
|
// taken -- callers that care assert the name in the deploy dialog anyway.
|
|
153
153
|
async defineDevelopmentStage(clusterName) {
|
|
154
154
|
await (0, test_1.expect)(this.defineStagesDialog).toBeVisible({ timeout: 60000 });
|
|
155
|
-
await this.developmentClusterSelect.click({ timeout: 30000 });
|
|
156
155
|
// Escape before interpolating: a cluster name can carry regex
|
|
157
156
|
// metacharacters -- "Test Cluster (v2)" is the common case -- which would
|
|
158
157
|
// otherwise mis-match or throw. Same treatment as
|
|
@@ -166,7 +165,31 @@ class ProcessApplicationPage {
|
|
|
166
165
|
.getByRole('option')
|
|
167
166
|
.filter({ hasNotText: 'Select a development cluster' })
|
|
168
167
|
.first();
|
|
169
|
-
|
|
168
|
+
// Committing a selection is what unmounts the option list: the dropdown is
|
|
169
|
+
// a Carbon Dropdown over Downshift, so the click that lands is also the
|
|
170
|
+
// click whose target disappears. Playwright's default actionability re-check
|
|
171
|
+
// reads that as a lost element ("element was detached from the DOM,
|
|
172
|
+
// retrying") and then spends the rest of its budget waiting for an option
|
|
173
|
+
// that has already done its job and will not come back -- three of the four
|
|
174
|
+
// failures in the 2026-08-31 SaaS 8.10 run ended that way, each on a
|
|
175
|
+
// 30000ms locator.click against an option the log shows it had resolved.
|
|
176
|
+
//
|
|
177
|
+
// So: dispatch the click without the post-click stability re-check, and
|
|
178
|
+
// treat the option list closing as the proof it landed rather than the
|
|
179
|
+
// click call resolving cleanly. Retried as a whole because a click that
|
|
180
|
+
// genuinely missed leaves the menu open, which the next pass sees and
|
|
181
|
+
// re-clicks. This also covers the menu opening with the wanted cluster
|
|
182
|
+
// already selected: clicking it still closes the menu, and the stage the
|
|
183
|
+
// caller asked for is bound either way.
|
|
184
|
+
await (0, test_1.expect)(async () => {
|
|
185
|
+
const menuOpen = await option.isVisible().catch(() => false);
|
|
186
|
+
if (!menuOpen) {
|
|
187
|
+
await this.developmentClusterSelect.click({ timeout: 15000 });
|
|
188
|
+
await (0, test_1.expect)(option).toBeVisible({ timeout: 15000 });
|
|
189
|
+
}
|
|
190
|
+
await option.click({ timeout: 15000, force: true });
|
|
191
|
+
await (0, test_1.expect)(option).toBeHidden({ timeout: 15000 });
|
|
192
|
+
}).toPass({ timeout: 120000, intervals: [1000, 2000, 5000] });
|
|
170
193
|
await this.defineStagesSaveButton.click({ timeout: 30000 });
|
|
171
194
|
await (0, test_1.expect)(this.defineStagesDialog).toBeHidden({ timeout: 60000 });
|
|
172
195
|
}
|
|
@@ -38,6 +38,7 @@ 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 goBackWithRetry(page: Page, maxRetries?: number): Promise<void>;
|
|
41
42
|
export declare function disableRBA(clusterName: string, homePage: HomePage, clusterPage: ClusterPage, clusterDetailsPage: ClusterDetailsPage, appsPage: AppsPage): Promise<void>;
|
|
42
43
|
export declare function enableAuthorizations(clusterName: string, homePage: HomePage, clusterPage: ClusterPage, clusterDetailsPage: ClusterDetailsPage, appsPage: AppsPage): Promise<void>;
|
|
43
44
|
export declare function clickInvitationLinkInEmail(page: Page, id: string): Promise<void>;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.waitForLoadingToFinish = exports.assertTestUsesCorrectOrganizationFromModeler = exports.expectCountToBeOneOf = exports.createAPIClient = exports.modelAndRunConnectorsTimerEventDiagram = exports.modelAndRunConnectorsDocHandlingDiagram = exports.deleteCluster = exports.deleteAllUserGroups = exports.assertTestUsesCorrectOrganization = exports.assertLatestAlertEmail = exports.clickInvitationLinkInEmail = exports.enableAuthorizations = exports.disableRBA = exports.runMultipleProcesses = exports.runProcess = exports.completeTaskWithRetry = exports.assertPageTextWithRetry = exports.openAppTabWithRetry = exports.assertLocatorVisibleWithRetry = exports.modelRestConnector = exports.modelDiagramFromFile = exports.withSessionRecovery = exports.isSignedOut = exports.loginWithRetry = void 0;
|
|
3
|
+
exports.waitForLoadingToFinish = exports.assertTestUsesCorrectOrganizationFromModeler = exports.expectCountToBeOneOf = exports.createAPIClient = exports.modelAndRunConnectorsTimerEventDiagram = exports.modelAndRunConnectorsDocHandlingDiagram = exports.deleteCluster = exports.deleteAllUserGroups = exports.assertTestUsesCorrectOrganization = exports.assertLatestAlertEmail = exports.clickInvitationLinkInEmail = exports.enableAuthorizations = exports.disableRBA = exports.goBackWithRetry = exports.runMultipleProcesses = exports.runProcess = exports.completeTaskWithRetry = exports.assertPageTextWithRetry = exports.openAppTabWithRetry = exports.assertLocatorVisibleWithRetry = exports.modelRestConnector = exports.modelDiagramFromFile = 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");
|
|
7
7
|
const sleep_1 = require("../../utils/sleep");
|
|
8
8
|
const randomSleep_1 = require("../../utils/randomSleep");
|
|
9
|
+
const LoginPage_1 = require("./LoginPage");
|
|
9
10
|
const fileUpload_1 = require("../../utils/fileUpload");
|
|
10
11
|
const mailSlurpClient_1 = require("../../utils/mailSlurpClient");
|
|
11
12
|
async function clearWebStorage(page) {
|
|
@@ -529,6 +530,59 @@ async function runMultipleProcesses(clusterName, page, modelerHomePage, modelerC
|
|
|
529
530
|
}
|
|
530
531
|
}
|
|
531
532
|
exports.runMultipleProcesses = runMultipleProcesses;
|
|
533
|
+
// The Console SPA drops its own session when auth0-spa-js fails a silent
|
|
534
|
+
// token renewal: it calls `/v2/logout` and the tab is bounced to the identity
|
|
535
|
+
// provider's login form. That happens mid-flight during the minutes-long
|
|
536
|
+
// component-health poll below, after which reloading only ever re-renders the
|
|
537
|
+
// login form. Re-authenticate as the console user and return to the page that
|
|
538
|
+
// was being polled, so the health assertion carries on unchanged.
|
|
539
|
+
function consoleSessionRecovery(page, clusterUrl) {
|
|
540
|
+
return async () => {
|
|
541
|
+
console.warn(`Console session lost while polling component health on ${clusterUrl}; re-authenticating.`);
|
|
542
|
+
await loginWithRetry(page, new LoginPage_1.LoginPage(page), {
|
|
543
|
+
username: process.env.C8_USERNAME,
|
|
544
|
+
password: process.env.C8_PASSWORD,
|
|
545
|
+
}, 0, 3);
|
|
546
|
+
await page.goto(clusterUrl, {
|
|
547
|
+
waitUntil: 'domcontentloaded',
|
|
548
|
+
timeout: 60000,
|
|
549
|
+
});
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
// `page.goBack()` rejects with `net::ERR_ABORTED` when the page it returns to
|
|
553
|
+
// immediately issues a navigation of its own (Console re-runs its auth
|
|
554
|
+
// redirect on entry): the history entry is superseded, not failed, and the
|
|
555
|
+
// browser still ends up on a usable shell -- sometimes after briefly leaving
|
|
556
|
+
// the tab blank. Judge the outcome by what actually rendered rather than by
|
|
557
|
+
// the rejection, and re-request Console directly if no app shell came back.
|
|
558
|
+
async function goBackWithRetry(page, maxRetries = 3) {
|
|
559
|
+
const homePage = new HomePage_1.HomePage(page);
|
|
560
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
561
|
+
try {
|
|
562
|
+
await page.goBack({ waitUntil: 'domcontentloaded', timeout: 60000 });
|
|
563
|
+
}
|
|
564
|
+
catch (error) {
|
|
565
|
+
console.warn(`goBack attempt ${attempt} failed: ${error}`);
|
|
566
|
+
}
|
|
567
|
+
await page
|
|
568
|
+
.waitForLoadState('domcontentloaded', { timeout: 60000 })
|
|
569
|
+
.catch(() => { });
|
|
570
|
+
if (await homePage.camundaComponentsButton
|
|
571
|
+
.first()
|
|
572
|
+
.isVisible({ timeout: 15000 })
|
|
573
|
+
.catch(() => false)) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
await (0, sleep_1.sleep)(2000);
|
|
577
|
+
}
|
|
578
|
+
await page
|
|
579
|
+
.goto('/', { waitUntil: 'domcontentloaded', timeout: 60000 })
|
|
580
|
+
.catch(() => { });
|
|
581
|
+
await (0, test_1.expect)(homePage.camundaComponentsButton.first()).toBeVisible({
|
|
582
|
+
timeout: 60000,
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
exports.goBackWithRetry = goBackWithRetry;
|
|
532
586
|
async function disableRBA(clusterName, homePage, clusterPage, clusterDetailsPage, appsPage) {
|
|
533
587
|
await appsPage.clickCamundaApps();
|
|
534
588
|
await appsPage.clickConsoleLink();
|
|
@@ -546,7 +600,8 @@ async function disableRBA(clusterName, homePage, clusterPage, clusterDetailsPage
|
|
|
546
600
|
await (0, test_1.expect)(clusterDetailsPage.rbaEnabledMessage.first()).not.toBeVisible({
|
|
547
601
|
timeout: 30000,
|
|
548
602
|
});
|
|
549
|
-
|
|
603
|
+
const detailsPage = clusterDetailsPage.settingsTab.page();
|
|
604
|
+
await clusterDetailsPage.assertComponentsHealth(undefined, consoleSessionRecovery(detailsPage, detailsPage.url()));
|
|
550
605
|
}
|
|
551
606
|
exports.disableRBA = disableRBA;
|
|
552
607
|
async function enableAuthorizations(clusterName, homePage, clusterPage, clusterDetailsPage, appsPage) {
|
|
@@ -577,7 +632,7 @@ async function enableAuthorizations(clusterName, homePage, clusterPage, clusterD
|
|
|
577
632
|
await homePage.clickClusters();
|
|
578
633
|
await clusterPage.assertClusterHealthyStatusWithRetry(clusterName, 120000, 600000);
|
|
579
634
|
await clusterPage.clickClusterLink(clusterName);
|
|
580
|
-
await clusterDetailsPage.assertComponentsHealth();
|
|
635
|
+
await clusterDetailsPage.assertComponentsHealth(undefined, consoleSessionRecovery(cdPage, cdPage.url()));
|
|
581
636
|
}
|
|
582
637
|
exports.enableAuthorizations = enableAuthorizations;
|
|
583
638
|
async function clickInvitationLinkInEmail(page, id) {
|
|
@@ -268,7 +268,7 @@ _8_10_1.test.describe('User Roles User Flow', () => {
|
|
|
268
268
|
});
|
|
269
269
|
});
|
|
270
270
|
await _8_10_1.test.step('Navigate to Tasklist and Ensure Tasklist is Accessible', async () => {
|
|
271
|
-
await
|
|
271
|
+
await (0, UtilitiesPage_1.goBackWithRetry)(page);
|
|
272
272
|
await appsPage.clickCamundaApps();
|
|
273
273
|
await appsPage.clickTasklist(clusterName);
|
|
274
274
|
await (0, test_1.expect)(taskPanelPage.taskListPageBanner).toBeVisible({
|
|
@@ -276,7 +276,7 @@ _8_10_1.test.describe('User Roles User Flow', () => {
|
|
|
276
276
|
});
|
|
277
277
|
});
|
|
278
278
|
await _8_10_1.test.step('Navigate to Identity and Ensure Identity is Accessible', async () => {
|
|
279
|
-
await
|
|
279
|
+
await (0, UtilitiesPage_1.goBackWithRetry)(page);
|
|
280
280
|
await appsPage.clickCamundaApps();
|
|
281
281
|
await appsPage.clickAdmin(clusterName);
|
|
282
282
|
await (0, test_1.expect)(ocIdentityHomePage.adminBanner).toBeVisible({
|
|
@@ -287,7 +287,7 @@ _8_10_1.test.describe('User Roles User Flow', () => {
|
|
|
287
287
|
});
|
|
288
288
|
});
|
|
289
289
|
await _8_10_1.test.step('Navigate to Optimize and Ensure Optimize is Accessible', async () => {
|
|
290
|
-
await
|
|
290
|
+
await (0, UtilitiesPage_1.goBackWithRetry)(page);
|
|
291
291
|
await appsPage.clickCamundaApps();
|
|
292
292
|
await appsPage.clickOptimize(clusterName);
|
|
293
293
|
await (0, test_1.expect)(optimizeHomePage.optimizeBanner).toBeVisible({
|
|
@@ -50,9 +50,22 @@ _8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
50
50
|
await (0, test_1.expect)(modelerCreatePage.viewProcessInstanceLink).toBeVisible({
|
|
51
51
|
timeout: 120000,
|
|
52
52
|
});
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
// Latch the Operate instance deep link on the CONTEXT before the click.
|
|
54
|
+
// Playwright raises `popup` only once the new tab's initial response has
|
|
55
|
+
// started loading, and a session-less Operate tab's auth redirect chain
|
|
56
|
+
// drops the instance path before anything commits -- so no listener
|
|
57
|
+
// attached to the popup afterwards ever sees that URL. Without it
|
|
58
|
+
// `waitForInstanceLocator` has nothing to re-open and can only reload
|
|
59
|
+
// whatever the tab was left on, where the instance page can never render:
|
|
60
|
+
// "Assertion of Instance History panel in Operate timed out after
|
|
61
|
+
// 480000ms (instance header not rendered, url=<cluster>/operate?continue,
|
|
62
|
+
// instance deep link never captured)". `openAppTabWithRetry` also
|
|
63
|
+
// registers the popup wait BEFORE the click, which `waitForEvent` after
|
|
64
|
+
// the click cannot. Same pattern as tests/8.10/smoke-tests.spec.ts.
|
|
65
|
+
const watchedInstanceUrl = (0, OperateProcessInstancePage_1.watchOperateInstanceUrl)(page.context());
|
|
66
|
+
const operateTab = await (0, UtilitiesPage_1.openAppTabWithRetry)(page, modelerCreatePage.viewProcessInstanceLink);
|
|
55
67
|
const operateTabProcessInstancePage = new OperateProcessInstancePage_1.OperateProcessInstancePage(operateTab);
|
|
68
|
+
await operateTabProcessInstancePage.rememberInstanceUrl(60000, watchedInstanceUrl);
|
|
56
69
|
await operateTabProcessInstancePage.closePopOverIfVisible();
|
|
57
70
|
await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTabProcessInstancePage, operateTabProcessInstancePage.completedIcon, 'Completed icon', 60000);
|
|
58
71
|
await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTab, operateTabProcessInstancePage.variablesList, 'Variable list', 30000);
|
|
@@ -92,9 +105,12 @@ _8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
92
105
|
await (0, test_1.expect)(modelerCreatePage.viewProcessInstanceLink).toBeVisible({
|
|
93
106
|
timeout: 240000,
|
|
94
107
|
});
|
|
95
|
-
|
|
96
|
-
|
|
108
|
+
// Context-level deep-link latch + click-then-wait ordering, as in the
|
|
109
|
+
// "REST Connector No Auth" flow above -- see the note there.
|
|
110
|
+
const watchedInstanceUrl = (0, OperateProcessInstancePage_1.watchOperateInstanceUrl)(page.context());
|
|
111
|
+
const operateTab = await (0, UtilitiesPage_1.openAppTabWithRetry)(page, modelerCreatePage.viewProcessInstanceLink);
|
|
97
112
|
const operateTabProcessInstancePage = new OperateProcessInstancePage_1.OperateProcessInstancePage(operateTab);
|
|
113
|
+
await operateTabProcessInstancePage.rememberInstanceUrl(60000, watchedInstanceUrl);
|
|
98
114
|
await operateTabProcessInstancePage.closePopOverIfVisible();
|
|
99
115
|
await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTabProcessInstancePage, operateTabProcessInstancePage.completedIcon, 'Completed icon', 60000);
|
|
100
116
|
await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTab, operateTabProcessInstancePage.variablesList, 'Variable list', 60000);
|
|
@@ -328,9 +344,12 @@ _8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
328
344
|
await (0, test_1.expect)(modelerCreatePage.viewProcessInstanceLink).toBeVisible({
|
|
329
345
|
timeout: 120000,
|
|
330
346
|
});
|
|
331
|
-
|
|
332
|
-
|
|
347
|
+
// Context-level deep-link latch + click-then-wait ordering, as in the
|
|
348
|
+
// "REST Connector No Auth" flow above -- see the note there.
|
|
349
|
+
const watchedInstanceUrl = (0, OperateProcessInstancePage_1.watchOperateInstanceUrl)(page.context());
|
|
350
|
+
const operateTab = await (0, UtilitiesPage_1.openAppTabWithRetry)(page, modelerCreatePage.viewProcessInstanceLink);
|
|
333
351
|
const operateTabProcessInstancePage = new OperateProcessInstancePage_1.OperateProcessInstancePage(operateTab);
|
|
352
|
+
await operateTabProcessInstancePage.rememberInstanceUrl(60000, watchedInstanceUrl);
|
|
334
353
|
await operateTabProcessInstancePage.closePopOverIfVisible();
|
|
335
354
|
await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTabProcessInstancePage, operateTabProcessInstancePage.completedIcon, 'Completed icon', 60000);
|
|
336
355
|
await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTab, operateTab.getByTestId('variables-list'), 'Variable list', 30000);
|
|
@@ -448,10 +467,12 @@ _8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
448
467
|
await (0, test_1.expect)(modelerCreatePage.viewProcessInstanceLink).toBeVisible({
|
|
449
468
|
timeout: 120000,
|
|
450
469
|
});
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
const
|
|
470
|
+
// Context-level deep-link latch + click-then-wait ordering, as in the
|
|
471
|
+
// "REST Connector No Auth" flow above -- see the note there.
|
|
472
|
+
const watchedInstanceUrl = (0, OperateProcessInstancePage_1.watchOperateInstanceUrl)(page.context());
|
|
473
|
+
const operateTab = await (0, UtilitiesPage_1.openAppTabWithRetry)(page, modelerCreatePage.viewProcessInstanceLink);
|
|
454
474
|
const operateTabProcessInstancePage = new OperateProcessInstancePage_1.OperateProcessInstancePage(operateTab);
|
|
475
|
+
await operateTabProcessInstancePage.rememberInstanceUrl(60000, watchedInstanceUrl);
|
|
455
476
|
await operateTabProcessInstancePage.closePopOverIfVisible();
|
|
456
477
|
await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTabProcessInstancePage, operateTabProcessInstancePage.completedIcon, 'completed icon');
|
|
457
478
|
// Operate no longer displays document filenames or storage metadata
|