che-server/tests/e2e/pageobjects/ide/Terminal.ts

243 lines
11 KiB
TypeScript

/*********************************************************************
* 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, Key, WebElement, error } from 'selenium-webdriver';
import { Logger } from '../../utils/Logger';
import { TimeoutConstants } from '../../TimeoutConstants';
@injectable()
export class Terminal {
private static readonly TERMINAL_ROWS_XPATH_LOCATOR_PREFFIX = '(//div[contains(@class, \'terminal-container\')]//div[contains(@class, \'terminal\')]//div[contains(@class, \'xterm-rows\')])';
constructor(@inject(CLASSES.DriverHelper) private readonly driverHelper: DriverHelper) { }
async waitTab(tabTitle: string, timeout: number = TimeoutConstants.TS_SELENIUM_TERMINAL_DEFAULT_TIMEOUT) {
Logger.debug(`Terminal.waitTab "${tabTitle}"`);
const terminalTabLocator: By = By.css(this.getTerminalTabCssLocator(tabTitle));
await this.driverHelper.waitVisibility(terminalTabLocator, timeout);
}
async waitTabAbsence(tabTitle: string, timeout: number = TimeoutConstants.TS_SELENIUM_TERMINAL_DEFAULT_TIMEOUT) {
Logger.debug(`Terminal.waitTabAbsence "${tabTitle}"`);
const terminalTabLocator: By = By.css(this.getTerminalTabCssLocator(tabTitle));
await this.driverHelper.waitDisappearanceWithTimeout(terminalTabLocator, timeout);
}
async clickOnTab(tabTitle: string, timeout: number = TimeoutConstants.TS_SELENIUM_TERMINAL_DEFAULT_TIMEOUT) {
Logger.debug(`Terminal.clickOnTab "${tabTitle}"`);
const terminalTabLocator: By = By.css(`${this.getTerminalTabCssLocator(tabTitle)} div.theia-tab-icon-label`);
await this, this.driverHelper.waitAndClick(terminalTabLocator, timeout);
}
async waitTabFocused(tabTitle: string, timeout: number = TimeoutConstants.TS_SELENIUM_TERMINAL_DEFAULT_TIMEOUT) {
Logger.debug(`Terminal.waitTabFocused "${tabTitle}"`);
const focusedTerminalTabLocator: By = this.getFocusedTerminalTabLocator(tabTitle);
await this.driverHelper.waitVisibility(focusedTerminalTabLocator, timeout);
}
async selectTerminalTab(tabTitle: string, timeout: number = TimeoutConstants.TS_SELENIUM_TERMINAL_DEFAULT_TIMEOUT) {
Logger.debug(`Terminal.selectTerminalTab "${tabTitle}"`);
await this.clickOnTab(tabTitle, timeout);
await this.waitTabFocused(tabTitle, timeout);
}
async clickOnTabCloseIcon(tabTitle: string, timeout: number = TimeoutConstants.TS_SELENIUM_TERMINAL_DEFAULT_TIMEOUT) {
Logger.debug(`Terminal.clickOnTabCloseIcon "${tabTitle}"`);
const terminalTabCloseIconLocator: By =
By.css(`${this.getTerminalTabCssLocator(tabTitle)} div.p-TabBar-tabCloseIcon`);
await this.driverHelper.waitAndClick(terminalTabCloseIconLocator, timeout);
}
async closeTerminalTab(tabTitle: string, timeout: number = TimeoutConstants.TS_SELENIUM_TERMINAL_DEFAULT_TIMEOUT) {
Logger.debug(`Terminal.closeTerminalTab "${tabTitle}"`);
await this.clickOnTabCloseIcon(tabTitle, timeout);
await this.waitTabAbsence(tabTitle, timeout);
}
async type(terminalTabTitle: string, text: string) {
Logger.debug(`Terminal.type "${terminalTabTitle}"`);
const terminalIndex: number = await this.getTerminalIndex(terminalTabTitle);
const terminalInteractionContainer: By = this.getTerminalEditorInteractionEditorLocator(terminalIndex);
await this.driverHelper.typeToInvisible(terminalInteractionContainer, text);
}
async rejectTerminalProcess(tabTitle: string, timeout: number = TimeoutConstants.TS_SELENIUM_TERMINAL_DEFAULT_TIMEOUT) {
Logger.debug(`Terminal.rejectTerminalProcess "${tabTitle}"`);
await this.selectTerminalTab(tabTitle, timeout);
await this.type(tabTitle, Key.chord(Key.CONTROL, 'c'));
}
async getText(terminalTab: string, timeout: number = TimeoutConstants.TS_SELENIUM_TERMINAL_DEFAULT_TIMEOUT): Promise<string> {
Logger.debug(`Terminal.getText tab: ${terminalTab}`);
const terminalIndex: number = await this.getTerminalIndex(terminalTab);
await this.selectTerminalTab(terminalTab, timeout);
return await this.driverHelper.waitAndGetText(By.xpath(Terminal.TERMINAL_ROWS_XPATH_LOCATOR_PREFFIX + `[${terminalIndex}]`), timeout);
}
async getTextFromProblemsTab(timeout: number = TimeoutConstants.TS_SELENIUM_TERMINAL_DEFAULT_TIMEOUT): Promise<string> {
Logger.debug(`Terminal.getTextFromProblemsTab`);
const problemsTabBodyLocator: By = By.xpath(`//div[@id='problems']`);
return await this.driverHelper.waitAndGetText(problemsTabBodyLocator, timeout);
}
async selectTabByPrefixAndWaitText(terminalTab: string, expectedText: string, timeout: number = TimeoutConstants.TS_SELENIUM_TERMINAL_DEFAULT_TIMEOUT) {
Logger.debug(`Terminal.selectTabByPrefixAndWaitText tab: ${terminalTab} text: ${expectedText}`);
const terminalTabLocatorWithPreffix: string = `//li[contains(@title, '${terminalTab}')]`;
const terminalIndex: number = await this.getTerminalIndex(terminalTab);
await this.driverHelper.waitAndClick(By.xpath(terminalTabLocatorWithPreffix), timeout);
await this.driverHelper.waitUntilTrue(async () => {
const terminalText: string = await this.driverHelper.waitAndGetText(By.xpath(Terminal.TERMINAL_ROWS_XPATH_LOCATOR_PREFFIX + `[${terminalIndex}]`), timeout);
return terminalText.includes(expectedText);
}, timeout);
}
async waitText(terminalTab: string, expectedText: string, timeout: number) {
Logger.debug(`Terminal.waitText tab: ${terminalTab} text: ${expectedText}`);
await this.selectTerminalTab(terminalTab, timeout);
await this.driverHelper.waitUntilTrue(async () => {
// separates each method iteration to the readable blocks in the terminal log
Logger.debug('----------------------------------------------');
const terminalText: string = await this.getText(terminalTab, timeout);
if (terminalText.includes(expectedText)) {
Logger.debug('Expected text is present in the terminal output');
return true;
}
Logger.debug('Expected text is not present in the terminal output');
await this.driverHelper.wait(1000);
return false;
}, timeout);
}
async waitTextInProblemsTab(expectedText: string, timeout: number) {
Logger.debug(`Terminal.waitTextInProblemsTab`);
await this.selectTerminalTab('Problems', timeout);
await this.driverHelper.waitUntilTrue(async () => {
// separates each method iteration to the readable blocks in the terminal log
Logger.debug('----------------------------------------------');
const terminalText: string = await this.getTextFromProblemsTab(timeout);
if (terminalText.includes(expectedText)) {
Logger.debug('Expected text is present in the terminal output');
return true;
}
Logger.debug('Expected text is not present in the terminal output');
await this.driverHelper.wait(1000);
return false;
}, timeout);
}
public async waitIconSuccess(taskName: string, timeout: number) {
const terminalTabLocator: By = By.css(`${this.getTerminalTabCssLocator(taskName)} div.p-TabBar-tabIcon`);
await this.driverHelper.waitVisibility(terminalTabLocator, TimeoutConstants.TS_SELENIUM_TERMINAL_DEFAULT_TIMEOUT);
let terminalClass = await this.driverHelper.waitAndGetElementAttribute(terminalTabLocator, 'class');
await this.driverHelper.getDriver().wait(async () => {
terminalClass = await this.driverHelper.waitAndGetElementAttribute(terminalTabLocator, 'class');
if (terminalClass.includes('fa-check')) { // css for tick icon
return true;
}
if (terminalClass.includes('fa-times-circle')) { // css for failed icon
Logger.error('Task "' + taskName + '" failed.');
throw new Error('Task "' + taskName + '" failed.');
}
return false;
}, timeout, 'Timed out waiting for task ' + taskName + ' to succeed.');
}
private getTerminalTabCssLocator(tabTitle: string): string {
return `li[title='${tabTitle}']`;
}
private getFocusedTerminalTabLocator(tabTitle: string): By {
return By.css(`li[title='${tabTitle}'].p-mod-current.theia-mod-active`);
}
private async getTerminalIndex(terminalTitle: string): Promise<number> {
for (let i: number = 0; i < 10; i++) {
try {
return await this.searchTerminalIndex(terminalTitle);
} catch (err) {
if (!(err instanceof error.NoSuchElementError)) {
throw err;
}
if ((err instanceof error.NoSuchElementError) && (i === 9)) {
throw err;
}
await this.driverHelper.wait(2000);
}
}
throw new error.NoSuchElementError(`The terminal with title '${terminalTitle}' has not been found.`);
}
private async searchTerminalIndex(terminalTitle: string): Promise<number> {
const terminalTabTitleXpathLocator: string = `//div[@id='theia-bottom-content-panel']` +
`//li[contains(@id, 'shell-tab-terminal') or contains(@id, 'shell-tab-plugin')]` +
`//div[@class='p-TabBar-tabLabel']`;
const terminalTabs: WebElement[] = await this.driverHelper.waitAllPresence(By.xpath(terminalTabTitleXpathLocator));
let terminalTitles: string[] = [];
for (let i: number = 1; i <= terminalTabs.length; i++) {
const terminalTabLocator: By = By.xpath(`(${terminalTabTitleXpathLocator})[${i}]`);
const currentTerminalTitle: string = await this.driverHelper.waitAndGetText(terminalTabLocator);
if (currentTerminalTitle.search(terminalTitle) > -1) {
return i;
}
terminalTitles.push(currentTerminalTitle);
}
throw new error.NoSuchElementError(`The terminal with title '${terminalTitle}' has not been found.\n` +
` > List of the tabs:\n > ${terminalTitles}`);
}
private getTerminalEditorInteractionEditorLocator(terminalIndex: number): By {
return By.xpath(`(//textarea[@aria-label='Terminal input'])[${terminalIndex}]`);
}
}