@camunda/e2e-test-suite 0.0.1459 → 0.0.1461
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/README.md
CHANGED
|
@@ -498,9 +498,13 @@ By following this convention, we ensure **clarity, consistency, and alignment wi
|
|
|
498
498
|
|
|
499
499
|
| Directory Name | Meaning |
|
|
500
500
|
| ----------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
501
|
+
| `camunda-platform-8.11-latest` | Runs SM tests against the latest 8.11 SNAPSHOT |
|
|
502
|
+
| `camunda-platform-8.10-latest` | Runs SM tests against the latest 8.10 SNAPSHOT |
|
|
501
503
|
| `camunda-platform-8.9-latest` | Runs SM tests against the latest 8.9 SNAPSHOT |
|
|
502
504
|
| `camunda-platform-8.8-latest` | Runs SM tests against the latest 8.8 SNAPSHOT |
|
|
503
505
|
| `camunda-platform-8.7-latest` | Runs SM tests against the latest 8.7 SNAPSHOT |
|
|
506
|
+
| `camunda-platform-8.11` | Runs SM tests against the 8.11 directory. This is where the `release-candidate-8.11` branch is located |
|
|
507
|
+
| `camunda-platform-8.10` | Runs SM tests against the 8.10 directory. This is where the `release-candidate-8.10` branch is located |
|
|
504
508
|
| `camunda-platform-8.9` | Runs SM tests against the 8.9 directory. This is where the `release-candidate-8.9` branch is located |
|
|
505
509
|
| `camunda-platform-8.8` | Runs SM tests against the 8.8 directory. This is where the `release-candidate-8.8` branch is located |
|
|
506
510
|
| `camunda-platform-8.7` | Runs SM tests against the 8.7 directory. This is where the `release-candidate-8.7` branch is located |
|
|
@@ -584,26 +588,25 @@ This applies to all changes that touch test specs, page objects, fixtures, or ut
|
|
|
584
588
|
|
|
585
589
|
The [SaaS/SM/C8Run PR](https://github.com/camunda/c8-cross-component-e2e-tests/actions/workflows/playwright.yml) workflow runs automatically on every pull request. It detects which `tests/` and `pages/` sub-directories have changed and runs the tests for those versions/platforms.
|
|
586
590
|
|
|
587
|
-
|
|
591
|
+
Every changed version gets its own run, so a PR can span several versions and platforms. Examples:
|
|
588
592
|
|
|
589
593
|
- Only `tests/8.10/` and/or `pages/8.10/` are changed → 8.10 SaaS tests run.
|
|
590
594
|
- `tests/8.9/` and `tests/8.10/` are changed → 8.9 and 8.10 SaaS tests run, each in its own organization.
|
|
591
|
-
-
|
|
595
|
+
- `tests/SM-8.8/` and `tests/SM-8.10/` are changed → one Helm install and test run for each SM version.
|
|
596
|
+
- `tests/c8Run-8.8/` and `tests/c8Run-8.10/` are changed → one c8Run build for each version and Tasklist project.
|
|
597
|
+
- `tests/8.10/` and `tests/SM-8.10/` are changed → the SaaS and the SM runs both start.
|
|
592
598
|
|
|
593
599
|
If no `tests/` or `pages/` folders are changed (e.g. infrastructure or tooling changes), the test run is skipped automatically — state this explicitly in the PR description.
|
|
594
600
|
|
|
595
601
|
#### When you must trigger tests manually
|
|
596
602
|
|
|
597
|
-
|
|
603
|
+
The automatic PR workflow covers every changed version and platform. The table below says where you still need an on-demand workflow. Test coverage is not a reason to keep a multi-version PR together: still split large changes for review (see [Multi-version PRs](#multi-version-prs) above).
|
|
598
604
|
|
|
599
605
|
| Scenario | Action required |
|
|
600
606
|
|---|---|
|
|
601
|
-
|
|
|
602
|
-
| Multiple SaaS versions changed (e.g. `8.9` + `8.8`) | Wait for the automatic PR workflow — it runs each SaaS version in its own organization |
|
|
603
|
-
| Multiple SM versions changed (e.g. `SM-8.9` + `SM-8.8`) | Trigger [SM On-Demand ≥8.8](https://github.com/camunda/c8-cross-component-e2e-tests/actions/workflows/playwright_sm_manual_install_8_8_plus.yml) or [SM On-Demand ≤8.7](https://github.com/camunda/c8-cross-component-e2e-tests/actions/workflows/playwright_sm_manual.yml) separately for each version, **or split the PR** |
|
|
604
|
-
| SaaS + SM versions changed together (e.g. `8.9` + `SM-8.8`) | Trigger both the SaaS and SM on-demand workflows, **or split the PR** |
|
|
607
|
+
| One or more versions changed, on any platforms (e.g. `8.9` + `8.10`, `SM-8.8` + `SM-8.10`, `8.9` + `SM-8.8`) | Wait for the automatic PR workflow |
|
|
605
608
|
| Connector tests | [SaaS Connectors](https://github.com/camunda/c8-cross-component-e2e-tests/actions/workflows/playwright_saas_connectors.yml) or the relevant SM connector workflow |
|
|
606
|
-
| C8 Run tests (`tests/c8Run-8.x/`) | [C8Run On-Demand
|
|
609
|
+
| C8 Run tests (`tests/c8Run-8.x/`) on macOS or Windows | [C8Run On-Demand Mac](https://github.com/camunda/c8-cross-component-e2e-tests/actions/workflows/playwright_c8Run_tests_manual_mac.yml) or [C8Run On-Demand Windows](https://github.com/camunda/c8-cross-component-e2e-tests/actions/workflows/playwright_c8Run_tests_manual_windows.yml) — the automatic PR workflow runs c8Run on Linux only |
|
|
607
610
|
|
|
608
611
|
#### Steps
|
|
609
612
|
|
|
@@ -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
|
}
|
|
@@ -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
|
});
|