improve e2e configuration (#5752)

* improve e2e configuration

* fix login

* fix login

* use storage

* fix

* default oauth

* improve share file test and add baseShareUrl init in e2e

* some click script better usage
This commit is contained in:
Eugenio Romano 2020-06-06 20:12:54 +01:00 committed by GitHub
parent 6f2ec24851
commit 1c030d7b48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 184 additions and 119 deletions

View File

@ -172,7 +172,6 @@ describe('Share file', () => {
await shareDialog.clickShareLinkButton();
const sharedLink = await shareDialog.getShareLink();
await BrowserActions.closeMenuAndDialogs();
await notificationHistoryPage.checkNotifyContains('Link copied to the clipboard');
await BrowserActions.getUrl(sharedLink);
await viewerPage.checkFileNameIsDisplayed(pngFileModel.name);
});
@ -184,13 +183,11 @@ describe('Share file', () => {
await shareDialog.clickShareLinkButton();
const sharedLink = await shareDialog.getShareLink();
await shareDialog.clickCloseButton();
await notificationHistoryPage.checkNotifyContains('Link copied to the clipboard');
await contentServicesPage.clickShareButton();
await shareDialog.checkDialogIsDisplayed();
await shareDialog.clickShareLinkButton();
const secondSharedLink = await shareDialog.getShareLink();
await BrowserActions.closeMenuAndDialogs();
await notificationHistoryPage.checkNotifyContains('Link copied to the clipboard');
await expect(sharedLink).toEqual(secondSharedLink);
await BrowserActions.getUrl(sharedLink);
await viewerPage.checkFileNameIsDisplayed(pngFileModel.name);

View File

@ -75,7 +75,6 @@ describe('SSO in ADF using ACS and AIS, Download Directive, Viewer, DocumentList
browser.params.testConfig.appConfig.oauth2.host,
browser.params.testConfig.appConfig.identityHost, false, true, browser.params.testConfig.appConfig.oauth2.clientId);
await loginSsoPage.clickOnSSOButton();
await loginSsoPage.loginSSOIdentityService(acsUser.email, acsUser.password);
await navigationBarPage.clickContentServicesButton();

View File

@ -29,7 +29,7 @@ describe('Auth Guard SSO', () => {
browser.params.testConfig.appConfig.oauth2.host,
browser.params.testConfig.appConfig.identityHost,
false, true, browser.params.testConfig.appConfig.oauth2.clientId);
await loginSSOPage.clickOnSSOButton();
await loginSSOPage.loginSSOIdentityService(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password);
await BrowserActions.getUrl(browser.params.testConfig.adf.url + '/cloud/simple-app');
await browser.sleep(1000);

View File

@ -39,7 +39,6 @@ describe('Login component - SSO', () => {
await settingsPage.setProviderEcmSso(browser.params.testConfig.appConfig.ecmHost,
browser.params.testConfig.appConfig.oauth2.host,
browser.params.testConfig.appConfig.identityHost, false, true, browser.params.testConfig.appConfig.oauth2.clientId);
await loginSSOPage.clickOnSSOButton();
await loginSSOPage.loginSSOIdentityService(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password);
});

View File

@ -29,7 +29,6 @@ describe('Logout component - SSO', () => {
await settingsPage.setProviderEcmSso(browser.params.testConfig.adf.url,
browser.params.testConfig.appConfig.oauth2.host,
browser.params.testConfig.appConfig.identityHost, false, true, browser.params.testConfig.appConfig.oauth2.clientId, '/login');
await loginSSOPage.clickOnSSOButton();
await loginSSOPage.loginSSOIdentityService(browser.params.testConfig.admin.email, browser.params.testConfig.admin.password);
await navigationBarPage.clickLogoutButton();

View File

@ -44,8 +44,6 @@ describe('User Info - SSO', () => {
browser.params.testConfig.appConfig.oauth2.host,
browser.params.testConfig.appConfig.identityHost, false, true, browser.params.testConfig.appConfig.oauth2.clientId);
await loginSSOPage.clickOnSSOButton();
await loginSSOPage.loginSSOIdentityService(identityUser.email, identityUser.password);
});

View File

@ -685,7 +685,7 @@ export class ContentServicesPage {
async selectItemWithCheckbox(itemName: string): Promise<void> {
const item: ElementFinder = element(by.css(`adf-datatable-row[aria-label="${itemName}"] mat-checkbox .mat-checkbox-input`));
await BrowserVisibility.waitUntilElementIsVisible(item);
await BrowserActions.click(item);
await BrowserActions.clickScript(item);
}
async unSelectItemWithCheckbox(itemName: string): Promise<void> {

View File

@ -162,9 +162,7 @@ export class TasksCloudDemoPage {
}
async clickStartNewTaskButton() {
await BrowserVisibility.waitUntilElementIsClickable(this.createButton);
await BrowserActions.click(this.createButton);
await BrowserVisibility.waitUntilElementIsClickable(this.newTaskButton);
await BrowserActions.click(this.newTaskButton);
}

View File

@ -24,7 +24,7 @@ export class ShareDialogPage {
togglePage = new TogglePage();
dateTimePickerPage = new DateTimePickerPage();
shareDialog = element(by.css('adf-share-dialog'));
dialogTitle = element(by.css('[data-automation-id="adf-share-dialog-title"]'));
dialogTitle = element.all(by.css('[data-automation-id="adf-share-dialog-title"]')).first();
shareToggle = element(by.css('[data-automation-id="adf-share-toggle"] label'));
expireToggle = element(by.css(`[data-automation-id="adf-expire-toggle"] label`));
shareToggleChecked = element(by.css('mat-dialog-container mat-slide-toggle.mat-checked'));

View File

@ -67,7 +67,7 @@ export class PermissionsPage {
async clickUserOrGroup(name): Promise<void> {
const userOrGroupName: ElementFinder = element(by.cssContainingText('mat-list-option .mat-list-text', name));
await BrowserActions.click(userOrGroupName);
await BrowserActions.clickScript(userOrGroupName);
await BrowserActions.click(this.addButton);
}

View File

@ -408,7 +408,7 @@ export class TaskDetailsPage {
}
async clickCompleteFormTask(): Promise<void> {
await BrowserActions.click(this.completeFormTask);
await BrowserActions.clickScript(this.completeFormTask);
}
async getEmptyTaskDetailsMessage(): Promise<string> {

View File

@ -27,6 +27,9 @@ const HR_USER_PASSWORD = process.env.HR_USER_PASSWORD || "defaulthruserpassword"
const USERNAME_ADF = process.env.USERNAME_ADF || "defaultuser";
const PASSWORD_ADF = process.env.PASSWORD_ADF || "defaultuserpassword";
const REDIRECT_URI = process.env.REDIRECT_URI || "/";
const REDIRECT_URI_LOGOUT = process.env.REDIRECT_URI_LOGOUT || "/logout";
const SCREENSHOT_URL = process.env.SCREENSHOT_URL || HOST;
const SCREENSHOT_PASSWORD = process.env.SCREENSHOT_PASSWORD || process.env.PASSWORD_ADF;
const SCREENSHOT_USERNAME = process.env.SCREENSHOT_USERNAME || process.env.USERNAME_ADF;
@ -46,8 +49,12 @@ const appConfig = {
"secret": "",
"implicitFlow": true,
"silentLogin": true,
"redirectUri": "/",
"redirectUriLogout": "/logout"
"redirectUri": REDIRECT_URI,
"redirectUriLogout": REDIRECT_URI_LOGOUT,
"publicUrls": [
"**/preview/s/*",
"**/settings"
]
}
};

View File

@ -38,6 +38,10 @@ export class CoreAutomationService {
setup() {
const adfProxy = window['adf'] || {};
adfProxy.getConfigField = (field: string): any => {
return this.appConfigService.get(field);
};
adfProxy.setConfigField = (field: string, value: string) => {
this.appConfigService.config[field] = JSON.parse(value);
};
@ -50,6 +54,10 @@ export class CoreAutomationService {
this.storageService.removeItem(key);
};
adfProxy.getStorageItem = (key: string): string => {
return this.storageService.getItem(key);
};
adfProxy.setUserPreference = (key: string, data: any) => {
this.userPreferencesService.set(key, data);
};

View File

@ -22,14 +22,25 @@ export class ApiService {
apiService: AlfrescoApi;
config: AlfrescoApiConfig;
config: AlfrescoApiConfig = new AlfrescoApiConfig({
authType: 'OAUTH',
oauth2: {
scope: 'openid',
secret: '',
implicitFlow: false,
silentLogin: false,
redirectUri: '/',
redirectUriLogout: '/logout'
}
});
constructor(clientIdOrAppConfig?: AlfrescoApiConfig | string, host?: string, hostSso?: string, provider?: string) {
if (browser.params.testConfig && browser.params.testConfig.appConfig) {
this.config = { ...browser.params.testConfig.appConfig };
this.config.hostEcm = browser.params.testConfig.appConfig.ecmHost;
this.config.hostBpm = browser.params.testConfig.appConfig.bpmHost;
this.config.hostEcm = browser.params.testConfig.appConfig.ecmHost;
this.config.hostBpm = browser.params.testConfig.appConfig.bpmHost;
}
if (clientIdOrAppConfig && typeof clientIdOrAppConfig !== 'string') {

View File

@ -0,0 +1,34 @@
/*!
* @license
* Copyright 2019 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 interface ApplicationRepresentation {
releaseId: string;
security: SecurityRepresentation[];
name: string;
infrastructure?: InfrastructureRepresentation;
}
export interface SecurityRepresentation {
role: string;
users: string[];
groups: string[];
}
export interface InfrastructureRepresentation {
connectors?: any;
bridges?: any;
}

View File

@ -18,6 +18,7 @@
import { element, by, browser, protractor, ElementFinder } from 'protractor';
import { BrowserVisibility } from '../utils/browser-visibility';
import { BrowserActions } from '../utils/browser-actions';
import { LocalStorageUtil } from '../utils/local-storage.util';
export class LoginSSOPage {
@ -49,7 +50,10 @@ export class LoginSSOPage {
}
async login(username: string, password: string) {
if (browser.params.testConfig.appConfig.authType === 'OAUTH') {
const authType = await LocalStorageUtil.getConfigField('authType');
if (!authType || authType === 'OAUTH') {
await this.loginSSOIdentityService(username, password);
} else {
await this.loginBasicAuth(username, password);
@ -59,16 +63,13 @@ export class LoginSSOPage {
async loginSSOIdentityService(username: string, password: string) {
browser.ignoreSynchronization = true;
let currentUrl;
const loginURL: string = browser.baseUrl + (browser.params.loginRoute ? browser.params.loginRoute : '');
try {
currentUrl = await browser.getCurrentUrl();
} catch (e) {
}
await browser.get(loginURL);
const oauth2 = await LocalStorageUtil.getConfigField('oauth2');
if (!currentUrl || currentUrl === '' || currentUrl === 'data:,') {
const loginURL: string = browser.baseUrl + (browser.params.loginRoute ? browser.params.loginRoute : '');
await browser.get(loginURL);
if (oauth2 && oauth2.silentLogin === false) {
await this.clickOnSSOButton();
}
await BrowserVisibility.waitUntilElementIsVisible(this.usernameField);

View File

@ -40,14 +40,14 @@ export class DateTimePickerCalendarPage {
}
async setTime(): Promise<void> {
await BrowserActions.click(this.hourTime);
await BrowserActions.click(this.minutesTime);
await BrowserActions.clickScript(this.hourTime);
await BrowserActions.clickScript(this.minutesTime);
}
async setDate(date?: string): Promise<boolean> {
try {
if (date) {
await BrowserActions.click(element(by.cssContainingText(`.mat-datetimepicker-calendar-body-cell-content`, date)));
await BrowserActions.clickScript(element.all(by.cssContainingText(`.mat-datetimepicker-calendar-body-cell-content`, date)).first());
} else {
await this.setToday();
}
@ -63,12 +63,10 @@ export class DateTimePickerCalendarPage {
}
async setDefaultEnabledHour(): Promise<void> {
await BrowserVisibility.waitUntilElementIsVisible(this.hoursPicker);
await BrowserActions.click(this.hoursPicker.all(this.firstEnabledHourSelector).first());
}
async setDefaultEnabledMinutes() {
await BrowserVisibility.waitUntilElementIsVisible(this.minutePicker);
await BrowserActions.click(this.minutePicker.all(this.firstEnabledMinutesSelector).first());
}
}

View File

@ -26,7 +26,7 @@ export class TogglePage {
const check = await toggle.getAttribute('class');
if (check.indexOf('mat-checked') < 0) {
const elem = toggle.all(by.css('input')).first();
await BrowserActions.click(elem);
await BrowserActions.clickScript(elem);
}
}
@ -35,7 +35,7 @@ export class TogglePage {
const check = await toggle.getAttribute('class');
if (check.indexOf('mat-checked') >= 0) {
const elem = toggle.all(by.css('input')).first();
await BrowserActions.click(elem);
await BrowserActions.clickScript(elem);
}
}
}

View File

@ -56,7 +56,7 @@ export class PaginationPage {
}
async clickItemsPerPageDropdown(): Promise<void> {
await BrowserActions.click(this.itemsPerPageOpenDropdown);
await BrowserActions.clickScript(this.itemsPerPageOpenDropdown);
}
async checkPaginationIsNotDisplayed() {

View File

@ -27,16 +27,16 @@ export class BrowserActions {
await BrowserVisibility.waitUntilElementIsClickable(elementFinder);
await elementFinder.click();
} catch (clickErr) {
try {
await browser.executeScript(`arguments[0].scrollIntoView();`, elementFinder);
await browser.executeScript(`arguments[0].click();`, elementFinder);
} catch (jsErr) {
Logger.error(`click error element ${elementFinder.locator()}`);
throw jsErr;
}
Logger.error(`click error element ${elementFinder.locator().toString()} consider to use directly clickScript`);
await this.clickScript(elementFinder);
}
}
static async clickScript(elementFinder: ElementFinder): Promise<void> {
await browser.executeScript(`arguments[0].scrollIntoView();`, elementFinder);
await browser.executeScript(`arguments[0].click();`, elementFinder);
}
static async waitUntilActionMenuIsVisible(): Promise<void> {
const actionMenu = element.all(by.css('div[role="menu"]')).first();
await BrowserVisibility.waitUntilElementIsVisible(actionMenu);

View File

@ -19,6 +19,12 @@ import { browser } from 'protractor';
export class LocalStorageUtil {
static async getConfigField(field: string): Promise<any> {
return browser.executeScript(
'return window.adf ? window.adf.getConfigField(`' + field + '`) : null;'
);
}
static async setConfigField(field: string, value: string): Promise<void> {
await browser.executeScript(
'window.adf.setConfigField(`' + field + '`, `' + value + '`);'
@ -37,6 +43,12 @@ export class LocalStorageUtil {
);
}
static async getStorageItem(field: string): Promise<any> {
return browser.executeScript(
'return window.adf ? window.adf.getStorageItem(`' + field + '`) : null;'
);
}
static async setUserPreference(field: string, value: any): Promise<void> {
await browser.executeScript(
'window.adf.setUserPreference(`' + field + '`, `' + value + '`);'

View File

@ -40,6 +40,11 @@ export class Application {
Logger.info(`[Application] Application: '${applicationId}' was deleted successfully.`);
}
async undeploy(applicationName: string): Promise<any> {
await this.requestApiHelper.delete(`${this.endPoint}${applicationName}`);
Logger.info(`[Application] Application ${applicationName} was undeployed successfully.`);
}
async deleteDescriptor(name: string): Promise<void> {
await this.requestApiHelper.delete(`v1/descriptors/${name}`);
Logger.info(`[Descriptor] Descriptor: '${name}' was deleted successfully.`);

View File

@ -1,69 +0,0 @@
/*!
* @license
* Copyright 2019 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 { Api } from '../../core/actions/api';
import { Application } from './application';
import { Logger } from '../../core/utils/logger';
import { browser } from 'protractor';
import { ResultSetPaging } from '@alfresco/js-api';
export class DeploymentAPI extends Api {
public application: Application;
constructor(ROOT: string = 'deployment-service') {
super(ROOT);
}
async setUp(): Promise<DeploymentAPI> {
await this.login();
this.application = new Application(this);
return this;
}
async tearDown(): Promise<void> {
await this.api.logout();
}
private async login(): Promise<void> {
try {
await this.api.login(
browser.params.adminapp.devops,
browser.params.adminapp.devops_password
);
} catch (error) {
Logger.error(error);
}
}
async deploy(releasedProject: any): Promise<void> {
await this.application.deploy(releasedProject);
}
async deleteDescriptor(name: string): Promise<void> {
await this.application.deleteDescriptor(name);
}
async getDescriptors(): Promise<ResultSetPaging> {
const descriptors = await this.application.getDescriptors();
return descriptors;
}
async getApplicationByStatus(status: string): Promise<ResultSetPaging> {
const applications = this.application.getApplicationsByStatus(status);
return applications;
}
}

View File

@ -0,0 +1,72 @@
/*!
* @license
* Copyright 2019 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 { NodeEntry } from '@alfresco/js-api';
import { E2eRequestApiHelper } from '../../core/actions/e2e-request-api.helper';
import { Api } from '../../core/actions/api';
import { ApplicationRepresentation } from '../../core/models/application-model';
import { Logger } from '../../core/utils/logger';
import { ApiUtil } from '../../core/actions/api.util';
export class Descriptor {
requestApiHelper: E2eRequestApiHelper;
endPoint = `/v1/descriptors/`;
constructor(api: Api) {
this.requestApiHelper = new E2eRequestApiHelper(api);
}
async create(model: ApplicationRepresentation): Promise<void> {
try {
await this.requestApiHelper.post<NodeEntry>(this.endPoint, {
bodyParam: model
});
Logger.info(`[Descriptor] Descriptor has been created with name: ${model.name}.`);
} catch (error) {
Logger.error(`[Descriptor] Create descriptor ${model.name} failed with message: ${error.message}`);
throw error;
}
}
async delete(name: string): Promise<void> {
try {
await this.retryUntilDescriptorIsInStatus(name, `DescriptorCreated`);
await this.requestApiHelper.delete(`${this.endPoint}${name}`);
Logger.info(`[Descriptor] Descriptor '${name}' was deleted successfully.`);
} catch (error) {
Logger.error(`[Descriptor] Delete descriptor ${name} failed with message: ${error.message}`);
}
}
async get(name: string): Promise<any> {
Logger.info(`[Descriptor] Get descriptor ${name} details.`);
try {
return this.requestApiHelper.get<any>(`${this.endPoint}${name}`);
} catch (error) {
Logger.error(`[Descriptor] Get descriptor ${name} details failed with message: ${error.message}`);
}
}
async retryUntilDescriptorIsInStatus(name: string, expectedStatus: string): Promise<any> {
const predicate = (result: { status: string }) => {
return result.status === expectedStatus;
};
const apiCall = async () => this.get(name);
return ApiUtil.waitForApi(apiCall, predicate);
}
}

View File

@ -22,7 +22,7 @@ export * from './process-instances.service';
export * from './message-events.service';
export * from './form-cloud.service';
export * from './tasks.service';
export * from './deployment-api';
export * from './modeling-api';
export * from './application';
export * from './descriptor';
export * from './project';

View File

@ -50,25 +50,21 @@ export class ProcessFiltersCloudComponentPage {
async clickProcessFilter(filterName: string): Promise<void> {
this.filter = this.getProcessFilterLocatorByFilterName(filterName);
await BrowserVisibility.waitUntilElementIsClickable(this.filter);
await BrowserActions.click(this.filter);
}
async clickAllProcessesFilter(): Promise<void> {
this.filter = this.getProcessFilterLocatorByFilterName('all-processes');
await BrowserVisibility.waitUntilElementIsClickable(this.filter);
await BrowserActions.click(this.filter);
}
async clickCompletedProcessesFilter(): Promise<void> {
this.filter = this.getProcessFilterLocatorByFilterName('completed-processes');
await BrowserVisibility.waitUntilElementIsClickable(this.filter);
await BrowserActions.click(this.filter);
}
async clickRunningProcessesFilter(): Promise<void> {
this.filter = this.getProcessFilterLocatorByFilterName('running-processes');
await BrowserVisibility.waitUntilElementIsClickable(this.filter);
await BrowserActions.click(this.filter);
}

View File

@ -63,7 +63,6 @@ export class StartProcessCloudPage {
async selectFirstOptionFromProcessDropdown(): Promise<void> {
await this.clickProcessDropdownArrow();
const selectFirstProcessDropdown = element.all(by.css('.mat-option-text')).first();
await BrowserVisibility.waitUntilElementIsPresent(selectFirstProcessDropdown);
await BrowserActions.click(selectFirstProcessDropdown);
}

View File

@ -199,6 +199,7 @@ exports.config = {
await LocalStorageUtil.setStorageItem('ecmHost', browser.params.testConfig.appConfig.ecmHost);
await LocalStorageUtil.setStorageItem('bpmHost', browser.params.testConfig.appConfig.bpmHost);
await LocalStorageUtil.setStorageItem('providers', browser.params.testConfig.appConfig.provider);
await LocalStorageUtil.setStorageItem('baseShareUrl', HOST);
if (browser.params.testConfig.appConfig.authType === 'OAUTH') {
await LocalStorageUtil.setStorageItem('authType', browser.params.testConfig.appConfig.authType);