Skip to content

Commit cf08ff6

Browse files
authored
[Test] Automate rename workspace test (#23838)
1 parent 01c12d7 commit cf08ff6

5 files changed

Lines changed: 219 additions & 1 deletion

File tree

tests/e2e/pageobjects/dashboard/workspace-details/WorkspaceDetails.ts

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ export class WorkspaceDetails {
2929
private static readonly CLOSE_STORAGE_TYPE_INFO_BUTTON: By = By.xpath('//button[@aria-label="Close"]');
3030
private static readonly STORAGE_TYPE_DOC_LINK: By = By.xpath('//div/p/a');
3131
private static readonly DEVFILE_DOC_LINK: By = By.xpath('//a[text()="Devfile Documentation"]');
32+
private static readonly RENAME_WORKSPACE_BUTTON: By = By.xpath('//button[@title="Edit Workspace Name"]');
33+
private static readonly RENAME_WORKSPACE_INPUT: By = By.id('edit-workspace-name');
34+
private static readonly RENAME_SAVE_BUTTON: By = By.xpath('//button[@data-testid="edit-workspace-name-save"]');
35+
private static readonly RENAME_CANCEL_BUTTON: By = By.xpath('//button[@data-testid="edit-workspace-name-cancel"]');
3236

3337
constructor(
3438
@inject(CLASSES.DriverHelper)
@@ -144,6 +148,50 @@ export class WorkspaceDetails {
144148
return await this.driverHelper.waitAndGetElementAttribute(WorkspaceDetails.DEVFILE_DOC_LINK, 'href');
145149
}
146150

151+
/**
152+
* devSpaces Dashboard does not allow editing the workspace display name while the workspace is running.
153+
*/
154+
async checkRenameButtonIsAbsent(): Promise<void> {
155+
Logger.debug();
156+
157+
await this.driverHelper.waitDisappearance(WorkspaceDetails.RENAME_WORKSPACE_BUTTON);
158+
}
159+
160+
async openRenameWorkspaceForm(): Promise<void> {
161+
Logger.debug();
162+
await this.driverHelper.waitAndClick(WorkspaceDetails.RENAME_WORKSPACE_BUTTON, TIMEOUT_CONSTANTS.TS_SELENIUM_LOAD_PAGE_TIMEOUT);
163+
}
164+
165+
async typeWorkspaceName(name: string): Promise<void> {
166+
Logger.debug(`name: "${name}"`);
167+
await this.driverHelper.type(WorkspaceDetails.RENAME_WORKSPACE_INPUT, name);
168+
}
169+
170+
async waitSaveButtonIsDisabled(): Promise<void> {
171+
Logger.debug();
172+
await this.driverHelper.waitAttributePresent(
173+
WorkspaceDetails.RENAME_SAVE_BUTTON,
174+
'disabled',
175+
TIMEOUT_CONSTANTS.TS_COMMON_DASHBOARD_WAIT_TIMEOUT
176+
);
177+
}
178+
179+
async cancelRenameWorkspace(): Promise<void> {
180+
Logger.debug();
181+
await this.driverHelper.waitAndClick(WorkspaceDetails.RENAME_CANCEL_BUTTON, TIMEOUT_CONSTANTS.TS_SELENIUM_LOAD_PAGE_TIMEOUT);
182+
}
183+
184+
/**
185+
* rename a workspace from the Overview tab (fill name + save).
186+
*/
187+
async renameWorkspace(newDisplayName: string): Promise<void> {
188+
Logger.debug(`newDisplayName: "${newDisplayName}"`);
189+
await this.openRenameWorkspaceForm();
190+
await this.typeWorkspaceName(newDisplayName);
191+
await this.driverHelper.waitAndClick(WorkspaceDetails.RENAME_SAVE_BUTTON);
192+
await this.waitWorkspaceTitle(newDisplayName);
193+
}
194+
147195
private getWorkspaceTitleLocator(workspaceName: string): By {
148196
return By.xpath(`//h1[text()='${workspaceName}']`);
149197
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/** *******************************************************************
2+
* copyright (c) 2026 Red Hat, Inc.
3+
*
4+
* This program and the accompanying materials are made
5+
* available under the terms of the Eclipse Public License 2.0
6+
* which is available at https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
**********************************************************************/
10+
import { e2eContainer } from '../../configs/inversify.config';
11+
import { CLASSES } from '../../configs/inversify.types';
12+
import { expect } from 'chai';
13+
import { WorkspaceHandlingTests } from '../../tests-library/WorkspaceHandlingTests';
14+
import { ProjectAndFileTests } from '../../tests-library/ProjectAndFileTests';
15+
import { LoginTests } from '../../tests-library/LoginTests';
16+
import { registerRunningWorkspace } from '../MochaHooks';
17+
import { BrowserTabsUtil } from '../../utils/BrowserTabsUtil';
18+
import { BASE_TEST_CONSTANTS } from '../../constants/BASE_TEST_CONSTANTS';
19+
import { Dashboard } from '../../pageobjects/dashboard/Dashboard';
20+
import { Workspaces } from '../../pageobjects/dashboard/Workspaces';
21+
import { WorkspaceDetails } from '../../pageobjects/dashboard/workspace-details/WorkspaceDetails';
22+
import { TIMEOUT_CONSTANTS } from '../../constants/TIMEOUT_CONSTANTS';
23+
24+
const stackName: string = BASE_TEST_CONSTANTS.TS_SELENIUM_DASHBOARD_SAMPLE_NAME || 'Empty Workspace';
25+
const RENAMED_WORKSPACE_NAME: string = 'new-ws';
26+
27+
suite(`Rename workspace ${BASE_TEST_CONSTANTS.TEST_ENVIRONMENT}`, function (): void {
28+
const workspaceHandlingTests: WorkspaceHandlingTests = e2eContainer.get(CLASSES.WorkspaceHandlingTests);
29+
const projectAndFileTests: ProjectAndFileTests = e2eContainer.get(CLASSES.ProjectAndFileTests);
30+
const loginTests: LoginTests = e2eContainer.get(CLASSES.LoginTests);
31+
const browserTabsUtil: BrowserTabsUtil = e2eContainer.get(CLASSES.BrowserTabsUtil);
32+
const dashboard: Dashboard = e2eContainer.get(CLASSES.Dashboard);
33+
const workspaces: Workspaces = e2eContainer.get(CLASSES.Workspaces);
34+
const workspaceDetails: WorkspaceDetails = e2eContainer.get(CLASSES.WorkspaceDetails);
35+
36+
let firstWorkspaceName: string = '';
37+
let secondWorkspaceName: string = '';
38+
39+
async function openDashboardWorkspacesList(): Promise<void> {
40+
await dashboard.openDashboard();
41+
await dashboard.clickWorkspacesButton();
42+
await workspaces.waitPage();
43+
}
44+
45+
async function openWorkspaceDetailsOverview(workspaceName: string): Promise<void> {
46+
await openDashboardWorkspacesList();
47+
await workspaces.clickWorkspaceListItemLink(workspaceName);
48+
await workspaceDetails.waitWorkspaceTitle(workspaceName);
49+
await workspaceDetails.waitLoaderDisappearance();
50+
}
51+
52+
suiteSetup('Login', async function (): Promise<void> {
53+
await loginTests.loginIntoChe();
54+
});
55+
56+
test(`Create workspace from sample (${stackName})`, async function (): Promise<void> {
57+
await workspaceHandlingTests.createAndOpenWorkspace(stackName);
58+
await workspaceHandlingTests.obtainWorkspaceNameFromStartingPage();
59+
firstWorkspaceName = WorkspaceHandlingTests.getWorkspaceName();
60+
registerRunningWorkspace(firstWorkspaceName);
61+
62+
await projectAndFileTests.waitWorkspaceReadinessForCheCodeEditor();
63+
});
64+
65+
test('Workspace details: rename must not be available while the workspace is running', async function (): Promise<void> {
66+
await openWorkspaceDetailsOverview(firstWorkspaceName);
67+
await workspaceDetails.checkRenameButtonIsAbsent();
68+
});
69+
70+
test('Stop the first workspace from the dashboard', async function (): Promise<void> {
71+
await workspaceHandlingTests.stopWorkspace(firstWorkspaceName);
72+
await browserTabsUtil.closeAllTabsExceptCurrent();
73+
});
74+
75+
test(`Rename stopped workspace to "${RENAMED_WORKSPACE_NAME}" from workspace details`, async function (): Promise<void> {
76+
await openWorkspaceDetailsOverview(firstWorkspaceName);
77+
await workspaceDetails.renameWorkspace(RENAMED_WORKSPACE_NAME);
78+
firstWorkspaceName = RENAMED_WORKSPACE_NAME;
79+
});
80+
81+
test(`Dashboard lists and details show "${RENAMED_WORKSPACE_NAME}"`, async function (): Promise<void> {
82+
await openDashboardWorkspacesList();
83+
await workspaces.waitWorkspaceListItem(RENAMED_WORKSPACE_NAME, TIMEOUT_CONSTANTS.TS_SELENIUM_LOAD_PAGE_TIMEOUT);
84+
await workspaces.clickWorkspaceListItemLink(RENAMED_WORKSPACE_NAME);
85+
await workspaceDetails.waitWorkspaceTitle(RENAMED_WORKSPACE_NAME);
86+
});
87+
88+
test(`Start "${RENAMED_WORKSPACE_NAME}" and wait until it is Running`, async function (): Promise<void> {
89+
await openDashboardWorkspacesList();
90+
91+
await workspaceHandlingTests.openWorkspace(RENAMED_WORKSPACE_NAME);
92+
await workspaceHandlingTests.obtainWorkspaceNameFromStartingPage();
93+
const startedWorkspaceName: string = WorkspaceHandlingTests.getWorkspaceName();
94+
expect(WorkspaceHandlingTests.getWorkspaceName()).equal(RENAMED_WORKSPACE_NAME);
95+
registerRunningWorkspace(startedWorkspaceName);
96+
await projectAndFileTests.waitWorkspaceReadinessForCheCodeEditor();
97+
});
98+
99+
test(`Stop "${RENAMED_WORKSPACE_NAME}" again`, async function (): Promise<void> {
100+
await workspaceHandlingTests.stopWorkspace(RENAMED_WORKSPACE_NAME);
101+
await browserTabsUtil.closeAllTabsExceptCurrent();
102+
});
103+
104+
test(`Workspace details: setting name to "${RENAMED_WORKSPACE_NAME}" when it is already the current name is rejected`, async function (): Promise<void> {
105+
await openWorkspaceDetailsOverview(RENAMED_WORKSPACE_NAME);
106+
await workspaceDetails.openRenameWorkspaceForm();
107+
await workspaceDetails.typeWorkspaceName(RENAMED_WORKSPACE_NAME);
108+
await workspaceDetails.waitSaveButtonIsDisabled();
109+
await workspaceDetails.cancelRenameWorkspace();
110+
});
111+
112+
test(`Create a second workspace from sample (${stackName})`, async function (): Promise<void> {
113+
await workspaceHandlingTests.createAndOpenWorkspace(stackName);
114+
await workspaceHandlingTests.obtainWorkspaceNameFromStartingPage();
115+
secondWorkspaceName = WorkspaceHandlingTests.getWorkspaceName();
116+
registerRunningWorkspace(secondWorkspaceName);
117+
await projectAndFileTests.waitWorkspaceReadinessForCheCodeEditor();
118+
});
119+
120+
test(`Second workspace details: rename to "${RENAMED_WORKSPACE_NAME}" shows conflict and does not apply`, async function (): Promise<void> {
121+
await workspaceHandlingTests.stopWorkspace(secondWorkspaceName);
122+
await browserTabsUtil.closeAllTabsExceptCurrent();
123+
124+
await openWorkspaceDetailsOverview(secondWorkspaceName);
125+
await workspaceDetails.openRenameWorkspaceForm();
126+
await workspaceDetails.typeWorkspaceName(RENAMED_WORKSPACE_NAME);
127+
await workspaceDetails.waitSaveButtonIsDisabled();
128+
await workspaceDetails.cancelRenameWorkspace();
129+
});
130+
131+
suiteTeardown('Stop and delete workspaces created in this suite', async function (): Promise<void> {
132+
await openDashboardWorkspacesList();
133+
134+
await dashboard.deleteStoppedWorkspaceByUI(secondWorkspaceName);
135+
await dashboard.deleteStoppedWorkspaceByUI(RENAMED_WORKSPACE_NAME);
136+
});
137+
138+
suiteTeardown('Unregister running workspace', function (): void {
139+
registerRunningWorkspace('');
140+
});
141+
});

tests/e2e/suites/online-ocp/UITest.suite.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ import '../../specs/dashboard-samples/Documentation.spec';
1515
import '../../specs/devconsole-intergration/DevConsoleIntegration.spec';
1616
import '../../specs/miscellaneous/CreateWorkspaceWithExistingNameFromGitUrl.spec';
1717
import '../../specs/miscellaneous/KubedockPodmanTest.spec';
18+
import '../../specs/miscellaneous/RenameWorkspace.spec';
1819
import '../../specs/miscellaneous/WorkspaceWithParent.spec';
1920
import '../../specs/miscellaneous/PredefinedNamespace.spec';

tests/e2e/tests-library/WorkspaceHandlingTests.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { ApiUrlResolver } from '../utils/workspace/ApiUrlResolver';
1919
import { TIMEOUT_CONSTANTS } from '../constants/TIMEOUT_CONSTANTS';
2020
import { DriverHelper } from '../utils/DriverHelper';
2121
import { By, error } from 'selenium-webdriver';
22+
import { Workspaces } from '../pageobjects/dashboard/Workspaces';
2223

2324
@injectable()
2425
export class WorkspaceHandlingTests {
@@ -38,7 +39,9 @@ export class WorkspaceHandlingTests {
3839
@inject(CLASSES.ApiUrlResolver)
3940
private readonly apiUrlResolver: ApiUrlResolver,
4041
@inject(CLASSES.DriverHelper)
41-
private readonly driverHelper: DriverHelper
42+
private readonly driverHelper: DriverHelper,
43+
@inject(CLASSES.Workspaces)
44+
private readonly workspaces: Workspaces
4245
) {}
4346

4447
static getWorkspaceName(): string {
@@ -62,6 +65,13 @@ export class WorkspaceHandlingTests {
6265
await this.browserTabsUtil.waitAndSwitchToAnotherWindow(WorkspaceHandlingTests.parentGUID, TIMEOUT_CONSTANTS.TS_IDE_LOAD_TIMEOUT);
6366
}
6467

68+
async openWorkspace(workspaceName: string): Promise<void> {
69+
await this.workspaces.clickOpenButton(workspaceName);
70+
await this.apiUrlResolver.getWorkspacesApiUrl();
71+
WorkspaceHandlingTests.parentGUID = await this.browserTabsUtil.getCurrentWindowHandle();
72+
await this.browserTabsUtil.waitAndSwitchToAnotherWindow(WorkspaceHandlingTests.parentGUID, TIMEOUT_CONSTANTS.TS_IDE_LOAD_TIMEOUT);
73+
}
74+
6575
async createAndOpenWorkspaceFromGitRepository(
6676
factoryUrl: string,
6777
branchName?: string,

tests/e2e/utils/DriverHelper.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,24 @@ export class DriverHelper {
419419
);
420420
}
421421

422+
/**
423+
* waits until the attribute is present on the element (e.g. boolean HTML `disabled`),
424+
* without requiring a specific attribute value.
425+
*/
426+
async waitAttributePresent(elementLocator: By, attribute: string, timeout: number): Promise<void> {
427+
Logger.trace(`${elementLocator}`);
428+
429+
await this.driver.wait(
430+
async (): Promise<boolean> => {
431+
const attributeValue: string | null = await this.waitAndGetElementAttribute(elementLocator, attribute, timeout);
432+
433+
return attributeValue != null;
434+
},
435+
timeout,
436+
`The '${attribute}' attribute is not present on '${elementLocator}'`
437+
);
438+
}
439+
422440
async type(elementLocator: By, text: string, timeout: number = TIMEOUT_CONSTANTS.TS_SELENIUM_CLICK_ON_VISIBLE_ITEM): Promise<void> {
423441
const polling: number = TIMEOUT_CONSTANTS.TS_SELENIUM_DEFAULT_POLLING;
424442
const attempts: number = Math.ceil(timeout / polling);

0 commit comments

Comments
 (0)