Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Feature implement python devfile #15736

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions tests/e2e/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export * from './utils/workspace/TestWorkspaceUtil';
export * from './utils/workspace/WorkspaceStatus';
export * from './pageobjects/dashboard/Dashboard';
export * from './pageobjects/dashboard/NewWorkspace';
export * from './pageobjects/dashboard/workspace-details/WorkspaceDetailsPlugins';
export * from './pageobjects/dashboard/workspace-details/WorkspaceDetails';
export * from './pageobjects/dashboard/Workspaces';
export * from './pageobjects/dashboard/workspace-details/WorkspaceDetails';
export * from './pageobjects/dashboard/workspace-details/WorkspaceDetailsPlugins';
export * from './pageobjects/ide/ContextMenu';
export * from './pageobjects/ide/DebugView';
export * from './pageobjects/ide/DialogWindow';
Expand Down
8 changes: 8 additions & 0 deletions tests/e2e/mocha-python-django.opts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
--timeout 2200000
Katka92 marked this conversation as resolved.
Show resolved Hide resolved
--reporter 'dist/driver/CheReporter.js'
-u tdd
--bail
--full-trace
--spec dist/tests/login/Login.spec.js
--spec dist/tests/devfiles/PythonDjango.spec.js
--require source-map-support/register
1 change: 1 addition & 0 deletions tests/e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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-python-django": "./generateIndex.sh && npm run lint && npm run tsc && mocha --opts mocha-python-django.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 ."
Expand Down
3 changes: 2 additions & 1 deletion tests/e2e/pageobjects/ide/DialogWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,9 @@ export class DialogWindow {
Logger.debug('DialogWindow.waitDialogAndOpenLink');

await this.waitDialog(timeout, dialogText);
await this.ide.waitApllicationIsReady(await this.getApplicationUrlFromDialog(dialogText), timeout);
const applicationUrlFromDialog: string = await this.getApplicationUrlFromDialog(dialogText);
await this.clickToOpenLinkButton();
await this.ide.waitApllicationIsReady(applicationUrlFromDialog, timeout);
await this.waitDialogDissappearance();
}

Expand Down
45 changes: 16 additions & 29 deletions tests/e2e/pageobjects/ide/Ide.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { DriverHelper } from '../../utils/DriverHelper';
import { injectable, inject } from 'inversify';
import { CLASSES } from '../../inversify.types';
import { TestConstants } from '../../TestConstants';
import { By, WebElement, error } from 'selenium-webdriver';
import { By, error } from 'selenium-webdriver';
import { Logger } from '../../utils/Logger';

export enum RightToolbarButton {
Expand Down Expand Up @@ -112,10 +112,8 @@ export class Ide {
await this.driverHelper.waitAndClick(By.xpath(yesButtonLocator));
}

async waitWorkspaceAndIde(workspaceNamespace: string,
workspaceName: string,
timeout: number = TestConstants.TS_SELENIUM_LOAD_PAGE_TIMEOUT) {

async waitWorkspaceAndIde(workspaceNamespace: string, workspaceName: string, timeout: number = TestConstants.TS_SELENIUM_LOAD_PAGE_TIMEOUT) {
// why are there unused arguments in this method?
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should consider refactoring this method. It's tied to HappyPath tests as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logger.debug('Ide.waitWorkspaceAndIde');

await this.waitAndSwitchToIdeFrame(timeout);
Expand Down Expand Up @@ -218,32 +216,21 @@ export class Ide {
await this.waitStatusBarContains(expectedTextInStatusBar, 20000);
}

async closeAllNotifications() {
const notificationLocator: By = By.css('.theia-Notification');

async closeAllNotifications(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) {
const statusBarNotificationsLocator: By = By.xpath(`//div[@id="theia-statusBar"]/div[@class="area right"]//div[contains(@title, 'Notification')]`);
const notificationCenterActionCloseAllButtonLocator: By = By.xpath(`//div[contains(@class, 'theia-notifications-container theia-notification-center open')]//ul[@class="theia-notification-actions"]/li[@class="clear-all"]`);
const notificationCenterClosedLocator: By = By.xpath(`//div[contains(@class, 'theia-notifications-container theia-notification-center closed')]`);
const notificationCenterContainerNotificationsLocator: By = By.xpath(`//div[contains(@class, 'theia-notifications-container theia-notification-center')]//div[@class='theia-notification-list']//*`);
Logger.debug('Ide.closeAllNotifications');

if (! await this.driverHelper.isVisible(notificationLocator)) {
return;
}

const notifications: WebElement[] = await this.driverHelper.waitAllPresence(notificationLocator);
const notificationsCapacity: number = notifications.length;

for (let i = 1; i <= notificationsCapacity; i++) {
const notificationLocator: By = By.xpath('//div[@class=\'theia-Notification\']//button[text()=\'Close\']');

try {
await this.driverHelper.waitAndClick(notificationLocator);
} catch (err) {
if (err instanceof error.TimeoutError) {
console.log(`The '${notificationLocator}' element is not visible and can't be clicked`);
continue;
}

throw err;
}
}
Logger.trace(`Ide.closeAllNotifications driverHelper click Notification Center icon.`);
ScrewTSW marked this conversation as resolved.
Show resolved Hide resolved
await this.driverHelper.waitAndClick(statusBarNotificationsLocator, 5_000);
Logger.trace(`Ide.closeAllNotifications driverHelper Notifications Center click closeAllNotifications.`);
await this.driverHelper.waitAndClick(notificationCenterActionCloseAllButtonLocator, 5_000);
Logger.trace(`Ide.closeAllNotifications driverHelper waitng for Notification Center to close.`);
await this.driverHelper.waitPresence(notificationCenterClosedLocator, 5_000);
ScrewTSW marked this conversation as resolved.
Show resolved Hide resolved
Logger.trace(`Ide.closeAllNotifications driverHelper test if any notifications remain in Notification Center.`);
await this.driverHelper.waitDisappearance(notificationCenterContainerNotificationsLocator, 5_000);
}

async performKeyCombination(keyCombination: string) {
Expand Down
6 changes: 1 addition & 5 deletions tests/e2e/pageobjects/ide/TopMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { CLASSES } from '../../inversify.types';
import { DriverHelper } from '../../utils/DriverHelper';
import { TestConstants } from '../../TestConstants';
import { By, error } from 'selenium-webdriver';
import { Ide } from './Ide';
import { Logger } from '../../utils/Logger';
import { QuickOpenContainer } from './QuickOpenContainer';

Expand All @@ -22,8 +21,7 @@ export class TopMenu {
private static readonly TOP_MENU_BUTTONS: string[] = ['File', 'Edit', 'Selection', 'View', 'Go', 'Debug', 'Terminal', 'Help'];

constructor(@inject(CLASSES.DriverHelper) private readonly driverHelper: DriverHelper,
@inject(CLASSES.Ide) private readonly ide: Ide,
@inject(CLASSES.QuickOpenContainer) private readonly quickOpenContainer: QuickOpenContainer) { }
@inject(CLASSES.QuickOpenContainer) private readonly quickOpenContainer: QuickOpenContainer) { }

public async waitTopMenu(timeout: number = TestConstants.TS_SELENIUM_DEFAULT_TIMEOUT) {
Logger.debug('TopMenu.waitTopMenu');
Expand All @@ -45,8 +43,6 @@ export class TopMenu {
Logger.debug(`TopMenu.clickOnTopMenuButton "${buttonText}"`);

const buttonLocator: By = this.getTopMenuButtonLocator(buttonText);

await this.ide.closeAllNotifications();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you explain a reason why you deleted this method

Copy link
Member Author

@ScrewTSW ScrewTSW Jan 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes absolutely no sense to close notifications after clicking the top menu item and it was stealing focus from the Terminal element causing the windows to close since the method closeAllNotifications is performing clicks.

If you want to make sure there are no notifications present, you should call it in your tests separately before you call TopMenu.clickOnTopMenuButton

I've discussed it with @Katka92 and @rhopp and they agreed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't make sense if notifications appear at the bottom left corner. as now But this method was put there when notifications appeared at the top center and override the top menu. Can you guarantee that it will not be there again for example tomorrow?

await this.driverHelper.waitAndClick(buttonLocator, timeout);
}

Expand Down
6 changes: 3 additions & 3 deletions tests/e2e/tests/devfiles/JavaMaven.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@ const codeNavigationClassName: string = 'String.class';
const stack : string = 'Java Maven';
const taskName: string = 'maven build';

suite('Java Maven test', async () => {
suite(`${stack} test`, async () => {
suite (`Create ${stack} workspace ${workspaceName}`, async () => {
workspaceHandling.createAndOpenWorkspace(workspaceName, stack);
projectAndFileTests.waitWorkspaceReadiness(workspaceName, sampleName, 'src');
});

suite('Validation of workspace build and run', async () => {
codeExecutionTests.runTask(taskName, 120000);
codeExecutionTests.runTask(taskName, 120_000);
codeExecutionTests.closeTerminal(taskName);
});

suite('Language server validation', async () => {
projectAndFileTests.openFile(fileFolderPath, tabTitle);
commonLsTests.waitLSInitialization('Starting Java Language Server', 1800000, 360000);
commonLsTests.waitLSInitialization('Starting Java Language Server', 1_800_000, 360_000);
commonLsTests.suggestionInvoking(tabTitle, 10, 20, 'append(char c) : PrintStream');
commonLsTests.errorHighlighting(tabTitle, 'error', 11);
commonLsTests.autocomplete(tabTitle, 10, 11, 'System - java.lang');
Expand Down
6 changes: 3 additions & 3 deletions tests/e2e/tests/devfiles/JavaVertx.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const buildTaskName: string = 'maven build';
const LSstarting: string = 'Starting Java Language Server';
const stack: string = 'Java Vert.x';

suite('Java Vert.x test', async () => {
suite(`${stack} test`, async () => {

suite (`Create ${stack} workspace ${workspaceName}`, async () => {
workspaceHandling.createAndOpenWorkspace(workspaceName, stack);
Expand All @@ -32,15 +32,15 @@ suite('Java Vert.x test', async () => {

suite('Language server validation', async () => {
projectAndFileTests.openFile(fileFolderPath, tabTitle);
commonLsTests.waitLSInitialization(LSstarting, 1800000, 360000);
commonLsTests.waitLSInitialization(LSstarting, 1_800_000, 360_000);
commonLsTests.suggestionInvoking(tabTitle, 19, 31, 'router(Vertx vertx) : Router');
commonLsTests.errorHighlighting(tabTitle, 'error', 20);
commonLsTests.autocomplete(tabTitle, 19, 7, 'Router - io.vertx.ext.web');
commonLsTests.codeNavigation(tabTitle, 19, 7, codeNavigationClassName);
});

suite('Validation of project build', async () => {
codeExecutionTests.runTask(buildTaskName, 120000);
codeExecutionTests.runTask(buildTaskName, 120_000);
codeExecutionTests.closeTerminal(buildTaskName);
});

Expand Down
52 changes: 52 additions & 0 deletions tests/e2e/tests/devfiles/PythonDjango.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*********************************************************************
* 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 { NameGenerator } from '../../utils/NameGenerator';
import 'reflect-metadata';
import * as codeExecutionHelper from '../../testsLibrary/CodeExecutionTests';
import * as workspaceHandler from '../../testsLibrary/WorksapceHandlingTests';
import * as projectManager from '../../testsLibrary/ProjectAndFileTests';

const workspaceName: string = NameGenerator.generate('wksp-test-', 5);
const workspaceStack: string = 'Python Django';
const workspaceSampleName: string = 'django-realworld-example-app';
const workspaceRootFolderName: string = 'conduit';

const taskInstallDependencies: string = 'install dependencies';
const taskMigrate: string = 'migrate';
const taskRunServer: string = 'run server';
const taskExpectedDialogText: string = 'A process is now listening on port 7000';

suite(`${workspaceStack} test`, async () => {

suite(`Create ${workspaceStack} workspace ${workspaceName}`, async () => {
workspaceHandler.createAndOpenWorkspace(workspaceName, workspaceStack);
projectManager.waitWorkspaceReadiness(workspaceName, workspaceSampleName, workspaceRootFolderName);
});

suite('Install dependencies', async () => {
codeExecutionHelper.runTask(taskInstallDependencies, 60_000);
codeExecutionHelper.closeTerminal(taskInstallDependencies);
});

suite('Migrate Django application project', async () => {
codeExecutionHelper.runTask(taskMigrate, 30_000);
codeExecutionHelper.closeTerminal(taskMigrate);
});

suite('Run django server', async () => {
codeExecutionHelper.runTaskWithDialogShellAndOpenLink(taskRunServer, taskExpectedDialogText, 30_000);
});

suite('Stop and remove workspace', async() => {
workspaceHandler.stopWorkspace(workspaceName);
workspaceHandler.removeWorkspace(workspaceName);
});

});
24 changes: 22 additions & 2 deletions tests/e2e/testsLibrary/CodeExecutionTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,42 @@
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/

import { CLASSES, Terminal, TopMenu, Ide } from '..';
import { CLASSES, Terminal, TopMenu, Ide, DialogWindow } from '..';
import { e2eContainer } from '../inversify.config';

const terminal: Terminal = e2eContainer.get(CLASSES.Terminal);
const topMenu: TopMenu = e2eContainer.get(CLASSES.TopMenu);
const ide: Ide = e2eContainer.get(CLASSES.Ide);
const dialogWindow: DialogWindow = e2eContainer.get(CLASSES.DialogWindow);

const dialogWindowCloseButtonText: string = 'close';

export function runTask(taskName: string, timeout: number) {
test( `Run command '${taskName}'`, async () => {
test(`Run command '${taskName}'`, async () => {
await topMenu.runTask(taskName);
await ide.waitNotification('has exited with code 0.', timeout);
});
}

export function runTaskWithDialogShellAndOpenLink(taskName: string, expectedDialogText: string, timeout: number) {
test(`Run command '${taskName}' expecting dialog shell`, async () => {
await topMenu.runTask(taskName);
await dialogWindow.waitDialogAndOpenLink(timeout, expectedDialogText);
});
}

export function runTaskWithDialogShellAndClose(taskName: string, expectedDialogText: string, timeout: number) {
test(`Run command '${taskName}' expecting dialog shell`, async () => {
await topMenu.runTask(taskName);
await dialogWindow.waitDialog(timeout, expectedDialogText);
await dialogWindow.clickToButton(dialogWindowCloseButtonText);
await dialogWindow.waitDialogDissappearance();
});
}

export function closeTerminal(taskName: string) {
test('Close the terminal tasks', async () => {
await ide.closeAllNotifications();
await terminal.closeTerminalTab(taskName);
});
}
27 changes: 27 additions & 0 deletions tests/e2e/utils/DriverHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { Logger } from './Logger';
@injectable()
export class DriverHelper {
private readonly driver: ThenableWebDriver;
private readonly BY_XPATH_LOCATOR_START_INDEX: number = 10;

constructor(@inject(TYPES.Driver) driver: IDriver) {
this.driver = driver.get();
Expand Down Expand Up @@ -521,6 +522,32 @@ export class DriverHelper {
throw new error.TimeoutError(`Exceeded maximum mouse move attempts, for the '${elementLocator}' element`);
}

public async hasChildren(elementLocator: By): Promise<boolean> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can suggest other variant:

    public async hasChildren(elementXpathLocator: String): Promise<boolean> {
        return await this.isVisible(By.xpath(`${elementXpathLocator}//*`));
    }

For me looks much simplier

const attempts: number = TestConstants.TS_SELENIUM_DEFAULT_ATTEMPTS;
const polling: number = TestConstants.TS_SELENIUM_DEFAULT_POLLING;
Logger.trace(`DriverHelper.hasChildren ${elementLocator}`);

for (let i = 0; i < attempts; i++) {
try {
const childrenLocatorString: string = elementLocator.toString().substr(this.BY_XPATH_LOCATOR_START_INDEX, elementLocator.toString().length - this.BY_XPATH_LOCATOR_START_INDEX - 1) + `//*`;
const childrenLocator: By = By.xpath(childrenLocatorString);
const children: Array<WebElement> = await this.driver.findElements(childrenLocator);
Logger.trace(`DriverHelper.hasChildren child elements locator '${childrenLocator}', child elements:${children.length}`);
if (children.length > 0) { return true; }
return false;
} catch (err) {
if (err instanceof error.StaleElementReferenceError) {
await this.wait(polling);
continue;
}

throw err;
}
}

throw new error.TimeoutError(`Exceeded maximum attempts to get children for '${elementLocator}'.`);
}

getDriver(): ThenableWebDriver {
Logger.trace('DriverHelper.getDriver');

Expand Down