From 3af3742d6f3263a0cc75cb9261b0bc04acdf487e Mon Sep 17 00:00:00 2001 From: Maxim Musienko Date: Tue, 25 Feb 2020 19:16:53 +0200 Subject: [PATCH] Add new test for checking git plus ssh workflow (#15561) Add new test for checking Git plus SSH workflaw --- tests/.infra/crw-ci/master/k8s/Jenkinsfile | 10 +- tests/.infra/crw-ci/nightly/k8s/Jenkinsfile | 53 +++++- tests/.infra/crw-ci/pr-check/k8s/Jenkinsfile | 5 +- .../pre-release-testing/k8s/Jenkinsfile | 49 ++++- tests/e2e/TestConstants.ts | 18 +- tests/e2e/driver/CheReporter.ts | 2 +- tests/e2e/index.ts | 4 +- tests/e2e/inversify.config.ts | 10 +- tests/e2e/inversify.types.ts | 3 + tests/e2e/mocha-git-ssh.opts | 7 + tests/e2e/package.json | 4 +- tests/e2e/pageobjects/ide/GitHubPlugin.ts | 85 --------- tests/e2e/pageobjects/ide/GitPlugin.ts | 97 ++++++++++ .../pageobjects/ide/OpenWorkspaceWidget.ts | 9 - .../e2e/pageobjects/ide/QuickOpenContainer.ts | 11 +- tests/e2e/tests/e2e/GitSsh.spec.ts | 127 +++++++++++++ tests/e2e/utils/DriverHelper.ts | 1 - tests/e2e/utils/VCS/CheGitApi.ts | 26 +++ tests/e2e/utils/VCS/github/GitHubUtil.ts | 47 +++++ .../requestHandlers/CheApiRequestHandler.ts | 2 +- .../e2e/utils/workspace/TestWorkspaceUtil.ts | 177 ++++++------------ 21 files changed, 509 insertions(+), 238 deletions(-) create mode 100644 tests/e2e/mocha-git-ssh.opts delete mode 100644 tests/e2e/pageobjects/ide/GitHubPlugin.ts create mode 100644 tests/e2e/pageobjects/ide/GitPlugin.ts create mode 100644 tests/e2e/tests/e2e/GitSsh.spec.ts create mode 100644 tests/e2e/utils/VCS/CheGitApi.ts create mode 100644 tests/e2e/utils/VCS/github/GitHubUtil.ts diff --git a/tests/.infra/crw-ci/master/k8s/Jenkinsfile b/tests/.infra/crw-ci/master/k8s/Jenkinsfile index ecb3b47045..e2ebc77bdd 100644 --- a/tests/.infra/crw-ci/master/k8s/Jenkinsfile +++ b/tests/.infra/crw-ci/master/k8s/Jenkinsfile @@ -49,7 +49,10 @@ pipeline { value: ''), booleanParam(name: 'createTestWorkspace', - value: true) + value: true), + + string(name: 'e2eTestParameters', + value: '') ] } } @@ -83,7 +86,10 @@ pipeline { value: ''), booleanParam(name: 'createTestWorkspace', - value: false) + value: false), + + string(name: 'e2eTestParameters', + value: '') ] } } diff --git a/tests/.infra/crw-ci/nightly/k8s/Jenkinsfile b/tests/.infra/crw-ci/nightly/k8s/Jenkinsfile index 5663a74ccf..6d8affaa4d 100644 --- a/tests/.infra/crw-ci/nightly/k8s/Jenkinsfile +++ b/tests/.infra/crw-ci/nightly/k8s/Jenkinsfile @@ -19,7 +19,7 @@ pipeline { stages { stage("Run E2E tests") { parallel { - stage('Run Happy path tests against nightly build') { + stage('Run Happy path tests') { steps { build job: 'basic-MultiUser-Che-check-e2e-tests-against-k8s', parameters: [ @@ -48,12 +48,15 @@ pipeline { value: ''), booleanParam(name: 'createTestWorkspace', - value: true) + value: true), + + string(name: 'e2eTestParameters', + value: '') ] } } - stage('Run devfile tests against nightly build') { + stage('Run devfile tests') { steps { build job: 'basic-MultiUser-Che-check-e2e-tests-against-k8s', parameters: [ @@ -82,10 +85,52 @@ pipeline { value: ''), booleanParam(name: 'createTestWorkspace', - value: false) + value: false), + + string(name: 'e2eTestParameters', + value: '') ] } } + + stage('Run Git SSH flow tests') { + steps { + withCredentials([string(credentialsId: 'e45af3e6-8061-4d02-b187-b1c3bb133d3a', variable: 'github_oauth_token')]) { + build job: 'basic-MultiUser-Che-check-e2e-tests-against-k8s', + parameters: [ + string(name: 'cheImageRepo', + value: 'quay.io/eclipse/che-server'), + + string(name: 'cheImageTag', + value: 'nightly'), + + booleanParam(name: 'buildChe', + value: false), + + string(name: 'ghprbSourceBranch', + value: 'CRW-391'), + + string(name: 'ghprbPullId', + value: ''), + + string(name: 'e2eTestToRun', + value: 'test-git-ssh'), + + string(name: 'testWorkspaceDevfileUrl', + value: ''), + + text(name: 'customResourceFileContent', + value: ''), + + booleanParam(name: 'createTestWorkspace', + value: false), + + string(name: 'e2eTestParameters', + value: "-e TS_GITHUB_TEST_REPO_ACCESS_TOKEN=$github_oauth_token -e TS_GITHUB_TEST_REPO=chepullreq4/Spoon-Knife -e NODE_TLS_REJECT_UNAUTHORIZED=0") + ] + } + } + } } } } diff --git a/tests/.infra/crw-ci/pr-check/k8s/Jenkinsfile b/tests/.infra/crw-ci/pr-check/k8s/Jenkinsfile index f333bd1c37..a776afa8a6 100644 --- a/tests/.infra/crw-ci/pr-check/k8s/Jenkinsfile +++ b/tests/.infra/crw-ci/pr-check/k8s/Jenkinsfile @@ -55,6 +55,9 @@ pipeline { string(name: 'ghprbSourceBranch', defaultValue: "master") + + string(name: 'e2eTestParameters', + defaultValue: "") } stages { @@ -311,7 +314,7 @@ pipeline { -e TS_SELENIUM_MULTIUSER="true" \\ -e TS_SELENIUM_USERNAME="admin" \\ -e TS_SELENIUM_PASSWORD="admin" \\ - -e TEST_SUITE="${e2eTestToRun}" \\ + -e TEST_SUITE="${e2eTestToRun}" ${e2eTestParameters} \\ -v ${WORKSPACE}/tests/e2e:/tmp/e2e:Z \\ quay.io/eclipse/che-e2e:nightly """ diff --git a/tests/.infra/crw-ci/pre-release-testing/k8s/Jenkinsfile b/tests/.infra/crw-ci/pre-release-testing/k8s/Jenkinsfile index 0ce6a5fb59..285df52475 100644 --- a/tests/.infra/crw-ci/pre-release-testing/k8s/Jenkinsfile +++ b/tests/.infra/crw-ci/pre-release-testing/k8s/Jenkinsfile @@ -116,7 +116,10 @@ pipeline { value: "$customResourceFileContent"), booleanParam(name: 'createTestWorkspace', - value: true) + value: true), + + string(name: 'e2eTestParameters', + value: '') ] } } @@ -152,11 +155,53 @@ pipeline { value: "$customResourceFileContent"), booleanParam(name: 'createTestWorkspace', - value: false) + value: false), + + string(name: 'e2eTestParameters', + value: '') ] } } } + + stage('Run Git SSH flow tests') { + steps { + withCredentials([string(credentialsId: 'e45af3e6-8061-4d02-b187-b1c3bb133d3a', variable: 'github_oauth_token')]) { + build job: 'basic-MultiUser-Che-check-e2e-tests-against-k8s', + parameters: [ + string(name: 'cheImageRepo', + value: 'quay.io/eclipse/che-server'), + + string(name: 'cheImageTag', + value: 'nightly'), + + booleanParam(name: 'buildChe', + value: false), + + string(name: 'ghprbSourceBranch', + value: 'CRW-391'), + + string(name: 'ghprbPullId', + value: ''), + + string(name: 'e2eTestToRun', + value: 'test-git-ssh'), + + string(name: 'testWorkspaceDevfileUrl', + value: ''), + + text(name: 'customResourceFileContent', + value: ''), + + booleanParam(name: 'createTestWorkspace', + value: false), + + string(name: 'e2eTestParameters', + value: "-e TS_GITHUB_TEST_REPO_ACCESS_TOKEN=$github_oauth_token -e TS_GITHUB_TEST_REPO=chepullreq4/Spoon-Knife -e NODE_TLS_REJECT_UNAUTHORIZED=0") + ] + } + } + } } } } diff --git a/tests/e2e/TestConstants.ts b/tests/e2e/TestConstants.ts index 2684dab6c1..82aac77b14 100644 --- a/tests/e2e/TestConstants.ts +++ b/tests/e2e/TestConstants.ts @@ -89,11 +89,6 @@ export const TestConstants = { */ TS_SELENIUM_PLUGIN_PRECENCE_ATTEMPTS: Number(process.env.TS_SELENIUM_PLUGIN_PRECENCE_ATTEMPTS) || 20, - /** - * Delay in milliseconds between checking plugin precence. - */ - TS_SELENIUM_PLUGIN_PRECENCE_POLLING: Number(process.env.TS_SELENIUM_PLUGIN_PRECENCE_POLLING) || 2000, - /** * Name of workspace created for 'Happy Path' scenario validation. */ @@ -228,5 +223,16 @@ export const TestConstants = { /** * Running test suite - possible variants can be found in package.json scripts part. */ - TEST_SUITE: process.env.TEST_SUITE || 'test-happy-path' + TEST_SUITE: process.env.TEST_SUITE || 'test-happy-path', + + /** + * The repo (with README.md in root) and access token are needed for to run test-git-ssh + */ + TS_GITHUB_TEST_REPO: process.env.TS_GITHUB_TEST_REPO || '', + + /** + * Token for a github repository with permissions which allow add the ssh keys + */ + TS_GITHUB_TEST_REPO_ACCESS_TOKEN: process.env.TS_GITHUB_TEST_REPO_ACCESS_TOKEN || '' + }; diff --git a/tests/e2e/driver/CheReporter.ts b/tests/e2e/driver/CheReporter.ts index 535b8036e2..72030d5ba2 100644 --- a/tests/e2e/driver/CheReporter.ts +++ b/tests/e2e/driver/CheReporter.ts @@ -48,7 +48,7 @@ class CheReporter extends mocha.reporters.Spec { TS_SELENIUM_WORKSPACE_STATUS_ATTEMPTS: ${TestConstants.TS_SELENIUM_WORKSPACE_STATUS_ATTEMPTS} TS_SELENIUM_WORKSPACE_STATUS_POLLING: ${TestConstants.TS_SELENIUM_WORKSPACE_STATUS_POLLING} TS_SELENIUM_PLUGIN_PRECENCE_ATTEMPTS: ${TestConstants.TS_SELENIUM_PLUGIN_PRECENCE_ATTEMPTS} - TS_SELENIUM_PLUGIN_PRECENCE_POLLING: ${TestConstants.TS_SELENIUM_PLUGIN_PRECENCE_POLLING} + TS_SELENIUM_PLUGIN_PRECENCE_POLLING: ${TestConstants.TS_SELENIUM_DEFAULT_POLLING} TS_SELENIUM_HAPPY_PATH_WORKSPACE_NAME: ${TestConstants.TS_SELENIUM_HAPPY_PATH_WORKSPACE_NAME} TS_SELENIUM_USERNAME: ${TestConstants.TS_SELENIUM_USERNAME} TS_SELENIUM_PASSWORD: ${TestConstants.TS_SELENIUM_PASSWORD} diff --git a/tests/e2e/index.ts b/tests/e2e/index.ts index 3dbea3abb0..c6b8b70fde 100644 --- a/tests/e2e/index.ts +++ b/tests/e2e/index.ts @@ -16,6 +16,8 @@ export * from './utils/requestHandlers/headers/IAuthorizationHeaderHandler'; export * from './utils/requestHandlers/tokens/CheMultiuserTokenHandler'; export * from './utils/requestHandlers/tokens/ITokenHandler'; export * from './utils/ScreenCatcher'; +export * from './utils/VCS/CheGitApi'; +export * from './utils/VCS/github/GitHubUtil'; export * from './utils/workspace/ITestWorkspaceUtil'; export * from './utils/workspace/TestWorkspaceUtil'; export * from './utils/workspace/WorkspaceStatus'; @@ -28,7 +30,7 @@ export * from './pageobjects/ide/ContextMenu'; export * from './pageobjects/ide/DebugView'; export * from './pageobjects/ide/DialogWindow'; export * from './pageobjects/ide/Editor'; -export * from './pageobjects/ide/GitHubPlugin'; +export * from './pageobjects/ide/GitPlugin'; export * from './pageobjects/ide/Ide'; export * from './pageobjects/ide/NotificationCenter'; export * from './pageobjects/ide/OpenWorkspaceWidget'; diff --git a/tests/e2e/inversify.config.ts b/tests/e2e/inversify.config.ts index 675d23ea3a..b215d370e4 100644 --- a/tests/e2e/inversify.config.ts +++ b/tests/e2e/inversify.config.ts @@ -34,7 +34,7 @@ import { Editor } from './pageobjects/ide/Editor'; import { TopMenu } from './pageobjects/ide/TopMenu'; import { QuickOpenContainer } from './pageobjects/ide/QuickOpenContainer'; import { PreviewWidget } from './pageobjects/ide/PreviewWidget'; -import { GitHubPlugin } from './pageobjects/ide/GitHubPlugin'; +import { GitPlugin } from './pageobjects/ide/GitPlugin'; import { RightToolbar } from './pageobjects/ide/RightToolbar'; import { Terminal } from './pageobjects/ide/Terminal'; import { DebugView } from './pageobjects/ide/DebugView'; @@ -52,7 +52,8 @@ import { CheMultiuserTokenHandler } from './utils/requestHandlers/tokens/CheMult import { CheSingleUserAuthorizationHeaderHandler } from './utils/requestHandlers/headers/CheSingleUserAuthorizationHeaderHandler'; import { ITokenHandler } from './utils/requestHandlers/tokens/ITokenHandler'; import { CheApiRequestHandler } from './utils/requestHandlers/CheApiRequestHandler'; - +import { CheGitApi } from './utils/VCS/CheGitApi'; +import { GitHubUtil} from './utils/VCS/github/GitHubUtil'; const e2eContainer: Container = new Container(); @@ -89,7 +90,7 @@ e2eContainer.bind(CLASSES.Editor).to(Editor).inSingletonScope(); e2eContainer.bind(CLASSES.TopMenu).to(TopMenu).inSingletonScope(); e2eContainer.bind(CLASSES.QuickOpenContainer).to(QuickOpenContainer).inSingletonScope(); e2eContainer.bind(CLASSES.PreviewWidget).to(PreviewWidget).inSingletonScope(); -e2eContainer.bind(CLASSES.GitHubPlugin).to(GitHubPlugin).inSingletonScope(); +e2eContainer.bind(CLASSES.GitPlugin).to(GitPlugin).inSingletonScope(); e2eContainer.bind(CLASSES.RightToolbar).to(RightToolbar).inSingletonScope(); e2eContainer.bind(CLASSES.Terminal).to(Terminal).inSingletonScope(); e2eContainer.bind(CLASSES.DebugView).to(DebugView).inSingletonScope(); @@ -102,5 +103,6 @@ e2eContainer.bind(CLASSES.CheLoginPage).to(CheLoginPage).inSinglet e2eContainer.bind(CLASSES.NotificationCenter).to(NotificationCenter).inSingletonScope(); e2eContainer.bind(CLASSES.PreferencesHandler).to(PreferencesHandler).inSingletonScope(); e2eContainer.bind(CLASSES.CheApiRequestHandler).to(CheApiRequestHandler).inSingletonScope(); - +e2eContainer.bind(CLASSES.CheGitApi).to(CheGitApi).inSingletonScope(); +e2eContainer.bind(CLASSES.GitHubUtil).to(GitHubUtil).inSingletonScope(); export { e2eContainer }; diff --git a/tests/e2e/inversify.types.ts b/tests/e2e/inversify.types.ts index 45a4de9263..71e521f998 100644 --- a/tests/e2e/inversify.types.ts +++ b/tests/e2e/inversify.types.ts @@ -44,6 +44,9 @@ const CLASSES = { OpenWorkspaceWidget: 'OpenWorkspaceWidget', ContextMenu: 'ContextMenu', CheLoginPage: 'CheLoginPage', + GitHubUtil: 'GitHubUtil', + CheGitApi: 'CheGitApi', + GitPlugin: 'GitPlugin', TestWorkspaceUtil: 'TestWorkspaceUtil', NotificationCenter: 'NotificationCenter', PreferencesHandler: 'PreferencesHandler', diff --git a/tests/e2e/mocha-git-ssh.opts b/tests/e2e/mocha-git-ssh.opts new file mode 100644 index 0000000000..2a730d1db1 --- /dev/null +++ b/tests/e2e/mocha-git-ssh.opts @@ -0,0 +1,7 @@ +--timeout 2200000 +--reporter 'dist/driver/CheReporter.js' +-u tdd +--bail +--full-trace +--spec dist/tests/e2e/GitSsh.spec.js +--require source-map-support/register diff --git a/tests/e2e/package.json b/tests/e2e/package.json index bdef51f5b3..a23eca92c1 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -14,6 +14,7 @@ "test-operatorhub-installation": "./generateIndex.sh && npm run lint && npm run tsc && mocha --opts mocha-che-operatorhub.opts", "test-wkspc-creation-and-ls": "./generateIndex.sh && npm run lint && npm run tsc && mocha --opts mocha-wkspc-creation-and-ls.opts", "test-java-vertx": "./generateIndex.sh && npm run lint && npm run tsc && mocha --opts mocha-java-vertx.opts", + "test-git-ssh": "./generateIndex.sh && npm run lint && npm run tsc && mocha --opts mocha-git-ssh.opts", "test-all-devfiles": "./generateIndex.sh && npm run lint && npm run tsc && mocha --opts mocha-all-devfiles.opts", "lint": "tslint --fix -p .", "tsc": "tsc -p ." @@ -35,7 +36,8 @@ "ts-node": "^8.0.3", "tslint": "5.10.0", "typed-rest-client": "^1.2.0", - "typescript": "^3.4.3" + "typescript": "^3.4.3", + "@eclipse-che/api": "^7.5.0-SNAPSHOT" }, "dependencies": { "inversify": "^5.0.1", diff --git a/tests/e2e/pageobjects/ide/GitHubPlugin.ts b/tests/e2e/pageobjects/ide/GitHubPlugin.ts deleted file mode 100644 index 312941f32a..0000000000 --- a/tests/e2e/pageobjects/ide/GitHubPlugin.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { injectable, inject } from 'inversify'; -import { CLASSES } from '../../inversify.types'; -import { DriverHelper } from '../../utils/DriverHelper'; -import { TestConstants } from '../../TestConstants'; -import { By, WebElement } from 'selenium-webdriver'; -import { Ide, RightToolbarButton } from './Ide'; -import { Logger } from '../../utils/Logger'; - -/********************************************************************* - * 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 - **********************************************************************/ - -@injectable() -export class GitHubPlugin { - constructor(@inject(CLASSES.DriverHelper) private readonly driverHelper: DriverHelper, - @inject(CLASSES.Ide) private readonly ide: Ide) { } - - async openGitHubPluginContainer(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { - Logger.debug('GitHubPlugin.openGitHubPluginContainer'); - - const selectedGitButtonLocator: By = By.xpath(Ide.SELECTED_GIT_BUTTON_XPATH); - - await this.ide.waitRightToolbarButton(RightToolbarButton.Git, timeout); - const isButtonEnabled: boolean = await this.driverHelper.waitVisibilityBoolean(selectedGitButtonLocator); - - if (!isButtonEnabled) { - await this.ide.waitAndClickRightToolbarButton(RightToolbarButton.Git); - } - - await this.waitGitHubContainer(timeout); - } - - async waitGitHubContainer(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { - Logger.debug('GitHubPlugin.waitGitHubContainer'); - - const githubContainerLocator: By = By.css('#theia-gitContainer .theia-git-main-container'); - - await this.driverHelper.waitVisibility(githubContainerLocator, timeout); - } - - async getChangesList(): Promise { - Logger.debug('GitHubPlugin.getChangesList'); - - const gitHubChangesLocator: By = By.xpath('//div[@id=\'theia-gitContainer\']//div[@id=\'unstagedChanges\']//div[contains(@class, \'gitItem\')]'); - const changesElements: WebElement[] = await this.driverHelper.waitAllPresence(gitHubChangesLocator); - const changesCount: number = changesElements.length; - let gitHubChanges: string[] = []; - - for (let i = 0; i < changesCount; i++) { - const gitHubChangesItemLocator: By = By.xpath(this.getGitHubChangesItemXpathLocator(i)); - const changesText: string = await this.driverHelper.waitAndGetText(gitHubChangesItemLocator); - - gitHubChanges.push(changesText); - } - - return gitHubChanges; - } - - async waitChangesPresence(changesText: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { - Logger.debug(`GitHubPlugin.waitChangesPresence "${changesText}"`); - - await this.driverHelper - .getDriver() - .wait(async () => { - const changes: string[] = await this.getChangesList(); - const isChangesPresent: boolean = changes.indexOf(changesText) !== -1; - - if (isChangesPresent) { - return true; - } - - }, timeout); - } - - private getGitHubChangesItemXpathLocator(index: number): string { - return `(//div[@id='theia-gitContainer']//div[@id='unstagedChanges']//div[contains(@class, 'gitItem')])[${index + 1}]`; - } - -} diff --git a/tests/e2e/pageobjects/ide/GitPlugin.ts b/tests/e2e/pageobjects/ide/GitPlugin.ts new file mode 100644 index 0000000000..be3e479169 --- /dev/null +++ b/tests/e2e/pageobjects/ide/GitPlugin.ts @@ -0,0 +1,97 @@ +import { injectable, inject } from 'inversify'; +import { CLASSES } from '../../inversify.types'; +import { DriverHelper } from '../../utils/DriverHelper'; +import { TestConstants } from '../../TestConstants'; +import { By } from 'selenium-webdriver'; +import { Logger } from '../../utils/Logger'; + +/********************************************************************* + * 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 + **********************************************************************/ + +@injectable() +export class GitPlugin { + private static readonly COMMIT_MESSAGE_TEXTAREA_CSS: string = 'textarea#theia-scm-input-message'; + + constructor(@inject(CLASSES.DriverHelper) private readonly driverHelper: DriverHelper) { } + + async openGitPluginContainer(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + Logger.debug('GitPlugin.openGitPluginContainer'); + + const sourceControlGitBtnXpathLocator: string = '//li[@id=\'shell-tab-scm-view-container\' and contains(@style, \'height\')]'; + await this.driverHelper.waitAndClick(By.xpath(sourceControlGitBtnXpathLocator), timeout); + await this.waitViewOfContainer(timeout); + } + + async waitViewOfContainer(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + Logger.debug('GitPlugin.waitViewOfContainer'); + + const gitHubContainerIdLocator: By = By.id('scm-view-container--scm-view'); + await this.driverHelper.waitVisibility(gitHubContainerIdLocator, timeout); + } + + async waitCommitMessageTextArea(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + Logger.debug('GitPlugin.waitCommitMessageTextArea'); + + const textAreaCssLocator: By = By.css(GitPlugin.COMMIT_MESSAGE_TEXTAREA_CSS); + await this.driverHelper.waitVisibility(textAreaCssLocator, timeout); + } + + async typeCommitMessage(commitMessage: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + Logger.debug('GitPlugin.typeCommitMessage'); + + await this.waitCommitMessageTextArea(timeout); + await this.driverHelper.type(By.css(GitPlugin.COMMIT_MESSAGE_TEXTAREA_CSS), commitMessage, timeout); + } + + async selectCommandInMoreActionsMenu(commandName: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + Logger.debug('GitPlugin.selectCommandInMoreActionsMenu'); + + await this.clickOnMoreActions(timeout); + await this.driverHelper.waitAndClick(By.xpath(`//li[@data-command]/div[text()=\'${commandName}\']`), timeout); + } + + async clickOnMoreActions(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + Logger.debug('GitPlugin.clickOnMoreActions'); + + await this.driverHelper.waitAndClick(By.id('__more__'), timeout); + } + + async waitChangedFileInChagesList(expectedItem: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + Logger.debug('GitPlugin.waitChangedFileInChagesList'); + + await this.driverHelper.waitPresence(By.xpath(`//div[@class='changesContainer']//span[text()=\'${expectedItem}\']`), timeout); + } + + async waitStagedFileInStagedChanges(expectedStagedItem: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + Logger.debug('GitPlugin.waitStagedFileInStagedChanges'); + + await this.driverHelper.waitPresence(By.xpath(`//div[text()='Staged Changes']/parent::div/following-sibling::div//span[text()=\'${expectedStagedItem}\']`), timeout); + } + + async commitFromScmView(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + Logger.debug('GitPlugin.commitFromScmView'); + + await this.driverHelper.waitAndClick(By.id('__scm-view-container_title:__plugin.scm.title.action.git.commit'), timeout); + } + + async stageAllChanges(expectedStagedItem: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + Logger.debug('GitPlugin.stageAllChanges'); + + await this.driverHelper.scrollTo(By.xpath('//div[@class=\'changesContainer\']//div[text()=\'Changes\']'), timeout); + await this.driverHelper.waitAndClick(By.xpath('//a[@title=\'Stage All Changes\']'), timeout); + await this.waitStagedFileInStagedChanges(expectedStagedItem); + } + + async waitDataIsSynchronized(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { + Logger.debug('GitPlugin.waitDataIsSynchronized'); + await this.driverHelper.waitDisappearance(By.xpath(`//div[contains(@title,'Synchronize Changes')]//span[contains(.,' 0↓')]`), timeout); + } + +} diff --git a/tests/e2e/pageobjects/ide/OpenWorkspaceWidget.ts b/tests/e2e/pageobjects/ide/OpenWorkspaceWidget.ts index 3bbfc0e0be..cbbf3b15c5 100644 --- a/tests/e2e/pageobjects/ide/OpenWorkspaceWidget.ts +++ b/tests/e2e/pageobjects/ide/OpenWorkspaceWidget.ts @@ -18,7 +18,6 @@ import { Logger } from '../../utils/Logger'; 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) { } @@ -63,12 +62,4 @@ export class OpenWorkspaceWidget { await this.driverHelper.waitAndClick(By.id(`/${currentPath}`)); } } - - async selectRootWorkspaceItemInDropDawn(rootProject: string) { - Logger.debug(`OpenWorkspaceWidget.selectRootWorkspaceItemInDropDawn "${rootProject}"`); - - await this.driverHelper.waitAndClick(By.css(OpenWorkspaceWidget.THEIA_LOCATION_LIST_CSS)); - await this.driverHelper.waitAndClick(By.css(`option[value=\'file:///${rootProject}']`)); - } - } diff --git a/tests/e2e/pageobjects/ide/QuickOpenContainer.ts b/tests/e2e/pageobjects/ide/QuickOpenContainer.ts index d865f180df..829b23e940 100644 --- a/tests/e2e/pageobjects/ide/QuickOpenContainer.ts +++ b/tests/e2e/pageobjects/ide/QuickOpenContainer.ts @@ -36,8 +36,7 @@ export class QuickOpenContainer { public async clickOnContainerItem(itemText: string, timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) { Logger.debug(`QuickOpenContainer.clickOnContainerItem "${itemText}"`); - const quickContainerItemLocator: By = By.xpath(`//div[@class='quick-open-entry']//span[text()='${itemText}']`); - + const quickContainerItemLocator: By = By.css(`div[aria-label="${itemText}, picker"]`); await this.waitContainer(timeout); await this.driverHelper.waitAndClick(quickContainerItemLocator, timeout); await this.waitContainerDisappearance(); @@ -45,8 +44,14 @@ export class QuickOpenContainer { public async type(text: string) { Logger.debug(`QuickOpenContainer.type "${text}"`); - await this.driverHelper.enterValue(By.css('.quick-open-input input'), text); } + public async typeAndSelectSuggestion(text: string, suggestedText: string) { + Logger.debug('QuickOpenContainer.typeAndSelectSuggestion'); + + await this.driverHelper.type(By.css('div.monaco-inputbox input.input'), text); + await this.clickOnContainerItem(suggestedText); + } + } diff --git a/tests/e2e/tests/e2e/GitSsh.spec.ts b/tests/e2e/tests/e2e/GitSsh.spec.ts new file mode 100644 index 0000000000..6daf1065ad --- /dev/null +++ b/tests/e2e/tests/e2e/GitSsh.spec.ts @@ -0,0 +1,127 @@ +/********************************************************************* + * 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 { assert } from 'chai'; +import { test } from 'mocha'; +import { e2eContainer } from '../../inversify.config'; +import { CLASSES, TYPES } from '../../inversify.types'; +import { Editor } from '../../pageobjects/ide/Editor'; +import { GitPlugin } from '../../pageobjects/ide/GitPlugin'; +import { Ide } from '../../pageobjects/ide/Ide'; +import { ProjectTree } from '../../pageobjects/ide/ProjectTree'; +import { QuickOpenContainer } from '../../pageobjects/ide/QuickOpenContainer'; +import { ICheLoginPage } from '../../pageobjects/login/ICheLoginPage'; +import { TestConstants } from '../../TestConstants'; +import { DriverHelper } from '../../utils/DriverHelper'; +import { NameGenerator } from '../../utils/NameGenerator'; +import { CheGitApi } from '../../utils/VCS/CheGitApi'; +import { GitHubUtil } from '../../utils/VCS/github/GitHubUtil'; +import { TestWorkspaceUtil } from '../../utils/workspace/TestWorkspaceUtil'; +import { TopMenu } from '../../pageobjects/ide/TopMenu'; + + + +const driverHelper: DriverHelper = e2eContainer.get(CLASSES.DriverHelper); +const ide: Ide = e2eContainer.get(CLASSES.Ide); +const quickOpenContainer: QuickOpenContainer = e2eContainer.get(CLASSES.QuickOpenContainer); +const editor: Editor = e2eContainer.get(CLASSES.Editor); +const namespace: string = TestConstants.TS_SELENIUM_USERNAME; +const workspaceName: string = TestConstants.TS_SELENIUM_HAPPY_PATH_WORKSPACE_NAME; +const topMenu: TopMenu = e2eContainer.get(CLASSES.TopMenu); +const loginPage: ICheLoginPage = e2eContainer.get(TYPES.CheLogin); +const gitHubUtils: GitHubUtil = e2eContainer.get(CLASSES.GitHubUtil); +const cheGitAPI: CheGitApi = e2eContainer.get(CLASSES.CheGitApi); +const projectTree: ProjectTree = e2eContainer.get(CLASSES.ProjectTree); +const gitPlugin: GitPlugin = e2eContainer.get(CLASSES.GitPlugin); +const testWorkspaceUtils: TestWorkspaceUtil = e2eContainer.get(TYPES.WorkspaceUtil); + + +suite('Git with ssh workflow', async () => { + const workspacePrefixUrl: string = `${TestConstants.TS_SELENIUM_BASE_URL}/dashboard/#/ide/TestConstants.TS_SELENIUM_USERNAME/`; + const wsNameCheckGeneratingKeys = 'checkGeneraringSsh'; + const wsNameCheckPropagatingKeys = 'checkPropagatingSsh'; + const committedFile = 'README.md'; + + suiteSetup(async function () { + const wsConfig = await testWorkspaceUtils.getBaseDevfile(); + wsConfig.metadata!.name = wsNameCheckGeneratingKeys; + await testWorkspaceUtils.createWsFromDevFile(wsConfig); + }); + + test('Login into workspace and open tree container', async () => { + await driverHelper.navigateToUrl(workspacePrefixUrl + wsNameCheckGeneratingKeys); + await loginPage.login(); + await ide.waitWorkspaceAndIde(namespace, workspaceName); + await projectTree.openProjectTreeContainer(); + }); + + test('Generate a SSH key', async () => { + await topMenu.selectOption('View', 'Find Command...'); + await quickOpenContainer.typeAndSelectSuggestion('SSH', 'SSH: generate key pair...'); + await ide.waitNotificationAndClickOnButton('Key pair successfully generated, do you want to view the public key', 'View'); + await editor.waitEditorOpened('Untitled-0'); + await editor.waitText('Untitled-0', 'ssh-rsa'); + }); + + + test('Add a SSH key to GitHub side and clone by ssh link', async () => { + const sshName: string = NameGenerator.generate('test-SSH-', 5); + const publicSshKey = await cheGitAPI.getPublicSSHKey(); + await gitHubUtils.addPublicSshKeyToUserAccount(TestConstants.TS_GITHUB_TEST_REPO_ACCESS_TOKEN, sshName, publicSshKey); + await cloneTestRepo(); + + }); + + test('Change commit and push', async function changeCommitAndPushFunc() { + const currentDate: string = Date.now().toString(); + await projectTree.expandPathAndOpenFile('Spoon-Knife', committedFile); + await editor.type(committedFile, currentDate + '\n', 1); + await gitPlugin.openGitPluginContainer(); + await gitPlugin.waitChangedFileInChagesList(committedFile); + await gitPlugin.stageAllChanges(committedFile); + await gitPlugin.waitChangedFileInChagesList(committedFile); + await gitPlugin.typeCommitMessage(this.test!.title + currentDate); + await gitPlugin.commitFromScmView(); + await gitPlugin.selectCommandInMoreActionsMenu('Push'); + await gitPlugin.waitDataIsSynchronized(); + const rawDataFromFile: string = await gitHubUtils.getRawContentFromFile(TestConstants.TS_GITHUB_TEST_REPO + '/master/' + committedFile); + assert.isTrue(rawDataFromFile.includes(currentDate)); + await testWorkspaceUtils.cleanUpAllWorkspaces(); + }); + + test('Check ssh key in a new workspace', async () => { + const data = await testWorkspaceUtils.getBaseDevfile(); + + data.metadata!.name = wsNameCheckPropagatingKeys; + await testWorkspaceUtils.createWsFromDevFile(data); + await driverHelper.navigateToUrl(workspacePrefixUrl + wsNameCheckPropagatingKeys); + await ide.waitWorkspaceAndIde(namespace, workspaceName); + await projectTree.openProjectTreeContainer(); + await cloneTestRepo(); + await projectTree.waitItem('Spoon-Knife'); + }); + +}); + +suite('Cleanup', async () => { + test('Remove test workspace', async () => { + await testWorkspaceUtils.cleanUpAllWorkspaces(); + }); +}); + +async function cloneTestRepo() { + const sshLinkToRepo: string = 'git@github.com:' + TestConstants.TS_GITHUB_TEST_REPO + '.git'; + const confirmMessage = 'Repository URL (Press \'Enter\' to confirm your input or \'Escape\' to cancel)'; + + await topMenu.selectOption('View', 'Find Command...'); + await quickOpenContainer.typeAndSelectSuggestion('clone', 'Git: Clone'); + await quickOpenContainer.typeAndSelectSuggestion(sshLinkToRepo, confirmMessage); +} + diff --git a/tests/e2e/utils/DriverHelper.ts b/tests/e2e/utils/DriverHelper.ts index 81a10a47ff..adf0bd4a06 100644 --- a/tests/e2e/utils/DriverHelper.ts +++ b/tests/e2e/utils/DriverHelper.ts @@ -107,7 +107,6 @@ export class DriverHelper { await this.wait(polling); continue; } - throw err; } } diff --git a/tests/e2e/utils/VCS/CheGitApi.ts b/tests/e2e/utils/VCS/CheGitApi.ts new file mode 100644 index 0000000000..854583e1ce --- /dev/null +++ b/tests/e2e/utils/VCS/CheGitApi.ts @@ -0,0 +1,26 @@ +import { injectable, inject } from 'inversify'; +import { CLASSES } from '../../inversify.types'; +import { CheApiRequestHandler } from '../../utils/requestHandlers/CheApiRequestHandler'; + + +@injectable() +export class CheGitApi { + static readonly GIT_API_ENTRIPOINT_URL = 'api/ssh/vcs'; + + constructor(@inject(CLASSES.CheApiRequestHandler) private readonly processRequestHandler: CheApiRequestHandler) { } + + + + public async getPublicSSHKey(): Promise { + + try { + const responce = await this.processRequestHandler.get(CheGitApi.GIT_API_ENTRIPOINT_URL); + return responce.data[0].publicKey; + } catch (error) { + console.error('Cannot get public ssh key with API \n' + error); + throw error; + } + + } + +} diff --git a/tests/e2e/utils/VCS/github/GitHubUtil.ts b/tests/e2e/utils/VCS/github/GitHubUtil.ts new file mode 100644 index 0000000000..5632335c60 --- /dev/null +++ b/tests/e2e/utils/VCS/github/GitHubUtil.ts @@ -0,0 +1,47 @@ +import { injectable } from 'inversify'; +import axios from 'axios'; + +@injectable() +export class GitHubUtil { + private static readonly GITHUB_API_ENTRIPOINT_URL = 'https://api.github.com/'; + /** + * add public part of ssh key to the defied github account + * @param authToken + * @param title + * @param key + */ + async addPublicSshKeyToUserAccount(authToken: string, title: string, key: string) { + const gitHubApiSshURL: string = GitHubUtil.GITHUB_API_ENTRIPOINT_URL + 'user/keys'; + const authHeader = { headers: { 'Authorization': 'token ' + authToken, 'Content-Type': 'application/json' } }; + + const data = { + title: `${title}`, + key: `${key}` + }; + + try { await axios.post(gitHubApiSshURL, JSON.stringify(data), authHeader); } catch (error) { + console.error('Cannot add the public key to the GitHub account: '); + console.error(error); + throw error; + } + } + + async getRawContentFromFile(pathToFile: string): Promise { + const gitHubContentEntryPointUrl: string = 'https://raw.githubusercontent.com/'; + const pathToRawContent: string = `${gitHubContentEntryPointUrl}${pathToFile}`; + const authorization: string = 'Authorization'; + const contentType: string = 'Content-Type'; + + try { + delete axios.defaults.headers.common[authorization]; + delete axios.defaults.headers.common[contentType]; + const response = await axios.get(`${gitHubContentEntryPointUrl}${pathToFile}`); + return response.data; + } catch (error) { + console.error('Cannot get content form the raw github content: ' + pathToRawContent); + console.error(error); + throw error; + } + } + +} diff --git a/tests/e2e/utils/requestHandlers/CheApiRequestHandler.ts b/tests/e2e/utils/requestHandlers/CheApiRequestHandler.ts index b67745567d..4d9ab66fa9 100644 --- a/tests/e2e/utils/requestHandlers/CheApiRequestHandler.ts +++ b/tests/e2e/utils/requestHandlers/CheApiRequestHandler.ts @@ -22,7 +22,7 @@ export class CheApiRequestHandler { return await axios.get(this.assembleUrl(relativeUrl), await this.headerHandler.get()); } - async post(relativeUrl: string, data?: string): Promise { + async post(relativeUrl: string, data?: string | any ): Promise { return await axios.post(this.assembleUrl(relativeUrl), data, await this.headerHandler.get()); } diff --git a/tests/e2e/utils/workspace/TestWorkspaceUtil.ts b/tests/e2e/utils/workspace/TestWorkspaceUtil.ts index c7c8a392f8..17c226804d 100644 --- a/tests/e2e/utils/workspace/TestWorkspaceUtil.ts +++ b/tests/e2e/utils/workspace/TestWorkspaceUtil.ts @@ -8,39 +8,35 @@ * SPDX-License-Identifier: EPL-2.0 **********************************************************************/ +import { che } from '@eclipse-che/api'; import { TestConstants } from '../../TestConstants'; import { injectable, inject } from 'inversify'; import { DriverHelper } from '../DriverHelper'; -import { CLASSES } from '../../inversify.types'; import 'reflect-metadata'; import { WorkspaceStatus } from './WorkspaceStatus'; import { ITestWorkspaceUtil } from './ITestWorkspaceUtil'; -import axios from 'axios'; -import querystring from 'querystring'; import { error } from 'selenium-webdriver'; -enum RequestType { - GET, - POST, - DELETE -} +import { CheApiRequestHandler } from '../requestHandlers/CheApiRequestHandler'; +import { CLASSES } from '../../inversify.types'; @injectable() export class TestWorkspaceUtil implements ITestWorkspaceUtil { - workspaceApiUrl: string = `${TestConstants.TS_SELENIUM_BASE_URL}/api/workspace`; + static readonly WORKSPACE_API_URL: string = 'api/workspace'; - constructor(@inject(CLASSES.DriverHelper) private readonly driverHelper: DriverHelper) { - - } + constructor( + @inject(CLASSES.DriverHelper) private readonly driverHelper: DriverHelper, + @inject(CLASSES.CheApiRequestHandler) private readonly processRequestHandler: CheApiRequestHandler + ) { } public async waitWorkspaceStatus(namespace: string, workspaceName: string, expectedWorkspaceStatus: WorkspaceStatus) { - const workspaceStatusApiUrl: string = `${this.workspaceApiUrl}/${namespace}:${workspaceName}`; + const workspaceStatusApiUrl: string = `${TestWorkspaceUtil.WORKSPACE_API_URL}/${namespace}:${workspaceName}`; const attempts: number = TestConstants.TS_SELENIUM_WORKSPACE_STATUS_ATTEMPTS; const polling: number = TestConstants.TS_SELENIUM_WORKSPACE_STATUS_POLLING; let workspaceStatus: string = ''; for (let i = 0; i < attempts; i++) { - const response = await this.processRequest(RequestType.GET, workspaceStatusApiUrl); + const response = await this.processRequestHandler.get(workspaceStatusApiUrl); if (response.status !== 200) { await this.driverHelper.wait(polling); @@ -60,12 +56,12 @@ export class TestWorkspaceUtil implements ITestWorkspaceUtil { } public async waitPluginAdding(namespace: string, workspaceName: string, pluginName: string) { - const workspaceStatusApiUrl: string = `${this.workspaceApiUrl}/${namespace}:${workspaceName}`; + const workspaceStatusApiUrl: string = `${TestWorkspaceUtil.WORKSPACE_API_URL}/${namespace}:${workspaceName}`; const attempts: number = TestConstants.TS_SELENIUM_PLUGIN_PRECENCE_ATTEMPTS; - const polling: number = TestConstants.TS_SELENIUM_PLUGIN_PRECENCE_POLLING; + const polling: number = TestConstants.TS_SELENIUM_DEFAULT_POLLING; for (let i = 0; i < attempts; i++) { - const response = await this.processRequest(RequestType.GET, workspaceStatusApiUrl); + const response = await this.processRequestHandler.get(workspaceStatusApiUrl); if (response.status !== 200) { await this.driverHelper.wait(polling); @@ -87,25 +83,34 @@ export class TestWorkspaceUtil implements ITestWorkspaceUtil { } } - public async getListOfWorkspaceId() { - const getAllWorkspacesResponse = await this.processRequest(RequestType.GET, this.workspaceApiUrl); + public async getListOfWorkspaceId(): Promise { + const getAllWorkspacesResponse = await this.processRequestHandler.get(TestWorkspaceUtil.WORKSPACE_API_URL); interface IMyObj { id: string; status: string; } + let stringified = JSON.stringify(getAllWorkspacesResponse.data); let arrayOfWorkspaces = JSON.parse(stringified); let wsList: Array = []; + for (let entry of arrayOfWorkspaces) { wsList.push(entry.id); } + return wsList; } + public async getIdOfRunningWorkspace(wsName: string): Promise { + const getWorkspacesByNameResponse = await this.processRequestHandler.get(`${TestWorkspaceUtil.WORKSPACE_API_URL}/:${wsName}`); + return getWorkspacesByNameResponse.data.id; + + } + public async getIdOfRunningWorkspaces(): Promise> { try { - const getAllWorkspacesResponse = await this.processRequest(RequestType.GET, this.workspaceApiUrl); + const getAllWorkspacesResponse = await this.processRequestHandler.get(TestWorkspaceUtil.WORKSPACE_API_URL); interface IMyObj { id: string; @@ -123,97 +128,46 @@ export class TestWorkspaceUtil implements ITestWorkspaceUtil { return idOfRunningWorkspace; } catch (err) { - console.log(`Getting id of running workspaces failed. URL used: ${this.workspaceApiUrl}`); + console.log(`Getting id of running workspaces failed. URL used: ${TestWorkspaceUtil.WORKSPACE_API_URL}`); throw err; } } - getIdOfRunningWorkspace(namespace: string): Promise { - throw new Error('Method not implemented.'); - } - public async removeWorkspaceById(id: string) { - const workspaceIdUrl: string = `${this.workspaceApiUrl}/${id}`; - const attempts: number = TestConstants.TS_SELENIUM_PLUGIN_PRECENCE_ATTEMPTS; - const polling: number = TestConstants.TS_SELENIUM_PLUGIN_PRECENCE_POLLING; - let stopped: Boolean = false; - - for (let i = 0; i < attempts; i++) { - - const getInfoResponse = await this.processRequest(RequestType.GET, workspaceIdUrl); - - if (getInfoResponse.data.status === 'STOPPED') { - stopped = true; - break; - } - await this.driverHelper.wait(polling); - } - - if (stopped) { - try { - const deleteWorkspaceResponse = await this.processRequest(RequestType.DELETE, workspaceIdUrl); - - // response code 204: "No Content" expected - if (deleteWorkspaceResponse.status !== 204) { - throw new Error(`Can not remove workspace. Code: ${deleteWorkspaceResponse.status} Data: ${deleteWorkspaceResponse.data}`); - } - } catch (err) { - console.log(`Removing of workspace failed.`); - throw err; - } - } else { - throw new Error(`Can not remove workspace with id ${id}, because it is still not in STOPPED state.`); - } - } - - async getCheBearerToken(): Promise { - let params = {}; - - let keycloakUrl = TestConstants.TS_SELENIUM_BASE_URL; - if ( keycloakUrl.substr(7, 4).includes('che')) { - const keycloakAuthSuffix = '/auth/realms/che/protocol/openid-connect/token'; - keycloakUrl = keycloakUrl.replace('che', 'keycloak') + keycloakAuthSuffix; - params = { - client_id: 'che-public', - username: TestConstants.TS_SELENIUM_USERNAME, - password: TestConstants.TS_SELENIUM_PASSWORD, - grant_type: 'password' - }; - } else { - const keycloakAuthSuffix = '/auth/realms/codeready/protocol/openid-connect/token'; - keycloakUrl = keycloakUrl.replace('codeready', 'keycloak') + keycloakAuthSuffix; - params = { - client_id: 'codeready-public', - username: TestConstants.TS_SELENIUM_USERNAME, - password: TestConstants.TS_SELENIUM_PASSWORD, - grant_type: 'password' - }; - } - + const workspaceIdUrl: string = `${TestWorkspaceUtil.WORKSPACE_API_URL}/${id}`; try { - const responseToObtainBearerToken = await axios.post(keycloakUrl, querystring.stringify(params)); - return responseToObtainBearerToken.data.access_token; + const deleteWorkspaceResponse = await this.processRequestHandler.delete(workspaceIdUrl); + // response code 204: "No Content" expected + if (deleteWorkspaceResponse.status !== 204) { + throw new Error(`Can not remove workspace. Code: ${deleteWorkspaceResponse.status} Data: ${deleteWorkspaceResponse.data}`); + } } catch (err) { - console.log(`Can not get bearer token. URL used: ${keycloakUrl}`); + console.log(`Removing of workspace failed.`); throw err; } - } public async stopWorkspaceById(id: string) { - const stopWorkspaceApiUrl: string = `${this.workspaceApiUrl}/${id}/runtime`; - try { - const stopWorkspaceResponse = await this.processRequest(RequestType.DELETE, stopWorkspaceApiUrl); + const stopWorkspaceApiUrl: string = `${TestWorkspaceUtil.WORKSPACE_API_URL}/${id}`; + try { + const stopWorkspaceResponse = await this.processRequestHandler.delete(`${stopWorkspaceApiUrl}/runtime`); // response code 204: "No Content" expected if (stopWorkspaceResponse.status !== 204) { throw new Error(`Can not stop workspace. Code: ${stopWorkspaceResponse.status} Data: ${stopWorkspaceResponse.data}`); } + + for (let i = 0; i < TestConstants.TS_SELENIUM_PLUGIN_PRECENCE_ATTEMPTS; i++) { + const wsStatus = await this.processRequestHandler.get(stopWorkspaceApiUrl); + if (wsStatus.data.status === 'STOPPED') { + break; + } + await this.driverHelper.wait(TestConstants.TS_SELENIUM_DEFAULT_POLLING); + } } catch (err) { console.log(`Stopping workspace failed. URL used: ${stopWorkspaceApiUrl}`); throw err; } - } public async cleanUpAllWorkspaces() { @@ -225,40 +179,29 @@ export class TestWorkspaceUtil implements ITestWorkspaceUtil { let listAllWorkspaces: Array = await this.getListOfWorkspaceId(); for (const entry of listAllWorkspaces) { - this.removeWorkspaceById(entry); + await this.removeWorkspaceById(entry); } } - removeWorkspace(namespace: string, workspaceId: string): void { - throw new Error('Method not implemented.'); + async createWsFromDevFile(customTemplate: che.workspace.devfile.Devfile) { + try { + await this.processRequestHandler.post(TestWorkspaceUtil.WORKSPACE_API_URL + '/devfile', customTemplate); + } catch (error) { + console.error(error); + throw error; + } } - stopWorkspace(namespace: string, workspaceId: string): void { - throw new Error('Method not implemented.'); - } + async getBaseDevfile(): Promise { + const baseDevfile: che.workspace.devfile.Devfile = { + apiVersion: '1.0.0', + metadata: { + name: 'test-workspace' + } + }; - async processRequest(reqType: RequestType, url: string) { - let response; - // maybe this check can be moved somewhere else at the begining so it will be executed just once - if (TestConstants.TS_SELENIUM_MULTIUSER === true) { - let authorization = 'Authorization'; - axios.defaults.headers.common[authorization] = 'Bearer ' + await this.getCheBearerToken(); - } - switch (reqType) { - case RequestType.GET: { - response = await axios.get(url); - break; - } - case RequestType.DELETE: { - response = await axios.delete(url); - break; - } - default: { - throw new Error('Unknown RequestType: ' + reqType); - } - } - return response; + return baseDevfile; } }