diff --git a/tests/e2e/pageobjects/dashboard/Dashboard.ts b/tests/e2e/pageobjects/dashboard/Dashboard.ts index 7f677b3a391..5df49f25add 100644 --- a/tests/e2e/pageobjects/dashboard/Dashboard.ts +++ b/tests/e2e/pageobjects/dashboard/Dashboard.ts @@ -44,6 +44,7 @@ export class Dashboard { }; private static readonly CONTINUE_WITH_DEFAULT_DEVFILE_BUTTON: By = By.xpath('//button[text()="Continue with default devfile"]'); private static readonly OPEN_EXISTING_WORKSPACE_LINK: By = By.xpath('//button[text()="Open the existing workspace"]'); + private static readonly CHOOSE_EDITOR_MENU: By = By.xpath('//*[@id="accordion-item-selector"]'); constructor( @inject(CLASSES.DriverHelper) @@ -57,7 +58,6 @@ export class Dashboard { await this.clickWorkspacesButton(); await this.workspaces.waitPage(); await this.workspaces.waitWorkspaceListItem(workspaceName); - await this.workspaces.waitWorkspaceWithRunningStatus(workspaceName); await this.workspaces.stopWorkspaceByActionsButton(workspaceName); await this.workspaces.waitWorkspaceWithStoppedStatus(workspaceName); } @@ -226,6 +226,18 @@ export class Dashboard { await this.driverHelper.waitAndClick(Dashboard.CONTINUE_WITH_DEFAULT_DEVFILE_BUTTON, timeout); } + async openChooseEditorMenu(timeout: number = TIMEOUT_CONSTANTS.TS_CLICK_DASHBOARD_ITEM_TIMEOUT): Promise { + Logger.debug('open "choose Editor" menu'); + + await this.driverHelper.waitAndClick(Dashboard.CHOOSE_EDITOR_MENU, timeout); + } + + async chooseEditor(editor: string, timeout: number = TIMEOUT_CONSTANTS.TS_CLICK_DASHBOARD_ITEM_TIMEOUT): Promise { + Logger.debug('select Editor. Editor: ' + editor); + + await this.driverHelper.waitAndClick(By.xpath(editor), timeout); + } + private getAboutMenuItemButtonLocator(text: string): By { return By.xpath(`//li/button[text()="${text}"]`); } diff --git a/tests/e2e/specs/dashboard-samples/StartWorkspaceUsingVSCodeDesktopSshEditor.spec.ts b/tests/e2e/specs/dashboard-samples/StartWorkspaceUsingVSCodeDesktopSshEditor.spec.ts new file mode 100644 index 00000000000..7ec4ce2e40a --- /dev/null +++ b/tests/e2e/specs/dashboard-samples/StartWorkspaceUsingVSCodeDesktopSshEditor.spec.ts @@ -0,0 +1,144 @@ +/** ******************************************************************* + * copyright (c) 2026 Red Hat, Inc. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + **********************************************************************/ + +import { KubernetesCommandLineToolsExecutor } from '../../utils/KubernetesCommandLineToolsExecutor'; +import fs from 'fs'; +import path from 'path'; +import YAML from 'yaml'; +import { e2eContainer } from '../../configs/inversify.config'; +import { CLASSES } from '../../configs/inversify.types'; +import { LoginTests } from '../../tests-library/LoginTests'; +import { Dashboard } from '../../pageobjects/dashboard/Dashboard'; +import { BrowserTabsUtil } from '../../utils/BrowserTabsUtil'; +import { WorkspaceHandlingTests } from '../../tests-library/WorkspaceHandlingTests'; +import { expect } from 'chai'; +import { Logger } from '../../utils/Logger'; +import { BASE_TEST_CONSTANTS } from '../../constants/BASE_TEST_CONSTANTS'; + +suite('Check Visual Studio Code (desktop) (SSH) with all samples', function (): void { + this.timeout(6000000); + const workspaceHandlingTests: WorkspaceHandlingTests = e2eContainer.get(CLASSES.WorkspaceHandlingTests); + const pathToSampleFile: string = path.resolve('resources/default-devfile.yaml'); + const workspaceName: string = YAML.parse(fs.readFileSync(pathToSampleFile, 'utf8')).metadata.name; + const kubernetesCommandLineToolsExecutor: KubernetesCommandLineToolsExecutor = e2eContainer.get( + CLASSES.KubernetesCommandLineToolsExecutor + ); + kubernetesCommandLineToolsExecutor.workspaceName = workspaceName; + const loginTests: LoginTests = e2eContainer.get(CLASSES.LoginTests); + const dashboard: Dashboard = e2eContainer.get(CLASSES.Dashboard); + const browserTabsUtil: BrowserTabsUtil = e2eContainer.get(CLASSES.BrowserTabsUtil); + + const vsCodeDesktopSshEditor: string = '//*[@id="editor-selector-card-che-incubator/che-code-sshd/latest"]'; + const titlexPath: string = '/html/body/h1'; + const ocPortForwardxPath: string = '//*[@id="port-forward"]'; + const sshKeyxPath: string = '//*[@id="key"]'; + const sshKonfigxPath: string = '//*[@id="config"]'; + + const samplesForCheck: string[] = [ + 'Empty Workspace', + 'JBoss EAP 8.0', + 'Java Lombok', + 'Node.js Express', + 'Python', + 'Quarkus REST API', + '.NET', + 'Ansible', + 'C/C++', + 'Go', + 'PHP' + ]; + + const gitRepoUrlsToCheck: string[] = [ + 'https://github.com/crw-qe/quarkus-api-example-public/tree/ubi8-latest', + 'https://github.com/crw-qe/ubi9-based-sample-public/tree/ubi9-minimal' + ]; + + const gitRepoUrlsToCheckAirgap: string[] = [ + 'https://gh.crw-qe.com/test-automation-only/ubi8/tree/ubi8-latest', + 'https://gh.crw-qe.com/test-automation-only/ubi9-based-sample-public/tree/ubi9-minimal' + ]; + + async function deleteWorkspace(): Promise { + await dashboard.openDashboard(); + await browserTabsUtil.closeAllTabsExceptCurrent(); + await dashboard.stopAndRemoveWorkspaceByUI(WorkspaceHandlingTests.getWorkspaceName()); + } + + suiteSetup('Login into Che', async function (): Promise { + await loginTests.loginIntoChe(); + }); + + async function testWorkspaceStartup(sampleNameOrUrl: string, isUrl: boolean): Promise { + await dashboard.openDashboard(); + if (isUrl) { + await workspaceHandlingTests.createAndOpenWorkspaceWithSpecificEditorAndGitUrl( + vsCodeDesktopSshEditor, + sampleNameOrUrl, + titlexPath + ); + } else { + await workspaceHandlingTests.createAndOpenWorkspaceWithSpecificEditorAndSample( + vsCodeDesktopSshEditor, + sampleNameOrUrl, + titlexPath + ); + } + + // check title + const headerText: string = await workspaceHandlingTests.getTextFromUIElementByXpath(titlexPath); + expect('Workspace ' + WorkspaceHandlingTests.getWorkspaceName() + ' is running').equal(headerText); + // check oc-port-forwarding + const ocPortForward: string = await workspaceHandlingTests.getTextFromUIElementByXpath(ocPortForwardxPath); + expect(ocPortForward).contains('oc port-forward -n admin-devspaces'); + // check ssh key + const sshKey: string = await workspaceHandlingTests.getTextFromUIElementByXpath(sshKeyxPath); + expect(sshKey).contains('-----BEGIN OPENSSH PRIVATE KEY-----').and.contains('-----END OPENSSH PRIVATE KEY-----'); + // check .ssh/kofig + const sshKonfig: string = await workspaceHandlingTests.getTextFromUIElementByXpath(sshKonfigxPath); + expect(sshKonfig) + .contains('HostName') + .and.contains('User') + .and.contains('Port') + .and.contains('IdentityFile') + .and.contains('UserKnownHostsFile'); + + await deleteWorkspace(); + } + + test('Test start of VSCode (desktop) (SSH) with default Samples', async function (): Promise { + for (const sampleName of samplesForCheck) { + await testWorkspaceStartup(sampleName, false); + } + }); + + test('Test start of VSCode (desktop) (SSH) with ubi', async function (): Promise { + if (BASE_TEST_CONSTANTS.IS_CLUSTER_DISCONNECTED()) { + Logger.info('Test cluster is disconnected. Using url for airgap cluster.'); + for (const url of gitRepoUrlsToCheckAirgap) { + await testWorkspaceStartup(url, true); + } + } else { + for (const url of gitRepoUrlsToCheck) { + await testWorkspaceStartup(url, true); + } + } + }); + + suiteTeardown('Delete DevWorkspace', async function (): Promise { + Logger.info('Deleting DevWorkspace... After all.'); + await dashboard.openDashboard(); + await browserTabsUtil.closeAllTabsExceptCurrent(); + try { + await dashboard.deleteStoppedWorkspaceByUI(WorkspaceHandlingTests.getWorkspaceName()); + } catch (e) { + Logger.info('Cannot find or stop DevWorkspace in suiteTeardown. Normal behaviour if test passed.'); + } + }); +}); diff --git a/tests/e2e/tests-library/WorkspaceHandlingTests.ts b/tests/e2e/tests-library/WorkspaceHandlingTests.ts index 0c1b6333c35..4e884d44709 100644 --- a/tests/e2e/tests-library/WorkspaceHandlingTests.ts +++ b/tests/e2e/tests-library/WorkspaceHandlingTests.ts @@ -150,6 +150,38 @@ export class WorkspaceHandlingTests { Logger.info('Start workspace progress description: ' + alertDescription); } + async createAndOpenWorkspaceWithSpecificEditorAndSample(editor: string, sampleName: string, xPath: string): Promise { + Logger.debug('Create and open workspace with specific Editor and Sample. Sample ' + editor); + await this.selectEditor(editor); + await this.createWorkspace.clickOnSampleNoEditorSelection(sampleName); + await this.waitForControlXpath(xPath); + } + + async createAndOpenWorkspaceWithSpecificEditorAndGitUrl(editor: string, sampleUrl: string, xPath: string): Promise { + Logger.debug('Create and open workspace with specific Editor and URL. Sample ' + editor); + await this.selectEditor(editor); + await this.createWorkspace.importFromGitUsingUI(sampleUrl); + await this.waitForControlXpath(xPath); + } + + async selectEditor(editor: string): Promise { + Logger.debug('select Editor. Editor: ' + editor); + await this.dashboard.openChooseEditorMenu(); + await this.dashboard.chooseEditor(editor); + } + + async getTextFromUIElementByXpath(xpath: string): Promise { + Logger.debug('returning text from xPath: ' + xpath); + return await this.driverHelper.getDriver().findElement(By.xpath(xpath)).getText(); + } + + private async waitForControlXpath(xPathToWait: string): Promise { + await this.browserTabsUtil.waitAndSwitchToAnotherWindow(WorkspaceHandlingTests.parentGUID, TIMEOUT_CONSTANTS.TS_IDE_LOAD_TIMEOUT); + await this.obtainWorkspaceNameFromStartingPage(); + + await this.driverHelper.waitVisibility(By.xpath(xPathToWait), TIMEOUT_CONSTANTS.TS_SELENIUM_START_WORKSPACE_TIMEOUT); + } + private async getWorkspaceAlertDescription(): Promise { try { return await this.driverHelper.getDriver().findElement(WorkspaceHandlingTests.WORKSPACE_ALERT_DESCRIPTION).getText();