@camunda/e2e-test-suite 0.0.823 → 0.0.824

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.
@@ -102,12 +102,15 @@ class ClusterPage {
102
102
  .filter({ hasNotText: 'Generation' }); //Filter out header row
103
103
  this.tasklistV1Api = page.getByText('Tasklist API v1 (legacy)');
104
104
  this.tasklistV2Api = page.getByText('Tasklist API v2');
105
- this.cluster = (clusterName) => this.clustersList.filter({ hasText: clusterName });
105
+ this.cluster = (clusterName) => this.clustersList.filter({
106
+ has: page.getByRole('link', { name: clusterName, exact: true }),
107
+ });
106
108
  this.clusterHealthiness = (clusterName) => this.cluster(clusterName).getByText(`Healthy`, {
107
109
  exact: true,
108
110
  });
109
111
  this.clusterLink = (clusterName) => this.cluster(clusterName).getByRole('link', {
110
112
  name: clusterName,
113
+ exact: true,
111
114
  });
112
115
  this.dialog = page.getByRole('dialog');
113
116
  this.clusterVersion = (clusterName, versionText) => this.cluster(clusterName).getByText(versionText, { exact: false });
@@ -126,7 +126,7 @@ class FormJsPage {
126
126
  await cluster.click();
127
127
  await (0, test_1.expect)(this.dialog
128
128
  .locator('label')
129
- .filter({ hasText: clusterName })
129
+ .filter({ hasText: new RegExp(`^${clusterName}`) })
130
130
  .getByRole('switch')).toBeChecked({ timeout: 30000 });
131
131
  await (0, test_1.expect)(this.dialog).toHaveText(healthyRegex, {
132
132
  timeout: healthCheckTimeout,
@@ -109,7 +109,11 @@ class HomePage {
109
109
  await (0, test_1.expect)(this.buttonSkipCustomization).toBeVisible({
110
110
  timeout: 30000,
111
111
  });
112
- await this.buttonSkipCustomization.click({ timeout: 60000 });
112
+ // Best-effort: dismiss any Carbon modal that has a named close button.
113
+ await this.closeInformationDialog();
114
+ // force: true bypasses the "pointer events intercepted by modal backdrop"
115
+ // check — the same pattern used by closeInformationDialog itself.
116
+ await this.buttonSkipCustomization.click({ timeout: 60000, force: true });
113
117
  await this.closeInformationDialog();
114
118
  return;
115
119
  }
@@ -610,13 +610,14 @@ class ModelerCreatePage {
610
610
  await (0, test_1.expect)(this.dialog).toBeVisible({ timeout: 30000 });
611
611
  const healthCheckTimeout = 30000;
612
612
  const healthyRegex = new RegExp(`${clusterName}.*?HealthydevManage`);
613
- const cluster = this.dialog.getByText(clusterName);
614
- await (0, test_1.expect)(cluster).toBeVisible({ timeout: 30000 });
615
- await cluster.click();
616
- await (0, test_1.expect)(this.dialog
613
+ const clusterLabel = this.dialog
617
614
  .locator('label')
618
- .filter({ hasText: clusterName })
619
- .getByRole('switch')).toBeChecked({ timeout: 30000 });
615
+ .filter({ hasText: new RegExp(`^${clusterName}`) });
616
+ await (0, test_1.expect)(clusterLabel).toBeVisible({ timeout: 30000 });
617
+ await clusterLabel.click();
618
+ await (0, test_1.expect)(clusterLabel.getByRole('switch')).toBeChecked({
619
+ timeout: 30000,
620
+ });
620
621
  await (0, test_1.expect)(this.dialog).toHaveText(healthyRegex, {
621
622
  timeout: healthCheckTimeout,
622
623
  });
@@ -1130,7 +1131,7 @@ class ModelerCreatePage {
1130
1131
  // before saving. This handles environments with multiple clusters.
1131
1132
  const clusterLabel = configureEnvDialog
1132
1133
  .locator('label')
1133
- .filter({ hasText: clusterName });
1134
+ .filter({ hasText: new RegExp(`^${clusterName}`) });
1134
1135
  const clusterSwitch = clusterLabel.getByRole('switch');
1135
1136
  await (0, test_1.expect)(clusterLabel).toBeVisible({ timeout });
1136
1137
  const alreadyChecked = (await clusterSwitch.getAttribute('aria-checked')) === 'true';
@@ -264,7 +264,7 @@ class ModelerHomePage {
264
264
  async selectCluster(clusterName) {
265
265
  //Select the correct cluster if multiple exist
266
266
  const baseClusterName = clusterName.split('(')[0].trim();
267
- const cluster = this.dialog.getByText(baseClusterName, { exact: false });
267
+ const cluster = this.dialog.getByText(new RegExp(`^${baseClusterName}`));
268
268
  await (0, test_1.expect)(cluster).toBeVisible({
269
269
  timeout: 30000,
270
270
  });
@@ -272,7 +272,7 @@ class ModelerHomePage {
272
272
  await (0, test_1.expect)(this.dialog
273
273
  .locator('label')
274
274
  .filter({
275
- hasText: clusterName,
275
+ hasText: new RegExp(`^${clusterName}`),
276
276
  })
277
277
  .getByRole('switch')).toBeChecked({ timeout: 30000 });
278
278
  }
@@ -66,6 +66,17 @@ _8_10_1.test.describe('Console User Flow Tests @tasklistV2', () => {
66
66
  await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTabProcessInstancePage, operateTabProcessInstancePage.completedIcon, 'completed icon in Operate', 60000);
67
67
  });
68
68
  });
69
+ (0, _8_10_1.test)('Create Cluster', async ({ homePage, clusterPage }) => {
70
+ _8_10_1.test.slow();
71
+ const newClusterName = 'UI Test Cluster';
72
+ await _8_10_1.test.step('Navigate to clusters and create cluster', async () => {
73
+ await homePage.clickClusters();
74
+ await clusterPage.createCluster(newClusterName);
75
+ });
76
+ await _8_10_1.test.step('Assert cluster is healthy', async () => {
77
+ await clusterPage.assertClusterHealthyStatusWithRetry(newClusterName, 300000, 1500000);
78
+ });
79
+ });
69
80
  //Waiting infra team to provide DNS configuration for email testing, skipping for now
70
81
  _8_10_1.test.skip('Alert Trigger Flow - Email Notification', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, clusterPage, clusterDetailsPage, connectorSettingsPage, operateHomePage, operateProcessesPage, operateProcessInstancePage, }) => {
71
82
  _8_10_1.test.slow();
@@ -7,6 +7,7 @@ const connectorSecrets_1 = require("../../utils/connectorSecrets");
7
7
  const mcpSecrets_1 = require("../../utils/mcpSecrets");
8
8
  const UtilitiesPage_1 = require("../../pages/8.10/UtilitiesPage");
9
9
  const users_1 = require("../../utils/users");
10
+ const consoleApiHelpers_1 = require("../../utils/consoleApiHelpers");
10
11
  _8_10_1.test.describe.configure({ mode: 'parallel' });
11
12
  _8_10_1.test.describe('Cluster Setup Tests', () => {
12
13
  _8_10_1.test.afterEach(async ({ page }, testInfo) => {
@@ -26,9 +27,16 @@ _8_10_1.test.describe('Cluster Setup Tests', () => {
26
27
  const clusterName = 'Test Cluster';
27
28
  const apiClientName = 'Test_API_Client' + (await (0, _setup_1.generateRandomStringAsync)(3));
28
29
  await (0, UtilitiesPage_1.loginWithRetry)(page, loginPage, users_1.testUsers.mainUser, (testInfo.workerIndex + 1) * 1000);
29
- await homePage.clickClusters();
30
- await clusterPage.createCluster(clusterName);
31
- await clusterPage.assertClusterHealthyStatusWithRetry(clusterName);
30
+ if (process.env.IS_PROD === 'true') {
31
+ await clusterPage.createCluster(clusterName);
32
+ await clusterPage.assertClusterHealthyStatusWithRetry(clusterName, 120000, 1500000);
33
+ }
34
+ else {
35
+ await (0, consoleApiHelpers_1.deleteClusterViaApi)(page, clusterName);
36
+ const clusterUuid = await (0, consoleApiHelpers_1.createClusterViaApi)(page, clusterName);
37
+ await (0, consoleApiHelpers_1.waitForClusterHealthyViaApi)(page, clusterUuid);
38
+ await homePage.clickClusters();
39
+ }
32
40
  await clusterPage.clickClusterLink(clusterName);
33
41
  await clusterDetailsPage.assertComponentsHealth();
34
42
  if (process.env.IS_AG !== 'true') {
@@ -55,9 +63,10 @@ _8_10_1.test.describe('Cluster Setup Tests', () => {
55
63
  const apiClientName = 'Test_API_Client' + (await (0, _setup_1.generateRandomStringAsync)(3));
56
64
  const mcpClientName = 'MCP_Test_API_Client' + (await (0, _setup_1.generateRandomStringAsync)(3));
57
65
  await (0, UtilitiesPage_1.loginWithRetry)(page, loginPage, users_1.testUsers.mainUser, (testInfo.workerIndex + 1) * 1000);
66
+ await (0, consoleApiHelpers_1.deleteClusterViaApi)(page, clusterName);
67
+ const clusterUuid = await (0, consoleApiHelpers_1.createClusterViaApi)(page, clusterName);
68
+ await (0, consoleApiHelpers_1.waitForClusterHealthyViaApi)(page, clusterUuid, 1500000);
58
69
  await homePage.clickClusters();
59
- await clusterPage.createCluster(clusterName);
60
- await clusterPage.assertClusterHealthyStatusWithRetry(clusterName, 300000, 1500000);
61
70
  await clusterPage.clickClusterLink(clusterName);
62
71
  await clusterDetailsPage.assertComponentsHealth();
63
72
  await auditLogPage.clickAuditLogTab();
@@ -79,12 +88,16 @@ _8_10_1.test.describe('Cluster Setup Tests', () => {
79
88
  _8_10_1.test.slow();
80
89
  const clusterName = 'AWS Cluster';
81
90
  await (0, UtilitiesPage_1.loginWithRetry)(page, loginPage, users_1.testUsers.mainUser, (testInfo.workerIndex + 1) * 1000);
82
- await homePage.clickClusters();
83
- await clusterPage.createCluster(clusterName, 'AWS');
84
- // AWS clusters on SaaS INT can take 10+ min to reach Healthy. The helper's
85
- // default totalTimeout (500s) only fits ~2 attempts at the 300s per-attempt
86
- // wait above — bump to 25 min to allow the full 5 retries to complete.
87
- await clusterPage.assertClusterHealthyStatusWithRetry(clusterName, 300000, 1500000);
91
+ if (process.env.IS_PROD === 'true') {
92
+ await clusterPage.createCluster(clusterName, 'AWS');
93
+ await clusterPage.assertClusterHealthyStatusWithRetry(clusterName, 120000, 1500000);
94
+ }
95
+ else {
96
+ await (0, consoleApiHelpers_1.deleteClusterViaApi)(page, clusterName);
97
+ const clusterUuid = await (0, consoleApiHelpers_1.createClusterViaApi)(page, clusterName, 'AWS');
98
+ await (0, consoleApiHelpers_1.waitForClusterHealthyViaApi)(page, clusterUuid, 1500000);
99
+ await homePage.clickClusters();
100
+ }
88
101
  await clusterPage.clickClusterLink(clusterName);
89
102
  await clusterDetailsPage.assertComponentsHealth();
90
103
  if (process.env.IS_AG !== 'true') {
@@ -0,0 +1,4 @@
1
+ import { Page } from '@playwright/test';
2
+ export declare function deleteClusterViaApi(page: Page, clusterName: string): Promise<void>;
3
+ export declare function createClusterViaApi(page: Page, clusterName: string, region?: string): Promise<string>;
4
+ export declare function waitForClusterHealthyViaApi(page: Page, clusterUuid: string, timeoutMs?: number): Promise<void>;
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.waitForClusterHealthyViaApi = exports.createClusterViaApi = exports.deleteClusterViaApi = void 0;
4
+ const test_1 = require("@playwright/test");
5
+ const sleep_1 = require("./sleep");
6
+ const PLAN_TYPE_ID = '574cde5f-54ae-43c1-851a-e6fb4f03423a';
7
+ const CHANNEL_ID = 'cf355219-cc2d-4266-83e1-2430998ddf30';
8
+ const GCP_REGION_ID = 'a6fadf1d-b2a0-44ab-b301-2f9aaf6681dc';
9
+ const AWS_REGION_ID = '30fcdd4f-05d7-1c7a-54df-8d9defbd250f';
10
+ const GENERATION_MAP = {
11
+ '8.10': '7df47bbb-8665-4598-b12b-f53338d4aff0',
12
+ '8.9': '32478fdf-dbea-47b9-abe2-e9cbfc6c4324',
13
+ '8.8': '7ff71498-dc2d-486f-9baa-e46b7b503c89',
14
+ '8.7': 'd7ab8eef-9dd2-4b61-8142-fb1e7eea6710',
15
+ };
16
+ function getConsoleBaseUrl() {
17
+ if (process.env.IS_PROD === 'true') {
18
+ return 'https://console.cloud.camunda.io';
19
+ }
20
+ // PLAYWRIGHT_BASE_URL is the UI hostname (console.ultrawombat.com); the actual
21
+ // API backend lives at console.cloud.ultrawombat.com — map accordingly.
22
+ const uiUrl = process.env.PLAYWRIGHT_BASE_URL ?? 'https://console.ultrawombat.com';
23
+ return uiUrl
24
+ .replace('console.ultrawombat.com', 'console.cloud.ultrawombat.com')
25
+ .replace('console.camunda.io', 'console.cloud.camunda.io');
26
+ }
27
+ function getOrgId() {
28
+ const orgId = process.env.ORG_ID;
29
+ if (!orgId)
30
+ throw new Error('ORG_ID env var is not set');
31
+ return orgId;
32
+ }
33
+ // Auth0 SPA JS stores tokens in localStorage under keys starting with @@auth0spajs@@
34
+ // cacheLocation: 'localstorage' is configured in the Console frontend auth store
35
+ async function getUserToken(page) {
36
+ const token = await page.evaluate(() => {
37
+ for (const key of Object.keys(localStorage)) {
38
+ if (key.startsWith('@@auth0spajs@@')) {
39
+ try {
40
+ const data = JSON.parse(localStorage.getItem(key) ?? '');
41
+ if (data?.body?.access_token)
42
+ return data.body.access_token;
43
+ }
44
+ catch {
45
+ // skip malformed entries
46
+ }
47
+ }
48
+ }
49
+ return null;
50
+ });
51
+ if (!token) {
52
+ throw new Error('Could not extract access token from browser localStorage — ' +
53
+ 'ensure loginWithRetry has been called before any cluster API operations');
54
+ }
55
+ return token;
56
+ }
57
+ async function findClusterByName(page, clusterName) {
58
+ const token = await getUserToken(page);
59
+ const orgId = getOrgId();
60
+ const response = await page.request.get(`${getConsoleBaseUrl()}/api/orgs/${orgId}/clusters`, { headers: { Authorization: `Bearer ${token}` } });
61
+ if (response.status() !== 200)
62
+ return undefined;
63
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
+ let data;
65
+ try {
66
+ data = await response.json();
67
+ }
68
+ catch {
69
+ return undefined;
70
+ }
71
+ const clusters = Array.isArray(data)
72
+ ? data
73
+ : data.clusters ?? [];
74
+ return clusters.find((c) => c.name === clusterName);
75
+ }
76
+ async function deleteClusterViaApi(page, clusterName) {
77
+ const cluster = await findClusterByName(page, clusterName);
78
+ if (!cluster)
79
+ return;
80
+ const token = await getUserToken(page);
81
+ const orgId = getOrgId();
82
+ await page.request.delete(`${getConsoleBaseUrl()}/api/orgs/${orgId}/clusters/${cluster.uuid}`, { headers: { Authorization: `Bearer ${token}` } });
83
+ await (0, sleep_1.sleep)(5000);
84
+ }
85
+ exports.deleteClusterViaApi = deleteClusterViaApi;
86
+ async function resolveGenerationId() {
87
+ const rawVersion = process.env.CLUSTER_VERSION ?? '';
88
+ const minorVersion = rawVersion.match(/^(\d+\.\d+)/)?.[1] ?? rawVersion;
89
+ const generationId = GENERATION_MAP[minorVersion];
90
+ if (generationId)
91
+ return generationId;
92
+ throw new Error(`No generation UUID found for CLUSTER_VERSION="${rawVersion}" ` +
93
+ `(resolved minor: "${minorVersion}"). ` +
94
+ `Known versions: ${Object.keys(GENERATION_MAP).join(', ')}`);
95
+ }
96
+ async function createClusterViaApi(page, clusterName, region = 'GCP') {
97
+ const token = await getUserToken(page);
98
+ const orgId = getOrgId();
99
+ const generationId = await resolveGenerationId();
100
+ const k8sContextId = region.toUpperCase() === 'AWS' ? AWS_REGION_ID : GCP_REGION_ID;
101
+ const createResponse = await page.request.post(`${getConsoleBaseUrl()}/api/orgs/${orgId}/clusters`, {
102
+ headers: { Authorization: `Bearer ${token}` },
103
+ data: {
104
+ name: clusterName,
105
+ planTypeId: PLAN_TYPE_ID,
106
+ channelId: CHANNEL_ID,
107
+ generationId,
108
+ k8sContextId,
109
+ autoUpdate: true,
110
+ stageLabel: 'dev',
111
+ },
112
+ });
113
+ if (createResponse.status() !== 200 && createResponse.status() !== 201) {
114
+ throw new Error(`Failed to create cluster "${clusterName}": ` +
115
+ `HTTP ${createResponse.status()} - ${await createResponse.text()}`);
116
+ }
117
+ const result = await createResponse.json();
118
+ const clusterId = result.clusterId ??
119
+ result.uuid ??
120
+ result.data?.cluster?.uuid ??
121
+ result.data?.clusterId;
122
+ if (!clusterId) {
123
+ throw new Error(`Cluster creation response missing UUID: ${JSON.stringify(result)}`);
124
+ }
125
+ return clusterId;
126
+ }
127
+ exports.createClusterViaApi = createClusterViaApi;
128
+ async function waitForClusterHealthyViaApi(page, clusterUuid, timeoutMs = 1500000) {
129
+ const orgId = getOrgId();
130
+ const url = `${getConsoleBaseUrl()}/api/orgs/${orgId}/clusters/${clusterUuid}`;
131
+ await (0, test_1.expect)(async () => {
132
+ const token = await getUserToken(page);
133
+ const response = await page.request.get(url, {
134
+ headers: { Authorization: `Bearer ${token}` },
135
+ });
136
+ (0, test_1.expect)(response.status()).toBe(200);
137
+ const data = await response.json();
138
+ const cluster = data.data ?? data;
139
+ (0, test_1.expect)(cluster.status?.ready).toBe('Healthy');
140
+ }).toPass({ timeout: timeoutMs, intervals: [30000, 60000] });
141
+ }
142
+ exports.waitForClusterHealthyViaApi = waitForClusterHealthyViaApi;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.823",
3
+ "version": "0.0.824",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",