@camunda/e2e-test-suite 0.0.1052 → 0.0.1053
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.
|
@@ -3,6 +3,7 @@ import { McpCredentials } from '../../utils/mcpSecrets';
|
|
|
3
3
|
import { ClientCredentialsDetailsPage } from './ClientCredentialsDetailsPage';
|
|
4
4
|
declare class ClusterDetailsPage {
|
|
5
5
|
private page;
|
|
6
|
+
private pendingClientScopeCheck;
|
|
6
7
|
readonly apiTab: Locator;
|
|
7
8
|
readonly createFirstClientButton: Locator;
|
|
8
9
|
readonly clientNameTextbox: Locator;
|
|
@@ -62,6 +63,8 @@ declare class ClusterDetailsPage {
|
|
|
62
63
|
clickOptimizeCheckbox(): Promise<void>;
|
|
63
64
|
clickCreateButton(): Promise<void>;
|
|
64
65
|
clickCloseModalButton(): Promise<void>;
|
|
66
|
+
assertAPIClientScopes(name: string): Promise<void>;
|
|
67
|
+
filterClientsList(name: string): Promise<void>;
|
|
65
68
|
clickSettingsTab(): Promise<void>;
|
|
66
69
|
enableAuthorizations(): Promise<void>;
|
|
67
70
|
disableAuthorizations(): Promise<void>;
|
|
@@ -8,6 +8,9 @@ const expectLocatorWithRetry_1 = require("../../utils/assertionHelpers/expectLoc
|
|
|
8
8
|
const ClientCredentialsDetailsPage_1 = require("./ClientCredentialsDetailsPage");
|
|
9
9
|
class ClusterDetailsPage {
|
|
10
10
|
page;
|
|
11
|
+
// Name of a client whose scopes still have to be checked against the
|
|
12
|
+
// clients list — see createAPIClient() / clickCloseModalButton().
|
|
13
|
+
pendingClientScopeCheck = null;
|
|
11
14
|
apiTab;
|
|
12
15
|
createFirstClientButton;
|
|
13
16
|
clientNameTextbox;
|
|
@@ -235,6 +238,47 @@ class ClusterDetailsPage {
|
|
|
235
238
|
}
|
|
236
239
|
async clickCloseModalButton() {
|
|
237
240
|
await this.closeModalButton.click({ timeout: 60000 });
|
|
241
|
+
const pendingClient = this.pendingClientScopeCheck;
|
|
242
|
+
if (pendingClient === null) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
this.pendingClientScopeCheck = null;
|
|
246
|
+
await this.assertAPIClientScopes(pendingClient);
|
|
247
|
+
}
|
|
248
|
+
// Asserts the scopes an API client was created with, read from its row in
|
|
249
|
+
// the clients list. Only meaningful once the credentials modal is closed:
|
|
250
|
+
// while it is open the panel behind it can still be showing its pre-create
|
|
251
|
+
// state and lists no row for the new client at all.
|
|
252
|
+
async assertAPIClientScopes(name) {
|
|
253
|
+
const row = this.clientsList.filter({ hasText: name });
|
|
254
|
+
const scopes = /(?=.*Orchestration)(?=.*Optimize)(?=.*Secrets)/;
|
|
255
|
+
await this.filterClientsList(name);
|
|
256
|
+
try {
|
|
257
|
+
await (0, test_1.expect)(row).toContainText(scopes, { timeout: 30000 });
|
|
258
|
+
}
|
|
259
|
+
catch (error) {
|
|
260
|
+
// A panel that rendered before the client existed — or that failed into
|
|
261
|
+
// the "Oops ... something went wrong." state — only picks the new client
|
|
262
|
+
// up on a refetch, which is what reloading the API tab forces.
|
|
263
|
+
console.warn(`assertAPIClientScopes: "${name}" is not listed yet; reloading the API tab and retrying. ${error}`);
|
|
264
|
+
await this.dismissOopsIfPresent();
|
|
265
|
+
await this.page.reload({ waitUntil: 'domcontentloaded' });
|
|
266
|
+
await this.clickAPITab();
|
|
267
|
+
await this.filterClientsList(name);
|
|
268
|
+
await (0, test_1.expect)(row).toContainText(scopes, { timeout: 60000 });
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// Narrows the clients table to one client through the table's own filter.
|
|
272
|
+
// The table paginates at ten rows and is not ordered by creation time, so on
|
|
273
|
+
// a cluster that already has clients a newly created one is usually on a
|
|
274
|
+
// page other than the one being displayed — matching a row without filtering
|
|
275
|
+
// first reports it as missing.
|
|
276
|
+
async filterClientsList(name) {
|
|
277
|
+
const filterInput = this.page.getByRole('searchbox', {
|
|
278
|
+
name: 'Filter table',
|
|
279
|
+
});
|
|
280
|
+
await (0, test_1.expect)(filterInput).toBeVisible({ timeout: 60000 });
|
|
281
|
+
await filterInput.fill(name);
|
|
238
282
|
}
|
|
239
283
|
async clickSettingsTab() {
|
|
240
284
|
await this.settingsTab.click({ timeout: 60000 });
|
|
@@ -515,24 +559,16 @@ class ClusterDetailsPage {
|
|
|
515
559
|
await (0, test_1.expect)(this.clientCredentialsDialog
|
|
516
560
|
.getByText('The Client Secret will not be shown again.')
|
|
517
561
|
.first()).toBeVisible();
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
const oopsVisible = await this.page
|
|
529
|
-
.getByRole('heading', { name: /Oops/i })
|
|
530
|
-
.isVisible({ timeout: 1000 })
|
|
531
|
-
.catch(() => false);
|
|
532
|
-
if (!oopsVisible)
|
|
533
|
-
throw err;
|
|
534
|
-
console.warn(`createAPIClient: clients list panel is in Oops state; skipping row scope assertion for "${attemptName}".`);
|
|
535
|
-
}
|
|
562
|
+
// The client now exists — the modal above is proof — so the scopes it
|
|
563
|
+
// was created with are checked in clickCloseModalButton() rather than
|
|
564
|
+
// here. The clients list behind the open modal is not a reliable
|
|
565
|
+
// source for that check: it can still be rendering its pre-create
|
|
566
|
+
// state (no row for this client) or have failed into "Oops ...
|
|
567
|
+
// something went wrong.", and the modal blocks the pointer events a
|
|
568
|
+
// reload would need. Asserting it here instead sent a created client
|
|
569
|
+
// round the retry loop, which both left orphan clients behind and
|
|
570
|
+
// discarded the credentials the caller has yet to read.
|
|
571
|
+
this.pendingClientScopeCheck = attemptName;
|
|
536
572
|
return;
|
|
537
573
|
}
|
|
538
574
|
catch (error) {
|
|
@@ -152,7 +152,6 @@ class ProcessApplicationPage {
|
|
|
152
152
|
// taken -- callers that care assert the name in the deploy dialog anyway.
|
|
153
153
|
async defineDevelopmentStage(clusterName) {
|
|
154
154
|
await (0, test_1.expect)(this.defineStagesDialog).toBeVisible({ timeout: 60000 });
|
|
155
|
-
await this.developmentClusterSelect.click({ timeout: 30000 });
|
|
156
155
|
// Escape before interpolating: a cluster name can carry regex
|
|
157
156
|
// metacharacters -- "Test Cluster (v2)" is the common case -- which would
|
|
158
157
|
// otherwise mis-match or throw. Same treatment as
|
|
@@ -166,7 +165,31 @@ class ProcessApplicationPage {
|
|
|
166
165
|
.getByRole('option')
|
|
167
166
|
.filter({ hasNotText: 'Select a development cluster' })
|
|
168
167
|
.first();
|
|
169
|
-
|
|
168
|
+
// Committing a selection is what unmounts the option list: the dropdown is
|
|
169
|
+
// a Carbon Dropdown over Downshift, so the click that lands is also the
|
|
170
|
+
// click whose target disappears. Playwright's default actionability re-check
|
|
171
|
+
// reads that as a lost element ("element was detached from the DOM,
|
|
172
|
+
// retrying") and then spends the rest of its budget waiting for an option
|
|
173
|
+
// that has already done its job and will not come back -- three of the four
|
|
174
|
+
// failures in the 2026-08-31 SaaS 8.10 run ended that way, each on a
|
|
175
|
+
// 30000ms locator.click against an option the log shows it had resolved.
|
|
176
|
+
//
|
|
177
|
+
// So: dispatch the click without the post-click stability re-check, and
|
|
178
|
+
// treat the option list closing as the proof it landed rather than the
|
|
179
|
+
// click call resolving cleanly. Retried as a whole because a click that
|
|
180
|
+
// genuinely missed leaves the menu open, which the next pass sees and
|
|
181
|
+
// re-clicks. This also covers the menu opening with the wanted cluster
|
|
182
|
+
// already selected: clicking it still closes the menu, and the stage the
|
|
183
|
+
// caller asked for is bound either way.
|
|
184
|
+
await (0, test_1.expect)(async () => {
|
|
185
|
+
const menuOpen = await option.isVisible().catch(() => false);
|
|
186
|
+
if (!menuOpen) {
|
|
187
|
+
await this.developmentClusterSelect.click({ timeout: 15000 });
|
|
188
|
+
await (0, test_1.expect)(option).toBeVisible({ timeout: 15000 });
|
|
189
|
+
}
|
|
190
|
+
await option.click({ timeout: 15000, force: true });
|
|
191
|
+
await (0, test_1.expect)(option).toBeHidden({ timeout: 15000 });
|
|
192
|
+
}).toPass({ timeout: 120000, intervals: [1000, 2000, 5000] });
|
|
170
193
|
await this.defineStagesSaveButton.click({ timeout: 30000 });
|
|
171
194
|
await (0, test_1.expect)(this.defineStagesDialog).toBeHidden({ timeout: 60000 });
|
|
172
195
|
}
|
|
@@ -50,9 +50,22 @@ _8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
50
50
|
await (0, test_1.expect)(modelerCreatePage.viewProcessInstanceLink).toBeVisible({
|
|
51
51
|
timeout: 120000,
|
|
52
52
|
});
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
// Latch the Operate instance deep link on the CONTEXT before the click.
|
|
54
|
+
// Playwright raises `popup` only once the new tab's initial response has
|
|
55
|
+
// started loading, and a session-less Operate tab's auth redirect chain
|
|
56
|
+
// drops the instance path before anything commits -- so no listener
|
|
57
|
+
// attached to the popup afterwards ever sees that URL. Without it
|
|
58
|
+
// `waitForInstanceLocator` has nothing to re-open and can only reload
|
|
59
|
+
// whatever the tab was left on, where the instance page can never render:
|
|
60
|
+
// "Assertion of Instance History panel in Operate timed out after
|
|
61
|
+
// 480000ms (instance header not rendered, url=<cluster>/operate?continue,
|
|
62
|
+
// instance deep link never captured)". `openAppTabWithRetry` also
|
|
63
|
+
// registers the popup wait BEFORE the click, which `waitForEvent` after
|
|
64
|
+
// the click cannot. Same pattern as tests/8.10/smoke-tests.spec.ts.
|
|
65
|
+
const watchedInstanceUrl = (0, OperateProcessInstancePage_1.watchOperateInstanceUrl)(page.context());
|
|
66
|
+
const operateTab = await (0, UtilitiesPage_1.openAppTabWithRetry)(page, modelerCreatePage.viewProcessInstanceLink);
|
|
55
67
|
const operateTabProcessInstancePage = new OperateProcessInstancePage_1.OperateProcessInstancePage(operateTab);
|
|
68
|
+
await operateTabProcessInstancePage.rememberInstanceUrl(60000, watchedInstanceUrl);
|
|
56
69
|
await operateTabProcessInstancePage.closePopOverIfVisible();
|
|
57
70
|
await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTabProcessInstancePage, operateTabProcessInstancePage.completedIcon, 'Completed icon', 60000);
|
|
58
71
|
await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTab, operateTabProcessInstancePage.variablesList, 'Variable list', 30000);
|
|
@@ -92,9 +105,12 @@ _8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
92
105
|
await (0, test_1.expect)(modelerCreatePage.viewProcessInstanceLink).toBeVisible({
|
|
93
106
|
timeout: 240000,
|
|
94
107
|
});
|
|
95
|
-
|
|
96
|
-
|
|
108
|
+
// Context-level deep-link latch + click-then-wait ordering, as in the
|
|
109
|
+
// "REST Connector No Auth" flow above -- see the note there.
|
|
110
|
+
const watchedInstanceUrl = (0, OperateProcessInstancePage_1.watchOperateInstanceUrl)(page.context());
|
|
111
|
+
const operateTab = await (0, UtilitiesPage_1.openAppTabWithRetry)(page, modelerCreatePage.viewProcessInstanceLink);
|
|
97
112
|
const operateTabProcessInstancePage = new OperateProcessInstancePage_1.OperateProcessInstancePage(operateTab);
|
|
113
|
+
await operateTabProcessInstancePage.rememberInstanceUrl(60000, watchedInstanceUrl);
|
|
98
114
|
await operateTabProcessInstancePage.closePopOverIfVisible();
|
|
99
115
|
await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTabProcessInstancePage, operateTabProcessInstancePage.completedIcon, 'Completed icon', 60000);
|
|
100
116
|
await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTab, operateTabProcessInstancePage.variablesList, 'Variable list', 60000);
|
|
@@ -328,9 +344,12 @@ _8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
328
344
|
await (0, test_1.expect)(modelerCreatePage.viewProcessInstanceLink).toBeVisible({
|
|
329
345
|
timeout: 120000,
|
|
330
346
|
});
|
|
331
|
-
|
|
332
|
-
|
|
347
|
+
// Context-level deep-link latch + click-then-wait ordering, as in the
|
|
348
|
+
// "REST Connector No Auth" flow above -- see the note there.
|
|
349
|
+
const watchedInstanceUrl = (0, OperateProcessInstancePage_1.watchOperateInstanceUrl)(page.context());
|
|
350
|
+
const operateTab = await (0, UtilitiesPage_1.openAppTabWithRetry)(page, modelerCreatePage.viewProcessInstanceLink);
|
|
333
351
|
const operateTabProcessInstancePage = new OperateProcessInstancePage_1.OperateProcessInstancePage(operateTab);
|
|
352
|
+
await operateTabProcessInstancePage.rememberInstanceUrl(60000, watchedInstanceUrl);
|
|
334
353
|
await operateTabProcessInstancePage.closePopOverIfVisible();
|
|
335
354
|
await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTabProcessInstancePage, operateTabProcessInstancePage.completedIcon, 'Completed icon', 60000);
|
|
336
355
|
await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTab, operateTab.getByTestId('variables-list'), 'Variable list', 30000);
|
|
@@ -448,10 +467,12 @@ _8_10_1.test.describe('Connectors User Flow Tests @tasklistV2', () => {
|
|
|
448
467
|
await (0, test_1.expect)(modelerCreatePage.viewProcessInstanceLink).toBeVisible({
|
|
449
468
|
timeout: 120000,
|
|
450
469
|
});
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
const
|
|
470
|
+
// Context-level deep-link latch + click-then-wait ordering, as in the
|
|
471
|
+
// "REST Connector No Auth" flow above -- see the note there.
|
|
472
|
+
const watchedInstanceUrl = (0, OperateProcessInstancePage_1.watchOperateInstanceUrl)(page.context());
|
|
473
|
+
const operateTab = await (0, UtilitiesPage_1.openAppTabWithRetry)(page, modelerCreatePage.viewProcessInstanceLink);
|
|
454
474
|
const operateTabProcessInstancePage = new OperateProcessInstancePage_1.OperateProcessInstancePage(operateTab);
|
|
475
|
+
await operateTabProcessInstancePage.rememberInstanceUrl(60000, watchedInstanceUrl);
|
|
455
476
|
await operateTabProcessInstancePage.closePopOverIfVisible();
|
|
456
477
|
await (0, UtilitiesPage_1.assertLocatorVisibleWithRetry)(operateTabProcessInstancePage, operateTabProcessInstancePage.completedIcon, 'completed icon');
|
|
457
478
|
// Operate no longer displays document filenames or storage metadata
|