@camunda/e2e-test-suite 0.0.1286 → 0.0.1288

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.
Files changed (34) hide show
  1. package/dist/pages/8.9/AppsPage.d.ts +9 -3
  2. package/dist/pages/8.9/AppsPage.js +262 -47
  3. package/dist/pages/8.9/ClusterDetailsPage.d.ts +5 -0
  4. package/dist/pages/8.9/ClusterDetailsPage.js +92 -9
  5. package/dist/pages/8.9/ClusterSecretsPage.js +5 -1
  6. package/dist/pages/8.9/ConnectorMarketplacePage.d.ts +1 -0
  7. package/dist/pages/8.9/ConnectorMarketplacePage.js +46 -3
  8. package/dist/pages/8.9/ConsoleOrganizationPage.d.ts +3 -2
  9. package/dist/pages/8.9/ConsoleOrganizationPage.js +47 -22
  10. package/dist/pages/8.9/FormJsPage.d.ts +4 -0
  11. package/dist/pages/8.9/FormJsPage.js +53 -19
  12. package/dist/pages/8.9/IdpCreatePage.js +5 -0
  13. package/dist/pages/8.9/ModelerCreatePage.d.ts +5 -0
  14. package/dist/pages/8.9/ModelerCreatePage.js +153 -21
  15. package/dist/pages/8.9/ModelerHomePage.d.ts +1 -0
  16. package/dist/pages/8.9/ModelerHomePage.js +29 -4
  17. package/dist/pages/8.9/PlayPage.js +11 -1
  18. package/dist/pages/8.9/ProcessApplicationPage.js +48 -4
  19. package/dist/pages/8.9/UtilitiesPage.js +57 -17
  20. package/dist/pages/8.9/WorkspaceMembersPage.d.ts +21 -0
  21. package/dist/pages/8.9/WorkspaceMembersPage.js +264 -0
  22. package/dist/tests/8.9/access-user-role-management.spec.js +111 -26
  23. package/dist/tests/8.9/console-user-flows.spec.js +4 -4
  24. package/dist/tests/8.9/idp-user-flows.spec.js +2 -1
  25. package/dist/tests/8.9/rba-enabled-v1-user-flows.spec.js +13 -4
  26. package/dist/tests/8.9/rba-enabled-v2-user-flows.spec.js +13 -4
  27. package/dist/tests/8.9/test-setup.spec.js +57 -4
  28. package/dist/tests/8.9/utr-enabled-user-flows.spec.js +18 -0
  29. package/dist/tests/8.9/web-modeler-user-flows.spec.js +6 -2
  30. package/dist/tests/SM-8.7/topology-orchestration-smoke.spec.js +11 -0
  31. package/dist/utils/consoleApiHelpers.d.ts +2 -0
  32. package/dist/utils/consoleApiHelpers.js +50 -4
  33. package/dist/utils/resetSession.js +6 -1
  34. package/package.json +1 -1
@@ -9,6 +9,7 @@ const roleAuthorizations_1 = require("../../utils/roleAuthorizations");
9
9
  const expectTextWithRetry_1 = require("../../utils/assertionHelpers/expectTextWithRetry");
10
10
  const expectLocatorWithRetry_1 = require("../../utils/assertionHelpers/expectLocatorWithRetry");
11
11
  const users_1 = require("../../utils/users");
12
+ const consoleApiHelpers_1 = require("../../utils/consoleApiHelpers");
12
13
  const mainUser = (0, users_1.getTestUser)('twentyFifthUser');
13
14
  _8_9_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV1', () => {
14
15
  const clusterNames = {
@@ -18,19 +19,30 @@ _8_9_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV1', () => {
18
19
  'RBA On User Flow - Permission for Selected Processes Only': 'Fourth RBA V1 Cluster',
19
20
  };
20
21
  _8_9_1.test.beforeEach(async ({ page, homePage, clusterPage, loginPage, clusterDetailsPage, appsPage }, testInfo) => {
22
+ // Claimed here, not in each body: creating the cluster and waiting for
23
+ // it to come back Healthy after the authorizations toggle can outlast
24
+ // the default per-test timeout before the body starts.
25
+ _8_9_1.test.slow();
21
26
  await (0, UtilitiesPage_1.loginWithRetry)(page, loginPage, mainUser, (testInfo.workerIndex + 1) * 1000);
22
27
  await homePage.clickClusters();
23
28
  const clusterName = clusterNames[testInfo.title];
24
29
  (0, test_1.expect)(clusterName, `No cluster mapping for test title: "${testInfo.title}")`).toBeTruthy();
25
30
  await clusterPage.createClusterIfNotExists(clusterName);
26
31
  await (0, UtilitiesPage_1.disableRBA)(clusterName, homePage, clusterPage, clusterDetailsPage, appsPage);
32
+ // The flows below hand off to test users with no elevated or DevOps org
33
+ // role, and Hub only lets those roles assign an environment to a
34
+ // workspace (camunda-hub#28955). Without this, the test user's deploy
35
+ // dialog never offers this cluster and every runProcessInstance retry
36
+ // fails until the test times out. Assign it now, while the elevated
37
+ // main user's session is active. test-setup creates the test users'
38
+ // workspaces up front so that they exist at this point.
39
+ await (0, consoleApiHelpers_1.linkClusterToWorkspacesNamedViaApi)(page, clusterName);
27
40
  });
28
41
  _8_9_1.test.afterEach(async ({ page }, testInfo) => {
29
42
  await (0, _setup_1.captureScreenshot)(page, testInfo);
30
43
  await (0, _setup_1.captureFailureVideo)(page, testInfo);
31
44
  });
32
45
  (0, _8_9_1.test)('RBA On User Flow - No User Permission', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, clusterPage, clusterDetailsPage, taskPanelPage, taskProcessesPage, operateHomePage, ocIdentityHomePage, ocIdentityAuthorizationsPage, ocIdentityRolesPage, loginPage, settingsPage, }, testInfo) => {
33
- _8_9_1.test.slow();
34
46
  const clusterName = clusterNames[testInfo.title];
35
47
  const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
36
48
  const processName = 'RBA_NO_PERM' + randomString;
@@ -150,7 +162,6 @@ _8_9_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV1', () => {
150
162
  });
151
163
  });
152
164
  (0, _8_9_1.test)('RBA On User Flow - Permission for One Process', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, clusterPage, clusterDetailsPage, taskPanelPage, taskProcessesPage, operateHomePage, loginPage, ocIdentityHomePage, ocIdentityAuthorizationsPage, ocIdentityRolesPage, settingsPage, }, testInfo) => {
153
- _8_9_1.test.slow();
154
165
  const clusterName = clusterNames[testInfo.title];
155
166
  const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
156
167
  const processName = 'RBA_ONE_PERM' + randomString;
@@ -298,7 +309,6 @@ _8_9_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV1', () => {
298
309
  });
299
310
  });
300
311
  (0, _8_9_1.test)('RBA On User Flow - Permission for All Processes with Full Permissions', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, clusterPage, clusterDetailsPage, taskPanelPage, taskProcessesPage, operateHomePage, ocIdentityHomePage, ocIdentityAuthorizationsPage, }, testInfo) => {
301
- _8_9_1.test.slow();
302
312
  const clusterName = clusterNames[testInfo.title];
303
313
  const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
304
314
  const processName = 'AFTER_RBA' + randomString;
@@ -367,7 +377,6 @@ _8_9_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV1', () => {
367
377
  });
368
378
  });
369
379
  (0, _8_9_1.test)('RBA On User Flow - Permission for Selected Processes Only', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, clusterPage, clusterDetailsPage, taskPanelPage, taskProcessesPage, operateHomePage, ocIdentityHomePage, ocIdentityAuthorizationsPage, ocIdentityRolesPage, loginPage, }, testInfo) => {
370
- _8_9_1.test.slow();
371
380
  const clusterName = clusterNames[testInfo.title];
372
381
  const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
373
382
  const processName = 'RBA_SELECTED_PERM' + randomString;
@@ -9,6 +9,7 @@ const roleAuthorizations_1 = require("../../utils/roleAuthorizations");
9
9
  const expectTextWithRetry_1 = require("../../utils/assertionHelpers/expectTextWithRetry");
10
10
  const expectTextWithPagination_1 = require("../../utils/assertionHelpers/expectTextWithPagination");
11
11
  const users_1 = require("../../utils/users");
12
+ const consoleApiHelpers_1 = require("../../utils/consoleApiHelpers");
12
13
  const mainUser = (0, users_1.getTestUser)('twentySixthUser');
13
14
  _8_9_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV2', () => {
14
15
  const clusterNames = {
@@ -18,6 +19,10 @@ _8_9_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV2', () => {
18
19
  'RBA On User Flow - Permission for Selected Processes Only': 'Fourth RBA V2 Cluster',
19
20
  };
20
21
  _8_9_1.test.beforeEach(async ({ page, homePage, clusterPage, loginPage, clusterDetailsPage, appsPage }, testInfo) => {
22
+ // Claimed here, not in each body: creating the cluster and waiting for
23
+ // it to come back Healthy after the authorizations toggle can outlast
24
+ // the default per-test timeout before the body starts.
25
+ _8_9_1.test.slow();
21
26
  await (0, UtilitiesPage_1.loginWithRetry)(page, loginPage, mainUser, (testInfo.workerIndex + 1) * 1000);
22
27
  await (0, test_1.expect)(homePage.clusterTab).toBeVisible({ timeout: 120000 });
23
28
  await homePage.clickClusters();
@@ -25,13 +30,20 @@ _8_9_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV2', () => {
25
30
  (0, test_1.expect)(clusterName, `No cluster mapping for test title: "${testInfo.title}")`).toBeTruthy();
26
31
  await clusterPage.createClusterIfNotExists(clusterName);
27
32
  await (0, UtilitiesPage_1.disableRBA)(clusterName, homePage, clusterPage, clusterDetailsPage, appsPage);
33
+ // The flows below hand off to test users with no elevated or DevOps org
34
+ // role, and Hub only lets those roles assign an environment to a
35
+ // workspace (camunda-hub#28955). Without this, the test user's deploy
36
+ // dialog never offers this cluster and every runProcessInstance retry
37
+ // fails until the test times out. Assign it now, while the elevated
38
+ // main user's session is active. test-setup creates the test users'
39
+ // workspaces up front so that they exist at this point.
40
+ await (0, consoleApiHelpers_1.linkClusterToWorkspacesNamedViaApi)(page, clusterName);
28
41
  });
29
42
  _8_9_1.test.afterEach(async ({ page }, testInfo) => {
30
43
  await (0, _setup_1.captureScreenshot)(page, testInfo);
31
44
  await (0, _setup_1.captureFailureVideo)(page, testInfo);
32
45
  });
33
46
  (0, _8_9_1.test)('RBA On User Flow - No User Permission', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, clusterPage, clusterDetailsPage, taskPanelPage, taskProcessesPage, operateHomePage, ocIdentityHomePage, ocIdentityAuthorizationsPage, ocIdentityRolesPage, loginPage, settingsPage, }, testInfo) => {
34
- _8_9_1.test.slow();
35
47
  const clusterName = clusterNames[testInfo.title];
36
48
  const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
37
49
  const processName = 'RBA_NO_PERM' + randomString;
@@ -137,7 +149,6 @@ _8_9_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV2', () => {
137
149
  });
138
150
  });
139
151
  (0, _8_9_1.test)('RBA On User Flow - Permission for One Process', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, clusterPage, clusterDetailsPage, taskPanelPage, taskProcessesPage, operateHomePage, loginPage, ocIdentityHomePage, ocIdentityAuthorizationsPage, ocIdentityRolesPage, settingsPage, }, testInfo) => {
140
- _8_9_1.test.slow();
141
152
  const clusterName = clusterNames[testInfo.title];
142
153
  const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
143
154
  const processName = 'RBA_ONE_PERM' + randomString;
@@ -292,7 +303,6 @@ _8_9_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV2', () => {
292
303
  });
293
304
  });
294
305
  (0, _8_9_1.test)('RBA On User Flow - Permission for All Processes with Full Permissions', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, clusterPage, clusterDetailsPage, taskPanelPage, taskProcessesPage, operateHomePage, ocIdentityHomePage, ocIdentityAuthorizationsPage, }, testInfo) => {
295
- _8_9_1.test.slow();
296
306
  const clusterName = clusterNames[testInfo.title];
297
307
  const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
298
308
  const processName = 'AFTER_RBA' + randomString;
@@ -361,7 +371,6 @@ _8_9_1.test.describe.parallel('RBA Enabled User Flows Test @tasklistV2', () => {
361
371
  });
362
372
  });
363
373
  (0, _8_9_1.test)('RBA On User Flow - Permission for Selected Processes Only', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, clusterPage, clusterDetailsPage, taskPanelPage, taskProcessesPage, operateHomePage, ocIdentityHomePage, ocIdentityAuthorizationsPage, ocIdentityRolesPage, loginPage, }, testInfo) => {
364
- _8_9_1.test.slow();
365
374
  const clusterName = clusterNames[testInfo.title];
366
375
  const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
367
376
  const processName = 'RBA_SELECTED_PERM' + randomString;
@@ -123,11 +123,64 @@ _8_9_1.test.describe('Cluster Setup Tests', () => {
123
123
  await clusterSecretsPage.createSetOfSecrets(clusterName, connectorSecrets_1.connectorSecretsValues);
124
124
  }
125
125
  });
126
- for (const [index, user] of (0, users_1.getTestUsers)().entries()) {
127
- (0, _8_9_1.test)(`Create Project Folder for User ${index + 1}`, async ({ page, loginPage, homePage, appsPage, modelerHomePage, }, testInfo) => {
128
- await _8_9_1.test.step('Log in', async () => {
129
- await (0, UtilitiesPage_1.loginWithRetry)(page, loginPage, user, (testInfo.workerIndex + 1) * 1000);
126
+ // The RBA specs hand off to these developer-role users, who cannot assign an
127
+ // environment to a workspace (camunda-hub#28955). Their workspaces must exist
128
+ // before the RBA beforeEach links its cluster to them as the admin main user.
129
+ const rbaHandoffUsers = process.env.IS_AG === 'true' && process.env.RUN_FULL_SUITE !== 'true'
130
+ ? []
131
+ : ['4', '5', '6']
132
+ .map((n) => ({
133
+ username: process.env[`C8_USERNAME_TEST_${n}`] ?? '',
134
+ password: process.env[`C8_PASSWORD_TEST_${n}`] ?? '',
135
+ title: `Create Project Folder for RBA hand-off user TEST_${n}`,
136
+ }))
137
+ .filter((user) => user.username !== '' && user.password !== '');
138
+ for (const [index, user] of [
139
+ ...(0, users_1.getTestUsers)().map((u, i) => ({
140
+ ...u,
141
+ title: `Create Project Folder for User ${i + 1}`,
142
+ })),
143
+ ...rbaHandoffUsers,
144
+ ].entries()) {
145
+ const isRbaHandoffUser = index >= (0, users_1.getTestUsers)().length;
146
+ (0, _8_9_1.test)(user.title, async ({ page, loginPage, homePage, appsPage, modelerHomePage }, testInfo) => {
147
+ const loggedIn = await _8_9_1.test.step('Log in', async () => {
148
+ // GitHub drops a create-organization job output whose value contains
149
+ // a masked secret, which leaves that slot's password empty. Only the
150
+ // specs that log in as this user need its workspace, and they fail
151
+ // on their own login with loginWithRetry's "no password" error.
152
+ if (index > 0 && user.username && !user.password) {
153
+ console.warn(`${user.title}: no password reached the runner`);
154
+ testInfo.annotations.push({
155
+ type: 'warning',
156
+ description: 'Password job output was dropped; no workspace',
157
+ });
158
+ return false;
159
+ }
160
+ try {
161
+ await (0, UtilitiesPage_1.loginWithRetry)(page, loginPage, user, (testInfo.workerIndex + 1) * 1000);
162
+ return true;
163
+ }
164
+ catch (error) {
165
+ // Every test depends on this setup project. A hand-off slot that
166
+ // this org did not provision (Auth0 rejected it in a PR run) must
167
+ // not block the whole suite; the RBA spec that logs in as that
168
+ // user fails on its own login instead.
169
+ if (!isRbaHandoffUser ||
170
+ !String(error).includes('AUTH0_INVALID_CREDENTIALS')) {
171
+ throw error;
172
+ }
173
+ console.warn(`${user.title}: ${String(error)}`);
174
+ testInfo.annotations.push({
175
+ type: 'warning',
176
+ description: 'Auth0 rejected this hand-off user; no workspace',
177
+ });
178
+ return false;
179
+ }
130
180
  });
181
+ if (!loggedIn) {
182
+ return;
183
+ }
131
184
  await _8_9_1.test.step('Navigate to Web Modeler', async () => {
132
185
  // Camunda Hub replaced Console + Web Modeler on SaaS: it lands on the
133
186
  // Modeler home directly and has no app switcher to wait for.
@@ -8,7 +8,9 @@ const sleep_1 = require("../../utils/sleep");
8
8
  const expectTextWithRetry_1 = require("../../utils/assertionHelpers/expectTextWithRetry");
9
9
  const expectLocatorWithRetry_1 = require("../../utils/assertionHelpers/expectLocatorWithRetry");
10
10
  const LoginPage_1 = require("../../pages/8.9/LoginPage");
11
+ const WorkspaceMembersPage_1 = require("../../pages/8.9/WorkspaceMembersPage");
11
12
  const users_1 = require("../../utils/users");
13
+ const consoleApiHelpers_1 = require("../../utils/consoleApiHelpers");
12
14
  const mainUser = (0, users_1.getTestUser)('twelfthUser');
13
15
  _8_9_1.test.describe.parallel('UTR Enabled User Flows Test @tasklistV1', () => {
14
16
  const clusterName = 'Test Cluster';
@@ -129,6 +131,22 @@ _8_9_1.test.describe.parallel('UTR Enabled User Flows Test @tasklistV1', () => {
129
131
  await ocIdentityGroupsPage.clickGroupId(groupNameMultiple);
130
132
  await ocIdentityGroupsPage.assignUserToGroup(testUser2.username);
131
133
  });
134
+ // Test user 1 is the org's Developer. Hub shows a non-admin only the
135
+ // environments assigned to a workspace they are Editor or Workspace Admin
136
+ // on (camunda-hub EnvironmentVisibilityResolver), so without this the
137
+ // Tasklist hand-off below finds no "Test Cluster" in the sidebar. Test
138
+ // user 2 is an org Admin and sees every environment already.
139
+ await _8_9_1.test.step('Grant Test User 1 Editor rights on a workspace with the cluster', async () => {
140
+ await appsPage.clickCamundaApps();
141
+ await appsPage.clickModeler();
142
+ await (0, test_1.expect)(modelerHomePage.modelerPageBanner).toBeVisible({
143
+ timeout: 120000,
144
+ });
145
+ const workspaceMembersPage = new WorkspaceMembersPage_1.WorkspaceMembersPage(page);
146
+ await workspaceMembersPage.openManageMembers('Cross Component Test Project');
147
+ await workspaceMembersPage.setMemberRole(testUser1.username, 'Editor');
148
+ await (0, consoleApiHelpers_1.linkClusterToWorkspaceViaApi)(page, workspaceMembersPage.workspaceId(), clusterName);
149
+ });
132
150
  await _8_9_1.test.step('Navigate to Web Modeler', async () => {
133
151
  await appsPage.clickCamundaApps();
134
152
  await appsPage.clickModeler();
@@ -34,7 +34,9 @@ _8_9_1.test.describe('Web Modeler User Flow Tests', () => {
34
34
  await (0, mailSlurpClient_1.deleteInbox)(inboxes[testInfo.title]);
35
35
  }
36
36
  });
37
- (0, _8_9_1.test)('Form.js Integration with User Task and AI Generated Form @ai-external', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, formJsPage, }) => {
37
+ // Skipped due to bug #29346: https://github.com/camunda/camunda-hub/issues/29346
38
+ // Also tracked in https://github.com/camunda/product-hub/issues/3655
39
+ _8_9_1.test.skip('Form.js Integration with User Task and AI Generated Form @ai-external', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, formJsPage, }) => {
38
40
  _8_9_1.test.slow();
39
41
  const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
40
42
  const processName = 'User_Task_Process_With_AI_Form' + randomString;
@@ -92,7 +94,9 @@ _8_9_1.test.describe('Web Modeler User Flow Tests', () => {
92
94
  await operateTabTaskDetailsPage.clickCompleteTaskButton();
93
95
  });
94
96
  });
95
- (0, _8_9_1.test)('Form.js Integration with AI generated Start form @ai-external', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, formJsPage, }) => {
97
+ // Skipped due to bug #29346: https://github.com/camunda/camunda-hub/issues/29346
98
+ // Also tracked in https://github.com/camunda/product-hub/issues/3655
99
+ _8_9_1.test.skip('Form.js Integration with AI generated Start form @ai-external', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, formJsPage, }) => {
96
100
  _8_9_1.test.slow();
97
101
  const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
98
102
  const processName = 'AI-generated-Start-form' + randomString;
@@ -20,6 +20,17 @@ SM_8_7_1.test.afterEach(async ({ page }, testInfo) => {
20
20
  const modelerHomePage = new TopologyModelerHomePage_1.TopologyModelerHomePage(page);
21
21
  const modelerCreatePage = new ModelerCreatePage_1.ModelerCreatePage(page);
22
22
  (0, KeycloakUtils_1.setAdminUserCredentials)(navigationPage);
23
+ // The 8.7 Hub shows a one-time "Effortless BPMN Project Deployment"
24
+ // advertising modal the first time a process application is created
25
+ // (ProcessApplicationStore gates it on the process_application_adv_shown_once
26
+ // localStorage flag). Each Playwright test starts with an empty context, so
27
+ // the modal always renders and covers the inline name input the shared
28
+ // creation flow waits on ([data-test="editable-input"]). Pre-seed the
29
+ // "already shown" flag -- the same suppression Hub's own e2e suite uses -- so
30
+ // the flow reaches the create-resource step directly.
31
+ await page.addInitScript(() => {
32
+ window.localStorage.setItem('process_application_adv_shown_once', 'true');
33
+ });
23
34
  await SM_8_7_1.test.step('Create and start a process in Web Modeler', async () => {
24
35
  await hubNavigationPage.goToModeler();
25
36
  await modelerHomePage.clickCreateNewProjectButton();
@@ -1,4 +1,5 @@
1
1
  import { Page } from '@playwright/test';
2
+ export declare function observeBearerTokens(page: Page): void;
2
3
  export declare function resetHubBearerToken(page: Page): void;
3
4
  export declare function deleteClusterViaApi(page: Page, clusterName: string): Promise<void>;
4
5
  export declare function createClusterViaApi(page: Page, clusterName: string, region?: string, tasklistV2Enabled?: boolean): Promise<string>;
@@ -13,4 +14,5 @@ export declare function linkClusterToWorkspaceViaApi(page: Page, workspaceId: st
13
14
  export declare function rememberHubWorkspaceId(page: Page, workspaceId: string): void;
14
15
  export declare function getHubWorkspaceId(page: Page): string | undefined;
15
16
  export declare function linkClusterToSharedWorkspaceViaApi(page: Page, clusterName: string): Promise<void>;
17
+ export declare function linkClusterToWorkspacesNamedViaApi(page: Page, clusterName: string, workspaceName?: string): Promise<void>;
16
18
  export declare function ensureClusterLinkedToSharedWorkspace(page: Page, clusterName: string): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ensureClusterLinkedToSharedWorkspace = exports.linkClusterToSharedWorkspaceViaApi = exports.getHubWorkspaceId = exports.rememberHubWorkspaceId = exports.linkClusterToWorkspaceViaApi = exports.waitForAiFeaturesEnabledViaApi = exports.areAiFeaturesEnabledViaApi = exports.waitForClusterRestartHealthyViaApi = exports.waitForClusterHealthyViaApi = exports.ensureClientAuditLoggingViaApi = exports.ensureTasklistV2Enabled = exports.ensureClusterViaApi = exports.createClusterViaApi = exports.deleteClusterViaApi = exports.resetHubBearerToken = void 0;
3
+ exports.ensureClusterLinkedToSharedWorkspace = exports.linkClusterToWorkspacesNamedViaApi = exports.linkClusterToSharedWorkspaceViaApi = exports.getHubWorkspaceId = exports.rememberHubWorkspaceId = exports.linkClusterToWorkspaceViaApi = exports.waitForAiFeaturesEnabledViaApi = exports.areAiFeaturesEnabledViaApi = exports.waitForClusterRestartHealthyViaApi = exports.waitForClusterHealthyViaApi = exports.ensureClientAuditLoggingViaApi = exports.ensureTasklistV2Enabled = exports.ensureClusterViaApi = exports.createClusterViaApi = exports.deleteClusterViaApi = exports.resetHubBearerToken = exports.observeBearerTokens = void 0;
4
4
  const test_1 = require("@playwright/test");
5
5
  const sleep_1 = require("./sleep");
6
6
  const PLAN_TYPE_ID = '574cde5f-54ae-43c1-851a-e6fb4f03423a';
@@ -52,6 +52,9 @@ function bearerTokenOf(url, authorization) {
52
52
  }
53
53
  return authorization.slice('Bearer '.length);
54
54
  }
55
+ // Exported so a login can start listening before Hub loads. Hub's Console
56
+ // pages go quiet once loaded (cluster updates arrive over one long-lived SSE
57
+ // request), so a first API call on an idle page otherwise sees no request.
55
58
  function observeBearerTokens(page) {
56
59
  if (tokenObservedPages.has(page)) {
57
60
  return;
@@ -71,6 +74,7 @@ function observeBearerTokens(page) {
71
74
  }
72
75
  });
73
76
  }
77
+ exports.observeBearerTokens = observeBearerTokens;
74
78
  // Exported so LoginPage can call this before a new login begins. Without it,
75
79
  // a page that signs in as a second user keeps answering getHubBearerToken()
76
80
  // with the first user's cached token until some later console.cloud/
@@ -876,9 +880,23 @@ async function linkClusterToWorkspaceViaApi(page, workspaceId, clusterName) {
876
880
  return;
877
881
  }
878
882
  const ids = [...new Set([...current.map((e) => e.id), backing.id])];
879
- const response = await clusterApiRequest(page, 'PUT', url, {
880
- environmentIds: ids,
881
- });
883
+ // Hub maps AccessDeniedException and an unresolvable path entity to a bare
884
+ // 404 (ExceptionHandlingAdvice). A PR run saw this PUT 404 once, right
885
+ // after the GET on the same workspace succeeded, and succeed on a retry.
886
+ // Bounded by count, not by toPass's timeout, which would also cut off
887
+ // clusterApiRequest's own 429/5xx backoff. A caller without cluster
888
+ // management access gets a 404 every time and fails below after 3 tries.
889
+ let response;
890
+ let notFoundCount = 0;
891
+ await (0, test_1.expect)(async () => {
892
+ response = await clusterApiRequest(page, 'PUT', url, {
893
+ environmentIds: ids,
894
+ });
895
+ if (response.status() === 404) {
896
+ notFoundCount++;
897
+ }
898
+ (0, test_1.expect)(response.status() !== 404 || notFoundCount >= 3, `PUT ${url} (linking "${clusterName}") returned 404`).toBe(true);
899
+ }).toPass({ intervals: [5000, 10000], timeout: 600000 });
882
900
  if (!response.ok()) {
883
901
  // clusterApiRequest returns non-2xx for non-transient statuses rather
884
902
  // than throwing, so an unchecked call would log success and let the
@@ -946,6 +964,34 @@ async function linkClusterToSharedWorkspaceViaApi(page, clusterName) {
946
964
  await linkClusterToWorkspaceViaApi(page, workspaceId, clusterName);
947
965
  }
948
966
  exports.linkClusterToSharedWorkspaceViaApi = linkClusterToSharedWorkspaceViaApi;
967
+ // Links `clusterName` to EVERY workspace named `workspaceName`, not just the
968
+ // first. Each test user creates its own "Cross Component Test Project"
969
+ // workspace in test-setup, and a user without an elevated or DevOps org role
970
+ // cannot assign an environment to it (camunda-hub#28955: the workspace
971
+ // Environments tab redirects to Members, and the PUT below is gated on
972
+ // hasClusterManagementAccess). A spec that creates a cluster as an elevated
973
+ // user and then hands off to such a user has to make the assignment up front,
974
+ // while the elevated session is still active.
975
+ async function linkClusterToWorkspacesNamedViaApi(page, clusterName, workspaceName = SHARED_HUB_WORKSPACE_NAME) {
976
+ const origin = await hubApiOrigin(page);
977
+ const response = await clusterApiRequest(page, 'GET', `${origin}/api/internal/v2/organizations/${encodeURIComponent(process.env.ORG_ID)}/workspaces?search=${encodeURIComponent(workspaceName)}&sort=name,asc&offset=0&limit=100`);
978
+ if (!response.ok()) {
979
+ throw new Error(`Failed to list workspaces named "${workspaceName}": ` +
980
+ `${response.status()} ${response.statusText()}`);
981
+ }
982
+ const body = (await response.json());
983
+ const data = (body.data ?? body);
984
+ const items = Array.isArray(data.items) ? data.items : [];
985
+ const workspaceIds = items
986
+ .map((item) => (item ?? {}))
987
+ .filter((item) => item.name === workspaceName)
988
+ .map((item) => item.id)
989
+ .filter((id) => typeof id === 'string');
990
+ for (const workspaceId of workspaceIds) {
991
+ await linkClusterToWorkspaceViaApi(page, workspaceId, clusterName);
992
+ }
993
+ }
994
+ exports.linkClusterToWorkspacesNamedViaApi = linkClusterToWorkspacesNamedViaApi;
949
995
  // Ensure-link at the point a deploy dialog is about to be opened. Resolves the
950
996
  // workspace from the id remembered while the flow was on it, else by name.
951
997
  // Cheap and idempotent: it returns early once the environment is assigned.
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resetSessionAll = exports.resetSession = void 0;
4
+ const test_1 = require("@playwright/test");
4
5
  const consoleApiHelpers_1 = require("./consoleApiHelpers");
5
6
  async function resetSession(browser, page) {
6
7
  const context = browser.contexts()[0];
@@ -10,7 +11,11 @@ async function resetSession(browser, page) {
10
11
  // session reset that re-authenticates as a different user must clear it
11
12
  // too, or getUserToken() can return the previous user's cached token.
12
13
  (0, consoleApiHelpers_1.resetHubBearerToken)(page);
13
- await page.reload();
14
+ // A logout just before this call can still be redirecting, which aborts
15
+ // the reload with net::ERR_ABORTED; retry until the page settles.
16
+ await (0, test_1.expect)(async () => {
17
+ await page.reload();
18
+ }).toPass({ timeout: 60000 });
14
19
  await page.waitForLoadState('domcontentloaded');
15
20
  await page.goto('/');
16
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.1286",
3
+ "version": "0.0.1288",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",