diff --git a/test/ui/common/constants.ts b/test/ui/common/constants.ts index 1a9077c2e..06055bafe 100644 --- a/test/ui/common/constants.ts +++ b/test/ui/common/constants.ts @@ -43,6 +43,7 @@ export const MENUS = { deleteProject: 'Delete Project', bindService: 'Bind Service', startDev: 'Start Dev', + stopDev: 'Stop Dev', }; export const COMPONENTS = { diff --git a/test/ui/common/ui/webviewView/openshiftTerminalWebviewView.ts b/test/ui/common/ui/webviewView/openshiftTerminalWebviewView.ts new file mode 100644 index 000000000..3835cbc2b --- /dev/null +++ b/test/ui/common/ui/webviewView/openshiftTerminalWebviewView.ts @@ -0,0 +1,98 @@ +/*----------------------------------------------------------------------------------------------- + * Copyright (c) Red Hat, Inc. All rights reserved. + * Licensed under the MIT License. See LICENSE file in the project root for license information. + *-----------------------------------------------------------------------------------------------*/ +import { By, Key, WebElement, WebviewView } from 'vscode-extension-tester'; +import { WebviewViewForm } from './webviewViewForm'; + +export class OpenshiftTerminalWebviewView extends WebviewViewForm { + + public constructor() { + super(); + } + + public async getTerminalText(): Promise { + const copyKeys = [`${Key.CONTROL}${Key.SHIFT}a`,`${Key.CONTROL}${Key.SHIFT}c`] + await this.sendKeysToTerminal(copyKeys); + const cb = await import('clipboardy'); + return await cb.read(); + } + + public async getActiveTabName(): Promise { + let text: string; + await this.enterWebviewView(async (webviewView) => { + const activeTab = await this.getActiveTab(webviewView); + text = await activeTab.getText(); + }) + return text; + } + + public async closeTab(name: string): Promise { + await this.enterWebviewView(async (webviewView) => { + const closeButton = await webviewView.findWebElement(By.xpath( + `//div[div[contains(text(),"${name}")]]//*[@data-testid="CloseIcon"]` + )); + await closeButton.click(); + }) + } + + public async closeActiveTab(): Promise { + await this.enterWebviewView(async (webviewView) => { + await webviewView.findWebElement(By.xpath('//button[@aria-selected = "true"]//*[@data-testid = "TerminalIcon"]')); + }); + } + + public async closeAllInactiveTabs(): Promise { + await this.enterWebviewView(async (webviewView) => { + const closeButtons = await webviewView.findWebElements(By.xpath('//button[@aria-selected = "false"]//*[@data-testid = "TerminalIcon"]')); + for (const button of closeButtons) { + await button.click(); + } + }) + } + + public async switchToTab(name: string): Promise { + await this.enterWebviewView(async (webviewView) => { + const tabs = await this.getTerminalTabs(webviewView); + for (const tab of tabs) { + const text = await tab.getText(); + if (text === name) { + await tab.click(); + } + } + }); + } + + public async sendKeysToTerminal(keys: string[]): Promise { + await this.enterWebviewView(async (webviewView) => { + const terminal = await this.getTerminalInstance(webviewView); + await terminal.click(); + for (const key of keys) { + await terminal.sendKeys(key); + } + }); + } + + public async isAnyTabOpened(): Promise { + let tabs: WebElement[]; + await this.enterWebviewView(async (webviewView) => { + tabs = await this.getTerminalTabs(webviewView); + }); + return tabs.length > 0; + } + + private async getActiveTab(webviewView: WebviewView): Promise { + return await webviewView.findWebElement(By.xpath('//button[@aria-selected = "true"]//div[*[name() = "svg"]]/div')) + } + + private async getTerminalTabs(webviewView: WebviewView): Promise { + return await webviewView.findWebElements(By.xpath('//div[*[name() = "svg"]]/div')) + } + + private async getTerminalInstance(webviewView: WebviewView): Promise { + return await webviewView.findWebElement(By.xpath('//textarea[@aria-label = "Terminal input"]')); + } + + + +} diff --git a/test/ui/common/ui/webviewView/webviewViewForm.ts b/test/ui/common/ui/webviewView/webviewViewForm.ts new file mode 100644 index 000000000..8fd09fe32 --- /dev/null +++ b/test/ui/common/ui/webviewView/webviewViewForm.ts @@ -0,0 +1,20 @@ +/*----------------------------------------------------------------------------------------------- + * Copyright (c) Red Hat, Inc. All rights reserved. + * Licensed under the MIT License. See LICENSE file in the project root for license information. + *-----------------------------------------------------------------------------------------------*/ +import { WebviewView } from 'vscode-extension-tester'; + +export abstract class WebviewViewForm { + + public async enterWebviewView(callbackFunction: (webviewView: WebviewView) => Promise): Promise { + const webviewView = new WebviewView(); + await webviewView.switchToFrame(); + let retValue: T; + try { + retValue = await callbackFunction(webviewView); + } finally { + await webviewView.switchBack(); + } + return retValue; + } +} diff --git a/test/ui/public-ui-test.ts b/test/ui/public-ui-test.ts index 42d7f6bf7..64c1eeb2d 100644 --- a/test/ui/public-ui-test.ts +++ b/test/ui/public-ui-test.ts @@ -15,6 +15,7 @@ import { checkOpenshiftView } from './suite/openshift'; import { testCreateServerlessFunction } from './suite/serverlessFunction'; import * as sourceMapSupport from 'source-map-support'; +import { testComponentContextMenu } from './suite/componentContextMenu'; sourceMapSupport.install(); @@ -45,6 +46,7 @@ describe('Extension public-facing UI tests', function() { }); checkAboutCommand(clusterIsSet); + testComponentContextMenu(); //tests requiring clusters incoming }); diff --git a/test/ui/suite/command-about.ts b/test/ui/suite/command-about.ts index 554d6dace..a848ca485 100644 --- a/test/ui/suite/command-about.ts +++ b/test/ui/suite/command-about.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See LICENSE file in the project root for license information. *-----------------------------------------------------------------------------------------------*/ -import { By, EditorView, Key, WebElement, WebviewView, Workbench } from 'vscode-extension-tester'; +import { By, EditorView, WebviewView, Workbench } from 'vscode-extension-tester'; import { activateCommand } from '../common/command-activator'; import { expect } from 'chai'; +import { OpenshiftTerminalWebviewView } from '../common/ui/webviewView/openshiftTerminalWebviewView'; export function checkAboutCommand(clusterIsSet: boolean) { describe('About Command', () => { @@ -15,14 +16,18 @@ export function checkAboutCommand(clusterIsSet: boolean) { const clusterServer = 'https://127.0.0.1'; let webviewView: WebviewView; - let terminalInstance: WebElement; + let openshiftTerminal: OpenshiftTerminalWebviewView; before(async () => { await new EditorView().closeAllEditors(); await activateCommand(command); }); - it('New terminal opens', async function () { + after(async () => { + await openshiftTerminal.closeTab('Show odo Version'); + }); + + it('New terminal opens', async function() { this.timeout(60_000); await new Promise((res) => setTimeout(res, 3_000)); await new Workbench().executeCommand( @@ -30,26 +35,23 @@ export function checkAboutCommand(clusterIsSet: boolean) { ); webviewView = new WebviewView(); await webviewView.switchToFrame(6_500); - terminalInstance = await webviewView.findWebElement( + await webviewView.findWebElement( By.xpath('//textarea[@aria-label = "Terminal input"]'), ); + await webviewView.switchBack(); }); - it('Terminal shows according information', async function () { + it('Terminal shows according information', async function() { this.timeout(60_000); - await terminalInstance.click(); + openshiftTerminal = new OpenshiftTerminalWebviewView(); - await terminalInstance.sendKeys(`${Key.CONTROL}${Key.SHIFT}a`); - await terminalInstance.sendKeys(`${Key.CONTROL}${Key.SHIFT}c`); - await webviewView.switchBack(); + const terminalText = await openshiftTerminal.getTerminalText(); - const cb = await import('clipboardy'); - const clipboard = await cb.read(); - expect(clipboard).to.contain(odoVersion); + expect(terminalText).to.contain(odoVersion) if (!clusterIsSet) { - expect(clipboard).to.contain(noClusterMessage); + expect(terminalText).to.contain(noClusterMessage); } else { - expect(clipboard).to.contain(clusterServer); + expect(terminalText).to.contain(clusterServer); } }); }); diff --git a/test/ui/suite/componentContextMenu.ts b/test/ui/suite/componentContextMenu.ts new file mode 100644 index 000000000..ff08749a4 --- /dev/null +++ b/test/ui/suite/componentContextMenu.ts @@ -0,0 +1,118 @@ +/*----------------------------------------------------------------------------------------------- + * Copyright (c) Red Hat, Inc. All rights reserved. + * Licensed under the MIT License. See LICENSE file in the project root for license information. + *-----------------------------------------------------------------------------------------------*/ + +import { + ActivityBar, + EditorView, + Key, + SideBarView, + ViewItem, + ViewSection, +} from 'vscode-extension-tester'; +import { collapse } from '../common/overdrives'; +import { MENUS, VIEWS } from '../common/constants'; +import { itemExists } from '../common/conditions'; +import { OpenshiftTerminalWebviewView } from '../common/ui/webviewView/openshiftTerminalWebviewView'; +import { expect } from 'chai'; + +export function testComponentContextMenu() { + describe('Component Context Menu', function() { + let view: SideBarView; + let section: ViewSection; + let component: ViewItem; + let openshiftTerminal: OpenshiftTerminalWebviewView; + + const componentName = 'nodejs-starter'; + const expectedTerminalName = `odo dev: ${componentName}`; + + before(async function context() { + this.timeout(10_000); + await new EditorView().closeAllEditors(); + view = await (await new ActivityBar().getViewControl(VIEWS.openshift)).openView(); + for (const item of [ + VIEWS.appExplorer, + VIEWS.compRegistries, + VIEWS.serverlessFunctions, + VIEWS.debugSessions, + ]) { + await collapse(await view.getContent().getSection(item)); + } + + section = await view.getContent().getSection(VIEWS.components); + }); + + it('Start Dev works', async function() { + this.timeout(60_000) + //start dev + component = await itemExists(componentName, section); + const contextMenu = await component.openContextMenu(); + await contextMenu.select(MENUS.startDev); + + //check openshift terminal for tab name + openshiftTerminal = new OpenshiftTerminalWebviewView(); + const terminalName = await openshiftTerminal.getActiveTabName(); + expect(terminalName).to.contain(expectedTerminalName) + + //decline odo telemetry + await openshiftTerminal.sendKeysToTerminal(['n', Key.ENTER]) + + //wait for start dev to finish + await itemExists(`${componentName} (dev starting)`, section); + await itemExists(`${componentName} (dev running)`, section, 30_000); + + //check terminal content + const terminalText = await openshiftTerminal.getTerminalText(); + expect(terminalText).to.contain(`Developing using the "${componentName}" Devfile`); + expect(terminalText).to.contain('Running on the cluster in Dev mode'); + expect(terminalText).to.contain('Pod is Running'); + expect(terminalText).to.contain('Waiting for the application to be ready'); + expect(terminalText).to.contain('Keyboard Commands'); + }); + + it('Stop Dev works', async function() { + this.timeout(80_000); + + //stop dev + const contextMenu = await component.openContextMenu(); + await contextMenu.select(MENUS.stopDev); + + //wait for dev to stop + await itemExists(`${componentName} (dev stopping)`, section); + await itemExists(componentName, section, 60_000); + + //check for terminal content + const terminalText = await openshiftTerminal.getTerminalText(); + expect(terminalText).to.include('Finished executing the application'); + expect(terminalText).to.include('Press any key to close this terminal'); + + //close tab and check + await openshiftTerminal.sendKeysToTerminal([Key.ENTER]); + expect(await openshiftTerminal.isAnyTabOpened()).to.be.false; + }); + + it('Stop Dev works by pressing Ctrl+c', async function() { + this.timeout(80_000) + //start dev + const contextMenu = await component.openContextMenu(); + await contextMenu.select(MENUS.startDev); + + //wait for start dev to finish + await itemExists(`${componentName} (dev starting)`, section); + await itemExists(`${componentName} (dev running)`, section, 30_000); + + //stop dev + await openshiftTerminal.sendKeysToTerminal([`${Key.CONTROL}c`]); + + //wait for stop dev to finish + await itemExists(`${componentName} (dev stopping)`, section); + await itemExists(componentName, section, 60_000); + + //check for terminal content + const terminalText = await openshiftTerminal.getTerminalText(); + expect(terminalText).to.include('Finished executing the application'); + expect(terminalText).to.include('Press any key to close this terminal'); + }) + }); +} diff --git a/test/ui/suite/createComponent.ts b/test/ui/suite/createComponent.ts index 1aba3ed89..16c56805a 100644 --- a/test/ui/suite/createComponent.ts +++ b/test/ui/suite/createComponent.ts @@ -147,6 +147,8 @@ export function testCreateComponent(path: string) { //check if component is in component view componentName = 'nodejs-starter'; expect(await section.findItem(componentName)).to.be.not.undefined; + + dlt = false; }); //Delete the component using file system @@ -167,19 +169,11 @@ export function testCreateComponent(path: string) { }); after(async function context() { - let prompt = await new Workbench().openCommandPrompt(); + const prompt = await new Workbench().openCommandPrompt(); await prompt.setText('>Workspaces: Remove Folder From Workspace...'); await prompt.confirm(); await prompt.setText('node-js-runtime'); await prompt.confirm(); - await new Promise((res) => { - setTimeout(res, 2_500); - }); - prompt = await new Workbench().openCommandPrompt(); - await prompt.setText('>Workspaces: Remove Folder From Workspace...'); - await prompt.confirm(); - await prompt.setText('nodejs-starter'); - await prompt.confirm(); }); async function createComponent(createCompView: CreateComponentWebView): Promise {