@camunda/e2e-test-suite 0.0.674 → 0.0.675

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.
@@ -6,16 +6,14 @@ declare class IdentityHomePage {
6
6
  readonly operateCell: Locator;
7
7
  readonly connectorsCell: Locator;
8
8
  readonly accessToAPIsTab: Locator;
9
- readonly writePermissionCell: Locator;
10
- readonly readPermissionCell: Locator;
11
9
  readonly assignPermissionsButton: Locator;
12
10
  readonly dialog: Locator;
13
- readonly writePermissionCheckBox: Locator;
14
- readonly readPermissionCheckBox: Locator;
15
11
  readonly addButton: Locator;
16
12
  readonly permissionCell: (name: string) => Locator;
17
13
  readonly select: Locator;
18
14
  constructor(page: Page);
15
+ private selectAllCheckboxes;
16
+ private assignPermissions;
19
17
  assignPermissionToTasklistAPI(): Promise<void>;
20
18
  assignPermissionToOperateAPI(): Promise<void>;
21
19
  selectOptionFromDropdown(option: string): Promise<void>;
@@ -11,12 +11,8 @@ class IdentityHomePage {
11
11
  operateCell;
12
12
  connectorsCell;
13
13
  accessToAPIsTab;
14
- writePermissionCell;
15
- readPermissionCell;
16
14
  assignPermissionsButton;
17
15
  dialog;
18
- writePermissionCheckBox;
19
- readPermissionCheckBox;
20
16
  addButton;
21
17
  permissionCell;
22
18
  select;
@@ -26,18 +22,10 @@ class IdentityHomePage {
26
22
  name: 'Camunda logo Identity',
27
23
  });
28
24
  this.accessToAPIsTab = page.getByRole('tab', { name: 'Access to APIs' });
29
- this.writePermissionCell = page.getByRole('cell', { name: 'write:*' });
30
- this.readPermissionCell = page.getByRole('cell', { name: 'read:*' });
31
25
  this.assignPermissionsButton = page.getByRole('button', {
32
26
  name: 'Assign permissions',
33
27
  });
34
28
  this.dialog = page.getByRole('dialog');
35
- this.writePermissionCheckBox = page
36
- .locator('label')
37
- .filter({ hasText: 'write:*' });
38
- this.readPermissionCheckBox = page
39
- .locator('label')
40
- .filter({ hasText: 'read:*' });
41
29
  this.addButton = page.getByRole('button', { name: 'Add' });
42
30
  this.permissionCell = (name) => page.getByRole('cell', { name: name });
43
31
  this.tasklistCell = this.permissionCell('Tasklist');
@@ -45,86 +33,67 @@ class IdentityHomePage {
45
33
  this.connectorsCell = this.permissionCell('Connectors');
46
34
  this.select = this.page.locator('select');
47
35
  }
48
- async assignPermissionToTasklistAPI() {
49
- await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.tasklistCell);
50
- await this.tasklistCell.click();
51
- await this.accessToAPIsTab.click();
52
- await (0, sleep_1.sleep)(2000);
53
- if ((await this.writePermissionCell.isVisible()) &&
54
- (await this.readPermissionCell.isVisible())) {
55
- console.log('Permissions already assigned to Tasklist API');
36
+ async selectAllCheckboxes(apiName) {
37
+ const labels = this.dialog.locator('.cds--checkbox-label');
38
+ try {
39
+ await labels.first().waitFor({ state: 'visible', timeout: 30000 });
40
+ for (const label of await labels.all()) {
41
+ await label.click();
42
+ }
43
+ return true;
56
44
  }
57
- else {
58
- await this.assignPermissionsButton.click();
59
- await this.selectOptionFromDropdown('Tasklist API');
60
- await (0, test_1.expect)(this.dialog).toBeVisible();
61
- await this.writePermissionCheckBox.waitFor({ state: 'visible' });
62
- await this.writePermissionCheckBox.click();
63
- await this.readPermissionCheckBox.click();
64
- await this.addButton.click();
65
- await (0, test_1.expect)(this.permissionCell('write:*')).toBeVisible();
66
- await (0, test_1.expect)(this.permissionCell('read:*')).toBeVisible();
67
- await (0, sleep_1.sleep)(5000);
45
+ catch {
46
+ console.log(`No checkboxes found for ${apiName}, skipping Add`);
47
+ return false;
68
48
  }
69
49
  }
70
- async assignPermissionToOperateAPI() {
71
- // The Operate API now exposes its permission scopes as `read` / `write`
72
- // (plus `read:users`) rather than the wildcard `read:*` / `write:*` the
73
- // shared writePermissionCell/readPermissionCheckBox locators target — those
74
- // wildcards still exist for the Tasklist API, so they stay untouched here.
75
- // The dialog renders Carbon checkboxes labelled e.g. "write (Write
76
- // permission)", and the assigned-permissions table renders the raw scope
77
- // name (`read` / `write`) in a cell.
78
- const operateWritePermissionCell = this.page.getByRole('cell', {
79
- name: 'write',
80
- exact: true,
81
- });
82
- const operateReadPermissionCell = this.page.getByRole('cell', {
83
- name: 'read',
84
- exact: true,
85
- });
86
- const operateWriteCheckBox = this.page.getByRole('checkbox', {
87
- name: 'write (Write permission)',
88
- });
89
- const operateReadCheckBox = this.page.getByRole('checkbox', {
90
- name: 'read (Read permission)',
91
- exact: true,
92
- });
93
- await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.operateCell);
94
- await this.operateCell.click();
50
+ async assignPermissions(appCell, apiName) {
51
+ await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, appCell);
52
+ await appCell.click();
95
53
  await this.accessToAPIsTab.click();
96
54
  await (0, sleep_1.sleep)(2000);
97
- if ((await operateWritePermissionCell.isVisible()) &&
98
- (await operateReadPermissionCell.isVisible())) {
99
- console.log('Permissions already assigned to Operate API');
55
+ if (await this.page
56
+ .getByRole('cell')
57
+ .filter({ hasText: /^write/ })
58
+ .isVisible()) {
59
+ console.log(`Permissions already assigned to ${apiName}`);
60
+ return;
61
+ }
62
+ await this.assignPermissionsButton.click();
63
+ await this.selectOptionFromDropdown(apiName);
64
+ await (0, test_1.expect)(this.dialog).toBeVisible();
65
+ const checked = await this.selectAllCheckboxes(apiName);
66
+ if (!checked) {
67
+ return;
100
68
  }
101
- else {
102
- await this.assignPermissionsButton.click();
103
- await this.selectOptionFromDropdown('Operate API');
104
- await (0, test_1.expect)(this.dialog).toBeVisible();
105
- await operateWriteCheckBox.waitFor({ state: 'visible' });
106
- await operateWriteCheckBox.click();
107
- await operateReadCheckBox.click();
69
+ await (0, sleep_1.sleep)(1000);
70
+ // Retry clicking Add until the dialog closes — the button can be
71
+ // present but unresponsive if the form hasn't registered the
72
+ // checkbox state yet (seen in docker compose: dialog stays open).
73
+ for (let attempt = 1; attempt <= 3; attempt++) {
108
74
  await this.addButton.click();
109
- await (0, test_1.expect)(operateWritePermissionCell).toBeVisible();
110
- await (0, test_1.expect)(operateReadPermissionCell).toBeVisible();
111
- await (0, sleep_1.sleep)(5000);
75
+ try {
76
+ await this.dialog.waitFor({ state: 'hidden', timeout: 5000 });
77
+ console.log(`Dialog closed after Add click (attempt ${attempt})`);
78
+ break;
79
+ }
80
+ catch {
81
+ console.log(`Dialog still open after Add attempt ${attempt}, retrying...`);
82
+ if (attempt === 3) {
83
+ throw new Error(`Dialog did not close after ${attempt} Add attempts for ${apiName}`);
84
+ }
85
+ await (0, sleep_1.sleep)(1000);
86
+ }
112
87
  }
88
+ await (0, sleep_1.sleep)(3000);
89
+ }
90
+ async assignPermissionToTasklistAPI() {
91
+ await this.assignPermissions(this.tasklistCell, 'Tasklist API');
92
+ }
93
+ async assignPermissionToOperateAPI() {
94
+ await this.assignPermissions(this.operateCell, 'Operate API');
113
95
  }
114
96
  async selectOptionFromDropdown(option) {
115
- // The Identity "Assign permissions to application" dialog renders the
116
- // API list as a Carbon Select, which wraps a NATIVE <select> with native
117
- // <option class="cds--select-option"> children. Native <option> elements
118
- // are never "visible" to Playwright (the browser draws the option list as
119
- // an OS-level widget, not as visible DOM), so clicking one via
120
- // getByRole('option').click() always times out waiting for visibility —
121
- // observed in nightly run 26731057113, where the option resolved but the
122
- // click never succeeded ("element is not visible").
123
- //
124
- // Locator.selectOption() is the correct primitive for a native <select>:
125
- // it sets the value and dispatches the `input`/`change` events that
126
- // Carbon's onChange handler listens for, closing the picker and
127
- // re-rendering the form with the write/read permission rows.
128
97
  await this.select.selectOption({ label: option });
129
98
  await (0, sleep_1.sleep)(1000);
130
99
  }
@@ -34,7 +34,7 @@ class OperateProcessInstancePage {
34
34
  }
35
35
  async completedIconAssertion() {
36
36
  let retryCount = 0;
37
- const maxRetries = 3;
37
+ const maxRetries = 5;
38
38
  while (retryCount < maxRetries) {
39
39
  try {
40
40
  await (0, test_1.expect)(this.completedIcon).toBeVisible({
@@ -43,14 +43,14 @@ class OperateProcessInstancePage {
43
43
  return; // Exit the function if the expectation is met
44
44
  }
45
45
  catch (error) {
46
- // If the active icon isn't found, reload the page and try again
46
+ // If the completed icon isn't found, reload the page and try again
47
47
  retryCount++;
48
48
  console.log(`Attempt ${retryCount} failed. Retrying...`);
49
49
  await this.page.reload();
50
50
  await (0, sleep_1.sleep)(10000);
51
51
  }
52
52
  }
53
- throw new Error(`Active icon not visible after ${maxRetries} attempts.`);
53
+ throw new Error(`Completed icon not visible after ${maxRetries} attempts.`);
54
54
  }
55
55
  async activeIconAssertion() {
56
56
  let retryCount = 0;
@@ -42,66 +42,77 @@ if (process.env.RUN_AS_DOCKER_COMPOSE == 'true') {
42
42
  });
43
43
  (0, c8Run_8_7_1.test)('Search for process definitions', async ({ request }) => {
44
44
  await (0, test_1.expect)(async () => {
45
- const processDefinitionsList = await request.post('/v1/process-definitions/search', {
45
+ const response = await request.post('/v1/process-definitions/search', {
46
46
  headers: {
47
47
  Authorization: `Bearer ${operateToken}`,
48
48
  'Content-Type': 'application/json',
49
49
  },
50
50
  });
51
- await (0, apiHelpers_1.assertResponseStatus)(processDefinitionsList, 200);
51
+ await (0, apiHelpers_1.assertResponseStatus)(response, 200);
52
52
  }).toPass(constants_1.defaultAssertionOptions);
53
53
  });
54
54
  (0, c8Run_8_7_1.test)('Get a process definition via key', async ({ request }) => {
55
- const searchProcessDefinitions = await request.post('/v1/process-definitions/search', {
56
- headers: {
57
- Authorization: `Bearer ${operateToken}`,
58
- 'Content-Type': 'application/json',
59
- },
60
- });
61
- const processKey = await searchProcessDefinitions.json();
62
- const response = await request.get('/v1/process-definitions/' + processKey.items[0].key, {
63
- headers: {
64
- Authorization: `Bearer ${operateToken}`,
65
- 'Content-Type': 'application/json',
66
- },
67
- });
68
- await (0, apiHelpers_1.assertResponseStatus)(response, 200);
55
+ await (0, test_1.expect)(async () => {
56
+ const searchResponse = await request.post('/v1/process-definitions/search', {
57
+ headers: {
58
+ Authorization: `Bearer ${operateToken}`,
59
+ 'Content-Type': 'application/json',
60
+ },
61
+ });
62
+ await (0, apiHelpers_1.assertResponseStatus)(searchResponse, 200);
63
+ const processKey = await searchResponse.json();
64
+ const response = await request.get('/v1/process-definitions/' + processKey.items[0].key, {
65
+ headers: {
66
+ Authorization: `Bearer ${operateToken}`,
67
+ 'Content-Type': 'application/json',
68
+ },
69
+ });
70
+ await (0, apiHelpers_1.assertResponseStatus)(response, 200);
71
+ }).toPass(constants_1.defaultAssertionOptions);
69
72
  });
70
73
  (0, c8Run_8_7_1.test)('Search for process instances', async ({ request }) => {
71
- const processInstancesList = await request.post('/v1/process-instances/search', {
72
- headers: {
73
- Authorization: `Bearer ${operateToken}`,
74
- 'Content-Type': 'application/json',
75
- },
76
- });
77
- await (0, apiHelpers_1.assertResponseStatus)(processInstancesList, 200);
74
+ await (0, test_1.expect)(async () => {
75
+ const response = await request.post('/v1/process-instances/search', {
76
+ headers: {
77
+ Authorization: `Bearer ${operateToken}`,
78
+ 'Content-Type': 'application/json',
79
+ },
80
+ });
81
+ await (0, apiHelpers_1.assertResponseStatus)(response, 200);
82
+ }).toPass(constants_1.defaultAssertionOptions);
78
83
  });
79
84
  (0, c8Run_8_7_1.test)('Search for flownode-instances', async ({ request }) => {
80
- const flowNodeInstancesList = await request.post('/v1/flownode-instances/search', {
81
- headers: {
82
- Authorization: `Bearer ${operateToken}`,
83
- 'Content-Type': 'application/json',
84
- },
85
- });
86
- await (0, apiHelpers_1.assertResponseStatus)(flowNodeInstancesList, 200);
85
+ await (0, test_1.expect)(async () => {
86
+ const response = await request.post('/v1/flownode-instances/search', {
87
+ headers: {
88
+ Authorization: `Bearer ${operateToken}`,
89
+ 'Content-Type': 'application/json',
90
+ },
91
+ });
92
+ await (0, apiHelpers_1.assertResponseStatus)(response, 200);
93
+ }).toPass(constants_1.defaultAssertionOptions);
87
94
  });
88
95
  (0, c8Run_8_7_1.test)('Search for variables for process instancess', async ({ request }) => {
89
- const variablesInstancesList = await request.post('/v1/variables/search', {
90
- headers: {
91
- Authorization: `Bearer ${operateToken}`,
92
- 'Content-Type': 'application/json',
93
- },
94
- });
95
- await (0, apiHelpers_1.assertResponseStatus)(variablesInstancesList, 200);
96
+ await (0, test_1.expect)(async () => {
97
+ const response = await request.post('/v1/variables/search', {
98
+ headers: {
99
+ Authorization: `Bearer ${operateToken}`,
100
+ 'Content-Type': 'application/json',
101
+ },
102
+ });
103
+ await (0, apiHelpers_1.assertResponseStatus)(response, 200);
104
+ }).toPass(constants_1.defaultAssertionOptions);
96
105
  });
97
106
  (0, c8Run_8_7_1.test)('Search for incidents', async ({ request }) => {
98
- const incidentsList = await request.post('/v1/incidents/search', {
99
- headers: {
100
- Authorization: `Bearer ${operateToken}`,
101
- 'Content-Type': 'application/json',
102
- },
103
- });
104
- await (0, apiHelpers_1.assertResponseStatus)(incidentsList, 200);
107
+ await (0, test_1.expect)(async () => {
108
+ const response = await request.post('/v1/incidents/search', {
109
+ headers: {
110
+ Authorization: `Bearer ${operateToken}`,
111
+ 'Content-Type': 'application/json',
112
+ },
113
+ });
114
+ await (0, apiHelpers_1.assertResponseStatus)(response, 200);
115
+ }).toPass(constants_1.defaultAssertionOptions);
105
116
  });
106
117
  });
107
118
  }
@@ -41,13 +41,13 @@ if (process.env.RUN_AS_DOCKER_COMPOSE == 'true') {
41
41
  });
42
42
  (0, c8Run_8_7_1.test)('Search for tasks', async ({ request }) => {
43
43
  await (0, test_1.expect)(async () => {
44
- const taskList = await request.post(tasklistBaseURL + '/v1/tasks/search', {
44
+ const response = await request.post(tasklistBaseURL + '/v1/tasks/search', {
45
45
  headers: {
46
46
  Authorization: `Bearer ${tasklistToken}`,
47
47
  'Content-Type': 'application/json',
48
48
  },
49
49
  });
50
- (0, test_1.expect)(taskList.status()).toBe(200);
50
+ (0, test_1.expect)(response.status()).toBe(200);
51
51
  }).toPass(constants_1.defaultAssertionOptions);
52
52
  });
53
53
  (0, c8Run_8_7_1.test)('Get a task via ID', async ({ request }) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.674",
3
+ "version": "0.0.675",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",