e2e tests

This commit is contained in:
Denys Vuika
2017-10-19 13:47:25 +01:00
parent aba476c15f
commit 7f12841e5a
45 changed files with 3213 additions and 26 deletions

View File

@@ -0,0 +1,35 @@
/*!
* @license
* Copyright 2017 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ElementFinder, element, by, ExpectedConditions as EC, browser } from 'protractor';
import { BROWSER_WAIT_TIMEOUT } from '../configs';
export abstract class Component {
component: ElementFinder;
constructor(selector: string, ancestor?: ElementFinder) {
const locator = by.css(selector);
this.component = ancestor
? ancestor.element(locator)
: element(locator);
}
wait() {
return browser.wait(EC.presenceOf(this.component), BROWSER_WAIT_TIMEOUT);
}
}

View File

@@ -0,0 +1,24 @@
/*!
* @license
* Copyright 2017 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './login/login';
export * from './header/header';
export * from './header/user-info';
export * from './data-table/data-table';
export * from './pagination/pagination';
export * from './sidenav/sidenav';
export * from './toolbar/toolbar';

View File

@@ -0,0 +1,110 @@
/*!
* @license
* Copyright 2017 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ElementFinder, ElementArrayFinder, promise, by, browser, ExpectedConditions as EC } from 'protractor';
import { BROWSER_WAIT_TIMEOUT } from '../../configs';
import { Component } from '../component';
export class DataTable extends Component {
private static selectors = {
root: 'adf-datatable',
head: 'table > thead',
columnHeader: 'tr > th',
sortedColumnHeader: `
th.adf-data-table__header--sorted-asc,
th.adf-data-table__header--sorted-desc
`,
body: 'table > tbody',
row: 'tr'
};
private head: ElementFinder = this.component.element(by.css(DataTable.selectors.head));
private body: ElementFinder = this.component.element(by.css(DataTable.selectors.body));
constructor(ancestor?: ElementFinder) {
super(DataTable.selectors.root, ancestor);
}
// Wait methods (waits for elements)
waitForHeader() {
return browser.wait(EC.presenceOf(this.head), BROWSER_WAIT_TIMEOUT);
}
// waitForEmptyState() {}
// Header/Column methods
getColumnHeaders(): ElementArrayFinder {
const locator = by.css(DataTable.selectors.columnHeader);
return this.head.all(locator);
}
getNthColumnHeader(nth: number): ElementFinder {
return this.getColumnHeaders().get(nth - 1);
}
getColumnHeaderByLabel(label: string): ElementFinder {
const locator = by.cssContainingText(DataTable.selectors.columnHeader, label);
return this.head.element(locator);
}
getSortedColumnHeader(): ElementFinder {
const locator = by.css(DataTable.selectors.sortedColumnHeader);
return this.head.element(locator);
}
sortByColumn(columnName: string): Promise<void> {
const column = this.getColumnHeaderByLabel(columnName);
const click = browser.actions().mouseMove(column).click();
return click.perform();
}
// Rows methods
getRows(): ElementArrayFinder {
return this.body.all(by.css(DataTable.selectors.row));
}
getNthRow(nth: number): ElementFinder {
return this.getRows().get(nth - 1);
}
getRowByContainingText(text: string): ElementFinder {
const locator = by.cssContainingText(DataTable.selectors.row, text);
return this.body.element(locator);
}
countRows(): promise.Promise<number> {
return this.getRows().count();
}
// Navigation/selection methods
doubleClickOnRowByContainingText(text: string): promise.Promise<void> {
const row = this.getRowByContainingText(text);
const dblClick = browser.actions().mouseMove(row).click().click();
return dblClick.perform();
}
clickOnRowByContainingText(text: string): promise.Promise<void> {
const row = this.getRowByContainingText(text);
const click = browser.actions().mouseMove(row).click();
return click.perform();
}
}

View File

@@ -0,0 +1,106 @@
/*!
* @license
* Copyright 2017 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ElementFinder, by, browser, protractor, ExpectedConditions as EC } from 'protractor';
import { BROWSER_WAIT_TIMEOUT } from '../../configs';
import { Component } from '../component';
export class CreateOrEditFolderDialog extends Component {
private static selectors = {
root: '.mat-dialog-container',
title: '.mat-dialog-title',
nameInput: '.mat-dialog-container .mat-input-element[placeholder="Name"]',
descriptionTextArea: '.mat-dialog-container .mat-input-element[placeholder="Description"]',
button: '.mat-dialog-actions button',
validationMessage: '.mat-hint span'
};
title: ElementFinder = this.component.element(by.css(CreateOrEditFolderDialog.selectors.title));
nameInput: ElementFinder = this.component.element(by.css(CreateOrEditFolderDialog.selectors.nameInput));
descriptionTextArea: ElementFinder = this.component.element(by.css(CreateOrEditFolderDialog.selectors.descriptionTextArea));
createButton: ElementFinder = this.component.element(by.cssContainingText(CreateOrEditFolderDialog.selectors.button, 'Create'));
cancelButton: ElementFinder = this.component.element(by.cssContainingText(CreateOrEditFolderDialog.selectors.button, 'Cancel'));
updateButton: ElementFinder = this.component.element(by.cssContainingText(CreateOrEditFolderDialog.selectors.button, 'Update'));
validationMessage: ElementFinder = this.component.element(by.css(CreateOrEditFolderDialog.selectors.validationMessage));
constructor(ancestor?: ElementFinder) {
super(CreateOrEditFolderDialog.selectors.root, ancestor);
}
waitForDialogToOpen() {
return browser.wait(EC.presenceOf(this.title), BROWSER_WAIT_TIMEOUT);
}
waitForDialogToClose() {
return browser.wait(EC.stalenessOf(this.title), BROWSER_WAIT_TIMEOUT);
}
getTitle(): Promise<string> {
return this.title.getText();
}
isValidationMessageDisplayed(): Promise<boolean> {
return this.validationMessage.isDisplayed();
}
getValidationMessage(): Promise<string> {
return this.isValidationMessageDisplayed()
.then(() => this.validationMessage.getText())
.catch(() => '');
}
enterName(name: string): CreateOrEditFolderDialog {
const { nameInput } = this;
nameInput.clear();
nameInput.sendKeys(name);
return this;
}
deleteNameWithBackspace(): Promise<void> {
const { nameInput } = this;
return nameInput.clear()
.then(() => {
return nameInput.sendKeys(' ', protractor.Key.CONTROL, 'a', protractor.Key.NULL, protractor.Key.BACK_SPACE);
});
}
enterDescription(description: string): CreateOrEditFolderDialog {
const { descriptionTextArea } = this;
descriptionTextArea.clear();
descriptionTextArea.sendKeys(description);
return this;
}
clickCreate() {
return this.createButton.click();
}
clickCancel() {
return this.cancelButton.click()
.then(() => this.waitForDialogToClose());
}
clickUpdate() {
return this.updateButton.click();
}
}

View File

@@ -0,0 +1,34 @@
/*!
* @license
* Copyright 2017 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ElementFinder, by } from 'protractor';
import { Component } from '../component';
import { UserInfo } from './user-info';
export class Header extends Component {
private locators = {
logoLink: by.css('.mdl-layout-title'),
userInfo: by.css('app-current-user')
};
logoLink: ElementFinder = this.component.element(this.locators.logoLink);
userInfo: UserInfo = new UserInfo(this.component);
constructor(ancestor?: ElementFinder) {
super('app-header', ancestor);
}
}

View File

@@ -0,0 +1,56 @@
/*!
* @license
* Copyright 2017 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ElementFinder, ElementArrayFinder, element, by, promise } from 'protractor';
import { Menu } from '../menu/menu';
import { Component } from '../component';
export class UserInfo extends Component {
private locators = {
avatar: by.css('.current-user__avatar'),
fullName: by.css('.current-user__full-name'),
menuItems: by.css('[md-menu-item]')
};
fullName: ElementFinder = this.component.element(this.locators.fullName);
avatar: ElementFinder = this.component.element(this.locators.avatar);
menu: Menu = new Menu();
constructor(ancestor?: ElementFinder) {
super('app-current-user', ancestor);
}
openMenu(): promise.Promise<Menu> {
const { menu, avatar } = this;
return avatar.click()
.then(() => menu.wait())
.then(() => menu);
}
get name(): Promise<string> {
return this.fullName.getText();
}
signOut(): Promise<void> {
return this.openMenu()
.then(menu => {
menu.clickMenuItem('Sign out');
});
}
}

View File

@@ -0,0 +1,67 @@
/*!
* @license
* Copyright 2017 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { by, ElementFinder } from 'protractor';
import { Component } from '../component';
export class LoginComponent extends Component {
static selector = 'alfresco-login';
private locators = {
usernameInput: by.css('input#username'),
passwordInput: by.css('input#password'),
submitButton: by.css('button#login-button'),
errorMessage: by.css('.login-error-message')
};
usernameInput: ElementFinder = this.component.element(this.locators.usernameInput);
passwordInput: ElementFinder = this.component.element(this.locators.passwordInput);
submitButton: ElementFinder = this.component.element(this.locators.submitButton);
errorMessage: ElementFinder = this.component.element(this.locators.errorMessage);
constructor(ancestor?: ElementFinder) {
super(LoginComponent.selector, ancestor);
}
enterUsername(username: string): LoginComponent {
const { usernameInput } = this;
usernameInput.clear();
usernameInput.sendKeys(username);
return this;
}
enterPassword(password: string): LoginComponent {
const { passwordInput } = this;
passwordInput.clear();
passwordInput.sendKeys(password);
return this;
}
enterCredentials(username: string, password: string) {
this.enterUsername(username).enterPassword(password);
return this;
}
submit(): Promise<void> {
return this.submitButton.click();
}
}

View File

@@ -0,0 +1,57 @@
/*!
* @license
* Copyright 2017 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ElementFinder, ElementArrayFinder, by, browser, ExpectedConditions as EC } from 'protractor';
import { BROWSER_WAIT_TIMEOUT } from '../../configs';
import { Component } from '../component';
export class Menu extends Component {
private static selectors = {
root: '.mat-menu-panel',
item: 'button.mat-menu-item'
};
items: ElementArrayFinder = this.component.all(by.css(Menu.selectors.item));
constructor(ancestor?: ElementFinder) {
super(Menu.selectors.root, ancestor);
}
wait() {
return browser.wait(EC.visibilityOf(this.items.get(0)), BROWSER_WAIT_TIMEOUT);
}
getNthItem(nth: number): ElementFinder {
return this.items.get(nth - 1);
}
getItemByLabel(label: string): ElementFinder {
return this.component.element(by.cssContainingText(Menu.selectors.item, label));
}
getItemTooltip(label: string): Promise<string> {
return this.getItemByLabel(label).getAttribute('title');
}
clicktNthItem(nth: number): Promise<void> {
return this.getNthItem(nth).click();
}
clickMenuItem(label: string): Promise<void> {
return this.getItemByLabel(label).click();
}
}

View File

@@ -0,0 +1,67 @@
/*!
* @license
* Copyright 2017 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ElementFinder, ElementArrayFinder, promise, by, browser, ExpectedConditions as EC } from 'protractor';
import { BROWSER_WAIT_TIMEOUT } from '../../configs';
import { Menu } from '../menu/menu';
import { Component } from '../component';
export class Pagination extends Component {
private static selectors = {
root: 'adf-pagination',
range: '.adf-pagination__range',
maxItems: '.adf-pagination__max-items',
currentPage: '.adf-pagination__current-page',
totalPages: '.adf-pagination__total-pages',
previousButton: '.adf-pagination__previous-button',
nextButton: '.adf-pagination__next-button',
maxItemsButton: '.adf-pagination__max-items + button[md-icon-button]',
pagesButton: '.adf-pagination__current-page + button[md-icon-button]'
};
range: ElementFinder = this.component.element(by.css(Pagination.selectors.range));
maxItems: ElementFinder = this.component.element(by.css(Pagination.selectors.maxItems));
currentPage: ElementFinder = this.component.element(by.css(Pagination.selectors.currentPage));
totalPages: ElementFinder = this.component.element(by.css(Pagination.selectors.totalPages));
previousButton: ElementFinder = this.component.element(by.css(Pagination.selectors.previousButton));
nextButton: ElementFinder = this.component.element(by.css(Pagination.selectors.nextButton));
maxItemsButton: ElementFinder = this.component.element(by.css(Pagination.selectors.maxItemsButton));
pagesButton: ElementFinder = this.component.element(by.css(Pagination.selectors.pagesButton));
menu: Menu = new Menu();
constructor(ancestor?: ElementFinder) {
super(Pagination.selectors.root, ancestor);
}
openMaxItemsMenu(): Promise<Menu> {
const { menu, maxItemsButton } = this;
return maxItemsButton.click()
.then(() => menu.wait())
.then(() => menu);
}
openCurrentPageMenu(): Promise<Menu> {
const { menu, pagesButton } = this;
return this.pagesButton.click()
.then(() => menu.wait())
.then(() => menu);
}
}

View File

@@ -0,0 +1,63 @@
/*!
* @license
* Copyright 2017 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ElementFinder, ElementArrayFinder, by, promise } from 'protractor';
import { Menu } from '../menu/menu';
import { Component } from '../component';
export class Sidenav extends Component {
private static selectors = {
root: 'app-sidenav',
link: '.sidenav-menu__item-link',
activeLink: '.sidenav-menu__item-link--active',
newButton: '.sidenav__section--new__button'
};
links: ElementArrayFinder = this.component.all(by.css(Sidenav.selectors.link));
activeLink: ElementFinder = this.component.element(by.css(Sidenav.selectors.activeLink));
newButton: ElementArrayFinder = this.component.all(by.css(Sidenav.selectors.newButton));
menu: Menu = new Menu();
constructor(ancestor?: ElementFinder) {
super(Sidenav.selectors.root, ancestor);
}
openNewMenu(): promise.Promise<Menu> {
const { menu, newButton } = this;
return newButton.click()
.then(() => menu.wait())
.then(() => menu);
}
isActiveByLabel(label: string): promise.Promise<boolean> {
return this
.getLinkByLabel(label)
.getWebElement()
.then(element => element.getAttribute('class'))
.then(className => className.includes(Sidenav.selectors.activeLink.replace('.', '')));
}
getLinkByLabel(label: string): ElementFinder {
return this.component.element(by.cssContainingText(Sidenav.selectors.link, label));
}
navigateToLinkByLabel(label: string): promise.Promise<void> {
return this.getLinkByLabel(label).click();
}
}

View File

@@ -0,0 +1,56 @@
/*!
* @license
* Copyright 2017 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ElementFinder, ElementArrayFinder, by, promise } from 'protractor';
import { Menu } from '../menu/menu';
import { Component } from '../component';
export class ToolbarActions extends Component {
private static selectors = {
root: 'adf-toolbar',
button: '.mat-icon-button'
};
menu: Menu = new Menu();
buttons: ElementArrayFinder = this.component.all(by.css(ToolbarActions.selectors.button));
constructor(ancestor?: ElementFinder) {
super(ToolbarActions.selectors.root, ancestor);
}
isEmpty(): Promise<boolean> {
return this.buttons.count().then(count => (count === 0));
}
getButtonByLabel(label: string): ElementFinder {
return this.component.element(by.cssContainingText(ToolbarActions.selectors.button, label));
}
getButtonByTitleAttribute(title: string): ElementFinder {
return this.component.element(by.css(`${ToolbarActions.selectors.button}[title="${title}"]`));
}
openMoreMenu(): promise.Promise<Menu> {
const { menu } = this;
const moreButton = this.getButtonByTitleAttribute('More actions');
return moreButton
.click()
.then(() => menu.wait())
.then(() => menu);
}
}

View File

@@ -0,0 +1,37 @@
/*!
* @license
* Copyright 2017 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ElementFinder, ElementArrayFinder, by } from 'protractor';
import { Menu } from '../menu/menu';
import { Component } from '../component';
export class ToolbarBreadcrumb extends Component {
private static selectors = {
root: 'adf-breadcrumb',
item: '.adf-breadcrumb-item'
};
items: ElementArrayFinder = this.component.all(by.css(ToolbarBreadcrumb.selectors.item));
constructor(ancestor?: ElementFinder) {
super(ToolbarBreadcrumb.selectors.root, ancestor);
}
getNthItem(nth: number): ElementFinder {
return this.items.get(nth - 1);
}
}

View File

@@ -0,0 +1,34 @@
/*!
* @license
* Copyright 2017 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ElementFinder, ElementArrayFinder, by } from 'protractor';
import { Component } from '../component';
import { ToolbarActions } from './toolbar-actions';
import { ToolbarBreadcrumb } from './toolbar-breadcrumb';
export class Toolbar extends Component {
private static selectors = {
root: '.inner-layout__header'
};
actions: ToolbarActions = new ToolbarActions(this.component);
breadcrumb: ToolbarBreadcrumb = new ToolbarBreadcrumb(this.component);
constructor(ancestor?: ElementFinder) {
super(Toolbar.selectors.root, ancestor);
}
}