@camunda/e2e-test-suite 0.0.1287 → 0.0.1289
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.
- package/dist/pages/8.8/AppsPage.d.ts +7 -3
- package/dist/pages/8.8/AppsPage.js +96 -26
- package/dist/pages/8.8/ModelerCreatePage.d.ts +3 -0
- package/dist/pages/8.8/ModelerCreatePage.js +25 -0
- package/dist/pages/8.8/UtilitiesPage.js +34 -17
- package/dist/pages/8.8/WorkspaceMembersPage.d.ts +1 -0
- package/dist/pages/8.8/WorkspaceMembersPage.js +21 -10
- package/dist/pages/8.9/AppsPage.d.ts +9 -3
- package/dist/pages/8.9/AppsPage.js +262 -47
- package/dist/pages/8.9/ClusterDetailsPage.d.ts +5 -0
- package/dist/pages/8.9/ClusterDetailsPage.js +92 -9
- package/dist/pages/8.9/ClusterSecretsPage.js +5 -1
- package/dist/pages/8.9/ConnectorMarketplacePage.d.ts +1 -0
- package/dist/pages/8.9/ConnectorMarketplacePage.js +46 -3
- package/dist/pages/8.9/ConsoleOrganizationPage.d.ts +3 -2
- package/dist/pages/8.9/ConsoleOrganizationPage.js +47 -22
- package/dist/pages/8.9/FormJsPage.d.ts +4 -0
- package/dist/pages/8.9/FormJsPage.js +53 -19
- package/dist/pages/8.9/IdpCreatePage.js +5 -0
- package/dist/pages/8.9/ModelerCreatePage.d.ts +5 -0
- package/dist/pages/8.9/ModelerCreatePage.js +153 -21
- package/dist/pages/8.9/ModelerHomePage.d.ts +1 -0
- package/dist/pages/8.9/ModelerHomePage.js +29 -4
- package/dist/pages/8.9/PlayPage.js +11 -1
- package/dist/pages/8.9/ProcessApplicationPage.js +48 -4
- package/dist/pages/8.9/UtilitiesPage.js +57 -17
- package/dist/pages/8.9/WorkspaceMembersPage.d.ts +21 -0
- package/dist/pages/8.9/WorkspaceMembersPage.js +264 -0
- package/dist/tests/8.8/access-user-role-management.spec.js +57 -29
- package/dist/tests/8.8/console-user-flows.spec.js +22 -1
- package/dist/tests/8.8/rba-enabled-v1-user-flows.spec.js +13 -4
- package/dist/tests/8.8/rba-enabled-v2-user-flows.spec.js +50 -14
- package/dist/tests/8.8/test-setup.spec.js +49 -4
- package/dist/tests/8.9/access-user-role-management.spec.js +111 -26
- package/dist/tests/8.9/console-user-flows.spec.js +4 -4
- package/dist/tests/8.9/idp-user-flows.spec.js +2 -1
- package/dist/tests/8.9/rba-enabled-v1-user-flows.spec.js +13 -4
- package/dist/tests/8.9/rba-enabled-v2-user-flows.spec.js +13 -4
- package/dist/tests/8.9/test-setup.spec.js +57 -4
- package/dist/tests/8.9/utr-enabled-user-flows.spec.js +18 -0
- package/dist/tests/8.9/web-modeler-user-flows.spec.js +6 -2
- package/dist/utils/consoleApiHelpers.d.ts +2 -0
- package/dist/utils/consoleApiHelpers.js +50 -4
- package/dist/utils/resetSession.js +6 -1
- package/package.json +1 -1
|
@@ -19,11 +19,13 @@ declare class AppsPage {
|
|
|
19
19
|
readonly camundaComponentsButton: Locator;
|
|
20
20
|
readonly camundaAppsButton: Locator;
|
|
21
21
|
readonly hubSidebar: Locator;
|
|
22
|
+
private hubReturnUrl?;
|
|
22
23
|
constructor(page: Page);
|
|
23
24
|
clickCluster(component: Locator, name: string): Promise<void>;
|
|
24
25
|
private expandHubClusterGroup;
|
|
25
26
|
private showAllWorkspaces;
|
|
26
27
|
private clickHubClusterApp;
|
|
28
|
+
goBackToHub(): Promise<void>;
|
|
27
29
|
private isOnHub;
|
|
28
30
|
clickModeler(): Promise<void>;
|
|
29
31
|
private clickHubModelerHome;
|
|
@@ -54,8 +56,10 @@ declare class AppsPage {
|
|
|
54
56
|
clickCamundaApps(maxRetries?: number): Promise<void>;
|
|
55
57
|
private dismissBlockingModal;
|
|
56
58
|
clickConsoleLink(): Promise<void>;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
private hubClusterAppLink;
|
|
60
|
+
private assertHubClusterAppPresent;
|
|
61
|
+
assertOperatePresent(clusterName: string, shouldBeVisible?: boolean): Promise<void>;
|
|
62
|
+
assertTasklistPresent(clusterName: string, shouldBeVisible?: boolean): Promise<void>;
|
|
63
|
+
assertOptimizePresent(clusterName: string, shouldBeVisible?: boolean): Promise<void>;
|
|
60
64
|
}
|
|
61
65
|
export { AppsPage };
|
|
@@ -27,9 +27,19 @@ class AppsPage {
|
|
|
27
27
|
camundaComponentsButton;
|
|
28
28
|
camundaAppsButton;
|
|
29
29
|
hubSidebar;
|
|
30
|
+
hubReturnUrl;
|
|
30
31
|
constructor(page) {
|
|
31
32
|
this.page = page;
|
|
32
|
-
|
|
33
|
+
// Hub has no "Modeler" switcher entry; Modeler is Hub's home, reached
|
|
34
|
+
// through the header logo link clickHubModelerHome() uses
|
|
35
|
+
// (NavShellDs.common.tsx). The final .first() keeps this strict-safe on a
|
|
36
|
+
// page that carries both.
|
|
37
|
+
this.modelerLink = page
|
|
38
|
+
.getByRole('link', { name: 'Modeler', exact: true })
|
|
39
|
+
.or(page
|
|
40
|
+
.locator('header[data-slot="app-header"]')
|
|
41
|
+
.getByRole('link', { name: /^Camunda (Modeler|Hub) home$/ }))
|
|
42
|
+
.first();
|
|
33
43
|
this.appSwitcherButton = page.getByLabel('App Switcher');
|
|
34
44
|
this.tasklistLink = page.getByRole('link', { name: 'Tasklist', exact: true });
|
|
35
45
|
this.identityLink = page.getByRole('link', { name: /^(Admin|Identity)$/ });
|
|
@@ -253,14 +263,8 @@ class AppsPage {
|
|
|
253
263
|
// cluster name and portalled OUTSIDE the "Main navigation" <nav> -- so a
|
|
254
264
|
// sidebar-scoped lookup finds nothing there. Look in that popover first;
|
|
255
265
|
// keep the sidebar-scoped lookup for the expanded-sidebar layout, where
|
|
256
|
-
// the links render inline.
|
|
257
|
-
|
|
258
|
-
// (opens in a new tab)").
|
|
259
|
-
const appLink = this.page
|
|
260
|
-
.getByRole('dialog', { name: clusterName, exact: true })
|
|
261
|
-
.getByRole('link', { name: appName })
|
|
262
|
-
.or(this.hubSidebar.getByRole('link', { name: appName }))
|
|
263
|
-
.first();
|
|
266
|
+
// the links render inline.
|
|
267
|
+
const appLink = this.hubClusterAppLink(clusterName, appName);
|
|
264
268
|
// Decide whether to expand from clusterName's OWN toggle state, not from
|
|
265
269
|
// whether "some" appLink is already visible sidebar-wide: in a
|
|
266
270
|
// multi-cluster org another cluster's group can already be expanded with
|
|
@@ -279,11 +283,22 @@ class AppsPage {
|
|
|
279
283
|
// navigation the Console app switcher gave these callers.
|
|
280
284
|
const href = await appLink.getAttribute('href');
|
|
281
285
|
if (href) {
|
|
286
|
+
this.hubReturnUrl = this.page.url();
|
|
282
287
|
await this.page.goto(new URL(href, this.page.url()).toString());
|
|
283
288
|
return;
|
|
284
289
|
}
|
|
285
290
|
await appLink.click({ timeout: 10000 });
|
|
286
291
|
}
|
|
292
|
+
// Returns from an app that clickHubClusterApp opened. page.goBack() alone
|
|
293
|
+
// can stop on one of the app's own login-redirect history entries, and the
|
|
294
|
+
// next Hub sidebar lookup then runs against the app instead of Hub.
|
|
295
|
+
async goBackToHub() {
|
|
296
|
+
await this.page.goBack();
|
|
297
|
+
if (this.hubReturnUrl &&
|
|
298
|
+
!new URL(this.page.url()).hostname.startsWith('hub.')) {
|
|
299
|
+
await this.page.goto(this.hubReturnUrl);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
287
302
|
// True once the tab is on a Camunda Hub host, where Modeler home is Hub's
|
|
288
303
|
// own home rather than a modeler.* hostname.
|
|
289
304
|
isOnHub() {
|
|
@@ -459,10 +474,7 @@ class AppsPage {
|
|
|
459
474
|
}
|
|
460
475
|
async clickIdentity(clusterName) {
|
|
461
476
|
if (new URL(this.page.url()).hostname.startsWith('hub.')) {
|
|
462
|
-
//
|
|
463
|
-
// for Operate/Tasklist/Optimize -- an anchored pattern fails the same
|
|
464
|
-
// way an exact-string match did for those apps if the rendered link's
|
|
465
|
-
// accessible name carries any extra content (icon/badge).
|
|
477
|
+
// Identity should be removed once camunda/camunda#46447 is fixed.
|
|
466
478
|
await this.clickHubClusterApp(clusterName, /Admin|Identity/);
|
|
467
479
|
return;
|
|
468
480
|
}
|
|
@@ -716,17 +728,26 @@ class AppsPage {
|
|
|
716
728
|
.catch(() => { });
|
|
717
729
|
}
|
|
718
730
|
async clickConsoleLink() {
|
|
719
|
-
// Camunda Hub
|
|
720
|
-
//
|
|
721
|
-
//
|
|
722
|
-
//
|
|
731
|
+
// Camunda Hub folded Console into the same persistent shell as
|
|
732
|
+
// Modeler/home: Console's Dashboard/Clusters/Organization links are just
|
|
733
|
+
// another section of the same "Main navigation" sidebar, not a separate
|
|
734
|
+
// app to switch into. Its header banner (homePage.consoleBanner, via
|
|
735
|
+
// hubHeader) is already visible from any Hub page, so there is no
|
|
736
|
+
// navigation action needed here -- mirrors the same hostname guard in
|
|
737
|
+
// clickModeler(). Clicking the sidebar's Clusters/Environments link is
|
|
738
|
+
// not just unnecessary but wrong: Hub shows that section only to org
|
|
739
|
+
// admins, so requiring it broke every RBA step that reaches here as a
|
|
740
|
+
// handed-off non-admin test user. Callers that do need the Clusters page
|
|
741
|
+
// call clickClusters() themselves; the rest assert consoleBanner.
|
|
742
|
+
//
|
|
743
|
+
// The diagram editor is the exception: it has no "Main navigation"
|
|
744
|
+
// sidebar at all, so a caller arriving from "Deploy & run" would find
|
|
745
|
+
// nothing here. Go home first there.
|
|
723
746
|
if (new URL(this.page.url()).hostname.startsWith('hub.')) {
|
|
724
|
-
|
|
725
|
-
.
|
|
726
|
-
.
|
|
727
|
-
|
|
728
|
-
await (0, test_1.expect)(consoleArea).toBeVisible({ timeout: 30000 });
|
|
729
|
-
await consoleArea.click({ timeout: 30000 });
|
|
747
|
+
if (!(await this.hubSidebar.isVisible().catch(() => false))) {
|
|
748
|
+
await this.clickModeler();
|
|
749
|
+
await (0, test_1.expect)(this.hubSidebar).toBeVisible({ timeout: 30000 });
|
|
750
|
+
}
|
|
730
751
|
return;
|
|
731
752
|
}
|
|
732
753
|
const maxRetries = 3;
|
|
@@ -750,7 +771,46 @@ class AppsPage {
|
|
|
750
771
|
}
|
|
751
772
|
throw new Error(`Failed to click the console link after ${maxRetries} attempts.`);
|
|
752
773
|
}
|
|
753
|
-
|
|
774
|
+
// Hub's replacement for the app switcher: a cluster's apps are the child
|
|
775
|
+
// links of its own sidebar group. Hub names every app link "<app> in
|
|
776
|
+
// <environment>", plus " (opens in a new tab)" when it leads to the live app
|
|
777
|
+
// (camunda-hub environment-sidebar-rows.ts) -- the nightly traces carry both
|
|
778
|
+
// shapes ("Operate in Third RBA V2 Cluster", "Admin in Admin Role Cluster
|
|
779
|
+
// (opens in a new tab)"). Anchor both halves rather than matching the bare
|
|
780
|
+
// app name: the flyout's first link is "Details of <environment>", so for
|
|
781
|
+
// "Admin Role Cluster" a bare /Admin|Identity/ resolved to that link, and
|
|
782
|
+
// inline it resolved to "Operate in Admin Role Cluster (opens in a new
|
|
783
|
+
// tab)", which precedes the Admin link. Anchoring on the environment also
|
|
784
|
+
// stops "Test Cluster" matching "UI Test Cluster".
|
|
785
|
+
hubClusterAppLink(clusterName, appName) {
|
|
786
|
+
const appPattern = typeof appName === 'string' ? escapeRegExp(appName) : appName.source;
|
|
787
|
+
const cluster = escapeRegExp(clusterName);
|
|
788
|
+
const appLinkName = new RegExp(`^(?:${appPattern}) in ${cluster}(?: \\(opens in a new tab\\))?$`);
|
|
789
|
+
return this.page
|
|
790
|
+
.getByRole('dialog', { name: clusterName, exact: true })
|
|
791
|
+
.getByRole('link', { name: appLinkName })
|
|
792
|
+
.or(this.hubSidebar.getByRole('link', { name: appLinkName }))
|
|
793
|
+
.first();
|
|
794
|
+
}
|
|
795
|
+
async assertHubClusterAppPresent(clusterName, appName, shouldBeVisible) {
|
|
796
|
+
const appLink = this.hubClusterAppLink(clusterName, appName);
|
|
797
|
+
// Not expanded for an absence check: expandHubClusterGroup throws when
|
|
798
|
+
// the user cannot see the environment at all, which is a pass here.
|
|
799
|
+
if (!shouldBeVisible) {
|
|
800
|
+
await (0, test_1.expect)(appLink).toBeHidden({ timeout: 30000 });
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
await this.expandHubClusterGroup(clusterName);
|
|
804
|
+
await (0, test_1.expect)(appLink).toBeVisible({ timeout: 30000 });
|
|
805
|
+
}
|
|
806
|
+
// On Hub the cluster name is what scopes the lookup: there is no app
|
|
807
|
+
// switcher panel listing apps org-wide, only each cluster's own sidebar
|
|
808
|
+
// group.
|
|
809
|
+
async assertOperatePresent(clusterName, shouldBeVisible = true) {
|
|
810
|
+
if (new URL(this.page.url()).hostname.startsWith('hub.')) {
|
|
811
|
+
await this.assertHubClusterAppPresent(clusterName, 'Operate', shouldBeVisible);
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
754
814
|
const maxRetries = 3;
|
|
755
815
|
for (let retries = 0; retries < maxRetries; retries++) {
|
|
756
816
|
try {
|
|
@@ -779,7 +839,12 @@ class AppsPage {
|
|
|
779
839
|
}
|
|
780
840
|
throw new Error(`Failed to assert the operate link visibility after ${maxRetries} attempts.`);
|
|
781
841
|
}
|
|
782
|
-
|
|
842
|
+
// See assertOperatePresent for the clusterName/Hub note.
|
|
843
|
+
async assertTasklistPresent(clusterName, shouldBeVisible = true) {
|
|
844
|
+
if (new URL(this.page.url()).hostname.startsWith('hub.')) {
|
|
845
|
+
await this.assertHubClusterAppPresent(clusterName, 'Tasklist', shouldBeVisible);
|
|
846
|
+
return;
|
|
847
|
+
}
|
|
783
848
|
const maxRetries = 3;
|
|
784
849
|
for (let retries = 0; retries < maxRetries; retries++) {
|
|
785
850
|
try {
|
|
@@ -808,7 +873,12 @@ class AppsPage {
|
|
|
808
873
|
}
|
|
809
874
|
throw new Error(`Failed to assert the tasklist link visibility after ${maxRetries} attempts.`);
|
|
810
875
|
}
|
|
811
|
-
|
|
876
|
+
// See assertOperatePresent for the clusterName/Hub note.
|
|
877
|
+
async assertOptimizePresent(clusterName, shouldBeVisible = true) {
|
|
878
|
+
if (new URL(this.page.url()).hostname.startsWith('hub.')) {
|
|
879
|
+
await this.assertHubClusterAppPresent(clusterName, 'Optimize', shouldBeVisible);
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
812
882
|
const maxRetries = 3;
|
|
813
883
|
for (let retries = 0; retries < maxRetries; retries++) {
|
|
814
884
|
try {
|
|
@@ -103,6 +103,8 @@ declare class ModelerCreatePage {
|
|
|
103
103
|
readonly timerValue: Locator;
|
|
104
104
|
readonly eighthPlacedElement: Locator;
|
|
105
105
|
readonly deployAndRunMainButton: Locator;
|
|
106
|
+
readonly deployRunActions: Locator;
|
|
107
|
+
readonly deployAndRunMenuItem: Locator;
|
|
106
108
|
readonly deployAndRunSubButton: Locator;
|
|
107
109
|
readonly dialog: Locator;
|
|
108
110
|
readonly backupDeployButton: Locator;
|
|
@@ -141,6 +143,7 @@ declare class ModelerCreatePage {
|
|
|
141
143
|
clickAdditionalActionsButton(): Promise<void>;
|
|
142
144
|
clickDeployMenuItem(): Promise<void>;
|
|
143
145
|
deployDiagram(clusterName: string): Promise<void>;
|
|
146
|
+
assertDeployAndRunNotPermitted(): Promise<void>;
|
|
144
147
|
clickStartInstanceMainButton(): Promise<void>;
|
|
145
148
|
clickStartInstanceSubButton(): Promise<void>;
|
|
146
149
|
runProcessInstance(clusterName: string, variables?: string, formVariables?: {
|
|
@@ -115,6 +115,8 @@ class ModelerCreatePage {
|
|
|
115
115
|
timerValue;
|
|
116
116
|
eighthPlacedElement;
|
|
117
117
|
deployAndRunMainButton;
|
|
118
|
+
deployRunActions;
|
|
119
|
+
deployAndRunMenuItem;
|
|
118
120
|
deployAndRunSubButton;
|
|
119
121
|
dialog;
|
|
120
122
|
backupDeployButton;
|
|
@@ -160,6 +162,13 @@ class ModelerCreatePage {
|
|
|
160
162
|
name: 'Deploy & run',
|
|
161
163
|
exact: true,
|
|
162
164
|
});
|
|
165
|
+
// camunda-hub deploy-run-combo-action: both the Carbon ComboButton and
|
|
166
|
+
// the design-system SplitButton carry this test id.
|
|
167
|
+
this.deployRunActions = page.getByTestId('deploy-run-actions');
|
|
168
|
+
this.deployAndRunMenuItem = page.getByRole('menuitem', {
|
|
169
|
+
name: 'Deploy & run',
|
|
170
|
+
exact: true,
|
|
171
|
+
});
|
|
163
172
|
this.deployAndRunSubButton = this.dialog.getByRole('button', {
|
|
164
173
|
name: 'Deploy & run',
|
|
165
174
|
exact: true,
|
|
@@ -674,6 +683,22 @@ class ModelerCreatePage {
|
|
|
674
683
|
await (0, test_1.expect)(this.viewProcessLink).toBeVisible({ timeout: 30000 });
|
|
675
684
|
await (0, test_1.expect)(this.viewProcessLink).toBeHidden({ timeout: 30000 });
|
|
676
685
|
}
|
|
686
|
+
// For a user whose org role lacks instance/workflow create permissions,
|
|
687
|
+
// Hub's checkIfAllowedToRun is false: the split button's primary action
|
|
688
|
+
// becomes "Deploy" and "Deploy & run" moves into its menu, disabled
|
|
689
|
+
// (camunda-hub BPMNDeployAndRunButtonsDs.tsx, deploy-run-permission.saas.ts).
|
|
690
|
+
// The Console-era expectation of a deploy that is attempted and then fails
|
|
691
|
+
// cannot happen, so assert the gate itself.
|
|
692
|
+
async assertDeployAndRunNotPermitted() {
|
|
693
|
+
await (0, test_1.expect)(this.deployRunActions.getByRole('button', { name: 'Deploy', exact: true })).toBeVisible({ timeout: 60000 });
|
|
694
|
+
await (0, test_1.expect)(this.deployAndRunMainButton).toHaveCount(0);
|
|
695
|
+
await this.deployRunActions
|
|
696
|
+
.getByRole('button', { name: 'Additional actions' })
|
|
697
|
+
.click({ timeout: 30000 });
|
|
698
|
+
await (0, test_1.expect)(this.deployAndRunMenuItem).toBeVisible({ timeout: 30000 });
|
|
699
|
+
await (0, test_1.expect)(this.deployAndRunMenuItem).toBeDisabled();
|
|
700
|
+
await this.page.keyboard.press('Escape');
|
|
701
|
+
}
|
|
677
702
|
async clickStartInstanceMainButton() {
|
|
678
703
|
// ensureStageCluster runs immediately before this on every runProcessInstance
|
|
679
704
|
// attempt and navigates twice, so the diagram's action bar may belong to a
|
|
@@ -83,8 +83,13 @@ async function loginWithRetry(page, loginPage, testUser, timeout, maxRetries = 5
|
|
|
83
83
|
// for the tab's whole lifetime; without clearing it here alongside
|
|
84
84
|
// cookies/storage, a flow that logs a different user into the same
|
|
85
85
|
// page could have getUserToken() return the PREVIOUS user's token.
|
|
86
|
-
|
|
86
|
+
// Listen from before Hub loads: an API helper called later on an idle
|
|
87
|
+
// Hub page would otherwise find no request carrying the token.
|
|
88
|
+
(0, consoleApiHelpers_1.observeBearerTokens)(page);
|
|
87
89
|
await page.goto('about:blank');
|
|
90
|
+
// After about:blank: until then the previous user's Hub page can still
|
|
91
|
+
// send a request, and its token must not count for the new session.
|
|
92
|
+
(0, consoleApiHelpers_1.resetHubBearerToken)(page);
|
|
88
93
|
await page
|
|
89
94
|
.evaluate(() => {
|
|
90
95
|
try {
|
|
@@ -409,24 +414,36 @@ async function enableAuthorizations(clusterName, homePage, clusterPage, clusterD
|
|
|
409
414
|
}
|
|
410
415
|
exports.enableAuthorizations = enableAuthorizations;
|
|
411
416
|
async function assertLatestAlertEmail(id, mailSlurp, processName, alertText) {
|
|
412
|
-
|
|
413
|
-
for
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
417
|
+
// Search the whole inbox for an email carrying alertText rather than asking
|
|
418
|
+
// for the "latest" one. waitForLatestEmail is documented as returning the
|
|
419
|
+
// last received email immediately when the inbox is NOT empty ("If you need
|
|
420
|
+
// to wait for an email for a non-empty inbox see the other receive methods"
|
|
421
|
+
// -- mailslurp-client's own JSDoc), and the alert flow calls this twice on
|
|
422
|
+
// the same inbox: the second call kept resolving to the first alert's mail
|
|
423
|
+
// ("New Incidents: 1") and failed its "New Incidents: 2" assertion on every
|
|
424
|
+
// retry, since retrying re-read the same already-delivered email. Matching
|
|
425
|
+
// on content also tolerates the alert sending more mails than the two this
|
|
426
|
+
// flow asserts on, and ordering between them.
|
|
427
|
+
let body;
|
|
428
|
+
await (0, test_1.expect)(async () => {
|
|
429
|
+
// minCount/retryTimeout make the server itself hold the request open until
|
|
430
|
+
// the inbox has grown, so this does not busy-poll between attempts.
|
|
431
|
+
const previews = await mailSlurp.getEmails(id, {
|
|
432
|
+
minCount: 1,
|
|
433
|
+
retryTimeout: 60000,
|
|
434
|
+
});
|
|
435
|
+
for (const preview of previews) {
|
|
436
|
+
const email = await mailSlurp.getEmail(preview.id);
|
|
437
|
+
if (email?.body?.includes(processName) &&
|
|
438
|
+
email.body.includes(alertText)) {
|
|
439
|
+
body = email.body;
|
|
440
|
+
return;
|
|
423
441
|
}
|
|
424
442
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
throw new Error(`Failed to get latest alert (${processName}) email for ${id} after ${maxRetries} attempts.`);
|
|
443
|
+
throw new Error(`No alert email for "${processName}" containing "${alertText}" in ` +
|
|
444
|
+
`inbox ${id} yet (${previews.length} mail(s) present)`);
|
|
445
|
+
}).toPass({ timeout: 360000, intervals: [5000, 10000, 20000] });
|
|
446
|
+
return body;
|
|
430
447
|
}
|
|
431
448
|
exports.assertLatestAlertEmail = assertLatestAlertEmail;
|
|
432
449
|
async function clickInvitationLinkInEmail(page, id, mailSlurp) {
|
|
@@ -10,6 +10,7 @@ declare class WorkspaceMembersPage {
|
|
|
10
10
|
constructor(page: Page);
|
|
11
11
|
showAllWorkspaces(): Promise<void>;
|
|
12
12
|
openManageMembers(workspaceName: string): Promise<void>;
|
|
13
|
+
workspaceId(): string;
|
|
13
14
|
private memberRow;
|
|
14
15
|
isMember(email: string): Promise<boolean>;
|
|
15
16
|
addMember(email: string, role: WorkspaceRole): Promise<void>;
|
|
@@ -106,6 +106,14 @@ class WorkspaceMembersPage {
|
|
|
106
106
|
await this.membersTab.click({ timeout: 30000 });
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
|
+
// Valid once openManageMembers has landed on /workspaces/<id>/manage.
|
|
110
|
+
workspaceId() {
|
|
111
|
+
const id = this.page.url().match(/\/workspaces\/([^/?#]+)\/manage/)?.[1];
|
|
112
|
+
if (!id) {
|
|
113
|
+
throw new Error(`Not on a workspace Manage page, cannot read its id: ${this.page.url()}`);
|
|
114
|
+
}
|
|
115
|
+
return decodeURIComponent(id);
|
|
116
|
+
}
|
|
109
117
|
memberRow(email) {
|
|
110
118
|
return this.page.getByRole('row').filter({ hasText: email }).first();
|
|
111
119
|
}
|
|
@@ -229,12 +237,11 @@ class WorkspaceMembersPage {
|
|
|
229
237
|
return;
|
|
230
238
|
}
|
|
231
239
|
const row = this.memberRow(email);
|
|
232
|
-
//
|
|
233
|
-
//
|
|
234
|
-
// rather than on text.
|
|
240
|
+
// camunda-hub MembersTable.tsx: the row's icon trigger is
|
|
241
|
+
// aria-label="Actions for <name>", data-test="member-actions-trigger".
|
|
235
242
|
await row
|
|
236
|
-
.
|
|
237
|
-
.or(row.
|
|
243
|
+
.locator('[data-test="member-actions-trigger"]')
|
|
244
|
+
.or(row.getByRole('button', { name: /^Actions for / }))
|
|
238
245
|
.first()
|
|
239
246
|
.click({ timeout: 30000 });
|
|
240
247
|
await this.page
|
|
@@ -242,11 +249,15 @@ class WorkspaceMembersPage {
|
|
|
242
249
|
.or(this.page.getByRole('option', { name: /^Remove( member)?$/i }))
|
|
243
250
|
.first()
|
|
244
251
|
.click({ timeout: 30000 });
|
|
245
|
-
//
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
252
|
+
// RemoveMemberDialog.tsx is a Radix AlertDialog, i.e. role="alertdialog",
|
|
253
|
+
// which getByRole('dialog') does NOT match -- the confirm button was
|
|
254
|
+
// unreachable. Prefer its own test id and keep the role lookup as a
|
|
255
|
+
// fallback.
|
|
256
|
+
await this.page
|
|
257
|
+
.locator('[data-test="remove-member-confirm"]')
|
|
258
|
+
.or(this.page
|
|
259
|
+
.getByRole('alertdialog', { name: 'Remove member' })
|
|
260
|
+
.getByRole('button', { name: 'Remove', exact: true }))
|
|
250
261
|
.first()
|
|
251
262
|
.click({ timeout: 30000 });
|
|
252
263
|
await (0, test_1.expect)(row).toBeHidden({ timeout: 60000 });
|
|
@@ -19,9 +19,13 @@ declare class AppsPage {
|
|
|
19
19
|
readonly camundaComponentsButton: Locator;
|
|
20
20
|
readonly camundaAppsButton: Locator;
|
|
21
21
|
readonly hubSidebar: Locator;
|
|
22
|
+
private hubReturnUrl?;
|
|
22
23
|
constructor(page: Page);
|
|
23
24
|
clickCluster(component: Locator, name: string): Promise<void>;
|
|
25
|
+
private expandHubClusterGroup;
|
|
26
|
+
private showAllWorkspaces;
|
|
24
27
|
private clickHubClusterApp;
|
|
28
|
+
goBackToHub(): Promise<void>;
|
|
25
29
|
clickModeler(): Promise<void>;
|
|
26
30
|
clickTasklist(clusterName: string): Promise<void>;
|
|
27
31
|
clickAdmin(clusterName: string): Promise<void>;
|
|
@@ -35,8 +39,10 @@ declare class AppsPage {
|
|
|
35
39
|
clickCamundaApps(maxRetries?: number): Promise<void>;
|
|
36
40
|
private dismissBlockingOverlays;
|
|
37
41
|
clickConsoleLink(): Promise<void>;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
private hubClusterAppLink;
|
|
43
|
+
private assertHubClusterAppPresent;
|
|
44
|
+
assertOperatePresent(clusterName: string, shouldBeVisible?: boolean): Promise<void>;
|
|
45
|
+
assertTasklistPresent(clusterName: string, shouldBeVisible?: boolean): Promise<void>;
|
|
46
|
+
assertOptimizePresent(clusterName: string, shouldBeVisible?: boolean): Promise<void>;
|
|
41
47
|
}
|
|
42
48
|
export { AppsPage };
|