Skip to content

Commit

Permalink
feat: check links to documentation page in Dashboard
Browse files Browse the repository at this point in the history
Signed-off-by: mdolhalo <[email protected]>
  • Loading branch information
mdolhalo committed Sep 8, 2023
1 parent a5a0151 commit e5e1d68
Show file tree
Hide file tree
Showing 9 changed files with 297 additions and 57 deletions.
2 changes: 1 addition & 1 deletion tests/e2e/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ module.exports = {
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/restrict-plus-operands': 'off',
'@typescript-eslint/no-namespace': 'off',
'@typescript-eslint/no-unused-expressions': 'error',
'@typescript-eslint/no-unused-expressions': 'off',
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/no-use-before-define': 'error',
'@typescript-eslint/no-var-requires': 'off',
Expand Down
16 changes: 16 additions & 0 deletions tests/e2e/constants/BASE_TEST_CONSTANTS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export enum Platform {
}

export const BASE_TEST_CONSTANTS: {
IS_PRODUCT_DOCUMENTATION_RELEASED: any;
TESTING_APPLICATION_VERSION: string;
TS_DEBUG_MODE: boolean;
TS_PLATFORM: string;
TS_SELENIUM_RESPONSE_INTERCEPTOR: boolean;
Expand All @@ -30,6 +32,20 @@ export const BASE_TEST_CONSTANTS: {
*/
TS_SELENIUM_BASE_URL: !process.env.TS_SELENIUM_BASE_URL ? 'http://sample-url' : process.env.TS_SELENIUM_BASE_URL.replace(/\/$/, ''),

/**
* testing application version
*/
TESTING_APPLICATION_VERSION: process.env.TESTING_APPLICATION_VERSION || '3.8',

/**
* is "https://access.redhat.com/documentation/en-us/red_hat_openshift_dev_spaces/{TESTING_APPLICATION_VERSION}/" available online
* false by default
*/
IS_PRODUCT_DOCUMENTATION_RELEASED: process.env.IS_PRODUCT_DOCUMENTATION_RELEASED === 'true',

/**
* is cluster disconnected of online
*/
IS_CLUSTER_DISCONNECTED: (): boolean => BASE_TEST_CONSTANTS.TS_SELENIUM_BASE_URL.includes('airgap'),

/**
Expand Down
49 changes: 48 additions & 1 deletion tests/e2e/pageobjects/dashboard/Dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ export class Dashboard {
private static readonly LOADER_ALERT: By = By.xpath('//*[@data-testid="loader-alert"]');
private static readonly LOGOUT_BUTTON: By = By.xpath('//button[text()="Logout"]');
private static readonly USER_SETTINGS_DROPDOWN: By = By.xpath('//header//button/span[text()!=""]//parent::button');
private static readonly INFO_DROPDOWN_BUTTON: By = By.xpath('//button[@aria-label="About Menu"]');
private static readonly ABOUT_DIALOG_WINDOW_CLOSE_BUTTON: By = By.xpath('//button[@aria-label="Close Dialog"]');

constructor(
@inject(CLASSES.DriverHelper)
private readonly driverHelper: DriverHelper,
readonly driverHelper: DriverHelper,
@inject(CLASSES.Workspaces) private readonly workspaces: Workspaces
) {}

Expand Down Expand Up @@ -69,6 +71,37 @@ export class Dashboard {
await this.waitPage();
}

async openAboutMenu(): Promise<void> {
Logger.debug();

await this.driverHelper.waitAndClick(Dashboard.INFO_DROPDOWN_BUTTON);
}

async selectAboutMenuItem(text: string): Promise<void> {
Logger.debug();

await this.driverHelper.waitAndClick(this.getAboutMenuItemButton(text));
}

getAboutDialogWindowMenuElements(): By[] {
Logger.debug();

return [
By.xpath('//dd[@data-testid="dashboard-version"]'),
this.getServerVersionItem(),
By.xpath('//dd[@data-testid="username"]'),
By.xpath('//dd[@data-testid="browser-name"]'),
By.xpath('//dd[@data-testid="browser-os"]'),
By.xpath('//dd[@data-testid="browser-version"]')
];
}

async getApplicationVersionFromAboutDialogWindow(): Promise<string> {
Logger.debug();

return await this.driverHelper.waitAndGetText(this.getServerVersionItem());
}

async waitPage(timeout: number = TIMEOUT_CONSTANTS.TS_SELENIUM_LOAD_PAGE_TIMEOUT): Promise<void> {
Logger.debug();

Expand Down Expand Up @@ -107,6 +140,12 @@ export class Dashboard {
await this.driverHelper.wait(TIMEOUT_CONSTANTS.TS_SELENIUM_DEFAULT_POLLING);
}

async closeAboutDialogWindow(): Promise<void> {
Logger.debug();

await this.driverHelper.waitAndClick(Dashboard.ABOUT_DIALOG_WINDOW_CLOSE_BUTTON);
}

async logout(timeout: number = TIMEOUT_CONSTANTS.TS_COMMON_DASHBOARD_WAIT_TIMEOUT): Promise<void> {
Logger.debug();

Expand All @@ -115,4 +154,12 @@ export class Dashboard {
await this.driverHelper.waitAndClick(Dashboard.LOGOUT_BUTTON, timeout);
await this.driverHelper.waitDisappearance(Dashboard.USER_SETTINGS_DROPDOWN, timeout);
}

private getAboutMenuItemButton(text: string): By {
return By.xpath(`//li/button[text()="${text}"]`);
}

private getServerVersionItem(): By {
return By.xpath('//dd[@data-testid="server-version"]');
}
}
61 changes: 35 additions & 26 deletions tests/e2e/pageobjects/dashboard/Workspaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class Workspaces {
);
private static readonly DELETE_CONFIRMATION_CHECKBOX: By = By.xpath('//input[@data-testid="confirmation-checkbox"]');
private static readonly CONFIRMATION_WINDOW: By = By.xpath('//div[@aria-label="Delete workspaces confirmation window"]');
private static readonly LEARN_MORE_DOC_LINK: By = By.xpath('//div/p/a');

constructor(
@inject(CLASSES.DriverHelper)
Expand All @@ -50,7 +51,7 @@ export class Workspaces {
async clickOpenButton(workspaceName: string, timeout: number = TIMEOUT_CONSTANTS.TS_SELENIUM_LOAD_PAGE_TIMEOUT): Promise<void> {
Logger.debug();

await this.driverHelper.waitAndClick(this.getOpenButtonLocator(workspaceName), timeout);
await this.driverHelper.waitAndClick(this.getOpenButton(workspaceName), timeout);
}

async waitWorkspaceListItem(
Expand All @@ -59,7 +60,7 @@ export class Workspaces {
): Promise<void> {
Logger.debug(`"${workspaceName}"`);

await this.driverHelper.waitVisibility(this.getWorkspaceListItemLocator(workspaceName), timeout);
await this.driverHelper.waitVisibility(this.getWorkspaceListItem(workspaceName), timeout);
}

async waitWorkspaceWithRunningStatus(
Expand All @@ -68,7 +69,7 @@ export class Workspaces {
): Promise<void> {
Logger.debug(`"${workspaceName}"`);

await this.driverHelper.waitVisibility(this.getWorkspaceStatusLocator(workspaceName, WorkspaceStatusUI.Running), timeout);
await this.driverHelper.waitVisibility(this.getWorkspaceStatus(workspaceName, WorkspaceStatusUI.Running), timeout);
}

async waitWorkspaceWithStoppedStatus(
Expand All @@ -77,28 +78,28 @@ export class Workspaces {
): Promise<void> {
Logger.debug(`"${workspaceName}"`);

await this.driverHelper.waitVisibility(this.getWorkspaceStatusLocator(workspaceName, WorkspaceStatusUI.Stopped), timeout);
await this.driverHelper.waitVisibility(this.getWorkspaceStatus(workspaceName, WorkspaceStatusUI.Stopped), timeout);
}

async clickWorkspaceListItem(
async clickWorkspaceListItemLink(
workspaceName: string,
timeout: number = TIMEOUT_CONSTANTS.TS_CLICK_DASHBOARD_ITEM_TIMEOUT
): Promise<void> {
Logger.debug(`"${workspaceName}"`);

await this.driverHelper.waitAndClick(this.getWorkspaceListItemLocator(workspaceName), timeout);
await this.driverHelper.waitAndClick(this.getOpenWorkspaceDetailsLink(workspaceName), timeout);
}

async clickActionsButton(workspaceName: string): Promise<void> {
Logger.debug(`of the '${workspaceName}' list item`);

await this.driverHelper.waitAndClick(this.getActionsLocator(workspaceName));
await this.driverHelper.waitAndClick(this.getActions(workspaceName));
}

async waitActionsPopup(workspaceName: string, timeout: number = TIMEOUT_CONSTANTS.TS_COMMON_DASHBOARD_WAIT_TIMEOUT): Promise<void> {
Logger.debug(`of the '${workspaceName}' list item`);

await this.driverHelper.waitVisibility(this.getExpandedActionsLocator(workspaceName), timeout);
await this.driverHelper.waitVisibility(this.getExpandedActions(workspaceName), timeout);
}

async openActionsPopup(workspaceName: string, timeout: number = TIMEOUT_CONSTANTS.TS_COMMON_DASHBOARD_WAIT_TIMEOUT): Promise<void> {
Expand All @@ -111,19 +112,19 @@ export class Workspaces {
async clickActionsDeleteButton(workspaceName: string): Promise<void> {
Logger.debug(`for the '${workspaceName}' list item`);

await this.driverHelper.waitAndClick(this.getActionsPopupButtonLocator(workspaceName, 'Delete Workspace'));
await this.driverHelper.waitAndClick(this.getActionsPopupButton(workspaceName, 'Delete Workspace'));
}

async clickActionsStopWorkspaceButton(workspaceName: string): Promise<void> {
Logger.debug(`for the '${workspaceName}' list item`);
// todo: workaround because of issue CRW-3649
try {
await this.driverHelper.waitAndClick(this.getActionsPopupButtonLocator(workspaceName, 'Stop Workspace'));
await this.driverHelper.waitAndClick(this.getActionsPopupButton(workspaceName, 'Stop Workspace'));
} catch (e) {
Logger.warn(`for the '${workspaceName}' list item - popup was missed, try to click one more time (issue CRW-3649).`);

await this.driverHelper.waitAndClick(this.getActionsLocator(workspaceName));
await this.driverHelper.waitAndClick(this.getActionsPopupButtonLocator(workspaceName, 'Stop Workspace'));
await this.driverHelper.waitAndClick(this.getActions(workspaceName));
await this.driverHelper.waitAndClick(this.getActionsPopupButton(workspaceName, 'Stop Workspace'));
}
}

Expand Down Expand Up @@ -181,7 +182,7 @@ export class Workspaces {
const polling: number = TIMEOUT_CONSTANTS.TS_SELENIUM_DEFAULT_POLLING;
const attempts: number = Math.ceil(timeout / polling);

await this.driverHelper.waitDisappearance(this.getWorkspaceListItemLocator(workspaceName), attempts, polling);
await this.driverHelper.waitDisappearance(this.getWorkspaceListItem(workspaceName), attempts, polling);
}

async getAllCreatedWorkspacesNames(timeout: number = TIMEOUT_CONSTANTS.TS_COMMON_DASHBOARD_WAIT_TIMEOUT): Promise<string[]> {
Expand All @@ -206,33 +207,41 @@ export class Workspaces {
return workspaceNames;
}

private getWorkspaceListItemLocator(workspaceName: string): By {
async getLearnMoreDocumentationLink(): Promise<string> {
Logger.debug();

return await this.driverHelper.waitAndGetElementAttribute(Workspaces.LEARN_MORE_DOC_LINK, 'href');
}

private getWorkspaceListItem(workspaceName: string): By {
return By.xpath(`//tr[td//a[text()='${workspaceName}']]`);
}

private getWorkspaceStatusLocator(workspaceName: string, workspaceStatus: WorkspaceStatusUI): By {
private getWorkspaceStatus(workspaceName: string, workspaceStatus: WorkspaceStatusUI): By {
return By.xpath(
`${
this.getWorkspaceListItemLocator(workspaceName).value
this.getWorkspaceListItem(workspaceName).value
}//span[@data-testid='workspace-status-indicator']//*[local-name()='svg' and @fill='${workspaceStatus}']`
);
}

private getActionsLocator(workspaceName: string): By {
return By.xpath(`${this.getWorkspaceListItemLocator(workspaceName).value}/td/div/button[@aria-label='Actions']`);
private getActions(workspaceName: string): By {
return By.xpath(`${this.getWorkspaceListItem(workspaceName).value}/td/div/button[@aria-label='Actions']`);
}

private getExpandedActionsLocator(workspaceName: string): By {
return By.xpath(
`${this.getWorkspaceListItemLocator(workspaceName).value}//button[@aria-label='Actions' and @aria-expanded='true']`
);
private getExpandedActions(workspaceName: string): By {
return By.xpath(`${this.getWorkspaceListItem(workspaceName).value}//button[@aria-label='Actions' and @aria-expanded='true']`);
}

private getActionsPopupButton(workspaceName: string, buttonText: string): By {
return By.xpath(`${this.getWorkspaceListItem(workspaceName).value}//li//button[text()='${buttonText}']`);
}

private getActionsPopupButtonLocator(workspaceName: string, buttonText: string): By {
return By.xpath(`${this.getWorkspaceListItemLocator(workspaceName).value}//li//button[text()='${buttonText}']`);
private getOpenButton(workspaceName: string): By {
return By.xpath(`${this.getWorkspaceListItem(workspaceName).value}//td[@data-key=5]//a[text()='Open']`);
}

private getOpenButtonLocator(workspaceName: string): By {
return By.xpath(`${this.getWorkspaceListItemLocator(workspaceName).value}//td[@data-key=5]//a[text()='Open']`);
private getOpenWorkspaceDetailsLink(workspaceName: string): By {
return By.xpath(`${this.getWorkspaceListItem(workspaceName).value}//a[text()='${workspaceName}']`);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export class WorkspaceDetails {
private static readonly SAVE_BUTTON: By = By.css('button[name="save-button"]');
private static readonly ENABLED_SAVE_BUTTON: By = By.css('button[name="save-button"][aria-disabled="false"]');
private static readonly WORKSPACE_DETAILS_LOADER: By = By.css('workspace-details-overview md-progress-linear');
private static readonly STORAGE_TYPE_INFO_BUTTON: By = By.xpath('//label[@for="storage-type"]//following-sibling::button');
private static readonly CLOSE_STORAGE_TYPE_INFO_BUTTON: By = By.xpath('//button[@aria-label="Close"]');
private static readonly STORAGE_TYPE_DOC_LINK: By = By.xpath('//div/p/a');
private static readonly DEVFILE_DOC_LINK: By = By.xpath('//a[text()="Devfile Documentation"]');

constructor(
@inject(CLASSES.DriverHelper)
Expand Down Expand Up @@ -101,20 +105,9 @@ export class WorkspaceDetails {
}

async waitTabsPresence(timeout: number = TIMEOUT_CONSTANTS.TS_COMMON_DASHBOARD_WAIT_TIMEOUT): Promise<void> {
Logger.debug('WorkspaceDetails.waitTabsPresence');

const workspaceDetailsTabs: Array<string> = [
'Overview',
'Projects',
'Containers',
'Servers',
'Env Variables',
'Volumes',
'Config',
'SSH',
'Plugins',
'Editors'
];
Logger.debug();

const workspaceDetailsTabs: Array<string> = ['Overview', 'Devfile', 'DevWorkspace', 'Logs', 'Events'];

for (const tabTitle of workspaceDetailsTabs) {
const workspaceDetailsTabLocator: By = this.getTabLocator(tabTitle);
Expand All @@ -124,22 +117,39 @@ export class WorkspaceDetails {
}

async selectTab(tabTitle: string, timeout: number = TIMEOUT_CONSTANTS.TS_COMMON_DASHBOARD_WAIT_TIMEOUT): Promise<void> {
Logger.debug(`WorkspaceDetails.selectTab ${tabTitle}`);
Logger.debug(`${tabTitle}`);

await this.clickOnTab(tabTitle, timeout);
await this.waitTabSelected(tabTitle, timeout);
}

private getWorkspaceTitleLocator(workspaceName: string): By {
return By.css(`che-row-toolbar[che-title='${workspaceName}']`);
async clickStorageTypeInfo(): Promise<void> {
Logger.debug();

await this.driverHelper.waitAndClick(WorkspaceDetails.STORAGE_TYPE_INFO_BUTTON);
}

private getTabLocator(tabTitle: string): By {
return By.xpath(`//md-tabs-canvas//md-tab-item//span[text()='${tabTitle}']`);
async getOpenStorageTypeDocumentationLink(): Promise<string> {
Logger.debug();

return await this.driverHelper.waitAndGetElementAttribute(WorkspaceDetails.STORAGE_TYPE_DOC_LINK, 'href');
}

async closeStorageTypeInfo(): Promise<void> {
Logger.debug();

await this.driverHelper.waitAndClick(WorkspaceDetails.CLOSE_STORAGE_TYPE_INFO_BUTTON);
}

async getDevfileDocumentationLink(): Promise<string> {
return await this.driverHelper.waitAndGetElementAttribute(WorkspaceDetails.DEVFILE_DOC_LINK, 'href');
}

private getWorkspaceTitleLocator(workspaceName: string): By {
return By.xpath(`//h1[text()='${workspaceName}']`);
}

private getSelectedTabLocator(tabTitle: string): By {
return By.xpath(`//md-tabs-canvas[@role='tablist']//md-tab-item[@aria-selected='true']//span[text()='${tabTitle}']`);
private getTabLocator(tabTitle: string): By {
return By.xpath(`//button[contains(@id,'${tabTitle}')]`);
}

private async waitSaveButton(timeout: number = TIMEOUT_CONSTANTS.TS_COMMON_DASHBOARD_WAIT_TIMEOUT): Promise<void> {
Expand All @@ -165,9 +175,4 @@ export class WorkspaceDetails {
const workspaceDetailsTabLocator: By = this.getTabLocator(tabTitle);
await this.driverHelper.waitAndClick(workspaceDetailsTabLocator, timeout);
}

private async waitTabSelected(tabTitle: string, timeout: number = TIMEOUT_CONSTANTS.TS_COMMON_DASHBOARD_WAIT_TIMEOUT): Promise<void> {
const selectedTabLocator: By = this.getSelectedTabLocator(tabTitle);
await this.driverHelper.waitVisibility(selectedTabLocator, timeout);
}
}
27 changes: 27 additions & 0 deletions tests/e2e/resources/default-devfile.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
kind: DevWorkspace
apiVersion: workspace.devfile.io/v1alpha2
metadata:
name: default
spec:
started: true
template:
projects:
- name: web-nodejs-sample
git:
remotes:
origin: "https://github.com/che-samples/web-nodejs-sample.git"
commands:
- id: say-hello
exec:
component: che-code-runtime-description
commandLine: echo "Hello from $(pwd)"
workingDir: ${PROJECT_SOURCE}/app
contributions:
- name: che-code
uri: https://eclipse-che.github.io/che-plugin-registry/main/v3/plugins/che-incubator/che-code/latest/devfile.yaml
components:
- name: che-code-runtime-description
container:
env:
- name: CODE_HOST
value: 0.0.0.0
Loading

0 comments on commit e5e1d68

Please sign in to comment.