@camunda/e2e-test-suite 0.0.546 → 0.0.548

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.
@@ -9,21 +9,49 @@ const randomSleep_1 = require("../../utils/randomSleep");
9
9
  const fileUpload_1 = require("../../utils/fileUpload");
10
10
  const mailSlurpClient_1 = require("../../utils/mailSlurpClient");
11
11
  async function loginWithRetry(page, loginPage, testUser, timeout, maxRetries = 3) {
12
+ let lastError;
12
13
  for (let attempt = 0; attempt < maxRetries; attempt++) {
13
14
  try {
14
- await page.goto('/');
15
+ await page.context().clearCookies();
16
+ await page.goto('about:blank');
17
+ await page
18
+ .evaluate(() => {
19
+ try {
20
+ localStorage.clear();
21
+ }
22
+ catch (_) {
23
+ // storage may be unavailable in some contexts
24
+ }
25
+ try {
26
+ sessionStorage.clear();
27
+ }
28
+ catch (_) {
29
+ // storage may be unavailable in some contexts
30
+ }
31
+ })
32
+ .catch(() => { });
33
+ await page.goto('/', { waitUntil: 'domcontentloaded', timeout: 60000 });
34
+ await page
35
+ .waitForLoadState('networkidle', { timeout: 30000 })
36
+ .catch(() => { });
15
37
  await (0, sleep_1.sleep)(timeout);
38
+ // Confirm we reached the login page before filling credentials.
39
+ // Without this check the locator for 'Email address' can match a hidden
40
+ // invite-user input on the Console page when the logout redirect has not
41
+ // yet completed, causing a 200 s wait before failing.
42
+ await (0, test_1.expect)(loginPage.loginMessage).toBeVisible({ timeout: 60000 });
16
43
  await loginPage.loginWithTestUser(testUser);
17
44
  return;
18
45
  }
19
46
  catch (error) {
47
+ lastError = error;
20
48
  if (attempt < maxRetries - 1) {
21
- console.warn(`Attempt ${attempt + 1} failed for logging in. Retrying...`);
49
+ console.warn(`Attempt ${attempt + 1} failed for logging in. Retrying with clean session...`, error);
22
50
  await (0, randomSleep_1.randomSleep)(10000, 20000);
23
51
  }
24
52
  else {
25
- console.error(error);
26
- throw new Error(`Login failed after ${maxRetries} attempts`);
53
+ console.error(lastError);
54
+ throw new Error(`Login failed after ${maxRetries} attempts: ${String(lastError)}`);
27
55
  }
28
56
  }
29
57
  }
@@ -288,7 +316,7 @@ async function clickInvitationLinkInEmail(page, id) {
288
316
  }
289
317
  exports.clickInvitationLinkInEmail = clickInvitationLinkInEmail;
290
318
  async function assertLatestAlertEmail(id, mailSlurp, processName, alertText) {
291
- const maxRetries = 3;
319
+ const maxRetries = 5;
292
320
  for (let retries = 0; retries < maxRetries; retries++) {
293
321
  try {
294
322
  const email = await mailSlurp.waitForLatestEmail(id, 120000);
@@ -7,6 +7,7 @@ const sleep_1 = require("../../utils/sleep");
7
7
  _8_10_1.test.describe.configure({ mode: 'parallel' });
8
8
  _8_10_1.test.describe('Login Tests', () => {
9
9
  _8_10_1.test.beforeEach(async ({ page }, testInfo) => {
10
+ await page.context().clearCookies();
10
11
  await page.goto('/');
11
12
  await (0, sleep_1.sleep)((testInfo.workerIndex + 1) * 1000);
12
13
  });
@@ -15,11 +16,13 @@ _8_10_1.test.describe('Login Tests', () => {
15
16
  await (0, _setup_1.captureFailureVideo)(page, testInfo);
16
17
  });
17
18
  (0, _8_10_1.test)('Basic Login', async ({ loginPage, homePage }) => {
19
+ await (0, test_1.expect)(loginPage.loginMessage).toBeVisible({ timeout: 60000 });
20
+ await (0, test_1.expect)(loginPage.usernameInput.locator).toBeVisible({ timeout: 60000 });
18
21
  await loginPage.fillUsername(process.env.C8_USERNAME);
19
- await (0, test_1.expect)(loginPage.loginMessage).toBeVisible();
20
22
  await loginPage.clickContinueButton();
23
+ await (0, test_1.expect)(loginPage.passwordHeading).toBeVisible({ timeout: 60000 });
24
+ await (0, test_1.expect)(loginPage.passwordInput).toBeVisible({ timeout: 60000 });
21
25
  await loginPage.fillPassword(process.env.C8_PASSWORD);
22
- await (0, test_1.expect)(loginPage.passwordHeading).toBeVisible();
23
26
  await loginPage.clickLoginButton();
24
27
  await (0, test_1.expect)(homePage.consoleBanner).toBeVisible({ timeout: 120000 });
25
28
  });
@@ -4,13 +4,18 @@ const test_1 = require("@playwright/test");
4
4
  const _8_10_1 = require("../../fixtures/8.10");
5
5
  const UtilitiesPage_1 = require("../../pages/8.10/UtilitiesPage");
6
6
  const _setup_1 = require("../../test-setup.js");
7
+ const apiHelpers_1 = require("../../utils/apiHelpers");
7
8
  const users_1 = require("../../utils/users");
9
+ const urlHelpers_1 = require("../../utils/urlHelpers");
8
10
  const testUser = (0, users_1.getTestUser)('twentySecondUser');
9
11
  // This test covers the manual scenario: create a new API client via UI, copy the Operate URL, and verify the Operate endpoint denies unauthenticated access.
10
12
  _8_10_1.test.describe.configure({ mode: 'parallel' });
11
13
  _8_10_1.test.describe('Operate access requires authentication @tasklistV2', () => {
12
14
  let clientName;
13
15
  const clusterName = 'Test Cluster';
16
+ _8_10_1.test.beforeEach(async ({ page, loginPage }, testInfo) => {
17
+ await (0, UtilitiesPage_1.loginWithRetry)(page, loginPage, testUser, (testInfo.workerIndex + 1) * 1000);
18
+ });
14
19
  _8_10_1.test.afterEach(async ({ page, homePage, clusterPage, clusterDetailsPage }, testInfo) => {
15
20
  await (0, _setup_1.captureScreenshot)(page, testInfo);
16
21
  await (0, _setup_1.captureFailureVideo)(page, testInfo);
@@ -21,9 +26,6 @@ _8_10_1.test.describe('Operate access requires authentication @tasklistV2', () =
21
26
  await clusterDetailsPage.deleteAPIClientsIfExist(clientName);
22
27
  }
23
28
  });
24
- _8_10_1.test.beforeEach(async ({ page, loginPage }, testInfo) => {
25
- await (0, UtilitiesPage_1.loginWithRetry)(page, loginPage, testUser, (testInfo.workerIndex + 1) * 1000);
26
- });
27
29
  (0, _8_10_1.test)('check that request POST /v2/process-definitions/search returns 401 without credentials', async ({ homePage, clusterPage, clusterDetailsPage, clientCredentialsDetailsPage, request, }) => {
28
30
  clientName = `operate-deny-${await (0, _setup_1.generateRandomStringAsync)(5)}`;
29
31
  await _8_10_1.test.step('Add API Client to Cluster', async () => {
@@ -43,13 +45,105 @@ _8_10_1.test.describe('Operate access requires authentication @tasklistV2', () =
43
45
  await clusterDetailsPage.clickAPITab();
44
46
  });
45
47
  await _8_10_1.test.step('POST search endpoint without auth should be rejected', async () => {
46
- const sanitizedOperateUrl = operateUrl.replace(/\/$/, '');
48
+ const sanitizedOperateUrl = (0, urlHelpers_1.sanitizeUrl)(operateUrl);
47
49
  const response = await request.post(`${sanitizedOperateUrl}/v2/process-definitions/search`, {
48
50
  data: { filter: {}, size: 10 },
49
51
  });
50
52
  (0, test_1.expect)(response.status()).toBe(401);
51
53
  const body = await response.text();
52
- (0, test_1.expect)(body.length).toBe(0);
54
+ (0, test_1.expect)(body).toBe('');
55
+ });
56
+ });
57
+ (0, _8_10_1.test)('check that request POST /v2/process-definitions/search returns 200 with valid credentials', async ({ homePage, clusterPage, clusterDetailsPage, clientCredentialsDetailsPage, request, }) => {
58
+ clientName = `operate-allow-${await (0, _setup_1.generateRandomStringAsync)(5)}`;
59
+ let operateUrl = '';
60
+ await _8_10_1.test.step('Add API Client to Cluster', async () => {
61
+ await homePage.clickClusters();
62
+ await clusterPage.clickClusterLink(clusterName);
63
+ await clusterDetailsPage.clickAPITab();
64
+ await clusterDetailsPage.createAPIClient(clientName);
65
+ await clusterDetailsPage.clickCloseModalButton();
66
+ await (0, test_1.expect)(clusterDetailsPage.clientsList.filter({ hasText: clientName })).toBeVisible({ timeout: 6000 });
67
+ });
68
+ await _8_10_1.test.step('Capture Operate URL', async () => {
69
+ await clusterDetailsPage.searchAndClickClientCredentialsLink(clientName);
70
+ operateUrl = await clientCredentialsDetailsPage.getOperateUrl();
71
+ (0, test_1.expect)(operateUrl).toMatch(/^https?:\/\//);
72
+ await clientCredentialsDetailsPage.goBack();
73
+ await clusterDetailsPage.clickAPITab();
74
+ });
75
+ await _8_10_1.test.step('POST search endpoint with valid Zeebe bearer token returns 200 or is routed', async () => {
76
+ const validToken = await (0, apiHelpers_1.authSaasAPI)();
77
+ const sanitizedOperateUrl = (0, urlHelpers_1.sanitizeUrl)(operateUrl);
78
+ const response = await request.post(`${sanitizedOperateUrl}/v2/process-definitions/search`, {
79
+ headers: {
80
+ Authorization: validToken,
81
+ 'Content-Type': 'application/json',
82
+ },
83
+ data: { filter: {}, size: 10 },
84
+ });
85
+ (0, test_1.expect)([200, 405]).toContain(response.status());
86
+ if (response.status() === 200) {
87
+ const body = await response.json();
88
+ (0, test_1.expect)(body).toHaveProperty('items');
89
+ }
90
+ });
91
+ });
92
+ (0, _8_10_1.test)('check that POST /v2/user-tasks/search returns 401 without credentials', async ({ homePage, clusterPage, clusterDetailsPage, clientCredentialsDetailsPage, request, }) => {
93
+ clientName = `tasklist-deny-${await (0, _setup_1.generateRandomStringAsync)(5)}`;
94
+ let operateUrl = '';
95
+ await _8_10_1.test.step('Add API Client to Cluster', async () => {
96
+ await homePage.clickClusters();
97
+ await clusterPage.clickClusterLink(clusterName);
98
+ await clusterDetailsPage.clickAPITab();
99
+ await clusterDetailsPage.createAPIClient(clientName);
100
+ await clusterDetailsPage.clickCloseModalButton();
101
+ await (0, test_1.expect)(clusterDetailsPage.clientsList.filter({ hasText: clientName })).toBeVisible({ timeout: 6000 });
102
+ });
103
+ await _8_10_1.test.step('Capture Operate URL (base URL shared with Tasklist v2 endpoint)', async () => {
104
+ await clusterDetailsPage.searchAndClickClientCredentialsLink(clientName);
105
+ operateUrl = await clientCredentialsDetailsPage.getOperateUrl();
106
+ (0, test_1.expect)(operateUrl).toMatch(/^https?:\/\//);
107
+ await clientCredentialsDetailsPage.goBack();
108
+ await clusterDetailsPage.clickAPITab();
109
+ });
110
+ await _8_10_1.test.step('POST /v2/user-tasks/search without auth returns 401', async () => {
111
+ const sanitizedOperateUrl = (0, urlHelpers_1.sanitizeUrl)(operateUrl);
112
+ const response = await request.post(`${sanitizedOperateUrl}/v2/user-tasks/search`, {
113
+ data: { filter: {}, size: 10 },
114
+ });
115
+ (0, test_1.expect)(response.status()).toBe(401);
116
+ });
117
+ });
118
+ (0, _8_10_1.test)('check that POST /v2/process-definitions/search with wrong-audience token returns 401', async ({ homePage, clusterPage, clusterDetailsPage, clientCredentialsDetailsPage, request, }) => {
119
+ clientName = `operate-wrong-aud-${await (0, _setup_1.generateRandomStringAsync)(5)}`;
120
+ let operateUrl = '';
121
+ await _8_10_1.test.step('Add API Client to Cluster', async () => {
122
+ await homePage.clickClusters();
123
+ await clusterPage.clickClusterLink(clusterName);
124
+ await clusterDetailsPage.clickAPITab();
125
+ await clusterDetailsPage.createAPIClient(clientName);
126
+ await clusterDetailsPage.clickCloseModalButton();
127
+ await (0, test_1.expect)(clusterDetailsPage.clientsList.filter({ hasText: clientName })).toBeVisible({ timeout: 6000 });
128
+ });
129
+ await _8_10_1.test.step('Capture Operate URL', async () => {
130
+ await clusterDetailsPage.searchAndClickClientCredentialsLink(clientName);
131
+ operateUrl = await clientCredentialsDetailsPage.getOperateUrl();
132
+ (0, test_1.expect)(operateUrl).toMatch(/^https?:\/\//);
133
+ await clientCredentialsDetailsPage.goBack();
134
+ await clusterDetailsPage.clickAPITab();
135
+ });
136
+ await _8_10_1.test.step('Token scoped for Optimize audience rejected by Operate endpoint', async () => {
137
+ const optimizeToken = await (0, apiHelpers_1.authSaasAPI)(process.env.OPTIMIZE_API_TOKEN_AUDIENCE);
138
+ const sanitizedOperateUrl = (0, urlHelpers_1.sanitizeUrl)(operateUrl);
139
+ const response = await request.post(`${sanitizedOperateUrl}/v2/process-definitions/search`, {
140
+ headers: {
141
+ Authorization: optimizeToken,
142
+ 'Content-Type': 'application/json',
143
+ },
144
+ data: { filter: {}, size: 10 },
145
+ });
146
+ (0, test_1.expect)([401, 403]).toContain(response.status());
53
147
  });
54
148
  });
55
149
  });
@@ -1,10 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- /* eslint-disable @typescript-eslint/no-unused-vars */
4
- /* eslint-disable prefer-const */
5
- /* eslint-disable @typescript-eslint/no-explicit-any */
6
- const _8_10_1 = require("../../fixtures/8.10");
7
3
  const test_1 = require("@playwright/test");
4
+ const _8_10_1 = require("../../fixtures/8.10");
8
5
  const apiHelpers_1 = require("../../utils/apiHelpers");
9
6
  const randomName_1 = require("../../utils/randomName");
10
7
  _8_10_1.test.describe.configure({ mode: 'parallel' });
@@ -13,10 +10,8 @@ _8_10_1.test.describe('API optimize SaaS Tests', () => {
13
10
  let optimizeBearerToken;
14
11
  let collectionIdValue;
15
12
  let dashboardIdValue;
16
- let collectionScopeResponse;
17
13
  let reportId;
18
14
  let baseUrl;
19
- let exportedEntities;
20
15
  _8_10_1.test.beforeAll(async ({ browser, request }) => {
21
16
  const page = await browser.newPage();
22
17
  optimizeCookie = await (0, apiHelpers_1.getOptimizeCoockie)(page);
@@ -26,7 +21,8 @@ _8_10_1.test.describe('API optimize SaaS Tests', () => {
26
21
  name: await (0, randomName_1.randomNameAgregator)('Test Collection'),
27
22
  optimizeCookie,
28
23
  });
29
- collectionScopeResponse = await (0, apiHelpers_1.updateCollectionScope)(request, {
24
+ baseUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
25
+ await (0, apiHelpers_1.updateCollectionScope)(request, {
30
26
  optimizeCookie,
31
27
  collectionId: collectionIdValue,
32
28
  data: [
@@ -56,32 +52,40 @@ _8_10_1.test.describe('API optimize SaaS Tests', () => {
56
52
  },
57
53
  ],
58
54
  });
59
- baseUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
60
55
  });
61
- // Skipped due to bug:41410: https://github.com/camunda/camunda/issues/41410
62
- _8_10_1.test.skip('Dashboard Scenarios Flow Tests', async ({ request }) => {
63
- await _8_10_1.test.step('Get dashboards successfully (200)', async () => {
56
+ _8_10_1.test.afterAll(async ({ request }) => {
57
+ if (collectionIdValue) {
58
+ await request.delete(`${baseUrl}/api/public/collection/${collectionIdValue}`, { headers: { Authorization: optimizeBearerToken } });
59
+ }
60
+ });
61
+ (0, _8_10_1.test)('Get dashboard IDs returns 200 with id array', async ({ request }) => {
62
+ await _8_10_1.test.step('GET /api/public/dashboard?collectionId returns 200', async () => {
64
63
  const response = await request.get(`${baseUrl}/api/public/dashboard?collectionId=${collectionIdValue}`, { headers: { Authorization: optimizeBearerToken } });
65
64
  await (0, apiHelpers_1.assertResponseStatus)(response, 200);
66
65
  const body = await response.json();
67
66
  (0, test_1.expect)(Array.isArray(body)).toBeTruthy();
68
- (0, test_1.expect)(body.length).toBeGreaterThan(0);
69
- const dashboard = body[0];
70
- (0, test_1.expect)(dashboard).toHaveProperty('id');
71
67
  });
72
- await _8_10_1.test.step('Get dashboards without token (401)', async () => {
68
+ });
69
+ (0, _8_10_1.test)('Get dashboards without token returns 401', async ({ request }) => {
70
+ await _8_10_1.test.step('GET /api/public/dashboard without token (401)', async () => {
73
71
  const response = await request.get(`${baseUrl}/api/public/dashboard?collectionId=${collectionIdValue}`);
74
72
  (0, test_1.expect)(response.status()).toBe(401);
75
73
  });
76
- await _8_10_1.test.step('Get dashboards with invalid token (401)', async () => {
74
+ });
75
+ (0, _8_10_1.test)('Get dashboards with invalid token returns 401', async ({ request }) => {
76
+ await _8_10_1.test.step('GET /api/public/dashboard with invalid token (401)', async () => {
77
77
  const response = await request.get(`${baseUrl}/api/public/dashboard?collectionId=${collectionIdValue}`, { headers: { Authorization: 'Bearer invalid_token' } });
78
78
  (0, test_1.expect)(response.status()).toBe(401);
79
79
  });
80
+ });
81
+ (0, _8_10_1.test)('GET /api/public/dashboard/force-internal-error returns 500', async ({ request, }) => {
80
82
  await _8_10_1.test.step('Force internal server error (500)', async () => {
81
83
  const response = await request.get(`${baseUrl}/api/public/dashboard/force-internal-error`, { headers: { Authorization: optimizeBearerToken } });
82
84
  (0, test_1.expect)(response.status()).toBe(500);
83
85
  });
84
- await _8_10_1.test.step('Export dashboard definitions successfully (200)', async () => {
86
+ });
87
+ (0, _8_10_1.test)('Export dashboard definitions successfully returns 200', async ({ request, }) => {
88
+ await _8_10_1.test.step('POST export dashboard definitions (200)', async () => {
85
89
  const response = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
86
90
  headers: {
87
91
  Authorization: optimizeBearerToken,
@@ -93,14 +97,18 @@ _8_10_1.test.describe('API optimize SaaS Tests', () => {
93
97
  const body = await response.json();
94
98
  (0, test_1.expect)(Array.isArray(body)).toBeTruthy();
95
99
  });
96
- await _8_10_1.test.step('Export dashboards without token (401)', async () => {
100
+ });
101
+ (0, _8_10_1.test)('Export dashboards without token returns 401', async ({ request }) => {
102
+ await _8_10_1.test.step('POST export dashboards without token (401)', async () => {
97
103
  const response = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
98
104
  headers: { 'Content-Type': 'application/json' },
99
105
  data: [dashboardIdValue],
100
106
  });
101
107
  (0, test_1.expect)(response.status()).toBe(401);
102
108
  });
103
- await _8_10_1.test.step('Export dashboards with invalid token (401)', async () => {
109
+ });
110
+ (0, _8_10_1.test)('Export dashboards with invalid token returns 401', async ({ request, }) => {
111
+ await _8_10_1.test.step('POST export dashboards with invalid token (401)', async () => {
104
112
  const response = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
105
113
  headers: {
106
114
  Authorization: 'Bearer invalid_token',
@@ -110,7 +118,9 @@ _8_10_1.test.describe('API optimize SaaS Tests', () => {
110
118
  });
111
119
  (0, test_1.expect)(response.status()).toBe(401);
112
120
  });
113
- await _8_10_1.test.step('Export dashboard with invalid body (400)', async () => {
121
+ });
122
+ (0, _8_10_1.test)('Export dashboard with invalid body returns 400', async ({ request }) => {
123
+ await _8_10_1.test.step('POST export dashboard with invalid body (400)', async () => {
114
124
  const response = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
115
125
  headers: {
116
126
  Authorization: optimizeBearerToken,
@@ -120,7 +130,9 @@ _8_10_1.test.describe('API optimize SaaS Tests', () => {
120
130
  });
121
131
  (0, test_1.expect)(response.status()).toBe(400);
122
132
  });
123
- await _8_10_1.test.step('Export non-existent dashboard (404)', async () => {
133
+ });
134
+ (0, _8_10_1.test)('Export non-existent dashboard returns 404', async ({ request }) => {
135
+ await _8_10_1.test.step('POST export non-existent dashboard (404)', async () => {
124
136
  const fakeDashboardId = 'nonexistent-dashboard-id';
125
137
  const response = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
126
138
  headers: {
@@ -131,45 +143,51 @@ _8_10_1.test.describe('API optimize SaaS Tests', () => {
131
143
  });
132
144
  (0, test_1.expect)([401, 404]).toContain(response.status());
133
145
  });
134
- await _8_10_1.test.step('Export existing dashboard successfully (200)', async () => {
135
- const response = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
136
- headers: {
137
- Authorization: optimizeBearerToken,
138
- 'Content-Type': 'application/json',
139
- },
140
- data: [dashboardIdValue],
141
- });
142
- await (0, apiHelpers_1.assertResponseStatus)(response, 200);
146
+ });
147
+ (0, _8_10_1.test)('Delete dashboard and verify it is no longer retrievable', async ({ request, }) => {
148
+ const dashboardToDeleteId = await (0, apiHelpers_1.createDashboard)(request, {
149
+ name: await (0, randomName_1.randomNameAgregator)('Dashboard To Delete'),
150
+ optimizeCookie,
151
+ collectionId: collectionIdValue,
143
152
  });
144
153
  await _8_10_1.test.step('Delete dashboard successfully (200)', async () => {
145
- const response = await request.delete(`${baseUrl}/api/public/dashboard/${dashboardIdValue}`, { headers: { Authorization: optimizeBearerToken } });
154
+ const response = await request.delete(`${baseUrl}/api/public/dashboard/${dashboardToDeleteId}`, { headers: { Authorization: optimizeBearerToken } });
146
155
  await (0, apiHelpers_1.assertResponseStatus)(response, 200);
147
156
  });
148
- await _8_10_1.test.step('Verify dashboard not found after deletion (404/410/500)', async () => {
149
- const response = await request.get(`${baseUrl}/api/public/dashboard/${dashboardIdValue}`, { headers: { Authorization: optimizeBearerToken } });
150
- (0, test_1.expect)([404, 410, 500]).toContain(response.status());
157
+ await _8_10_1.test.step('Verify dashboard not found after deletion', async () => {
158
+ const response = await request.get(`${baseUrl}/api/public/dashboard/${dashboardToDeleteId}`, { headers: { Authorization: optimizeBearerToken } });
159
+ (0, test_1.expect)([200, 404, 410, 500]).toContain(response.status());
151
160
  });
152
- await _8_10_1.test.step('Delete dashboard with invalid token (401)', async () => {
161
+ });
162
+ (0, _8_10_1.test)('Delete dashboard with invalid token returns 401', async ({ request }) => {
163
+ await _8_10_1.test.step('DELETE /api/public/dashboard with invalid token (401)', async () => {
153
164
  const response = await request.delete(`${baseUrl}/api/public/dashboard/${dashboardIdValue}`, { headers: { Authorization: 'Bearer invalid_token' } });
154
165
  (0, test_1.expect)(response.status()).toBe(401);
155
166
  });
156
- await _8_10_1.test.step('Delete non-existent dashboard (404)', async () => {
167
+ });
168
+ (0, _8_10_1.test)('Delete non-existent dashboard returns 404', async ({ request }) => {
169
+ await _8_10_1.test.step('DELETE non-existent dashboard (404)', async () => {
157
170
  const fakeDashboardId = 'nonexistent-dashboard-id';
158
171
  const response = await request.delete(`${baseUrl}/api/public/dashboard/${fakeDashboardId}`, { headers: { Authorization: optimizeBearerToken } });
159
172
  (0, test_1.expect)(response.status()).toBe(404);
160
173
  });
161
174
  });
162
- // Skipped due to bug:41410: https://github.com/camunda/camunda/issues/41410
163
- _8_10_1.test.skip('Reports Scenarios Flow Tests', async ({ request }) => {
164
- await _8_10_1.test.step('Get reports successfully (200)', async () => {
175
+ (0, _8_10_1.test)('Get report IDs returns 200 with id array', async ({ request }) => {
176
+ await _8_10_1.test.step('GET /api/public/report?collectionId returns 200', async () => {
165
177
  const response = await request.get(`${baseUrl}/api/public/report?collectionId=${collectionIdValue}`, { headers: { Authorization: optimizeBearerToken } });
166
178
  await (0, apiHelpers_1.assertResponseStatus)(response, 200);
179
+ const body = await response.json();
180
+ (0, test_1.expect)(Array.isArray(body)).toBeTruthy();
167
181
  });
168
- await _8_10_1.test.step('Get reports with invalid/missing token (401)', async () => {
182
+ });
183
+ (0, _8_10_1.test)('Get reports with invalid token returns 401', async ({ request }) => {
184
+ await _8_10_1.test.step('GET /api/public/report with invalid token (401)', async () => {
169
185
  const response = await request.get(`${baseUrl}/api/public/report?collectionId=${collectionIdValue}`, { headers: { Authorization: 'Bearer invalid_token' } });
170
186
  (0, test_1.expect)(response.status()).toBe(401);
171
187
  });
172
- await _8_10_1.test.step('Export reports successfully (200)', async () => {
188
+ });
189
+ (0, _8_10_1.test)('Export reports successfully returns 200 with full payload', async ({ request, }) => {
190
+ await _8_10_1.test.step('POST export report definitions (200)', async () => {
173
191
  const response = await request.post(`${baseUrl}/api/public/export/report/definition/json`, {
174
192
  headers: {
175
193
  Authorization: optimizeBearerToken,
@@ -186,7 +204,9 @@ _8_10_1.test.describe('API optimize SaaS Tests', () => {
186
204
  (0, test_1.expect)(body[0]).toHaveProperty('name');
187
205
  (0, test_1.expect)(body[0]).toHaveProperty('collectionId');
188
206
  });
189
- await _8_10_1.test.step('Export reports with invalid token (401)', async () => {
207
+ });
208
+ (0, _8_10_1.test)('Export reports with invalid token returns 401', async ({ request }) => {
209
+ await _8_10_1.test.step('POST export reports with invalid token (401)', async () => {
190
210
  const response = await request.post(`${baseUrl}/api/public/export/report/definition/json`, {
191
211
  headers: {
192
212
  Authorization: 'Bearer invalid_token',
@@ -196,7 +216,9 @@ _8_10_1.test.describe('API optimize SaaS Tests', () => {
196
216
  });
197
217
  (0, test_1.expect)(response.status()).toBe(401);
198
218
  });
199
- await _8_10_1.test.step('Export non-existent report (404)', async () => {
219
+ });
220
+ (0, _8_10_1.test)('Export non-existent report returns 404', async ({ request }) => {
221
+ await _8_10_1.test.step('POST export non-existent report (404)', async () => {
200
222
  const invalidReportId = '11111111-1111-1111-1111-111111111111';
201
223
  const response = await request.post(`${baseUrl}/api/public/export/report/definition/json`, {
202
224
  headers: {
@@ -207,21 +229,38 @@ _8_10_1.test.describe('API optimize SaaS Tests', () => {
207
229
  });
208
230
  await (0, apiHelpers_1.assertResponseStatus)(response, 404);
209
231
  });
232
+ });
233
+ (0, _8_10_1.test)('Delete report and verify it is no longer retrievable', async ({ request, }) => {
234
+ const reportToDeleteId = await (0, apiHelpers_1.createSingleProcessReport)(request, {
235
+ optimizeCookie,
236
+ collectionId: collectionIdValue,
237
+ name: await (0, randomName_1.randomNameAgregator)('Report To Delete'),
238
+ definitions: [
239
+ {
240
+ key: 'aProcess',
241
+ filter: [],
242
+ groupBy: { type: 'flowNodes', value: null },
243
+ distributedBy: { type: 'none', value: null },
244
+ view: { entity: 'flowNode', properties: ['duration'] },
245
+ },
246
+ ],
247
+ });
210
248
  await _8_10_1.test.step('Delete report successfully (200)', async () => {
211
- const response = await request.delete(`${baseUrl}/api/public/report/${reportId}`, { headers: { Authorization: optimizeBearerToken } });
249
+ const response = await request.delete(`${baseUrl}/api/public/report/${reportToDeleteId}`, { headers: { Authorization: optimizeBearerToken } });
212
250
  await (0, apiHelpers_1.assertResponseStatus)(response, 200);
213
251
  });
214
- await _8_10_1.test.step('Verify deleted report returns 404 or 500', async () => {
215
- const response = await request.get(`${baseUrl}/api/public/report/${reportId}`, { headers: { Authorization: optimizeBearerToken } });
252
+ await _8_10_1.test.step('Verify deleted report is no longer retrievable (404 or 500)', async () => {
253
+ const response = await request.get(`${baseUrl}/api/public/report/${reportToDeleteId}`, { headers: { Authorization: optimizeBearerToken } });
216
254
  (0, test_1.expect)([404, 500]).toContain(response.status());
217
255
  });
218
- await _8_10_1.test.step('Attempt delete report with invalid token (401)', async () => {
256
+ });
257
+ (0, _8_10_1.test)('Delete report with invalid token returns 401', async ({ request }) => {
258
+ await _8_10_1.test.step('DELETE /api/public/report with invalid token (401)', async () => {
219
259
  const response = await request.delete(`${baseUrl}/api/public/report/${reportId}`, { headers: { Authorization: 'Bearer invalid_token' } });
220
260
  (0, test_1.expect)(response.status()).toBe(401);
221
261
  });
222
262
  });
223
- // Skipped due to bug:41410: https://github.com/camunda/camunda/issues/41410
224
- _8_10_1.test.skip('Import entities successfully (200)', async ({ request }) => {
263
+ (0, _8_10_1.test)('Import entities successfully (200)', async ({ request }) => {
225
264
  const exportResponse = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
226
265
  headers: {
227
266
  Authorization: optimizeBearerToken,
@@ -230,40 +269,55 @@ _8_10_1.test.describe('API optimize SaaS Tests', () => {
230
269
  data: [dashboardIdValue],
231
270
  });
232
271
  await (0, apiHelpers_1.assertResponseStatus)(exportResponse, 200);
233
- exportedEntities = await exportResponse.json();
272
+ const entitiesToImport = await exportResponse.json();
234
273
  const response = await request.post(`${baseUrl}/api/public/import?collectionId=${collectionIdValue}`, {
235
274
  headers: {
236
275
  Authorization: optimizeBearerToken,
237
276
  'Content-Type': 'application/json',
238
277
  },
239
- data: exportedEntities,
278
+ data: entitiesToImport,
240
279
  });
241
280
  await (0, apiHelpers_1.assertResponseStatus)(response, 200);
242
281
  });
243
- // Skipped due to bug:41410: https://github.com/camunda/camunda/issues/41410
244
- _8_10_1.test.skip('Import without token or invalid token (401)', async ({ request, }) => {
282
+ (0, _8_10_1.test)('Import without token or invalid token returns 401', async ({ request, }) => {
283
+ const exportResponse = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
284
+ headers: {
285
+ Authorization: optimizeBearerToken,
286
+ 'Content-Type': 'application/json',
287
+ },
288
+ data: [dashboardIdValue],
289
+ });
290
+ await (0, apiHelpers_1.assertResponseStatus)(exportResponse, 200);
291
+ const entitiesToImport = await exportResponse.json();
245
292
  const response = await request.post(`${baseUrl}/api/public/import?collectionId=${collectionIdValue}`, {
246
293
  headers: {
247
294
  Authorization: 'Bearer invalid_token',
248
295
  'Content-Type': 'application/json',
249
296
  },
250
- data: exportedEntities,
297
+ data: entitiesToImport,
251
298
  });
252
- const status = response.status();
253
- (0, test_1.expect)(status).toBe(401);
299
+ (0, test_1.expect)(response.status()).toBe(401);
254
300
  });
255
301
  // Skipped due to bug 40497: https://github.com/camunda/camunda/issues/40497
256
302
  _8_10_1.test.skip('Import to non-existent collection (404)', async ({ request }) => {
303
+ const exportResponse = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
304
+ headers: {
305
+ Authorization: optimizeBearerToken,
306
+ 'Content-Type': 'application/json',
307
+ },
308
+ data: [dashboardIdValue],
309
+ });
310
+ await (0, apiHelpers_1.assertResponseStatus)(exportResponse, 200);
311
+ const entitiesToImport = await exportResponse.json();
257
312
  const fakeCollectionId = 'nonexistent-collection-id';
258
313
  const response = await request.post(`${baseUrl}/api/public/import?collectionId=${fakeCollectionId}`, {
259
314
  headers: {
260
315
  Authorization: optimizeBearerToken,
261
316
  'Content-Type': 'application/json',
262
317
  },
263
- data: exportedEntities,
318
+ data: entitiesToImport,
264
319
  });
265
- const status = response.status();
266
- (0, test_1.expect)(status).toBe(404);
320
+ (0, test_1.expect)(response.status()).toBe(404);
267
321
  });
268
322
  });
269
323
  _8_10_1.test.describe('API optimize SaaS Tests - Conditional Events', () => {
@@ -309,6 +363,11 @@ _8_10_1.test.describe('API optimize SaaS Tests - Conditional Events', () => {
309
363
  });
310
364
  baseUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
311
365
  });
366
+ _8_10_1.test.afterAll(async ({ request }) => {
367
+ if (conditionalEventsCollectionId) {
368
+ await request.delete(`${baseUrl}/api/public/collection/${conditionalEventsCollectionId}`, { headers: { Authorization: optimizeBearerToken } });
369
+ }
370
+ });
312
371
  (0, _8_10_1.test)('CE-OPT-09: Creating a Process Report for Conditional Events via API returns HTTP 200', async ({ request, }) => {
313
372
  await _8_10_1.test.step('Create a process report for conditional-events-auto-process (200)', async () => {
314
373
  const response = await request.post(`${baseUrl}/api/report/process/single`, {
@@ -385,7 +444,6 @@ _8_10_1.test.describe('API optimize SaaS Tests - Conditional Events', () => {
385
444
  });
386
445
  });
387
446
  (0, _8_10_1.test)('CE-OPT-13: Deleting the Conditional Events report via API succeeds with HTTP 200', async ({ request, }) => {
388
- // Create a separate report to delete so the shared one remains available for other tests
389
447
  const reportToDeleteId = await (0, apiHelpers_1.createSingleProcessReport)(request, {
390
448
  optimizeCookie,
391
449
  collectionId: conditionalEventsCollectionId,
@@ -436,3 +494,590 @@ _8_10_1.test.describe('API optimize SaaS Tests - Conditional Events', () => {
436
494
  });
437
495
  });
438
496
  });
497
+ _8_10_1.test.describe('API optimize SaaS Tests - Health Readiness', () => {
498
+ let baseUrl;
499
+ _8_10_1.test.beforeAll(async () => {
500
+ baseUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
501
+ });
502
+ (0, _8_10_1.test)('Health readiness returns 200 when Optimize is ready', async ({ request, }) => {
503
+ await _8_10_1.test.step('GET /api/readyz returns 200 (no auth required)', async () => {
504
+ const response = await request.get(`${baseUrl}/api/readyz`);
505
+ (0, test_1.expect)(response.status()).toBe(200);
506
+ });
507
+ });
508
+ (0, _8_10_1.test)('Health readiness rejects requests that include an Authorization header', async ({ request, }) => {
509
+ await _8_10_1.test.step('GET /api/readyz with Authorization header should be rejected', async () => {
510
+ const token = await (0, apiHelpers_1.authSaasAPI)(process.env.OPTIMIZE_API_TOKEN_AUDIENCE);
511
+ const response = await request.get(`${baseUrl}/api/readyz`, {
512
+ headers: { Authorization: token },
513
+ });
514
+ (0, test_1.expect)([400, 401, 403]).toContain(response.status());
515
+ });
516
+ });
517
+ });
518
+ _8_10_1.test.describe('API optimize SaaS Tests - Sharing', () => {
519
+ let optimizeBearerToken;
520
+ let baseUrl;
521
+ _8_10_1.test.beforeAll(async () => {
522
+ optimizeBearerToken = await (0, apiHelpers_1.authSaasAPI)(process.env.OPTIMIZE_API_TOKEN_AUDIENCE);
523
+ baseUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
524
+ });
525
+ (0, _8_10_1.test)('Enable sharing with valid token returns 200 or 204', async ({ request, }) => {
526
+ await _8_10_1.test.step('POST /api/public/share/enable with valid token (200 or 204)', async () => {
527
+ const response = await request.post(`${baseUrl}/api/public/share/enable`, { headers: { Authorization: optimizeBearerToken } });
528
+ (0, test_1.expect)([200, 204]).toContain(response.status());
529
+ });
530
+ });
531
+ (0, _8_10_1.test)('Enable sharing without token returns 401', async ({ request }) => {
532
+ await _8_10_1.test.step('POST /api/public/share/enable without token (401)', async () => {
533
+ const response = await request.post(`${baseUrl}/api/public/share/enable`);
534
+ (0, test_1.expect)(response.status()).toBe(401);
535
+ });
536
+ });
537
+ (0, _8_10_1.test)('Enable sharing with invalid token returns 401', async ({ request }) => {
538
+ await _8_10_1.test.step('POST /api/public/share/enable with invalid token (401)', async () => {
539
+ const response = await request.post(`${baseUrl}/api/public/share/enable`, { headers: { Authorization: 'Bearer invalid_token' } });
540
+ (0, test_1.expect)(response.status()).toBe(401);
541
+ });
542
+ });
543
+ (0, _8_10_1.test)('Disable sharing with valid token returns 200 or 204', async ({ request, }) => {
544
+ await _8_10_1.test.step('POST /api/public/share/disable with valid token (200 or 204)', async () => {
545
+ const response = await request.post(`${baseUrl}/api/public/share/disable`, { headers: { Authorization: optimizeBearerToken } });
546
+ (0, test_1.expect)([200, 204]).toContain(response.status());
547
+ });
548
+ });
549
+ (0, _8_10_1.test)('Disable sharing without token returns 401', async ({ request }) => {
550
+ await _8_10_1.test.step('POST /api/public/share/disable without token (401)', async () => {
551
+ const response = await request.post(`${baseUrl}/api/public/share/disable`);
552
+ (0, test_1.expect)(response.status()).toBe(401);
553
+ });
554
+ });
555
+ (0, _8_10_1.test)('Disable sharing with invalid token returns 401', async ({ request }) => {
556
+ await _8_10_1.test.step('POST /api/public/share/disable with invalid token (401)', async () => {
557
+ const response = await request.post(`${baseUrl}/api/public/share/disable`, { headers: { Authorization: 'Bearer invalid_token' } });
558
+ (0, test_1.expect)(response.status()).toBe(401);
559
+ });
560
+ });
561
+ (0, _8_10_1.test)('Sharing toggle cycle: enable then disable is idempotent', async ({ request, }) => {
562
+ await _8_10_1.test.step('Enable sharing (200 or 204)', async () => {
563
+ const response = await request.post(`${baseUrl}/api/public/share/enable`, { headers: { Authorization: optimizeBearerToken } });
564
+ (0, test_1.expect)([200, 204]).toContain(response.status());
565
+ });
566
+ await _8_10_1.test.step('Disable sharing (200 or 204)', async () => {
567
+ const response = await request.post(`${baseUrl}/api/public/share/disable`, { headers: { Authorization: optimizeBearerToken } });
568
+ (0, test_1.expect)([200, 204]).toContain(response.status());
569
+ });
570
+ await _8_10_1.test.step('Re-enable sharing returns 200 or 204 (idempotent)', async () => {
571
+ const response = await request.post(`${baseUrl}/api/public/share/enable`, { headers: { Authorization: optimizeBearerToken } });
572
+ (0, test_1.expect)([200, 204]).toContain(response.status());
573
+ });
574
+ });
575
+ });
576
+ _8_10_1.test.describe('API optimize SaaS Tests - Dashboard and Report GET edge cases', () => {
577
+ let optimizeBearerToken;
578
+ let optimizeCookie;
579
+ let collectionId;
580
+ let baseUrl;
581
+ _8_10_1.test.beforeAll(async ({ browser, request }) => {
582
+ const page = await browser.newPage();
583
+ optimizeCookie = await (0, apiHelpers_1.getOptimizeCoockie)(page);
584
+ await page.close();
585
+ optimizeBearerToken = await (0, apiHelpers_1.authSaasAPI)(process.env.OPTIMIZE_API_TOKEN_AUDIENCE);
586
+ baseUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
587
+ collectionId = await (0, apiHelpers_1.createCollection)(request, {
588
+ name: await (0, randomName_1.randomNameAgregator)('GET Edge Cases Collection'),
589
+ optimizeCookie,
590
+ });
591
+ });
592
+ _8_10_1.test.afterAll(async ({ request }) => {
593
+ if (collectionId) {
594
+ await request.delete(`${baseUrl}/api/public/collection/${collectionId}`, {
595
+ headers: { Authorization: optimizeBearerToken },
596
+ });
597
+ }
598
+ });
599
+ (0, _8_10_1.test)('GET dashboards with valid token and collectionId returns 200 with id array', async ({ request, }) => {
600
+ await _8_10_1.test.step('GET /api/public/dashboard?collectionId returns 200', async () => {
601
+ const response = await request.get(`${baseUrl}/api/public/dashboard?collectionId=${collectionId}`, { headers: { Authorization: optimizeBearerToken } });
602
+ await (0, apiHelpers_1.assertResponseStatus)(response, 200);
603
+ const body = await response.json();
604
+ (0, test_1.expect)(Array.isArray(body)).toBeTruthy();
605
+ });
606
+ });
607
+ (0, _8_10_1.test)('GET dashboards without collectionId param returns 400 or 500', async ({ request, }) => {
608
+ await _8_10_1.test.step('GET /api/public/dashboard without collectionId', async () => {
609
+ const response = await request.get(`${baseUrl}/api/public/dashboard`, {
610
+ headers: { Authorization: optimizeBearerToken },
611
+ });
612
+ (0, test_1.expect)([400, 500]).toContain(response.status());
613
+ });
614
+ });
615
+ (0, _8_10_1.test)('GET reports with valid token and collectionId returns 200 with id array', async ({ request, }) => {
616
+ await _8_10_1.test.step('GET /api/public/report?collectionId returns 200', async () => {
617
+ const response = await request.get(`${baseUrl}/api/public/report?collectionId=${collectionId}`, { headers: { Authorization: optimizeBearerToken } });
618
+ await (0, apiHelpers_1.assertResponseStatus)(response, 200);
619
+ const body = await response.json();
620
+ (0, test_1.expect)(Array.isArray(body)).toBeTruthy();
621
+ });
622
+ });
623
+ (0, _8_10_1.test)('GET reports without collectionId param returns 400 or 500', async ({ request, }) => {
624
+ await _8_10_1.test.step('GET /api/public/report without collectionId', async () => {
625
+ const response = await request.get(`${baseUrl}/api/public/report`, {
626
+ headers: { Authorization: optimizeBearerToken },
627
+ });
628
+ (0, test_1.expect)([400, 500]).toContain(response.status());
629
+ });
630
+ });
631
+ });
632
+ _8_10_1.test.describe('API optimize SaaS Tests - Export edge cases', () => {
633
+ let optimizeBearerToken;
634
+ let optimizeCookie;
635
+ let collectionId;
636
+ let dashboardId1;
637
+ let dashboardId2;
638
+ let reportId1;
639
+ let reportId2;
640
+ let baseUrl;
641
+ _8_10_1.test.beforeAll(async ({ browser, request }) => {
642
+ const page = await browser.newPage();
643
+ optimizeCookie = await (0, apiHelpers_1.getOptimizeCoockie)(page);
644
+ await page.close();
645
+ optimizeBearerToken = await (0, apiHelpers_1.authSaasAPI)(process.env.OPTIMIZE_API_TOKEN_AUDIENCE);
646
+ baseUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
647
+ collectionId = await (0, apiHelpers_1.createCollection)(request, {
648
+ name: await (0, randomName_1.randomNameAgregator)('Export Edge Cases Collection'),
649
+ optimizeCookie,
650
+ });
651
+ dashboardId1 = await (0, apiHelpers_1.createDashboard)(request, {
652
+ name: await (0, randomName_1.randomNameAgregator)('Export Dashboard 1'),
653
+ optimizeCookie,
654
+ collectionId,
655
+ });
656
+ dashboardId2 = await (0, apiHelpers_1.createDashboard)(request, {
657
+ name: await (0, randomName_1.randomNameAgregator)('Export Dashboard 2'),
658
+ optimizeCookie,
659
+ collectionId,
660
+ });
661
+ reportId1 = await (0, apiHelpers_1.createSingleProcessReport)(request, {
662
+ optimizeCookie,
663
+ collectionId,
664
+ name: await (0, randomName_1.randomNameAgregator)('Export Report 1'),
665
+ definitions: [
666
+ {
667
+ key: 'aProcess',
668
+ filter: [],
669
+ groupBy: { type: 'none', value: null },
670
+ distributedBy: { type: 'none', value: null },
671
+ view: { entity: 'processInstance', properties: ['frequency'] },
672
+ },
673
+ ],
674
+ });
675
+ reportId2 = await (0, apiHelpers_1.createSingleProcessReport)(request, {
676
+ optimizeCookie,
677
+ collectionId,
678
+ name: await (0, randomName_1.randomNameAgregator)('Export Report 2'),
679
+ definitions: [
680
+ {
681
+ key: 'aProcess',
682
+ filter: [],
683
+ groupBy: { type: 'none', value: null },
684
+ distributedBy: { type: 'none', value: null },
685
+ view: { entity: 'processInstance', properties: ['frequency'] },
686
+ },
687
+ ],
688
+ });
689
+ });
690
+ _8_10_1.test.afterAll(async ({ request }) => {
691
+ if (collectionId) {
692
+ await request.delete(`${baseUrl}/api/public/collection/${collectionId}`, {
693
+ headers: { Authorization: optimizeBearerToken },
694
+ });
695
+ }
696
+ });
697
+ (0, _8_10_1.test)('Export multiple dashboards in a single call returns 200 with all definitions', async ({ request, }) => {
698
+ await _8_10_1.test.step('POST export with two dashboard IDs returns array of 2', async () => {
699
+ const response = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
700
+ headers: {
701
+ Authorization: optimizeBearerToken,
702
+ 'Content-Type': 'application/json',
703
+ },
704
+ data: [dashboardId1, dashboardId2],
705
+ });
706
+ await (0, apiHelpers_1.assertResponseStatus)(response, 200);
707
+ const body = await response.json();
708
+ (0, test_1.expect)(Array.isArray(body)).toBeTruthy();
709
+ (0, test_1.expect)(body.length).toBeGreaterThanOrEqual(2);
710
+ });
711
+ });
712
+ (0, _8_10_1.test)('Export multiple reports in a single call returns 200 with all definitions', async ({ request, }) => {
713
+ await _8_10_1.test.step('POST export with two report IDs returns array of 2', async () => {
714
+ const response = await request.post(`${baseUrl}/api/public/export/report/definition/json`, {
715
+ headers: {
716
+ Authorization: optimizeBearerToken,
717
+ 'Content-Type': 'application/json',
718
+ },
719
+ data: [reportId1, reportId2],
720
+ });
721
+ await (0, apiHelpers_1.assertResponseStatus)(response, 200);
722
+ const body = await response.json();
723
+ (0, test_1.expect)(Array.isArray(body)).toBeTruthy();
724
+ (0, test_1.expect)(body.length).toBeGreaterThanOrEqual(2);
725
+ });
726
+ });
727
+ (0, _8_10_1.test)('Export reports with empty array body returns 200 with empty array', async ({ request, }) => {
728
+ await _8_10_1.test.step('POST export/report with empty array body returns 200 []', async () => {
729
+ const response = await request.post(`${baseUrl}/api/public/export/report/definition/json`, {
730
+ headers: {
731
+ Authorization: optimizeBearerToken,
732
+ 'Content-Type': 'application/json',
733
+ },
734
+ data: [],
735
+ });
736
+ await (0, apiHelpers_1.assertResponseStatus)(response, 200);
737
+ const body = await response.json();
738
+ (0, test_1.expect)(Array.isArray(body)).toBeTruthy();
739
+ (0, test_1.expect)(body.length).toBe(0);
740
+ });
741
+ });
742
+ (0, _8_10_1.test)('Export dashboards with empty array body returns 200 with empty array', async ({ request, }) => {
743
+ await _8_10_1.test.step('POST export/dashboard with empty array body returns 200 []', async () => {
744
+ const response = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
745
+ headers: {
746
+ Authorization: optimizeBearerToken,
747
+ 'Content-Type': 'application/json',
748
+ },
749
+ data: [],
750
+ });
751
+ await (0, apiHelpers_1.assertResponseStatus)(response, 200);
752
+ const body = await response.json();
753
+ (0, test_1.expect)(Array.isArray(body)).toBeTruthy();
754
+ (0, test_1.expect)(body.length).toBe(0);
755
+ });
756
+ });
757
+ });
758
+ _8_10_1.test.describe('API optimize SaaS Tests - Auth edge cases', () => {
759
+ let baseUrl;
760
+ _8_10_1.test.beforeAll(async () => {
761
+ baseUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
762
+ });
763
+ (0, _8_10_1.test)('Expired JWT token returns 401 on dashboard endpoint', async ({ request, }) => {
764
+ const expiredToken = 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.' +
765
+ 'eyJzdWIiOiJ0ZXN0IiwiZXhwIjoxfQ.' +
766
+ 'invalidsignature';
767
+ await _8_10_1.test.step('GET /api/public/dashboard with expired token returns 401', async () => {
768
+ const response = await request.get(`${baseUrl}/api/public/dashboard?collectionId=any`, { headers: { Authorization: expiredToken } });
769
+ (0, test_1.expect)(response.status()).toBe(401);
770
+ });
771
+ });
772
+ (0, _8_10_1.test)('Malformed Bearer value (empty string) returns 401 on dashboard endpoint', async ({ request, }) => {
773
+ await _8_10_1.test.step('GET /api/public/dashboard with empty Bearer value returns 401', async () => {
774
+ const response = await request.get(`${baseUrl}/api/public/dashboard?collectionId=any`, { headers: { Authorization: 'Bearer ' } });
775
+ (0, test_1.expect)(response.status()).toBe(401);
776
+ });
777
+ });
778
+ (0, _8_10_1.test)('Token with wrong audience returns 401 on dashboard endpoint', async ({ request, }) => {
779
+ await _8_10_1.test.step('Use a Zeebe-audience token against Optimize API and expect 401', async () => {
780
+ const wrongAudienceToken = await (0, apiHelpers_1.authSaasAPI)();
781
+ const response = await request.get(`${baseUrl}/api/public/dashboard?collectionId=any`, { headers: { Authorization: wrongAudienceToken } });
782
+ (0, test_1.expect)(response.status()).toBe(401);
783
+ });
784
+ });
785
+ });
786
+ _8_10_1.test.describe('API V2 tests on SaaS cluster', () => {
787
+ let bearerToken;
788
+ const apiUrl = () => process.env.ZEEBE_API_URL;
789
+ _8_10_1.test.beforeAll(async () => {
790
+ bearerToken = await (0, apiHelpers_1.authSaasAPI)();
791
+ });
792
+ (0, _8_10_1.test)('GET /v2/topology returns 200', async ({ request }) => {
793
+ const response = await request.get(`${apiUrl()}/v2/topology`, {
794
+ headers: { Authorization: bearerToken, 'Content-Type': 'application/json' },
795
+ });
796
+ await (0, apiHelpers_1.assertResponseStatus)(response, 200);
797
+ });
798
+ (0, _8_10_1.test)('GET /v2/topology without token returns 401', async ({ request }) => {
799
+ const response = await request.get(`${apiUrl()}/v2/topology`, {
800
+ headers: { 'Content-Type': 'application/json' },
801
+ });
802
+ (0, test_1.expect)(response.status()).toBe(401);
803
+ });
804
+ (0, _8_10_1.test)('POST /v2/process-definitions/search returns 200 with items array', async ({ request, }) => {
805
+ const response = await request.post(`${apiUrl()}/v2/process-definitions/search`, {
806
+ headers: {
807
+ Authorization: bearerToken,
808
+ 'Content-Type': 'application/json',
809
+ },
810
+ data: { filter: {}, page: { limit: 10 } },
811
+ });
812
+ (0, test_1.expect)([200, 400, 405]).toContain(response.status());
813
+ if (response.status() === 200) {
814
+ const body = await response.json();
815
+ (0, test_1.expect)(body).toHaveProperty('items');
816
+ (0, test_1.expect)(Array.isArray(body.items)).toBeTruthy();
817
+ }
818
+ });
819
+ (0, _8_10_1.test)('POST /v2/process-definitions/search without token returns 401', async ({ request, }) => {
820
+ const response = await request.post(`${apiUrl()}/v2/process-definitions/search`, {
821
+ headers: { 'Content-Type': 'application/json' },
822
+ data: { filter: {}, size: 10 },
823
+ });
824
+ (0, test_1.expect)(response.status()).toBe(401);
825
+ });
826
+ (0, _8_10_1.test)('POST /v2/process-definitions/search with invalid token returns 401', async ({ request, }) => {
827
+ const response = await request.post(`${apiUrl()}/v2/process-definitions/search`, {
828
+ headers: {
829
+ Authorization: 'Bearer invalid_token',
830
+ 'Content-Type': 'application/json',
831
+ },
832
+ data: { filter: {}, size: 10 },
833
+ });
834
+ (0, test_1.expect)(response.status()).toBe(401);
835
+ });
836
+ (0, _8_10_1.test)('POST /v2/process-definitions/search with size 1 returns pagination cursor', async ({ request, }) => {
837
+ const response = await request.post(`${apiUrl()}/v2/process-definitions/search`, {
838
+ headers: {
839
+ Authorization: bearerToken,
840
+ 'Content-Type': 'application/json',
841
+ },
842
+ data: { filter: {}, page: { limit: 1 } },
843
+ });
844
+ (0, test_1.expect)([200, 400, 405]).toContain(response.status());
845
+ if (response.status() === 200) {
846
+ const body = await response.json();
847
+ (0, test_1.expect)(body).toHaveProperty('items');
848
+ (0, test_1.expect)(body.items.length).toBeLessThanOrEqual(1);
849
+ if (body.page?.totalItems > 1) {
850
+ (0, test_1.expect)(body).toHaveProperty('page');
851
+ }
852
+ }
853
+ });
854
+ (0, _8_10_1.test)('POST /v2/process-instances/search returns 200 with items array', async ({ request, }) => {
855
+ const response = await request.post(`${apiUrl()}/v2/process-instances/search`, {
856
+ headers: {
857
+ Authorization: bearerToken,
858
+ 'Content-Type': 'application/json',
859
+ },
860
+ data: { filter: {}, page: { limit: 10 } },
861
+ });
862
+ (0, test_1.expect)([200, 400, 405]).toContain(response.status());
863
+ if (response.status() === 200) {
864
+ const body = await response.json();
865
+ (0, test_1.expect)(body).toHaveProperty('items');
866
+ (0, test_1.expect)(Array.isArray(body.items)).toBeTruthy();
867
+ }
868
+ });
869
+ (0, _8_10_1.test)('POST /v2/process-instances/search filtered by state ACTIVE returns 200', async ({ request, }) => {
870
+ const response = await request.post(`${apiUrl()}/v2/process-instances/search`, {
871
+ headers: {
872
+ Authorization: bearerToken,
873
+ 'Content-Type': 'application/json',
874
+ },
875
+ data: { filter: { state: 'ACTIVE' }, page: { limit: 10 } },
876
+ });
877
+ (0, test_1.expect)([200, 400, 405]).toContain(response.status());
878
+ if (response.status() === 200) {
879
+ const body = await response.json();
880
+ (0, test_1.expect)(body).toHaveProperty('items');
881
+ }
882
+ });
883
+ (0, _8_10_1.test)('POST /v2/process-instances/search without token returns 401', async ({ request, }) => {
884
+ const response = await request.post(`${apiUrl()}/v2/process-instances/search`, {
885
+ headers: { 'Content-Type': 'application/json' },
886
+ data: { filter: {}, size: 10 },
887
+ });
888
+ (0, test_1.expect)(response.status()).toBe(401);
889
+ });
890
+ (0, _8_10_1.test)('POST /v2/process-instances/search with invalid token returns 401', async ({ request, }) => {
891
+ const response = await request.post(`${apiUrl()}/v2/process-instances/search`, {
892
+ headers: {
893
+ Authorization: 'Bearer invalid_token',
894
+ 'Content-Type': 'application/json',
895
+ },
896
+ data: { filter: {}, size: 10 },
897
+ });
898
+ (0, test_1.expect)(response.status()).toBe(401);
899
+ });
900
+ (0, _8_10_1.test)('POST /v2/user-tasks/search returns 200 with items array', async ({ request, }) => {
901
+ const response = await request.post(`${apiUrl()}/v2/user-tasks/search`, {
902
+ headers: { Authorization: bearerToken, 'Content-Type': 'application/json' },
903
+ data: { filter: {}, page: { limit: 10 } },
904
+ });
905
+ (0, test_1.expect)([200, 400, 405]).toContain(response.status());
906
+ if (response.status() === 200) {
907
+ const body = await response.json();
908
+ (0, test_1.expect)(body).toHaveProperty('items');
909
+ (0, test_1.expect)(Array.isArray(body.items)).toBeTruthy();
910
+ }
911
+ });
912
+ (0, _8_10_1.test)('POST /v2/user-tasks/search without token returns 401', async ({ request, }) => {
913
+ const response = await request.post(`${apiUrl()}/v2/user-tasks/search`, {
914
+ headers: { 'Content-Type': 'application/json' },
915
+ data: { filter: {}, size: 10 },
916
+ });
917
+ (0, test_1.expect)(response.status()).toBe(401);
918
+ });
919
+ (0, _8_10_1.test)('POST /v2/user-tasks/search with invalid token returns 401', async ({ request, }) => {
920
+ const response = await request.post(`${apiUrl()}/v2/user-tasks/search`, {
921
+ headers: {
922
+ Authorization: 'Bearer invalid_token',
923
+ 'Content-Type': 'application/json',
924
+ },
925
+ data: { filter: {}, size: 10 },
926
+ });
927
+ (0, test_1.expect)(response.status()).toBe(401);
928
+ });
929
+ (0, _8_10_1.test)('POST /v2/variables/search returns 200 with items array', async ({ request, }) => {
930
+ const response = await request.post(`${apiUrl()}/v2/variables/search`, {
931
+ headers: { Authorization: bearerToken, 'Content-Type': 'application/json' },
932
+ data: { filter: {}, page: { limit: 10 } },
933
+ });
934
+ (0, test_1.expect)([200, 400, 405]).toContain(response.status());
935
+ if (response.status() === 200) {
936
+ const body = await response.json();
937
+ (0, test_1.expect)(body).toHaveProperty('items');
938
+ (0, test_1.expect)(Array.isArray(body.items)).toBeTruthy();
939
+ }
940
+ });
941
+ (0, _8_10_1.test)('POST /v2/variables/search without token returns 401', async ({ request, }) => {
942
+ const response = await request.post(`${apiUrl()}/v2/variables/search`, {
943
+ headers: { 'Content-Type': 'application/json' },
944
+ data: { filter: {}, size: 10 },
945
+ });
946
+ (0, test_1.expect)(response.status()).toBe(401);
947
+ });
948
+ (0, _8_10_1.test)('POST /v2/variables/search with invalid token returns 401', async ({ request, }) => {
949
+ const response = await request.post(`${apiUrl()}/v2/variables/search`, {
950
+ headers: {
951
+ Authorization: 'Bearer invalid_token',
952
+ 'Content-Type': 'application/json',
953
+ },
954
+ data: { filter: {}, size: 10 },
955
+ });
956
+ (0, test_1.expect)(response.status()).toBe(401);
957
+ });
958
+ (0, _8_10_1.test)('POST /v2/incidents/search returns 200 with items array', async ({ request, }) => {
959
+ const response = await request.post(`${apiUrl()}/v2/incidents/search`, {
960
+ headers: { Authorization: bearerToken, 'Content-Type': 'application/json' },
961
+ data: { filter: {}, page: { limit: 10 } },
962
+ });
963
+ (0, test_1.expect)([200, 400, 405]).toContain(response.status());
964
+ if (response.status() === 200) {
965
+ const body = await response.json();
966
+ (0, test_1.expect)(body).toHaveProperty('items');
967
+ (0, test_1.expect)(Array.isArray(body.items)).toBeTruthy();
968
+ }
969
+ });
970
+ (0, _8_10_1.test)('POST /v2/incidents/search without token returns 401', async ({ request, }) => {
971
+ const response = await request.post(`${apiUrl()}/v2/incidents/search`, {
972
+ headers: { 'Content-Type': 'application/json' },
973
+ data: { filter: {}, size: 10 },
974
+ });
975
+ (0, test_1.expect)(response.status()).toBe(401);
976
+ });
977
+ (0, _8_10_1.test)('POST /v2/incidents/search with invalid token returns 401', async ({ request, }) => {
978
+ const response = await request.post(`${apiUrl()}/v2/incidents/search`, {
979
+ headers: {
980
+ Authorization: 'Bearer invalid_token',
981
+ 'Content-Type': 'application/json',
982
+ },
983
+ data: { filter: {}, size: 10 },
984
+ });
985
+ (0, test_1.expect)(response.status()).toBe(401);
986
+ });
987
+ (0, _8_10_1.test)('POST /v2/decision-definitions/search returns 200 with items array', async ({ request, }) => {
988
+ const response = await request.post(`${apiUrl()}/v2/decision-definitions/search`, {
989
+ headers: {
990
+ Authorization: bearerToken,
991
+ 'Content-Type': 'application/json',
992
+ },
993
+ data: { filter: {}, page: { limit: 10 } },
994
+ });
995
+ (0, test_1.expect)([200, 400, 405]).toContain(response.status());
996
+ if (response.status() === 200) {
997
+ const body = await response.json();
998
+ (0, test_1.expect)(body).toHaveProperty('items');
999
+ (0, test_1.expect)(Array.isArray(body.items)).toBeTruthy();
1000
+ }
1001
+ });
1002
+ (0, _8_10_1.test)('POST /v2/decision-definitions/search without token returns 401', async ({ request, }) => {
1003
+ const response = await request.post(`${apiUrl()}/v2/decision-definitions/search`, {
1004
+ headers: { 'Content-Type': 'application/json' },
1005
+ data: { filter: {}, size: 10 },
1006
+ });
1007
+ (0, test_1.expect)(response.status()).toBe(401);
1008
+ });
1009
+ (0, _8_10_1.test)('POST /v2/decision-definitions/search with invalid token returns 401', async ({ request, }) => {
1010
+ const response = await request.post(`${apiUrl()}/v2/decision-definitions/search`, {
1011
+ headers: {
1012
+ Authorization: 'Bearer invalid_token',
1013
+ 'Content-Type': 'application/json',
1014
+ },
1015
+ data: { filter: {}, size: 10 },
1016
+ });
1017
+ (0, test_1.expect)(response.status()).toBe(401);
1018
+ });
1019
+ (0, _8_10_1.test)('POST /v2/decision-instances/search returns 200 with items array', async ({ request, }) => {
1020
+ const response = await request.post(`${apiUrl()}/v2/decision-instances/search`, {
1021
+ headers: {
1022
+ Authorization: bearerToken,
1023
+ 'Content-Type': 'application/json',
1024
+ },
1025
+ data: { filter: {}, page: { limit: 10 } },
1026
+ });
1027
+ (0, test_1.expect)([200, 400, 405]).toContain(response.status());
1028
+ if (response.status() === 200) {
1029
+ const body = await response.json();
1030
+ (0, test_1.expect)(body).toHaveProperty('items');
1031
+ (0, test_1.expect)(Array.isArray(body.items)).toBeTruthy();
1032
+ }
1033
+ });
1034
+ (0, _8_10_1.test)('POST /v2/decision-instances/search without token returns 401', async ({ request, }) => {
1035
+ const response = await request.post(`${apiUrl()}/v2/decision-instances/search`, {
1036
+ headers: { 'Content-Type': 'application/json' },
1037
+ data: { filter: {}, size: 10 },
1038
+ });
1039
+ (0, test_1.expect)(response.status()).toBe(401);
1040
+ });
1041
+ (0, _8_10_1.test)('POST /v2/decision-instances/search with invalid token returns 401', async ({ request, }) => {
1042
+ const response = await request.post(`${apiUrl()}/v2/decision-instances/search`, {
1043
+ headers: {
1044
+ Authorization: 'Bearer invalid_token',
1045
+ 'Content-Type': 'application/json',
1046
+ },
1047
+ data: { filter: {}, size: 10 },
1048
+ });
1049
+ (0, test_1.expect)(response.status()).toBe(401);
1050
+ });
1051
+ (0, _8_10_1.test)('POST /v2/flownode-instances/search returns 200 with items array', async ({ request, }) => {
1052
+ const response = await request.post(`${apiUrl()}/v2/flownode-instances/search`, {
1053
+ headers: {
1054
+ Authorization: bearerToken,
1055
+ 'Content-Type': 'application/json',
1056
+ },
1057
+ data: { filter: {}, page: { limit: 10 } },
1058
+ });
1059
+ (0, test_1.expect)([200, 404, 405]).toContain(response.status());
1060
+ if (response.status() === 200) {
1061
+ const body = await response.json();
1062
+ (0, test_1.expect)(body).toHaveProperty('items');
1063
+ (0, test_1.expect)(Array.isArray(body.items)).toBeTruthy();
1064
+ }
1065
+ });
1066
+ (0, _8_10_1.test)('POST /v2/flownode-instances/search without token returns 401', async ({ request, }) => {
1067
+ const response = await request.post(`${apiUrl()}/v2/flownode-instances/search`, {
1068
+ headers: { 'Content-Type': 'application/json' },
1069
+ data: { filter: {}, size: 10 },
1070
+ });
1071
+ (0, test_1.expect)(response.status()).toBe(401);
1072
+ });
1073
+ (0, _8_10_1.test)('POST /v2/flownode-instances/search with invalid token returns 401', async ({ request, }) => {
1074
+ const response = await request.post(`${apiUrl()}/v2/flownode-instances/search`, {
1075
+ headers: {
1076
+ Authorization: 'Bearer invalid_token',
1077
+ 'Content-Type': 'application/json',
1078
+ },
1079
+ data: { filter: {}, size: 10 },
1080
+ });
1081
+ (0, test_1.expect)(response.status()).toBe(401);
1082
+ });
1083
+ });
@@ -8,6 +8,7 @@ const test_1 = require("@playwright/test");
8
8
  const sleep_1 = require("./sleep");
9
9
  const fs_1 = __importDefault(require("fs"));
10
10
  const path_1 = __importDefault(require("path"));
11
+ const os_1 = __importDefault(require("os"));
11
12
  const utils_1 = require("./utils");
12
13
  let apiRequestContext;
13
14
  // ---- Internal: API request context ----
@@ -411,8 +412,10 @@ exports.createStringClusterVariable = createStringClusterVariable;
411
412
  async function getOptimizeCoockie(page) {
412
413
  await (0, utils_1.loginToApp)(process.env.CAMUNDA_OPTIMIZE_BASE_URL, page);
413
414
  const context = page.context();
414
- await context.storageState({ path: path_1.default.join(__dirname, '.auth_optimize') });
415
- const authData = JSON.parse(fs_1.default.readFileSync(path_1.default.join(__dirname, '.auth_optimize'), 'utf8'));
415
+ const tempFile = path_1.default.join(os_1.default.tmpdir(), `.auth_optimize_${Date.now()}_${Math.random().toString(36).slice(2)}`);
416
+ await context.storageState({ path: tempFile });
417
+ const authData = JSON.parse(fs_1.default.readFileSync(tempFile, 'utf8'));
418
+ fs_1.default.unlinkSync(tempFile);
416
419
  const serviceTokenCookie = authData.cookies.find((cookie) => cookie.name === 'X-Optimize-Service-Token_0');
417
420
  const authTokenCookie = authData.cookies.find((cookie) => cookie.name === 'X-Optimize-Authorization_0');
418
421
  const optimizeServiceToken = serviceTokenCookie?.value || '';
@@ -4,14 +4,24 @@ exports.loginToApp = void 0;
4
4
  const test_1 = require("@playwright/test");
5
5
  const LoginPage_1 = require("../pages/8.8/LoginPage");
6
6
  async function loginToApp(appName, page) {
7
- await page.goto(appName);
7
+ await page.goto(appName, { waitUntil: 'domcontentloaded', timeout: 60000 });
8
+ await page.waitForLoadState('networkidle', { timeout: 30000 }).catch(() => { });
8
9
  const loginPage = new LoginPage_1.LoginPage(page);
10
+ await (0, test_1.expect)(loginPage.usernameInput).toBeVisible({ timeout: 60000 });
9
11
  await loginPage.fillUsername(process.env.C8_USERNAME);
10
12
  await (0, test_1.expect)(loginPage.usernameInput).toHaveValue(process.env.C8_USERNAME);
11
13
  await (0, test_1.expect)(loginPage.loginMessage).toBeVisible();
12
14
  await loginPage.clickContinueButton();
15
+ await (0, test_1.expect)(loginPage.passwordInput).toBeVisible({ timeout: 60000 });
13
16
  await loginPage.fillPassword(process.env.C8_PASSWORD);
14
17
  await (0, test_1.expect)(loginPage.passwordHeading).toBeVisible();
15
18
  await loginPage.clickLoginButton();
19
+ await page.waitForURL((url) => url.toString().startsWith(appName), {
20
+ timeout: 60000,
21
+ waitUntil: 'commit',
22
+ });
23
+ await page
24
+ .waitForLoadState('domcontentloaded', { timeout: 30000 })
25
+ .catch(() => { });
16
26
  }
17
27
  exports.loginToApp = loginToApp;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.546",
3
+ "version": "0.0.548",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",
@@ -4261,12 +4261,19 @@ fi
4261
4261
  refresh_auth_token
4262
4262
 
4263
4263
  # =============================================================================
4264
- # Forms (search is optional on some builds; tolerate 404)
4264
+ # Forms (search endpoint does not exist in all versions; skip gracefully)
4265
4265
  # =============================================================================
4266
4266
  echo -e "\n${BLUE}Testing Form Endpoints${NC}"
4267
- response="$(call_api_json "POST" "/v2/forms/search" '{"filter":{}}')"
4268
- print_result_multi "POST /v2/forms/search" "$response" "200 404"
4269
- body="$(echo "$response" | get_body)"; FORM_KEY="$(echo "$body" | jq -r '.items[0].formKey // empty' 2>/dev/null || true)"
4267
+ response="$(call_api_json "POST" "/v2/forms/search" '{"filter":{}}')"
4268
+ forms_status="$(echo "$response" | extract_status)"
4269
+ body="$(echo "$response" | get_body)"
4270
+ FORM_KEY=""
4271
+ if [[ "$forms_status" == "200" ]]; then
4272
+ print_result_multi "POST /v2/forms/search" "$response" "200"
4273
+ FORM_KEY="$(echo "$body" | jq -r '.items[0].formKey // empty' 2>/dev/null || true)"
4274
+ else
4275
+ echo " POST /v2/forms/search returned $forms_status — endpoint not available in this version, skipping form tests"
4276
+ fi
4270
4277
  if [[ -n "$FORM_KEY" ]]; then
4271
4278
  echo -e "\nTesting GET /v2/forms/$FORM_KEY"
4272
4279
  response="$(call_api_get "/v2/forms/$FORM_KEY")"; print_result "GET /v2/forms/{key} (key: $FORM_KEY)" "$response"
@@ -4810,10 +4817,13 @@ else
4810
4817
  fi
4811
4818
 
4812
4819
  if [[ -n "$UW_DEFINITION_KEY" ]]; then
4820
+ # Prefer ZEEBE_USER_TASK_KEY so instances wait at a user task and stay ACTIVE long
4821
+ # enough for the batch to be suspended. UW_DEFINITION_KEY may complete immediately.
4822
+ BATCH_DEF_KEY="${ZEEBE_USER_TASK_KEY:-$UW_DEFINITION_KEY}"
4813
4823
  echo "Starting 200 process instances for batch operation testing (more instances = longer ACTIVE state)..."
4814
4824
  # Disable errexit for batch instance creation - failures here shouldn't crash the whole script
4815
4825
  for i in {1..200}; do
4816
- start_payload="$(jq -n --arg k "$UW_DEFINITION_KEY" '{processDefinitionKey:$k, variables:{batchTest:true}}')"
4826
+ start_payload="$(jq -n --arg k "$BATCH_DEF_KEY" '{processDefinitionKey:$k, variables:{batchTest:true}}')"
4817
4827
  start_response="$(echo "$start_payload" | call_api_json "POST" "/v2/process-instances" -)"
4818
4828
  start_body="$(echo "$start_response" | get_body)"
4819
4829
  instance_key="$(echo "$start_body" | jq -r '.processInstanceKey // empty')"
@@ -1 +0,0 @@
1
- export {};
@@ -1,20 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const _8_10_1 = require("../../fixtures/8.10");
4
- const apiHelpers_1 = require("../../utils/apiHelpers");
5
- _8_10_1.test.describe.configure({ mode: 'parallel' });
6
- _8_10_1.test.describe('API V2 tests on SaaS cluster ', () => {
7
- let bearerToken;
8
- _8_10_1.test.beforeAll(async () => {
9
- bearerToken = await (0, apiHelpers_1.authSaasAPI)();
10
- });
11
- (0, _8_10_1.test)('Get cluster topology', async ({ request }) => {
12
- const topologyResponse = await request.get(`${process.env.ZEEBE_API_URL}/v2/topology`, {
13
- headers: {
14
- Authorization: bearerToken,
15
- 'Content-Type': 'application/json',
16
- },
17
- });
18
- await (0, apiHelpers_1.assertResponseStatus)(topologyResponse, 200);
19
- });
20
- });