diff --git a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties index ba9866a5750..899696384c1 100644 --- a/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties +++ b/assembly/assembly-wsmaster-war/src/main/webapp/WEB-INF/classes/che/che.properties @@ -648,10 +648,10 @@ che.wsagent.cors.enabled=true ## Factory defaults. # Editor and plugin which will be used for factories which are created from remote git repository # which doesn't contain any Che-specific workspace descriptors (like .devfile of .factory.json) -che.factory.default_editor=eclipse/che-theia/7.0.0 +che.factory.default_editor=eclipse/che-theia/next # multiple plugins must be comma-separated, for example: # pluginFooPublisher/pluginFooName/pluginFooVersion,pluginBarPublisher/pluginBarName/pluginBarVersion -che.factory.default_plugins=eclipse/che-machine-exec-plugin/7.0.0 +che.factory.default_plugins=eclipse/che-machine-exec-plugin/next ## Devfile settings @@ -660,7 +660,7 @@ che.factory.default_plugins=eclipse/che-machine-exec-plugin/7.0.0 # Default Editor that should be provisioned into Devfile if there is no specified Editor # Format is `editorPublisher/editorName/editorVersion` value. # `NULL` or absence of value means that default editor should not be provisioned. -che.workspace.devfile.default_editor=eclipse/che-theia/7.0.0 +che.workspace.devfile.default_editor=eclipse/che-theia/next # Default Plugins which should be provisioned for Default Editor. # All the plugins from this list that are not explicitly mentioned in the user-defined devfile @@ -668,4 +668,4 @@ che.workspace.devfile.default_editor=eclipse/che-theia/7.0.0 # the same as the default one (even if in different version). # Format is comma-separated `pluginPublisher/pluginName/pluginVersion` values, for example # eclipse/che-theia-exec-plugin/0.0.1,eclipse/che-theia-terminal-plugin/0.0.1 -che.workspace.devfile.default_editor.plugins=eclipse/che-machine-exec-plugin/7.0.0 +che.workspace.devfile.default_editor.plugins=eclipse/che-machine-exec-plugin/next diff --git a/deploy/kubernetes/helm/che/custom-charts/che-devfile-registry/values.yaml b/deploy/kubernetes/helm/che/custom-charts/che-devfile-registry/values.yaml index acc037c61ac..2de9080b0e0 100644 --- a/deploy/kubernetes/helm/che/custom-charts/che-devfile-registry/values.yaml +++ b/deploy/kubernetes/helm/che/custom-charts/che-devfile-registry/values.yaml @@ -8,7 +8,7 @@ # cheDevfileRegistry: - image: quay.io/eclipse/che-devfile-registry:7.0.0 + image: quay.io/eclipse/che-devfile-registry:nightly imagePullPolicy: Always memoryLimit: 256Mi memoryRequests: 16Mi diff --git a/deploy/kubernetes/helm/che/custom-charts/che-plugin-registry/values.yaml b/deploy/kubernetes/helm/che/custom-charts/che-plugin-registry/values.yaml index 90eef4f2652..8c3b6acbdd8 100644 --- a/deploy/kubernetes/helm/che/custom-charts/che-plugin-registry/values.yaml +++ b/deploy/kubernetes/helm/che/custom-charts/che-plugin-registry/values.yaml @@ -8,7 +8,7 @@ # chePluginRegistry: - image: quay.io/eclipse/che-plugin-registry:7.0.0 + image: quay.io/eclipse/che-plugin-registry:nightly imagePullPolicy: Always memoryLimit: 256Mi memoryRequests: 16Mi diff --git a/deploy/kubernetes/helm/che/values.yaml b/deploy/kubernetes/helm/che/values.yaml index 84f9e38bf6f..11ac588b6ea 100644 --- a/deploy/kubernetes/helm/che/values.yaml +++ b/deploy/kubernetes/helm/che/values.yaml @@ -16,7 +16,7 @@ cheWorkspaceHttpProxy: "" cheWorkspaceHttpsProxy: "" cheWorkspaceNoProxy: "" -cheImage: eclipse/che-server:7.0.0 +cheImage: eclipse/che-server:nigthly cheImagePullPolicy: Always cheKeycloakRealm: "che" cheKeycloakClientId: "che-public" diff --git a/deploy/openshift/deploy_che.sh b/deploy/openshift/deploy_che.sh index c2940ea64c5..be4620d7247 100755 --- a/deploy/openshift/deploy_che.sh +++ b/deploy/openshift/deploy_che.sh @@ -203,7 +203,7 @@ export KEYCLOAK_PASSWORD=${KEYCLOAK_PASSWORD:-${DEFAULT_KEYCLOAK_PASSWORD}} ### ### Plugin Registry settings ### -DEFAULT_PLUGIN_REGISTRY_IMAGE_TAG="7.0.0" +DEFAULT_PLUGIN_REGISTRY_IMAGE_TAG="nightly" export PLUGIN_REGISTRY_IMAGE_TAG=${PLUGIN_REGISTRY_IMAGE_TAG:-${DEFAULT_PLUGIN_REGISTRY_IMAGE_TAG}} DEFAULT_PLUGIN_REGISTRY_IMAGE="quay.io/eclipse/che-plugin-registry" @@ -218,7 +218,7 @@ export PLUGIN__REGISTRY__URL=${PLUGIN__REGISTRY__URL:-${DEFAULT_PLUGIN__REGISTRY ### ### Devfile Registry settings ### -DEFAULT_DEVFILE_REGISTRY_IMAGE_TAG="7.0.0" +DEFAULT_DEVFILE_REGISTRY_IMAGE_TAG="nightly" export DEVFILE_REGISTRY_IMAGE_TAG=${DEVFILE_REGISTRY_IMAGE_TAG:-${DEFAULT_DEVFILE_REGISTRY_IMAGE_TAG}} DEFAULT_DEVFILE_REGISTRY_IMAGE="quay.io/eclipse/che-devfile-registry" diff --git a/e2e/driver/CheReporter.ts b/e2e/driver/CheReporter.ts index ac1d3d7ced8..43f92a91642 100644 --- a/e2e/driver/CheReporter.ts +++ b/e2e/driver/CheReporter.ts @@ -147,7 +147,7 @@ class CheReporter extends mocha.reporters.Spec { // stop and remove running workspace if (TestConstants.DELETE_WORKSPACE_ON_FAILED_TEST) { - console.log("Property DELETE_WORKSPACE_ON_FAILED_TEST se to true - trying to stop and delete running workspace.") + console.log('Property DELETE_WORKSPACE_ON_FAILED_TEST se to true - trying to stop and delete running workspace.'); let namespace = TestConstants.TS_SELENIUM_USERNAME; let workspaceId = await testWorkspaceUtil.getIdOfRunningWorkspace(namespace); testWorkspaceUtil.stopWorkspaceById(workspaceId); diff --git a/e2e/driver/ContainerInitializer.ts b/e2e/driver/ContainerInitializer.ts index 7f2d8f1ac76..6cb5874a9e9 100644 --- a/e2e/driver/ContainerInitializer.ts +++ b/e2e/driver/ContainerInitializer.ts @@ -38,6 +38,7 @@ import { TestConstants } from '../TestConstants'; import { OcpLoginPage } from '../pageobjects/openshift/OcpLoginPage'; import { OcpWebConsolePage } from '../pageobjects/openshift/OcpWebConsolePage'; import { OcpLoginByTempAdmin } from '../pageobjects/login/OcpLoginByTempAdmin'; +import { OpenWorkspaceWidget } from '../pageobjects/ide/OpenWorkspaceWidget'; import { ITestWorkspaceUtil } from '..'; export function getContainer(): Container { @@ -73,5 +74,6 @@ export function getContainer(): Container { e2eContainer.bind(CLASSES.ScreenCatcher).to(ScreenCatcher).inSingletonScope(); e2eContainer.bind(CLASSES.OcpLoginPage).to(OcpLoginPage).inSingletonScope(); e2eContainer.bind(CLASSES.OcpWebConsolePage).to(OcpWebConsolePage).inSingletonScope(); + e2eContainer.bind(CLASSES.OpenWorkspaceWidget).to(OpenWorkspaceWidget).inSingletonScope(); return e2eContainer; } diff --git a/e2e/inversify.types.ts b/e2e/inversify.types.ts index a746c9a4dcf..b3b8e9911b6 100644 --- a/e2e/inversify.types.ts +++ b/e2e/inversify.types.ts @@ -36,7 +36,8 @@ const CLASSES = { WarningDialog: 'WarningDialog', ScreenCatcher: 'ScreenCatcher', OcpLoginPage: 'OcpLoginPage', - OcpWebConsolePage: 'OcpWebConsolePage' + OcpWebConsolePage: 'OcpWebConsolePage', + OpenWorkspaceWidget: 'OpenWorkspaceWidget' }; export { TYPES, CLASSES }; diff --git a/e2e/mocha-happy-path.opts b/e2e/mocha-happy-path.opts index 65d114cdfc2..9247645e92c 100644 --- a/e2e/mocha-happy-path.opts +++ b/e2e/mocha-happy-path.opts @@ -1,4 +1,4 @@ ---timeout 1200000 +--timeout 2200000 --reporter 'dist/driver/CheReporter.js' -u tdd --bail diff --git a/e2e/package.json b/e2e/package.json index 72f67dbf3f0..a33d77f82c4 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -18,6 +18,7 @@ "@types/node": "^11.13.4", "@types/rimraf": "^2.0.2", "@types/selenium-webdriver": "^3.0.16", + "axios": "0.17.1", "chai": "^4.2.0", "chromedriver": "^2.46.0", "mocha": "^6.1.4", @@ -26,8 +27,7 @@ "ts-node": "^8.0.3", "tslint": "5.10.0", "typed-rest-client": "^1.2.0", - "typescript": "^3.4.3", - "axios": "0.17.1" + "typescript": "^3.4.3" }, "dependencies": { "inversify": "^5.0.1", diff --git a/e2e/pageobjects/ide/Editor.ts b/e2e/pageobjects/ide/Editor.ts index a3d53c831b6..7118b63b3ce 100644 --- a/e2e/pageobjects/ide/Editor.ts +++ b/e2e/pageobjects/ide/Editor.ts @@ -172,7 +172,7 @@ export class Editor { timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT, polling: number = TestConstants.TS_SELENIUM_DEFAULT_POLLING) { - await this.selectTab(editorTabTitle); + await this.selectTab(editorTabTitle, timeout); await this.driverHelper.getDriver().wait(async () => { await this.performKeyCombination(editorTabTitle, Key.chord(Key.CONTROL, Key.END)); const editorText: string = await this.getEditorVisibleText(editorTabTitle); @@ -228,7 +228,6 @@ export class Editor { async waitStoppedDebugBreakpoint(tabTitle: string, lineNumber: number, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { const stoppedDebugBreakpointLocator: By = By.xpath(await this.getStoppedDebugBreakpointXpathLocator(tabTitle, lineNumber)); - await this.driverHelper.waitVisibility(stoppedDebugBreakpointLocator, timeout); } diff --git a/e2e/pageobjects/ide/Ide.ts b/e2e/pageobjects/ide/Ide.ts index 7d1e9e052fb..7a050bde6f2 100644 --- a/e2e/pageobjects/ide/Ide.ts +++ b/e2e/pageobjects/ide/Ide.ts @@ -24,8 +24,8 @@ export enum RightToolbarButton { @injectable() export class Ide { - public static readonly EXPLORER_BUTTON_XPATH: string = '(//ul[@class=\'p-TabBar-content\']//li[@title=\'Explorer\'])[1]'; - public static readonly SELECTED_EXPLORER_BUTTON_XPATH: string = '(//ul[@class=\'p-TabBar-content\']//li[@title=\'Explorer\' and contains(@class, \'p-mod-current\')])[1]'; + public static readonly EXPLORER_BUTTON_ID: string = 'shell-tab-explorer-view-container'; + public static readonly SELECTED_EXPLORER_BUTTON_CSS: string = 'li#shell-tab-explorer-view-container.theia-mod-active'; public static readonly ACTIVATED_IDE_IFRAME_CSS: string = '#ide-iframe-window[aria-hidden=\'false\']'; public static readonly SELECTED_GIT_BUTTON_XPATH: string = '(//ul[@class=\'p-TabBar-content\']//li[@title=\'Git\' and contains(@class, \'p-mod-current\')])[1]'; private static readonly TOP_MENU_PANEL_CSS: string = '#theia-app-shell #theia-top-panel .p-MenuBar-content'; @@ -95,9 +95,7 @@ export class Ide { } async clickOnNotificationButton(notificationText: string, buttonText: string) { - const notificationLocator: string = this.getNotificationXpathLocator(notificationText); - const yesButtonLocator: string = notificationLocator + `//button[text()=\'${buttonText}\']`; - + const yesButtonLocator: string = `//div[@class='theia-notification-list']//span[contains(.,'${notificationText}')]/parent::div/parent::div/parent::div/div[@class='theia-notification-list-item-content-bottom']//div[@class='theia-notification-buttons']//button[text()='${buttonText}'] `; await this.driverHelper.waitAndClick(By.xpath(yesButtonLocator)); } @@ -111,7 +109,7 @@ export class Ide { } async waitIde(timeout: number = TestConstants.TS_SELENIUM_LOAD_PAGE_TIMEOUT) { - const mainIdeParts: Array = [By.css(Ide.TOP_MENU_PANEL_CSS), By.css(Ide.LEFT_CONTENT_PANEL_CSS), By.xpath(Ide.EXPLORER_BUTTON_XPATH)]; + const mainIdeParts: Array = [By.css(Ide.TOP_MENU_PANEL_CSS), By.css(Ide.LEFT_CONTENT_PANEL_CSS), By.id(Ide.EXPLORER_BUTTON_ID)]; for (const idePartLocator of mainIdeParts) { await this.driverHelper.waitVisibility(idePartLocator, timeout); @@ -233,7 +231,7 @@ export class Ide { } async getApplicationUrlFromNotification(notificationText: string) { - const notificationTextLocator: By = By.xpath(`//div[@class='theia-Notification']//p[contains(@id,'${notificationText}')]`); + const notificationTextLocator: By = By.xpath(`//div[@class='theia-notification-message']/span[contains(.,'${notificationText}')]`); let notification = await this.driverHelper.waitAndGetText(notificationTextLocator); let regexp: RegExp = new RegExp('^.*(https?://.*)$'); @@ -247,17 +245,16 @@ export class Ide { async waitApllicationIsReady(url: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { - - await this.driverHelper.getDriver().wait(async () => { + let res = await axios.get(url); + await this.driverHelper.getDriver().wait(async () => { try { - let res = await axios.get(url); - + res = await axios.get(url); if (res.status === 200) { - console.log('Application is ready for use.'); + console.log('Application is ready for use. App url:'); return true; } } catch (error) { - console.log('Application is not yet ready for use'); + console.log('Application is not yet ready for use'); } await this.driverHelper.wait(TestConstants.TS_SELENIUM_DEFAULT_POLLING); @@ -266,16 +263,18 @@ export class Ide { private getSelectedRightToolbarButtonLocator(buttonTitle: string): By { return By.xpath(`//div[@id='theia-left-content-panel']//ul[@class='p-TabBar-content']` + - `//li[@title='${buttonTitle}' and contains(@id, 'shell-tab')] and contains(@class, 'p-mod-current')`); + `//li[@title[contains(.,'${buttonTitle}')] and contains(@id, 'shell-tab')] and contains(@class, 'p-mod-current')`); } private getRightToolbarButtonLocator(buttonTitle: String): By { return By.xpath(`//div[@id='theia-left-content-panel']//ul[@class='p-TabBar-content']` + - `//li[@title='${buttonTitle}' and contains(@id, 'shell-tab')]`); + `//li[@title[contains(.,'${buttonTitle}')] and contains(@id, 'shell-tab')]`); } private getNotificationXpathLocator(notificationText: string): string { - return `//div[@class='theia-Notification' and contains(@id,'${notificationText}')]`; + return `//div[@class='theia-notification-message']/span[contains(.,'${notificationText}')]`; } + + } diff --git a/e2e/pageobjects/ide/OpenWorkspaceWidget.ts b/e2e/pageobjects/ide/OpenWorkspaceWidget.ts new file mode 100644 index 00000000000..728b6273c73 --- /dev/null +++ b/e2e/pageobjects/ide/OpenWorkspaceWidget.ts @@ -0,0 +1,60 @@ +/********************************************************************* + * Copyright (c) 2019 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 { injectable, inject } from 'inversify'; +import { CLASSES } from '../../inversify.types'; +import { DriverHelper } from '../../utils/DriverHelper'; +import { By } from 'selenium-webdriver'; +import { TestConstants } from '../../TestConstants'; + +@injectable() +export class OpenWorkspaceWidget { + private static readonly OPEN_WORKSPACE_MAIN_VIEW_XPATH = '//div[@class=\'dialogTitle\']/div[text()=\'Open Workspace\']'; + private static readonly OPEN_WORKSPACE_OPEN_BTN_CSS = 'div.dialogControl>button.main'; + private static readonly THEIA_LOCATION_LIST_CSS = 'select.theia-LocationList'; + + constructor(@inject(CLASSES.DriverHelper) private readonly driverHelper: DriverHelper) { + } + + async waitOpenWorkspaceWidget(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + await this.driverHelper.waitVisibility(By.xpath(OpenWorkspaceWidget.OPEN_WORKSPACE_MAIN_VIEW_XPATH), timeout); + } + + async waitWidgetIsClosed(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + await this.driverHelper.waitDisappearance(By.xpath(OpenWorkspaceWidget.OPEN_WORKSPACE_MAIN_VIEW_XPATH), timeout); + } + + async selectItemInTree(pathToItem: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + await this.driverHelper.waitAndClick(By.id(pathToItem), timeout); + } + + async clickOnOpenButton(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + await this.driverHelper.waitAndClick(By.css(OpenWorkspaceWidget.OPEN_WORKSPACE_OPEN_BTN_CSS), timeout); + } + + async selectItemInTreeAndOpenWorkspace(item: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + await this.selectItemInTree(item, timeout); + await this.clickOnOpenButton(); + await this.waitWidgetIsClosed(); + } + + async expandTreeToPath(path: string) { + const pathNodes: string[] = path.split('/'); + for (let currentPath of pathNodes) { + await this.driverHelper.waitAndClick(By.id(`/${currentPath}`)); + } + } + + async selectRootWorkspaceItemInDropDawn(rootProject: string) { + await this.driverHelper.waitAndClick(By.css(OpenWorkspaceWidget.THEIA_LOCATION_LIST_CSS)); + await this.driverHelper.waitAndClick(By.css(`option[value=\'file:///${rootProject}']`)); + } + + +} diff --git a/e2e/pageobjects/ide/PreviewWidget.ts b/e2e/pageobjects/ide/PreviewWidget.ts index 51f29fea537..59d0b310e6c 100644 --- a/e2e/pageobjects/ide/PreviewWidget.ts +++ b/e2e/pageobjects/ide/PreviewWidget.ts @@ -20,9 +20,9 @@ export class PreviewWidget { @inject(CLASSES.Ide) private readonly ide: Ide) { } async waitAndSwitchToWidgetFrame() { - const iframeLocator: By = By.css('.theia-mini-browser iframe'); - + const iframeLocator: By = By.css('div.theia-mini-browser iframe'); await this.driverHelper.waitAndSwitchToFrame(iframeLocator); + } async waitPreviewWidget(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { @@ -36,16 +36,12 @@ export class PreviewWidget { async waitContentAvailable(contentLocator: By, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT, polling: number = TestConstants.TS_SELENIUM_DEFAULT_POLLING * 5) { - await this.waitAndSwitchToWidgetFrame(); - await this.driverHelper.getDriver().wait(async () => { const isApplicationTitleVisible: boolean = await this.driverHelper.isVisible(contentLocator); - if (isApplicationTitleVisible) { await this.driverHelper.getDriver().switchTo().defaultContent(); await this.ide.waitAndSwitchToIdeFrame(); - return true; } @@ -56,6 +52,24 @@ export class PreviewWidget { }, timeout); } + async waitContentAvailableInAssociatedWorkspace(contentLocator: By, + timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT, + polling: number = TestConstants.TS_SELENIUM_DEFAULT_POLLING * 5) { + await this.waitAndSwitchToWidgetFrame(); + await this.driverHelper.getDriver().wait(async () => { + const isApplicationTitleVisible: boolean = await this.driverHelper.isVisible(contentLocator); + if (isApplicationTitleVisible) { + await this.driverHelper.getDriver().switchTo().defaultContent(); + return true; + } + + await this.driverHelper.getDriver().switchTo().defaultContent(); + await this.refreshPage(); + await this.waitAndSwitchToWidgetFrame(); + await this.driverHelper.wait(polling); + }, timeout); + } + async waitVisibility(element: By, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { await this.driverHelper.waitVisibility(element, timeout); } diff --git a/e2e/pageobjects/ide/ProjectTree.ts b/e2e/pageobjects/ide/ProjectTree.ts index 932c687a68c..a86d3262496 100644 --- a/e2e/pageobjects/ide/ProjectTree.ts +++ b/e2e/pageobjects/ide/ProjectTree.ts @@ -26,7 +26,7 @@ export class ProjectTree { @inject(CLASSES.Editor) private readonly editor: Editor) { } async openProjectTreeContainer(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { - const selectedExplorerButtonLocator: By = By.xpath(Ide.SELECTED_EXPLORER_BUTTON_XPATH); + const selectedExplorerButtonLocator: By = By.css(Ide.SELECTED_EXPLORER_BUTTON_CSS); await this.ide.waitRightToolbarButton(RightToolbarButton.Explorer, timeout); @@ -40,13 +40,14 @@ export class ProjectTree { } async waitItemExpanded(itemPath: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { - const expandedItemLocator: By = By.css(this.getExpandedItemCssLocator(itemPath)); - + const locator: string = await this.getExpandedItemCssLocator(itemPath); + const expandedItemLocator: By = By.css(locator); await this.driverHelper.waitVisibility(expandedItemLocator, timeout); } async waitItemCollapsed(itemPath: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { - const collapsedItemLocator: By = By.css(this.getCollapsedItemCssLocator(itemPath)); + const locator: string = await this.getCollapsedItemCssLocator(itemPath); + const collapsedItemLocator: By = By.css(locator); await this.driverHelper.waitVisibility(collapsedItemLocator, timeout); } @@ -62,35 +63,40 @@ export class ProjectTree { } async waitItem(itemPath: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { - await this.driverHelper.waitVisibility(By.css(this.getItemCss(itemPath)), timeout); + const locator: string = await this.getItemCss(itemPath); + await this.driverHelper.waitVisibility(By.css(locator), timeout); } async waitItemDisappearance(itemPath: string, attempts: number = TestConstants.TS_SELENIUM_DEFAULT_ATTEMPTS, polling: number = TestConstants.TS_SELENIUM_DEFAULT_POLLING) { - - await this.driverHelper.waitDisappearance(By.css(this.getItemCss(itemPath)), attempts, polling); + const locator: string = await this.getItemCss(itemPath); + await this.driverHelper.waitDisappearance(By.css(locator), attempts, polling); } async clickOnItem(itemPath: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { - await this.driverHelper.waitAndClick(By.css(this.getItemCss(itemPath)), timeout); + const locator: string = await this.getItemCss(itemPath); + await this.driverHelper.waitAndClick(By.css(locator), timeout); await this.waitItemSelected(itemPath, timeout); + } async waitItemSelected(itemPath: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { const selectedItemLocator: By = By.css(`div[title='/projects/${itemPath}'].theia-mod-selected.theia-mod-focus`); - await this.driverHelper.waitVisibility(selectedItemLocator, timeout); } async expandItem(itemPath: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { - const expandIconLocator: By = By.css(this.getExpandIconCssLocator(itemPath)); + const locator: string = await this.getExpandIconCssLocator(itemPath); + const expandIconLocator: By = By.css(locator); const treeItemLocator: By = By.css(this.getTreeItemCssLocator(itemPath)); + + + await this.driverHelper.getDriver().wait(async () => { const classAttributeValue: string = await this.driverHelper.waitAndGetElementAttribute(expandIconLocator, 'class', timeout); const isItemCollapsed: boolean = classAttributeValue.search('theia-mod-collapsed') > 0; - if (isItemCollapsed) { await this.driverHelper.waitAndClick(treeItemLocator, timeout); } @@ -110,7 +116,8 @@ export class ProjectTree { } async collapseItem(itemPath: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { - const expandIconLocator: By = By.css(this.getExpandIconCssLocator(itemPath)); + const locator: string = await this.getExpandIconCssLocator(itemPath); + const expandIconLocator: By = By.css(locator); const treeItemLocator: By = By.css(this.getTreeItemCssLocator(itemPath)); const classAttributeValue: string = await this.driverHelper.waitAndGetElementAttribute(expandIconLocator, 'class', timeout); @@ -124,14 +131,14 @@ export class ProjectTree { } async expandPathAndOpenFile(pathToItem: string, fileName: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { - let currentPath: string = ''; + let projectName: string = ''; let paths: Array = new Array(); // make direct path for each project tree item pathToItem.split('/') .forEach(item => { - currentPath = `${currentPath}/${item}`; - paths.push(currentPath); + projectName = `${projectName}/${item}`; + paths.push(projectName); }); // expand each project tree item @@ -147,15 +154,49 @@ export class ProjectTree { await this.editor.waitTab(fileName); } + async expandPathAndOpenFileInAssociatedWorkspace(pathToItem: string, fileName: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + let projectName: string = pathToItem.split('/')[0]; + let pathEntry = `${projectName}`; + let pathToItemInAssociatedWorkspace = pathToItem.replace(`${projectName}/`, ''); + let paths: Array = new Array(); + + // if we in the root of project + if (pathToItem.split('/').length < 2) { + await this.clickOnItem(`${projectName}/${fileName}`, timeout); + return; + } + // make direct path for each project tree item + pathToItemInAssociatedWorkspace.split('/') + .forEach(item => { + pathEntry = pathEntry + `/${item}`; + paths.push(pathEntry); + }); + + + // expand each project tree item + for (const path of paths) { + await this.expandItem(path, timeout); + } + // open file + await this.clickOnItem(`${projectName}/${pathToItemInAssociatedWorkspace}/${fileName}`, timeout); + + // check file appearance in the editor + await this.editor.waitEditorOpened(fileName, timeout); + await this.editor.waitTab(fileName); + } + async waitProjectImported(projectName: string, rootSubItem: string, attempts: number = TestConstants.TS_SELENIUM_DEFAULT_ATTEMPTS, visibilityItemPolling: number = TestConstants.TS_SELENIUM_DEFAULT_POLLING * 5, triesPolling: number = TestConstants.TS_SELENIUM_DEFAULT_POLLING * 30) { - const rootItem: string = `/${projectName}`; - const rootItemLocator: By = By.css(this.getTreeItemCssLocator(`/${projectName}`)); - const rootSubitemLocator: By = By.css(this.getTreeItemCssLocator(`/${projectName}/${rootSubItem}`)); + const rootItem: string = `${projectName}`; + const rootItemLocator: By = By.css(this.getTreeItemCssLocator(`${projectName}`)); + const rootSubitemLocator: By = By.css(this.getTreeItemCssLocator(`${projectName}/${rootSubItem}`)); + + + for (let i = 0; i < attempts; i++) { const isProjectFolderVisible = await this.driverHelper.waitVisibilityBoolean(rootItemLocator, attempts, visibilityItemPolling); @@ -189,24 +230,37 @@ export class ProjectTree { throw new error.TimeoutError('Exceeded the maximum number of checking attempts, project has not been imported'); } - private getItemCss(itemPath: string): string { - return `div[id='/projects:/projects/${itemPath}']`; + + private async getWorkspacePathEntry(): Promise { + const nodeAttribute: string = 'data-node-id'; + const splitDelimeter = ':'; + const attribute: string = await this.driverHelper.waitAndGetElementAttribute(By.css(`div[${nodeAttribute}]`), nodeAttribute); + return attribute.split(splitDelimeter)[0] + splitDelimeter; + } + + + private async getItemCss(itemPath: string): Promise { + const entry: string = await this.getWorkspacePathEntry(); + return `div[id='${entry}/projects/${itemPath}']`; } - private getCollapsedItemCssLocator(itemPath: string): string { - return `${this.getExpandIconCssLocator(itemPath)}.theia-mod-collapsed`; + private async getCollapsedItemCssLocator(itemPath: string): Promise { + const item: string = await this.getExpandIconCssLocator(itemPath); + return item + '.theia-mod-collapsed'; } - private getExpandedItemCssLocator(itemPath: string): string { - return `${this.getExpandIconCssLocator(itemPath)}:not(.theia-mod-collapsed)`; + private async getExpandedItemCssLocator(itemPath: string): Promise { + const item: string = await this.getExpandIconCssLocator(itemPath); + return item + ':not(.theia-mod-collapsed)'; } - private getExpandIconCssLocator(itemPath: string): string { - return `div[data-node-id='/projects:/projects${itemPath}']`; + private async getExpandIconCssLocator(itemPath: string): Promise { + const entry: string = await this.getWorkspacePathEntry(); + return `div[data-node-id='${entry}/projects/${itemPath}']`; } private getTreeItemCssLocator(itemPath: string): string { - return `.theia-TreeNode[title='/projects${itemPath}']`; + return `.theia-TreeNode[title='/projects/${itemPath}']`; } } diff --git a/e2e/tests/e2e_happy_path/HappyPath.spec.ts b/e2e/tests/e2e_happy_path/HappyPath.spec.ts index 7ab30d2b982..06756c228fc 100644 --- a/e2e/tests/e2e_happy_path/HappyPath.spec.ts +++ b/e2e/tests/e2e_happy_path/HappyPath.spec.ts @@ -20,9 +20,10 @@ import { PreviewWidget } from '../../pageobjects/ide/PreviewWidget'; import { TestConstants } from '../../TestConstants'; import { RightToolbar } from '../../pageobjects/ide/RightToolbar'; import { By, Key, error } from 'selenium-webdriver'; -import { Terminal } from '../../pageobjects/ide/Terminal'; import { DebugView } from '../../pageobjects/ide/DebugView'; import { WarningDialog } from '../../pageobjects/ide/WarningDialog'; +import { Terminal } from '../../pageobjects/ide/Terminal'; +import { OpenWorkspaceWidget } from '../../pageobjects/ide/OpenWorkspaceWidget'; import * as fs from 'fs'; const driverHelper: DriverHelper = e2eContainer.get(CLASSES.DriverHelper); @@ -36,7 +37,7 @@ const rightToolbar: RightToolbar = e2eContainer.get(CLASSES.RightToolbar); const terminal: Terminal = e2eContainer.get(CLASSES.Terminal); const debugView: DebugView = e2eContainer.get(CLASSES.DebugView); const warningDialog: WarningDialog = e2eContainer.get(CLASSES.WarningDialog); - +const openWorkspaceWidget: OpenWorkspaceWidget = e2eContainer.get(CLASSES.OpenWorkspaceWidget); const projectName: string = 'petclinic'; const namespace: string = TestConstants.TS_SELENIUM_USERNAME; const workspaceName: string = TestConstants.TS_SELENIUM_HAPPY_PATH_WORKSPACE_NAME; @@ -67,21 +68,27 @@ suite('Validation of workspace start', async () => { test('Wait workspace running state', async () => { await ide.waitWorkspaceAndIde(namespace, workspaceName); + await projectTree.openProjectTreeContainer(); + await projectTree.waitProjectImported(projectName, 'src'); }); test('Wait until project is imported', async () => { + const rootWsName: string = 'projects'; + const mainWindowHandle: string = await driverHelper.getDriver().getWindowHandle(); + await topMenu.selectOption('File', 'Open Workspace...'); + await openWorkspaceWidget.selectRootWorkspaceItemInDropDawn(rootWsName); + await openWorkspaceWidget.selectItemInTreeAndOpenWorkspace(`/${rootWsName}/${projectName}`); + await closeMainWindowAndSwitchToWorkspace(mainWindowHandle); await projectTree.openProjectTreeContainer(); - await projectTree.waitProjectImported(projectName, 'src'); - await projectTree.expandItem(`/${projectName}`); }); -}); +}); suite('Language server validation', async () => { test('Java LS initialization', async () => { - await projectTree.expandPathAndOpenFile(pathToJavaFolder, javaFileName); + await projectTree.expandPathAndOpenFileInAssociatedWorkspace(pathToJavaFolder, javaFileName); await editor.selectTab(javaFileName); await ide.checkLsInitializationStart('Starting Java Language Server'); - await ide.waitStatusBarTextAbsence('Starting Java Language Server', 360000); + await ide.waitStatusBarTextAbsence('Starting Java Language Server', 1800000); await checkJavaPathCompletion(); await ide.waitStatusBarTextAbsence('Building workspace', 360000); }); @@ -113,7 +120,7 @@ suite('Language server validation', async () => { }); test.skip('Yaml LS initialization', async () => { - await projectTree.expandPathAndOpenFile(pathToYamlFolder, yamlFileName); + await projectTree.expandPathAndOpenFileInAssociatedWorkspace(pathToYamlFolder, yamlFileName); await editor.waitEditorAvailable(yamlFileName); await editor.clickOnTab(yamlFileName); await editor.waitTabFocused(yamlFileName); @@ -126,9 +133,8 @@ suite('Language server validation', async () => { suite('Validation of workspace build and run', async () => { test('Build application', async () => { await runTask('che: build-file-output'); - - await projectTree.expandPathAndOpenFile(projectName, 'build-output.txt'); - await editor.followAndWaitForText('build-output.txt', '[INFO] BUILD SUCCESS', 180000, 5000); + await projectTree.expandPathAndOpenFileInAssociatedWorkspace(projectName, 'build-output.txt'); + await editor.followAndWaitForText('build-output.txt', '[INFO] BUILD SUCCESS', 220000, 5000); }); test('Run application', async () => { @@ -138,7 +144,7 @@ suite('Validation of workspace build and run', async () => { }); test('Check the running application', async () => { - await previewWidget.waitContentAvailable(SpringAppLocators.springTitleLocator, 60000, 10000); + await previewWidget.waitContentAvailableInAssociatedWorkspace(SpringAppLocators.springTitleLocator, 60000, 10000); }); test('Close preview widget', async () => { @@ -150,14 +156,13 @@ suite('Validation of workspace build and run', async () => { await terminal.closeTerminalTab('build-file-output'); await terminal.rejectTerminalProcess('run'); await terminal.closeTerminalTab('run'); - await warningDialog.waitAndCloseIfAppear(); }); }); suite('Display source code changes in the running application', async () => { test('Change source code', async () => { - await projectTree.expandPathAndOpenFile(pathToChangedJavaFileFolder, changedJavaFileName); + await projectTree.expandPathAndOpenFileInAssociatedWorkspace(pathToChangedJavaFileFolder, changedJavaFileName); await editor.waitEditorAvailable(changedJavaFileName); await editor.clickOnTab(changedJavaFileName); await editor.waitTabFocused(changedJavaFileName); @@ -169,28 +174,22 @@ suite('Display source code changes in the running application', async () => { test('Build application with changes', async () => { await runTask('che: build'); - - await projectTree.expandPathAndOpenFile(projectName, 'build.txt'); + await projectTree.expandPathAndOpenFileInAssociatedWorkspace(projectName, 'build.txt'); await editor.waitEditorAvailable('build.txt'); await editor.clickOnTab('build.txt'); await editor.waitTabFocused('build.txt'); - await editor.followAndWaitForText('build.txt', '[INFO] BUILD SUCCESS', 180000, 5000); + await editor.followAndWaitForText('build.txt', '[INFO] BUILD SUCCESS', 300000, 5000); }); test('Run application with changes', async () => { await runTask('che: run-with-changes'); - await ide.waitNotificationAndConfirm('A new process is now listening on port 8080', 120000); await ide.waitNotificationAndOpenLink('Redirect is now enabled on port 8080', 120000); }); test('Check changes are displayed', async () => { - await previewWidget.waitContentAvailable(SpringAppLocators.springTitleLocator, 60000, 10000); - await previewWidget.waitAndSwitchToWidgetFrame(); - await previewWidget.waitAndClick(SpringAppLocators.springMenuButtonLocator); - await previewWidget.waitAndClick(SpringAppLocators.springErrorButtonLocator); - await previewWidget.waitVisibility(SpringAppLocators.springErrorMessageLocator); - await previewWidget.switchBackToIdeFrame(); + await previewWidget.waitContentAvailableInAssociatedWorkspace(SpringAppLocators.springTitleLocator, 60000, 10000); + checkErrorMessageInApplicationController(); }); test('Close preview widget', async () => { @@ -201,14 +200,13 @@ suite('Display source code changes in the running application', async () => { test('Close running terminal processes and tabs', async () => { await terminal.rejectTerminalProcess('run-with-changes'); await terminal.closeTerminalTab('run-with-changes'); - await warningDialog.waitAndCloseIfAppear(); }); }); suite('Validation of debug functionality', async () => { test('Open file and activate breakpoint', async () => { - await projectTree.expandPathAndOpenFile(pathToJavaFolder, javaFileName); + await projectTree.expandPathAndOpenFileInAssociatedWorkspace(pathToJavaFolder, javaFileName); await editor.selectTab(javaFileName); await editor.moveCursorToLineAndChar(javaFileName, 34, 1); await editor.activateBreakpoint(javaFileName, 32); @@ -216,28 +214,26 @@ suite('Validation of debug functionality', async () => { test('Launch debug', async () => { await runTask('che: run-debug'); - - await ide.waitNotificationAndConfirm('A new process is now listening on port 8080', 120000); - await ide.waitNotificationAndOpenLink('Redirect is now enabled on port 8080', 120000); + await ide.waitNotificationAndConfirm('A new process is now listening on port 8080', 180000); + await ide.waitNotificationAndOpenLink('Redirect is now enabled on port 8080', 180000); }); test('Check content of the launched application', async () => { - await previewWidget.waitContentAvailable(SpringAppLocators.springErrorMessageLocator, 60000, 10000); + await checkErrorMessageInApplicationController(); }); test('Open debug configuration file', async () => { - await topMenu.selectOption('Debug', 'Open Configurations'); + await isureClickOnDebugMenu(); await editor.waitEditorAvailable('launch.json'); await editor.selectTab('launch.json'); }); test('Add debug configuration options', async () => { - await editor.moveCursorToLineAndChar('launch.json', 5, 22); + await editor.moveCursorToLineAndChar('launch.json', 6, 24); await editor.performKeyCombination('launch.json', Key.chord(Key.CONTROL, Key.SPACE)); await editor.clickOnSuggestion('Java: Launch Program in Current File'); await editor.waitTabWithUnsavedStatus('launch.json'); await editor.waitText('launch.json', '\"name\": \"Debug (Launch) - Current File\"'); - await editor.waitTabWithSavedStatus('launch.json'); }); @@ -248,12 +244,33 @@ suite('Validation of debug functionality', async () => { await debugView.clickOnDebugConfigurationDropDown(); await debugView.clickOnDebugConfigurationItem('Debug (Launch) - Current File'); await debugView.clickOnRunDebugButton(); - await previewWidget.refreshPage(); - await editor.waitStoppedDebugBreakpoint(javaFileName, 32); + try { + await editor.waitStoppedDebugBreakpoint(javaFileName, 32); + } catch (err) { + await previewWidget.refreshPage(); + await editor.waitStoppedDebugBreakpoint(javaFileName, 32); + } }); }); +async function checkErrorMessageInApplicationController() { + await previewWidget.waitAndSwitchToWidgetFrame(); + await previewWidget.waitAndClick(SpringAppLocators.springMenuButtonLocator); + await previewWidget.waitAndClick(SpringAppLocators.springErrorButtonLocator); + await previewWidget.waitVisibility(SpringAppLocators.springErrorMessageLocator); + await driverHelper.getDriver().switchTo().defaultContent(); +} + +async function closeMainWindowAndSwitchToWorkspace(mainWindowHandle: string) { + await driverHelper.switchToSecondWindow(mainWindowHandle); + const secondWindowHandle: string = await driverHelper.getDriver().getWindowHandle(); + await driverHelper.getDriver().switchTo().window(mainWindowHandle); + await driverHelper.getDriver().close(); + await driverHelper.getDriver().switchTo().window(secondWindowHandle); + await driverHelper.getDriver().manage().window().setSize(TestConstants.TS_SELENIUM_RESOLUTION_WIDTH, TestConstants.TS_SELENIUM_RESOLUTION_HEIGHT); +} + async function runTask(task: string) { await topMenu.selectOption('Terminal', 'Run Task...'); try { @@ -269,6 +286,13 @@ async function runTask(task: string) { await quickOpenContainer.clickOnContainerItem(task); } +//sometimes under high loading the forst click can be failed +async function isureClickOnDebugMenu() { + try { await topMenu.selectOption('Debug', 'Open Configurations'); } catch (e) { + console.log(`After clicking to the Debug top menu the menu has been not opened, try to click again...`); + await topMenu.selectOption('Debug', 'Open Configurations'); + } +} async function checkJavaPathCompletion() { if (await ide.isNotificationPresent('Classpath is incomplete. Only syntax errors will be reported')) { @@ -291,5 +315,5 @@ async function checkJavaPathCompletion() { await editor.type(classPathFilename, classpathText, 1); await editor.waitTabWithSavedStatus(classPathFilename); } - } + diff --git a/e2e/utils/DriverHelper.ts b/e2e/utils/DriverHelper.ts index 3c767a2c3d7..c81c7760af6 100644 --- a/e2e/utils/DriverHelper.ts +++ b/e2e/utils/DriverHelper.ts @@ -436,4 +436,19 @@ export class DriverHelper { return this.driver; } + async waitOpenningSecondWindow(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + await this.driver.wait(async () => { + const handles: string[] = await this.driver.getAllWindowHandles(); + if (handles.length > 1) { + return true; + } + }, timeout); + } + + async switchToSecondWindow(mainWindowHandle: string) { + await this.waitOpenningSecondWindow(); + const handles: string[] = await this.driver.getAllWindowHandles(); + handles.splice(handles.indexOf(mainWindowHandle), 1); + await this.driver.switchTo().window(handles[0]); + } } diff --git a/e2e/utils/workspace/TestWorkspaceUtil.ts b/e2e/utils/workspace/TestWorkspaceUtil.ts index dbf89bd8b23..2ab1814a357 100644 --- a/e2e/utils/workspace/TestWorkspaceUtil.ts +++ b/e2e/utils/workspace/TestWorkspaceUtil.ts @@ -78,14 +78,14 @@ export class TestWorkspaceUtil implements ITestWorkspaceUtil { } public async getIdOfRunningWorkspace(namespace: string): Promise { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } - + removeWorkspaceById(id: string): void { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } - + stopWorkspaceById(id: string): void { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } }