@camunda/e2e-test-suite 0.0.1176 → 0.0.1178

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.
@@ -256,8 +256,12 @@ class ConsoleOrganizationPage {
256
256
  });
257
257
  const alphaFeature = this.rows.filter({ hasText: name }).first();
258
258
  if ((await alphaFeature.count()) < 1) {
259
- console.error(`No alpha feature(${name}) is found.`);
260
- return;
259
+ // Returning here used to report the feature as enabled while nothing
260
+ // had been enabled at all, so the org silently stayed opted out and
261
+ // every dependent spec failed later on a missing UI affordance.
262
+ // Throw instead: the surrounding loop reloads and retries, and a
263
+ // genuinely absent row fails this step where the cause is visible.
264
+ throw new Error(`No alpha feature(${name}) is found.`);
261
265
  }
262
266
  //Opt-in The Alpha Feature
263
267
  const alphaFeatureOptInButton = alphaFeature.getByRole('button', {
@@ -64,14 +64,17 @@ class OCIdentityRolesPage {
64
64
  name: SEARCH_MAPPING_RULE_LABEL,
65
65
  }))
66
66
  .first();
67
- // Clicking that trigger opens a Radix popover that portals to <body> as a
68
- // sibling of the dialog, and the editable field is the cmdk <input> inside
69
- // it - the trigger div carries the same role and accessible name but
70
- // swallows key events, so typing at it leaves the query empty. Match the
71
- // popover's input, falling back to the dialog-scoped searchbox that
72
- // pre-design-system builds render instead of the trigger/popover pair.
67
+ // The current design-system modal renders the cmdk <input cmdk-input> inline
68
+ // inside the dialog (data-slot="command-input", role="combobox"), so the
69
+ // trigger and the editable field are the same element - match that input
70
+ // scoped to the dialog. Older builds render a MultiSelect whose trigger div
71
+ // opens a Radix popover that portals to <body> as a sibling of the dialog,
72
+ // with the editable cmdk <input> inside it; that popover input is outside
73
+ // the dialog subtree, so match it page-wide. Pre-design-system builds render
74
+ // a dialog-scoped searchbox instead of either - fall back to that last.
73
75
  this.mappingSearchBox = page
74
76
  .locator('[data-slot="multi-select-content"] input[cmdk-input]')
77
+ .or(this.assignMappingModal.locator('input[cmdk-input]'))
75
78
  .or(this.assignMappingModal.getByRole('searchbox', {
76
79
  name: SEARCH_MAPPING_RULE_LABEL,
77
80
  }))
@@ -25,6 +25,10 @@ _8_10_1.test.describe('Cluster Setup Tests', () => {
25
25
  await homePage.clickOrganization();
26
26
  await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(page, consoleOrganizationsPage.settingsTab, 'settings tab', 60000, false, 2);
27
27
  await consoleOrganizationsPage.enableAlphaFeature('AI-powered features');
28
+ // Confirm the opt-in against Accounts rather than trusting the Console
29
+ // toggle: the AI specs depend on this flag, and a setup that passes without
30
+ // it leaves them failing on a button Web Modeler never renders.
31
+ await (0, consoleApiHelpers_1.waitForAiFeaturesEnabledViaApi)(page);
28
32
  });
29
33
  (0, _8_10_1.test)('Create Default Cluster', async ({ page, loginPage, homePage, clusterPage, clusterDetailsPage, clusterSecretsPage, }, testInfo) => {
30
34
  _8_10_1.test.slow();
@@ -11,6 +11,7 @@ const OptimizeDashboardPage_1 = require("../../pages/8.10/OptimizeDashboardPage"
11
11
  const fileUpload_1 = require("../../utils/fileUpload");
12
12
  const _setup_1 = require("../../test-setup.js");
13
13
  const UtilitiesPage_1 = require("../../pages/8.10/UtilitiesPage");
14
+ const consoleApiHelpers_1 = require("../../utils/consoleApiHelpers");
14
15
  const deleteOrg_1 = require("../../utils/deleteOrg");
15
16
  const sleep_1 = require("../../utils/sleep");
16
17
  const mailSlurpClient_1 = require("../../utils/mailSlurpClient");
@@ -34,11 +35,25 @@ _8_10_1.test.describe('Web Modeler User Flow Tests', () => {
34
35
  await (0, mailSlurpClient_1.deleteInbox)(inboxes[testInfo.title]);
35
36
  }
36
37
  });
37
- (0, _8_10_1.test)('Form.js Integration with User Task and AI Generated Form @ai-external', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, formJsPage, }) => {
38
+ (0, _8_10_1.test)('Form.js Integration with User Task and AI Generated Form @ai-external', async ({ page, homePage, modelerHomePage, appsPage, modelerCreatePage, formJsPage, consoleOrganizationsPage, }) => {
38
39
  _8_10_1.test.slow();
39
40
  const randomString = await (0, _setup_1.generateRandomStringAsync)(3);
40
41
  const processName = 'User_Task_Process_With_AI_Form' + randomString;
41
42
  const formName = 'New form' + randomString;
43
+ // Web Modeler renders the AI Form Generator only while the org carries the
44
+ // 'aifeatures' early-access flag, which the 'Enable Alpha Features' setup
45
+ // test is responsible for. When that opt-in does not take, the palette
46
+ // button is simply absent and this test times out on it with nothing
47
+ // pointing at the org. Confirm the prerequisite here, and opt in when it is
48
+ // missing, so the flow no longer depends on setup having succeeded silently.
49
+ await _8_10_1.test.step('Ensure AI Features Are Enabled For The Organization', async () => {
50
+ if (await (0, consoleApiHelpers_1.areAiFeaturesEnabledViaApi)(page)) {
51
+ return;
52
+ }
53
+ await homePage.clickOrganization();
54
+ await consoleOrganizationsPage.enableAlphaFeature('AI-powered features');
55
+ await (0, consoleApiHelpers_1.waitForAiFeaturesEnabledViaApi)(page);
56
+ });
42
57
  await _8_10_1.test.step('Navigate to Web Modeler', async () => {
43
58
  await (0, test_1.expect)(homePage.camundaComponentsButton).toBeVisible({
44
59
  timeout: 120000,
@@ -70,12 +85,27 @@ _8_10_1.test.describe('Web Modeler User Flow Tests', () => {
70
85
  await (0, test_1.expect)(modelerCreatePage.viewProcessInstanceLink).toBeVisible({
71
86
  timeout: 180000,
72
87
  });
73
- await modelerCreatePage.clickViewProcessInstanceLink();
74
- const operateTab = await page.waitForEvent('popup', { timeout: 60000 });
88
+ // Latch the Operate instance deep link on the CONTEXT before the click.
89
+ // Playwright raises `popup` only once the new tab's initial response has
90
+ // started loading, and a session-less Operate tab's auth redirect chain
91
+ // drops the instance path before anything commits -- so no listener
92
+ // attached to the popup afterwards ever sees that URL. Without it
93
+ // `waitForInstanceLocator` has nothing to re-open and can only poll
94
+ // whatever the tab was left on, where the instance page can never
95
+ // render: "Assertion of Instance History panel in Operate timed out
96
+ // after 480000ms (instance header not rendered,
97
+ // url=<cluster>/operate?continue, instance deep link never captured)".
98
+ // `openAppTabWithRetry` also registers the popup wait BEFORE the click,
99
+ // which `waitForEvent` after the click cannot, and re-clicks a click
100
+ // that resolved without opening a tab. Same pattern as
101
+ // tests/8.10/connectors-user-flows.spec.ts and smoke-tests.spec.ts.
102
+ const watchedInstanceUrl = (0, OperateProcessInstancePage_1.watchOperateInstanceUrl)(page.context());
103
+ const operateTab = await (0, UtilitiesPage_1.openAppTabWithRetry)(page, modelerCreatePage.viewProcessInstanceLink);
75
104
  const operateTabAppsPage = new AppsPage_1.AppsPage(operateTab);
76
105
  const operateTabOperateProcessInstancePage = new OperateProcessInstancePage_1.OperateProcessInstancePage(operateTab);
77
106
  const operateTabTaskPanelPage = new TaskPanelPage_1.TaskPanelPage(operateTab);
78
107
  const operateTabTaskDetailsPage = new TaskDetailsPage_1.TaskDetailsPage(operateTab);
108
+ await operateTabOperateProcessInstancePage.rememberInstanceUrl(60000, watchedInstanceUrl);
79
109
  await operateTabOperateProcessInstancePage.closePopOverIfVisible();
80
110
  await (0, test_1.expect)(operateTabOperateProcessInstancePage.activeIcon).toBeVisible({ timeout: 180000 });
81
111
  await operateTabAppsPage.clickCamundaApps();
@@ -85,6 +115,12 @@ _8_10_1.test.describe('Web Modeler User Flow Tests', () => {
85
115
  await (0, test_1.expect)(operateTabTaskDetailsPage.assignedToMeText).toBeVisible({
86
116
  timeout: 60000,
87
117
  });
118
+ // Tasklist mounts the embedded form asynchronously once the task is
119
+ // assigned, so the task pane is briefly empty. fillName() inherits the
120
+ // 10s global actionTimeout, which the form does not always beat -- wait
121
+ // for its first field (reloading between attempts, since a form that
122
+ // failed to mount stays empty) instead of racing it.
123
+ await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTab, operateTabTaskDetailsPage.nameInput, 'AI generated form name field', 60000, false, 3);
88
124
  await operateTabTaskDetailsPage.fillName('test user');
89
125
  await operateTabTaskDetailsPage.checkCheckbox();
90
126
  await operateTabTaskDetailsPage.fillDate('11/11/1999');
@@ -6,3 +6,5 @@ export declare function ensureTasklistV2Enabled(page: Page, clusterUuid: string)
6
6
  export declare function ensureClientAuditLoggingViaApi(page: Page, clusterUuid: string): Promise<void>;
7
7
  export declare function waitForClusterHealthyViaApi(page: Page, clusterUuid: string, timeoutMs?: number): Promise<void>;
8
8
  export declare function waitForClusterRestartHealthyViaApi(page: Page, clusterUuid: string, timeoutMs?: number): Promise<void>;
9
+ export declare function areAiFeaturesEnabledViaApi(page: Page): Promise<boolean>;
10
+ export declare function waitForAiFeaturesEnabledViaApi(page: Page, timeoutMs?: number): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.waitForClusterRestartHealthyViaApi = exports.waitForClusterHealthyViaApi = exports.ensureClientAuditLoggingViaApi = exports.ensureTasklistV2Enabled = exports.ensureClusterViaApi = exports.createClusterViaApi = exports.deleteClusterViaApi = void 0;
3
+ exports.waitForAiFeaturesEnabledViaApi = exports.areAiFeaturesEnabledViaApi = exports.waitForClusterRestartHealthyViaApi = exports.waitForClusterHealthyViaApi = exports.ensureClientAuditLoggingViaApi = exports.ensureTasklistV2Enabled = exports.ensureClusterViaApi = exports.createClusterViaApi = exports.deleteClusterViaApi = void 0;
4
4
  const test_1 = require("@playwright/test");
5
5
  const sleep_1 = require("./sleep");
6
6
  const PLAN_TYPE_ID = '574cde5f-54ae-43c1-851a-e6fb4f03423a';
@@ -552,3 +552,49 @@ async function waitForClusterRestartHealthyViaApi(page, clusterUuid, timeoutMs =
552
552
  }).toPass({ timeout: timeoutMs, intervals: [30000, 60000] });
553
553
  }
554
554
  exports.waitForClusterRestartHealthyViaApi = waitForClusterRestartHealthyViaApi;
555
+ // Web Modeler only mounts the AI form assistant -- and with it the
556
+ // "AI Form Generator" palette button -- when the organization has accepted the
557
+ // alpha/AI terms AND carries the 'aifeatures' early-access flag. camunda-hub
558
+ // gates the whole module on that pair in
559
+ // frontend/apps/hub/src/experiments/ai-features/utils/are-ai-features-enabled.saas.ts.
560
+ // The 'Enable Alpha Features' setup test drives the opt-in through Console, but
561
+ // it can finish green without the opt-in having taken (see
562
+ // ConsoleOrganizationPage.enableAlphaFeature), and then every AI spec in that
563
+ // org fails on a button that is simply never rendered. Reading the flag back
564
+ // from Accounts turns that invisible state into an assertable one.
565
+ const AI_EARLY_ACCESS_FEATURE = 'aifeatures';
566
+ function getAccountsBaseUrl() {
567
+ if (process.env.IS_PROD === 'true') {
568
+ return 'https://accounts.cloud.camunda.io';
569
+ }
570
+ const uiUrl = process.env.PLAYWRIGHT_BASE_URL ?? 'https://console.ultrawombat.com';
571
+ return uiUrl
572
+ .replace('console.ultrawombat.com', 'accounts.cloud.ultrawombat.com')
573
+ .replace('console.camunda.io', 'accounts.cloud.camunda.io');
574
+ }
575
+ async function areAiFeaturesEnabledViaApi(page) {
576
+ const response = await clusterApiRequest(page, 'GET', `${getAccountsBaseUrl()}/api/organizations/${getOrgId()}`);
577
+ if (!response.ok()) {
578
+ throw new Error(`Failed to read organization info: HTTP ${response.status()} ` +
579
+ `${await response.text()}`);
580
+ }
581
+ const organization = (await response.json());
582
+ return (organization.hasAcceptedBetaTerms === true &&
583
+ (organization.earlyAccessFeatures ?? []).includes(AI_EARLY_ACCESS_FEATURE));
584
+ }
585
+ exports.areAiFeaturesEnabledViaApi = areAiFeaturesEnabledViaApi;
586
+ // Console reports the opt-in as done before Accounts serves the updated
587
+ // organization record, so poll rather than reading once.
588
+ async function waitForAiFeaturesEnabledViaApi(page, timeoutMs = 180000) {
589
+ await test_1.expect
590
+ .poll(async () => await areAiFeaturesEnabledViaApi(page), {
591
+ timeout: timeoutMs,
592
+ intervals: [5000],
593
+ message: `Organization ${getOrgId()} does not have the ` +
594
+ `'${AI_EARLY_ACCESS_FEATURE}' early-access feature enabled, so Web ` +
595
+ 'Modeler will not render the AI Form Generator. The alpha/AI terms ' +
596
+ 'opt-in in organization settings did not take.',
597
+ })
598
+ .toBe(true);
599
+ }
600
+ exports.waitForAiFeaturesEnabledViaApi = waitForAiFeaturesEnabledViaApi;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.1176",
3
+ "version": "0.0.1178",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",