@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.
- package/dist/pages/8.10/OperateProcessInstancePage.d.ts +8 -0
- package/dist/pages/8.10/OperateProcessInstancePage.js +56 -0
- package/dist/pages/8.8/TaskPanelPage.d.ts +1 -0
- package/dist/pages/8.8/TaskPanelPage.js +21 -0
- package/dist/tests/8.10/agentic-ai-user-flows.spec.js +87 -0
- package/dist/tests/8.8/aws-cluster-user-flows.spec.js +1 -3
- package/package.json +1 -1
|
@@ -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
|
|
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, [
|