@camunda/e2e-test-suite 0.0.1470 → 0.0.1471

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.
@@ -10,7 +10,8 @@ declare class BrowseAllConnectorMarketplacePage {
10
10
  constructor(page: Page);
11
11
  isOpen(): Promise<boolean>;
12
12
  waitForConnectorSearchResult(connectorName?: string): Promise<Locator>;
13
- importConnectorToProject(connectorName?: string): Promise<boolean>;
13
+ importConnectorToProject(connectorName?: string, failOnImportError?: boolean): Promise<boolean>;
14
+ private completeImportWithoutRetry;
14
15
  private waitForInspectionReady;
15
16
  private waitForAnyVisible;
16
17
  private completeImportReviewIfNecessary;
@@ -49,7 +49,7 @@ class BrowseAllConnectorMarketplacePage {
49
49
  // Resolves to whether an "Add" click actually happened. It is false when
50
50
  // the project already had the connector ("Added to this project"), in which
51
51
  // case the import write path was never exercised.
52
- async importConnectorToProject(connectorName) {
52
+ async importConnectorToProject(connectorName, failOnImportError = false) {
53
53
  const connectorCard = await this.waitForConnectorSearchResult(connectorName);
54
54
  await connectorCard.click({ timeout: 60000 });
55
55
  const browseAllDialog = this.page.getByRole('dialog').filter({
@@ -78,6 +78,10 @@ class BrowseAllConnectorMarketplacePage {
78
78
  }
79
79
  await addButton.click({ timeout: 60000 });
80
80
  addClicked = true;
81
+ if (failOnImportError) {
82
+ await this.completeImportWithoutRetry(addedStatus, retryButton);
83
+ break;
84
+ }
81
85
  if (await this.completeImportReviewIfNecessary(addButton, retryButton, addedStatus)) {
82
86
  break;
83
87
  }
@@ -92,6 +96,44 @@ class BrowseAllConnectorMarketplacePage {
92
96
  await browseAllDialog.waitFor({ state: 'hidden', timeout: 60000 });
93
97
  return addClicked;
94
98
  }
99
+ // Unlike the legacy flow, import regression coverage must not recover from
100
+ // a rejected write or mistake the always-visible heading for completion.
101
+ async completeImportWithoutRetry(addedStatus, retryButton) {
102
+ const importError = this.page.getByText(/couldn.t add this connector/i);
103
+ const waitForState = async (allowReview) => {
104
+ let state = '';
105
+ await test_1.expect
106
+ .poll(async () => {
107
+ if ((await importError.isVisible()) ||
108
+ (await retryButton.isVisible())) {
109
+ state = 'error';
110
+ }
111
+ else if (await addedStatus.isVisible()) {
112
+ state = 'added';
113
+ }
114
+ else if (allowReview && (await this.importModal.isVisible())) {
115
+ state = 'review';
116
+ }
117
+ return state;
118
+ }, { timeout: 120000, intervals: [100, 250, 500] })
119
+ .not.toBe('');
120
+ if (state === 'error') {
121
+ throw new Error('Hub rejected the Marketplace connector import.');
122
+ }
123
+ return state;
124
+ };
125
+ if ((await waitForState(true)) === 'review') {
126
+ await this.importModal
127
+ .getByRole('button', {
128
+ name: /^(Add to project|Add to workspace|Replace resource|Save as copy)$/,
129
+ })
130
+ .click({ timeout: 60000 });
131
+ await waitForState(false);
132
+ await (0, test_1.expect)(this.importModal).toBeHidden({ timeout: 30000 });
133
+ }
134
+ await (0, test_1.expect)(addedStatus).toBeVisible({ timeout: 30000 });
135
+ await (0, test_1.expect)(importError).toBeHidden();
136
+ }
95
137
  async waitForInspectionReady(addButton, addedStatus, retryButton) {
96
138
  const checkingStatus = this.marketplaceDetailFooter.getByText('Checking this project…', { exact: true });
97
139
  await (0, test_1.expect)(async () => {
@@ -19,7 +19,8 @@ declare class ConnectorMarketplacePage {
19
19
  clickCancelButton(): Promise<void>;
20
20
  clickReplaceResourceButton(): Promise<void>;
21
21
  waitForConnectorSearchResults(): Promise<void>;
22
- downloadConnectorToProject(): Promise<void>;
22
+ importConnectorAndAssertNoImportError(): Promise<boolean>;
23
+ downloadConnectorToProject(failOnImportError?: boolean): Promise<boolean>;
23
24
  private confirmImport;
24
25
  private waitForImportToSettle;
25
26
  private getLegacyDownloadButton;
@@ -90,14 +90,35 @@ class ConnectorMarketplacePage {
90
90
  }
91
91
  throw new Error('Marketplace connector search failed after retries due to persistent error or no matching results');
92
92
  }
93
- async downloadConnectorToProject() {
93
+ async importConnectorAndAssertNoImportError() {
94
+ let rejectedWrite;
95
+ const observeWrite = (response) => {
96
+ if (response.request().method() === 'POST' &&
97
+ response.url().includes('/internal/files') &&
98
+ !response.ok()) {
99
+ rejectedWrite = `Connector import returned HTTP ${response.status()}.`;
100
+ }
101
+ };
102
+ this.page.on('response', observeWrite);
103
+ try {
104
+ const imported = await this.downloadConnectorToProject(true);
105
+ if (rejectedWrite) {
106
+ throw new Error(rejectedWrite);
107
+ }
108
+ await (0, test_1.expect)(this.page.getByText(/couldn.t add this connector/i)).toBeHidden();
109
+ return imported;
110
+ }
111
+ finally {
112
+ this.page.off('response', observeWrite);
113
+ }
114
+ }
115
+ async downloadConnectorToProject(failOnImportError = false) {
94
116
  if (await this.browseAllConnectorMarketplacePage.isOpen()) {
95
- await this.browseAllConnectorMarketplacePage.importConnectorToProject(this.connectorName);
96
- return;
117
+ return await this.browseAllConnectorMarketplacePage.importConnectorToProject(this.connectorName, failOnImportError);
97
118
  }
98
119
  await this.clickDownloadToProjectButton();
99
120
  if (await this.confirmImport()) {
100
- return;
121
+ return true;
101
122
  }
102
123
  // The modal is up but rendered none of the known import actions. Dismiss it
103
124
  // so it does not block the next click on the diagram, then fail here:
@@ -111,6 +132,7 @@ class ConnectorMarketplacePage {
111
132
  // Hub imported without asking for confirmation, so the write is in flight
112
133
  // here too.
113
134
  await this.waitForImportToSettle();
135
+ return true;
114
136
  }
115
137
  // Hub's import modal labels its primary button after the project state:
116
138
  // "Add to project" when the template is new to the project, "Replace
@@ -38,6 +38,10 @@ declare class ModelerCreatePage {
38
38
  readonly restConnectorOption: Locator;
39
39
  readonly changeElementSearchInput: Locator;
40
40
  readonly changeElementResults: Locator;
41
+ readonly aiAgentTaskConnectorOption: Locator;
42
+ readonly aiAgentSubProcessConnectorOption: Locator;
43
+ readonly adHocSubProcessOption: Locator;
44
+ readonly propertiesPanel: Locator;
41
45
  readonly reusableAssetsTab: Locator;
42
46
  readonly marketPlaceButton: Locator;
43
47
  readonly clientIdTextbox: Locator;
@@ -125,6 +129,7 @@ declare class ModelerCreatePage {
125
129
  clickAppendTaskButton(): Promise<void>;
126
130
  private padHoverTargets;
127
131
  hoverOnLocator(locator: Locator, selector?: string): Promise<void>;
132
+ appendTaskToStartEvent(): Promise<string>;
128
133
  clickAppendGatewayButton(): Promise<void>;
129
134
  setEngineVersion(version: string): Promise<void>;
130
135
  clickChangeTypeButton(): Promise<void>;
@@ -167,6 +172,10 @@ declare class ModelerCreatePage {
167
172
  revealChangeElementTemplate(option: Locator, searchTerm: string): Promise<boolean>;
168
173
  private isVisibleWithin;
169
174
  private openChangeElementPopup;
175
+ openChangeElementPopupForElement(elementId: string): Promise<void>;
176
+ assertChangeElementTemplateListed(option: Locator, searchTerm: string, elementId: string): Promise<void>;
177
+ changeElementToAdHocSubProcess(elementId: string): Promise<void>;
178
+ applyAiAgentSubProcessTemplate(): Promise<void>;
170
179
  private clickChangeElementTemplateOption;
171
180
  clickWebhookMessageStartEventConnectorOption(): Promise<void>;
172
181
  clickWebhookIdInput(): Promise<void>;
@@ -45,6 +45,10 @@ class ModelerCreatePage {
45
45
  restConnectorOption;
46
46
  changeElementSearchInput;
47
47
  changeElementResults;
48
+ aiAgentTaskConnectorOption;
49
+ aiAgentSubProcessConnectorOption;
50
+ adHocSubProcessOption;
51
+ propertiesPanel;
48
52
  reusableAssetsTab;
49
53
  marketPlaceButton;
50
54
  clientIdTextbox;
@@ -229,6 +233,10 @@ class ModelerCreatePage {
229
233
  .locator('[data-id="replace.template-io.camunda.connectors.HttpJson.v2"]');
230
234
  this.changeElementSearchInput = page.locator('.djs-popup-search input');
231
235
  this.changeElementResults = page.locator('.djs-popup-results');
236
+ this.aiAgentTaskConnectorOption = this.changeElementResults.locator('[data-id="replace.template-io.camunda.connectors.agenticai.ai-agent-task.v2"]');
237
+ this.aiAgentSubProcessConnectorOption = this.changeElementResults.locator('[data-id="replace.template-io.camunda.connectors.agenticai.ai-agent-subprocess.v2"]');
238
+ this.adHocSubProcessOption = this.changeElementResults.locator('[data-id="replace-with-ad-hoc-subprocess"]');
239
+ this.propertiesPanel = page.locator('[data-test="properties-panel"]');
232
240
  // The tab renders its label as text content, and the product source
233
241
  // spells it both "Reusable Assets" and "Reusable assets".
234
242
  this.reusableAssetsTab = page.getByRole('tab', {
@@ -735,6 +743,26 @@ class ModelerCreatePage {
735
743
  }
736
744
  throw new Error(`Canvas pad entries "${selector}" did not open${lastError ? `: ${lastError}` : ''}`);
737
745
  }
746
+ async appendTaskToStartEvent() {
747
+ const startEvent = this.page.locator('.djs-shape[data-element-id="StartEvent_1"]');
748
+ await (0, test_1.expect)(startEvent).toBeVisible({ timeout: 30000 });
749
+ await (0, test_1.expect)(async () => {
750
+ if (!(await startEvent.evaluate((element) => element.classList.contains('selected')))) {
751
+ await startEvent.click();
752
+ }
753
+ await (0, test_1.expect)(startEvent).toHaveClass(/(?:^|\s)selected(?:\s|$)/);
754
+ }).toPass({ timeout: 30000 });
755
+ await this.page
756
+ .getByRole('button', { name: 'Append task', exact: true })
757
+ .click();
758
+ const task = this.page.locator('.djs-shape.selected');
759
+ await (0, test_1.expect)(task).not.toHaveAttribute('data-element-id', 'StartEvent_1');
760
+ const elementId = await task.getAttribute('data-element-id');
761
+ if (!elementId) {
762
+ throw new Error('The appended task has no element ID.');
763
+ }
764
+ return elementId;
765
+ }
738
766
  async clickAppendGatewayButton() {
739
767
  await this.appendGatewayButton.click({ timeout: 90000 });
740
768
  }
@@ -771,6 +799,7 @@ class ModelerCreatePage {
771
799
  return;
772
800
  }
773
801
  }
802
+ throw new Error(`The diagram did not commit Camunda version ${version}.`);
774
803
  }
775
804
  async clickChangeTypeButton() {
776
805
  try {
@@ -1334,6 +1363,56 @@ class ModelerCreatePage {
1334
1363
  await element.click({ timeout: 30000 });
1335
1364
  await this.clickChangeTypeButton();
1336
1365
  }
1366
+ async openChangeElementPopupForElement(elementId) {
1367
+ const element = this.page.locator(`.djs-shape[data-element-id="${elementId}"]`);
1368
+ await this.page.keyboard.press('Escape');
1369
+ await (0, test_1.expect)(this.changeElementSearchInput).toBeHidden({ timeout: 30000 });
1370
+ await (0, test_1.expect)(element).toBeVisible({ timeout: 30000 });
1371
+ await (0, test_1.expect)(async () => {
1372
+ if (!(await element.evaluate((shape) => shape.classList.contains('selected')))) {
1373
+ await element.click({ timeout: 5000 });
1374
+ }
1375
+ await (0, test_1.expect)(element).toHaveClass(/(?:^|\s)selected(?:\s|$)/);
1376
+ }).toPass({ timeout: 30000 });
1377
+ await this.changeTypeButton.click({ timeout: 30000 });
1378
+ await (0, test_1.expect)(this.changeElementSearchInput).toBeVisible({ timeout: 30000 });
1379
+ }
1380
+ async assertChangeElementTemplateListed(option, searchTerm, elementId) {
1381
+ await this.openChangeElementPopupForElement(elementId);
1382
+ const revealed = await this.revealChangeElementTemplate(option, searchTerm);
1383
+ (0, test_1.expect)(revealed, `"${searchTerm}" was not listed after the Marketplace import.`).toBe(true);
1384
+ await (0, test_1.expect)(option).toBeVisible({ timeout: 30000 });
1385
+ await (0, test_1.expect)(option).toBeEnabled({ timeout: 30000 });
1386
+ }
1387
+ async changeElementToAdHocSubProcess(elementId) {
1388
+ const element = this.page.locator(`.djs-shape[data-element-id="${elementId}"]`);
1389
+ await this.openChangeElementPopupForElement(elementId);
1390
+ await this.changeElementSearchInput.fill('');
1391
+ await this.changeElementSearchInput.pressSequentially('Ad-hoc', {
1392
+ delay: 50,
1393
+ });
1394
+ await this.adHocSubProcessOption.click({ timeout: 30000 });
1395
+ await (0, test_1.expect)(element.locator('[data-marker="adhoc"]')).toBeVisible({
1396
+ timeout: 30000,
1397
+ });
1398
+ }
1399
+ async applyAiAgentSubProcessTemplate() {
1400
+ await this.aiAgentSubProcessConnectorOption.click({ timeout: 30000 });
1401
+ await this.changeElementSearchInput.fill('');
1402
+ await this.changeElementSearchInput.pressSequentially('OpenAI API', {
1403
+ delay: 50,
1404
+ });
1405
+ const popup = this.page.locator('.djs-popup');
1406
+ await popup
1407
+ .getByRole('option', { name: 'OpenAI API', exact: true })
1408
+ .click({ timeout: 30000 });
1409
+ await (0, test_1.expect)(popup).toBeHidden({ timeout: 30000 });
1410
+ await (0, test_1.expect)(this.page
1411
+ .locator('.bio-properties-panel-header')
1412
+ .getByText('AI Agent Sub-process', { exact: true })).toBeVisible({ timeout: 60000 });
1413
+ await (0, test_1.expect)(this.propertiesPanel.getByText('Applied', { exact: true })).toBeVisible({ timeout: 30000 });
1414
+ await (0, test_1.expect)(this.templateNotFoundTag).toBeHidden({ timeout: 10000 });
1415
+ }
1337
1416
  async clickChangeElementTemplateOption(option, element, searchTerm) {
1338
1417
  const maxRetries = 3;
1339
1418
  let lastError;
@@ -7,6 +7,8 @@ declare class ModelerHomePage {
7
7
  private processApplicationName?;
8
8
  private workspaceProjectsUrl?;
9
9
  private workspacesOverviewUrl?;
10
+ private disposableWorkspaceUrl?;
11
+ private disposableWorkspaceName?;
10
12
  readonly modelerPageBanner: Locator;
11
13
  readonly createNewProjectButton: Locator;
12
14
  readonly createWorkspaceButton: Locator;
@@ -65,6 +67,9 @@ declare class ModelerHomePage {
65
67
  password: string;
66
68
  }): Promise<void>;
67
69
  private ensureWorkspaceOpen;
70
+ createDisposableWorkspace(name: string): Promise<void>;
71
+ deleteDisposableWorkspace(): Promise<void>;
72
+ createBpmnTemplateWithoutEnvironment(): Promise<void>;
68
73
  private gotoWorkspacesOverview;
69
74
  private createWorkspace;
70
75
  private submitWorkspaceWizard;
@@ -82,7 +87,7 @@ declare class ModelerHomePage {
82
87
  private waitForEntityCrumb;
83
88
  clickProjectBreadcrumb(): Promise<void>;
84
89
  clickWorkspaceBreadcrumb(): Promise<void>;
85
- enterResourceContainer(name?: string): Promise<void>;
90
+ enterResourceContainer(name?: string, assignEnvironment?: boolean): Promise<void>;
86
91
  private clickCreateProcessApplication;
87
92
  private ensureInResourceContainer;
88
93
  returnToResourceContainer(): Promise<void>;
@@ -30,6 +30,8 @@ class ModelerHomePage {
30
30
  // re-walk the hierarchy from the root each time.
31
31
  workspaceProjectsUrl;
32
32
  workspacesOverviewUrl;
33
+ disposableWorkspaceUrl;
34
+ disposableWorkspaceName;
33
35
  modelerPageBanner;
34
36
  createNewProjectButton;
35
37
  // /workspaces overview controls (flag-on). The "Create workspace" CTA is
@@ -211,6 +213,7 @@ class ModelerHomePage {
211
213
  // sibling options chevron whose name would otherwise substring-match.
212
214
  this.createWorkspaceButton = page.getByRole('button', {
213
215
  name: 'Create workspace',
216
+ exact: true,
214
217
  });
215
218
  this.workspaceOpenButton = page
216
219
  .getByRole('button', { name: /^open workspace /i })
@@ -479,6 +482,64 @@ class ModelerHomePage {
479
482
  }
480
483
  }
481
484
  }
485
+ // A fresh Workspace forces the Marketplace import write instead of reusing
486
+ // templates registered by another test.
487
+ async createDisposableWorkspace(name) {
488
+ await this.gotoWorkspacesOverview();
489
+ await this.waitForModelerReady();
490
+ await this.clickMessageBanner();
491
+ await this.dismissModals();
492
+ this.disposableWorkspaceName = name;
493
+ try {
494
+ await this.createWorkspace(name);
495
+ }
496
+ finally {
497
+ const current = new URL(this.page.url());
498
+ if (/\/workspaces?\/[^/]+\/projects$/.test(current.pathname)) {
499
+ this.disposableWorkspaceUrl = `${current.origin}${current.pathname}`;
500
+ }
501
+ }
502
+ }
503
+ async deleteDisposableWorkspace() {
504
+ if (!this.disposableWorkspaceName) {
505
+ return;
506
+ }
507
+ if (this.disposableWorkspaceUrl) {
508
+ await this.page.goto(this.disposableWorkspaceUrl.replace(/\/projects$/, '/manage?tab=general'), { waitUntil: 'domcontentloaded', timeout: 30000 });
509
+ }
510
+ else {
511
+ await this.gotoWorkspacesOverview();
512
+ await this.waitForModelerReady(30000);
513
+ const manage = this.page.getByRole('button', {
514
+ name: `Manage workspace ${this.disposableWorkspaceName}`,
515
+ exact: true,
516
+ });
517
+ await (0, test_1.expect)(manage).toBeVisible({ timeout: 15000 });
518
+ await manage.click({ timeout: 15000 });
519
+ await this.page
520
+ .getByRole('tab', { name: 'General', exact: true })
521
+ .click({ timeout: 15000 });
522
+ }
523
+ await this.page
524
+ .getByRole('tabpanel', { name: 'General', exact: true })
525
+ .getByRole('button', { name: 'Delete', exact: true })
526
+ .click({ timeout: 30000 });
527
+ const dialog = this.page.getByRole('dialog').filter({
528
+ has: this.page.getByRole('heading', { name: /^Delete workspace(?:\s|$)/ }),
529
+ });
530
+ await dialog
531
+ .getByRole('button', { name: 'Delete workspace', exact: true })
532
+ .click({ timeout: 30000 });
533
+ await this.page.waitForURL(/\/workspaces\/?$/, { timeout: 30000 });
534
+ this.disposableWorkspaceUrl = undefined;
535
+ this.disposableWorkspaceName = undefined;
536
+ }
537
+ // Import-only coverage needs a resource container, not a deployment target.
538
+ async createBpmnTemplateWithoutEnvironment() {
539
+ await this.enterResourceContainer(undefined, false);
540
+ await this.processApplicationPage.createNewDropdown.click({ timeout: 60000 });
541
+ await this.clickBpmnTemplateOption();
542
+ }
482
543
  // Navigates to the Workspaces overview by resolving it against the Modeler app
483
544
  // root in the current URL, so it works whether Modeler is served under
484
545
  // `/modeler` (Self-Managed combined ingress) or at the app root.
@@ -820,7 +881,7 @@ class ModelerHomePage {
820
881
  // One container per test rather than one shared with the seeded workspace:
821
882
  // deploying is process-application-wide, so a shared container would let one
822
883
  // spec's deploy pick up every other spec's half-modelled diagram.
823
- async enterResourceContainer(name) {
884
+ async enterResourceContainer(name, assignEnvironment = true) {
824
885
  if (this.processApplicationUrl) {
825
886
  const stillInside = await this.becameVisible(this.processApplicationPage.breadcrumb, 5000);
826
887
  if (!stillInside) {
@@ -845,7 +906,9 @@ class ModelerHomePage {
845
906
  // this connection step (or failing on the warning). ensureStageCluster()
846
907
  // memoizes per test, so runProcessInstance()'s own call is a no-op after
847
908
  // this.
848
- await this.processApplicationPage.ensureStageCluster(process.env.MODELER_CLUSTER_NAME);
909
+ if (assignEnvironment) {
910
+ await this.processApplicationPage.ensureStageCluster(process.env.MODELER_CLUSTER_NAME);
911
+ }
849
912
  }
850
913
  // Which control creates a process application depends on the hub version
851
914
  // under test. Since camunda/camunda-hub#25831 it is a plain primary button on
@@ -2,10 +2,25 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const test_1 = require("@playwright/test");
4
4
  const SM_8_11_1 = require("../../fixtures/SM-8.11");
5
+ const ModelerHomePage_1 = require("../../pages/SM-8.11/ModelerHomePage");
5
6
  const _setup_1 = require("../../test-setup.js");
6
7
  const UtilitiesPage_1 = require("../../pages/SM-8.11/UtilitiesPage");
7
8
  const sleep_1 = require("../../utils/sleep");
8
9
  const constants_1 = require("../../utils/constants");
10
+ const test = SM_8_11_1.test.extend({
11
+ modelerHomePage: [
12
+ async ({ page }, use) => {
13
+ const home = new ModelerHomePage_1.ModelerHomePage(page);
14
+ try {
15
+ await use(home);
16
+ }
17
+ finally {
18
+ await home.deleteDisposableWorkspace();
19
+ }
20
+ },
21
+ { scope: 'test', timeout: 180000 },
22
+ ],
23
+ });
9
24
  // Serial: the two active webhook tests both fall back to importing the
10
25
  // "Webhook Connector" marketplace package (which bundles the Boundary,
11
26
  // Intermediate and Message-Start template variants) when the template is not
@@ -17,31 +32,31 @@ const constants_1 = require("../../utils/constants");
17
32
  // Running them serially lets the first import register the templates
18
33
  // Workspace-wide; the second test then reveals them directly and skips the
19
34
  // import, so there is no collision.
20
- SM_8_11_1.test.describe.configure({ mode: 'serial' });
21
- SM_8_11_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
35
+ test.describe.configure({ mode: 'serial' });
36
+ test.describe('Connectors User Flow Tests @tasklistV2', () => {
22
37
  const mtTenant = process.env.IS_MT === 'true' ? '<default>' : '';
23
- SM_8_11_1.test.beforeEach(async ({ navigationPage }, testInfo) => {
38
+ test.beforeEach(async ({ navigationPage }, testInfo) => {
24
39
  await navigationPage.goToModeler((testInfo.workerIndex + 1) * 1000);
25
40
  });
26
- SM_8_11_1.test.afterEach(async ({ page }, testInfo) => {
41
+ test.afterEach(async ({ page }, testInfo) => {
27
42
  await (0, _setup_1.captureScreenshot)(page, testInfo);
28
43
  await (0, _setup_1.captureFailureVideo)(page, testInfo);
29
44
  });
30
45
  // Skipped due to bug #7716: https://github.com/camunda/connectors/issues/7716
31
- SM_8_11_1.test.skip('REST Connector No Auth User Flow', async ({ page, operateHomePage, modelerHomePage, operateProcessInstancePage, modelerCreatePage, connectorSettingsPage, operateProcessesPage, connectorMarketplacePage, navigationPage, }) => {
32
- SM_8_11_1.test.slow();
46
+ test.skip('REST Connector No Auth User Flow', async ({ page, operateHomePage, modelerHomePage, operateProcessInstancePage, modelerCreatePage, connectorSettingsPage, operateProcessesPage, connectorMarketplacePage, navigationPage, }) => {
47
+ test.slow();
33
48
  const processName = 'REST_Connector_No_Auth_Process' + (await (0, _setup_1.generateRandomStringAsync)(3));
34
- await SM_8_11_1.test.step('Open Cross Component Test Project and Create a BPMN Diagram Template', async () => {
49
+ await test.step('Open Cross Component Test Project and Create a BPMN Diagram Template', async () => {
35
50
  await modelerHomePage.clickCrossComponentProjectFolder();
36
51
  await modelerHomePage.clickDiagramTypeDropdown();
37
52
  await modelerHomePage.clickBpmnTemplateOption();
38
53
  });
39
- await SM_8_11_1.test.step('Create BPMN Diagram with REST Connector and Start Process Instance', async () => {
54
+ await test.step('Create BPMN Diagram with REST Connector and Start Process Instance', async () => {
40
55
  await (0, UtilitiesPage_1.modelRestConnector)(modelerCreatePage, connectorSettingsPage, connectorMarketplacePage, processName, constants_1.REST_MOCK_URL, 'noAuth', 'body');
41
56
  await modelerCreatePage.runProcessInstance('', mtTenant);
42
57
  await (0, sleep_1.sleep)(20000);
43
58
  });
44
- await SM_8_11_1.test.step('View Process Instance in Operate, assert it completes and assert result expression', async () => {
59
+ await test.step('View Process Instance in Operate, assert it completes and assert result expression', async () => {
45
60
  await navigationPage.goToOperate();
46
61
  await operateHomePage.clickProcessesTab();
47
62
  await operateProcessesPage.clickProcessInstanceLink(processName, 'completed');
@@ -56,21 +71,21 @@ SM_8_11_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
56
71
  });
57
72
  });
58
73
  });
59
- (0, SM_8_11_1.test)('REST Connector Bearer Token Auth User Flow', async ({ page, operateHomePage, modelerHomePage, modelerCreatePage, connectorSettingsPage, navigationPage, operateProcessInstancePage, operateProcessesPage, connectorMarketplacePage, }) => {
60
- SM_8_11_1.test.slow();
74
+ test('REST Connector Bearer Token Auth User Flow', async ({ page, operateHomePage, modelerHomePage, modelerCreatePage, connectorSettingsPage, navigationPage, operateProcessInstancePage, operateProcessesPage, connectorMarketplacePage, }) => {
75
+ test.slow();
61
76
  const processName = 'REST_Connector_Bearer_Auth_Process' +
62
77
  (await (0, _setup_1.generateRandomStringAsync)(3));
63
- await SM_8_11_1.test.step('Open Cross Component Test Project and Create a BPMN Diagram Template', async () => {
78
+ await test.step('Open Cross Component Test Project and Create a BPMN Diagram Template', async () => {
64
79
  await modelerHomePage.clickCrossComponentProjectFolder();
65
80
  await modelerHomePage.clickDiagramTypeDropdown();
66
81
  await modelerHomePage.clickBpmnTemplateOption();
67
82
  });
68
- await SM_8_11_1.test.step('Create BPMN Diagram with REST Connector with Basic Auth and Start Process Instance', async () => {
83
+ await test.step('Create BPMN Diagram with REST Connector with Basic Auth and Start Process Instance', async () => {
69
84
  await (0, UtilitiesPage_1.modelRestConnector)(modelerCreatePage, connectorSettingsPage, connectorMarketplacePage, processName, constants_1.REST_MOCK_URL, 'bearer', '{message:response.body.message}');
70
85
  await modelerCreatePage.runProcessInstance('', mtTenant);
71
86
  await (0, sleep_1.sleep)(20000);
72
87
  });
73
- await SM_8_11_1.test.step('View Process Instance in Operate, assert it completes and assert result expression', async () => {
88
+ await test.step('View Process Instance in Operate, assert it completes and assert result expression', async () => {
74
89
  await navigationPage.goToOperate();
75
90
  await operateHomePage.clickProcessesTab();
76
91
  await operateProcessesPage.clickProcessInstanceLink(processName, 'completed');
@@ -85,16 +100,16 @@ SM_8_11_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
85
100
  });
86
101
  });
87
102
  });
88
- (0, SM_8_11_1.test)('Message Start Event Webhook Connector No Auth User Flow', async ({ modelerHomePage, navigationPage, modelerCreatePage, request, operateHomePage, operateProcessInstancePage, operateProcessesPage, connectorMarketplacePage, connectorSettingsPage, }) => {
89
- SM_8_11_1.test.slow();
103
+ test('Message Start Event Webhook Connector No Auth User Flow', async ({ modelerHomePage, navigationPage, modelerCreatePage, request, operateHomePage, operateProcessInstancePage, operateProcessesPage, connectorMarketplacePage, connectorSettingsPage, }) => {
104
+ test.slow();
90
105
  const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
91
106
  const processName = 'Start_Event_Webhook_Connector_No_Auth_Process' + randomString;
92
- await SM_8_11_1.test.step('Open Cross Component Test Project and Create a BPMN Diagram Template', async () => {
107
+ await test.step('Open Cross Component Test Project and Create a BPMN Diagram Template', async () => {
93
108
  await modelerHomePage.clickCrossComponentProjectFolder();
94
109
  await modelerHomePage.clickDiagramTypeDropdown();
95
110
  await modelerHomePage.clickBpmnTemplateOption();
96
111
  });
97
- await SM_8_11_1.test.step('Create BPMN Diagram with REST Connector and Deploy Diagram', async () => {
112
+ await test.step('Create BPMN Diagram with REST Connector and Deploy Diagram', async () => {
98
113
  await (0, test_1.expect)(modelerCreatePage.generalPanel).toBeVisible({
99
114
  timeout: 60000,
100
115
  });
@@ -133,7 +148,7 @@ SM_8_11_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
133
148
  await modelerCreatePage.deployProcessInstance(mtTenant);
134
149
  await (0, sleep_1.sleep)(20000);
135
150
  });
136
- await SM_8_11_1.test.step('Make Authorization Request', async () => {
151
+ await test.step('Make Authorization Request', async () => {
137
152
  const baseURL = process.env.PLAYWRIGHT_BASE_URL ||
138
153
  `http://gke-${process.env.BASE_URL}.ci.distro.ultrawombat.com`;
139
154
  const url = `${baseURL}/connectors/inbound/test-webhook-id${randomString}`;
@@ -154,7 +169,7 @@ SM_8_11_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
154
169
  throw new Error(`Webhook request failed with status ${response?.status()} after ${attempt} attempts`);
155
170
  }
156
171
  });
157
- await SM_8_11_1.test.step('Assert Diagram Has Successfully Completed in Operate', async () => {
172
+ await test.step('Assert Diagram Has Successfully Completed in Operate', async () => {
158
173
  await navigationPage.goToOperate();
159
174
  await operateHomePage.clickProcessesTab();
160
175
  await operateProcessesPage.clickProcessInstanceLink(processName, 'completed');
@@ -163,15 +178,15 @@ SM_8_11_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
163
178
  });
164
179
  });
165
180
  // Skipped due to bug #7716: https://github.com/camunda/connectors/issues/7716
166
- SM_8_11_1.test.skip('Connector Secrets User Flow', async ({ operateHomePage, modelerHomePage, navigationPage, modelerCreatePage, connectorSettingsPage, operateProcessInstancePage, operateProcessesPage, connectorMarketplacePage, }) => {
167
- SM_8_11_1.test.slow();
181
+ test.skip('Connector Secrets User Flow', async ({ operateHomePage, modelerHomePage, navigationPage, modelerCreatePage, connectorSettingsPage, operateProcessInstancePage, operateProcessesPage, connectorMarketplacePage, }) => {
182
+ test.slow();
168
183
  const processName = 'REST_Connector_Process' + (await (0, _setup_1.generateRandomStringAsync)(3));
169
- await SM_8_11_1.test.step('Open Cross Component Test Project and Create a BPMN Diagram Template', async () => {
184
+ await test.step('Open Cross Component Test Project and Create a BPMN Diagram Template', async () => {
170
185
  await modelerHomePage.clickCrossComponentProjectFolder();
171
186
  await modelerHomePage.clickDiagramTypeDropdown();
172
187
  await modelerHomePage.clickBpmnTemplateOption();
173
188
  });
174
- await SM_8_11_1.test.step('Create BPMN Diagram with REST Connector using secrets and Start Process Instance', async () => {
189
+ await test.step('Create BPMN Diagram with REST Connector using secrets and Start Process Instance', async () => {
175
190
  await (0, UtilitiesPage_1.modelRestConnector)(modelerCreatePage, connectorSettingsPage, connectorMarketplacePage, processName, constants_1.REST_MOCK_BASIC_AUTH_URL, 'basic', '{message:response.body.message}', {
176
191
  resultVariable: 'result',
177
192
  basicAuthCredentials: {
@@ -182,7 +197,7 @@ SM_8_11_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
182
197
  await modelerCreatePage.runProcessInstance('', mtTenant);
183
198
  await (0, sleep_1.sleep)(20000);
184
199
  });
185
- await SM_8_11_1.test.step('View Process Instance in Operate, assert it completes and assert result expression', async () => {
200
+ await test.step('View Process Instance in Operate, assert it completes and assert result expression', async () => {
186
201
  await navigationPage.goToOperate();
187
202
  await operateHomePage.clickProcessesTab();
188
203
  await operateProcessesPage.clickProcessInstanceLink(processName, 'completed');
@@ -192,16 +207,16 @@ SM_8_11_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
192
207
  await operateProcessInstancePage.assertProcessVariableContainsText('message', '"Message from Mock!"');
193
208
  });
194
209
  });
195
- (0, SM_8_11_1.test)('Intermediate Event Webhook Connector No Auth User Flow', async ({ modelerHomePage, modelerCreatePage, request, operateHomePage, operateProcessInstancePage, operateProcessesPage, navigationPage, connectorMarketplacePage, connectorSettingsPage, }) => {
196
- SM_8_11_1.test.slow();
210
+ test('Intermediate Event Webhook Connector No Auth User Flow', async ({ modelerHomePage, modelerCreatePage, request, operateHomePage, operateProcessInstancePage, operateProcessesPage, navigationPage, connectorMarketplacePage, connectorSettingsPage, }) => {
211
+ test.slow();
197
212
  const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
198
213
  const processName = 'Intermediate_Event_Webhook_Connector_No_Auth_Process' + randomString;
199
- await SM_8_11_1.test.step('Open Cross Component Test Project and Create a BPMN Diagram Template', async () => {
214
+ await test.step('Open Cross Component Test Project and Create a BPMN Diagram Template', async () => {
200
215
  await modelerHomePage.clickCrossComponentProjectFolder();
201
216
  await modelerHomePage.clickDiagramTypeDropdown();
202
217
  await modelerHomePage.clickBpmnTemplateOption();
203
218
  });
204
- await SM_8_11_1.test.step('Create BPMN Diagram with Intermediate Event Webhook Connector and Run Diagram', async () => {
219
+ await test.step('Create BPMN Diagram with Intermediate Event Webhook Connector and Run Diagram', async () => {
205
220
  await (0, test_1.expect)(modelerCreatePage.generalPanel).toBeVisible({
206
221
  timeout: 120000,
207
222
  });
@@ -251,7 +266,7 @@ SM_8_11_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
251
266
  await modelerCreatePage.runProcessInstance('', mtTenant);
252
267
  await (0, sleep_1.sleep)(20000);
253
268
  });
254
- await SM_8_11_1.test.step('Make Authorization Request', async () => {
269
+ await test.step('Make Authorization Request', async () => {
255
270
  const baseURL = process.env.PLAYWRIGHT_BASE_URL ||
256
271
  `http://gke-${process.env.BASE_URL}.ci.distro.ultrawombat.com`;
257
272
  const url = `${baseURL}/connectors/inbound/test-webhook-intermediate${randomString}`;
@@ -272,7 +287,7 @@ SM_8_11_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
272
287
  }
273
288
  await (0, sleep_1.sleep)(60000);
274
289
  });
275
- await SM_8_11_1.test.step('Assert Diagram Has Successfully Completed in Operate', async () => {
290
+ await test.step('Assert Diagram Has Successfully Completed in Operate', async () => {
276
291
  await navigationPage.goToOperate();
277
292
  await operateHomePage.clickProcessesTab();
278
293
  await operateProcessesPage.clickProcessInstanceLink(processName, 'completed');
@@ -280,14 +295,62 @@ SM_8_11_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
280
295
  (0, test_1.expect)(result).toBe('completed');
281
296
  });
282
297
  });
298
+ const importAiAgentFromMarketplace = async ({ modelerHomePage, modelerCreatePage, connectorMarketplacePage, }, processName) => {
299
+ await test.step('Create a Workspace and a BPMN Diagram Template', async () => {
300
+ await modelerHomePage.createDisposableWorkspace('AI Agent Import Workspace ' + processName);
301
+ await modelerHomePage.createBpmnTemplateWithoutEnvironment();
302
+ });
303
+ const taskElementId = await test.step('Append a Task and Open the Change Element Popup', async () => {
304
+ await (0, test_1.expect)(modelerCreatePage.generalPanel).toBeVisible({
305
+ timeout: 120000,
306
+ });
307
+ // Do not rename the selected start event's ID before appending a task.
308
+ await modelerCreatePage.enterDiagramName(processName);
309
+ const elementId = await modelerCreatePage.appendTaskToStartEvent();
310
+ // Modeler's diagram targets currently stop at 8.10, which supports AI v2.
311
+ // The deployment under test remains SM 8.11; these flows do not deploy.
312
+ await modelerCreatePage.setEngineVersion('8.10');
313
+ await modelerCreatePage.openChangeElementPopupForElement(elementId);
314
+ return elementId;
315
+ });
316
+ await test.step('Import the AI Agent Connector from the Marketplace', async () => {
317
+ await modelerCreatePage.clickMarketPlaceButton();
318
+ await connectorMarketplacePage.clickSearchForConnectorTextbox();
319
+ await connectorMarketplacePage.fillSearchForConnectorTextbox('AI Agent');
320
+ await connectorMarketplacePage.waitForConnectorSearchResults();
321
+ const imported = await connectorMarketplacePage.importConnectorAndAssertNoImportError();
322
+ (0, test_1.expect)(imported, 'A fresh Workspace must exercise the AI Agent Marketplace import write.').toBe(true);
323
+ });
324
+ return taskElementId;
325
+ };
326
+ test('AI Agent Marketplace Import User Flow', async ({ modelerHomePage, modelerCreatePage, connectorMarketplacePage, }) => {
327
+ test.slow();
328
+ const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
329
+ const taskElementId = await importAiAgentFromMarketplace({ modelerHomePage, modelerCreatePage, connectorMarketplacePage }, 'AI_Agent_Marketplace_Import_Process' + randomString);
330
+ await test.step('Assert the AI Agent Task Template Is Available', async () => {
331
+ await modelerCreatePage.assertChangeElementTemplateListed(modelerCreatePage.aiAgentTaskConnectorOption, 'AI Agent', taskElementId);
332
+ });
333
+ });
334
+ test('AI Agent Sub-process Marketplace Import User Flow', async ({ modelerHomePage, modelerCreatePage, connectorMarketplacePage, }) => {
335
+ test.slow();
336
+ const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
337
+ const taskElementId = await importAiAgentFromMarketplace({ modelerHomePage, modelerCreatePage, connectorMarketplacePage }, 'AI_Agent_SubProcess_Import_Process' + randomString);
338
+ await test.step('Turn the Task into an Ad-hoc Sub-process', async () => {
339
+ await modelerCreatePage.changeElementToAdHocSubProcess(taskElementId);
340
+ });
341
+ await test.step('Assert the AI Agent Sub-process Template Is Available and Applies', async () => {
342
+ await modelerCreatePage.assertChangeElementTemplateListed(modelerCreatePage.aiAgentSubProcessConnectorOption, 'AI Agent', taskElementId);
343
+ await modelerCreatePage.applyAiAgentSubProcessTemplate();
344
+ });
345
+ });
283
346
  //Skipped due to bug 1252: https://github.com/camunda/c8-cross-component-e2e-tests/issues/1252
284
- SM_8_11_1.test.skip('Connectors Timer Event User Flow', async ({ page, modelerHomePage, modelerCreatePage, connectorMarketplacePage, connectorTemplatePage, navigationPage, operateProcessInstancePage, operateHomePage, operateProcessesPage, }) => {
285
- SM_8_11_1.test.slow();
347
+ test.skip('Connectors Timer Event User Flow', async ({ page, modelerHomePage, modelerCreatePage, connectorMarketplacePage, connectorTemplatePage, navigationPage, operateProcessInstancePage, operateHomePage, operateProcessesPage, }) => {
348
+ test.slow();
286
349
  const processName = 'Connectors_Timer_Event_Process' + (await (0, _setup_1.generateRandomStringAsync)(3));
287
- await SM_8_11_1.test.step('Open Cross Component Test Project and Create a REST Connector Document Handling Flow', async () => {
350
+ await test.step('Open Cross Component Test Project and Create a REST Connector Document Handling Flow', async () => {
288
351
  await (0, UtilitiesPage_1.modelAndRunConnectorsTimerEventDiagram)(modelerCreatePage, modelerHomePage, processName, page, connectorMarketplacePage, connectorTemplatePage, mtTenant);
289
352
  });
290
- await SM_8_11_1.test.step('View Process Instance in Operate, assert it completes and assert variable values are correct', async () => {
353
+ await test.step('View Process Instance in Operate, assert it completes and assert variable values are correct', async () => {
291
354
  await navigationPage.goToOperate();
292
355
  await operateHomePage.clickProcessesTab();
293
356
  await operateProcessesPage.clickProcessInstanceLink(processName);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.1470",
3
+ "version": "0.0.1471",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",