@camunda/e2e-test-suite 0.0.1175 → 0.0.1177

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', {
@@ -8,6 +8,16 @@ const users_1 = require("../../utils/users");
8
8
  const agenticDashboardHelpers_1 = require("../../utils/agenticDashboardHelpers");
9
9
  const testUser = (0, users_1.getTestUser)('twentyFirstUser');
10
10
  const clusterName = 'Test Cluster';
11
+ // No reason for serial has been established here, and it is kept rather than
12
+ // flipped only because nobody has measured either way. All five tests are
13
+ // read-only against the agentic dashboard (the one interaction,
14
+ // changeDashboardDateRange, is a view-local Carbon dropdown), so there is no
15
+ // shared state to protect. Serial does NOT cut login volume either: beforeEach
16
+ // runs per test, so it is five logins as twentyFirstUser either way, and that
17
+ // user is also used by navigation, optimize-agentic-control-panel-ui and
18
+ // saas-migration-path-user-flows, none of which are serial. Rate-limit hits are
19
+ // absorbed by loginWithRetry and have never been traced to a failure
20
+ // (docs/saas-login-investigation.md). See docs/serial-specs.md.
11
21
  _8_10_1.test.describe.configure({ mode: 'serial' });
12
22
  _8_10_1.test.describe('Agentic Control Plane Dashboard API Tests @agentic-dashboard-api @8.10', () => {
13
23
  _8_10_1.test.beforeEach(async ({ page, loginPage }, testInfo) => {
@@ -4,6 +4,16 @@ const test_1 = require("@playwright/test");
4
4
  const _8_10_1 = require("../../fixtures/8.10");
5
5
  const apiHelpers_1 = require("../../utils/apiHelpers");
6
6
  const sleep_1 = require("../../utils/sleep");
7
+ // Serial is load-bearing twice over. Ordering: T11728029 asserts a business_*
8
+ // pattern is present in the default, untouched includeVariableNames, and the
9
+ // three variableFilter writers below (T11728020, T11728034, T11735087) replace
10
+ // that list -- so T11728029 has to run before them, which parallel mode cannot
11
+ // guarantee. (T11735088/T11735089 write processDefinitionFilter instead, and
12
+ // T11728030/T11728019 write nothing.) Snapshot:
13
+ // beforeAll reads the live config into originalFilters and afterAll writes it
14
+ // back; in parallel each worker runs beforeAll, so a worker starting second
15
+ // would snapshot another worker's mutation and restore that as "original",
16
+ // leaving the shared cluster dirty after the run. See docs/serial-specs.md.
7
17
  _8_10_1.test.describe.configure({ mode: 'serial' });
8
18
  let managementToken;
9
19
  let clusterUuid;
@@ -17,6 +17,13 @@ let dataFiltersAvailable = false;
17
17
  const skipIfUnavailable = () => {
18
18
  _8_10_1.test.skip(!dataFiltersAvailable, 'Data Filters section is not available on this cluster (feature not yet enabled)');
19
19
  };
20
+ // Serial is load-bearing: these 18 tests drive the same Console Data Filters
21
+ // form for the same cluster, and several of them Save and reload to assert
22
+ // persistence -- the same whole-config write the API spec issues, just from the
23
+ // browser. The Read-only operator describe also asserts the fields are not
24
+ // editable, which only holds if nobody else is mid-edit. The client-side
25
+ // negative tests would survive parallelising on their own, but they share the
26
+ // form with the ones that save. See docs/serial-specs.md.
20
27
  _8_10_1.test.describe.configure({ mode: 'serial' });
21
28
  _8_10_1.test.describe('Optimize Data Filters UI @optimize-data-filters-ui @8.10', () => {
22
29
  _8_10_1.test.slow();
@@ -5,6 +5,16 @@ const _8_10_1 = require("../../fixtures/8.10");
5
5
  const apiHelpers_1 = require("../../utils/apiHelpers");
6
6
  const randomName_1 = require("../../utils/randomName");
7
7
  const sleep_1 = require("../../utils/sleep");
8
+ // Serial is load-bearing: the two describes below each request a DIFFERENT
9
+ // exporter setting on the same cluster-wide singleton in beforeAll
10
+ // (variableValueTypeExclusion: ['Object'] vs optimizeModeEnabled: true), then
11
+ // assert on what Optimize actually imported over a ~180s poll. These are
12
+ // PARTIAL updates, not whole configs -- setFilters below re-reads the live
13
+ // config and sends {...current, ...config}, so in declaration order the second
14
+ // beforeAll inherits ['Object'] from the first. Serial order is what makes that
15
+ // inheritance deterministic; run concurrently, each beforeAll read-modify-writes
16
+ // on top of whatever the other left behind and which settings are active during
17
+ // an assertion depends on interleaving. See docs/serial-specs.md.
8
18
  _8_10_1.test.describe.configure({ mode: 'serial' });
9
19
  const VARIABLE_DEFINITION_FILE = './resources/Job_Worker_Process.bpmn';
10
20
  const VARIABLE_DEFINITION_KEY = 'Job_Worker_Process';
@@ -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.1175",
3
+ "version": "0.0.1177",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",