@camunda/e2e-test-suite 0.0.572 → 0.0.573

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,44 @@ 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);
16
38
  await loginPage.loginWithTestUser(testUser);
17
39
  return;
18
40
  }
19
41
  catch (error) {
42
+ lastError = error;
20
43
  if (attempt < maxRetries - 1) {
21
- console.warn(`Attempt ${attempt + 1} failed for logging in. Retrying...`);
44
+ console.warn(`Attempt ${attempt + 1} failed for logging in. Retrying with clean session...`, error);
22
45
  await (0, randomSleep_1.randomSleep)(10000, 20000);
23
46
  }
24
47
  else {
25
- console.error(error);
26
- throw new Error(`Login failed after ${maxRetries} attempts`);
48
+ console.error(lastError);
49
+ throw new Error(`Login failed after ${maxRetries} attempts: ${String(lastError)}`);
27
50
  }
28
51
  }
29
52
  }
@@ -6,7 +6,7 @@ _8_9_1.test.describe.configure({ mode: 'parallel' });
6
6
  _8_9_1.test.describe('API V2 tests on SaaS cluster ', () => {
7
7
  let bearerToken;
8
8
  _8_9_1.test.beforeAll(async () => {
9
- bearerToken = await (0, apiHelpers_1.authSaasAPI)();
9
+ bearerToken = await (0, apiHelpers_1.authSaasAPI)(process.env.ZEEBE_API_TOKEN_AUDIENCE);
10
10
  });
11
11
  (0, _8_9_1.test)('Get cluster topology', async ({ request }) => {
12
12
  const topologyResponse = await request.get(`${process.env.ZEEBE_API_URL}/v2/topology`, {
@@ -4,6 +4,7 @@ const test_1 = require("@playwright/test");
4
4
  const _8_9_1 = require("../../fixtures/8.9");
5
5
  const UtilitiesPage_1 = require("../../pages/8.9/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");
8
9
  const urlHelpers_1 = require("../../utils/urlHelpers");
9
10
  const testUser = (0, users_1.getTestUser)('twentySecondUser');
@@ -25,8 +26,9 @@ _8_9_1.test.describe('Operate access requires authentication @tasklistV2', () =>
25
26
  await clusterDetailsPage.deleteAPIClientsIfExist(clientName);
26
27
  }
27
28
  });
28
- (0, _8_9_1.test)('check that request POST /v2/process-definitions/search returns 401 without credentials', async ({ homePage, clusterPage, clusterDetailsPage, clientCredentialsDetailsPage, request, }) => {
29
- clientName = `operate-deny-${await (0, _setup_1.generateRandomStringAsync)(5)}`;
29
+ (0, _8_9_1.test)('check that request POST /v2/process-definitions/search returns 200 with valid credentials', async ({ homePage, clusterPage, clusterDetailsPage, clientCredentialsDetailsPage, request, }) => {
30
+ clientName = `operate-allow-${await (0, _setup_1.generateRandomStringAsync)(5)}`;
31
+ let operateUrl = '';
30
32
  await _8_9_1.test.step('Add API Client to Cluster', async () => {
31
33
  await homePage.clickClusters();
32
34
  await clusterPage.clickClusterLink(clusterName);
@@ -35,22 +37,81 @@ _8_9_1.test.describe('Operate access requires authentication @tasklistV2', () =>
35
37
  await clusterDetailsPage.clickCloseModalButton();
36
38
  await (0, test_1.expect)(clusterDetailsPage.clientsList.filter({ hasText: clientName })).toBeVisible({ timeout: 6000 });
37
39
  });
38
- let operateUrl = '';
39
- await _8_9_1.test.step('Capture Operate URL from client credentials page and close it', async () => {
40
+ await _8_9_1.test.step('Capture Operate URL', async () => {
40
41
  await clusterDetailsPage.searchAndClickClientCredentialsLink(clientName);
41
42
  operateUrl = await clientCredentialsDetailsPage.getOperateUrl();
42
43
  (0, test_1.expect)(operateUrl).toMatch(/^https?:\/\//);
43
44
  await clientCredentialsDetailsPage.goBack();
44
45
  await clusterDetailsPage.clickAPITab();
45
46
  });
46
- await _8_9_1.test.step('POST search endpoint without auth should be rejected', async () => {
47
+ await _8_9_1.test.step('POST search endpoint with valid Zeebe bearer token returns 200 or 405', async () => {
48
+ const validToken = await (0, apiHelpers_1.authSaasAPI)();
47
49
  const sanitizedOperateUrl = (0, urlHelpers_1.sanitizeUrl)(operateUrl);
48
50
  const response = await request.post(`${sanitizedOperateUrl}/v2/process-definitions/search`, {
51
+ headers: {
52
+ Authorization: validToken,
53
+ 'Content-Type': 'application/json',
54
+ },
55
+ data: { filter: {}, size: 10 },
56
+ });
57
+ (0, test_1.expect)([200, 405]).toContain(response.status());
58
+ });
59
+ });
60
+ (0, _8_9_1.test)('check that POST /v2/user-tasks/search returns 401 without credentials', async ({ homePage, clusterPage, clusterDetailsPage, clientCredentialsDetailsPage, request, }) => {
61
+ clientName = `tasklist-deny-${await (0, _setup_1.generateRandomStringAsync)(5)}`;
62
+ let operateUrl = '';
63
+ await _8_9_1.test.step('Add API Client to Cluster', async () => {
64
+ await homePage.clickClusters();
65
+ await clusterPage.clickClusterLink(clusterName);
66
+ await clusterDetailsPage.clickAPITab();
67
+ await clusterDetailsPage.createAPIClient(clientName);
68
+ await clusterDetailsPage.clickCloseModalButton();
69
+ await (0, test_1.expect)(clusterDetailsPage.clientsList.filter({ hasText: clientName })).toBeVisible({ timeout: 6000 });
70
+ });
71
+ await _8_9_1.test.step('Capture Operate URL (base URL shared with Tasklist v2 endpoint)', async () => {
72
+ await clusterDetailsPage.searchAndClickClientCredentialsLink(clientName);
73
+ operateUrl = await clientCredentialsDetailsPage.getOperateUrl();
74
+ (0, test_1.expect)(operateUrl).toMatch(/^https?:\/\//);
75
+ await clientCredentialsDetailsPage.goBack();
76
+ await clusterDetailsPage.clickAPITab();
77
+ });
78
+ await _8_9_1.test.step('POST /v2/user-tasks/search without auth returns 401', async () => {
79
+ const sanitizedOperateUrl = (0, urlHelpers_1.sanitizeUrl)(operateUrl);
80
+ const response = await request.post(`${sanitizedOperateUrl}/v2/user-tasks/search`, {
49
81
  data: { filter: {}, size: 10 },
50
82
  });
51
83
  (0, test_1.expect)(response.status()).toBe(401);
52
- const body = await response.text();
53
- (0, test_1.expect)(body).toBe('');
84
+ });
85
+ });
86
+ (0, _8_9_1.test)('check that POST /v2/process-definitions/search with wrong-audience token returns 401', async ({ homePage, clusterPage, clusterDetailsPage, clientCredentialsDetailsPage, request, }) => {
87
+ clientName = `operate-wrong-aud-${await (0, _setup_1.generateRandomStringAsync)(5)}`;
88
+ let operateUrl = '';
89
+ await _8_9_1.test.step('Add API Client to Cluster', async () => {
90
+ await homePage.clickClusters();
91
+ await clusterPage.clickClusterLink(clusterName);
92
+ await clusterDetailsPage.clickAPITab();
93
+ await clusterDetailsPage.createAPIClient(clientName);
94
+ await clusterDetailsPage.clickCloseModalButton();
95
+ await (0, test_1.expect)(clusterDetailsPage.clientsList.filter({ hasText: clientName })).toBeVisible({ timeout: 6000 });
96
+ });
97
+ await _8_9_1.test.step('Capture Operate URL', async () => {
98
+ await clusterDetailsPage.searchAndClickClientCredentialsLink(clientName);
99
+ operateUrl = await clientCredentialsDetailsPage.getOperateUrl();
100
+ (0, test_1.expect)(operateUrl).toMatch(/^https?:\/\//);
101
+ await clientCredentialsDetailsPage.goBack();
102
+ await clusterDetailsPage.clickAPITab();
103
+ });
104
+ await _8_9_1.test.step('Token scoped for Optimize audience rejected by Operate endpoint', async () => {
105
+ const optimizeToken = await (0, apiHelpers_1.authSaasAPI)(process.env.OPTIMIZE_API_TOKEN_AUDIENCE);
106
+ const sanitizedOperateUrl = (0, urlHelpers_1.sanitizeUrl)(operateUrl);
107
+ const response = await request.post(`${sanitizedOperateUrl}/v2/process-definitions/search`, {
108
+ headers: {
109
+ Authorization: optimizeToken,
110
+ 'Content-Type': 'application/json',
111
+ },
112
+ data: { filter: {}, size: 10 },
113
+ });
114
+ (0, test_1.expect)([401, 403]).toContain(response.status());
54
115
  });
55
116
  });
56
117
  });
@@ -1,10 +1,8 @@
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_9_1 = require("../../fixtures/8.9");
7
3
  const test_1 = require("@playwright/test");
4
+ const _8_9_1 = require("../../fixtures/8.9");
5
+ const constants_1 = require("../../utils/constants");
8
6
  const apiHelpers_1 = require("../../utils/apiHelpers");
9
7
  const randomName_1 = require("../../utils/randomName");
10
8
  _8_9_1.test.describe.configure({ mode: 'parallel' });
@@ -13,10 +11,8 @@ _8_9_1.test.describe('API optimize SaaS Tests', () => {
13
11
  let optimizeBearerToken;
14
12
  let collectionIdValue;
15
13
  let dashboardIdValue;
16
- let collectionScopeResponse;
17
14
  let reportId;
18
15
  let baseUrl;
19
- let exportedEntities;
20
16
  _8_9_1.test.beforeAll(async ({ browser, request }) => {
21
17
  const page = await browser.newPage();
22
18
  optimizeCookie = await (0, apiHelpers_1.getOptimizeCoockie)(page);
@@ -26,7 +22,8 @@ _8_9_1.test.describe('API optimize SaaS Tests', () => {
26
22
  name: await (0, randomName_1.randomNameAgregator)('Test Collection'),
27
23
  optimizeCookie,
28
24
  });
29
- collectionScopeResponse = await (0, apiHelpers_1.updateCollectionScope)(request, {
25
+ baseUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
26
+ await (0, apiHelpers_1.updateCollectionScope)(request, {
30
27
  optimizeCookie,
31
28
  collectionId: collectionIdValue,
32
29
  data: [
@@ -56,11 +53,14 @@ _8_9_1.test.describe('API optimize SaaS Tests', () => {
56
53
  },
57
54
  ],
58
55
  });
59
- baseUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
60
56
  });
61
- // Skipped due to bug:41410: https://github.com/camunda/camunda/issues/41410
62
- _8_9_1.test.skip('Dashboard Scenarios Flow Tests', async ({ request }) => {
63
- await _8_9_1.test.step('Get dashboards successfully (200)', async () => {
57
+ _8_9_1.test.afterAll(async ({ request }) => {
58
+ if (collectionIdValue) {
59
+ await request.delete(`${baseUrl}/api/public/collection/${collectionIdValue}`, { headers: { Authorization: optimizeBearerToken } });
60
+ }
61
+ });
62
+ (0, _8_9_1.test)('Get dashboard IDs returns 200 with id array', async ({ request }) => {
63
+ await _8_9_1.test.step('GET /api/public/dashboard?collectionId returns 200', async () => {
64
64
  const response = await request.get(`${baseUrl}/api/public/dashboard?collectionId=${collectionIdValue}`, { headers: { Authorization: optimizeBearerToken } });
65
65
  await (0, apiHelpers_1.assertResponseStatus)(response, 200);
66
66
  const body = await response.json();
@@ -69,19 +69,27 @@ _8_9_1.test.describe('API optimize SaaS Tests', () => {
69
69
  const dashboard = body[0];
70
70
  (0, test_1.expect)(dashboard).toHaveProperty('id');
71
71
  });
72
- await _8_9_1.test.step('Get dashboards without token (401)', async () => {
72
+ });
73
+ (0, _8_9_1.test)('Get dashboards without token returns 401', async ({ request }) => {
74
+ await _8_9_1.test.step('GET /api/public/dashboard without token (401)', async () => {
73
75
  const response = await request.get(`${baseUrl}/api/public/dashboard?collectionId=${collectionIdValue}`);
74
76
  (0, test_1.expect)(response.status()).toBe(401);
75
77
  });
76
- await _8_9_1.test.step('Get dashboards with invalid token (401)', async () => {
78
+ });
79
+ (0, _8_9_1.test)('Get dashboards with invalid token returns 401', async ({ request }) => {
80
+ await _8_9_1.test.step('GET /api/public/dashboard with invalid token (401)', async () => {
77
81
  const response = await request.get(`${baseUrl}/api/public/dashboard?collectionId=${collectionIdValue}`, { headers: { Authorization: 'Bearer invalid_token' } });
78
82
  (0, test_1.expect)(response.status()).toBe(401);
79
83
  });
84
+ });
85
+ (0, _8_9_1.test)('GET /api/public/dashboard/force-internal-error returns 500', async ({ request, }) => {
80
86
  await _8_9_1.test.step('Force internal server error (500)', async () => {
81
87
  const response = await request.get(`${baseUrl}/api/public/dashboard/force-internal-error`, { headers: { Authorization: optimizeBearerToken } });
82
88
  (0, test_1.expect)(response.status()).toBe(500);
83
89
  });
84
- await _8_9_1.test.step('Export dashboard definitions successfully (200)', async () => {
90
+ });
91
+ (0, _8_9_1.test)('Export dashboard definitions successfully returns 200', async ({ request, }) => {
92
+ await _8_9_1.test.step('POST export dashboard definitions (200)', async () => {
85
93
  const response = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
86
94
  headers: {
87
95
  Authorization: optimizeBearerToken,
@@ -93,14 +101,18 @@ _8_9_1.test.describe('API optimize SaaS Tests', () => {
93
101
  const body = await response.json();
94
102
  (0, test_1.expect)(Array.isArray(body)).toBeTruthy();
95
103
  });
96
- await _8_9_1.test.step('Export dashboards without token (401)', async () => {
104
+ });
105
+ (0, _8_9_1.test)('Export dashboards without token returns 401', async ({ request }) => {
106
+ await _8_9_1.test.step('POST export dashboards without token (401)', async () => {
97
107
  const response = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
98
108
  headers: { 'Content-Type': 'application/json' },
99
109
  data: [dashboardIdValue],
100
110
  });
101
111
  (0, test_1.expect)(response.status()).toBe(401);
102
112
  });
103
- await _8_9_1.test.step('Export dashboards with invalid token (401)', async () => {
113
+ });
114
+ (0, _8_9_1.test)('Export dashboards with invalid token returns 401', async ({ request, }) => {
115
+ await _8_9_1.test.step('POST export dashboards with invalid token (401)', async () => {
104
116
  const response = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
105
117
  headers: {
106
118
  Authorization: 'Bearer invalid_token',
@@ -110,7 +122,9 @@ _8_9_1.test.describe('API optimize SaaS Tests', () => {
110
122
  });
111
123
  (0, test_1.expect)(response.status()).toBe(401);
112
124
  });
113
- await _8_9_1.test.step('Export dashboard with invalid body (400)', async () => {
125
+ });
126
+ (0, _8_9_1.test)('Export dashboard with invalid body returns 400', async ({ request }) => {
127
+ await _8_9_1.test.step('POST export dashboard with invalid body (400)', async () => {
114
128
  const response = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
115
129
  headers: {
116
130
  Authorization: optimizeBearerToken,
@@ -120,7 +134,9 @@ _8_9_1.test.describe('API optimize SaaS Tests', () => {
120
134
  });
121
135
  (0, test_1.expect)(response.status()).toBe(400);
122
136
  });
123
- await _8_9_1.test.step('Export non-existent dashboard (404)', async () => {
137
+ });
138
+ (0, _8_9_1.test)('Export non-existent dashboard returns 404', async ({ request }) => {
139
+ await _8_9_1.test.step('POST export non-existent dashboard (404)', async () => {
124
140
  const fakeDashboardId = 'nonexistent-dashboard-id';
125
141
  const response = await request.post(`${baseUrl}/api/public/export/dashboard/definition/json`, {
126
142
  headers: {
@@ -131,45 +147,53 @@ _8_9_1.test.describe('API optimize SaaS Tests', () => {
131
147
  });
132
148
  (0, test_1.expect)([401, 404]).toContain(response.status());
133
149
  });
134
- await _8_9_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);
150
+ });
151
+ (0, _8_9_1.test)('Delete dashboard and verify it is no longer retrievable', async ({ request, }) => {
152
+ const dashboardToDeleteId = await (0, apiHelpers_1.createDashboard)(request, {
153
+ name: await (0, randomName_1.randomNameAgregator)('Dashboard To Delete'),
154
+ optimizeCookie,
155
+ collectionId: collectionIdValue,
143
156
  });
144
157
  await _8_9_1.test.step('Delete dashboard successfully (200)', async () => {
145
- const response = await request.delete(`${baseUrl}/api/public/dashboard/${dashboardIdValue}`, { headers: { Authorization: optimizeBearerToken } });
158
+ const response = await request.delete(`${baseUrl}/api/public/dashboard/${dashboardToDeleteId}`, { headers: { Authorization: optimizeBearerToken } });
146
159
  await (0, apiHelpers_1.assertResponseStatus)(response, 200);
147
160
  });
148
- await _8_9_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());
161
+ await _8_9_1.test.step('Verify dashboard not found after deletion', async () => {
162
+ await (0, test_1.expect)(async () => {
163
+ const response = await request.get(`${baseUrl}/api/public/dashboard/${dashboardToDeleteId}`, { headers: { Authorization: optimizeBearerToken } });
164
+ (0, test_1.expect)([404, 410, 500]).toContain(response.status());
165
+ }).toPass({ ...constants_1.defaultAssertionOptions, timeout: 60000 });
151
166
  });
152
- await _8_9_1.test.step('Delete dashboard with invalid token (401)', async () => {
167
+ });
168
+ (0, _8_9_1.test)('Delete dashboard with invalid token returns 401', async ({ request }) => {
169
+ await _8_9_1.test.step('DELETE /api/public/dashboard with invalid token (401)', async () => {
153
170
  const response = await request.delete(`${baseUrl}/api/public/dashboard/${dashboardIdValue}`, { headers: { Authorization: 'Bearer invalid_token' } });
154
171
  (0, test_1.expect)(response.status()).toBe(401);
155
172
  });
156
- await _8_9_1.test.step('Delete non-existent dashboard (404)', async () => {
173
+ });
174
+ (0, _8_9_1.test)('Delete non-existent dashboard returns 404', async ({ request }) => {
175
+ await _8_9_1.test.step('DELETE non-existent dashboard (404)', async () => {
157
176
  const fakeDashboardId = 'nonexistent-dashboard-id';
158
177
  const response = await request.delete(`${baseUrl}/api/public/dashboard/${fakeDashboardId}`, { headers: { Authorization: optimizeBearerToken } });
159
178
  (0, test_1.expect)(response.status()).toBe(404);
160
179
  });
161
180
  });
162
- // Skipped due to bug:41410: https://github.com/camunda/camunda/issues/41410
163
- _8_9_1.test.skip('Reports Scenarios Flow Tests', async ({ request }) => {
164
- await _8_9_1.test.step('Get reports successfully (200)', async () => {
181
+ (0, _8_9_1.test)('Get report IDs returns 200 with id array', async ({ request }) => {
182
+ await _8_9_1.test.step('GET /api/public/report?collectionId returns 200', async () => {
165
183
  const response = await request.get(`${baseUrl}/api/public/report?collectionId=${collectionIdValue}`, { headers: { Authorization: optimizeBearerToken } });
166
184
  await (0, apiHelpers_1.assertResponseStatus)(response, 200);
185
+ const body = await response.json();
186
+ (0, test_1.expect)(Array.isArray(body)).toBeTruthy();
167
187
  });
168
- await _8_9_1.test.step('Get reports with invalid/missing token (401)', async () => {
188
+ });
189
+ (0, _8_9_1.test)('Get reports with invalid token returns 401', async ({ request }) => {
190
+ await _8_9_1.test.step('GET /api/public/report with invalid token (401)', async () => {
169
191
  const response = await request.get(`${baseUrl}/api/public/report?collectionId=${collectionIdValue}`, { headers: { Authorization: 'Bearer invalid_token' } });
170
192
  (0, test_1.expect)(response.status()).toBe(401);
171
193
  });
172
- await _8_9_1.test.step('Export reports successfully (200)', async () => {
194
+ });
195
+ (0, _8_9_1.test)('Export reports successfully returns 200 with full payload', async ({ request, }) => {
196
+ await _8_9_1.test.step('POST export report definitions (200)', async () => {
173
197
  const response = await request.post(`${baseUrl}/api/public/export/report/definition/json`, {
174
198
  headers: {
175
199
  Authorization: optimizeBearerToken,
@@ -186,7 +210,9 @@ _8_9_1.test.describe('API optimize SaaS Tests', () => {
186
210
  (0, test_1.expect)(body[0]).toHaveProperty('name');
187
211
  (0, test_1.expect)(body[0]).toHaveProperty('collectionId');
188
212
  });
189
- await _8_9_1.test.step('Export reports with invalid token (401)', async () => {
213
+ });
214
+ (0, _8_9_1.test)('Export reports with invalid token returns 401', async ({ request }) => {
215
+ await _8_9_1.test.step('POST export reports with invalid token (401)', async () => {
190
216
  const response = await request.post(`${baseUrl}/api/public/export/report/definition/json`, {
191
217
  headers: {
192
218
  Authorization: 'Bearer invalid_token',
@@ -196,7 +222,9 @@ _8_9_1.test.describe('API optimize SaaS Tests', () => {
196
222
  });
197
223
  (0, test_1.expect)(response.status()).toBe(401);
198
224
  });
199
- await _8_9_1.test.step('Export non-existent report (404)', async () => {
225
+ });
226
+ (0, _8_9_1.test)('Export non-existent report returns 404', async ({ request }) => {
227
+ await _8_9_1.test.step('POST export non-existent report (404)', async () => {
200
228
  const invalidReportId = '11111111-1111-1111-1111-111111111111';
201
229
  const response = await request.post(`${baseUrl}/api/public/export/report/definition/json`, {
202
230
  headers: {
@@ -207,21 +235,32 @@ _8_9_1.test.describe('API optimize SaaS Tests', () => {
207
235
  });
208
236
  await (0, apiHelpers_1.assertResponseStatus)(response, 404);
209
237
  });
238
+ });
239
+ (0, _8_9_1.test)('Delete report and verify it is no longer retrievable', async ({ request, }) => {
240
+ const reportToDeleteId = await (0, apiHelpers_1.createSingleProcessReport)(request, {
241
+ optimizeCookie,
242
+ collectionId: collectionIdValue,
243
+ name: await (0, randomName_1.randomNameAgregator)('Report To Delete'),
244
+ definitions: [
245
+ {
246
+ key: 'aProcess',
247
+ filter: [],
248
+ groupBy: { type: 'flowNodes', value: null },
249
+ distributedBy: { type: 'none', value: null },
250
+ view: { entity: 'flowNode', properties: ['duration'] },
251
+ },
252
+ ],
253
+ });
210
254
  await _8_9_1.test.step('Delete report successfully (200)', async () => {
211
- const response = await request.delete(`${baseUrl}/api/public/report/${reportId}`, { headers: { Authorization: optimizeBearerToken } });
255
+ const response = await request.delete(`${baseUrl}/api/public/report/${reportToDeleteId}`, { headers: { Authorization: optimizeBearerToken } });
212
256
  await (0, apiHelpers_1.assertResponseStatus)(response, 200);
213
257
  });
214
- await _8_9_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 } });
258
+ await _8_9_1.test.step('Verify deleted report is no longer retrievable (404 or 500)', async () => {
259
+ const response = await request.get(`${baseUrl}/api/public/report/${reportToDeleteId}`, { headers: { Authorization: optimizeBearerToken } });
216
260
  (0, test_1.expect)([404, 500]).toContain(response.status());
217
261
  });
218
- await _8_9_1.test.step('Attempt delete report with invalid token (401)', async () => {
219
- const response = await request.delete(`${baseUrl}/api/public/report/${reportId}`, { headers: { Authorization: 'Bearer invalid_token' } });
220
- (0, test_1.expect)(response.status()).toBe(401);
221
- });
222
262
  });
223
- // Skipped due to bug:41410: https://github.com/camunda/camunda/issues/41410
224
- _8_9_1.test.skip('Import entities successfully (200)', async ({ request }) => {
263
+ (0, _8_9_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_9_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_9_1.test.skip('Import without token or invalid token (401)', async ({ request, }) => {
282
+ (0, _8_9_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_9_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_9_1.test.describe('API optimize SaaS Tests - Conditional Events', () => {
@@ -309,6 +363,11 @@ _8_9_1.test.describe('API optimize SaaS Tests - Conditional Events', () => {
309
363
  });
310
364
  baseUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
311
365
  });
366
+ _8_9_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_9_1.test)('CE-OPT-09: Creating a Process Report for Conditional Events via API returns HTTP 200', async ({ request, }) => {
313
372
  await _8_9_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`, {
@@ -435,3 +494,590 @@ _8_9_1.test.describe('API optimize SaaS Tests - Conditional Events', () => {
435
494
  });
436
495
  });
437
496
  });
497
+ _8_9_1.test.describe('API optimize SaaS Tests - Health Readiness', () => {
498
+ let baseUrl;
499
+ _8_9_1.test.beforeAll(async () => {
500
+ baseUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
501
+ });
502
+ (0, _8_9_1.test)('Health readiness returns 200 when Optimize is ready', async ({ request, }) => {
503
+ await _8_9_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_9_1.test)('Health readiness rejects requests that include an Authorization header', async ({ request, }) => {
509
+ await _8_9_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_9_1.test.describe('API optimize SaaS Tests - Sharing', () => {
519
+ let optimizeBearerToken;
520
+ let baseUrl;
521
+ _8_9_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_9_1.test)('Enable sharing with valid token returns 200 or 204', async ({ request, }) => {
526
+ await _8_9_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_9_1.test)('Enable sharing without token returns 401', async ({ request }) => {
532
+ await _8_9_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_9_1.test)('Enable sharing with invalid token returns 401', async ({ request }) => {
538
+ await _8_9_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_9_1.test)('Disable sharing with valid token returns 200 or 204', async ({ request, }) => {
544
+ await _8_9_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_9_1.test)('Disable sharing without token returns 401', async ({ request }) => {
550
+ await _8_9_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_9_1.test)('Disable sharing with invalid token returns 401', async ({ request }) => {
556
+ await _8_9_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_9_1.test)('Sharing toggle cycle: enable then disable is idempotent', async ({ request, }) => {
562
+ await _8_9_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_9_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_9_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_9_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_9_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_9_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_9_1.test)('GET dashboards with valid token and collectionId returns 200 with id array', async ({ request, }) => {
600
+ await _8_9_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_9_1.test)('GET dashboards without collectionId param returns 400 or 500', async ({ request, }) => {
608
+ await _8_9_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_9_1.test)('GET reports with valid token and collectionId returns 200 with id array', async ({ request, }) => {
616
+ await _8_9_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_9_1.test)('GET reports without collectionId param returns 400 or 500', async ({ request, }) => {
624
+ await _8_9_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_9_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_9_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_9_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_9_1.test)('Export multiple dashboards in a single call returns 200 with all definitions', async ({ request, }) => {
698
+ await _8_9_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_9_1.test)('Export multiple reports in a single call returns 200 with all definitions', async ({ request, }) => {
713
+ await _8_9_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_9_1.test)('Export reports with empty array body returns 200 with empty array', async ({ request, }) => {
728
+ await _8_9_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_9_1.test)('Export dashboards with empty array body returns 200 with empty array', async ({ request, }) => {
743
+ await _8_9_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_9_1.test.describe('API optimize SaaS Tests - Auth edge cases', () => {
759
+ let baseUrl;
760
+ _8_9_1.test.beforeAll(async () => {
761
+ baseUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
762
+ });
763
+ (0, _8_9_1.test)('Expired JWT token returns 401 on dashboard endpoint', async ({ request, }) => {
764
+ const expiredToken = 'Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.' +
765
+ 'eyJzdWIiOiJ0ZXN0IiwiZXhwIjoxfQ.' +
766
+ 'invalidsignature';
767
+ await _8_9_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_9_1.test)('Malformed Bearer value (empty string) returns 401 on dashboard endpoint', async ({ request, }) => {
773
+ await _8_9_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_9_1.test)('Token with wrong audience returns 401 on dashboard endpoint', async ({ request, }) => {
779
+ await _8_9_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_9_1.test.describe('API V2 tests on SaaS cluster', () => {
787
+ let bearerToken;
788
+ const apiUrl = () => process.env.ZEEBE_API_URL;
789
+ _8_9_1.test.beforeAll(async () => {
790
+ bearerToken = await (0, apiHelpers_1.authSaasAPI)();
791
+ });
792
+ (0, _8_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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_9_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
+ });
@@ -410,14 +410,48 @@ async function createStringClusterVariable(authToken, environment = 'saas', cust
410
410
  }
411
411
  exports.createStringClusterVariable = createStringClusterVariable;
412
412
  async function getOptimizeCoockie(page) {
413
- await (0, utils_1.loginToApp)(process.env.CAMUNDA_OPTIMIZE_BASE_URL, page);
413
+ const optimizeUrl = process.env.CAMUNDA_OPTIMIZE_BASE_URL;
414
+ const maxLoginAttempts = 3;
415
+ // Auth0 can fail or rate-limit concurrent logins. Retry the whole flow if
416
+ // loginToApp throws (e.g. waitForURL times out after a bad Auth0 redirect).
417
+ for (let loginAttempt = 0; loginAttempt < maxLoginAttempts; loginAttempt++) {
418
+ try {
419
+ await page.goto('about:blank');
420
+ await page.context().clearCookies();
421
+ await (0, utils_1.loginToApp)(optimizeUrl, page);
422
+ break;
423
+ }
424
+ catch (error) {
425
+ if (loginAttempt < maxLoginAttempts - 1) {
426
+ console.warn(`getOptimizeCoockie: login attempt ${loginAttempt + 1} failed, retrying after backoff...`, error);
427
+ await page.goto('about:blank');
428
+ await page.waitForTimeout(5000 * (loginAttempt + 1));
429
+ }
430
+ else {
431
+ throw error;
432
+ }
433
+ }
434
+ }
414
435
  const context = page.context();
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);
419
- const serviceTokenCookie = authData.cookies.find((cookie) => cookie.name === 'X-Optimize-Service-Token_0');
420
- const authTokenCookie = authData.cookies.find((cookie) => cookie.name === 'X-Optimize-Authorization_0');
436
+ // Optimize sets its session cookies asynchronously after the Auth0 redirect.
437
+ // Poll storage state until both cookies are present (up to ~30 s).
438
+ let serviceTokenCookie;
439
+ let authTokenCookie;
440
+ for (let i = 0; i < 10; i++) {
441
+ const tempFile = path_1.default.join(os_1.default.tmpdir(), `.auth_optimize_${Date.now()}_${Math.random().toString(36).slice(2)}`);
442
+ await context.storageState({ path: tempFile });
443
+ const authData = JSON.parse(fs_1.default.readFileSync(tempFile, 'utf8'));
444
+ fs_1.default.unlinkSync(tempFile);
445
+ serviceTokenCookie = authData.cookies.find((cookie) => cookie.name === 'X-Optimize-Service-Token_0');
446
+ authTokenCookie = authData.cookies.find((cookie) => cookie.name === 'X-Optimize-Authorization_0');
447
+ if (serviceTokenCookie?.value && authTokenCookie?.value) {
448
+ break;
449
+ }
450
+ if (i < 9) {
451
+ console.warn(`getOptimizeCoockie: cookies not ready yet (attempt ${i + 1}/10), retrying in 3 s...`);
452
+ await page.waitForTimeout(3000);
453
+ }
454
+ }
421
455
  const optimizeServiceToken = serviceTokenCookie?.value || '';
422
456
  const optimizeAuthToken = authTokenCookie?.value || '';
423
457
  const optimizeCookie = `X-Optimize-Service-Token_0=${optimizeServiceToken}; X-Optimize-Authorization_0=${optimizeAuthToken}`;
@@ -12,9 +12,9 @@ async function loginToApp(appName, page) {
12
12
  await (0, test_1.expect)(loginPage.usernameInput).toHaveValue(process.env.C8_USERNAME);
13
13
  await (0, test_1.expect)(loginPage.loginMessage).toBeVisible();
14
14
  await loginPage.clickContinueButton();
15
- await (0, test_1.expect)(loginPage.passwordInput).toBeVisible({ timeout: 60000 });
15
+ await (0, test_1.expect)(loginPage.passwordInput).toBeVisible({ timeout: 90000 });
16
16
  await loginPage.fillPassword(process.env.C8_PASSWORD);
17
- await (0, test_1.expect)(loginPage.passwordHeading).toBeVisible();
17
+ await (0, test_1.expect)(loginPage.passwordHeading).toBeVisible({ timeout: 90000 });
18
18
  await loginPage.clickLoginButton();
19
19
  await page.waitForURL((url) => url.toString().startsWith(appName), {
20
20
  timeout: 60000,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.572",
3
+ "version": "0.0.573",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",