From 411ceda62ea0e5de3847a558295fb2fea5205d8c Mon Sep 17 00:00:00 2001 From: Oleksii Korniienko Date: Wed, 30 Sep 2026 15:15:57 +0200 Subject: [PATCH 1/5] Added AutomaticRestartFromLocalDevfile test Assisted-by: Claude Code Signed-off-by: Oleksii Korniienko --- .../AutomaticRestartFromLocalDevfile.spec.ts | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts diff --git a/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts b/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts new file mode 100644 index 00000000000..4699275fb32 --- /dev/null +++ b/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts @@ -0,0 +1,167 @@ +/** ******************************************************************* + * 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 { expect } from 'chai'; +import { e2eContainer } from '../../configs/inversify.config'; +import { CLASSES } from '../../configs/inversify.types'; +import { BASE_TEST_CONSTANTS } from '../../constants/BASE_TEST_CONSTANTS'; +import { FACTORY_TEST_CONSTANTS } from '../../constants/FACTORY_TEST_CONSTANTS'; +import { TIMEOUT_CONSTANTS } from '../../constants/TIMEOUT_CONSTANTS'; +import { LoginTests } from '../../tests-library/LoginTests'; +import { WorkspaceHandlingTests } from '../../tests-library/WorkspaceHandlingTests'; +import { Dashboard } from '../../pageobjects/dashboard/Dashboard'; +import { CreateWorkspace } from '../../pageobjects/dashboard/CreateWorkspace'; +import { UserPreferences } from '../../pageobjects/dashboard/UserPreferences'; +import { Workspaces } from '../../pageobjects/dashboard/Workspaces'; +import { WorkspaceDetails } from '../../pageobjects/dashboard/workspace-details/WorkspaceDetails'; +import { BrowserTabsUtil } from '../../utils/BrowserTabsUtil'; +import { DriverHelper } from '../../utils/DriverHelper'; +import { Logger } from '../../utils/Logger'; +import { StringUtil } from '../../utils/StringUtil'; +import { ViewSection } from 'monaco-page-objects'; +import { By } from 'selenium-webdriver'; +import { ProjectAndFileTests } from '../../tests-library/ProjectAndFileTests'; +import { registerRunningWorkspace } from '../MochaHooks'; + +suite(`Create workspace from private SSH repo with default devfile ${BASE_TEST_CONSTANTS.TEST_ENVIRONMENT}`, function (): void { + const loginTests: LoginTests = e2eContainer.get(CLASSES.LoginTests); + const workspaceHandlingTests: WorkspaceHandlingTests = e2eContainer.get(CLASSES.WorkspaceHandlingTests); + const dashboard: Dashboard = e2eContainer.get(CLASSES.Dashboard); + const createWorkspace: CreateWorkspace = e2eContainer.get(CLASSES.CreateWorkspace); + const workspaces: Workspaces = e2eContainer.get(CLASSES.Workspaces); + const workspaceDetails: WorkspaceDetails = e2eContainer.get(CLASSES.WorkspaceDetails); + const browserTabsUtil: BrowserTabsUtil = e2eContainer.get(CLASSES.BrowserTabsUtil); + const userPreferences: UserPreferences = e2eContainer.get(CLASSES.UserPreferences); + const projectAndFileTests: ProjectAndFileTests = e2eContainer.get(CLASSES.ProjectAndFileTests); + const driverHelper: DriverHelper = e2eContainer.get(CLASSES.DriverHelper); + const gitSshUrl: string = process.env.TS_SELENIUM_GIT_SSH_REPO_URL || ''; + const privateSshKey: string = FACTORY_TEST_CONSTANTS.TS_SELENIUM_SSH_PRIVATE_KEY; + const publicSshKey: string = FACTORY_TEST_CONSTANTS.TS_SELENIUM_SSH_PUBLIC_KEY; + const editorXpath: string = '//*[@id="editor-selector-card-che-incubator/che-code/latest"]'; + const xPathWorkspacePart: string = '//*[@id="workbench.parts.sidebar"]'; + let currentTabHandle: string = 'undefined'; + + suiteSetup('Login into Che', async function (): Promise { + if (BASE_TEST_CONSTANTS.IS_CLUSTER_DISCONNECTED()) { + Logger.info('Test cluster is disconnected. Skipping private SSH repo suite.'); + this.skip(); + } + + await loginTests.loginIntoChe(); + }); + + function clearCurrentTabHandle(): void { + currentTabHandle = 'undefined'; + } + + async function deleteSshKeysIfExists(): Promise { + await dashboard.openDashboard(); + await userPreferences.openUserPreferencesPage(); + await userPreferences.openSshKeyTab(); + if (await userPreferences.isSshKeyPresent()) { + await userPreferences.deleteSshKeys(); + } + } + + test('Add SSH key', async function (): Promise { + await deleteSshKeysIfExists(); + await userPreferences.addSshKeysFromStrings(privateSshKey, publicSshKey); + }); + + test('Create workspace using SSH URL of private repo', async function (): Promise { + await dashboard.openDashboard(); + currentTabHandle = await browserTabsUtil.getCurrentWindowHandle(); + await dashboard.clickCreateWorkspaceButton(); + await createWorkspace.waitPage(); + await dashboard.openChooseEditorMenu(); + await dashboard.chooseEditor(editorXpath); + await createWorkspace.importFromGitUsingUI(gitSshUrl); + await browserTabsUtil.waitAndSwitchToAnotherWindow(currentTabHandle, TIMEOUT_CONSTANTS.TS_IDE_LOAD_TIMEOUT); + await workspaceHandlingTests.obtainWorkspaceNameFromStartingPage(); + registerRunningWorkspace(WorkspaceHandlingTests.getWorkspaceName()); + }); + + test('Verify "Failed to fetch devfile" warning appears', async function (): Promise { + Logger.info('Verifying warning about failed devfile fetch'); + const alertText: string = await dashboard.getLoaderAlert(); + Logger.info(`Loader alert text: ${alertText}`); + expect(alertText).to.include('Failed to fetch devfile'); + }); + + test('Wait for workspace to start successfully without restart dialog', async function (): Promise { + Logger.info('Waiting for IDE to load — workspace should start with default devfile, no restart dialog expected'); + + const isRestartDialogVisible: boolean = await driverHelper.isVisible( + By.xpath('//span[text()="Restart with default devfile"]') + ); + expect(isRestartDialogVisible, 'Restart dialog should not appear for workspace started with default devfile').to.be.false; + }); + + test('Verify no errors or alerts after workspace start', async function (): Promise { + Logger.info('Verifying no error dialogs or alerts are present after workspace start'); + await driverHelper.waitVisibility(By.xpath(xPathWorkspacePart), TIMEOUT_CONSTANTS.TS_IDE_START_TIMEOUT); + + const isErrorDialogVisible: boolean = await driverHelper.isVisible( + By.xpath('//*[@class="dialog-message-text"]') + ); + expect(isErrorDialogVisible, 'Error dialog should not be present').to.be.false; + + const isAlertVisible: boolean = await driverHelper.isVisible(By.css('h4[class*="alert__title"]')); + expect(isAlertVisible, 'Workspace alert should not be present').to.be.false; + }); + + test('Verify correct project is present in Explorer', async function (): Promise { + const projectName: string = StringUtil.getProjectNameFromGitUrl(gitSshUrl); + await projectAndFileTests.waitWorkspaceReadinessForCheCodeEditor(); + const projectSection: ViewSection = await projectAndFileTests.getProjectViewSession(); + expect(await projectAndFileTests.getProjectTreeItem(projectSection, projectName), 'Project folder was not imported').not + .undefined; + }); + + test('Navigate to workspace Devfile tab and verify devfile content is not available', async function (): Promise { + const workspaceName: string = WorkspaceHandlingTests.getWorkspaceName(); + + Logger.info('Opening Dashboard and navigating to Workspaces page'); + await dashboard.openDashboard(); + await dashboard.clickWorkspacesButton(); + await workspaces.waitPage(); + + Logger.info(`Opening workspace details for: ${workspaceName}`); + await workspaces.clickWorkspaceListItemLink(workspaceName); + await workspaceDetails.waitWorkspaceTitle(workspaceName); + + Logger.info('Selecting Devfile tab'); + await workspaceDetails.selectTab('Devfile'); + + Logger.info('Verifying "Devfile content is not available" message is shown'); + const devfileUnavailableLocator: By = By.xpath('//*[contains(text(), "Devfile content is not available")]'); + await driverHelper.waitVisibility(devfileUnavailableLocator, TIMEOUT_CONSTANTS.TS_COMMON_DASHBOARD_WAIT_TIMEOUT); + }); + + suiteTeardown('Delete DevWorkspace', async function (): Promise { + Logger.debug('Delete DevWorkspace. After each test.'); + if (currentTabHandle !== 'undefined') { + await browserTabsUtil.switchToWindow(currentTabHandle); + } + + await dashboard.openDashboard(); + await browserTabsUtil.closeAllTabsExceptCurrent(); + + if (WorkspaceHandlingTests.getWorkspaceName() !== 'undefined') { + Logger.debug('Workspace name is defined. Deleting workspace...'); + await dashboard.deleteStoppedWorkspaceByUI(WorkspaceHandlingTests.getWorkspaceName()); + } + + WorkspaceHandlingTests.clearWorkspaceName(); + clearCurrentTabHandle(); + registerRunningWorkspace(''); + await deleteSshKeysIfExists(); + }); +}); From 1d91fc9f753ab0a3100e6b86f30cda1886f232e1 Mon Sep 17 00:00:00 2001 From: Oleksii Korniienko Date: Tue, 6 Oct 2026 17:02:39 +0200 Subject: [PATCH 2/5] Reworked 'Wait for workspace to start successfully without restart dialog' due to PR review Assisted-by: Claude Code Signed-off-by: Oleksii Korniienko --- .../AutomaticRestartFromLocalDevfile.spec.ts | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts b/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts index 4699275fb32..f5c9d8d707e 100644 --- a/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts +++ b/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts @@ -98,15 +98,25 @@ suite(`Create workspace from private SSH repo with default devfile ${BASE_TEST_C test('Wait for workspace to start successfully without restart dialog', async function (): Promise { Logger.info('Waiting for IDE to load — workspace should start with default devfile, no restart dialog expected'); - const isRestartDialogVisible: boolean = await driverHelper.isVisible( - By.xpath('//span[text()="Restart with default devfile"]') - ); - expect(isRestartDialogVisible, 'Restart dialog should not appear for workspace started with default devfile').to.be.false; + const restartDialogLocator: By = By.xpath('//span[text()="Restart with default devfile"]'); + const polling: number = TIMEOUT_CONSTANTS.TS_SELENIUM_DEFAULT_POLLING; + const attempts: number = Math.ceil(TIMEOUT_CONSTANTS.TS_IDE_START_TIMEOUT / polling); + + // verify the restart dialog never appears during the whole workspace start window + for (let i: number = 0; i < attempts; i++) { + const isRestartDialogVisible: boolean = await driverHelper.isVisible(restartDialogLocator); + expect(isRestartDialogVisible, 'Restart dialog should not appear for workspace started with default devfile').to.be + .false; + + await driverHelper.wait(polling); + } + + // wait for workspace to start + await driverHelper.waitVisibility(By.xpath(xPathWorkspacePart), TIMEOUT_CONSTANTS.TS_IDE_START_TIMEOUT); }); test('Verify no errors or alerts after workspace start', async function (): Promise { Logger.info('Verifying no error dialogs or alerts are present after workspace start'); - await driverHelper.waitVisibility(By.xpath(xPathWorkspacePart), TIMEOUT_CONSTANTS.TS_IDE_START_TIMEOUT); const isErrorDialogVisible: boolean = await driverHelper.isVisible( By.xpath('//*[@class="dialog-message-text"]') From 7c13d226098126c25d8ce5b4690fab70c036ab4b Mon Sep 17 00:00:00 2001 From: Oleksii Korniienko Date: Tue, 6 Oct 2026 17:07:26 +0200 Subject: [PATCH 3/5] Prettier fixes Signed-off-by: Oleksii Korniienko --- .../AutomaticRestartFromLocalDevfile.spec.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts b/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts index f5c9d8d707e..82a69ca6166 100644 --- a/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts +++ b/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts @@ -105,8 +105,7 @@ suite(`Create workspace from private SSH repo with default devfile ${BASE_TEST_C // verify the restart dialog never appears during the whole workspace start window for (let i: number = 0; i < attempts; i++) { const isRestartDialogVisible: boolean = await driverHelper.isVisible(restartDialogLocator); - expect(isRestartDialogVisible, 'Restart dialog should not appear for workspace started with default devfile').to.be - .false; + expect(isRestartDialogVisible, 'Restart dialog should not appear for workspace started with default devfile').to.be.false; await driverHelper.wait(polling); } @@ -118,9 +117,7 @@ suite(`Create workspace from private SSH repo with default devfile ${BASE_TEST_C test('Verify no errors or alerts after workspace start', async function (): Promise { Logger.info('Verifying no error dialogs or alerts are present after workspace start'); - const isErrorDialogVisible: boolean = await driverHelper.isVisible( - By.xpath('//*[@class="dialog-message-text"]') - ); + const isErrorDialogVisible: boolean = await driverHelper.isVisible(By.xpath('//*[@class="dialog-message-text"]')); expect(isErrorDialogVisible, 'Error dialog should not be present').to.be.false; const isAlertVisible: boolean = await driverHelper.isVisible(By.css('h4[class*="alert__title"]')); @@ -131,8 +128,7 @@ suite(`Create workspace from private SSH repo with default devfile ${BASE_TEST_C const projectName: string = StringUtil.getProjectNameFromGitUrl(gitSshUrl); await projectAndFileTests.waitWorkspaceReadinessForCheCodeEditor(); const projectSection: ViewSection = await projectAndFileTests.getProjectViewSession(); - expect(await projectAndFileTests.getProjectTreeItem(projectSection, projectName), 'Project folder was not imported').not - .undefined; + expect(await projectAndFileTests.getProjectTreeItem(projectSection, projectName), 'Project folder was not imported').not.undefined; }); test('Navigate to workspace Devfile tab and verify devfile content is not available', async function (): Promise { From 32971a4be1a96d1586fafa610a35fb7403621e51 Mon Sep 17 00:00:00 2001 From: Oleksii Korniienko Date: Tue, 6 Oct 2026 18:33:56 +0200 Subject: [PATCH 4/5] Added exit from loop if worskpace was started Assisted-by: Claude Code Signed-off-by: Oleksii Korniienko --- .../restart-tests/AutomaticRestartFromLocalDevfile.spec.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts b/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts index 82a69ca6166..dedc0821c10 100644 --- a/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts +++ b/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts @@ -107,6 +107,12 @@ suite(`Create workspace from private SSH repo with default devfile ${BASE_TEST_C const isRestartDialogVisible: boolean = await driverHelper.isVisible(restartDialogLocator); expect(isRestartDialogVisible, 'Restart dialog should not appear for workspace started with default devfile').to.be.false; + // stop polling as soon as the workspace has started + const isWorkspacePartVisible: boolean = await driverHelper.isVisible(By.xpath(xPathWorkspacePart)); + if (isWorkspacePartVisible) { + break; + } + await driverHelper.wait(polling); } From 7c7e87994f9bf392a104e4cbe943921575ea30df Mon Sep 17 00:00:00 2001 From: Oleksii Korniienko Date: Tue, 13 Oct 2026 15:21:19 +0200 Subject: [PATCH 5/5] Small fixes due to PR review Assisted-by: Claude Code Signed-off-by: Oleksii Korniienko --- .../restart-tests/AutomaticRestartFromLocalDevfile.spec.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts b/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts index dedc0821c10..5893310ad1c 100644 --- a/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts +++ b/tests/e2e/specs/restart-tests/AutomaticRestartFromLocalDevfile.spec.ts @@ -54,6 +54,8 @@ suite(`Create workspace from private SSH repo with default devfile ${BASE_TEST_C this.skip(); } + expect(gitSshUrl, 'TS_SELENIUM_GIT_SSH_REPO_URL environment variable must be set to a private SSH repo URL').to.not.be.empty; + await loginTests.loginIntoChe(); }); @@ -117,7 +119,7 @@ suite(`Create workspace from private SSH repo with default devfile ${BASE_TEST_C } // wait for workspace to start - await driverHelper.waitVisibility(By.xpath(xPathWorkspacePart), TIMEOUT_CONSTANTS.TS_IDE_START_TIMEOUT); + await driverHelper.waitVisibility(By.xpath(xPathWorkspacePart), polling); }); test('Verify no errors or alerts after workspace start', async function (): Promise {