@camunda/e2e-test-suite 0.0.1443 → 0.0.1444
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,340 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProcessApplicationPage = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
const submitModalWithRetry_1 = require("../../utils/assertionHelpers/submitModalWithRetry");
|
|
6
|
+
// ModelerHomePage creates the container, while ModelerCreatePage later binds
|
|
7
|
+
// its stage. Both page objects share the same Playwright Page.
|
|
8
|
+
const resourceContainers = new WeakMap();
|
|
9
|
+
// Workspace ids whose Environment an org admin assigned from a separate session
|
|
10
|
+
// (see assignEnvironmentToWorkspace), keyed by the non-admin user's Page. That
|
|
11
|
+
// user cannot open the workspace Environments tab, so ensureStageCluster skips it.
|
|
12
|
+
const adminAssignedWorkspaces = new WeakMap();
|
|
13
|
+
// Flag-on Self-Managed 8.10 (camunda-hub#27903: workspaces-enabled=true) renamed
|
|
14
|
+
// the entity hierarchy: the legacy "project folder" is now a Workspace and the
|
|
15
|
+
// legacy "process application" is now a Project (camunda-hub#28005). A Project is
|
|
16
|
+
// the resource container -- files, folders and uploads live directly inside it,
|
|
17
|
+
// there is no separate process-application level to descend into. It is created
|
|
18
|
+
// from the workspace's projects listing via the "Create project" split button +
|
|
19
|
+
// dialog, and the app lands on `/workspace/{wsId}/projects/{projectId}`.
|
|
20
|
+
//
|
|
21
|
+
// camunda-hub#29210 ("Environments phase1") then moved cluster assignment up a
|
|
22
|
+
// level again: a Project no longer has its own "Define stages" dialog or
|
|
23
|
+
// "Connected clusters" card at all. Assignment is now an Environment (backed by
|
|
24
|
+
// a registered cluster) assigned to the whole Workspace via its Environments
|
|
25
|
+
// settings tab (`/workspaces/{wsId}/settings?tab=environments`), shared by every
|
|
26
|
+
// Project inside that Workspace.
|
|
27
|
+
//
|
|
28
|
+
// This page object owns that container's surface (creating a Project) plus the
|
|
29
|
+
// Workspace-level Environment assignment Deploy/Play need. Entering and leaving
|
|
30
|
+
// it is driven from ModelerHomePage so the ~150 existing spec call sites keep
|
|
31
|
+
// working unchanged. Method names are kept from the flag-off era
|
|
32
|
+
// ("...ProcessApplication...", "...StageCluster...") so the callers do not have
|
|
33
|
+
// to change.
|
|
34
|
+
class ProcessApplicationPage {
|
|
35
|
+
page;
|
|
36
|
+
// The workspace projects listing's "Create project" split button. exact:true
|
|
37
|
+
// because the split button has a sibling "Create project options" chevron whose
|
|
38
|
+
// accessible name substring-matches otherwise (mirrors the hub team's own
|
|
39
|
+
// WorkspaceProjectsPage.newProjectButton). Falls back to the legacy flag-off
|
|
40
|
+
// "Create process application" button so a deployment with the flag still off
|
|
41
|
+
// keeps working.
|
|
42
|
+
createProcessApplicationButton;
|
|
43
|
+
// Legacy flag-off fallback only: the "Create new" menu item. Not rendered under
|
|
44
|
+
// workspaces-enabled, where the split button above is the entry point.
|
|
45
|
+
createProcessApplicationOption;
|
|
46
|
+
creationDialog;
|
|
47
|
+
nameInput;
|
|
48
|
+
submitButton;
|
|
49
|
+
cancelButton;
|
|
50
|
+
breadcrumb;
|
|
51
|
+
folderExplorer;
|
|
52
|
+
titleBar;
|
|
53
|
+
createNewDropdown;
|
|
54
|
+
inlineNameInput;
|
|
55
|
+
// Workspace management -> Environments tab (camunda-hub#29210). Page-scoped,
|
|
56
|
+
// not container-scoped: this tab lives on a different page
|
|
57
|
+
// (`/workspaces/{wsId}/settings`) than the Project detail these other
|
|
58
|
+
// locators target.
|
|
59
|
+
manageEnvironmentsHeading;
|
|
60
|
+
assignedEnvironmentCards;
|
|
61
|
+
editEnvironmentsButton;
|
|
62
|
+
environmentEditorRegion;
|
|
63
|
+
saveEnvironmentsButton;
|
|
64
|
+
constructor(page) {
|
|
65
|
+
this.page = page;
|
|
66
|
+
this.createProcessApplicationButton = page
|
|
67
|
+
.getByRole('button', { name: 'Create project', exact: true })
|
|
68
|
+
.or(page.getByRole('button', { name: 'Create process application' }))
|
|
69
|
+
.first();
|
|
70
|
+
this.createProcessApplicationOption = page.locator('[data-test="create-process-application"]');
|
|
71
|
+
// The workspace-mode create-project dialog carries a "Project name" field and
|
|
72
|
+
// has no accessible dialog name; scope to it via that field. Keeps the legacy
|
|
73
|
+
// flag-off "Create a process application" dialog as a fallback.
|
|
74
|
+
this.creationDialog = page
|
|
75
|
+
.getByRole('dialog')
|
|
76
|
+
.filter({ has: page.getByLabel('Project name') })
|
|
77
|
+
.or(page.getByRole('dialog', { name: 'Create a process application' }))
|
|
78
|
+
.first();
|
|
79
|
+
// Deliberately scoped to the dialog: ModelerCreatePage owns an unscoped
|
|
80
|
+
// getByLabel('Name', {exact: true}) for the properties panel, which would
|
|
81
|
+
// resolve to two elements while the creation modal is open.
|
|
82
|
+
this.nameInput = this.creationDialog
|
|
83
|
+
.getByLabel('Project name')
|
|
84
|
+
.or(this.creationDialog.getByLabel('Name', { exact: true }))
|
|
85
|
+
.first();
|
|
86
|
+
this.submitButton = this.creationDialog
|
|
87
|
+
.getByRole('button', { name: 'Create project', exact: true })
|
|
88
|
+
.or(this.creationDialog.getByRole('button', { name: 'Create', exact: true }))
|
|
89
|
+
.first();
|
|
90
|
+
this.cancelButton = this.creationDialog.getByRole('button', {
|
|
91
|
+
name: 'Cancel',
|
|
92
|
+
exact: true,
|
|
93
|
+
});
|
|
94
|
+
// Some shells expose a labelled resources region ("Resources" on the DS
|
|
95
|
+
// twin, "Folder explorer" on Carbon); the 8.10 flag-on Project detail does
|
|
96
|
+
// not, so this is only a soft readiness signal, never a required scope.
|
|
97
|
+
this.folderExplorer = page
|
|
98
|
+
.getByRole('region', { name: 'Folder explorer' })
|
|
99
|
+
.or(page.getByRole('region', { name: 'Resources' }))
|
|
100
|
+
.first();
|
|
101
|
+
// Proof we are on a Project (resource container) page: the flag-on Project
|
|
102
|
+
// detail renders this title-bar hook.
|
|
103
|
+
this.titleBar = page.locator('[data-test="process-application-title-bar"]');
|
|
104
|
+
// Any Project-detail marker also serves as an "inside the container" probe.
|
|
105
|
+
this.breadcrumb = this.titleBar.or(this.folderExplorer).first();
|
|
106
|
+
// The Project detail's "Create new" menu. Flag-on 8.10 renders it as
|
|
107
|
+
// [data-test="create-action-dropdown"] (aria-label "Create new"); older
|
|
108
|
+
// shells used [data-test="diagram-dropdown"]. Match either, page-wide,
|
|
109
|
+
// because the flag-on Project detail exposes no resources region to scope to.
|
|
110
|
+
this.createNewDropdown = page
|
|
111
|
+
.locator('[data-test="create-action-dropdown"]')
|
|
112
|
+
.or(page.locator('[data-test="diagram-dropdown"]'))
|
|
113
|
+
.or(page.getByRole('button', { name: 'Create new' }))
|
|
114
|
+
.first();
|
|
115
|
+
this.inlineNameInput = page.locator('[data-test="editable-input"]');
|
|
116
|
+
// "Assigned environments" heading: WorkspaceEnvironmentsTab's ready signal
|
|
117
|
+
// (it only renders once the workspace-scoped read has resolved).
|
|
118
|
+
this.manageEnvironmentsHeading = page.locator('[data-test="manage-environments-heading"]');
|
|
119
|
+
this.assignedEnvironmentCards = page.locator('[data-test="environment-card"]');
|
|
120
|
+
// Reads "Add environments" with none assigned yet, "Edit environments"
|
|
121
|
+
// otherwise -- same data-test either way.
|
|
122
|
+
this.editEnvironmentsButton = page.locator('[data-test="edit-environments-button"]');
|
|
123
|
+
this.environmentEditorRegion = page.getByRole('group', {
|
|
124
|
+
name: 'Edit assigned environments',
|
|
125
|
+
});
|
|
126
|
+
// Page-scoped, not environmentEditorRegion-scoped: Save/Cancel are
|
|
127
|
+
// rendered in the tab's header row, a SIBLING of the "Edit assigned
|
|
128
|
+
// environments" group, not a descendant of it.
|
|
129
|
+
this.saveEnvironmentsButton = page.getByRole('button', {
|
|
130
|
+
name: 'Save',
|
|
131
|
+
exact: true,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// Any one of the title bar, the resource region or the create-new dropdown
|
|
135
|
+
// proves we are on the Project (resource container) page.
|
|
136
|
+
pageReady() {
|
|
137
|
+
return this.titleBar
|
|
138
|
+
.or(this.createNewDropdown)
|
|
139
|
+
.or(this.folderExplorer)
|
|
140
|
+
.first();
|
|
141
|
+
}
|
|
142
|
+
async waitForProcessApplication(timeout = 90000) {
|
|
143
|
+
await (0, test_1.expect)(this.pageReady()).toBeVisible({ timeout });
|
|
144
|
+
}
|
|
145
|
+
async clickCreateProcessApplicationButton() {
|
|
146
|
+
await (0, test_1.expect)(this.createProcessApplicationButton).toBeVisible({
|
|
147
|
+
timeout: 60000,
|
|
148
|
+
});
|
|
149
|
+
await this.createProcessApplicationButton.click({ timeout: 60000 });
|
|
150
|
+
}
|
|
151
|
+
async clickCreateProcessApplicationOption() {
|
|
152
|
+
await (0, test_1.expect)(this.createProcessApplicationOption).toBeVisible({
|
|
153
|
+
timeout: 60000,
|
|
154
|
+
});
|
|
155
|
+
await this.createProcessApplicationOption.click({ timeout: 60000 });
|
|
156
|
+
}
|
|
157
|
+
// A Project is named up front in the create-project dialog. The inline path is
|
|
158
|
+
// kept as a fallback for the legacy flag-off "process application" surface.
|
|
159
|
+
async nameNewProcessApplication(name) {
|
|
160
|
+
const isModal = await this.creationDialog
|
|
161
|
+
.waitFor({ state: 'visible', timeout: 15000 })
|
|
162
|
+
.then(() => true)
|
|
163
|
+
.catch(() => false);
|
|
164
|
+
if (isModal) {
|
|
165
|
+
await this.nameInput.click({ timeout: 30000 });
|
|
166
|
+
await this.nameInput.fill(name);
|
|
167
|
+
await (0, test_1.expect)(this.nameInput).toHaveValue(name, { timeout: 10000 });
|
|
168
|
+
await (0, submitModalWithRetry_1.submitModalWithRetry)(this.submitButton, this.creationDialog);
|
|
169
|
+
// The app navigates to the new Project's own detail page on submit.
|
|
170
|
+
await this.page
|
|
171
|
+
.waitForURL(/\/projects\/[^/?#]+/, { timeout: 60000 })
|
|
172
|
+
.catch(() => { });
|
|
173
|
+
await this.waitForProcessApplication();
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
await (0, test_1.expect)(this.inlineNameInput).toBeVisible({ timeout: 30000 });
|
|
177
|
+
await this.inlineNameInput.click({ timeout: 60000 });
|
|
178
|
+
await this.inlineNameInput.fill(name);
|
|
179
|
+
await this.inlineNameInput.press('Enter');
|
|
180
|
+
await this.waitForProcessApplication();
|
|
181
|
+
}
|
|
182
|
+
rememberResourceContainer(url) {
|
|
183
|
+
resourceContainers.set(this.page, { url });
|
|
184
|
+
}
|
|
185
|
+
// Derives the shared Workspace's Environments settings URL from a Project
|
|
186
|
+
// detail URL (".../workspaces/<wsId>/projects/<projectId>[?...]"): the
|
|
187
|
+
// origin/app-root plus "/workspaces/<wsId>/settings?tab=environments" -- the
|
|
188
|
+
// same deep link camunda-hub's own useManageEnvironmentsLink builds for the
|
|
189
|
+
// deploy dialog's "Manage environments" link. Always emits the plural
|
|
190
|
+
// "workspaces" regardless of which spelling the container URL used
|
|
191
|
+
// (ModelerHomePage.openWorkspaceTile still tolerates a singular
|
|
192
|
+
// "/workspace/<id>/projects" from camunda-hub#28928 not yet being merged
|
|
193
|
+
// everywhere) -- the settings route itself (App.tsx) is plural-only, with no
|
|
194
|
+
// singular alias, so reusing a singular prefix verbatim would 404.
|
|
195
|
+
workspaceEnvironmentsUrl(containerUrl) {
|
|
196
|
+
const match = containerUrl.match(/^(.*)\/workspaces?\/([^/?#]+)\//);
|
|
197
|
+
if (!match) {
|
|
198
|
+
throw new Error(`Could not derive the workspace Environments URL from "${containerUrl}": ` +
|
|
199
|
+
'expected a "/workspaces/<id>/..." segment.');
|
|
200
|
+
}
|
|
201
|
+
const [, origin, workspaceId] = match;
|
|
202
|
+
return `${origin}/workspaces/${workspaceId}/settings?tab=environments`;
|
|
203
|
+
}
|
|
204
|
+
// Assigns an Environment to the shared Workspace via its Environments
|
|
205
|
+
// settings tab, so Deploy/Play (and Play's "Configure environment") find a
|
|
206
|
+
// usable target instead of rendering "No environments assigned" and staying
|
|
207
|
+
// disabled -- camunda-hub#29210 moved this out of the Project detail's own
|
|
208
|
+
// "Define stages" dialog and up to the Workspace, shared by every Project
|
|
209
|
+
// inside it.
|
|
210
|
+
//
|
|
211
|
+
// Checks the read-only "Assigned environments" list before entering edit
|
|
212
|
+
// mode, so a Workspace another (parallel) test already assigned is left
|
|
213
|
+
// alone regardless of this Page's own memo flag below -- required because
|
|
214
|
+
// every test's Project lives in the one shared Workspace. This is a
|
|
215
|
+
// best-effort check, not a lock: two tests racing to assign at once each
|
|
216
|
+
// still write their own snapshot of the selected set (Save persists the
|
|
217
|
+
// whole set, it does not merge), so a genuine concurrent race could still
|
|
218
|
+
// trigger two writes. It converges correctly here only because every test
|
|
219
|
+
// targets the one MODELER_CLUSTER_NAME (or, when unset, is happy with
|
|
220
|
+
// whichever single Environment ends up assigned).
|
|
221
|
+
async ensureStageCluster(clusterName) {
|
|
222
|
+
const container = resourceContainers.get(this.page);
|
|
223
|
+
if (!container ||
|
|
224
|
+
(container.stageBound && container.developmentCluster === clusterName)) {
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
if (adminAssignedWorkspaces
|
|
228
|
+
.get(this.page)
|
|
229
|
+
?.has(this.workspaceId(container.url))) {
|
|
230
|
+
container.stageBound = true;
|
|
231
|
+
container.developmentCluster = clusterName;
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const returnTo = this.page.url();
|
|
235
|
+
await this.assignEnvironmentToWorkspace(container.url, clusterName);
|
|
236
|
+
container.stageBound = true;
|
|
237
|
+
container.developmentCluster = clusterName;
|
|
238
|
+
await this.page.goto(returnTo, { waitUntil: 'domcontentloaded' });
|
|
239
|
+
}
|
|
240
|
+
// Assigns an Environment to the workspace that `workspaceUrl` (any
|
|
241
|
+
// "/workspaces/<id>/..." URL) belongs to, driving this Page's own session.
|
|
242
|
+
async assignEnvironmentToWorkspace(workspaceUrl, clusterName) {
|
|
243
|
+
const environmentsUrl = this.workspaceEnvironmentsUrl(workspaceUrl);
|
|
244
|
+
await (0, test_1.expect)(async () => {
|
|
245
|
+
await this.page.goto(environmentsUrl, { waitUntil: 'domcontentloaded' });
|
|
246
|
+
await this.assignWorkspaceEnvironment(clusterName);
|
|
247
|
+
}).toPass({ timeout: 240000, intervals: [1000, 2000, 5000] });
|
|
248
|
+
}
|
|
249
|
+
markWorkspaceEnvironmentAssignedByAdmin(workspaceUrl) {
|
|
250
|
+
const assigned = adminAssignedWorkspaces.get(this.page) ?? new Set();
|
|
251
|
+
assigned.add(this.workspaceId(workspaceUrl));
|
|
252
|
+
adminAssignedWorkspaces.set(this.page, assigned);
|
|
253
|
+
}
|
|
254
|
+
workspaceId(workspaceUrl) {
|
|
255
|
+
const match = workspaceUrl.match(/\/workspaces?\/([^/?#]+)\//);
|
|
256
|
+
if (!match) {
|
|
257
|
+
throw new Error(`Could not derive the workspace id from "${workspaceUrl}": ` +
|
|
258
|
+
'expected a "/workspaces/<id>/..." segment.');
|
|
259
|
+
}
|
|
260
|
+
return match[1];
|
|
261
|
+
}
|
|
262
|
+
async assignWorkspaceEnvironment(clusterName) {
|
|
263
|
+
const tabReady = await this.manageEnvironmentsHeading
|
|
264
|
+
.or(this.editEnvironmentsButton)
|
|
265
|
+
.first()
|
|
266
|
+
.waitFor({ state: 'visible', timeout: 90000 })
|
|
267
|
+
.then(() => true)
|
|
268
|
+
.catch(() => false);
|
|
269
|
+
if (!tabReady) {
|
|
270
|
+
// WorkspaceManageBody silently normalises an unrecognised/unusable
|
|
271
|
+
// "?tab=environments" back to Members -- e.g. isEnvironmentsEnabled()
|
|
272
|
+
// off, or the current user failing canManageWorkspaceEnvironments
|
|
273
|
+
// (assigning is stricter than viewing: a plain Editor with no
|
|
274
|
+
// elevated/DevOps org role cannot assign at all). Name the likely
|
|
275
|
+
// cause rather than timing out with an opaque "not visible".
|
|
276
|
+
const onMembersTab = await this.page
|
|
277
|
+
.locator('[data-test="tab-members"][data-state="active"]')
|
|
278
|
+
.isVisible()
|
|
279
|
+
.catch(() => false);
|
|
280
|
+
throw new Error('The workspace Environments settings tab never rendered' +
|
|
281
|
+
(onMembersTab ? ' -- landed on the Members tab instead' : '') +
|
|
282
|
+
'. Either the Environments feature is off for this deployment, or the ' +
|
|
283
|
+
"current user lacks permission to manage this workspace's environments.");
|
|
284
|
+
}
|
|
285
|
+
const alreadyAssigned = clusterName
|
|
286
|
+
? this.assignedEnvironmentCards.filter({ hasText: clusterName })
|
|
287
|
+
: this.assignedEnvironmentCards;
|
|
288
|
+
if (await alreadyAssigned
|
|
289
|
+
.first()
|
|
290
|
+
.isVisible()
|
|
291
|
+
.catch(() => false)) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
await this.editEnvironmentsButton.click({ timeout: 30000 });
|
|
295
|
+
await (0, test_1.expect)(this.environmentEditorRegion).toBeVisible({ timeout: 30000 });
|
|
296
|
+
const escapedClusterName = clusterName?.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
297
|
+
// A picker card's toggle button is "Select <name> (<clusterName or
|
|
298
|
+
// clusterId>)" (see camunda-hub EnvironmentPickerCard) -- match the
|
|
299
|
+
// cluster name in that parenthetical rather than the environment's own
|
|
300
|
+
// display name, which is a separate, admin-chosen field that need not
|
|
301
|
+
// match the cluster at all.
|
|
302
|
+
const targetOption = clusterName
|
|
303
|
+
? this.environmentEditorRegion
|
|
304
|
+
.getByRole('button', {
|
|
305
|
+
name: new RegExp(`^Select .*\\(${escapedClusterName}\\)`),
|
|
306
|
+
})
|
|
307
|
+
.first()
|
|
308
|
+
: this.environmentEditorRegion
|
|
309
|
+
.getByRole('button', { name: /^Select / })
|
|
310
|
+
.first();
|
|
311
|
+
const optionAppeared = await targetOption
|
|
312
|
+
.waitFor({ state: 'visible', timeout: 30000 })
|
|
313
|
+
.then(() => true)
|
|
314
|
+
.catch(() => false);
|
|
315
|
+
if (!optionAppeared) {
|
|
316
|
+
throw new Error(clusterName
|
|
317
|
+
? `No Environment backed by cluster "${clusterName}" is available to assign to this ` +
|
|
318
|
+
'workspace. Register the cluster (or its Environment) first.'
|
|
319
|
+
: 'No Environment is available to assign to this workspace: the picker shows "No ' +
|
|
320
|
+
'environments available".');
|
|
321
|
+
}
|
|
322
|
+
await targetOption.click({ timeout: 30000 });
|
|
323
|
+
await (0, test_1.expect)(this.saveEnvironmentsButton).toBeEnabled({ timeout: 30000 });
|
|
324
|
+
await this.saveEnvironmentsButton.click({ timeout: 30000 });
|
|
325
|
+
await (0, test_1.expect)(this.environmentEditorRegion).toBeHidden({ timeout: 60000 });
|
|
326
|
+
}
|
|
327
|
+
async openByName(name) {
|
|
328
|
+
// Workspace-mode projects table: the whole row is the click target (see the
|
|
329
|
+
// hub team's WorkspaceProjectsPage). Fall back to the legacy title cell.
|
|
330
|
+
const projectRow = this.page
|
|
331
|
+
.getByRole('row', { name })
|
|
332
|
+
.first()
|
|
333
|
+
.or(this.page.getByRole('row').filter({ hasText: name }).getByTitle(name))
|
|
334
|
+
.first();
|
|
335
|
+
await (0, test_1.expect)(projectRow).toBeVisible({ timeout: 90000 });
|
|
336
|
+
await projectRow.click({ timeout: 60000 });
|
|
337
|
+
await this.waitForProcessApplication();
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
exports.ProcessApplicationPage = ProcessApplicationPage;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
declare class SettingsPage {
|
|
3
|
+
private page;
|
|
4
|
+
readonly openSettingsButton: Locator;
|
|
5
|
+
readonly logoutButton: Locator;
|
|
6
|
+
readonly openUserSidebarButton: Locator;
|
|
7
|
+
constructor(page: Page);
|
|
8
|
+
clickOpenSettingsButton(): Promise<void>;
|
|
9
|
+
clickOpenUserSideBarButton(): Promise<void>;
|
|
10
|
+
clickLogoutButton(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
export { SettingsPage };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SettingsPage = void 0;
|
|
4
|
+
class SettingsPage {
|
|
5
|
+
page;
|
|
6
|
+
openSettingsButton;
|
|
7
|
+
logoutButton;
|
|
8
|
+
openUserSidebarButton;
|
|
9
|
+
constructor(page) {
|
|
10
|
+
this.page = page;
|
|
11
|
+
this.openSettingsButton = page
|
|
12
|
+
.getByRole('button', { name: /^User menu/ })
|
|
13
|
+
.or(page.getByLabel('Open Settings'))
|
|
14
|
+
.first();
|
|
15
|
+
this.openUserSidebarButton = page
|
|
16
|
+
.getByRole('button', { name: /^User menu/ })
|
|
17
|
+
.or(page.getByLabel('Open User Sidebar'))
|
|
18
|
+
.first();
|
|
19
|
+
this.logoutButton = page
|
|
20
|
+
.getByRole('menuitem', { name: /log out/i })
|
|
21
|
+
.or(page.getByRole('button', { name: 'Log out' }))
|
|
22
|
+
.first();
|
|
23
|
+
}
|
|
24
|
+
async clickOpenSettingsButton() {
|
|
25
|
+
await this.openSettingsButton.click({ timeout: 120000 });
|
|
26
|
+
}
|
|
27
|
+
async clickOpenUserSideBarButton() {
|
|
28
|
+
await this.openUserSidebarButton.click({ timeout: 120000 });
|
|
29
|
+
}
|
|
30
|
+
async clickLogoutButton() {
|
|
31
|
+
await this.logoutButton.click({ timeout: 30000 });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.SettingsPage = SettingsPage;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
declare class TaskDetailsPage {
|
|
3
|
+
private page;
|
|
4
|
+
readonly assignToMeButton: Locator;
|
|
5
|
+
readonly completeButton: Locator;
|
|
6
|
+
readonly unassignButton: Locator;
|
|
7
|
+
readonly assignee: Locator;
|
|
8
|
+
readonly completeTaskButton: Locator;
|
|
9
|
+
readonly addVariableButton: Locator;
|
|
10
|
+
readonly detailsPanel: Locator;
|
|
11
|
+
readonly detailsHeader: Locator;
|
|
12
|
+
readonly pendingTaskDescription: Locator;
|
|
13
|
+
readonly pickATaskHeader: Locator;
|
|
14
|
+
readonly emptyTaskMessage: Locator;
|
|
15
|
+
readonly nameInput: Locator;
|
|
16
|
+
readonly addressInput: Locator;
|
|
17
|
+
readonly ageInput: Locator;
|
|
18
|
+
readonly variablesTable: Locator;
|
|
19
|
+
readonly nameColumnHeader: Locator;
|
|
20
|
+
readonly valueColumnHeader: Locator;
|
|
21
|
+
readonly form: Locator;
|
|
22
|
+
readonly numberInput: Locator;
|
|
23
|
+
readonly incrementButton: Locator;
|
|
24
|
+
readonly decrementButton: Locator;
|
|
25
|
+
readonly dateInput: Locator;
|
|
26
|
+
readonly timeInput: Locator;
|
|
27
|
+
readonly checkbox: Locator;
|
|
28
|
+
readonly selectDropdown: Locator;
|
|
29
|
+
readonly tagList: Locator;
|
|
30
|
+
readonly detailsInfo: Locator;
|
|
31
|
+
readonly textInput: Locator;
|
|
32
|
+
readonly taskCompletedBanner: Locator;
|
|
33
|
+
readonly assignedToMeText: Locator;
|
|
34
|
+
readonly noPreviewContainer: Locator;
|
|
35
|
+
readonly previewContainer: Locator;
|
|
36
|
+
constructor(page: Page);
|
|
37
|
+
clickAssignToMeButton(): Promise<void>;
|
|
38
|
+
clickUnassignButton(): Promise<void>;
|
|
39
|
+
clickCompleteTaskButton(): Promise<void>;
|
|
40
|
+
clickAddVariableButton(): Promise<void>;
|
|
41
|
+
replaceExistingVariableValue(values: {
|
|
42
|
+
name: string;
|
|
43
|
+
value: string;
|
|
44
|
+
}): Promise<void>;
|
|
45
|
+
getNthVariableNameInput(nth: number): Locator;
|
|
46
|
+
getNthVariableValueInput(nth: number): Locator;
|
|
47
|
+
addVariable(payload: {
|
|
48
|
+
name: string;
|
|
49
|
+
value: string;
|
|
50
|
+
}): Promise<void>;
|
|
51
|
+
fillNumber(number: string): Promise<void>;
|
|
52
|
+
clickIncrementButton(): Promise<void>;
|
|
53
|
+
clickDecrementButton(): Promise<void>;
|
|
54
|
+
fillDate(date: string): Promise<void>;
|
|
55
|
+
enterTime(time: string): Promise<void>;
|
|
56
|
+
checkCheckbox(): Promise<void>;
|
|
57
|
+
selectDropdownValue(value: string): Promise<void>;
|
|
58
|
+
clickRadioButton(radioBtnLabel: string): Promise<void>;
|
|
59
|
+
checkChecklistBox(label: string): Promise<void>;
|
|
60
|
+
enterTwoValuesInTagList(value1: string, value2: string): Promise<void>;
|
|
61
|
+
clickTextInput(): Promise<void>;
|
|
62
|
+
fillTextInput(value: string): Promise<void>;
|
|
63
|
+
priorityAssertion(priority: string): Promise<void>;
|
|
64
|
+
taskAssertion(name: string): Promise<void>;
|
|
65
|
+
assertLoadedImage(documentName: string): Promise<void>;
|
|
66
|
+
assertDocumentDownloadButton(documentName: string): Promise<void>;
|
|
67
|
+
assertPdfPreviewViewerExists(): Promise<void>;
|
|
68
|
+
assertTextIsPresent(expectedText: string): Promise<void>;
|
|
69
|
+
}
|
|
70
|
+
export { TaskDetailsPage };
|