@camunda/e2e-test-suite 0.0.483 → 0.0.485

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.
@@ -1,4 +1,5 @@
1
1
  import { Page, Locator } from '@playwright/test';
2
+ import { McpCredentials } from '../../utils/mcpSecrets';
2
3
  import { ClientCredentialsDetailsPage } from './ClientCredentialsDetailsPage';
3
4
  declare class ClusterDetailsPage {
4
5
  private page;
@@ -41,6 +42,9 @@ declare class ClusterDetailsPage {
41
42
  readonly reviewUpdateButton: Locator;
42
43
  readonly updateAvailableDialog: Locator;
43
44
  readonly updateButton: Locator;
45
+ readonly mcpSupportToggle: Locator;
46
+ readonly mcpTab: Locator;
47
+ readonly mcpTextarea: Locator;
44
48
  constructor(page: Page);
45
49
  clickAPITab(): Promise<void>;
46
50
  clickCreateClientButton(): Promise<void>;
@@ -67,7 +71,7 @@ declare class ClusterDetailsPage {
67
71
  deleteAlerts(): Promise<void>;
68
72
  private doDelete;
69
73
  assertComponentsHealth(components?: string[]): Promise<void>;
70
- createAPIClientAndReturnVariables(name: string, setEnvVariables?: boolean): Promise<{
74
+ createAPIClientAndReturnVariables(name: string, setEnvVariables?: boolean, clusterType?: string): Promise<{
71
75
  [key: string]: string;
72
76
  }>;
73
77
  createAPIClient(name: string): Promise<void>;
@@ -82,5 +86,9 @@ declare class ClusterDetailsPage {
82
86
  checkOrchestrationClusterCheckbox(): Promise<void>;
83
87
  clickReviewUpdateButton(): Promise<void>;
84
88
  performClusterUpdate(): Promise<void>;
89
+ checkMcpSupportToggle(): Promise<void>;
90
+ clickMCPTab(): Promise<void>;
91
+ getMcpConfigurationText(): Promise<string>;
92
+ createMCPClientAndReturnVariables(name: string, setEnvVariables?: boolean): Promise<McpCredentials>;
85
93
  }
86
94
  export { ClusterDetailsPage };
@@ -47,6 +47,9 @@ class ClusterDetailsPage {
47
47
  reviewUpdateButton;
48
48
  updateAvailableDialog;
49
49
  updateButton;
50
+ mcpSupportToggle;
51
+ mcpTab;
52
+ mcpTextarea;
50
53
  constructor(page) {
51
54
  this.page = page;
52
55
  this.apiTab = page.getByRole('tab', { name: 'API' });
@@ -145,6 +148,11 @@ class ClusterDetailsPage {
145
148
  this.updateButton = this.updateAvailableDialog.getByRole('button', {
146
149
  name: 'Update',
147
150
  });
151
+ this.mcpSupportToggle = page.getByRole('switch', {
152
+ name: /Enable MCP Support/i,
153
+ });
154
+ this.mcpTab = page.getByRole('tab', { name: 'MCP' });
155
+ this.mcpTextarea = page.getByRole('textbox', { name: 'Copy to clipboard' });
148
156
  }
149
157
  async clickAPITab() {
150
158
  await (0, test_1.expect)(this.apiTab).toBeVisible({ timeout: 60000 });
@@ -372,7 +380,7 @@ class ClusterDetailsPage {
372
380
  });
373
381
  }
374
382
  }
375
- async createAPIClientAndReturnVariables(name, setEnvVariables = true) {
383
+ async createAPIClientAndReturnVariables(name, setEnvVariables = true, clusterType) {
376
384
  await this.createAPIClient(name);
377
385
  await this.clickEnvVarsButton();
378
386
  const variables = {
@@ -384,13 +392,29 @@ class ClusterDetailsPage {
384
392
  optimizeBaseUrl: await this.clientCredentialsText(/export CAMUNDA_OPTIMIZE_BASE_URL='([^']+)'/),
385
393
  };
386
394
  if (setEnvVariables) {
387
- process.env.ZEEBE_API_CLIENT_ID = variables.zeebeClientId;
388
- process.env.ZEEBE_API_CLIENT_SECRET = variables.zeebeClientSecret;
389
- process.env.ZEEBE_API_AUTH_URL = variables.zeebeAuthUrl;
390
- process.env.ZEEBE_API_URL = variables.zeebeUrl;
391
- process.env.ZEEBE_API_TOKEN_AUDIENCE = variables.tokenAudience;
392
- process.env.CAMUNDA_OPTIMIZE_BASE_URL = variables.optimizeBaseUrl;
393
- process.env.OPTIMIZE_API_TOKEN_AUDIENCE = 'optimize.ultrawombat.com';
395
+ if (clusterType) {
396
+ // Use cluster-specific suffixed env vars
397
+ const suffix = clusterType.toUpperCase().replace(/[\s-]/g, '_');
398
+ process.env[`ZEEBE_API_CLIENT_ID_${suffix}`] = variables.zeebeClientId;
399
+ process.env[`ZEEBE_API_CLIENT_SECRET_${suffix}`] =
400
+ variables.zeebeClientSecret;
401
+ process.env[`ZEEBE_API_AUTH_URL_${suffix}`] = variables.zeebeAuthUrl;
402
+ process.env[`ZEEBE_API_URL_${suffix}`] = variables.zeebeUrl;
403
+ process.env[`ZEEBE_API_TOKEN_AUDIENCE_${suffix}`] =
404
+ variables.tokenAudience;
405
+ process.env[`CAMUNDA_OPTIMIZE_BASE_URL_${suffix}`] =
406
+ variables.optimizeBaseUrl;
407
+ console.log(`[ClusterDetailsPage] Set credentials for cluster type: ${clusterType}`);
408
+ }
409
+ else {
410
+ process.env.ZEEBE_API_CLIENT_ID = variables.zeebeClientId;
411
+ process.env.ZEEBE_API_CLIENT_SECRET = variables.zeebeClientSecret;
412
+ process.env.ZEEBE_API_AUTH_URL = variables.zeebeAuthUrl;
413
+ process.env.ZEEBE_API_URL = variables.zeebeUrl;
414
+ process.env.ZEEBE_API_TOKEN_AUDIENCE = variables.tokenAudience;
415
+ process.env.CAMUNDA_OPTIMIZE_BASE_URL = variables.optimizeBaseUrl;
416
+ process.env.OPTIMIZE_API_TOKEN_AUDIENCE = 'optimize.ultrawombat.com';
417
+ }
394
418
  }
395
419
  return variables;
396
420
  }
@@ -500,5 +524,45 @@ class ClusterDetailsPage {
500
524
  await (0, test_1.expect)(this.updateAvailableDialog).toBeVisible({ timeout: 30000 });
501
525
  await this.updateButton.click({ timeout: 60000 });
502
526
  }
527
+ async checkMcpSupportToggle() {
528
+ if (!(await this.mcpSupportToggle.isVisible())) {
529
+ throw new Error('MCP Support toggle is not visible');
530
+ }
531
+ // Only click if not already enabled to avoid disabling on reruns
532
+ if (!(await this.mcpSupportToggle.isChecked())) {
533
+ await this.mcpSupportToggle.click({ force: true });
534
+ }
535
+ await (0, test_1.expect)(this.mcpSupportToggle).toBeChecked();
536
+ }
537
+ async clickMCPTab() {
538
+ await (0, test_1.expect)(this.mcpTab).toBeVisible({ timeout: 60000 });
539
+ await this.mcpTab.click();
540
+ }
541
+ async getMcpConfigurationText() {
542
+ await (0, test_1.expect)(this.mcpTextarea).toBeVisible({ timeout: 60000 });
543
+ return await this.mcpTextarea.innerText();
544
+ }
545
+ async createMCPClientAndReturnVariables(name, setEnvVariables = true) {
546
+ await this.createAPIClient(name);
547
+ await this.clickMCPTab();
548
+ const mcpConfigText = await this.getMcpConfigurationText();
549
+ const mcpConfig = JSON.parse(mcpConfigText);
550
+ const env = Object.values(mcpConfig.servers)[0].env;
551
+ const credentials = {
552
+ baseURL: env.CAMUNDA_BASE_URL,
553
+ clientID: env.CAMUNDA_CLIENT_ID,
554
+ clientSecret: env.CAMUNDA_CLIENT_SECRET,
555
+ OauthURL: env.CAMUNDA_OAUTH_URL,
556
+ tokenAudience: env.CAMUNDA_TOKEN_AUDIENCE,
557
+ };
558
+ if (setEnvVariables) {
559
+ process.env.MCP_BASE_URL = credentials.baseURL;
560
+ process.env.MCP_CLIENT_ID = credentials.clientID;
561
+ process.env.MCP_CLIENT_SECRET = credentials.clientSecret;
562
+ process.env.MCP_OAUTH_URL = credentials.OauthURL;
563
+ process.env.MCP_TOKEN_AUDIENCE = credentials.tokenAudience;
564
+ }
565
+ return credentials;
566
+ }
503
567
  }
504
568
  exports.ClusterDetailsPage = ClusterDetailsPage;
@@ -119,6 +119,7 @@ declare class ModelerCreatePage {
119
119
  clickViewProcessInstanceLink(): Promise<void>;
120
120
  clickNameInput(): Promise<void>;
121
121
  clickUserTask(id: string): Promise<void>;
122
+ clickCanvasElement(elementId: string): Promise<void>;
122
123
  fillNameInput(name: string): Promise<void>;
123
124
  clickJobTypeInput(): Promise<void>;
124
125
  fillJobTypeInput(name: string): Promise<void>;
@@ -560,6 +560,12 @@ class ModelerCreatePage {
560
560
  const priorityUserTask = this.page.locator(`[data-element-id="${id}"]`);
561
561
  await priorityUserTask.click({ timeout: 60000 });
562
562
  }
563
+ async clickCanvasElement(elementId) {
564
+ await this.page
565
+ .locator(`[data-element-id="${elementId}"]`)
566
+ .first()
567
+ .click({ timeout: 30000 });
568
+ }
563
569
  async fillNameInput(name) {
564
570
  await this.nameInput.fill(name);
565
571
  }
@@ -65,6 +65,7 @@ class PlayPage {
65
65
  });
66
66
  }
67
67
  async clickCompleteJobButton() {
68
+ await this.completeJobButton.waitFor({ state: 'visible', timeout: 30000 });
68
69
  await this.completeJobButton.click();
69
70
  }
70
71
  async clickStartInstanceButton() {
@@ -72,6 +73,7 @@ class PlayPage {
72
73
  let attempts = 0;
73
74
  while (attempts < maxRetries) {
74
75
  try {
76
+ await this.page.waitForLoadState('domcontentloaded');
75
77
  await this.diagram.waitFor({ state: 'visible', timeout: 60000 });
76
78
  await this.startInstanceButton
77
79
  .or(this.startInstanceWithCachedButton)
@@ -86,7 +88,7 @@ class PlayPage {
86
88
  catch (error) {
87
89
  if (attempts >= maxRetries - 1)
88
90
  throw error;
89
- await (0, sleep_1.sleep)(5000);
91
+ await this.page.waitForLoadState('load');
90
92
  attempts++;
91
93
  }
92
94
  }
@@ -98,12 +100,21 @@ class PlayPage {
98
100
  'Start new instance',
99
101
  'Start instance',
100
102
  ];
101
- for (const buttonName of buttonVariations) {
102
- const button = this.page.getByRole('button', { name: buttonName });
103
- const count = await button.count();
104
- if (count > 0) {
105
- await button.click();
106
- return;
103
+ // Wait for the page to settle before checking for modal buttons
104
+ await this.page.waitForLoadState('domcontentloaded');
105
+ const maxAttempts = 3;
106
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
107
+ for (const buttonName of buttonVariations) {
108
+ const button = this.page.getByRole('button', { name: buttonName });
109
+ const count = await button.count();
110
+ if (count > 0) {
111
+ await button.waitFor({ state: 'visible', timeout: 10000 });
112
+ await button.click();
113
+ return;
114
+ }
115
+ }
116
+ if (attempt < maxAttempts - 1) {
117
+ await this.page.waitForLoadState('load');
107
118
  }
108
119
  }
109
120
  throw new Error('Could not find start instance button with any expected variation');
@@ -113,44 +124,24 @@ class PlayPage {
113
124
  await this.startInstanceWithCachedButton.click({ timeout: 30000 });
114
125
  }
115
126
  async waitForInstanceDetailsToBeLoaded() {
116
- const maxRetries = 2;
117
- let attempts = 0;
118
- while (attempts < maxRetries) {
119
- try {
120
- await (0, test_1.expect)(this.page.getByText(/process instance key/i)).toBeVisible({
121
- timeout: maxWaitTimeSeconds,
122
- });
123
- await (0, test_1.expect)(this.page.getByText(/This process instance has no variables/i)).toBeVisible({
124
- timeout: maxWaitTimeSeconds,
125
- });
126
- return;
127
- }
128
- catch (error) {
129
- if (attempts >= maxRetries - 1)
130
- throw error;
131
- await this.page.reload();
132
- attempts++;
133
- }
134
- }
127
+ // Do NOT use page.reload() — it destroys Play's embedded context.
128
+ await (0, test_1.expect)(this.page.getByText(/process instance key/i)).toBeVisible({
129
+ timeout: maxWaitTimeSeconds,
130
+ });
131
+ await (0, test_1.expect)(this.page.getByText(/This process instance has no variables/i)).toBeVisible({
132
+ timeout: maxWaitTimeSeconds,
133
+ });
135
134
  }
136
135
  async waitForNextElementToBeActive(historyItem) {
137
136
  const locator = this.page.getByText(new RegExp(`^${historyItem}`, 'i'));
138
- const maxRetries = 6; // ~1 min with the waits below
139
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
140
- try {
141
- await (0, test_1.expect)(locator.first()).toBeVisible({ timeout: 20000 });
142
- return;
143
- }
144
- catch (e) {
145
- if (attempt === maxRetries)
146
- throw e;
147
- await this.page.reload();
148
- await this.diagram.waitFor({ state: 'visible', timeout: 30000 });
149
- await this.page.waitForTimeout(5000);
150
- }
151
- }
137
+ // Do NOT use page.reload() here Play is embedded in the modeler and
138
+ // reloading destroys the Play instance context, making the diagram
139
+ // permanently invisible. Use a long timeout instead, consistent with
140
+ // waitForCompleteJobButtonToBeAvailable.
141
+ await (0, test_1.expect)(locator.first()).toBeVisible({ timeout: maxWaitTimeSeconds });
152
142
  }
153
143
  async waitForProcessToBeCompleted() {
144
+ // Do NOT use page.reload() — it destroys Play's embedded context.
154
145
  await (0, test_1.expect)(this.page.getByText('Completed')).toBeVisible({
155
146
  timeout: maxWaitTimeSeconds,
156
147
  });
@@ -171,6 +162,7 @@ class PlayPage {
171
162
  }
172
163
  }
173
164
  async clickViewScenarioButton() {
165
+ await this.viewScenarioButton.waitFor({ state: 'visible', timeout: 30000 });
174
166
  await this.viewScenarioButton.click();
175
167
  }
176
168
  async enterScenarioName(scenarioName) {
@@ -192,6 +184,10 @@ class PlayPage {
192
184
  await this.viewAllScenariosButton.click();
193
185
  }
194
186
  async confirmSaveScenario() {
187
+ await this.confirmSaveScenarioButton.waitFor({
188
+ state: 'visible',
189
+ timeout: 10000,
190
+ });
195
191
  await this.confirmSaveScenarioButton.click();
196
192
  }
197
193
  async getDeleteIconForScenario(scenarioName) {
@@ -202,12 +198,21 @@ class PlayPage {
202
198
  }
203
199
  async deleteScenario(scenarioName) {
204
200
  const deleteIcon = await this.getDeleteIconForScenario(scenarioName);
201
+ await deleteIcon.waitFor({ state: 'visible', timeout: 10000 });
205
202
  await deleteIcon.click();
206
203
  }
207
204
  async confirmDeleteScenario() {
205
+ await this.confirmDeleteScenarioButton.waitFor({
206
+ state: 'visible',
207
+ timeout: 10000,
208
+ });
208
209
  await this.confirmDeleteScenarioButton.click();
209
210
  }
210
211
  async clickRunAllScenariosButton() {
212
+ await this.runAllScenariosButton.waitFor({
213
+ state: 'visible',
214
+ timeout: 30000,
215
+ });
211
216
  await this.runAllScenariosButton.click();
212
217
  }
213
218
  async clickmessagePublishButton() {
@@ -221,8 +226,9 @@ class PlayPage {
221
226
  await (0, expectLocatorWithRetry_1.expectLocatorWithRetry)(this.page, this.page
222
227
  .locator('tr', { hasText: scenarioName })
223
228
  .locator('text=Completed'), {
224
- totalTimeout: 90000,
225
- visibilityTimeout: 20000,
229
+ totalTimeout: 120000,
230
+ visibilityTimeout: 30000,
231
+ maxRetries: 5,
226
232
  postAction: async () => {
227
233
  if (await this.retryButton.isVisible()) {
228
234
  await this.retryButton.click();
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const _8_10_1 = require("../../../fixtures/8.10");
4
+ const UtilitiesPage_1 = require("../../../pages/8.10/UtilitiesPage");
5
+ const _setup_1 = require("../../../test-setup.js");
6
+ const apiHelpers_1 = require("../../../utils/apiHelpers");
7
+ const users_1 = require("../../../utils/users");
8
+ const test_1 = require("@playwright/test");
9
+ const testUser = (0, users_1.getTestUser)('fourteenthUser');
10
+ let authToken;
11
+ let instanceKey;
12
+ _8_10_1.test.describe.configure({ mode: 'parallel' });
13
+ _8_10_1.test.describe('Orchestration Cluster MCP Server - Cluster Tools @tasklistV2', () => {
14
+ const clusterName = 'Agentic Orchestration Cluster';
15
+ _8_10_1.test.beforeAll(async () => {
16
+ authToken = await (0, apiHelpers_1.authSaasAPI)(undefined, 'agentic_cluster');
17
+ const processDefinitionKey = await (0, apiHelpers_1.deployProcess)('./resources/mcp_server_saas/mcp_cluster_tools.bpmn', authToken, 'saas', 'agentic_cluster');
18
+ if (processDefinitionKey == null) {
19
+ throw new Error('Failed to deploy cluster tools process');
20
+ }
21
+ instanceKey = await (0, apiHelpers_1.createProcessInstance)(String(processDefinitionKey), authToken, 'saas', 'agentic_cluster');
22
+ });
23
+ _8_10_1.test.beforeEach(async ({ page, loginPage }, testInfo) => {
24
+ await (0, UtilitiesPage_1.loginWithRetry)(page, loginPage, testUser, (testInfo.workerIndex + 1) * 1000);
25
+ });
26
+ _8_10_1.test.afterEach(async ({ page }, testInfo) => {
27
+ await (0, _setup_1.captureScreenshot)(page, testInfo);
28
+ await (0, _setup_1.captureFailureVideo)(page, testInfo);
29
+ });
30
+ const clusterToolTests = [
31
+ {
32
+ name: 'getClusterStatus - returns cluster health status',
33
+ variable: 'mcpResultClusterStatus',
34
+ assertions: [
35
+ {
36
+ text: 'HEALTHY',
37
+ description: 'cluster status response contains healthy field',
38
+ },
39
+ ],
40
+ },
41
+ {
42
+ name: 'getTopology - returns cluster topology information',
43
+ variable: 'mcpResultTopology',
44
+ assertions: [
45
+ { text: 'brokers', description: 'topology response contains brokers' },
46
+ {
47
+ text: 'partitionsCount',
48
+ description: 'topology response contains partitions count',
49
+ },
50
+ ],
51
+ },
52
+ ];
53
+ for (const testCase of clusterToolTests) {
54
+ (0, _8_10_1.test)(testCase.name, async ({ homePage, appsPage, operateHomePage, operateProcessesPage, operateProcessInstancePage, }) => {
55
+ _8_10_1.test.slow();
56
+ await _8_10_1.test.step('Navigate to completed process in Operate', async () => {
57
+ await homePage.clickClusters();
58
+ await appsPage.clickOperate(clusterName);
59
+ await (0, test_1.expect)(operateHomePage.operateBanner).toBeVisible({
60
+ timeout: 60000,
61
+ });
62
+ await operateHomePage.clickProcessesTab();
63
+ await operateProcessesPage.clickProcessCompletedCheckbox();
64
+ await operateProcessesPage.applyMoreFilters('Process Instance Key(s)', String(instanceKey));
65
+ await operateProcessesPage.clickProcessInstanceLink('MCP Cluster tools');
66
+ await operateProcessInstancePage.assertProcessCompleteStatusWithRetry();
67
+ });
68
+ for (const assertion of testCase.assertions) {
69
+ await _8_10_1.test.step(`Verify ${assertion.description}`, async () => {
70
+ await operateProcessInstancePage.assertProcessVariableContainsText(testCase.variable, assertion.text);
71
+ });
72
+ }
73
+ });
74
+ }
75
+ });
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const _8_10_1 = require("../../../fixtures/8.10");
4
+ const UtilitiesPage_1 = require("../../../pages/8.10/UtilitiesPage");
5
+ const _setup_1 = require("../../../test-setup.js");
6
+ const apiHelpers_1 = require("../../../utils/apiHelpers");
7
+ const incidentHelpers_1 = require("../../../utils/incidentHelpers");
8
+ const users_1 = require("../../../utils/users");
9
+ const test_1 = require("@playwright/test");
10
+ const testUser = (0, users_1.getTestUser)('fifteenthUser');
11
+ let authToken;
12
+ let toolsInstanceKey;
13
+ _8_10_1.test.describe.configure({ mode: 'parallel' });
14
+ _8_10_1.test.describe('Orchestration Cluster MCP Server - Incident Tools @tasklistV2', () => {
15
+ const clusterName = 'Agentic Orchestration Cluster';
16
+ _8_10_1.test.beforeAll(async () => {
17
+ authToken = await (0, apiHelpers_1.authSaasAPI)(undefined, 'agentic_cluster');
18
+ const [testFileProcessKey, toolsProcessKey] = await Promise.all([
19
+ (0, apiHelpers_1.deployProcess)('./resources/mcp_server_saas/mcp_incident_test_file.bpmn', authToken, 'saas', 'agentic_cluster'),
20
+ (0, apiHelpers_1.deployProcess)('./resources/mcp_server_saas/mcp_incident_tools.bpmn', authToken, 'saas', 'agentic_cluster'),
21
+ ]);
22
+ if (testFileProcessKey == null || toolsProcessKey == null) {
23
+ throw new Error('Failed to deploy incident tools processes');
24
+ }
25
+ // Create test instance — the call activity in the BPMN references a
26
+ // non-existent process, so Zeebe raises a CALLED_ELEMENT_ERROR incident
27
+ // immediately without needing a worker.
28
+ const testInstanceKey = await (0, apiHelpers_1.createProcessInstance)(String(testFileProcessKey), authToken, 'saas', 'agentic_cluster');
29
+ // Get the incident key created by the test instance (with retry logic)
30
+ const testIncidentKey = await (0, incidentHelpers_1.getIncidentKeyByProcessInstanceKey)(testInstanceKey, authToken, 'saas', 'agentic_cluster');
31
+ console.log('Creating tools instance with variables:', {
32
+ testFileProcessKey,
33
+ testIncidentKey,
34
+ });
35
+ // Create tools instance with required variables
36
+ toolsInstanceKey = await (0, apiHelpers_1.createProcessInstance)(String(toolsProcessKey), authToken, 'saas', 'agentic_cluster', { testFileProcessKey, testIncidentKey });
37
+ });
38
+ _8_10_1.test.beforeEach(async ({ page, loginPage }, testInfo) => {
39
+ await (0, UtilitiesPage_1.loginWithRetry)(page, loginPage, testUser, (testInfo.workerIndex + 1) * 1000);
40
+ });
41
+ _8_10_1.test.afterEach(async ({ page }, testInfo) => {
42
+ await (0, _setup_1.captureScreenshot)(page, testInfo);
43
+ await (0, _setup_1.captureFailureVideo)(page, testInfo);
44
+ });
45
+ const incidentToolTests = [
46
+ {
47
+ name: 'searchIncidents - search incidents with filters such as processDefinitionKey',
48
+ variable: 'mcpResultSearchIncidents',
49
+ assertions: [
50
+ {
51
+ text: 'totalItems',
52
+ description: 'search response contains totalItems field',
53
+ },
54
+ {
55
+ text: 'incidentKey',
56
+ description: 'search response contains incidentKey field',
57
+ },
58
+ {
59
+ text: 'processDefinitionKey',
60
+ description: 'search response contains processDefinitionKey field',
61
+ },
62
+ {
63
+ text: 'state',
64
+ description: 'search response contains state field',
65
+ },
66
+ ],
67
+ },
68
+ {
69
+ name: 'getIncident - retrieve an incident by key',
70
+ variable: 'mcpResultGetIncident',
71
+ assertions: [
72
+ {
73
+ text: 'incidentKey',
74
+ description: 'response contains incidentKey field',
75
+ },
76
+ {
77
+ text: 'processInstanceKey',
78
+ description: 'response contains processInstanceKey field',
79
+ },
80
+ {
81
+ text: 'errorType',
82
+ description: 'response contains errorType field',
83
+ },
84
+ {
85
+ text: 'state',
86
+ description: 'response contains state field',
87
+ },
88
+ ],
89
+ },
90
+ {
91
+ name: 'resolveIncident - resolve an incident by key',
92
+ variable: 'mcpResultResolveIncident',
93
+ assertions: [
94
+ {
95
+ text: 'resolved',
96
+ description: 'response confirms incident was resolved',
97
+ },
98
+ ],
99
+ },
100
+ ];
101
+ for (const testCase of incidentToolTests) {
102
+ (0, _8_10_1.test)(testCase.name, async ({ homePage, appsPage, operateHomePage, operateProcessesPage, operateProcessInstancePage, }) => {
103
+ _8_10_1.test.slow();
104
+ await _8_10_1.test.step('Navigate to completed process in Operate', async () => {
105
+ await homePage.clickClusters();
106
+ await appsPage.clickOperate(clusterName);
107
+ await (0, test_1.expect)(operateHomePage.operateBanner).toBeVisible({
108
+ timeout: 60000,
109
+ });
110
+ await operateHomePage.clickProcessesTab();
111
+ await operateProcessesPage.clickProcessCompletedCheckbox();
112
+ await operateProcessesPage.applyMoreFilters('Process Instance Key(s)', String(toolsInstanceKey));
113
+ await operateProcessesPage.clickProcessInstanceLink('MCP Incident Tools');
114
+ await operateProcessInstancePage.assertProcessCompleteStatusWithRetry();
115
+ });
116
+ for (const assertion of testCase.assertions) {
117
+ await _8_10_1.test.step(assertion.description, async () => {
118
+ await operateProcessInstancePage.assertProcessVariableContainsText(testCase.variable, assertion.text);
119
+ });
120
+ }
121
+ });
122
+ }
123
+ });
@@ -4,6 +4,7 @@ const test_1 = require("@playwright/test");
4
4
  const _8_10_1 = require("../../fixtures/8.10");
5
5
  const _setup_1 = require("../../test-setup.js");
6
6
  const connectorSecrets_1 = require("../../utils/connectorSecrets");
7
+ const mcpSecrets_1 = require("../../utils/mcpSecrets");
7
8
  const UtilitiesPage_1 = require("../../pages/8.10/UtilitiesPage");
8
9
  const users_1 = require("../../utils/users");
9
10
  _8_10_1.test.describe.configure({ mode: 'parallel' });
@@ -44,6 +45,32 @@ _8_10_1.test.describe('Cluster Setup Tests', () => {
44
45
  await clusterSecretsPage.createSetOfSecrets(clusterName, connectorSecrets_1.agentcoreSecretsValues);
45
46
  }
46
47
  });
48
+ (0, _8_10_1.test)('Create Agentic Orchestration Cluster', async ({ page, loginPage, homePage, clusterPage, clusterDetailsPage, clusterSecretsPage, }, testInfo) => {
49
+ _8_10_1.test.skip(process.env.IS_PROD === 'true', 'Skipping test because not required on PROD test org');
50
+ _8_10_1.test.skip(process.env.IS_AG === 'true', 'Skipping test because not required when IS_AG is true');
51
+ _8_10_1.test.slow();
52
+ const clusterName = 'Agentic Orchestration Cluster';
53
+ const apiClientName = 'Test_API_Client' + (await (0, _setup_1.generateRandomStringAsync)(3));
54
+ const mcpClientName = 'MCP_Test_API_Client' + (await (0, _setup_1.generateRandomStringAsync)(3));
55
+ await (0, UtilitiesPage_1.loginWithRetry)(page, loginPage, users_1.testUsers.mainUser, (testInfo.workerIndex + 1) * 1000);
56
+ await homePage.clickClusters();
57
+ await clusterPage.createCluster(clusterName);
58
+ await clusterPage.assertClusterHealthyStatusWithRetry(clusterName);
59
+ await clusterPage.clickClusterLink(clusterName);
60
+ await clusterDetailsPage.assertComponentsHealth();
61
+ await clusterDetailsPage.clickSettingsTab();
62
+ await clusterDetailsPage.checkMcpSupportToggle();
63
+ await homePage.clickClusters();
64
+ await clusterPage.assertClusterHealthyStatusWithRetry(clusterName);
65
+ await clusterPage.clickClusterLink(clusterName);
66
+ await clusterDetailsPage.clickAPITab();
67
+ await clusterDetailsPage.createAPIClientAndReturnVariables(apiClientName, true, 'agentic_cluster');
68
+ await clusterDetailsPage.clickCloseModalButton();
69
+ const mcpCredentials = await clusterDetailsPage.createMCPClientAndReturnVariables(mcpClientName);
70
+ await clusterDetailsPage.clickCloseModalButton();
71
+ await clusterPage.clickConnectorSecretsTab(clusterName);
72
+ await clusterSecretsPage.createSetOfSecrets(clusterName, (0, mcpSecrets_1.mcpSecretsValues)(mcpCredentials));
73
+ });
47
74
  (0, _8_10_1.test)('Create AWS Cluster', async ({ page, loginPage, homePage, clusterPage, clusterDetailsPage, clusterSecretsPage, }, testInfo) => {
48
75
  _8_10_1.test.slow();
49
76
  const clusterName = 'AWS Cluster';
@@ -4,6 +4,7 @@ const test_1 = require("@playwright/test");
4
4
  const SM_8_10_1 = require("../../fixtures/SM-8.10");
5
5
  const _setup_1 = require("../../test-setup.js");
6
6
  const sleep_1 = require("../../utils/sleep");
7
+ const UtilitiesPage_1 = require("../../pages/SM-8.10/UtilitiesPage");
7
8
  SM_8_10_1.test.describe.configure({ mode: 'parallel' });
8
9
  SM_8_10_1.test.describe('Deploy and run a process in Play', () => {
9
10
  SM_8_10_1.test.beforeEach(async ({ navigationPage }, testInfo) => {
@@ -13,49 +14,15 @@ SM_8_10_1.test.describe('Deploy and run a process in Play', () => {
13
14
  await (0, _setup_1.captureScreenshot)(page, testInfo);
14
15
  await (0, _setup_1.captureFailureVideo)(page, testInfo);
15
16
  });
16
- (0, SM_8_10_1.test)('User Tasks and Service Task', async ({ modelerHomePage, modelerCreatePage, playPage, }) => {
17
+ (0, SM_8_10_1.test)('User Tasks and Service Task', async ({ page, modelerHomePage, modelerCreatePage, playPage, }) => {
17
18
  SM_8_10_1.test.slow();
18
19
  const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
19
20
  const processName = 'Play_Test_Process' + randomString;
20
21
  await SM_8_10_1.test.step('Open Cross Component Test Project', async () => {
21
22
  await modelerHomePage.clickCrossComponentProjectFolder();
22
23
  });
23
- await SM_8_10_1.test.step('Add A BPMN Template To The Project', async () => {
24
- await modelerHomePage.clickDiagramTypeDropdown();
25
- await modelerHomePage.clickBpmnTemplateOption();
26
- });
27
24
  await SM_8_10_1.test.step('Create a BPMN Diagram with user tasks and service task', async () => {
28
- await (0, test_1.expect)(modelerCreatePage.generalPanel).toBeVisible({
29
- timeout: 120000,
30
- });
31
- await modelerCreatePage.enterDiagramName(processName);
32
- await (0, sleep_1.sleep)(2000);
33
- // Add a user task with Zeebe user task implementation
34
- await modelerCreatePage.clickAppendElementButton();
35
- await modelerCreatePage.clickAppendTaskButton();
36
- await modelerCreatePage.clickChangeTypeButton();
37
- await modelerCreatePage.clickUserTaskOption();
38
- await modelerCreatePage.chooseImplementationOption('zeebeUserTask');
39
- await modelerCreatePage.clickGeneralPropertiesPanel();
40
- await modelerCreatePage.clickElemendIdInput();
41
- await modelerCreatePage.fillElementIdInput('zeebe-user-task' + randomString);
42
- await (0, sleep_1.sleep)(1000);
43
- // Add a service task
44
- await modelerCreatePage.clickAppendElementButton();
45
- await modelerCreatePage.clickAppendTaskButton();
46
- await modelerCreatePage.clickChangeTypeButton();
47
- await modelerCreatePage.clickServiceTaskOption();
48
- await modelerCreatePage.clickGeneralPropertiesPanel();
49
- await modelerCreatePage.clickElemendIdInput();
50
- await modelerCreatePage.fillElementIdInput('service-task' + randomString);
51
- await modelerCreatePage.clickTaskDefinitionPropertiesPanel();
52
- await modelerCreatePage.clickJobTypeInput();
53
- await modelerCreatePage.fillJobTypeInput('someJob' + randomString);
54
- await (0, sleep_1.sleep)(1000);
55
- // Add end event
56
- await modelerCreatePage.clickAppendElementButton();
57
- await modelerCreatePage.clickAppendEndEventButton();
58
- await (0, sleep_1.sleep)(2000);
25
+ await (0, UtilitiesPage_1.modelDiagramFromFile)(page, modelerHomePage, modelerCreatePage, processName, 'Play_Test_User_Service_Task');
59
26
  });
60
27
  await SM_8_10_1.test.step('Open Play', async () => {
61
28
  await modelerCreatePage.switchToPlay();
@@ -65,10 +32,10 @@ SM_8_10_1.test.describe('Deploy and run a process in Play', () => {
65
32
  await playPage.dismissStartModal();
66
33
  await playPage.clickStartInstanceButton();
67
34
  await playPage.waitForInstanceDetailsToBeLoaded();
68
- await playPage.waitForNextElementToBeActive('zeebe-user-task' + randomString);
35
+ await playPage.waitForNextElementToBeActive('play-user-task');
69
36
  await playPage.waitForCompleteJobButtonToBeAvailable();
70
37
  await playPage.clickCompleteJobButton();
71
- await playPage.waitForNextElementToBeActive('service-task' + randomString);
38
+ await playPage.waitForNextElementToBeActive('play-service-task');
72
39
  await playPage.waitForCompleteJobButtonToBeAvailable();
73
40
  await playPage.clickCompleteJobButton();
74
41
  await playPage.waitForProcessToBeCompleted();
@@ -96,7 +63,11 @@ SM_8_10_1.test.describe('Deploy and run a process in Play', () => {
96
63
  await modelerCreatePage.clickSecondPlacedGateway();
97
64
  await modelerCreatePage.clickAppendElementButton();
98
65
  await modelerCreatePage.clickAppendEndEventButton('parallelGateway');
99
- await (0, sleep_1.sleep)(5000);
66
+ await (0, test_1.expect)(modelerCreatePage.generalPanel).toBeVisible({
67
+ timeout: 30000,
68
+ });
69
+ // Wait for modeler background auto-save to complete
70
+ await (0, sleep_1.sleep)(2000);
100
71
  });
101
72
  await SM_8_10_1.test.step('Open Play', async () => {
102
73
  await modelerCreatePage.switchToPlay();
@@ -113,17 +84,23 @@ SM_8_10_1.test.describe('Deploy and run a process in Play', () => {
113
84
  await playPage.clickSaveScenarioButton();
114
85
  await playPage.enterScenarioName(scenarioName);
115
86
  await playPage.confirmSaveScenario();
116
- await (0, sleep_1.sleep)(2000);
87
+ await (0, test_1.expect)(playPage.saveScenarioModal).toBeHidden({ timeout: 10000 });
117
88
  });
118
89
  await SM_8_10_1.test.step('Update the saved scenario', async () => {
119
90
  await modelerCreatePage.switchToImplementTab();
120
- await (0, sleep_1.sleep)(2000);
91
+ await (0, test_1.expect)(modelerCreatePage.generalPanel).toBeVisible({
92
+ timeout: 30000,
93
+ });
121
94
  await modelerCreatePage.clickFirstPlacedGateway();
122
95
  await modelerCreatePage.clickAppendElementButton();
123
96
  await modelerCreatePage.clickAppendTaskButton();
124
97
  await modelerCreatePage.clickConnectToOtherElementButton();
125
98
  await modelerCreatePage.clickSecondPlacedGateway();
126
- await (0, sleep_1.sleep)(5000);
99
+ await (0, test_1.expect)(modelerCreatePage.generalPanel).toBeVisible({
100
+ timeout: 30000,
101
+ });
102
+ // Wait for modeler background auto-save to complete
103
+ await (0, sleep_1.sleep)(2000);
127
104
  await modelerCreatePage.switchToPlay();
128
105
  // After diagram modification, Play may show a "Continue" button
129
106
  // to redeploy, or it may show the diagram directly. Try clicking
@@ -153,7 +130,9 @@ SM_8_10_1.test.describe('Deploy and run a process in Play', () => {
153
130
  await SM_8_10_1.test.step('Delete test scenario', async () => {
154
131
  await playPage.deleteScenario(scenarioName);
155
132
  await playPage.confirmDeleteScenario();
156
- await (0, test_1.expect)(playPage.getScenarioRow(scenarioName)).toHaveCount(0);
133
+ await (0, test_1.expect)(playPage.getScenarioRow(scenarioName)).toHaveCount(0, {
134
+ timeout: 10000,
135
+ });
157
136
  });
158
137
  });
159
138
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.483",
3
+ "version": "0.0.485",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,56 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:zeebe="http://camunda.org/schema/zeebe/1.0" xmlns:modeler="http://camunda.org/schema/modeler/1.0" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_1" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Web Modeler" exporterVersion="ff1274a" modeler:executionPlatform="Camunda Cloud" modeler:executionPlatformVersion="8.10.0">
3
+ <bpmn:process id="Play_Test_User_Service_Task" name="Play_Test_User_Service_Task" isExecutable="true">
4
+ <bpmn:startEvent id="StartEvent_1">
5
+ <bpmn:outgoing>Flow_1</bpmn:outgoing>
6
+ </bpmn:startEvent>
7
+ <bpmn:sequenceFlow id="Flow_1" sourceRef="StartEvent_1" targetRef="play-user-task" />
8
+ <bpmn:userTask id="play-user-task" name="play-user-task">
9
+ <bpmn:extensionElements>
10
+ <zeebe:userTask />
11
+ </bpmn:extensionElements>
12
+ <bpmn:incoming>Flow_1</bpmn:incoming>
13
+ <bpmn:outgoing>Flow_2</bpmn:outgoing>
14
+ </bpmn:userTask>
15
+ <bpmn:sequenceFlow id="Flow_2" sourceRef="play-user-task" targetRef="play-service-task" />
16
+ <bpmn:serviceTask id="play-service-task" name="play-service-task">
17
+ <bpmn:extensionElements>
18
+ <zeebe:taskDefinition type="play-job-type" />
19
+ </bpmn:extensionElements>
20
+ <bpmn:incoming>Flow_2</bpmn:incoming>
21
+ <bpmn:outgoing>Flow_3</bpmn:outgoing>
22
+ </bpmn:serviceTask>
23
+ <bpmn:endEvent id="EndEvent_1">
24
+ <bpmn:incoming>Flow_3</bpmn:incoming>
25
+ </bpmn:endEvent>
26
+ <bpmn:sequenceFlow id="Flow_3" sourceRef="play-service-task" targetRef="EndEvent_1" />
27
+ </bpmn:process>
28
+ <bpmndi:BPMNDiagram id="BPMNDiagram_1">
29
+ <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Play_Test_User_Service_Task">
30
+ <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
31
+ <dc:Bounds x="150" y="100" width="36" height="36" />
32
+ </bpmndi:BPMNShape>
33
+ <bpmndi:BPMNShape id="Activity_UserTask_di" bpmnElement="play-user-task">
34
+ <dc:Bounds x="240" y="78" width="100" height="80" />
35
+ </bpmndi:BPMNShape>
36
+ <bpmndi:BPMNShape id="Activity_ServiceTask_di" bpmnElement="play-service-task">
37
+ <dc:Bounds x="400" y="78" width="100" height="80" />
38
+ </bpmndi:BPMNShape>
39
+ <bpmndi:BPMNShape id="Event_EndEvent_di" bpmnElement="EndEvent_1">
40
+ <dc:Bounds x="562" y="100" width="36" height="36" />
41
+ </bpmndi:BPMNShape>
42
+ <bpmndi:BPMNEdge id="Flow_1_di" bpmnElement="Flow_1">
43
+ <di:waypoint x="186" y="118" />
44
+ <di:waypoint x="240" y="118" />
45
+ </bpmndi:BPMNEdge>
46
+ <bpmndi:BPMNEdge id="Flow_2_di" bpmnElement="Flow_2">
47
+ <di:waypoint x="340" y="118" />
48
+ <di:waypoint x="400" y="118" />
49
+ </bpmndi:BPMNEdge>
50
+ <bpmndi:BPMNEdge id="Flow_3_di" bpmnElement="Flow_3">
51
+ <di:waypoint x="500" y="118" />
52
+ <di:waypoint x="562" y="118" />
53
+ </bpmndi:BPMNEdge>
54
+ </bpmndi:BPMNPlane>
55
+ </bpmndi:BPMNDiagram>
56
+ </bpmn:definitions>