@camunda/e2e-test-suite 0.0.917 → 0.0.919

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,6 +79,7 @@ declare class ClusterDetailsPage {
79
79
  createAPIClientAndReturnVariables(name: string, setEnvVariables?: boolean): Promise<{
80
80
  [key: string]: string;
81
81
  }>;
82
+ private dismissCreateClientDialogIfPresent;
82
83
  createAPIClient(name: string): Promise<void>;
83
84
  clickEnvVarsButton(): Promise<void>;
84
85
  clientCredentialsText(regex: RegExp): Promise<string>;
@@ -484,21 +484,59 @@ class ClusterDetailsPage {
484
484
  }
485
485
  return variables;
486
486
  }
487
+ // A rejected POST .../clients leaves the create dialog open with an inline
488
+ // "Error fetch error" banner instead of closing it. Carbon's modal overlay
489
+ // then intercepts pointer events for the whole page, so nothing else can be
490
+ // clicked until the dialog is gone.
491
+ async dismissCreateClientDialogIfPresent() {
492
+ const isOpen = await this.createClientCredentialsDialog
493
+ .isVisible()
494
+ .catch(() => false);
495
+ if (!isOpen) {
496
+ return;
497
+ }
498
+ await this.page.keyboard.press('Escape');
499
+ try {
500
+ await (0, test_1.expect)(this.createClientCredentialsDialog).not.toBeVisible({
501
+ timeout: 10000,
502
+ });
503
+ }
504
+ catch {
505
+ await this.page.reload();
506
+ await this.page.waitForLoadState('domcontentloaded');
507
+ await this.clickAPITab();
508
+ }
509
+ }
487
510
  async createAPIClient(name) {
488
- await this.clickCreateClientButton();
489
- await (0, test_1.expect)(this.createClientCredentialsDialog).toBeVisible({
490
- timeout: 30000,
491
- });
492
- await this.fillAPIClientName(name);
493
- await this.checkOrchestrationClusterCheckbox();
494
- await this.checkOptimizeCheckbox();
495
- await this.checkSecretsCheckbox();
496
- await this.clickCreateButton();
497
- // The credentials dialog only renders once POST .../clients resolves, and
498
- // that call can take well over 20s on a freshly created cluster.
499
- await (0, test_1.expect)(this.clientCredentialsDialog).toBeVisible({
500
- timeout: 60000,
501
- });
511
+ const maxAttempts = 3;
512
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
513
+ await this.clickCreateClientButton();
514
+ await (0, test_1.expect)(this.createClientCredentialsDialog).toBeVisible({
515
+ timeout: 30000,
516
+ });
517
+ await this.fillAPIClientName(name);
518
+ await this.checkOrchestrationClusterCheckbox();
519
+ await this.checkOptimizeCheckbox();
520
+ await this.checkSecretsCheckbox();
521
+ await this.clickCreateButton();
522
+ // The credentials dialog only renders once POST .../clients resolves, and
523
+ // that call can take well over 20s on a freshly created cluster. When the
524
+ // Console API rejects the call instead, the credentials dialog never
525
+ // arrives and the create dialog stays open on its error banner, so close
526
+ // it and retry the create from a clean state rather than leaving the page
527
+ // wedged behind a modal nobody dismissed.
528
+ const created = await this.clientCredentialsDialog
529
+ .waitFor({ state: 'visible', timeout: 60000 })
530
+ .then(() => true)
531
+ .catch(() => false);
532
+ if (created) {
533
+ break;
534
+ }
535
+ await this.dismissCreateClientDialogIfPresent();
536
+ if (attempt === maxAttempts) {
537
+ throw new Error(`Client credentials dialog never appeared for API client "${name}" after ${maxAttempts} attempts`);
538
+ }
539
+ }
502
540
  await (0, test_1.expect)(this.clientCredentialsDialog.getByText('The Client Secret will not be shown again.')).toBeVisible({ timeout: 30000 });
503
541
  // The clients table behind the dialog re-fetches after the create, and
504
542
  // renders a skeleton (no `row` roles at all) until that GET resolves — so
@@ -21,6 +21,8 @@ declare class ClusterSecretsPage {
21
21
  constructor(page: Page);
22
22
  deleteConnectorSecretsIfExist(): Promise<void>;
23
23
  clickCreateNewSecretButton(cluster: string): Promise<void>;
24
+ private dismissOpenDialogIfPresent;
25
+ private secretExists;
24
26
  clickClusterBanner(): Promise<void>;
25
27
  clickCluster(name: string): Promise<void>;
26
28
  clickKeyInput(): Promise<void>;
@@ -107,7 +107,41 @@ class ClusterSecretsPage {
107
107
  await this.createNewSecretButton.click({ timeout: 30000 });
108
108
  }
109
109
  }
110
+ // A rejected Console API call leaves the create/import modal open with an
111
+ // inline "Error fetch error" banner instead of closing it. Carbon's modal
112
+ // overlay then intercepts pointer events for the whole page, so nothing else
113
+ // can be clicked until the dialog is gone.
114
+ async dismissOpenDialogIfPresent() {
115
+ const isOpen = await this.dialog.isVisible().catch(() => false);
116
+ if (!isOpen) {
117
+ return;
118
+ }
119
+ await this.page.keyboard.press('Escape');
120
+ try {
121
+ await (0, test_1.expect)(this.dialog).not.toBeVisible({ timeout: 10000 });
122
+ }
123
+ catch {
124
+ await this.page.reload();
125
+ await (0, test_1.expect)(this.dialog).not.toBeVisible({ timeout: 30000 });
126
+ }
127
+ }
128
+ async secretExists(name) {
129
+ try {
130
+ await (0, test_1.expect)(this.secretsSearchBox).toBeVisible({ timeout: 30000 });
131
+ await this.secretsSearchBox.click();
132
+ await this.secretsSearchBox.fill(name);
133
+ await (0, test_1.expect)(this.page.getByRole('row', { name: new RegExp(`^${name}\\b`, 'i') })).toBeVisible({ timeout: 10000 });
134
+ return true;
135
+ }
136
+ catch {
137
+ return false;
138
+ }
139
+ }
110
140
  async clickClusterBanner() {
141
+ // This is the recovery path taken when a click times out, and a leftover
142
+ // modal overlay is exactly what makes those clicks time out - so the
143
+ // overlay has to be cleared here or recovery can never succeed either.
144
+ await this.dismissOpenDialogIfPresent();
111
145
  await (0, test_1.expect)(this.clusterBanner).toBeVisible({ timeout: 30000 });
112
146
  await this.clusterBanner.click({ timeout: 60000 });
113
147
  }
@@ -153,20 +187,41 @@ class ClusterSecretsPage {
153
187
  await this.createButton.click({ timeout: 30000 });
154
188
  }
155
189
  async createNewSecret(key, value, cluster) {
156
- await this.clickCreateNewSecretButton(cluster);
157
- await this.clickKeyInput();
158
- await this.fillKeyInput(key);
159
- await (0, sleep_1.sleep)(1000);
160
- await this.clickValueInput();
161
- await this.fillValueInput(value);
162
- await (0, sleep_1.sleep)(1000);
163
- await this.clickCreateButton();
164
- await (0, test_1.expect)(this.page.getByText('Creating...')).not.toBeVisible({
165
- timeout: 60000,
166
- });
167
- await (0, test_1.expect)(this.createNewSecretButton).toBeVisible({
168
- timeout: 60000,
169
- });
190
+ const maxAttempts = 3;
191
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
192
+ await this.clickCreateNewSecretButton(cluster);
193
+ await this.clickKeyInput();
194
+ await this.fillKeyInput(key);
195
+ await (0, sleep_1.sleep)(1000);
196
+ await this.clickValueInput();
197
+ await this.fillValueInput(value);
198
+ await (0, sleep_1.sleep)(1000);
199
+ await this.clickCreateButton();
200
+ await (0, test_1.expect)(this.page.getByText('Creating...')).not.toBeVisible({
201
+ timeout: 60000,
202
+ });
203
+ // The dialog closing is the only reliable signal that the create actually
204
+ // succeeded: when the Console API rejects the POST the dialog stays open
205
+ // on an inline "Error fetch error" banner, yet createNewSecretButton is
206
+ // still in the DOM behind the overlay so waiting on it passes regardless.
207
+ // Returning from there wedged every later interaction on the page, so
208
+ // dismiss the modal and retry the create instead.
209
+ const closed = await this.dialog
210
+ .waitFor({ state: 'hidden', timeout: 30000 })
211
+ .then(() => true)
212
+ .catch(() => false);
213
+ if (closed) {
214
+ await (0, test_1.expect)(this.createNewSecretButton).toBeVisible({
215
+ timeout: 60000,
216
+ });
217
+ return;
218
+ }
219
+ await this.dismissOpenDialogIfPresent();
220
+ if (await this.secretExists(key)) {
221
+ return;
222
+ }
223
+ }
224
+ throw new Error(`Failed to create connector secret "${key}" after ${maxAttempts} attempts`);
170
225
  }
171
226
  async createSetOfSecrets(cluster, secrets) {
172
227
  await this.bulkImportSecrets(cluster, secrets);
@@ -28,10 +28,14 @@ _8_10_1.test.describe('Navigation Tests', () => {
28
28
  await appsPage.clickConsoleLink();
29
29
  await (0, test_1.expect)(homePage.consoleBanner).toBeVisible({ timeout: 120000 });
30
30
  });
31
- (0, _8_10_1.test)('Navigate to Operate', async ({ appsPage, operateHomePage }) => {
31
+ (0, _8_10_1.test)('Navigate to Operate', async ({ page, appsPage, operateHomePage }) => {
32
32
  await appsPage.clickCamundaApps();
33
33
  await appsPage.clickOperate(clusterName);
34
- await (0, test_1.expect)(operateHomePage.operateBanner).toBeVisible({ timeout: 120000 });
34
+ // Operate is a Vite SPA: when a module chunk 404s mid-deploy it renders a
35
+ // terminal "Something went wrong - Please reload the page" error boundary
36
+ // on which no amount of waiting helps. Reload between attempts so the next
37
+ // one fetches an index.html whose asset hashes resolve.
38
+ await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(page, operateHomePage.operateBanner, 'Operate banner', 60000, false, 3);
35
39
  });
36
40
  (0, _8_10_1.test)('Navigate to Optimize', async ({ appsPage, optimizeHomePage }) => {
37
41
  await appsPage.clickCamundaApps();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camunda/e2e-test-suite",
3
- "version": "0.0.917",
3
+ "version": "0.0.919",
4
4
  "description": "End-to-end test helpers for Camunda 8",
5
5
  "repository": {
6
6
  "type": "git",