@camunda/e2e-test-suite 0.0.1273 → 0.0.1274
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 +2 -0
- package/dist/pages/8.8/AppsPage.js +122 -0
- package/dist/pages/8.8/ClusterDetailsPage.d.ts +1 -0
- package/dist/pages/8.8/ClusterDetailsPage.js +29 -10
- package/dist/pages/8.8/ClusterPage.js +35 -2
- package/dist/pages/8.8/ClusterSecretsPage.d.ts +1 -0
- package/dist/pages/8.8/ClusterSecretsPage.js +62 -2
- package/dist/pages/8.8/FormJsPage.js +55 -24
- package/dist/pages/8.8/HomePage.d.ts +3 -0
- package/dist/pages/8.8/HomePage.js +62 -7
- package/dist/pages/8.8/LoginPage.js +32 -1
- package/dist/pages/8.8/ModelerCreatePage.d.ts +1 -0
- package/dist/pages/8.8/ModelerCreatePage.js +78 -16
- package/dist/pages/8.8/ModelerHomePage.d.ts +10 -0
- package/dist/pages/8.8/ModelerHomePage.js +339 -14
- package/dist/pages/8.8/UtilitiesPage.js +47 -2
- package/dist/tests/8.8/test-setup.spec.js +29 -4
- package/dist/utils/consoleApiHelpers.d.ts +5 -0
- package/dist/utils/consoleApiHelpers.js +177 -2
- package/dist/utils/hubDeployDialog.d.ts +3 -0
- package/dist/utils/hubDeployDialog.js +74 -0
- package/dist/utils/hubResourceRename.d.ts +4 -0
- package/dist/utils/hubResourceRename.js +74 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.waitForAiFeaturesEnabledViaApi = exports.areAiFeaturesEnabledViaApi = exports.waitForClusterRestartHealthyViaApi = exports.waitForClusterHealthyViaApi = exports.ensureClientAuditLoggingViaApi = exports.ensureTasklistV2Enabled = exports.ensureClusterViaApi = exports.createClusterViaApi = exports.deleteClusterViaApi = exports.resetHubBearerToken = void 0;
|
|
3
|
+
exports.ensureClusterLinkedToSharedWorkspace = exports.linkClusterToSharedWorkspaceViaApi = exports.getHubWorkspaceId = exports.rememberHubWorkspaceId = exports.linkClusterToWorkspaceViaApi = exports.waitForAiFeaturesEnabledViaApi = exports.areAiFeaturesEnabledViaApi = exports.waitForClusterRestartHealthyViaApi = exports.waitForClusterHealthyViaApi = exports.ensureClientAuditLoggingViaApi = exports.ensureTasklistV2Enabled = exports.ensureClusterViaApi = exports.createClusterViaApi = exports.deleteClusterViaApi = exports.resetHubBearerToken = void 0;
|
|
4
4
|
const test_1 = require("@playwright/test");
|
|
5
5
|
const sleep_1 = require("./sleep");
|
|
6
6
|
const PLAN_TYPE_ID = '574cde5f-54ae-43c1-851a-e6fb4f03423a';
|
|
@@ -169,7 +169,7 @@ const API_WRITE_TIMEOUT_MS = 45000;
|
|
|
169
169
|
// POST is the only non-idempotent verb used here: repeating it creates a second
|
|
170
170
|
// cluster under the same name. Let its timeout surface instead, so the caller
|
|
171
171
|
// can recover by looking the cluster up by name (see createClusterViaApi).
|
|
172
|
-
const TIMEOUT_RETRYABLE_METHODS = ['GET', 'PATCH', 'DELETE'];
|
|
172
|
+
const TIMEOUT_RETRYABLE_METHODS = ['GET', 'PUT', 'PATCH', 'DELETE'];
|
|
173
173
|
async function clusterApiRequest(page, method, url, data) {
|
|
174
174
|
let response;
|
|
175
175
|
for (let attempt = 1; attempt <= API_MAX_ATTEMPTS; attempt++) {
|
|
@@ -751,3 +751,178 @@ async function waitForAiFeaturesEnabledViaApi(page, timeoutMs = 180000) {
|
|
|
751
751
|
.toBe(true);
|
|
752
752
|
}
|
|
753
753
|
exports.waitForAiFeaturesEnabledViaApi = waitForAiFeaturesEnabledViaApi;
|
|
754
|
+
// Camunda Hub gates deployment on ENVIRONMENTS assigned to the workspace, not
|
|
755
|
+
// on the org's clusters: with the feature on, the deploy modal lists only the
|
|
756
|
+
// workspace's assigned environments, so a cluster created after the workspace
|
|
757
|
+
// exists is invisible there and the picker comes up empty. The wizard's
|
|
758
|
+
// "Assign environments" step only covers a workspace created AFTER its
|
|
759
|
+
// cluster, which the nightly cannot guarantee -- test-setup creates clusters
|
|
760
|
+
// and workspaces in parallel.
|
|
761
|
+
//
|
|
762
|
+
// Endpoints and semantics mirror camunda-hub's own
|
|
763
|
+
// e2e-playwright/api/modeler/EnvironmentsApiClient.ts.
|
|
764
|
+
async function hubApiOrigin(page) {
|
|
765
|
+
return new URL(page.url()).origin;
|
|
766
|
+
}
|
|
767
|
+
// Every `/api/internal/v2/**` body is wrapped by Hub's
|
|
768
|
+
// `ResponseBodyWrapperAdvice` as `{data, errors}`, so the array lives under
|
|
769
|
+
// `data` -- reading the body directly yielded an object and every array
|
|
770
|
+
// operation on it threw. Mirrors camunda-hub's own `toEnvironments`.
|
|
771
|
+
function toHubEnvironments(body) {
|
|
772
|
+
const payload = body && typeof body === 'object' && 'data' in body
|
|
773
|
+
? body.data
|
|
774
|
+
: body;
|
|
775
|
+
const list = Array.isArray(payload) ? payload : [];
|
|
776
|
+
return list
|
|
777
|
+
.map((item) => (item ?? {}))
|
|
778
|
+
.filter((item) => typeof item.id === 'string')
|
|
779
|
+
.map((item) => ({
|
|
780
|
+
id: item.id,
|
|
781
|
+
name: typeof item.name === 'string' ? item.name : '',
|
|
782
|
+
clusterName: typeof item.clusterName === 'string' ? item.clusterName : '',
|
|
783
|
+
discoveryState: typeof item.discoveryState === 'string' ? item.discoveryState : '',
|
|
784
|
+
}));
|
|
785
|
+
}
|
|
786
|
+
async function listOrganizationEnvironments(page) {
|
|
787
|
+
const origin = await hubApiOrigin(page);
|
|
788
|
+
const response = await clusterApiRequest(page, 'GET', `${origin}/api/internal/v2/organizations/${encodeURIComponent(process.env.ORG_ID)}/environments`);
|
|
789
|
+
// 404 means the environments feature is off for this org: there is nothing
|
|
790
|
+
// to assign and deployment is not gated on it, so callers no-op.
|
|
791
|
+
if (response.status() === 404) {
|
|
792
|
+
return null;
|
|
793
|
+
}
|
|
794
|
+
return toHubEnvironments(await response.json());
|
|
795
|
+
}
|
|
796
|
+
async function listWorkspaceEnvironments(page, workspaceId) {
|
|
797
|
+
const origin = await hubApiOrigin(page);
|
|
798
|
+
const response = await clusterApiRequest(page, 'GET', `${origin}/api/internal/v2/workspaces/${encodeURIComponent(workspaceId)}/environments`);
|
|
799
|
+
if (!response.ok()) {
|
|
800
|
+
// NOT an empty list: the caller feeds this into a full-set replace, so
|
|
801
|
+
// reporting "nothing assigned" for a 401/5xx would unassign every
|
|
802
|
+
// environment the workspace already had.
|
|
803
|
+
throw new Error(`Failed to read environments for workspace ${workspaceId}: ` +
|
|
804
|
+
`${response.status()} ${response.statusText()}`);
|
|
805
|
+
}
|
|
806
|
+
return toHubEnvironments(await response.json());
|
|
807
|
+
}
|
|
808
|
+
// Assigns the environment backing `clusterName` to `workspaceId`, keeping the
|
|
809
|
+
// workspace's existing assignments: the PUT is a full-set REPLACE, so sending
|
|
810
|
+
// only the new id would silently unassign every other cluster the suite had
|
|
811
|
+
// already linked (the AWS cluster, a sibling Tasklist-mode project's default).
|
|
812
|
+
async function linkClusterToWorkspaceViaApi(page, workspaceId, clusterName) {
|
|
813
|
+
const environments = await listOrganizationEnvironments(page);
|
|
814
|
+
if (environments === null) {
|
|
815
|
+
console.log('Environments feature is off for this org; leaving the workspace unlinked');
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
const matches = environments.filter((environment) => environment.clusterName === clusterName);
|
|
819
|
+
// Prefer an ACTIVE environment over a stale NOT_REPORTED duplicate left by
|
|
820
|
+
// an earlier cluster of the same name.
|
|
821
|
+
const match = matches.find((environment) => environment.discoveryState === 'ACTIVE') ??
|
|
822
|
+
matches[0];
|
|
823
|
+
if (!match) {
|
|
824
|
+
throw new Error(`No environment backs cluster "${clusterName}"; available: ` +
|
|
825
|
+
(environments
|
|
826
|
+
.map((e) => `${e.name}@${e.clusterName} (${e.discoveryState})`)
|
|
827
|
+
.join(', ') || '<none>'));
|
|
828
|
+
}
|
|
829
|
+
const assigned = await listWorkspaceEnvironments(page, workspaceId);
|
|
830
|
+
if (assigned.some((environment) => environment.id === match.id)) {
|
|
831
|
+
// Logged rather than returning silently: a quiet no-op here is
|
|
832
|
+
// indistinguishable from the helper never having run at all, which is
|
|
833
|
+
// exactly the ambiguity that hid an earlier bug.
|
|
834
|
+
console.log(`Cluster "${clusterName}" is already linked to workspace ${workspaceId}`);
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
const origin = await hubApiOrigin(page);
|
|
838
|
+
const url = `${origin}/api/internal/v2/workspaces/${encodeURIComponent(workspaceId)}/environments`;
|
|
839
|
+
// Read-modify-write against a full-set replace, and test-setup runs its
|
|
840
|
+
// cluster tests in parallel: two workers can read the same assignment list
|
|
841
|
+
// and the second PUT then drops the first one's environment. Re-read after
|
|
842
|
+
// writing and retry when our own id is missing, so a lost update heals
|
|
843
|
+
// instead of silently removing a cluster from every later deploy dialog.
|
|
844
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
845
|
+
const current = attempt === 1
|
|
846
|
+
? assigned
|
|
847
|
+
: await listWorkspaceEnvironments(page, workspaceId);
|
|
848
|
+
if (attempt > 1 && current.some((e) => e.id === match.id)) {
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
const ids = [...new Set([...current.map((e) => e.id), match.id])];
|
|
852
|
+
const response = await clusterApiRequest(page, 'PUT', url, {
|
|
853
|
+
environmentIds: ids,
|
|
854
|
+
});
|
|
855
|
+
if (!response.ok()) {
|
|
856
|
+
// clusterApiRequest returns non-2xx for non-transient statuses rather
|
|
857
|
+
// than throwing, so an unchecked call would log success and let the
|
|
858
|
+
// deploy fail much later with an unrelated-looking empty picker.
|
|
859
|
+
throw new Error(`Failed to link cluster "${clusterName}" to workspace ${workspaceId}: ` +
|
|
860
|
+
`${response.status()} ${response.statusText()}`);
|
|
861
|
+
}
|
|
862
|
+
const after = await listWorkspaceEnvironments(page, workspaceId);
|
|
863
|
+
if (after.some((e) => e.id === match.id)) {
|
|
864
|
+
console.log(`Linked cluster "${clusterName}" (environment ${match.id}) to workspace ${workspaceId}`);
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
throw new Error(`Cluster "${clusterName}" did not stay linked to workspace ${workspaceId} ` +
|
|
869
|
+
'after 3 attempts; a concurrent environment replace keeps dropping it');
|
|
870
|
+
}
|
|
871
|
+
exports.linkClusterToWorkspaceViaApi = linkClusterToWorkspaceViaApi;
|
|
872
|
+
// The Hub workspace the suite's shared project lives in. Remembered while the
|
|
873
|
+
// flow is on a /workspaces/<id> path so later steps -- the deploy dialog runs
|
|
874
|
+
// from the form editor, whose path has no workspace segment -- can still
|
|
875
|
+
// address it.
|
|
876
|
+
const hubWorkspaceIds = new WeakMap();
|
|
877
|
+
function rememberHubWorkspaceId(page, workspaceId) {
|
|
878
|
+
hubWorkspaceIds.set(page, workspaceId);
|
|
879
|
+
}
|
|
880
|
+
exports.rememberHubWorkspaceId = rememberHubWorkspaceId;
|
|
881
|
+
function getHubWorkspaceId(page) {
|
|
882
|
+
return hubWorkspaceIds.get(page);
|
|
883
|
+
}
|
|
884
|
+
exports.getHubWorkspaceId = getHubWorkspaceId;
|
|
885
|
+
// Resolves the suite's shared Hub workspace by NAME. Needed at cluster-creation
|
|
886
|
+
// time, where there is no /workspaces/<id> page URL to read the id from.
|
|
887
|
+
// Endpoint and payload shape from camunda-hub's own
|
|
888
|
+
// e2e-playwright/api/modeler/WorkspacesApiClient.ts: the list is nested as
|
|
889
|
+
// `{data: {items: [...]}}`, not the bare array the environments routes return.
|
|
890
|
+
const SHARED_HUB_WORKSPACE_NAME = 'Cross Component Test Project';
|
|
891
|
+
async function findHubWorkspaceIdByName(page, name) {
|
|
892
|
+
const origin = await hubApiOrigin(page);
|
|
893
|
+
const response = await clusterApiRequest(page, 'GET', `${origin}/api/internal/v2/organizations/${encodeURIComponent(process.env.ORG_ID)}/workspaces?sort=name,asc&offset=0&limit=100`);
|
|
894
|
+
if (!response.ok()) {
|
|
895
|
+
return undefined;
|
|
896
|
+
}
|
|
897
|
+
const body = (await response.json());
|
|
898
|
+
const data = (body.data ?? body);
|
|
899
|
+
const items = Array.isArray(data.items) ? data.items : [];
|
|
900
|
+
const match = items
|
|
901
|
+
.map((item) => (item ?? {}))
|
|
902
|
+
.find((item) => item.name === name && typeof item.id === 'string');
|
|
903
|
+
return match?.id;
|
|
904
|
+
}
|
|
905
|
+
// Links a just-created cluster to the suite's shared workspace. Safe to call
|
|
906
|
+
// right after creating a cluster by API or through the UI: when the workspace
|
|
907
|
+
// does not exist yet -- test-setup creates clusters and workspaces in parallel,
|
|
908
|
+
// so either order happens -- this logs and returns, and the ensure-link at
|
|
909
|
+
// deploy time covers that case instead.
|
|
910
|
+
async function linkClusterToSharedWorkspaceViaApi(page, clusterName) {
|
|
911
|
+
const workspaceId = getHubWorkspaceId(page) ??
|
|
912
|
+
(await findHubWorkspaceIdByName(page, SHARED_HUB_WORKSPACE_NAME));
|
|
913
|
+
if (!workspaceId) {
|
|
914
|
+
console.log(`Shared workspace "${SHARED_HUB_WORKSPACE_NAME}" does not exist yet; ` +
|
|
915
|
+
`"${clusterName}" will be linked when a deploy first needs it`);
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
rememberHubWorkspaceId(page, workspaceId);
|
|
919
|
+
await linkClusterToWorkspaceViaApi(page, workspaceId, clusterName);
|
|
920
|
+
}
|
|
921
|
+
exports.linkClusterToSharedWorkspaceViaApi = linkClusterToSharedWorkspaceViaApi;
|
|
922
|
+
// Ensure-link at the point a deploy dialog is about to be opened. Resolves the
|
|
923
|
+
// workspace from the id remembered while the flow was on it, else by name.
|
|
924
|
+
// Cheap and idempotent: it returns early once the environment is assigned.
|
|
925
|
+
async function ensureClusterLinkedToSharedWorkspace(page, clusterName) {
|
|
926
|
+
await linkClusterToSharedWorkspaceViaApi(page, clusterName);
|
|
927
|
+
}
|
|
928
|
+
exports.ensureClusterLinkedToSharedWorkspace = ensureClusterLinkedToSharedWorkspace;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertClusterSelectedInDialog = exports.selectOnlyThisResourceInDialog = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
// Hub's deploy / "Deploy & run" dialog scopes a deployment through a
|
|
6
|
+
// design-system select. The trigger carries no aria-labelledby, so its
|
|
7
|
+
// accessible name is its own VALUE ("All resources") rather than the
|
|
8
|
+
// "Resources to deploy" label beside it -- a name-based lookup silently
|
|
9
|
+
// matched nothing and left every deployment scoped to the whole project.
|
|
10
|
+
// Address it by id instead.
|
|
11
|
+
//
|
|
12
|
+
// Shared by the form deploy dialog and the diagram deploy-and-run dialog so
|
|
13
|
+
// the two cannot drift apart.
|
|
14
|
+
async function selectOnlyThisResourceInDialog(page, dialog) {
|
|
15
|
+
const scopeSelect = dialog
|
|
16
|
+
.locator('#deployment-scope-selection')
|
|
17
|
+
.or(dialog.getByRole('combobox', { name: 'Resources to deploy' }))
|
|
18
|
+
.first();
|
|
19
|
+
const hasScopeSelect = await scopeSelect
|
|
20
|
+
.waitFor({ state: 'visible', timeout: 10000 })
|
|
21
|
+
.then(() => true)
|
|
22
|
+
.catch(() => false);
|
|
23
|
+
if (!hasScopeSelect) {
|
|
24
|
+
// On Hub the default scope is the WHOLE PROJECT, so quietly accepting a
|
|
25
|
+
// missing trigger turns a resource deployment into a project deployment
|
|
26
|
+
// -- and the deployment confirmation is worded broadly enough that the
|
|
27
|
+
// test would still pass. Fail there; only the pre-Hub shells, which have
|
|
28
|
+
// no scope selector at all, may continue.
|
|
29
|
+
if (new URL(page.url()).hostname.startsWith('hub.')) {
|
|
30
|
+
throw new Error('Hub deploy dialog exposed no deployment-scope select ' +
|
|
31
|
+
'(#deployment-scope-selection); refusing to fall back to deploying ' +
|
|
32
|
+
'the whole project');
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
if ((await scopeSelect.innerText()).includes('Only this resource')) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
await scopeSelect.click();
|
|
40
|
+
// Page-scoped: the select renders its options in a portal attached to
|
|
41
|
+
// <body>, outside the dialog element.
|
|
42
|
+
await page
|
|
43
|
+
.getByRole('option', { name: 'Only this resource' })
|
|
44
|
+
.or(page.getByRole('menuitem', { name: 'Only this resource' }))
|
|
45
|
+
.or(page.getByText('Only this resource', { exact: true }))
|
|
46
|
+
.first()
|
|
47
|
+
.click({ timeout: 30000 });
|
|
48
|
+
await (0, test_1.expect)(scopeSelect).toContainText('Only this resource', {
|
|
49
|
+
timeout: 10000,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
exports.selectOnlyThisResourceInDialog = selectOnlyThisResourceInDialog;
|
|
53
|
+
// Confirms the chosen cluster is selected in a deploy dialog.
|
|
54
|
+
//
|
|
55
|
+
// Carbon nested a toggle `switch` inside the cluster's <label>; Hub's
|
|
56
|
+
// design-system dialog renders the control as a sibling radio next to the
|
|
57
|
+
// cluster name (auto-selected when a single cluster is offered), so a
|
|
58
|
+
// label-scoped lookup finds nothing at all. Search the DIALOG for a control
|
|
59
|
+
// named after the cluster, and keep the label-scoped shapes for the older
|
|
60
|
+
// shells.
|
|
61
|
+
async function assertClusterSelectedInDialog(dialog, clusterName) {
|
|
62
|
+
const byName = new RegExp(`^${clusterName}`);
|
|
63
|
+
const labelRow = dialog.locator('label').filter({ hasText: byName }).first();
|
|
64
|
+
const control = dialog
|
|
65
|
+
.getByRole('radio', { name: byName })
|
|
66
|
+
.or(dialog.getByRole('switch', { name: byName }))
|
|
67
|
+
.or(dialog.getByRole('checkbox', { name: byName }))
|
|
68
|
+
.or(labelRow.getByRole('switch'))
|
|
69
|
+
.or(labelRow.getByRole('radio'))
|
|
70
|
+
.or(labelRow.getByRole('checkbox'))
|
|
71
|
+
.first();
|
|
72
|
+
await (0, test_1.expect)(control).toBeChecked({ timeout: 30000 });
|
|
73
|
+
}
|
|
74
|
+
exports.assertClusterSelectedInDialog = assertClusterSelectedInDialog;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Page, Locator } from '@playwright/test';
|
|
2
|
+
export declare function hubBreadcrumbs(page: Page): Locator;
|
|
3
|
+
export declare function hubRenameInput(page: Page): Locator;
|
|
4
|
+
export declare function renameHubResource(page: Page, currentName: string, newName: string): Promise<void>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renameHubResource = exports.hubRenameInput = exports.hubBreadcrumbs = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
// Camunda Hub no longer exposes a resource's title as a directly editable
|
|
6
|
+
// field: the rename happens in a shared dialog (`rename-input` /
|
|
7
|
+
// `rename-submit`) opened from the CURRENT breadcrumb crumb's actions menu.
|
|
8
|
+
//
|
|
9
|
+
// Ported from camunda-hub's own e2e-playwright/pages/BasePage.ts
|
|
10
|
+
// (`renameCurrentEntityViaBreadcrumb`) rather than invented, including two
|
|
11
|
+
// hazards documented there:
|
|
12
|
+
// * the trigger must be scoped to the breadcrumbs and taken as `.last()` --
|
|
13
|
+
// an unscoped `/ actions$/` match can resolve to the PARENT's crumb
|
|
14
|
+
// mid-navigation and rename the parent instead;
|
|
15
|
+
// * NavProvider keeps the previous page's settled crumbs while the new
|
|
16
|
+
// route's are still pending, so gate positively on the trigger naming the
|
|
17
|
+
// entity we mean to rename before opening its menu.
|
|
18
|
+
function hubBreadcrumbs(page) {
|
|
19
|
+
return page
|
|
20
|
+
.locator('[data-test="breadcrumbs"]')
|
|
21
|
+
.or(page.getByRole('navigation', { name: 'Context navigation' }))
|
|
22
|
+
.or(page.getByRole('navigation', { name: 'Breadcrumb', exact: true }));
|
|
23
|
+
}
|
|
24
|
+
exports.hubBreadcrumbs = hubBreadcrumbs;
|
|
25
|
+
function hubRenameInput(page) {
|
|
26
|
+
return page
|
|
27
|
+
.locator('[data-test="rename-input"]')
|
|
28
|
+
.or(page.locator('[data-test="editable-input"]'))
|
|
29
|
+
.first();
|
|
30
|
+
}
|
|
31
|
+
exports.hubRenameInput = hubRenameInput;
|
|
32
|
+
function breadcrumbActionsButton(page) {
|
|
33
|
+
return hubBreadcrumbs(page)
|
|
34
|
+
.getByRole('button', { name: / actions$/ })
|
|
35
|
+
.last();
|
|
36
|
+
}
|
|
37
|
+
// `currentName` is the server default the resource still carries (e.g.
|
|
38
|
+
// "New form", "New BPMN diagram"); it is what the gate matches on.
|
|
39
|
+
async function renameHubResource(page, currentName, newName) {
|
|
40
|
+
const breadcrumbs = hubBreadcrumbs(page);
|
|
41
|
+
const trigger = breadcrumbActionsButton(page);
|
|
42
|
+
const renameInput = page.locator('[data-test="rename-input"]');
|
|
43
|
+
const renameSubmit = page.locator('[data-test="rename-submit"]');
|
|
44
|
+
const hasBreadcrumbRename = await trigger
|
|
45
|
+
.waitFor({ state: 'visible', timeout: 30000 })
|
|
46
|
+
.then(() => true)
|
|
47
|
+
.catch(() => false);
|
|
48
|
+
if (!hasBreadcrumbRename) {
|
|
49
|
+
// A shell that still renames through the inline editable title: fill it
|
|
50
|
+
// directly, the way this flow always used to.
|
|
51
|
+
const inline = hubRenameInput(page);
|
|
52
|
+
await (0, test_1.expect)(inline).toBeVisible({ timeout: 60000 });
|
|
53
|
+
await inline.fill(newName, { timeout: 60000 });
|
|
54
|
+
await inline.press('Enter');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
await (0, test_1.expect)(trigger).toHaveAccessibleName(`${currentName} actions`, {
|
|
58
|
+
timeout: 30000,
|
|
59
|
+
});
|
|
60
|
+
await (0, test_1.expect)(async () => {
|
|
61
|
+
if (await breadcrumbs.getByText(newName).isVisible())
|
|
62
|
+
return;
|
|
63
|
+
await trigger.click({ timeout: 15000 });
|
|
64
|
+
await page.getByRole('menuitem', { name: 'Rename' }).click({ timeout: 15000 });
|
|
65
|
+
await (0, test_1.expect)(renameInput).toBeVisible({ timeout: 15000 });
|
|
66
|
+
await renameInput.fill(newName);
|
|
67
|
+
await (0, test_1.expect)(renameInput).toHaveValue(newName, { timeout: 15000 });
|
|
68
|
+
await renameSubmit.click({ timeout: 15000 });
|
|
69
|
+
await (0, test_1.expect)(breadcrumbs.getByText(newName)).toBeVisible({
|
|
70
|
+
timeout: 15000,
|
|
71
|
+
});
|
|
72
|
+
}).toPass({ timeout: 120000 });
|
|
73
|
+
}
|
|
74
|
+
exports.renameHubResource = renameHubResource;
|