@camunda/e2e-test-suite 0.0.775 → 0.0.777

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.
@@ -79,7 +79,10 @@ class OperateProcessesPage {
79
79
  await this.processFinishedInstancesCheckbox.click();
80
80
  }
81
81
  async clickProcessInstanceLink(processName) {
82
- const maxRetries = 3;
82
+ // Increased from 3 to 5 retries: process-instance indexing in Operate can take
83
+ // longer than 3 × (60 s visible + 10 s reload) on busy SaaS environments,
84
+ // causing intermittent failures in the webhook connector nightly tests.
85
+ const maxRetries = 5;
83
86
  for (let retries = 0; retries < maxRetries; retries++) {
84
87
  try {
85
88
  await (0, test_1.expect)(this.processInstanceLink(processName)).toBeVisible({
@@ -398,8 +398,13 @@ class ModelerCreatePage {
398
398
  await this.userTaskOption.click();
399
399
  }
400
400
  async clickForm(name) {
401
+ // Baseline upgrade runs use fixed form names, so a Playwright retry re-creates
402
+ // a form with the same name and the picker then lists two identical entries.
403
+ // Scope to the first matching item so the link step stays deterministic on retry
404
+ // instead of failing with a strict-mode violation.
401
405
  await this.page
402
406
  .locator(`[data-test="item-${name}"]`)
407
+ .first()
403
408
  .getByText(name)
404
409
  .click({ timeout: 90000 });
405
410
  }
@@ -364,8 +364,12 @@ class ClusterPage {
364
364
  }
365
365
  }
366
366
  async assertClusterHealthyStatusWithRetry(name, visibilityTimeout = 100000, totalTimeout = 420000, isUpgradeScenario = false) {
367
- // For upgrade scenarios, use much longer timeouts and more retries instead of fixed waits
368
- const actualTotalTimeout = isUpgradeScenario ? 900000 : totalTimeout; // 15 minutes for upgrades
367
+ // For upgrade scenarios, use much longer timeouts and more retries instead of fixed waits.
368
+ // After the 8.7->8.8 upgrade the cluster's components report healthy and the generation
369
+ // flips to 8.8 well before the cluster-list overall Status leaves "Updating"; that overall
370
+ // flip to "Healthy" can lag past 15 min, so allow 25 min here (job timeout is 60 min and the
371
+ // caller runs under test.slow()).
372
+ const actualTotalTimeout = isUpgradeScenario ? 1500000 : totalTimeout; // 25 minutes for upgrades
369
373
  const actualVisibilityTimeout = isUpgradeScenario
370
374
  ? 15000
371
375
  : visibilityTimeout; // Shorter individual checks
@@ -17,6 +17,7 @@ declare class FormJsPage {
17
17
  readonly deploySubButton: Locator;
18
18
  readonly dialog: Locator;
19
19
  readonly deploySuccessNotification: Locator;
20
+ readonly deployErrorMessage: Locator;
20
21
  constructor(page: Page);
21
22
  generateAIForm(request?: string): Promise<void>;
22
23
  clickAIFormGeneratorButton(): Promise<void>;
@@ -21,6 +21,7 @@ class FormJsPage {
21
21
  deploySubButton;
22
22
  dialog;
23
23
  deploySuccessNotification;
24
+ deployErrorMessage;
24
25
  constructor(page) {
25
26
  this.page = page;
26
27
  this.aiFormGeneratorButton = page.getByRole('button', {
@@ -54,6 +55,7 @@ class FormJsPage {
54
55
  this.deploySuccessNotification = page
55
56
  .locator('.cds--toast-notification--success')
56
57
  .filter({ hasText: 'Form deployed!' });
58
+ this.deployErrorMessage = this.dialog.getByText('The form failed to deploy');
57
59
  }
58
60
  async generateAIForm(request = 'Create a form with the following fields: 1. A text field with the label "Full Name" 2. A number with the label "Count" 3. A date input with the label "Date of birth"4. A Checkbox with the label "Agree"') {
59
61
  await (0, test_1.expect)(this.aiFormGeneratorButton).toBeVisible({
@@ -120,13 +122,34 @@ class FormJsPage {
120
122
  await this.deploySubButton.click({ timeout: 60000 });
121
123
  }
122
124
  async deployForm(clusterName) {
123
- await this.clickDeployButton();
124
- await (0, test_1.expect)(this.dialog).toBeVisible({
125
- timeout: 30000,
126
- });
127
- await this.selectCluster(clusterName);
128
- await this.clickDeploySubButton();
129
- await (0, test_1.expect)(this.deploySuccessNotification).toBeVisible({ timeout: 30000 });
125
+ // After a 8.7->8.8 upgrade, orchestration-cluster authorization commands
126
+ // propagate across partitions for several minutes; deploy is rejected with
127
+ // FORBIDDEN until the deployer's grant lands. In migration runs, reload and
128
+ // retry through that propagation window instead of failing on the transient
129
+ // rejection. Non-migration runs keep the original single-attempt behavior.
130
+ const maxAttempts = process.env.IS_MIGRATION === 'true' ? 15 : 1;
131
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
132
+ await this.clickDeployButton();
133
+ await (0, test_1.expect)(this.dialog).toBeVisible({
134
+ timeout: 30000,
135
+ });
136
+ await this.selectCluster(clusterName);
137
+ await this.clickDeploySubButton();
138
+ if (attempt === maxAttempts) {
139
+ await (0, test_1.expect)(this.deploySuccessNotification).toBeVisible({
140
+ timeout: 30000,
141
+ });
142
+ return;
143
+ }
144
+ const deployed = await this.deploySuccessNotification
145
+ .waitFor({ state: 'visible', timeout: 30000 })
146
+ .then(() => true)
147
+ .catch(() => false);
148
+ if (deployed) {
149
+ return;
150
+ }
151
+ await this.page.reload();
152
+ }
130
153
  }
131
154
  }
132
155
  exports.FormJsPage = FormJsPage;
@@ -12,6 +12,7 @@ declare class ModelerCreatePage {
12
12
  readonly startInstanceMainButton: Locator;
13
13
  readonly startInstanceSubButton: Locator;
14
14
  readonly viewProcessInstanceLink: Locator;
15
+ readonly deployErrorMessage: Locator;
15
16
  readonly nameInput: Locator;
16
17
  readonly diagramBreadcrumb: Locator;
17
18
  readonly renameDiagramNameButton: Locator;
@@ -18,6 +18,7 @@ class ModelerCreatePage {
18
18
  startInstanceMainButton;
19
19
  startInstanceSubButton;
20
20
  viewProcessInstanceLink;
21
+ deployErrorMessage;
21
22
  nameInput;
22
23
  diagramBreadcrumb;
23
24
  renameDiagramNameButton;
@@ -147,6 +148,7 @@ class ModelerCreatePage {
147
148
  this.viewProcessLink = page.getByRole('button', {
148
149
  name: 'View process in Operate',
149
150
  });
151
+ this.deployErrorMessage = page.getByText('The diagram failed to deploy & run');
150
152
  this.nameInput = page.getByLabel('Name', { exact: true });
151
153
  this.diagramBreadcrumb = page.locator('[data-test="breadcrumb-diagram"]');
152
154
  this.renameDiagramNameButton = page.getByText('Rename');
@@ -545,7 +547,13 @@ class ModelerCreatePage {
545
547
  }
546
548
  }
547
549
  async runProcessInstance(clusterName, variables = '', formVariables = {}, textToVisible = '') {
548
- const maxRetries = 10;
550
+ // After a 8.7->8.8 upgrade, orchestration-cluster authorization commands
551
+ // propagate across partitions for several minutes; deploy & run is rejected
552
+ // with FORBIDDEN until the deployer's grant lands. In migration runs, detect
553
+ // that rejection and retry through the propagation window (more attempts),
554
+ // rather than returning a silently-failed run.
555
+ const isMigration = process.env.IS_MIGRATION === 'true';
556
+ const maxRetries = isMigration ? 20 : 10;
549
557
  const retryDelayMs = 20000;
550
558
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
551
559
  try {
@@ -576,6 +584,15 @@ class ModelerCreatePage {
576
584
  });
577
585
  }
578
586
  await this.clickStartInstanceSubButton();
587
+ if (isMigration) {
588
+ const deployRejected = await this.deployErrorMessage
589
+ .waitFor({ state: 'visible', timeout: 5000 })
590
+ .then(() => true)
591
+ .catch(() => false);
592
+ if (deployRejected) {
593
+ throw new Error('Deploy & run rejected; authorization may still be propagating after upgrade');
594
+ }
595
+ }
579
596
  return;
580
597
  }
581
598
  catch (error) {
@@ -79,7 +79,10 @@ class OperateProcessesPage {
79
79
  await this.processFinishedInstancesCheckbox.click();
80
80
  }
81
81
  async clickProcessInstanceLink(processName) {
82
- const maxRetries = 3;
82
+ // Increased from 3 to 5 retries: process-instance indexing in Operate can take
83
+ // longer than 3 × (60 s visible + 10 s reload) on busy SaaS environments,
84
+ // causing intermittent failures in the webhook connector nightly tests.
85
+ const maxRetries = 5;
83
86
  for (let retries = 0; retries < maxRetries; retries++) {
84
87
  try {
85
88
  await (0, test_1.expect)(this.processInstanceLink(processName)).toBeVisible({
@@ -79,7 +79,10 @@ class OperateProcessesPage {
79
79
  await this.processFinishedInstancesCheckbox.click();
80
80
  }
81
81
  async clickProcessInstanceLink(processName) {
82
- const maxRetries = 3;
82
+ // Increased from 3 to 5 retries: process-instance indexing in Operate can take
83
+ // longer than 3 × (60 s visible + 10 s reload) on busy SaaS environments,
84
+ // causing intermittent failures in the webhook connector nightly tests.
85
+ const maxRetries = 5;
83
86
  for (let retries = 0; retries < maxRetries; retries++) {
84
87
  try {
85
88
  await (0, test_1.expect)(this.processInstanceLink(processName)).toBeVisible({
@@ -100,8 +100,12 @@ _8_7_1.test.describe('Smoke Tests', () => {
100
100
  });
101
101
  });
102
102
  await _8_7_1.test.step('Open process instance in Operate and verify it is active', async () => {
103
- await modelerCreatePage.clickViewProcessInstanceLink();
104
- appTab = await page.waitForEvent('popup', { timeout: TIMEOUT.navigation });
103
+ // Register the popup waiter before the click so the Operate tab is not missed
104
+ // if it opens in the gap between the click resolving and waitForEvent starting.
105
+ [appTab] = await Promise.all([
106
+ page.waitForEvent('popup', { timeout: TIMEOUT.navigation }),
107
+ modelerCreatePage.clickViewProcessInstanceLink(),
108
+ ]);
105
109
  const appTabProcessInstancePage = new OperateProcessInstancePage_1.OperateProcessInstancePage(appTab);
106
110
  await (0, test_1.expect)(appTabProcessInstancePage.activeIcon).toBeVisible({
107
111
  timeout: TIMEOUT.processActive,
@@ -209,10 +213,12 @@ _8_7_1.test.describe('Smoke Tests', () => {
209
213
  });
210
214
  });
211
215
  await _8_7_1.test.step('Assert process completes in Operate and verify connector result', async () => {
212
- await modelerCreatePage.clickViewProcessInstanceLink();
213
- const operateTab = await page.waitForEvent('popup', {
214
- timeout: TIMEOUT.navigation,
215
- });
216
+ // Register the popup waiter before the click so the Operate tab is not missed
217
+ // if it opens in the gap between the click resolving and waitForEvent starting.
218
+ const [operateTab] = await Promise.all([
219
+ page.waitForEvent('popup', { timeout: TIMEOUT.navigation }),
220
+ modelerCreatePage.clickViewProcessInstanceLink(),
221
+ ]);
216
222
  const operateHomePage = new OperateHomePage_1.OperateHomePage(operateTab);
217
223
  const operateProcessesPage = new OperateProcessesPage_1.OperateProcessesPage(operateTab);
218
224
  const operateProcessInstancePage = new OperateProcessInstancePage_1.OperateProcessInstancePage(operateTab);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.775",
3
+ "version": "0.0.777",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",