@camunda/e2e-test-suite 0.0.1458 → 0.0.1460

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.
@@ -17,6 +17,10 @@ declare class OperateProcessInstancePage {
17
17
  readonly messageVariable: Locator;
18
18
  readonly whatsNewPopUp: Locator;
19
19
  readonly gotItButton: Locator;
20
+ readonly detailsTab: Locator;
21
+ readonly agentInstancePanel: Locator;
22
+ readonly conversationHistorySection: Locator;
23
+ readonly conversationHistoryTrigger: Locator;
20
24
  constructor(page: Page);
21
25
  connectorResultVariableValue(variableName: string): Promise<Locator>;
22
26
  rememberInstanceUrl(timeout?: number, watchedUrl?: InstanceUrlWatcher): Promise<void>;
@@ -36,6 +40,10 @@ declare class OperateProcessInstancePage {
36
40
  assertProcessVariableContainsText(variableName: string, text: string): Promise<void>;
37
41
  assertProcessVariableHasDocumentCount(variableName: string, count: number, softFail?: boolean): Promise<void>;
38
42
  clickDiagramTask(taskName: string): Promise<void>;
43
+ openAgentConversation(elementId: string): Promise<void>;
44
+ assertReadableReasoning(reasoningHistoryItemKey: string, answerHistoryItemKey: string): Promise<void>;
45
+ assertEncryptedReasoningIsHidden(reasoningHistoryItemKey: string, answerHistoryItemKey: string): Promise<void>;
46
+ private assertFinalAnswerVisible;
39
47
  assertActiveTokenIsPresent(): Promise<void>;
40
48
  assertDetailsPopoverIsVisible(): Promise<void>;
41
49
  assertBusinessIdVisible(businessId: string): Promise<void>;
@@ -158,6 +158,10 @@ class OperateProcessInstancePage {
158
158
  messageVariable;
159
159
  whatsNewPopUp;
160
160
  gotItButton;
161
+ detailsTab;
162
+ agentInstancePanel;
163
+ conversationHistorySection;
164
+ conversationHistoryTrigger;
161
165
  constructor(page) {
162
166
  this.page = page;
163
167
  this.diagram = page.getByTestId('diagram');
@@ -185,6 +189,16 @@ class OperateProcessInstancePage {
185
189
  name: "Here's what moved in Operate",
186
190
  });
187
191
  this.gotItButton = page.getByRole('button', { name: 'Got it' });
192
+ this.detailsTab = page
193
+ .getByLabel('Process Instance Bottom Panel Tabs')
194
+ .getByRole('link', { name: 'Details' });
195
+ this.agentInstancePanel = page.getByRole('article', {
196
+ name: 'Agent Instance',
197
+ });
198
+ this.conversationHistorySection = this.agentInstancePanel.getByTestId('agent-conversation-history-section');
199
+ this.conversationHistoryTrigger = this.conversationHistorySection.getByRole('button', {
200
+ name: 'Conversation history',
201
+ });
188
202
  }
189
203
  async connectorResultVariableValue(variableName) {
190
204
  return await this.page.getByTestId(variableName).locator('td').last();
@@ -874,6 +888,48 @@ class OperateProcessInstancePage {
874
888
  await (0, test_1.expect)(diagramTask).toBeVisible();
875
889
  await diagramTask.click();
876
890
  }
891
+ async openAgentConversation(elementId) {
892
+ const agentTask = this.diagram.locator(`.djs-element[data-element-id="${elementId}"]`);
893
+ // BpmnJS ignores element clicks until element statistics load and the
894
+ // element receives the op-selectable marker.
895
+ await (0, test_1.expect)(agentTask).toHaveClass(/op-selectable/, { timeout: 30000 });
896
+ await agentTask.click();
897
+ await (0, test_1.expect)(this.detailsTab).toBeVisible();
898
+ await this.detailsTab.click();
899
+ await (0, test_1.expect)(this.agentInstancePanel).toBeVisible({ timeout: 30000 });
900
+ await (0, test_1.expect)(this.conversationHistoryTrigger).toHaveAttribute('aria-expanded', 'false');
901
+ await this.conversationHistoryTrigger.click();
902
+ await (0, test_1.expect)(this.conversationHistoryTrigger).toHaveAttribute('aria-expanded', 'true');
903
+ }
904
+ async assertReadableReasoning(reasoningHistoryItemKey, answerHistoryItemKey) {
905
+ const reasoningMessage = this.conversationHistorySection.getByTestId(`conversation-message-${reasoningHistoryItemKey}`);
906
+ const thinkingRegion = reasoningMessage.getByRole('region', {
907
+ name: 'Thinking',
908
+ });
909
+ const answerMessage = this.conversationHistorySection.getByTestId(`conversation-message-${answerHistoryItemKey}`);
910
+ await (0, test_1.expect)(reasoningMessage).toHaveAttribute('aria-label', 'Assistant message', { timeout: 60000 });
911
+ await (0, test_1.expect)(thinkingRegion).toBeVisible();
912
+ await (0, test_1.expect)(thinkingRegion.locator('p')).toHaveText(/\S/);
913
+ await (0, test_1.expect)(thinkingRegion.getByRole('button')).toHaveCount(0);
914
+ await this.assertFinalAnswerVisible(answerMessage);
915
+ await (0, test_1.expect)(this.conversationHistorySection).not.toContainText(/camunda\.agenticai\.content\.type|encrypted_content|signature|"payload"/);
916
+ }
917
+ async assertEncryptedReasoningIsHidden(reasoningHistoryItemKey, answerHistoryItemKey) {
918
+ const reasoningMessage = this.conversationHistorySection.getByTestId(`conversation-message-${reasoningHistoryItemKey}`);
919
+ const answerMessage = this.conversationHistorySection.getByTestId(`conversation-message-${answerHistoryItemKey}`);
920
+ await (0, test_1.expect)(reasoningMessage).toHaveAttribute('aria-label', 'Assistant message', { timeout: 60000 });
921
+ await (0, test_1.expect)(reasoningMessage).toBeVisible({ timeout: 60000 });
922
+ await (0, test_1.expect)(reasoningMessage.getByRole('region', { name: 'Thinking' })).toHaveCount(0);
923
+ await (0, test_1.expect)(this.conversationHistorySection).not.toContainText(/encrypted_content|camunda\.agenticai\.content\.type|"payload"|signature/);
924
+ await this.assertFinalAnswerVisible(answerMessage);
925
+ }
926
+ async assertFinalAnswerVisible(answerMessage) {
927
+ await (0, test_1.expect)(answerMessage).toHaveAttribute('aria-label', 'Assistant message', { timeout: 60000 });
928
+ // The Thinking note is a <section> named via aria-labelledby, so match
929
+ // only answer text that is not nested inside it.
930
+ const answerOutsideThinking = answerMessage.locator('xpath=.//*[text()[contains(., "Answer:")]][not(ancestor::section[@aria-labelledby = //*[normalize-space(text())="Thinking"]/@id])]');
931
+ await (0, test_1.expect)(answerOutsideThinking.first()).toBeVisible({ timeout: 60000 });
932
+ }
877
933
  async assertActiveTokenIsPresent() {
878
934
  await (0, test_1.expect)(this.activeIcon).toBeVisible({ timeout: 60000 });
879
935
  }
@@ -10,6 +10,7 @@ declare class TaskPanelPage {
10
10
  readonly expandButton: Locator;
11
11
  readonly processesInfoText: Locator;
12
12
  constructor(page: Page);
13
+ waitForTasklistLoaded(timeout?: number): Promise<void>;
13
14
  openTask(name: string, taskKeys?: string[]): Promise<void>;
14
15
  filterBy(option: 'All open' | 'Unassigned' | 'Assigned to me' | 'Completed'): Promise<void>;
15
16
  taskCount(name: string): Promise<number>;
@@ -30,6 +30,27 @@ class TaskPanelPage {
30
30
  });
31
31
  this.processesInfoText = page.getByText('Browse and run processes published by your organization');
32
32
  }
33
+ // Tasklist renders its route error page ("Something went wrong") when its
34
+ // lazily loaded JS chunks 404 -- seen on the AWS cluster when index.html
35
+ // and the asset chunks came from different builds. A reload fetches a
36
+ // consistent shell + chunks; the banner assertion itself is unchanged.
37
+ async waitForTasklistLoaded(timeout = 120000) {
38
+ const errorPage = this.page.getByRole('heading', {
39
+ name: 'Something went wrong',
40
+ });
41
+ const maxReloads = 3;
42
+ for (let attempt = 1; attempt <= maxReloads; attempt++) {
43
+ await (0, test_1.expect)(this.taskListPageBanner.or(errorPage).first()).toBeVisible({
44
+ timeout,
45
+ });
46
+ if (await this.taskListPageBanner.isVisible()) {
47
+ return;
48
+ }
49
+ console.warn(`Tasklist failed to load (attempt ${attempt}/${maxReloads}), reloading`);
50
+ await this.page.reload();
51
+ }
52
+ await (0, test_1.expect)(this.taskListPageBanner).toBeVisible({ timeout });
53
+ }
33
54
  async openTask(name, taskKeys) {
34
55
  let attempts = 0;
35
56
  // Tasklist's importer can lag well behind Zeebe under heavy concurrent
@@ -4,6 +4,7 @@ const _8_10_1 = require("../../fixtures/8.10");
4
4
  const UtilitiesPage_1 = require("../../pages/8.10/UtilitiesPage");
5
5
  const _setup_1 = require("../../test-setup.js");
6
6
  const apiHelpers_1 = require("../../utils/apiHelpers");
7
+ const agentInstanceHistoryHelpers_1 = require("../../utils/agentInstanceHistoryHelpers");
7
8
  const sleep_1 = require("../../utils/sleep");
8
9
  const users_1 = require("../../utils/users");
9
10
  const testUser = (0, users_1.getTestUser)('fourteenthUser');
@@ -62,6 +63,21 @@ const aiAgentSubProcessTestCases = [
62
63
  bpmnPath: './resources/agentic_ai/ai-agent-subprocess-with-tools_aws_key.bpmn',
63
64
  },
64
65
  ];
66
+ const agentReasoningTestCases = [
67
+ {
68
+ name: 'Readable reasoning appears in Operate',
69
+ bpmnPath: './resources/agentic_ai/ai-agent-chat-with-tools_anthropic_bedrock_apikey.bpmn',
70
+ provider: 'anthropic-adaptive',
71
+ reasoningMode: 'readable',
72
+ },
73
+ {
74
+ name: 'Encrypted reasoning stays hidden in Operate',
75
+ bpmnPath: './resources/agentic_ai/ai-agent-chat-with-tools_open_ai.bpmn',
76
+ provider: 'openai-encrypted',
77
+ reasoningMode: 'encrypted-only',
78
+ },
79
+ ];
80
+ const agentReasoningResources = new Map();
65
81
  const regexTestCases = [
66
82
  {
67
83
  regex_ai_agent_time: /The current date and time in ([A-Za-zÀ-ÿ .,'-]+(?:, [A-Za-zÀ-ÿ .,'-]+)*), is approximately ((?:0?[1-9]|1[0-2]):[0-5][0-9] (?:AM|PM)) on ([A-Z][a-z]+ (?:[1-9]|[12][0-9]|3[01]), \d{4})\./,
@@ -205,4 +221,75 @@ _8_10_1.test.describe('Agentic Orchestation User Flows', () => {
205
221
  });
206
222
  }
207
223
  });
224
+ _8_10_1.test.describe('AI Agent reasoning in Operate', () => {
225
+ // Playwright requires destructured fixture arguments.
226
+ // eslint-disable-next-line no-empty-pattern
227
+ _8_10_1.test.afterEach(async ({}, testInfo) => {
228
+ const resources = agentReasoningResources.get(testInfo.testId);
229
+ if (!resources) {
230
+ return;
231
+ }
232
+ const cleanupErrors = await (0, agentInstanceHistoryHelpers_1.cleanupAgentReasoningResources)(resources.processInstanceKey, resources.processDefinitionKey, authToken);
233
+ agentReasoningResources.delete(testInfo.testId);
234
+ if (cleanupErrors.length === 1) {
235
+ throw cleanupErrors[0];
236
+ }
237
+ if (cleanupErrors.length > 1) {
238
+ throw new AggregateError(cleanupErrors, 'Agent reasoning resource cleanup failed');
239
+ }
240
+ });
241
+ for (const params of agentReasoningTestCases) {
242
+ (0, _8_10_1.test)(params.name, async ({ appsPage, operateHomePage, operateProcessInstancePage, operateProcessesPage, taskPanelPage, taskDetailsPage, }, testInfo) => {
243
+ _8_10_1.test.slow();
244
+ const suffix = await (0, _setup_1.generateRandomStringAsync)(6);
245
+ const processId = `ai-agent-reasoning-${params.provider}-${suffix.toLowerCase()}`;
246
+ const processName = `AI Agent ${params.name} ${suffix}`;
247
+ const resources = {};
248
+ agentReasoningResources.set(testInfo.testId, resources);
249
+ const processDefinitionKey = await (0, agentInstanceHistoryHelpers_1.deployAgentReasoningProcess)(params.bpmnPath, authToken, processId, processName, params.provider);
250
+ resources.processDefinitionKey = processDefinitionKey;
251
+ const processInstanceKey = await (0, apiHelpers_1.createProcessInstance)(processDefinitionKey, authToken);
252
+ resources.processInstanceKey = processInstanceKey;
253
+ await _8_10_1.test.step('Wait for the agent feedback task and committed history', async () => {
254
+ await appsPage.clickCamundaApps();
255
+ await appsPage.clickTasklist(clusterName);
256
+ await taskPanelPage.openTask(processName);
257
+ await taskDetailsPage.clickAssignToMeButton();
258
+ await taskDetailsPage.assertAIResponse(/Answer:/);
259
+ });
260
+ const historyKeys = await (0, agentInstanceHistoryHelpers_1.waitForAgentReasoningHistory)(processInstanceKey, authToken, params.reasoningMode);
261
+ await _8_10_1.test.step('Verify the conversation in Operate', async () => {
262
+ await appsPage.clickCamundaApps();
263
+ await appsPage.clickOperate(clusterName);
264
+ await operateHomePage.clickProcessesTab();
265
+ await operateProcessesPage.applyMoreFilters('Process Instance Key(s)', processInstanceKey);
266
+ await operateProcessesPage.clickProcessInstanceLink(processName);
267
+ await operateProcessInstancePage.closePopOverIfVisible();
268
+ await operateProcessInstancePage.assertActiveTokenIsPresent();
269
+ await operateProcessInstancePage.openAgentConversation('AI_Agent');
270
+ if (params.reasoningMode === 'readable') {
271
+ await operateProcessInstancePage.assertReadableReasoning(historyKeys.reasoningHistoryItemKey, historyKeys.answerHistoryItemKey);
272
+ }
273
+ else {
274
+ await operateProcessInstancePage.assertEncryptedReasoningIsHidden(historyKeys.reasoningHistoryItemKey, historyKeys.answerHistoryItemKey);
275
+ }
276
+ });
277
+ await _8_10_1.test.step('Complete the feedback task and finish the process', async () => {
278
+ await appsPage.clickCamundaApps();
279
+ await appsPage.clickTasklist(clusterName);
280
+ await taskPanelPage.openTask(processName);
281
+ await taskDetailsPage.checkChecklistBox('Are you satisfied with the result?');
282
+ await taskDetailsPage.clickCompleteTaskButton();
283
+ await appsPage.clickCamundaApps();
284
+ await appsPage.clickOperate(clusterName);
285
+ await operateHomePage.clickProcessesTab();
286
+ await operateProcessesPage.clickProcessCompletedCheckbox();
287
+ await operateProcessesPage.applyMoreFilters('Process Instance Key(s)', processInstanceKey);
288
+ await operateProcessesPage.clickProcessInstanceLink(processName);
289
+ await operateProcessInstancePage.assertProcessCompleteStatusWithRetry();
290
+ resources.processInstanceKey = undefined;
291
+ });
292
+ });
293
+ }
294
+ });
208
295
  });
@@ -94,9 +94,7 @@ _8_8_1.test.describe('AWS Cluster User Flows Test', () => {
94
94
  await _8_8_1.test.step('Navigate to Tasklist and Assert Document Upload And Preview', async () => {
95
95
  await appsPage.clickCamundaApps();
96
96
  await appsPage.clickTasklist(clusterName);
97
- await (0, test_1.expect)(taskPanelPage.taskListPageBanner).toBeVisible({
98
- timeout: 120000,
99
- });
97
+ await taskPanelPage.waitForTasklistLoaded();
100
98
  await taskPanelPage.openTask(processName);
101
99
  await taskDetailsPage.clickAssignToMeButton();
102
100
  await (0, fileUpload_1.uploadFile)(page, [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.1458",
3
+ "version": "0.0.1460",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",