@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.
@@ -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
- try {
519
- await (0, test_1.expect)(this.clientsList.filter({ hasText: attemptName })).toContainText(/(?=.*Orchestration)(?=.*Optimize)(?=.*Secrets)/, {
520
- timeout: 10000,
521
- });
522
- }
523
- catch (err) {
524
- // The clients list panel can land in "Oops ... something went wrong."
525
- // while the post-create modal is still open. The modal blocks pointer
526
- // events so we can't click Reload here; the modal's "Client Secret will
527
- // not be shown again" message above already proves the client exists.
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
- await option.click({ timeout: 30000 });
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
- await clusterDetailsPage.assertComponentsHealth();
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 page.goBack();
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 page.goBack();
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 page.goBack();
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
- await modelerCreatePage.clickViewProcessInstanceLink();
54
- const operateTab = await page.waitForEvent('popup', { timeout: 60000 });
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
- await modelerCreatePage.clickViewProcessInstanceLink();
96
- const operateTab = await page.waitForEvent('popup', { timeout: 60000 });
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
- await modelerCreatePage.clickViewProcessInstanceLink();
332
- const operateTab = await page.waitForEvent('popup');
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
- await modelerCreatePage.clickViewProcessInstanceLink();
452
- const operateTabPromise = page.waitForEvent('popup', { timeout: 60000 });
453
- const operateTab = await operateTabPromise;
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.1052",
3
+ "version": "0.0.1054",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",