@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
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
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();
|