@camunda/e2e-test-suite 0.0.882 → 0.0.884
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.7/ModelerCreatePage.js +13 -13
- package/dist/tests/c8Run-8.9/connectors-user-flows.spec.js +8 -3
- package/dist/tests/c8Run-8.9/mcp-connector-authentication.spec.js +6 -1
- package/dist/tests/c8Run-8.9/mcp-user-flows.spec.js +5 -1
- package/dist/utils/apiHelpers.d.ts +2 -1
- package/dist/utils/apiHelpers.js +61 -2
- package/package.json +1 -1
|
@@ -118,7 +118,11 @@ class ModelerCreatePage {
|
|
|
118
118
|
name: 'Change element',
|
|
119
119
|
exact: true,
|
|
120
120
|
});
|
|
121
|
-
|
|
121
|
+
// The change-element popup lists the project's deployed resources next to
|
|
122
|
+
// the built-in element types, so a role+name match on 'User task' is
|
|
123
|
+
// ambiguous whenever a diagram name contains it (e.g. "Optimize Job Worker
|
|
124
|
+
// User Task Diagram"). Target the built-in entry by its stable data-id.
|
|
125
|
+
this.userTaskOption = page.locator('.djs-popup-results [data-id="replace-with-user-task"]');
|
|
122
126
|
this.appendEndEventButton = page.getByRole('button', {
|
|
123
127
|
name: 'Append end event',
|
|
124
128
|
});
|
|
@@ -146,10 +150,11 @@ class ModelerCreatePage {
|
|
|
146
150
|
this.deploySubButton = page.locator('[data-test="deploy-action"]');
|
|
147
151
|
this.cancelButton = page.getByRole('button', { name: 'Cancel' });
|
|
148
152
|
this.restConnectorOption = page.locator('[data-id="replace.template-io.camunda.connectors.HttpJson.v2"]');
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
+
// Change-element popup entries are `role="option"` under
|
|
154
|
+
// `.djs-popup-results`, and their accessible name now includes the entry
|
|
155
|
+
// description, so no role+name matcher resolves them. Target the stable
|
|
156
|
+
// element-template data-id instead (same selector the hub e2e suite uses).
|
|
157
|
+
this.webhookMessageStartEventConnectorOption = page.locator('.djs-popup-results [data-id="replace.template-io.camunda.connectors.webhook.WebhookConnectorStartMessage.v1"]');
|
|
153
158
|
this.webhookTab = page.getByRole('tab', { name: 'Webhook' });
|
|
154
159
|
this.webhookIsActiveButton = page.getByRole('button', {
|
|
155
160
|
name: 'Webhook is active in Test',
|
|
@@ -178,9 +183,7 @@ class ModelerCreatePage {
|
|
|
178
183
|
this.intermediateBoundaryEvent = page.getByRole('button', {
|
|
179
184
|
name: 'Append intermediate/boundary',
|
|
180
185
|
});
|
|
181
|
-
this.intermediateWebhookConnectorOption = page.
|
|
182
|
-
name: 'Webhook Intermediate Event',
|
|
183
|
-
});
|
|
186
|
+
this.intermediateWebhookConnectorOption = page.locator('.djs-popup-results [data-id="replace.template-io.camunda.connectors.webhook.WebhookConnectorIntermediate.v1"]');
|
|
184
187
|
this.correlationKeyPayloadInput = page.getByLabel('Correlation key (payload)');
|
|
185
188
|
this.correlationKeyProcessInput = page.getByLabel('Correlation key (process)');
|
|
186
189
|
this.form = page.locator('[class="fjs-container"]');
|
|
@@ -214,7 +217,7 @@ class ModelerCreatePage {
|
|
|
214
217
|
this.secondPlacedGateway = page
|
|
215
218
|
.locator('[data-element-id*="Gateway"]')
|
|
216
219
|
.last();
|
|
217
|
-
this.serviceTaskOption = page.
|
|
220
|
+
this.serviceTaskOption = page.locator('.djs-popup-results [data-id="replace-with-service-task"]');
|
|
218
221
|
this.taskDefinitionPanel = page.locator('[data-group-id="group-taskDefinition"]');
|
|
219
222
|
this.jobTypeInput = page.getByRole('textbox', { name: /job type/i });
|
|
220
223
|
this.playTab = page.getByRole('tab', {
|
|
@@ -256,10 +259,7 @@ class ModelerCreatePage {
|
|
|
256
259
|
.getByText('zeebeClientId');
|
|
257
260
|
this.variableAssignmentValueTextboxThirdVariable = page.locator('#bio-properties-panel-StartEvent_1-output-2-source div');
|
|
258
261
|
this.connectorsDocHandlingStartEventElement = page.locator('g:nth-child(5) > .djs-element > .djs-hit');
|
|
259
|
-
this.timerEventOption = page.
|
|
260
|
-
name: 'Timer intermediate catch event',
|
|
261
|
-
exact: true,
|
|
262
|
-
});
|
|
262
|
+
this.timerEventOption = page.locator('.djs-popup-results [data-id="replace-with-timer-intermediate-catch"]');
|
|
263
263
|
this.timerEventSettings = page.getByText(/^Timer$/).first();
|
|
264
264
|
this.timerType = page.getByLabel('Type');
|
|
265
265
|
this.timerValue = page.getByLabel('Value');
|
|
@@ -11,8 +11,13 @@ const webhookAssertionOptions = {
|
|
|
11
11
|
intervals: [10000, 15000, 30000, 30000],
|
|
12
12
|
timeout: 120000,
|
|
13
13
|
};
|
|
14
|
+
// The REST connector tasks in this suite are all io.camunda:http-json:1, so the
|
|
15
|
+
// suite cannot pass unless that worker is subscribed. Requiring it here turns a
|
|
16
|
+
// half-started connectors runtime into one fast, explicit setup failure instead
|
|
17
|
+
// of five tests independently timing out after 12 minutes each.
|
|
18
|
+
const requiredConnectorJobTypes = ['io.camunda:http-json:1'];
|
|
14
19
|
c8Run_8_9_1.test.beforeAll(async () => {
|
|
15
|
-
await (0, apiHelpers_1.waitForConnectorsReady)();
|
|
20
|
+
await (0, apiHelpers_1.waitForConnectorsReady)(undefined, 900000, requiredConnectorJobTypes);
|
|
16
21
|
await Promise.all([
|
|
17
22
|
(0, zeebeClient_1.deploy)('./resources/Basic_Auth_REST_Connector.bpmn'),
|
|
18
23
|
(0, zeebeClient_1.deploy)('./resources/Intermediate_Event_Webhook_Connector_No_Auth_Process.bpmn'),
|
|
@@ -77,7 +82,7 @@ c8Run_8_9_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
77
82
|
});
|
|
78
83
|
(0, c8Run_8_9_1.test)('Start Event Webhook Connector No Auth User Flow', async ({ page, request, operateHomePage, operateProcessInstancePage, operateProcessesPage, }) => {
|
|
79
84
|
await c8Run_8_9_1.test.step('Make Authorization Request', async () => {
|
|
80
|
-
await (0,
|
|
85
|
+
await (0, apiHelpers_1.waitForInboundWebhookRegistered)('test-webhook-id');
|
|
81
86
|
await (0, test_1.expect)(async () => {
|
|
82
87
|
const response = await request.get(process.env.C8RUN_CONNECTORS_API_URL_LATEST +
|
|
83
88
|
'/inbound/test-webhook-id');
|
|
@@ -95,7 +100,7 @@ c8Run_8_9_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
95
100
|
});
|
|
96
101
|
(0, c8Run_8_9_1.test)('Intermediate Event Webhook Connector No Auth User Flow', async ({ request, operateHomePage, operateProcessInstancePage, operateProcessesPage, }) => {
|
|
97
102
|
await c8Run_8_9_1.test.step('Make Authorization Request', async () => {
|
|
98
|
-
await (0,
|
|
103
|
+
await (0, apiHelpers_1.waitForInboundWebhookRegistered)('test-webhook-intermediate');
|
|
99
104
|
await (0, test_1.expect)(async () => {
|
|
100
105
|
const response = await request.post(process.env.C8RUN_CONNECTORS_API_URL_LATEST +
|
|
101
106
|
'/inbound/test-webhook-intermediate', {
|
|
@@ -31,7 +31,12 @@ c8Run_8_9_1.test.beforeAll(async () => {
|
|
|
31
31
|
await (0, apiHelpers_1.validateMcpServerHealth)('http://127.0.0.1:12001'); // No auth
|
|
32
32
|
await (0, apiHelpers_1.validateMcpServerHealth)('http://127.0.0.1:12002'); // Basic auth
|
|
33
33
|
await (0, apiHelpers_1.validateMcpServerHealth)('http://127.0.0.1:12004'); // OAuth
|
|
34
|
-
|
|
34
|
+
// Every process in this suite drives io.camunda.agenticai:mcpremoteclient:1,
|
|
35
|
+
// so gate on that worker actually being subscribed rather than on the runtime
|
|
36
|
+
// merely reporting UP.
|
|
37
|
+
await (0, apiHelpers_1.waitForConnectorsReady)(undefined, 600000, [
|
|
38
|
+
'io.camunda.agenticai:mcpremoteclient:1',
|
|
39
|
+
]);
|
|
35
40
|
await (0, keycloakHelpers_1.validateKeycloakHealth)();
|
|
36
41
|
bearerToken = await (0, keycloakHelpers_1.getAccessToken)();
|
|
37
42
|
await (0, zeebeClient_1.deploy)([
|
|
@@ -12,7 +12,11 @@ c8Run_8_9_1.test.beforeAll(async () => {
|
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
14
|
await (0, apiHelpers_1.validateMcpServerHealth)();
|
|
15
|
-
|
|
15
|
+
// The MCP client connector task cannot complete unless this worker is
|
|
16
|
+
// subscribed, so require it instead of only a healthy runtime.
|
|
17
|
+
await (0, apiHelpers_1.waitForConnectorsReady)(undefined, 600000, [
|
|
18
|
+
'io.camunda.agenticai:mcpremoteclient:1',
|
|
19
|
+
]);
|
|
16
20
|
await (0, zeebeClient_1.deploy)(['./resources/mcp_server/mcp_remote_client_operations.bpmn']);
|
|
17
21
|
await (0, zeebeClient_1.createInstances)('mcp_remote_client', 1, 1);
|
|
18
22
|
await (0, sleep_1.sleep)(2000);
|
|
@@ -16,7 +16,8 @@ export declare function authSaasAPI(audience?: string, clusterType?: string): Pr
|
|
|
16
16
|
export declare function authSmAPI(): Promise<string>;
|
|
17
17
|
export declare function authC8runAPI(name: string, password: string): Promise<void>;
|
|
18
18
|
export declare function validateMcpServerHealth(serverUrl?: string): Promise<APIResponse>;
|
|
19
|
-
export declare function waitForConnectorsReady(baseUrl?: string | undefined, timeoutMs?: number): Promise<void>;
|
|
19
|
+
export declare function waitForConnectorsReady(baseUrl?: string | undefined, timeoutMs?: number, requiredJobTypes?: string[]): Promise<void>;
|
|
20
|
+
export declare function waitForInboundWebhookRegistered(webhookPath: string, baseUrl?: string | undefined, timeoutMs?: number): Promise<void>;
|
|
20
21
|
export declare function deployProcess(filePath: string, authToken?: string, environment?: 'saas' | 'sm', clusterType?: string): Promise<number | null>;
|
|
21
22
|
export declare function createProcessInstance(processDefinitionKey: string, authToken?: string, environment?: 'saas' | 'sm', clusterType?: string, variables?: Record<string, unknown>): Promise<string>;
|
|
22
23
|
export declare function waitForProcessInstanceVisible(processInstanceKey: string, authToken?: string, environment?: 'saas' | 'sm', clusterType?: string, maxAttempts?: number, retryDelayMs?: number): Promise<void>;
|
package/dist/utils/apiHelpers.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.updateClusterDataFilters = exports.getClusterDataFilters = exports.getTestClusterUuid = exports.authConsoleManagementAPI = exports.updateCollectionScope = exports.createSingleProcessReport = exports.createDashboard = exports.createCollection = exports.getOptimizeCookieSm = exports.getOptimizeCoockie = exports.createStringClusterVariable = exports.createJsonClusterVariable = exports.waitForProcessInstanceVisible = exports.createProcessInstance = exports.deployProcess = exports.waitForConnectorsReady = exports.validateMcpServerHealth = exports.authC8runAPI = exports.authSmAPI = exports.authSaasAPI = exports.authAPI = exports.buildZeebeApiUrl = exports.retryOn500 = exports.sendRequestAndAssertResponse = exports.assertUnauthorizedResponseBody = exports.expectUnauthorizedErrorBody = exports.assertResponseStatus = exports.getApiRequestContext = void 0;
|
|
6
|
+
exports.updateClusterDataFilters = exports.getClusterDataFilters = exports.getTestClusterUuid = exports.authConsoleManagementAPI = exports.updateCollectionScope = exports.createSingleProcessReport = exports.createDashboard = exports.createCollection = exports.getOptimizeCookieSm = exports.getOptimizeCoockie = exports.createStringClusterVariable = exports.createJsonClusterVariable = exports.waitForProcessInstanceVisible = exports.createProcessInstance = exports.deployProcess = exports.waitForInboundWebhookRegistered = exports.waitForConnectorsReady = exports.validateMcpServerHealth = exports.authC8runAPI = exports.authSmAPI = exports.authSaasAPI = exports.authAPI = exports.buildZeebeApiUrl = exports.retryOn500 = exports.sendRequestAndAssertResponse = exports.assertUnauthorizedResponseBody = exports.expectUnauthorizedErrorBody = exports.assertResponseStatus = exports.getApiRequestContext = void 0;
|
|
7
7
|
const test_1 = require("@playwright/test");
|
|
8
8
|
const sleep_1 = require("./sleep");
|
|
9
9
|
const fs_1 = __importDefault(require("fs"));
|
|
@@ -316,7 +316,7 @@ exports.validateMcpServerHealth = validateMcpServerHealth;
|
|
|
316
316
|
// Guards against the c8Run startup race where the connectors runtime on :8086
|
|
317
317
|
// comes up later than Zeebe/Operate, leaving process instances stuck "active"
|
|
318
318
|
// and webhook endpoints unregistered.
|
|
319
|
-
async function waitForConnectorsReady(baseUrl = process.env.C8RUN_CONNECTORS_API_URL_LATEST, timeoutMs = 300000) {
|
|
319
|
+
async function waitForConnectorsReady(baseUrl = process.env.C8RUN_CONNECTORS_API_URL_LATEST, timeoutMs = 300000, requiredJobTypes = []) {
|
|
320
320
|
if (!baseUrl) {
|
|
321
321
|
throw new Error('waitForConnectorsReady: missing connectors API URL ' +
|
|
322
322
|
'(C8RUN_CONNECTORS_API_URL_LATEST).');
|
|
@@ -343,11 +343,70 @@ async function waitForConnectorsReady(baseUrl = process.env.C8RUN_CONNECTORS_API
|
|
|
343
343
|
consecutiveHealthy = 0;
|
|
344
344
|
throw new Error(`connectors runtime health returned ${response.status()}`);
|
|
345
345
|
}
|
|
346
|
+
if (requiredJobTypes.length > 0) {
|
|
347
|
+
const registered = await registeredConnectorJobTypes(baseUrl);
|
|
348
|
+
const missing = requiredJobTypes.filter((type) => !registered.has(type));
|
|
349
|
+
if (missing.length > 0) {
|
|
350
|
+
consecutiveHealthy = 0;
|
|
351
|
+
throw new Error('connectors runtime has not subscribed job workers for: ' +
|
|
352
|
+
missing.join(', '));
|
|
353
|
+
}
|
|
354
|
+
}
|
|
346
355
|
consecutiveHealthy += 1;
|
|
347
356
|
(0, test_1.expect)(consecutiveHealthy).toBeGreaterThanOrEqual(requiredConsecutive);
|
|
348
357
|
}).toPass({ intervals: [5000, 10000, 15000], timeout: timeoutMs });
|
|
349
358
|
}
|
|
350
359
|
exports.waitForConnectorsReady = waitForConnectorsReady;
|
|
360
|
+
// `/actuator/health` flips to UP as soon as the web layer is serving and the
|
|
361
|
+
// Zeebe topology is reachable — it says nothing about whether the runtime
|
|
362
|
+
// actually subscribed its connector job workers. A runtime that started while
|
|
363
|
+
// the broker was still installing its partition can sit at UP indefinitely with
|
|
364
|
+
// no worker subscribed, which leaves every connector task active and inbound
|
|
365
|
+
// webhooks unregistered. The runtime publishes one
|
|
366
|
+
// camunda_client_worker_job_activated_total series per subscribed worker type,
|
|
367
|
+
// so the presence of that series is the real readiness signal.
|
|
368
|
+
async function registeredConnectorJobTypes(baseUrl) {
|
|
369
|
+
const response = await apiRequestContext.get(`${baseUrl}/actuator/prometheus`, { timeout: 15000 });
|
|
370
|
+
if (response.status() !== 200) {
|
|
371
|
+
throw new Error(`connectors runtime metrics returned ${response.status()}`);
|
|
372
|
+
}
|
|
373
|
+
const metrics = await response.text();
|
|
374
|
+
const pattern = /camunda_client_worker_job_activated_total\{type="([^"]+)"\}/g;
|
|
375
|
+
const types = new Set();
|
|
376
|
+
let match;
|
|
377
|
+
while ((match = pattern.exec(metrics)) !== null) {
|
|
378
|
+
types.add(match[1]);
|
|
379
|
+
}
|
|
380
|
+
return types;
|
|
381
|
+
}
|
|
382
|
+
// Waits until the connectors runtime reports a healthy inbound subscription for
|
|
383
|
+
// `webhookPath`. Registration happens asynchronously after deployment, so
|
|
384
|
+
// calling the endpoint before it lands returns 404 for a start-event webhook and
|
|
385
|
+
// 500 for an intermediate-event one — exactly what the nightly observed. Polling
|
|
386
|
+
// the runtime's own registration list makes that a checked precondition instead
|
|
387
|
+
// of a fixed sleep that silently expires.
|
|
388
|
+
async function waitForInboundWebhookRegistered(webhookPath, baseUrl = process.env.C8RUN_CONNECTORS_API_URL_LATEST, timeoutMs = 300000) {
|
|
389
|
+
if (!baseUrl) {
|
|
390
|
+
throw new Error('waitForInboundWebhookRegistered: missing connectors API URL ' +
|
|
391
|
+
'(C8RUN_CONNECTORS_API_URL_LATEST).');
|
|
392
|
+
}
|
|
393
|
+
apiRequestContext = await getApiRequestContext();
|
|
394
|
+
await (0, test_1.expect)(async () => {
|
|
395
|
+
const response = await apiRequestContext.get(`${baseUrl}/inbound`, {
|
|
396
|
+
timeout: 15000,
|
|
397
|
+
});
|
|
398
|
+
if (response.status() !== 200) {
|
|
399
|
+
throw new Error(`connectors runtime /inbound returned ${response.status()}`);
|
|
400
|
+
}
|
|
401
|
+
const registrations = (await response.json());
|
|
402
|
+
const registered = registrations.some((registration) => registration.data?.path === webhookPath &&
|
|
403
|
+
registration.health?.status === 'UP');
|
|
404
|
+
if (!registered) {
|
|
405
|
+
throw new Error(`inbound webhook '${webhookPath}' is not registered and healthy yet`);
|
|
406
|
+
}
|
|
407
|
+
}).toPass({ intervals: [5000, 10000, 15000], timeout: timeoutMs });
|
|
408
|
+
}
|
|
409
|
+
exports.waitForInboundWebhookRegistered = waitForInboundWebhookRegistered;
|
|
351
410
|
// ---- Zeebe: API helpers ----
|
|
352
411
|
async function deployProcess(filePath, authToken, environment, clusterType) {
|
|
353
412
|
try {
|