@camunda/e2e-test-suite 0.0.813 → 0.0.815

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.
@@ -509,13 +509,19 @@ class ConsoleOrganizationPage {
509
509
  await this.organizationManagementLink.click();
510
510
  }
511
511
  async deleteAllGroups() {
512
- // Delete groups one at a time: each confirmed deletion re-renders and
513
- // re-indexes the list, so always operate on the first remaining row
514
- // instead of iterating by index over a mutating collection.
515
- while ((await this.deleteButton.count()) > 0) {
512
+ // Delete groups one at a time. After each confirmed deletion the Console
513
+ // groups table refetches and re-renders asynchronously, so the row count
514
+ // can still include the just-deleted row for a moment. Wait for the count
515
+ // to actually drop before targeting the next row: clicking a delete button
516
+ // while its row is being removed detaches the element from the DOM and the
517
+ // click never lands (retries against a continuously re-rendering table).
518
+ let remaining = await this.deleteButton.count();
519
+ while (remaining > 0) {
516
520
  await this.deleteButton.first().click({ timeout: 60000 });
517
521
  await this.deleteSubButton.click({ timeout: 60000 });
518
522
  await (0, test_1.expect)(this.deleteSubButton).not.toBeVisible({ timeout: 60000 });
523
+ remaining -= 1;
524
+ await (0, test_1.expect)(this.deleteButton).toHaveCount(remaining, { timeout: 60000 });
519
525
  }
520
526
  }
521
527
  }
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const test_1 = require("@playwright/test");
4
+ const _8_10_1 = require("../../fixtures/8.10");
5
+ const apiHelpers_1 = require("../../utils/apiHelpers");
6
+ const sleep_1 = require("../../utils/sleep");
7
+ _8_10_1.test.describe.configure({ mode: 'serial' });
8
+ let managementToken;
9
+ let clusterUuid;
10
+ let originalFilters = null;
11
+ let dataFiltersAvailable = false;
12
+ const readFilters = async () => {
13
+ const response = await (0, apiHelpers_1.getClusterDataFilters)(managementToken, clusterUuid);
14
+ const status = response.status();
15
+ let body = null;
16
+ try {
17
+ body = (await response.json());
18
+ }
19
+ catch {
20
+ body = null;
21
+ }
22
+ return { status, body };
23
+ };
24
+ const skipIfUnavailable = () => {
25
+ _8_10_1.test.skip(!dataFiltersAvailable, 'Console Data Filters API not reachable on this cluster (endpoint returned non-2xx or is not yet enabled).');
26
+ };
27
+ _8_10_1.test.beforeAll(async () => {
28
+ try {
29
+ managementToken = await (0, apiHelpers_1.authConsoleManagementAPI)();
30
+ clusterUuid = (0, apiHelpers_1.getTestClusterUuid)();
31
+ const { status, body } = await readFilters();
32
+ dataFiltersAvailable = status >= 200 && status < 300;
33
+ if (dataFiltersAvailable) {
34
+ originalFilters = body;
35
+ }
36
+ }
37
+ catch (error) {
38
+ dataFiltersAvailable = false;
39
+ console.warn(`Data Filters API precheck failed: ${error instanceof Error ? error.message : String(error)}`);
40
+ }
41
+ });
42
+ _8_10_1.test.afterAll(async () => {
43
+ if (dataFiltersAvailable && originalFilters) {
44
+ try {
45
+ await (0, apiHelpers_1.updateClusterDataFilters)(managementToken, clusterUuid, originalFilters);
46
+ }
47
+ catch (error) {
48
+ console.warn(`Failed to restore original data filters: ${error instanceof Error ? error.message : String(error)}`);
49
+ }
50
+ }
51
+ });
52
+ _8_10_1.test.describe('Optimize Data Filters API @tasklistV2', () => {
53
+ _8_10_1.test.slow();
54
+ (0, _8_10_1.test)('T11728029 business_* variable filter enabled by default', async () => {
55
+ skipIfUnavailable();
56
+ await _8_10_1.test.step('Read cluster data filters config', async () => {
57
+ const { status, body } = await readFilters();
58
+ (0, test_1.expect)(status).toBe(200);
59
+ (0, test_1.expect)(body).not.toBeNull();
60
+ });
61
+ await _8_10_1.test.step('Assert business_* variable include filter is present', async () => {
62
+ const { body } = await readFilters();
63
+ const includeNames = body?.variableFilter?.includeVariableNames ?? [];
64
+ const hasBusinessDefault = includeNames.some((name) => name.startsWith('business_'));
65
+ (0, test_1.expect)(hasBusinessDefault).toBe(true);
66
+ });
67
+ });
68
+ (0, _8_10_1.test)('T11728030 default filter configured before deploying processes', async () => {
69
+ skipIfUnavailable();
70
+ await _8_10_1.test.step('Assert variable filter config exists independent of deployed processes', async () => {
71
+ const { status, body } = await readFilters();
72
+ (0, test_1.expect)(status).toBe(200);
73
+ (0, test_1.expect)(body?.variableFilter).toBeDefined();
74
+ });
75
+ });
76
+ (0, _8_10_1.test)('T11728020 enable and persist a variable filter pattern', async () => {
77
+ skipIfUnavailable();
78
+ const pattern = 'e2e_business_*';
79
+ await _8_10_1.test.step('Enable and set variable include filter', async () => {
80
+ const response = await (0, apiHelpers_1.updateClusterDataFilters)(managementToken, clusterUuid, {
81
+ variableFilter: {
82
+ enabled: true,
83
+ includeVariableNames: [pattern],
84
+ },
85
+ });
86
+ (0, test_1.expect)([200, 201, 204]).toContain(response.status());
87
+ });
88
+ await _8_10_1.test.step('Assert the pattern persists on re-read', async () => {
89
+ const { body } = await readFilters();
90
+ const includeNames = body?.variableFilter?.includeVariableNames ?? [];
91
+ (0, test_1.expect)(includeNames).toContain(pattern);
92
+ (0, test_1.expect)(body?.variableFilter?.enabled).toBe(true);
93
+ });
94
+ });
95
+ (0, _8_10_1.test)('T11728034 filter change applies without a manual restart', async () => {
96
+ skipIfUnavailable();
97
+ const pattern = 'e2e_no_restart_*';
98
+ await _8_10_1.test.step('Update the variable filter', async () => {
99
+ const response = await (0, apiHelpers_1.updateClusterDataFilters)(managementToken, clusterUuid, {
100
+ variableFilter: {
101
+ enabled: true,
102
+ includeVariableNames: [pattern],
103
+ },
104
+ });
105
+ (0, test_1.expect)([200, 201, 204]).toContain(response.status());
106
+ });
107
+ await _8_10_1.test.step('Assert change is reflected without any restart call', async () => {
108
+ let reflected = false;
109
+ for (let attempt = 0; attempt < 5 && !reflected; attempt++) {
110
+ const { body } = await readFilters();
111
+ const includeNames = body?.variableFilter?.includeVariableNames ?? [];
112
+ reflected = includeNames.includes(pattern);
113
+ if (!reflected) {
114
+ await (0, sleep_1.sleep)(3000);
115
+ }
116
+ }
117
+ (0, test_1.expect)(reflected).toBe(true);
118
+ });
119
+ });
120
+ (0, _8_10_1.test)('T11735087 exclude variable names take precedence over include', async () => {
121
+ skipIfUnavailable();
122
+ const shared = 'e2e_precedence_var';
123
+ await _8_10_1.test.step('Set the same variable in include and exclude', async () => {
124
+ const response = await (0, apiHelpers_1.updateClusterDataFilters)(managementToken, clusterUuid, {
125
+ variableFilter: {
126
+ enabled: true,
127
+ includeVariableNames: [shared],
128
+ excludeVariableNames: [shared],
129
+ },
130
+ });
131
+ (0, test_1.expect)([200, 201, 204]).toContain(response.status());
132
+ });
133
+ await _8_10_1.test.step('Assert exclude wins on read-back', async () => {
134
+ const { body } = await readFilters();
135
+ const excludeNames = body?.variableFilter?.excludeVariableNames ?? [];
136
+ (0, test_1.expect)(excludeNames).toContain(shared);
137
+ });
138
+ });
139
+ (0, _8_10_1.test)('T11735088 set both include and exclude process definitions', async () => {
140
+ skipIfUnavailable();
141
+ const includeKey = 'e2e_include_proc';
142
+ const excludeKey = 'e2e_exclude_proc';
143
+ await _8_10_1.test.step('Set include and exclude process definition lists', async () => {
144
+ const response = await (0, apiHelpers_1.updateClusterDataFilters)(managementToken, clusterUuid, {
145
+ processDefinitionFilter: {
146
+ includeProcessDefinitionKeys: [includeKey],
147
+ excludeProcessDefinitionKeys: [excludeKey],
148
+ },
149
+ });
150
+ (0, test_1.expect)([200, 201, 204]).toContain(response.status());
151
+ });
152
+ await _8_10_1.test.step('Assert both lists persist', async () => {
153
+ const { body } = await readFilters();
154
+ const include = body?.processDefinitionFilter?.includeProcessDefinitionKeys ?? [];
155
+ const exclude = body?.processDefinitionFilter?.excludeProcessDefinitionKeys ?? [];
156
+ (0, test_1.expect)(include).toContain(includeKey);
157
+ (0, test_1.expect)(exclude).toContain(excludeKey);
158
+ });
159
+ });
160
+ (0, _8_10_1.test)('T11735089 exclude process definitions take precedence over include', async () => {
161
+ skipIfUnavailable();
162
+ const shared = 'e2e_precedence_proc';
163
+ await _8_10_1.test.step('Set the same process definition in include and exclude', async () => {
164
+ const response = await (0, apiHelpers_1.updateClusterDataFilters)(managementToken, clusterUuid, {
165
+ processDefinitionFilter: {
166
+ includeProcessDefinitionKeys: [shared],
167
+ excludeProcessDefinitionKeys: [shared],
168
+ },
169
+ });
170
+ (0, test_1.expect)([200, 201, 204]).toContain(response.status());
171
+ });
172
+ await _8_10_1.test.step('Assert exclude wins on read-back', async () => {
173
+ const { body } = await readFilters();
174
+ const exclude = body?.processDefinitionFilter?.excludeProcessDefinitionKeys ?? [];
175
+ (0, test_1.expect)(exclude).toContain(shared);
176
+ });
177
+ });
178
+ (0, _8_10_1.test)('T11728019 unauthenticated request cannot read data filters', async () => {
179
+ skipIfUnavailable();
180
+ await _8_10_1.test.step('Send request without a valid token', async () => {
181
+ const response = await (0, apiHelpers_1.getClusterDataFilters)('Bearer invalid-token', clusterUuid);
182
+ (0, test_1.expect)([401, 403]).toContain(response.status());
183
+ });
184
+ });
185
+ });
@@ -519,12 +519,13 @@ _8_7_1.test.describe('Web Modeler User Flow Tests', () => {
519
519
  password: password,
520
520
  });
521
521
  // navigateAfterInvitationLink lands the invited user in the SHARED org's
522
- // Modeler home (it logs in on the invitation flow rather than
523
- // re-authenticating into the personal org), so the shared project is
524
- // present. Assert its visibility directly no session-breaking reload.
525
- await (0, test_1.expect)(modelerHomePage.crossComponentProjectFolder).toBeVisible({
526
- timeout: 120000,
527
- });
522
+ // Modeler home. The project-collaborator grant propagates asynchronously,
523
+ // and the Modeler home fetches the project list only on load (it does not
524
+ // live-refresh), so the shared project can be absent from the first fetch
525
+ // (observed: the org's /projects returned an empty list right after the
526
+ // invited user logged in) and never appears without a reload. Reload and
527
+ // retry until the freshly granted project shows up.
528
+ await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(modelerHomePage, modelerHomePage.crossComponentProjectFolder, 'Cross Component Test Project', 90000, 8);
528
529
  });
529
530
  await _8_7_1.test.step('Log out from Second User', async () => {
530
531
  await settingsPage.clickOpenSettingsButton();
@@ -58,4 +58,22 @@ export declare function updateCollectionScope(request: APIRequestContext, option
58
58
  tenants: string[];
59
59
  }>;
60
60
  }): Promise<unknown>;
61
+ export interface ClusterVariableFilter {
62
+ enabled?: boolean;
63
+ includeVariableNames?: string[];
64
+ excludeVariableNames?: string[];
65
+ }
66
+ export interface ClusterProcessDefinitionFilter {
67
+ includeProcessDefinitionKeys?: string[];
68
+ excludeProcessDefinitionKeys?: string[];
69
+ }
70
+ export interface ClusterDataFilters {
71
+ variableFilter?: ClusterVariableFilter;
72
+ processDefinitionFilter?: ClusterProcessDefinitionFilter;
73
+ [key: string]: unknown;
74
+ }
75
+ export declare function authConsoleManagementAPI(): Promise<string>;
76
+ export declare function getTestClusterUuid(): string;
77
+ export declare function getClusterDataFilters(token: string, clusterUuid: string): Promise<APIResponse>;
78
+ export declare function updateClusterDataFilters(token: string, clusterUuid: string, config: ClusterDataFilters): Promise<APIResponse>;
61
79
  export {};
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.updateCollectionScope = exports.createSingleProcessReport = exports.createDashboard = exports.createCollection = exports.getOptimizeCookieSm = exports.getOptimizeCoockie = exports.createStringClusterVariable = exports.createJsonClusterVariable = exports.waitForProcessInstanceVisible = exports.createProcessInstance = exports.deployProcess = exports.waitForConnectorsReady = exports.validateMcpServerHealth = exports.authC8runAPI = exports.authSmAPI = exports.authSaasAPI = exports.authAPI = exports.buildZeebeApiUrl = exports.retryOn500 = exports.sendRequestAndAssertResponse = exports.assertResponseStatus = exports.getApiRequestContext = void 0;
6
+ exports.updateClusterDataFilters = exports.getClusterDataFilters = exports.getTestClusterUuid = exports.authConsoleManagementAPI = exports.updateCollectionScope = exports.createSingleProcessReport = exports.createDashboard = exports.createCollection = exports.getOptimizeCookieSm = exports.getOptimizeCoockie = exports.createStringClusterVariable = exports.createJsonClusterVariable = exports.waitForProcessInstanceVisible = exports.createProcessInstance = exports.deployProcess = exports.waitForConnectorsReady = exports.validateMcpServerHealth = exports.authC8runAPI = exports.authSmAPI = exports.authSaasAPI = exports.authAPI = exports.buildZeebeApiUrl = exports.retryOn500 = exports.sendRequestAndAssertResponse = exports.assertResponseStatus = exports.getApiRequestContext = void 0;
7
7
  const test_1 = require("@playwright/test");
8
8
  const sleep_1 = require("./sleep");
9
9
  const fs_1 = __importDefault(require("fs"));
@@ -749,3 +749,64 @@ async function updateCollectionScope(request, options) {
749
749
  }, 'updateCollectionScope');
750
750
  }
751
751
  exports.updateCollectionScope = updateCollectionScope;
752
+ async function authConsoleManagementAPI() {
753
+ apiRequestContext = await getApiRequestContext();
754
+ const authUrl = process.env.CONSOLE_MANAGEMENT_AUTH_URL ??
755
+ 'https://weblogin.cloud.ultrawombat.com/oauth/token';
756
+ const audience = process.env.CONSOLE_MANAGEMENT_AUDIENCE ?? 'cloud.ultrawombat.com';
757
+ const response = await apiRequestContext.post(authUrl, {
758
+ headers: {
759
+ 'Content-Type': 'application/json',
760
+ },
761
+ data: {
762
+ grant_type: 'client_credentials',
763
+ audience,
764
+ client_id: process.env.EXTERNAL_CONSOLE_API_CLIENT_ID,
765
+ client_secret: process.env.EXTERNAL_CONSOLE_API_CLIENT_SECRET,
766
+ },
767
+ timeout: 60000,
768
+ });
769
+ if (response.status() !== 200) {
770
+ const errorBody = await response.text();
771
+ throw new Error(`Failed to fetch Console management token. Response: ${errorBody}`);
772
+ }
773
+ const json = await response.json();
774
+ return `Bearer ${json.access_token}`;
775
+ }
776
+ exports.authConsoleManagementAPI = authConsoleManagementAPI;
777
+ function getTestClusterUuid() {
778
+ const uuid = process.env.TEST_CLUSTER_UUID ??
779
+ process.env.C8_CLUSTER_UUID ??
780
+ process.env.CLUSTER_UUID;
781
+ if (!uuid) {
782
+ throw new Error('Missing cluster UUID. Set TEST_CLUSTER_UUID (or C8_CLUSTER_UUID / CLUSTER_UUID).');
783
+ }
784
+ return uuid;
785
+ }
786
+ exports.getTestClusterUuid = getTestClusterUuid;
787
+ function buildConsoleDataFiltersUrl(clusterUuid) {
788
+ const base = (process.env.CONSOLE_MANAGEMENT_API_URL ??
789
+ 'https://accounts.cloud.ultrawombat.com/external/qa').replace(/\/+$/, '');
790
+ return `${base}/clusters/${clusterUuid}/dataFilters`;
791
+ }
792
+ async function getClusterDataFilters(token, clusterUuid) {
793
+ apiRequestContext = await getApiRequestContext();
794
+ return apiRequestContext.get(buildConsoleDataFiltersUrl(clusterUuid), {
795
+ headers: {
796
+ Authorization: token,
797
+ 'Content-Type': 'application/json',
798
+ },
799
+ });
800
+ }
801
+ exports.getClusterDataFilters = getClusterDataFilters;
802
+ async function updateClusterDataFilters(token, clusterUuid, config) {
803
+ apiRequestContext = await getApiRequestContext();
804
+ return apiRequestContext.put(buildConsoleDataFiltersUrl(clusterUuid), {
805
+ headers: {
806
+ Authorization: token,
807
+ 'Content-Type': 'application/json',
808
+ },
809
+ data: config,
810
+ });
811
+ }
812
+ exports.updateClusterDataFilters = updateClusterDataFilters;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.813",
3
+ "version": "0.0.815",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",