@camunda/e2e-test-suite 0.0.1443 → 0.0.1445
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/.github/c8_versions.json +1 -0
- package/README.md +2 -0
- package/dist/fixtures/SM-8.11.d.ts +77 -0
- package/dist/fixtures/SM-8.11.js +176 -0
- package/dist/pages/SM-8.11/Authorization.d.ts +39 -0
- package/dist/pages/SM-8.11/Authorization.js +349 -0
- package/dist/pages/SM-8.11/ClusterDetailsPage.d.ts +8 -0
- package/dist/pages/SM-8.11/ClusterDetailsPage.js +65 -0
- package/dist/pages/SM-8.11/ClusterPage.d.ts +11 -0
- package/dist/pages/SM-8.11/ClusterPage.js +37 -0
- package/dist/pages/SM-8.11/ConnectorMarketplacePage.d.ts +27 -0
- package/dist/pages/SM-8.11/ConnectorMarketplacePage.js +187 -0
- package/dist/pages/SM-8.11/ConnectorSettingsPage.d.ts +41 -0
- package/dist/pages/SM-8.11/ConnectorSettingsPage.js +153 -0
- package/dist/pages/SM-8.11/ConnectorTemplatePage.d.ts +11 -0
- package/dist/pages/SM-8.11/ConnectorTemplatePage.js +34 -0
- package/dist/pages/SM-8.11/FormJsPage.d.ts +33 -0
- package/dist/pages/SM-8.11/FormJsPage.js +109 -0
- package/dist/pages/SM-8.11/HomePage.d.ts +20 -0
- package/dist/pages/SM-8.11/HomePage.js +49 -0
- package/dist/pages/SM-8.11/IdentityTenantPage.d.ts +53 -0
- package/dist/pages/SM-8.11/IdentityTenantPage.js +264 -0
- package/dist/pages/SM-8.11/IdentityUsersPage.d.ts +92 -0
- package/dist/pages/SM-8.11/IdentityUsersPage.js +485 -0
- package/dist/pages/SM-8.11/KeycloakAdminPage.d.ts +17 -0
- package/dist/pages/SM-8.11/KeycloakAdminPage.js +78 -0
- package/dist/pages/SM-8.11/KeycloakLoginPage.d.ts +12 -0
- package/dist/pages/SM-8.11/KeycloakLoginPage.js +32 -0
- package/dist/pages/SM-8.11/KeycloakUtils.d.ts +17 -0
- package/dist/pages/SM-8.11/KeycloakUtils.js +94 -0
- package/dist/pages/SM-8.11/LoginPage.d.ts +21 -0
- package/dist/pages/SM-8.11/LoginPage.js +112 -0
- package/dist/pages/SM-8.11/ManagementIdentityPage.d.ts +44 -0
- package/dist/pages/SM-8.11/ManagementIdentityPage.js +176 -0
- package/dist/pages/SM-8.11/ModelerCreatePage.d.ts +238 -0
- package/dist/pages/SM-8.11/ModelerCreatePage.js +1950 -0
- package/dist/pages/SM-8.11/ModelerHomePage.d.ts +97 -0
- package/dist/pages/SM-8.11/ModelerHomePage.js +956 -0
- package/dist/pages/SM-8.11/NavigationPage.d.ts +58 -0
- package/dist/pages/SM-8.11/NavigationPage.js +389 -0
- package/dist/pages/SM-8.11/OCIdentityAuthorizationsPage.d.ts +10 -0
- package/dist/pages/SM-8.11/OCIdentityAuthorizationsPage.js +22 -0
- package/dist/pages/SM-8.11/OCIdentityClusterVariablesPage.d.ts +22 -0
- package/dist/pages/SM-8.11/OCIdentityClusterVariablesPage.js +120 -0
- package/dist/pages/SM-8.11/OCIdentityGroupsPage.d.ts +39 -0
- package/dist/pages/SM-8.11/OCIdentityGroupsPage.js +154 -0
- package/dist/pages/SM-8.11/OCIdentityHomePage.d.ts +23 -0
- package/dist/pages/SM-8.11/OCIdentityHomePage.js +115 -0
- package/dist/pages/SM-8.11/OCIdentityMappingRulesPage.d.ts +23 -0
- package/dist/pages/SM-8.11/OCIdentityMappingRulesPage.js +116 -0
- package/dist/pages/SM-8.11/OCIdentityRolesPage.d.ts +61 -0
- package/dist/pages/SM-8.11/OCIdentityRolesPage.js +433 -0
- package/dist/pages/SM-8.11/OCTenantPage.d.ts +73 -0
- package/dist/pages/SM-8.11/OCTenantPage.js +399 -0
- package/dist/pages/SM-8.11/OperateHomePage.d.ts +26 -0
- package/dist/pages/SM-8.11/OperateHomePage.js +139 -0
- package/dist/pages/SM-8.11/OperateProcessInstancePage.d.ts +30 -0
- package/dist/pages/SM-8.11/OperateProcessInstancePage.js +276 -0
- package/dist/pages/SM-8.11/OperateProcessesPage.d.ts +38 -0
- package/dist/pages/SM-8.11/OperateProcessesPage.js +383 -0
- package/dist/pages/SM-8.11/OptimizeCollectionsPage.d.ts +17 -0
- package/dist/pages/SM-8.11/OptimizeCollectionsPage.js +62 -0
- package/dist/pages/SM-8.11/OptimizeDashboardPage.d.ts +14 -0
- package/dist/pages/SM-8.11/OptimizeDashboardPage.js +63 -0
- package/dist/pages/SM-8.11/OptimizeHomePage.d.ts +19 -0
- package/dist/pages/SM-8.11/OptimizeHomePage.js +76 -0
- package/dist/pages/SM-8.11/OptimizeReportPage.d.ts +50 -0
- package/dist/pages/SM-8.11/OptimizeReportPage.js +209 -0
- package/dist/pages/SM-8.11/PlayPage.d.ts +47 -0
- package/dist/pages/SM-8.11/PlayPage.js +389 -0
- package/dist/pages/SM-8.11/ProcessApplicationPage.d.ts +35 -0
- package/dist/pages/SM-8.11/ProcessApplicationPage.js +340 -0
- package/dist/pages/SM-8.11/SettingsPage.d.ts +12 -0
- package/dist/pages/SM-8.11/SettingsPage.js +34 -0
- package/dist/pages/SM-8.11/TaskDetailsPage.d.ts +70 -0
- package/dist/pages/SM-8.11/TaskDetailsPage.js +273 -0
- package/dist/pages/SM-8.11/TaskPanelPage.d.ts +29 -0
- package/dist/pages/SM-8.11/TaskPanelPage.js +320 -0
- package/dist/pages/SM-8.11/TaskProcessesPage.d.ts +15 -0
- package/dist/pages/SM-8.11/TaskProcessesPage.js +77 -0
- package/dist/pages/SM-8.11/UtilitiesPage.d.ts +38 -0
- package/dist/pages/SM-8.11/UtilitiesPage.js +573 -0
- package/dist/pages/SM-8.11/optimizeReportUtils.d.ts +6 -0
- package/dist/pages/SM-8.11/optimizeReportUtils.js +77 -0
- package/dist/tests/SM-8.11/cluster-management-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/cluster-management-user-flows.spec.js +48 -0
- package/dist/tests/SM-8.11/cluster-variables.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/cluster-variables.spec.js +226 -0
- package/dist/tests/SM-8.11/connectors-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/connectors-user-flows.spec.js +307 -0
- package/dist/tests/SM-8.11/document-handling-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/document-handling-user-flows.spec.js +174 -0
- package/dist/tests/SM-8.11/hto-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/hto-user-flows.spec.js +410 -0
- package/dist/tests/SM-8.11/hub-web-modeler-smoke.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/hub-web-modeler-smoke.spec.js +24 -0
- package/dist/tests/SM-8.11/identity-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/identity-user-flows.spec.js +220 -0
- package/dist/tests/SM-8.11/license-key-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/license-key-user-flows.spec.js +461 -0
- package/dist/tests/SM-8.11/login.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/login.spec.js +24 -0
- package/dist/tests/SM-8.11/migration-path-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/migration-path-user-flows.spec.js +263 -0
- package/dist/tests/SM-8.11/mt-enabled-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/mt-enabled-user-flows.spec.js +607 -0
- package/dist/tests/SM-8.11/optimize-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/optimize-user-flows.spec.js +505 -0
- package/dist/tests/SM-8.11/play.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/play.spec.js +144 -0
- package/dist/tests/SM-8.11/rba-enabled-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/rba-enabled-user-flows.spec.js +712 -0
- package/dist/tests/SM-8.11/smoke-tests.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/smoke-tests.spec.js +167 -0
- package/dist/tests/SM-8.11/test-setup.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/test-setup.spec.js +24 -0
- package/dist/tests/SM-8.11/topology-orchestration-smoke.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/topology-orchestration-smoke.spec.js +58 -0
- package/dist/tests/SM-8.11/web-modeler-user-flows.spec.d.ts +1 -0
- package/dist/tests/SM-8.11/web-modeler-user-flows.spec.js +223 -0
- package/package.json +1 -1
- package/playwright.config.ts +21 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createKeycloakUser = exports.createUser = exports.setupKeycloakUser = exports.keycloakAdminCredentials = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
const sleep_1 = require("../../utils/sleep");
|
|
6
|
+
const keycloakAdminCredentials = () => {
|
|
7
|
+
return {
|
|
8
|
+
username: process.env.DISTRO_QA_E2E_TESTS_KEYCLOAK_USERNAME || 'admin',
|
|
9
|
+
password: process.env.DISTRO_QA_E2E_TESTS_KEYCLOAK_PASSWORD,
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
exports.keycloakAdminCredentials = keycloakAdminCredentials;
|
|
13
|
+
const setupKeycloakUser = async (navigationPage, identityPage, keycloakLoginPage, keycloakAdminPage, ocIdentityHomePage, ocIdentityRolesPage, assignToDefaultTenant = false, managementIdentityTenantPage) => {
|
|
14
|
+
const testUsername = `test-user-${new Date().getTime()}-${Math.random()
|
|
15
|
+
.toString(36)
|
|
16
|
+
.substring(7)}`;
|
|
17
|
+
const testPassword = 'test-password';
|
|
18
|
+
await createKeycloakUser(navigationPage, keycloakLoginPage, keycloakAdminPage, testUsername, testPassword);
|
|
19
|
+
await createIdentityUser(navigationPage, identityPage, testUsername);
|
|
20
|
+
if (assignToDefaultTenant && managementIdentityTenantPage) {
|
|
21
|
+
const userEmail = `qacamunda+${testUsername}@gmail.com`;
|
|
22
|
+
await navigationPage.goToManagementIdentity();
|
|
23
|
+
await (0, test_1.expect)(identityPage.usersLink).toBeVisible({ timeout: 10000 });
|
|
24
|
+
if (await identityPage.tenantTab.isVisible()) {
|
|
25
|
+
await managementIdentityTenantPage.assignUserToTenant('Default', testUsername, userEmail);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
await createOCUser(navigationPage, ocIdentityHomePage, ocIdentityRolesPage, testUsername);
|
|
29
|
+
return { testUsername, testPassword };
|
|
30
|
+
};
|
|
31
|
+
exports.setupKeycloakUser = setupKeycloakUser;
|
|
32
|
+
async function createUser(navigationPage, identityPage, keycloakLoginPage, keycloakAdminPage, username, password) {
|
|
33
|
+
await createKeycloakUser(navigationPage, keycloakLoginPage, keycloakAdminPage, username, password);
|
|
34
|
+
await createIdentityUser(navigationPage, identityPage, username);
|
|
35
|
+
}
|
|
36
|
+
exports.createUser = createUser;
|
|
37
|
+
// Keycloak occasionally serves "We are sorry... Unexpected error when
|
|
38
|
+
// handling authentication request to identity provider" instead of
|
|
39
|
+
// completing the admin login (the same transient 5xx NavigationPage.goTo's
|
|
40
|
+
// progressive backoff already guards against for app logins). This flow logs
|
|
41
|
+
// into the Keycloak admin console directly rather than through goTo, so it
|
|
42
|
+
// needs the same retry of the whole login sequence instead of failing the
|
|
43
|
+
// test outright on the first hit.
|
|
44
|
+
async function loginToKeycloakAdmin(navigationPage, keycloakLoginPage, keycloakAdminPage, maxRetries = 5) {
|
|
45
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
46
|
+
try {
|
|
47
|
+
await navigationPage.goToKeycloak();
|
|
48
|
+
await keycloakLoginPage.toBeVisible();
|
|
49
|
+
await keycloakLoginPage.fillUsername((0, exports.keycloakAdminCredentials)().username);
|
|
50
|
+
await keycloakLoginPage.fillPassword((0, exports.keycloakAdminCredentials)().password);
|
|
51
|
+
await keycloakLoginPage.clickLogin();
|
|
52
|
+
await keycloakAdminPage.toBeVisible();
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (attempt >= maxRetries - 1) {
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
console.warn(`[KeycloakUtils] Keycloak admin login attempt ${attempt + 1} failed, retrying: ${error instanceof Error ? error.message : error}`);
|
|
60
|
+
await (0, sleep_1.sleep)(2000 * (attempt + 1));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function createKeycloakUser(navigationPage, keycloakLoginPage, keycloakAdminPage, username, password) {
|
|
65
|
+
await loginToKeycloakAdmin(navigationPage, keycloakLoginPage, keycloakAdminPage);
|
|
66
|
+
await keycloakAdminPage.switchToCamundaPlatform();
|
|
67
|
+
await keycloakAdminPage.clickUsersTab();
|
|
68
|
+
await keycloakAdminPage.clickAddUser();
|
|
69
|
+
await keycloakAdminPage.fillUser(username);
|
|
70
|
+
await keycloakAdminPage.saveUser();
|
|
71
|
+
await keycloakAdminPage.fillPassword(password);
|
|
72
|
+
await keycloakAdminPage.savePassword();
|
|
73
|
+
}
|
|
74
|
+
exports.createKeycloakUser = createKeycloakUser;
|
|
75
|
+
const createIdentityUser = async (navigationPage, managementIdentity, username) => {
|
|
76
|
+
await navigationPage.goToManagementIdentity();
|
|
77
|
+
await managementIdentity.clickUsersLink();
|
|
78
|
+
await managementIdentity.clickUser(username);
|
|
79
|
+
await managementIdentity.clickAssignedRolesTab();
|
|
80
|
+
await managementIdentity.clickAssignRolesButton();
|
|
81
|
+
if (process.env.IS_OPTIMIZE !== 'false') {
|
|
82
|
+
await managementIdentity.clickOptimizeCheckbox();
|
|
83
|
+
}
|
|
84
|
+
await managementIdentity.clickModelerCheckbox();
|
|
85
|
+
await managementIdentity.clickIdentityCheckbox();
|
|
86
|
+
await managementIdentity.clickOrchestrationCheckbox();
|
|
87
|
+
await managementIdentity.clickAddButton();
|
|
88
|
+
};
|
|
89
|
+
const createOCUser = async (navigationPage, ocIdentityHomePage, ocIdentityRolesPage, username) => {
|
|
90
|
+
await navigationPage.goToOCAdmin();
|
|
91
|
+
await ocIdentityHomePage.clickRolesTab();
|
|
92
|
+
await ocIdentityRolesPage.clickAdminRole();
|
|
93
|
+
await ocIdentityRolesPage.assignUserToRole(username);
|
|
94
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
declare class LoginPage {
|
|
3
|
+
private page;
|
|
4
|
+
readonly usernameInput: Locator;
|
|
5
|
+
readonly passwordInput: Locator;
|
|
6
|
+
readonly loginButton: Locator;
|
|
7
|
+
readonly coreUsernameInput: Locator;
|
|
8
|
+
readonly corePasswordInput: Locator;
|
|
9
|
+
readonly coreLoginButton: Locator;
|
|
10
|
+
readonly keycloakBanner: Locator;
|
|
11
|
+
private useCore;
|
|
12
|
+
private useKeycloak;
|
|
13
|
+
constructor(page: Page);
|
|
14
|
+
detectLoginForm(): Promise<void>;
|
|
15
|
+
fillUsername(username: string): Promise<void>;
|
|
16
|
+
clickUsername(): Promise<void>;
|
|
17
|
+
fillPassword(password: string): Promise<void>;
|
|
18
|
+
clickLoginButton(): Promise<void>;
|
|
19
|
+
login(username: string, password: string): Promise<boolean>;
|
|
20
|
+
}
|
|
21
|
+
export { LoginPage };
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LoginPage = void 0;
|
|
4
|
+
class LoginPage {
|
|
5
|
+
page;
|
|
6
|
+
usernameInput;
|
|
7
|
+
passwordInput;
|
|
8
|
+
loginButton;
|
|
9
|
+
coreUsernameInput;
|
|
10
|
+
corePasswordInput;
|
|
11
|
+
coreLoginButton;
|
|
12
|
+
keycloakBanner;
|
|
13
|
+
useCore = false;
|
|
14
|
+
useKeycloak = false;
|
|
15
|
+
constructor(page) {
|
|
16
|
+
this.page = page;
|
|
17
|
+
this.usernameInput = page.getByLabel('Username or email');
|
|
18
|
+
this.passwordInput = page.getByLabel('Password');
|
|
19
|
+
this.loginButton = page.getByRole('button', { name: /log in|sign in/i });
|
|
20
|
+
this.coreUsernameInput = page.getByPlaceholder('Username');
|
|
21
|
+
this.corePasswordInput = page.getByPlaceholder('Password');
|
|
22
|
+
this.coreLoginButton = page.getByRole('button', { name: 'Login' });
|
|
23
|
+
this.keycloakBanner = page.locator('body#keycloak-bg[data-page-id="login-login"]');
|
|
24
|
+
}
|
|
25
|
+
async detectLoginForm() {
|
|
26
|
+
// If we're on an app page that's about to redirect to KC, wait for
|
|
27
|
+
// the navigation to complete before looking for login form elements.
|
|
28
|
+
const url = this.page.url();
|
|
29
|
+
if (!url.includes('/auth/') && !url.includes('/login-callback')) {
|
|
30
|
+
await this.page
|
|
31
|
+
.waitForURL((u) => u.pathname.includes('/auth/'), {
|
|
32
|
+
timeout: 30000,
|
|
33
|
+
waitUntil: 'domcontentloaded',
|
|
34
|
+
})
|
|
35
|
+
.catch(() => { });
|
|
36
|
+
}
|
|
37
|
+
const kcUsername = this.page.locator('#username');
|
|
38
|
+
const anyForm = kcUsername
|
|
39
|
+
.or(this.coreLoginButton)
|
|
40
|
+
.or(this.keycloakBanner)
|
|
41
|
+
.first();
|
|
42
|
+
await anyForm.waitFor({ state: 'visible', timeout: 30000 });
|
|
43
|
+
if (await this.coreLoginButton.isVisible()) {
|
|
44
|
+
this.useCore = true;
|
|
45
|
+
}
|
|
46
|
+
else if (await this.keycloakBanner.isVisible()) {
|
|
47
|
+
this.useKeycloak = true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async fillUsername(username) {
|
|
51
|
+
if (await this.page.locator('#username').isVisible()) {
|
|
52
|
+
await this.page.fill('#username', username);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
await (this.useCore
|
|
56
|
+
? this.coreUsernameInput.fill(username)
|
|
57
|
+
: this.usernameInput.fill(username));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async clickUsername() {
|
|
61
|
+
if (await this.page.locator('#username').isVisible()) {
|
|
62
|
+
await this.page.click('#username');
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
await (this.useCore
|
|
66
|
+
? this.coreUsernameInput.click({ timeout: 60000 })
|
|
67
|
+
: this.usernameInput.click({ timeout: 60000 }));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async fillPassword(password) {
|
|
71
|
+
if (await this.page.locator('#password').isVisible()) {
|
|
72
|
+
await this.page.fill('#password', password);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
await (this.useCore
|
|
76
|
+
? this.corePasswordInput.fill(password)
|
|
77
|
+
: this.passwordInput.fill(password));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async clickLoginButton() {
|
|
81
|
+
if (await this.page.locator('button[type="submit"]').isVisible()) {
|
|
82
|
+
// The Keycloak submit path was the only click here without an explicit
|
|
83
|
+
// timeout, so it inherited the 10s actionTimeout while page.click waits
|
|
84
|
+
// out the OAuth redirect chain it triggers. Match the 60s budget the
|
|
85
|
+
// sibling branches already use.
|
|
86
|
+
await this.page.click('button[type="submit"]', { timeout: 60000 });
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
await (this.useCore
|
|
90
|
+
? this.coreLoginButton.click({ timeout: 60000 })
|
|
91
|
+
: this.loginButton.click({ timeout: 60000 }));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Resolves to whether credentials were actually submitted. A missing form is
|
|
95
|
+
// not an error -- the caller may already hold a session, in which case there
|
|
96
|
+
// is nothing to fill -- but the caller has to be able to tell that apart from
|
|
97
|
+
// a submitted form, because only the latter leaves a page transition pending.
|
|
98
|
+
async login(username, password) {
|
|
99
|
+
try {
|
|
100
|
+
await this.detectLoginForm();
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
await this.clickUsername();
|
|
106
|
+
await this.fillUsername(username);
|
|
107
|
+
await this.fillPassword(password);
|
|
108
|
+
await this.clickLoginButton();
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.LoginPage = LoginPage;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
declare class ManagementIdentityPage {
|
|
3
|
+
private page;
|
|
4
|
+
readonly identityBanner: Locator;
|
|
5
|
+
readonly usersLink: Locator;
|
|
6
|
+
readonly assignedRolesTab: Locator;
|
|
7
|
+
readonly demoUser: Locator;
|
|
8
|
+
readonly confirmDeleteButton: Locator;
|
|
9
|
+
readonly assignRolesButton: Locator;
|
|
10
|
+
readonly addButton: Locator;
|
|
11
|
+
readonly optimizeCheckbox: Locator;
|
|
12
|
+
readonly modelerCheckbox: Locator;
|
|
13
|
+
readonly deleteButton: Locator;
|
|
14
|
+
readonly deleteSubButton: Locator;
|
|
15
|
+
readonly tenantTab: Locator;
|
|
16
|
+
readonly licenseKeyTagNonProduction: Locator;
|
|
17
|
+
readonly licenseKeyTagNonCommercial: Locator;
|
|
18
|
+
readonly licenseKeyTagProduction: Locator;
|
|
19
|
+
readonly licenseKeyTagCommercial: Locator;
|
|
20
|
+
readonly modelerAdminCheckbox: Locator;
|
|
21
|
+
readonly identityCheckbox: Locator;
|
|
22
|
+
readonly openSidebarButton: Locator;
|
|
23
|
+
readonly logoutButton: Locator;
|
|
24
|
+
readonly orchestrationCheckbox: Locator;
|
|
25
|
+
constructor(page: Page);
|
|
26
|
+
clickUsersLink(): Promise<void>;
|
|
27
|
+
clickUser(username: string): Promise<void>;
|
|
28
|
+
clickAssignedRolesTab(): Promise<void>;
|
|
29
|
+
clickDemoUser(): Promise<void>;
|
|
30
|
+
clickDeleteAccessButton(accessToDelete: string | string[]): Promise<void>;
|
|
31
|
+
clickConfirmDeleteButton(): Promise<void>;
|
|
32
|
+
clickAssignRolesButton(): Promise<void>;
|
|
33
|
+
clickAddButton(): Promise<void>;
|
|
34
|
+
clickOptimizeCheckbox(): Promise<void>;
|
|
35
|
+
clickModelerCheckbox(): Promise<void>;
|
|
36
|
+
clickIdentityCheckbox(): Promise<void>;
|
|
37
|
+
clickModelerAdminCheckbox(): Promise<void>;
|
|
38
|
+
clickUserGroup(name: string): Promise<void>;
|
|
39
|
+
clickTenantTab(): Promise<void>;
|
|
40
|
+
clickOpenSidebarButton(): Promise<void>;
|
|
41
|
+
clickLogoutButton(): Promise<void>;
|
|
42
|
+
clickOrchestrationCheckbox(): Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
export { ManagementIdentityPage };
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ManagementIdentityPage = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
class ManagementIdentityPage {
|
|
6
|
+
page;
|
|
7
|
+
identityBanner;
|
|
8
|
+
usersLink;
|
|
9
|
+
assignedRolesTab;
|
|
10
|
+
demoUser;
|
|
11
|
+
confirmDeleteButton;
|
|
12
|
+
assignRolesButton;
|
|
13
|
+
addButton;
|
|
14
|
+
optimizeCheckbox;
|
|
15
|
+
modelerCheckbox;
|
|
16
|
+
deleteButton;
|
|
17
|
+
deleteSubButton;
|
|
18
|
+
tenantTab;
|
|
19
|
+
licenseKeyTagNonProduction;
|
|
20
|
+
licenseKeyTagNonCommercial;
|
|
21
|
+
licenseKeyTagProduction;
|
|
22
|
+
licenseKeyTagCommercial;
|
|
23
|
+
modelerAdminCheckbox;
|
|
24
|
+
identityCheckbox;
|
|
25
|
+
openSidebarButton;
|
|
26
|
+
logoutButton;
|
|
27
|
+
orchestrationCheckbox;
|
|
28
|
+
constructor(page) {
|
|
29
|
+
this.page = page;
|
|
30
|
+
this.identityBanner = page
|
|
31
|
+
.locator('a')
|
|
32
|
+
.filter({ hasText: /^Camunda logo\s*Management Identity$/ })
|
|
33
|
+
.first();
|
|
34
|
+
this.usersLink = page.locator('a').filter({ hasText: 'Users' });
|
|
35
|
+
this.tenantTab = page
|
|
36
|
+
.getByRole('link', { name: 'Tenants' })
|
|
37
|
+
.or(page.locator('a').filter({ hasText: 'Tenants' }))
|
|
38
|
+
.first();
|
|
39
|
+
this.assignedRolesTab = page.getByRole('tab', { name: 'Assigned roles' });
|
|
40
|
+
this.demoUser = page.getByRole('cell', { name: 'demo' }).first();
|
|
41
|
+
this.confirmDeleteButton = page.getByRole('button', {
|
|
42
|
+
name: 'danger Delete',
|
|
43
|
+
});
|
|
44
|
+
this.assignRolesButton = page.getByRole('button', { name: 'Assign roles' });
|
|
45
|
+
this.addButton = page.getByRole('button', { name: 'Add' });
|
|
46
|
+
this.optimizeCheckbox = page
|
|
47
|
+
.locator('label')
|
|
48
|
+
.filter({ hasText: 'Optimize (Grants full access to Optimize)' });
|
|
49
|
+
// The chart's Keycloak preset (camunda-platform-helm#6896) renamed the Hub
|
|
50
|
+
// application and added "Hub"/"Hub Admin" roles while keeping the identical
|
|
51
|
+
// legacy "Web Modeler"/"Web Modeler Admin" roles for 8.10-alpha upgrades,
|
|
52
|
+
// so the straddle alternation that matched either wording now resolves to
|
|
53
|
+
// two rows and trips strict mode. Both roles grant the same permissions on
|
|
54
|
+
// the same application; target the go-forward "Hub" name, which is also
|
|
55
|
+
// what the chart assigns its own QA users. The `for` attribute is the role
|
|
56
|
+
// name, so this survives further description rewording.
|
|
57
|
+
this.modelerCheckbox = page.locator('label[for="Hub"]');
|
|
58
|
+
this.identityCheckbox = page.locator('label').filter({
|
|
59
|
+
hasText: 'ManagementIdentity (Provides full access to Management Identity)',
|
|
60
|
+
});
|
|
61
|
+
this.deleteSubButton = page.getByRole('button', { name: 'danger delete' });
|
|
62
|
+
this.deleteButton = page.locator('button', { hasText: 'danger' });
|
|
63
|
+
this.licenseKeyTagNonProduction = page
|
|
64
|
+
.getByText('Non-production license')
|
|
65
|
+
.first();
|
|
66
|
+
this.licenseKeyTagNonCommercial = page.getByText('Non-commercial license');
|
|
67
|
+
this.licenseKeyTagProduction = page.getByText('Production license').first();
|
|
68
|
+
this.licenseKeyTagCommercial = page.getByText('Commercial license');
|
|
69
|
+
// Same two-role split as `modelerCheckbox` above.
|
|
70
|
+
this.modelerAdminCheckbox = page.locator('label[for="Hub Admin"]');
|
|
71
|
+
this.openSidebarButton = page.getByLabel('Open User Sidebar');
|
|
72
|
+
this.logoutButton = page.getByRole('button', { name: 'Log out' });
|
|
73
|
+
this.orchestrationCheckbox = page.locator('label').filter({
|
|
74
|
+
hasText: 'Orchestration (Grants full access to Orchestration)',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
async clickUsersLink() {
|
|
78
|
+
try {
|
|
79
|
+
await this.usersLink.click({ timeout: 180000, force: true });
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
await this.page.reload();
|
|
83
|
+
await this.usersLink.click({ timeout: 180000, force: true });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
async clickUser(username) {
|
|
87
|
+
const searchbox = this.page.getByRole('searchbox');
|
|
88
|
+
await searchbox.click({ timeout: 30000 });
|
|
89
|
+
await searchbox.fill(username);
|
|
90
|
+
const user = this.page.getByRole('cell', { name: username, exact: true });
|
|
91
|
+
await (0, test_1.expect)(user).toBeVisible({ timeout: 60000 });
|
|
92
|
+
await user.click();
|
|
93
|
+
}
|
|
94
|
+
async clickAssignedRolesTab() {
|
|
95
|
+
await (0, test_1.expect)(this.assignedRolesTab).toBeVisible({ timeout: 60000 });
|
|
96
|
+
await this.assignedRolesTab.click();
|
|
97
|
+
}
|
|
98
|
+
async clickDemoUser() {
|
|
99
|
+
await (0, test_1.expect)(this.demoUser).toBeVisible({ timeout: 60000 });
|
|
100
|
+
await this.demoUser.click({ timeout: 180000 });
|
|
101
|
+
}
|
|
102
|
+
// The assigned-roles table can come up with a stale row set shortly after the
|
|
103
|
+
// roles were assigned — run 32920068272 recorded `/api/users/<id>/roles`
|
|
104
|
+
// returning Optimize while the table repeated the neighbouring row's label —
|
|
105
|
+
// and it does not re-render on its own. Rebuild it from a fresh response
|
|
106
|
+
// between attempts before reporting the role as missing.
|
|
107
|
+
async clickDeleteAccessButton(accessToDelete) {
|
|
108
|
+
const candidates = typeof accessToDelete === 'string' ? [accessToDelete] : accessToDelete;
|
|
109
|
+
const rowSelector = candidates.map((c) => `:text("${c}")`).join(', ');
|
|
110
|
+
const roleRow = this.page.locator(rowSelector).first();
|
|
111
|
+
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
112
|
+
const rowVisible = await roleRow
|
|
113
|
+
.waitFor({ state: 'visible', timeout: 30000 })
|
|
114
|
+
.then(() => true)
|
|
115
|
+
.catch(() => false);
|
|
116
|
+
if (rowVisible) {
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
await this.page.reload({ waitUntil: 'domcontentloaded' });
|
|
120
|
+
await this.clickAssignedRolesTab();
|
|
121
|
+
}
|
|
122
|
+
await (0, test_1.expect)(roleRow, `No assigned role matching ${candidates
|
|
123
|
+
.map((c) => `"${c}"`)
|
|
124
|
+
.join(' or ')} was found. The role may already be removed, or its ` +
|
|
125
|
+
`label may have changed in this version.`).toBeVisible({ timeout: 60000 });
|
|
126
|
+
// Click the Delete control inside the matched role's own table row. A
|
|
127
|
+
// `td:right-of(:text(...))` spatial match resolves to a neighbouring row's
|
|
128
|
+
// button once the roles table carries rows before the target (Orchestration
|
|
129
|
+
// is ordered above Hub), so it removed the wrong role and left the intended
|
|
130
|
+
// one still assigned.
|
|
131
|
+
await this.page
|
|
132
|
+
.locator('tr')
|
|
133
|
+
.filter({ has: this.page.locator(rowSelector) })
|
|
134
|
+
.first()
|
|
135
|
+
.getByRole('button', { name: 'Delete' })
|
|
136
|
+
.click({ timeout: 60000 });
|
|
137
|
+
}
|
|
138
|
+
async clickConfirmDeleteButton() {
|
|
139
|
+
await (0, test_1.expect)(this.confirmDeleteButton, 'The delete-role confirmation dialog did not appear.').toBeVisible({ timeout: 60000 });
|
|
140
|
+
await this.confirmDeleteButton.click();
|
|
141
|
+
}
|
|
142
|
+
async clickAssignRolesButton() {
|
|
143
|
+
await this.assignRolesButton.click();
|
|
144
|
+
}
|
|
145
|
+
async clickAddButton() {
|
|
146
|
+
await this.addButton.click();
|
|
147
|
+
}
|
|
148
|
+
async clickOptimizeCheckbox() {
|
|
149
|
+
await this.optimizeCheckbox.click();
|
|
150
|
+
}
|
|
151
|
+
async clickModelerCheckbox() {
|
|
152
|
+
await this.modelerCheckbox.click();
|
|
153
|
+
}
|
|
154
|
+
async clickIdentityCheckbox() {
|
|
155
|
+
await this.identityCheckbox.click();
|
|
156
|
+
}
|
|
157
|
+
async clickModelerAdminCheckbox() {
|
|
158
|
+
await this.modelerAdminCheckbox.click();
|
|
159
|
+
}
|
|
160
|
+
async clickUserGroup(name) {
|
|
161
|
+
await this.page.getByRole('cell', { name: name }).click();
|
|
162
|
+
}
|
|
163
|
+
async clickTenantTab() {
|
|
164
|
+
await this.tenantTab.click();
|
|
165
|
+
}
|
|
166
|
+
async clickOpenSidebarButton() {
|
|
167
|
+
await this.openSidebarButton.click({ timeout: 60000 });
|
|
168
|
+
}
|
|
169
|
+
async clickLogoutButton() {
|
|
170
|
+
await this.logoutButton.click({ timeout: 60000 });
|
|
171
|
+
}
|
|
172
|
+
async clickOrchestrationCheckbox() {
|
|
173
|
+
await this.orchestrationCheckbox.click();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
exports.ManagementIdentityPage = ManagementIdentityPage;
|