mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-31 17:38:48 +00:00
[ci:force] - fixing lint
This commit is contained in:
@@ -8,5 +8,6 @@
|
||||
"@alfresco/js-api": ["../../../dist/libs/js-api"],
|
||||
"@alfresco/js-api/*": ["../../../dist/libs/js-api/*"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "index.ts"]
|
||||
}
|
||||
|
@@ -6,5 +6,5 @@
|
||||
"useDefineForClassFields": false
|
||||
},
|
||||
"files": ["src/test.ts"],
|
||||
"include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts"]
|
||||
"include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts", "index.ts"]
|
||||
}
|
||||
|
@@ -15,5 +15,6 @@
|
||||
},
|
||||
"target": "ES2022",
|
||||
"useDefineForClassFields": false
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "index.ts"]
|
||||
}
|
||||
|
@@ -6,5 +6,5 @@
|
||||
"useDefineForClassFields": false
|
||||
},
|
||||
"files": ["src/test.ts"],
|
||||
"include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts"]
|
||||
"include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts", "index.ts"]
|
||||
}
|
||||
|
@@ -15,4 +15,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const isBrowser = (): boolean => typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
||||
export const isBrowser = (): boolean => typeof window?.document !== 'undefined';
|
||||
|
@@ -0,0 +1,138 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* 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 { ApiService } from '../../../../shared/api/api.service';
|
||||
import { StringUtil } from '../../../../shared/utils/string.util';
|
||||
import { ApiUtil } from '../../../../shared/api/api.util';
|
||||
import { Logger } from '../../utils/logger';
|
||||
import { browser } from 'protractor';
|
||||
|
||||
export class GroupIdentityService {
|
||||
api: ApiService;
|
||||
|
||||
constructor(api: ApiService) {
|
||||
this.api = api;
|
||||
}
|
||||
|
||||
async createIdentityGroup(groupName = StringUtil.generateRandomString(5)): Promise<any> {
|
||||
await this.createGroup(groupName);
|
||||
const group = await this.getGroupInfoByGroupName(groupName);
|
||||
return group;
|
||||
}
|
||||
|
||||
async deleteIdentityGroup(groupId): Promise<void> {
|
||||
await this.deleteGroup(groupId);
|
||||
}
|
||||
|
||||
async createGroup(groupName: string): Promise<any> {
|
||||
const path = '/groups';
|
||||
const method = 'POST';
|
||||
const queryParams = {};
|
||||
const postBody = {
|
||||
name: `${groupName}-${browser.params.groupSuffix}`
|
||||
};
|
||||
const data = await this.api.performIdentityOperation(path, method, queryParams, postBody);
|
||||
return data;
|
||||
}
|
||||
|
||||
async deleteGroup(groupId: string): Promise<any> {
|
||||
const path = `/groups/${groupId}`;
|
||||
const method = 'DELETE';
|
||||
const queryParams = {};
|
||||
const postBody = {};
|
||||
const data = await this.api.performIdentityOperation(path, method, queryParams, postBody);
|
||||
return data;
|
||||
}
|
||||
|
||||
async getGroupInfoByGroupName(groupName: string): Promise<any> {
|
||||
Logger.log(`Get GroupInfoByGroupName ${groupName}`);
|
||||
|
||||
const predicate = (result: any) => !!result;
|
||||
|
||||
const apiCall = async () => {
|
||||
try {
|
||||
const path = `/groups`;
|
||||
const method = 'GET';
|
||||
const queryParams = { search: groupName };
|
||||
const postBody = {};
|
||||
|
||||
const data = await this.api.performIdentityOperation(path, method, queryParams, postBody);
|
||||
|
||||
Logger.log(`Data ${JSON.stringify(data)}`);
|
||||
|
||||
return data[0];
|
||||
Logger.error('Group not found');
|
||||
} catch (error) {
|
||||
Logger.error('Group not found');
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return ApiUtil.waitForApi(apiCall, predicate);
|
||||
}
|
||||
|
||||
async assignRole(groupId: string, roleId: string, roleName: string): Promise<any> {
|
||||
Logger.log(`Assign to group ${groupId} Role ${roleName}`);
|
||||
|
||||
const path = `/groups/${groupId}/role-mappings/realm`;
|
||||
const method = 'POST';
|
||||
const queryParams = {};
|
||||
const postBody = [{ id: roleId, name: roleName }];
|
||||
|
||||
const data = await this.api.performIdentityOperation(path, method, queryParams, postBody);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add client roles.
|
||||
* @param groupId ID of the target group
|
||||
* @param clientId ID of the client
|
||||
* @param roleId ID of the clientRole
|
||||
* @param roleName of the clientRole
|
||||
* @returns Promise
|
||||
*/
|
||||
async addClientRole(groupId: string, clientId: string, roleId: string, roleName: string): Promise<any> {
|
||||
const path = `/groups/${groupId}/role-mappings/clients/${clientId}`;
|
||||
const method = 'POST';
|
||||
const queryParams = {};
|
||||
const postBody = [
|
||||
{
|
||||
id: roleId,
|
||||
name: roleName,
|
||||
composite: false,
|
||||
clientRole: true,
|
||||
containerId: clientId
|
||||
}
|
||||
];
|
||||
return this.api.performIdentityOperation(path, method, queryParams, postBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the client ID using the app name.
|
||||
* @param applicationName Name of the app
|
||||
* @returns client ID string
|
||||
*/
|
||||
async getClientIdByApplicationName(applicationName: string): Promise<any> {
|
||||
const path = `/clients`;
|
||||
const method = 'GET';
|
||||
const queryParams = { clientId: applicationName };
|
||||
const postBody = {};
|
||||
|
||||
const data = await this.api.performIdentityOperation(path, method, queryParams, postBody);
|
||||
return data[0].id;
|
||||
}
|
||||
}
|
34
lib/testing/src/lib/protractor/core/models/tenant.ts
Normal file
34
lib/testing/src/lib/protractor/core/models/tenant.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* 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 { StringUtil } from '../../../shared/utils/string.util';
|
||||
|
||||
/**
|
||||
* Create tenant JSON Object
|
||||
* @param details - JSON object used to overwrite the default values
|
||||
*/
|
||||
export class Tenant {
|
||||
active = true;
|
||||
configuration = 'DefaultConfig';
|
||||
domain = 'DefaultDomain';
|
||||
maxUsers = 10;
|
||||
name = StringUtil.generateRandomString();
|
||||
|
||||
constructor(details?: any) {
|
||||
Object.assign(this, details);
|
||||
}
|
||||
}
|
@@ -0,0 +1,569 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* 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 { browser, by, element, protractor, ElementFinder, ElementArrayFinder, $, $$ } from 'protractor';
|
||||
import { BrowserVisibility } from '../utils/browser-visibility';
|
||||
import { BrowserActions } from '../utils/browser-actions';
|
||||
import { Logger } from '../utils/logger';
|
||||
import { materialLocators } from './public-api';
|
||||
|
||||
const MAX_LOADING_TIME = 120000;
|
||||
|
||||
export class DataTableComponentPage {
|
||||
rootElement: ElementFinder;
|
||||
list: ElementArrayFinder;
|
||||
contents: ElementArrayFinder;
|
||||
tableBody: ElementFinder;
|
||||
allColumns: ElementArrayFinder;
|
||||
selectedRowNumber: ElementFinder;
|
||||
allSelectedRows: ElementArrayFinder;
|
||||
selectAll: ElementFinder;
|
||||
selectAllChecked: ElementFinder;
|
||||
emptyList: ElementFinder;
|
||||
emptyListTitle: ElementFinder;
|
||||
emptyListSubtitle: ElementFinder;
|
||||
noContentContainer: ElementFinder;
|
||||
mainMenuButton: ElementFinder;
|
||||
|
||||
rows = `adf-datatable div[class*='adf-datatable-body'] adf-datatable-row[class*='adf-datatable-row']`;
|
||||
|
||||
constructor(rootElement = $$('adf-datatable').first()) {
|
||||
this.rootElement = rootElement;
|
||||
this.list = this.rootElement.$$(`div[class*='adf-datatable-body'] adf-datatable-row[class*='adf-datatable-row']`);
|
||||
this.contents = this.rootElement.$$('.adf-datatable-body span');
|
||||
this.tableBody = this.rootElement.$$(`.adf-datatable-body`).first();
|
||||
this.allColumns = this.rootElement.$$('div[data-automation-id*="auto_header_content_id"]');
|
||||
this.mainMenuButton = this.rootElement.$('[data-automation-id="adf-datatable-main-menu-button"]');
|
||||
this.selectedRowNumber = this.rootElement.$(`adf-datatable-row[class*='is-selected'] div[data-automation-id*='text_']`);
|
||||
this.allSelectedRows = this.rootElement.$$(`adf-datatable-row[class*='is-selected']`);
|
||||
this.selectAllChecked = this.rootElement.$(`div[class*='adf-datatable-header'] ${materialLocators.Checkbox.root}.mat-mdc-checkbox-checked`);
|
||||
this.selectAll = this.rootElement.$(`div[class*='adf-datatable-header'] ${materialLocators.Checkbox.root}`);
|
||||
this.emptyList = this.rootElement.$(`adf-empty-content`);
|
||||
this.emptyListTitle = this.rootElement.$(`.adf-empty-content__title`);
|
||||
this.emptyListSubtitle = this.rootElement.$(`.adf-empty-content__subtitle`);
|
||||
this.noContentContainer = $(`div[class*='adf-no-content-container']`);
|
||||
}
|
||||
|
||||
geCellElementDetail(detail: string): ElementArrayFinder {
|
||||
return $$(`adf-datatable div[title="${detail}"] span`);
|
||||
}
|
||||
|
||||
async checkAllRowsButtonIsDisplayed(): Promise<void> {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.selectAll);
|
||||
}
|
||||
|
||||
async checkAllRows(): Promise<void> {
|
||||
await BrowserActions.click(this.selectAll);
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.selectAllChecked);
|
||||
}
|
||||
|
||||
async uncheckAllRows(): Promise<void> {
|
||||
await BrowserActions.click(this.selectAll);
|
||||
await BrowserVisibility.waitUntilElementIsNotVisible(this.selectAllChecked);
|
||||
}
|
||||
|
||||
async clickCheckbox(columnName: string, columnValue: string): Promise<void> {
|
||||
const checkbox = this.getRowCheckbox(columnName, columnValue);
|
||||
await BrowserActions.click(checkbox);
|
||||
}
|
||||
|
||||
async checkRowIsNotChecked(columnName: string, columnValue: string): Promise<void> {
|
||||
const rowSelector = this.getRowCheckboxChecked(columnName, columnValue);
|
||||
await BrowserVisibility.waitUntilElementIsNotVisible(rowSelector);
|
||||
}
|
||||
|
||||
async checkRowIsChecked(columnName: string, columnValue: string): Promise<void> {
|
||||
const rowCheckbox = this.getRowCheckboxChecked(columnName, columnValue);
|
||||
await BrowserVisibility.waitUntilElementIsVisible(rowCheckbox);
|
||||
}
|
||||
|
||||
getRowCheckbox(columnName: string, columnValue: string): ElementFinder {
|
||||
return this.getRow(columnName, columnValue).$(materialLocators.Checkbox.root);
|
||||
}
|
||||
|
||||
getRowCheckboxChecked(columnName: string, columnValue: string): ElementFinder {
|
||||
return this.getRow(columnName, columnValue).$('mat-checkbox.mat-mdc-checkbox-checked');
|
||||
}
|
||||
|
||||
async checkNoRowIsSelected(): Promise<void> {
|
||||
await BrowserVisibility.waitUntilElementIsNotVisible(this.selectedRowNumber);
|
||||
}
|
||||
|
||||
async getNumberOfSelectedRows(): Promise<number> {
|
||||
return this.allSelectedRows.count();
|
||||
}
|
||||
|
||||
async selectRow(columnName: string, columnValue: string): Promise<void> {
|
||||
await BrowserActions.closeMenuAndDialogs();
|
||||
const row = this.getRow(columnName, columnValue);
|
||||
await BrowserActions.click(row);
|
||||
}
|
||||
|
||||
async selectRowWithKeyboard(columnName: string, columnValue: string): Promise<void> {
|
||||
await browser.actions().sendKeys(protractor.Key.COMMAND).perform();
|
||||
await this.selectRow(columnName, columnValue);
|
||||
await browser.actions().sendKeys(protractor.Key.NULL).perform();
|
||||
}
|
||||
|
||||
async checkRowIsSelected(columnName: string, columnValue: string): Promise<void> {
|
||||
const selectedRow = this.getCellElementByValue(columnName, columnValue).element(
|
||||
by.xpath(`ancestor::adf-datatable-row[contains(@class, 'is-selected')]`)
|
||||
);
|
||||
await BrowserVisibility.waitUntilElementIsVisible(selectedRow);
|
||||
}
|
||||
|
||||
async checkRowIsNotSelected(columnName: string, columnValue: string): Promise<void> {
|
||||
const selectedRow = this.getCellElementByValue(columnName, columnValue).element(
|
||||
by.xpath(`ancestor::adf-datatable-row[contains(@class, 'is-selected')]`)
|
||||
);
|
||||
await BrowserVisibility.waitUntilElementIsNotVisible(selectedRow);
|
||||
}
|
||||
|
||||
async getColumnValueForRow(identifyingColumn: string, identifyingValue: string, columnName: string): Promise<string> {
|
||||
const row = this.getRow(identifyingColumn, identifyingValue);
|
||||
await BrowserVisibility.waitUntilElementIsVisible(row);
|
||||
const rowColumn = row.$(`div[title="${columnName}"] span`);
|
||||
return BrowserActions.getText(rowColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the list is sorted.
|
||||
* @param sortOrder 'ASC' if the list is await expected to be sorted ascending and 'DESC' for descending
|
||||
* @param columnTitle titleColumn column
|
||||
* @param listType 'string' for string typed lists and 'number' for number typed (int, float) lists
|
||||
* @returns 'true' if the list is sorted as await expected and 'false' if it isn't
|
||||
*/
|
||||
async checkListIsSorted(sortOrder: string, columnTitle: string, listType: string = 'STRING'): Promise<any> {
|
||||
const column = $$(`div.adf-datatable-cell[title='${columnTitle}'] span`);
|
||||
await BrowserVisibility.waitUntilElementIsVisible(column.first());
|
||||
const initialList: string[] = [];
|
||||
|
||||
const length = await column.count();
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const text: string = await BrowserActions.getText(column.get(i));
|
||||
if (text.length !== 0) {
|
||||
initialList.push(text.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
let sortedList = [...initialList];
|
||||
if (listType.toLocaleLowerCase() === 'string') {
|
||||
sortedList = sortedList.sort();
|
||||
} else if (listType.toLocaleLowerCase() === 'number') {
|
||||
sortedList = sortedList.sort((a, b) => parseInt(a, 10) - parseInt(b, 10));
|
||||
} else if (listType.toLocaleLowerCase() === 'priority') {
|
||||
sortedList = sortedList.sort(this.sortPriority);
|
||||
}
|
||||
|
||||
if (['desc', 'descending'].includes(sortOrder.toLocaleLowerCase())) {
|
||||
sortedList = sortedList.reverse();
|
||||
}
|
||||
|
||||
return initialList.toString() === sortedList.toString();
|
||||
}
|
||||
|
||||
sortPriority(a: string, b: string) {
|
||||
if (a === b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (a.toLocaleLowerCase() === 'none') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (a.toLocaleLowerCase() === 'low') {
|
||||
if (b === 'none') {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (a.toLocaleLowerCase() === 'normal') {
|
||||
if (b.toLocaleLowerCase() === 'high') {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
async rightClickOnRow(columnName: string, columnValue: string): Promise<void> {
|
||||
await this.rightClickOnItem(columnName, columnValue);
|
||||
await BrowserVisibility.waitUntilElementIsVisible($('#adf-context-menu-content'));
|
||||
}
|
||||
|
||||
async getTooltip(columnName: string, columnValue: string): Promise<string> {
|
||||
return BrowserActions.getAttribute(this.getCellElementByValue(columnName, columnValue), 'title');
|
||||
}
|
||||
|
||||
async rightClickOnItem(columnName: string, columnValue: string): Promise<void> {
|
||||
const row = this.getRow(columnName, columnValue);
|
||||
await BrowserActions.rightClick(row);
|
||||
}
|
||||
|
||||
getFileHyperlink(filename: string): ElementFinder {
|
||||
return element(by.cssContainingText('adf-name-column[class*="adf-datatable-link"] span', filename));
|
||||
}
|
||||
|
||||
async numberOfRows(): Promise<number> {
|
||||
try {
|
||||
await this.waitForFirstRow();
|
||||
return this.rootElement.$$(this.rows).count();
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
async waitForFirstRow(): Promise<void> {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.rootElement.$$(this.rows).first());
|
||||
}
|
||||
|
||||
async getAllRowsColumnValues(column: string): Promise<string[]> {
|
||||
let columnValues: string[] = [];
|
||||
const columnLocator = $$(
|
||||
`adf-datatable div[class*='adf-datatable-body'] adf-datatable-row[class*='adf-datatable-row'] div[title="${column}"] span`
|
||||
);
|
||||
|
||||
await BrowserVisibility.waitUntilElementIsPresent(columnLocator.first(), 1000);
|
||||
try {
|
||||
await BrowserVisibility.waitUntilElementIsPresent(columnLocator.first(), 1000);
|
||||
columnValues = await columnLocator.filter(async (el) => el.isPresent()).map(async (el) => el.getText());
|
||||
} catch (error) {
|
||||
Logger.log(error);
|
||||
}
|
||||
|
||||
return columnValues;
|
||||
}
|
||||
|
||||
async getRowsWithSameColumnValues(columnName: string, columnValue: string) {
|
||||
const columnLocator = `div[title='${columnName}'] div[data-automation-id="text_${columnValue}"] span`;
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.rootElement.$$(columnLocator).first());
|
||||
return this.rootElement.$$(columnLocator).getText();
|
||||
}
|
||||
|
||||
async doubleClickRow(columnName: string, columnValue: string): Promise<void> {
|
||||
const row = this.getRow(columnName, columnValue);
|
||||
await BrowserActions.click(row);
|
||||
await browser.actions().sendKeys(protractor.Key.ENTER).perform();
|
||||
}
|
||||
|
||||
async waitForTableBody(): Promise<void> {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.tableBody);
|
||||
}
|
||||
|
||||
// @deprecated Use Playwright API instead
|
||||
async getFirstElementDetail(detail: string): Promise<string> {
|
||||
const firstNode = $$(`adf-datatable div[title="${detail}"] span`).first();
|
||||
return BrowserActions.getText(firstNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the list by name column.
|
||||
* @param sortOrder 'ASC' to sort the list ascendant and 'DESC' for descendant
|
||||
* @param titleColumn column title
|
||||
*/
|
||||
async sortByColumn(sortOrder: string, titleColumn: string): Promise<void> {
|
||||
const locator = $(`div[data-automation-id="auto_id_${titleColumn}"]`);
|
||||
await BrowserVisibility.waitUntilElementIsVisible(locator);
|
||||
const result = await BrowserActions.getAttribute(locator, 'class');
|
||||
|
||||
if (sortOrder.toLocaleLowerCase() === 'asc') {
|
||||
if (!result.includes('sorted-asc')) {
|
||||
if (result.includes('sorted-desc') || result.includes('sortable')) {
|
||||
await BrowserActions.click(locator);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (result.includes('sorted-asc')) {
|
||||
await BrowserActions.click(locator);
|
||||
} else if (result.includes('sortable')) {
|
||||
await BrowserActions.click(locator);
|
||||
await BrowserActions.click(locator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async checkContentIsDisplayed(columnName: string, columnValue: string, retry = 0): Promise<void> {
|
||||
Logger.log(`Wait content is displayed ${columnName} ${columnValue} retry: ${retry}`);
|
||||
const row = this.getCellElementByValue(columnName, columnValue);
|
||||
|
||||
try {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(row);
|
||||
} catch (error) {
|
||||
if (retry < 3) {
|
||||
retry++;
|
||||
await this.checkContentIsDisplayed(columnName, columnValue, retry);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async checkContentIsNotDisplayed(columnName: string, columnValue: string, retry = 0): Promise<void> {
|
||||
Logger.log(`Wait content is displayed ${columnName} ${columnValue} retry: ${retry}`);
|
||||
const row = this.getCellElementByValue(columnName, columnValue);
|
||||
|
||||
try {
|
||||
await BrowserVisibility.waitUntilElementIsNotVisible(row);
|
||||
} catch (error) {
|
||||
if (retry < 3) {
|
||||
retry++;
|
||||
await this.checkContentIsNotDisplayed(columnName, columnValue, retry);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getRow(columnName: string, columnValue: string): ElementFinder {
|
||||
return this.rootElement
|
||||
.all(
|
||||
by.xpath(
|
||||
`//div[starts-with(@title, '${columnName}')]//div[contains(@data-automation-id, '${columnValue}')]//ancestor::adf-datatable-row[contains(@class, 'adf-datatable-row')]`
|
||||
)
|
||||
)
|
||||
.first();
|
||||
}
|
||||
|
||||
// @deprecated use Playwright instead
|
||||
getRowByIndex(index: number): ElementFinder {
|
||||
return this.rootElement.element(
|
||||
by.xpath(`//div[contains(@class,'adf-datatable-body')]//adf-datatable-row[contains(@class,'adf-datatable-row')][${index}]`)
|
||||
);
|
||||
}
|
||||
|
||||
async contentInPosition(position: number): Promise<string> {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.contents.first());
|
||||
return BrowserActions.getText(this.contents.get(position - 1));
|
||||
}
|
||||
|
||||
getCellElementByValue(columnName: string, columnValue: string, columnPrefix = 'text_'): ElementFinder {
|
||||
return this.rootElement.$$(`div[title="${columnName}"] div[data-automation-id="${columnPrefix}${columnValue}"] span`).first();
|
||||
}
|
||||
|
||||
async tableIsLoaded(): Promise<void> {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.rootElement);
|
||||
}
|
||||
|
||||
async waitTillContentLoaded(): Promise<void> {
|
||||
if (await this.isSpinnerPresent()) {
|
||||
Logger.log('wait datatable loading spinner disappear');
|
||||
await BrowserVisibility.waitUntilElementIsNotVisible(
|
||||
this.rootElement.element(by.tagName(materialLocators.Progress.spinner.root)),
|
||||
MAX_LOADING_TIME
|
||||
);
|
||||
|
||||
if (await this.isEmpty()) {
|
||||
Logger.log('empty page');
|
||||
} else {
|
||||
await this.waitFirstElementPresent();
|
||||
}
|
||||
} else if (await this.isEmpty()) {
|
||||
Logger.log('empty page');
|
||||
} else {
|
||||
try {
|
||||
Logger.log('wait datatable loading spinner is present');
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.rootElement.element(by.tagName(materialLocators.Progress.spinner.root)), 2000);
|
||||
await BrowserVisibility.waitUntilElementIsNotVisible(
|
||||
this.rootElement.element(by.tagName(materialLocators.Progress.spinner.root)),
|
||||
MAX_LOADING_TIME
|
||||
);
|
||||
} catch (error) {
|
||||
Logger.error('Loading spinner is not present');
|
||||
}
|
||||
|
||||
if (await this.isEmpty()) {
|
||||
Logger.log('empty page');
|
||||
} else {
|
||||
await this.waitFirstElementPresent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async waitTillContentLoadedInfinitePagination(): Promise<void> {
|
||||
await browser.sleep(500);
|
||||
|
||||
if (await this.isInfiniteSpinnerPresent()) {
|
||||
Logger.log('wait datatable loading spinner disappear');
|
||||
await BrowserVisibility.waitUntilElementIsNotVisible(element(by.tagName(materialLocators.Progress.bar.root)));
|
||||
|
||||
if (await this.isEmpty()) {
|
||||
Logger.log('empty page');
|
||||
} else {
|
||||
await this.waitFirstElementPresent();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Logger.log('wait datatable loading spinner is present');
|
||||
await BrowserVisibility.waitUntilElementIsVisible(element(by.tagName(materialLocators.Progress.bar.root)));
|
||||
} catch (error) {
|
||||
Logger.error('Infinite pagination spinner is not present');
|
||||
}
|
||||
if (await this.isEmpty()) {
|
||||
Logger.log('empty page');
|
||||
} else {
|
||||
await this.waitFirstElementPresent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @deprecated use Playwright instead
|
||||
async isColumnDisplayed(columnTitle: string): Promise<boolean> {
|
||||
const isColumnDisplayed = (await this.allColumns).some(async (column) => {
|
||||
const columnText = await column.getText();
|
||||
return columnText === columnTitle;
|
||||
});
|
||||
|
||||
return isColumnDisplayed;
|
||||
}
|
||||
|
||||
// @deprecated use Playwright instead
|
||||
async getNumberOfColumns(): Promise<number> {
|
||||
return this.allColumns.count();
|
||||
}
|
||||
|
||||
async getNumberOfRows(): Promise<number> {
|
||||
return this.list.count();
|
||||
}
|
||||
|
||||
getCellByRowNumberAndColumnName(rowNumber: number, columnName: string): ElementFinder {
|
||||
return this.list.get(rowNumber).$$(`div[title="${columnName}"] span`).first();
|
||||
}
|
||||
|
||||
getCellByRowContentAndColumn(rowColumn: string, rowContent: string, columnName: string): ElementFinder {
|
||||
return this.getRow(rowColumn, rowContent).$(`div[title='${columnName}']`);
|
||||
}
|
||||
|
||||
async selectRowByContent(content: string): Promise<void> {
|
||||
const row = this.getCellByContent(content);
|
||||
await BrowserActions.click(row);
|
||||
}
|
||||
|
||||
async checkRowByContentIsSelected(folderName: string): Promise<void> {
|
||||
const selectedRow = this.getCellByContent(folderName).element(by.xpath(`ancestor::adf-datatable-row[contains(@class, 'is-selected')]`));
|
||||
await BrowserVisibility.waitUntilElementIsVisible(selectedRow);
|
||||
}
|
||||
|
||||
async checkRowByContentIsNotSelected(folderName: string): Promise<void> {
|
||||
const selectedRow = this.getCellByContent(folderName).element(by.xpath(`ancestor::adf-datatable-row[contains(@class, 'is-selected')]`));
|
||||
await BrowserVisibility.waitUntilElementIsNotVisible(selectedRow);
|
||||
}
|
||||
|
||||
getCellByContent(content: string): ElementFinder {
|
||||
return this.rootElement
|
||||
.all(by.cssContainingText(`adf-datatable-row[class*='adf-datatable-row'] div[class*='adf-datatable-cell']`, content))
|
||||
.first();
|
||||
}
|
||||
|
||||
async checkCellByHighlightContent(content: string): Promise<void> {
|
||||
const cell = this.rootElement.element(
|
||||
by.cssContainingText(
|
||||
`adf-datatable-row[class*='adf-datatable-row'] div[class*='adf-name-location-cell-name'] span.adf-highlight`,
|
||||
content
|
||||
)
|
||||
);
|
||||
await BrowserVisibility.waitUntilElementIsVisible(cell);
|
||||
}
|
||||
|
||||
async clickRowByContent(name: string): Promise<void> {
|
||||
const resultElement = this.rootElement.$$(`div[data-automation-id='${name}']`).first();
|
||||
await BrowserActions.click(resultElement);
|
||||
}
|
||||
|
||||
async clickRowByContentCheckbox(name: string): Promise<void> {
|
||||
const resultElement = this.rootElement
|
||||
.$$(`div[data-automation-id='${name}']`)
|
||||
.first()
|
||||
.element(by.xpath(`ancestor::adf-datatable-row/label/${materialLocators.Checkbox.root}`));
|
||||
browser.actions().mouseMove(resultElement);
|
||||
await BrowserActions.click(resultElement);
|
||||
}
|
||||
|
||||
async checkRowContentIsDisplayed(content: string): Promise<void> {
|
||||
const resultElement = this.rootElement.$$(`div[data-automation-id='${content}']`).first();
|
||||
await BrowserVisibility.waitUntilElementIsVisible(resultElement);
|
||||
}
|
||||
|
||||
async checkRowContentIsNotDisplayed(content: string): Promise<void> {
|
||||
const resultElement = this.rootElement.$$(`div[data-automation-id='${content}']`).first();
|
||||
await BrowserVisibility.waitUntilElementIsNotVisible(resultElement);
|
||||
}
|
||||
|
||||
async checkRowContentIsDisabled(content: string): Promise<void> {
|
||||
const resultElement = this.rootElement.$$(`div[data-automation-id='${content}'] div.adf-cell-value img[aria-label='Disabled']`).first();
|
||||
await BrowserVisibility.waitUntilElementIsVisible(resultElement);
|
||||
}
|
||||
|
||||
async doubleClickRowByContent(name: string): Promise<void> {
|
||||
const resultElement = this.rootElement.$$(`div[data-automation-id='${name}']`).first();
|
||||
await BrowserActions.click(resultElement);
|
||||
await browser.actions().sendKeys(protractor.Key.ENTER).perform();
|
||||
}
|
||||
|
||||
async isEmpty(): Promise<boolean> {
|
||||
await browser.sleep(500);
|
||||
|
||||
let isDisplayed;
|
||||
|
||||
try {
|
||||
isDisplayed = await this.emptyList.isDisplayed();
|
||||
} catch (error) {
|
||||
isDisplayed = false;
|
||||
}
|
||||
|
||||
Logger.log(`empty page isDisplayed ${isDisplayed}`);
|
||||
|
||||
return isDisplayed;
|
||||
}
|
||||
|
||||
private async isSpinnerPresent(): Promise<boolean> {
|
||||
let isSpinnerPresent;
|
||||
|
||||
try {
|
||||
isSpinnerPresent = await this.rootElement.element(by.tagName(materialLocators.Progress.spinner.root)).isDisplayed();
|
||||
} catch (error) {
|
||||
isSpinnerPresent = false;
|
||||
}
|
||||
|
||||
return isSpinnerPresent;
|
||||
}
|
||||
|
||||
private async isInfiniteSpinnerPresent(): Promise<boolean> {
|
||||
let isSpinnerPresent;
|
||||
|
||||
try {
|
||||
isSpinnerPresent = await this.rootElement.element(by.tagName(materialLocators.Progress.bar.root)).isDisplayed();
|
||||
} catch (error) {
|
||||
isSpinnerPresent = false;
|
||||
}
|
||||
|
||||
return isSpinnerPresent;
|
||||
}
|
||||
|
||||
private async waitFirstElementPresent(): Promise<void> {
|
||||
try {
|
||||
Logger.log('wait first element is present');
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.contents.first());
|
||||
} catch (error) {
|
||||
Logger.log('Possible empty page');
|
||||
}
|
||||
}
|
||||
}
|
65
lib/testing/src/lib/protractor/core/pages/snackbar.page.ts
Normal file
65
lib/testing/src/lib/protractor/core/pages/snackbar.page.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* 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 { $, $$ } from 'protractor';
|
||||
import { BrowserVisibility } from '../utils/browser-visibility';
|
||||
import { BrowserActions } from '../utils/browser-actions';
|
||||
|
||||
/** @deprecated Use Playwright API instead */
|
||||
export class SnackbarPage {
|
||||
notificationSnackBar = $$(`[data-automation-id='adf-snackbar-message-content']`).first();
|
||||
snackBarAction = $(`[data-automation-id='adf-snackbar-message-content-action-button']`);
|
||||
snackBarContainerCss = $$('adf-snackbar-content');
|
||||
decorativeIconSnackBar = $(`[data-automation-id='adf-snackbar-decorative-icon']`);
|
||||
|
||||
async waitForSnackBarToAppear(timeout = 5000) {
|
||||
return BrowserVisibility.waitUntilElementIsVisible(this.snackBarContainerCss.first(), timeout, 'snackbar did not appear');
|
||||
}
|
||||
|
||||
async waitForSnackBarToClose(timeout = 15000) {
|
||||
return BrowserVisibility.waitUntilElementIsNotVisible(this.snackBarContainerCss.first(), timeout);
|
||||
}
|
||||
|
||||
async getSnackBarMessage(): Promise<string> {
|
||||
await this.waitForSnackBarToAppear();
|
||||
return this.notificationSnackBar.getText();
|
||||
}
|
||||
|
||||
async getSnackBarActionMessage(): Promise<string> {
|
||||
await this.waitForSnackBarToAppear();
|
||||
return this.snackBarAction.getText();
|
||||
}
|
||||
|
||||
async getSnackBarDecorativeIcon(): Promise<string> {
|
||||
await this.waitForSnackBarToAppear();
|
||||
return this.decorativeIconSnackBar.getText();
|
||||
}
|
||||
|
||||
async clickSnackBarAction(): Promise<void> {
|
||||
await this.waitForSnackBarToAppear();
|
||||
await BrowserActions.click(this.snackBarAction);
|
||||
}
|
||||
|
||||
async isNotificationSnackBarDisplayed(): Promise<boolean> {
|
||||
try {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.notificationSnackBar, 2000);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
214
lib/testing/src/lib/protractor/core/test-element.ts
Normal file
214
lib/testing/src/lib/protractor/core/test-element.ts
Normal file
@@ -0,0 +1,214 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* 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, element, ElementFinder, protractor, $ } from 'protractor';
|
||||
import { BrowserActions } from './utils/browser-actions';
|
||||
import { BrowserVisibility } from './utils/browser-visibility';
|
||||
|
||||
/**
|
||||
* Provides a wrapper for the most common operations with the page elements.
|
||||
*/
|
||||
export class TestElement {
|
||||
constructor(public elementFinder: ElementFinder) {}
|
||||
|
||||
/**
|
||||
* Create a new instance with the element located by the id
|
||||
* @param id The id of the element
|
||||
* @returns test element wrapper
|
||||
*/
|
||||
static byId(id: string): TestElement {
|
||||
return new TestElement(element(by.id(id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with the element located by the CSS class name
|
||||
* @param selector The CSS class name to lookup
|
||||
* @returns test element wrapper
|
||||
*/
|
||||
static byCss(selector: string): TestElement {
|
||||
return new TestElement($(selector));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with the element that contains specific text
|
||||
* @param selector the CSS selector
|
||||
* @param text the text within the target element
|
||||
* @returns test element wrapper
|
||||
*/
|
||||
static byText(selector: string, text: string): TestElement {
|
||||
return new TestElement(element(by.cssContainingText(selector, text)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance with the element with specific HTML tag name
|
||||
* @param selector the HTML tag name
|
||||
* @returns test element wrapper
|
||||
*/
|
||||
static byTag(selector: string): TestElement {
|
||||
return new TestElement(element(by.tagName(selector)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a click on this element
|
||||
* @returns a promise that will be resolved when the click command has completed
|
||||
*/
|
||||
async click() {
|
||||
return BrowserActions.click(this.elementFinder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that an element is present on the DOM of a page and visible
|
||||
* @param waitTimeout How long to wait for the condition to be true
|
||||
* @returns a promise that will be resolved when the element is present on the DOM of a page and visible
|
||||
*/
|
||||
async isVisible(waitTimeout?: number): Promise<boolean> {
|
||||
try {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.elementFinder, waitTimeout);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the element is present on the DOM of a page and visible
|
||||
* @param waitTimeout How long to wait for the condition to be true
|
||||
* @returns a promise that will be resolved when the element is present on the DOM of a page and visible
|
||||
*/
|
||||
async waitVisible(waitTimeout?: number): Promise<any> {
|
||||
return BrowserVisibility.waitUntilElementIsVisible(this.elementFinder, waitTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the element is either invisible or not present on the DOM
|
||||
* @param waitTimeout How long to wait for the condition to be true
|
||||
* @returns a promise that will be resolved when the element is either invisible or not present on the DOM
|
||||
*/
|
||||
async waitNotVisible(waitTimeout?: number): Promise<any> {
|
||||
return BrowserVisibility.waitUntilElementIsNotVisible(this.elementFinder, waitTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that an element is present on the DOM of a page
|
||||
* @param waitTimeout How long to wait for the condition to be true
|
||||
* @returns a promise that will be resolved when the element is present on the DOM of a page
|
||||
*/
|
||||
async isPresent(waitTimeout?: number): Promise<boolean> {
|
||||
try {
|
||||
await BrowserVisibility.waitUntilElementIsPresent(this.elementFinder, waitTimeout);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the element is present on the DOM of a page
|
||||
* @param waitTimeout How long to wait for the condition to be true
|
||||
* @returns a promise that will be resolved when the element is present on the DOM of a page
|
||||
*/
|
||||
async waitPresent(waitTimeout?: number): Promise<any> {
|
||||
return BrowserVisibility.waitUntilElementIsPresent(this.elementFinder, waitTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the element is not attached to the DOM of a page
|
||||
* @param waitTimeout How long to wait for the condition to be true
|
||||
* @returns a promise that will be resolved when the element is not attached to the DOM of a page
|
||||
*/
|
||||
async waitNotPresent(waitTimeout?: number): Promise<any> {
|
||||
return BrowserVisibility.waitUntilElementIsNotPresent(this.elementFinder, waitTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the given text is present in the element’s value
|
||||
* @param value the text to check
|
||||
* @returns a promise that will be resolved when the element has the given value
|
||||
*/
|
||||
async waitHasValue(value: string): Promise<any> {
|
||||
return BrowserVisibility.waitUntilElementHasValue(this.elementFinder, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query whether the DOM element represented by this instance is enabled.
|
||||
* @returns a promise that will be resolved with the enabled state of the element
|
||||
*/
|
||||
async isEnabled(): Promise<boolean> {
|
||||
return this.elementFinder.isEnabled();
|
||||
}
|
||||
/**
|
||||
* Query whether the DOM element represented by this instance is disabled.
|
||||
* @returns a promise that will be resolved with the disabled state of the element
|
||||
*/
|
||||
async isDisabled(): Promise<boolean> {
|
||||
return !(await this.elementFinder.isEnabled());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether this element is currently displayed.
|
||||
* @returns a promise that will be resolved with the displayed state of the element
|
||||
*/
|
||||
async isDisplayed(): Promise<boolean> {
|
||||
try {
|
||||
await this.elementFinder.isDisplayed();
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query for the value of the given attribute of the element.
|
||||
* @param attributeName The name of the attribute to query.
|
||||
* @returns a promise that will be resolved with the value of the attribute
|
||||
*/
|
||||
async getAttribute(attributeName: string): Promise<string> {
|
||||
return BrowserActions.getAttribute(this.elementFinder, attributeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the visible (i.e. not hidden by CSS) innerText of this element, including sub-elements, without any leading or trailing whitespace.
|
||||
* @returns a promise that will be resolved with the visible innerText of the element
|
||||
*/
|
||||
async getText(): Promise<string> {
|
||||
return BrowserActions.getText(this.elementFinder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `value` attribute for the given input element
|
||||
* @returns input value
|
||||
*/
|
||||
getInputValue(): Promise<string> {
|
||||
return BrowserActions.getInputValue(this.elementFinder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter the text
|
||||
* @param text the text to enter
|
||||
*/
|
||||
async typeText(text: string): Promise<void> {
|
||||
await BrowserActions.clearSendKeys(this.elementFinder, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the input using Ctrl+A and Backspace combination
|
||||
*/
|
||||
async clearInput() {
|
||||
await this.elementFinder.clear();
|
||||
await this.elementFinder.sendKeys(' ', protractor.Key.CONTROL, 'a', protractor.Key.NULL, protractor.Key.BACK_SPACE);
|
||||
}
|
||||
}
|
31
lib/testing/src/lib/protractor/core/utils/array.util.ts
Normal file
31
lib/testing/src/lib/protractor/core/utils/array.util.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* 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 class ArrayUtil {
|
||||
/**
|
||||
* Returns TRUE if the first array contains all elements from the second one.
|
||||
* @param superset source array
|
||||
* @param subset target array
|
||||
* @returns `true` if array is included, otherwise `false`
|
||||
*/
|
||||
static arrayContainsArray(superset: any[], subset: any[]): boolean {
|
||||
if (0 === subset.length) {
|
||||
return false;
|
||||
}
|
||||
return subset.every((value) => superset.indexOf(value) >= 0);
|
||||
}
|
||||
}
|
31
lib/testing/src/lib/protractor/core/utils/protractor.util.ts
Normal file
31
lib/testing/src/lib/protractor/core/utils/protractor.util.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* 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, browser } from 'protractor';
|
||||
|
||||
/**
|
||||
* Tagged template to convert a sting to an `ElementFinder`.
|
||||
* @example ```const item = byCss`.adf-breadcrumb-item-current`;```
|
||||
* @example ```const item = byCss`${variable}`;```
|
||||
* @param literals literals
|
||||
* @param placeholders placeholders
|
||||
* @returns Instance of `ElementFinder` type.
|
||||
*/
|
||||
export const byCss = (literals: TemplateStringsArray, ...placeholders: string[]): ElementFinder => {
|
||||
const selector = literals[0] || placeholders[0];
|
||||
return browser.$(selector);
|
||||
};
|
@@ -0,0 +1,115 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* 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, element, $, ElementFinder, $$ } from 'protractor';
|
||||
import { BrowserVisibility } from '../../core/utils/browser-visibility';
|
||||
import { BrowserActions } from '../../core/utils/browser-actions';
|
||||
import { FormFields } from '../../core/pages/form/form-fields';
|
||||
import { TestElement } from '../../core/test-element';
|
||||
import { materialLocators } from '../../public-api';
|
||||
|
||||
export class GroupCloudComponentPage {
|
||||
groupCloudSearch = $('input[data-automation-id="adf-cloud-group-search-input"]');
|
||||
groupField = $('group-cloud-widget .adf-readonly');
|
||||
formFields = new FormFields();
|
||||
|
||||
getGroupRowLocatorByName = async (name: string): Promise<ElementFinder> =>
|
||||
$$(`${materialLocators.Option.root}[data-automation-id="adf-cloud-group-chip-${name}"]`).first();
|
||||
|
||||
async searchGroups(name: string): Promise<void> {
|
||||
await BrowserActions.clearSendKeys(this.groupCloudSearch, name, 100);
|
||||
}
|
||||
|
||||
async searchGroupsToExisting(name: string) {
|
||||
await BrowserActions.clearSendKeys(this.groupCloudSearch, name, 100);
|
||||
}
|
||||
|
||||
async getGroupsFieldContent(): Promise<string> {
|
||||
return BrowserActions.getInputValue(this.groupCloudSearch);
|
||||
}
|
||||
|
||||
async selectGroupFromList(name: string): Promise<void> {
|
||||
const groupRow = await this.getGroupRowLocatorByName(name);
|
||||
|
||||
await BrowserActions.click(groupRow);
|
||||
await BrowserVisibility.waitUntilElementIsNotVisible(groupRow);
|
||||
}
|
||||
|
||||
async checkGroupIsDisplayed(name: string): Promise<void> {
|
||||
const groupRow = await this.getGroupRowLocatorByName(name);
|
||||
await BrowserVisibility.waitUntilElementIsVisible(groupRow);
|
||||
}
|
||||
|
||||
async checkGroupIsNotDisplayed(name: string): Promise<void> {
|
||||
const groupRow = await this.getGroupRowLocatorByName(name);
|
||||
await BrowserVisibility.waitUntilElementIsNotVisible(groupRow);
|
||||
}
|
||||
|
||||
async checkSelectedGroup(group: string): Promise<boolean> {
|
||||
try {
|
||||
await TestElement.byText(`${materialLocators.Chip.grid.row.root}[data-automation-id*="adf-cloud-group-chip-"]`, group).waitVisible();
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async checkGroupNotSelected(group: string): Promise<void> {
|
||||
await BrowserVisibility.waitUntilElementIsNotVisible(
|
||||
element(by.cssContainingText(`${materialLocators.Chip.grid.row.root}[data-automation-id*="adf-cloud-group-chip-"]`, group))
|
||||
);
|
||||
}
|
||||
|
||||
async removeSelectedGroup(group: string): Promise<void> {
|
||||
const locator = $(
|
||||
`${materialLocators.Chip.grid.row.root}[data-automation-id*="adf-cloud-group-chip-${group}"] ${materialLocators.Icon.root}`
|
||||
);
|
||||
await BrowserActions.click(locator);
|
||||
}
|
||||
|
||||
async isGroupWidgetVisible(fieldId: string): Promise<boolean> {
|
||||
try {
|
||||
await this.formFields.checkWidgetIsVisible(fieldId);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async checkGroupWidgetIsReadOnly(): Promise<boolean> {
|
||||
try {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.groupField);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async checkGroupActiveField(name: string): Promise<boolean> {
|
||||
try {
|
||||
await BrowserActions.clearSendKeys(this.groupField, name);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async checkNoResultsFoundError(): Promise<void> {
|
||||
const errorLocator = $('[data-automation-id="adf-cloud-group-no-results"]');
|
||||
await BrowserVisibility.waitUntilElementIsVisible(errorLocator);
|
||||
}
|
||||
}
|
131
lib/testing/src/lib/shared/api/api.service.ts
Normal file
131
lib/testing/src/lib/shared/api/api.service.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* 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 { AlfrescoApi, AlfrescoApiConfig } from '@alfresco/js-api';
|
||||
import { LoggerLike } from '../utils/logger';
|
||||
|
||||
export interface ApiServiceConfig {
|
||||
appConfig: AlfrescoApiConfig;
|
||||
users: {
|
||||
[key: string]: {
|
||||
username: string;
|
||||
password: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
export class ApiService {
|
||||
apiService: AlfrescoApi;
|
||||
|
||||
constructor(private config: ApiServiceConfig, private logger: LoggerLike) {
|
||||
this.logger.log('Api Service configuration' + JSON.stringify(this.config));
|
||||
this.apiService = new AlfrescoApi(this.config.appConfig);
|
||||
}
|
||||
|
||||
getInstance(): AlfrescoApi {
|
||||
return this.apiService;
|
||||
}
|
||||
|
||||
async login(username: string, password: string): Promise<void> {
|
||||
await this.apiService.login(username, password);
|
||||
}
|
||||
|
||||
/**
|
||||
* Login using one of the account profiles from the `browser.params.testConfig`.
|
||||
* Example: loginWithProfile('admin')
|
||||
* @param profileName profile name
|
||||
*/
|
||||
async loginWithProfile(profileName: string): Promise<void> {
|
||||
const profile = this.config.users[profileName];
|
||||
if (profile) {
|
||||
this.logger.log(
|
||||
`try to login with ${profile.username} on HOST: ${this.apiService.config.hostEcm} AUTHTYPE: ${this.apiService.config.authType} PROVIDER: ${this.apiService.config.provider}`
|
||||
);
|
||||
try {
|
||||
await this.apiService.login(profile.username, profile.password);
|
||||
this.logger.log(`Successfuly logged in as ${profile.username}`);
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to login with ${profile.username}`, error?.message);
|
||||
throw new Error(`Login failed with ${profile.username}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Login profile "${profileName}" not found on "browser.params.testConfig".`);
|
||||
}
|
||||
}
|
||||
|
||||
// @deprecated
|
||||
async performBpmOperation(path: string, method: string, queryParams: any, postBody: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const uri = this.config.appConfig.hostBpm + path;
|
||||
const pathParams = {};
|
||||
const formParams = {};
|
||||
const contentTypes = ['application/json'];
|
||||
const accepts = ['application/json'];
|
||||
|
||||
const headerParams = {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
Authorization: 'bearer ' + this.apiService.oauth2Auth.token
|
||||
};
|
||||
|
||||
this.apiService.processClient
|
||||
.callCustomApi(uri, method, pathParams, queryParams, headerParams, formParams, postBody, contentTypes, accepts, Object)
|
||||
.then((data) => resolve(data))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
}
|
||||
|
||||
// @deprecated
|
||||
async performIdentityOperation(path: string, method: string, queryParams: any, postBody: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const uri = this.config.appConfig.oauth2.host.replace('/realms', '/admin/realms') + path;
|
||||
const pathParams = {};
|
||||
const formParams = {};
|
||||
const contentTypes = ['application/json'];
|
||||
const accepts = ['application/json'];
|
||||
|
||||
const headerParams = {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
Authorization: 'bearer ' + this.apiService.oauth2Auth.token
|
||||
};
|
||||
|
||||
return this.apiService.processClient
|
||||
.callCustomApi(uri, method, pathParams, queryParams, headerParams, formParams, postBody, contentTypes, accepts, Object)
|
||||
.then((data) => resolve(data))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
}
|
||||
|
||||
// @deprecated
|
||||
async performECMOperation(path: string, method: string, queryParams: any, postBody: any): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const uri = this.config.appConfig.hostEcm + path;
|
||||
const pathParams = {};
|
||||
const formParams = {};
|
||||
const contentTypes = ['application/json'];
|
||||
const accepts = ['application/json'];
|
||||
|
||||
const headerParams = {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
Authorization: 'bearer ' + this.apiService.oauth2Auth.token
|
||||
};
|
||||
|
||||
this.apiService.contentClient
|
||||
.callCustomApi(uri, method, pathParams, queryParams, headerParams, formParams, postBody, contentTypes, accepts, Object)
|
||||
.then((data) => resolve(data))
|
||||
.catch((err) => reject(err));
|
||||
});
|
||||
}
|
||||
}
|
125
lib/testing/src/lib/shared/utils/string.util.ts
Normal file
125
lib/testing/src/lib/shared/utils/string.util.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* 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 class StringUtil {
|
||||
static generatePasswordString(length: number = 8): string {
|
||||
let text = '';
|
||||
const lowerCaseLimit = Math.floor(length / 2);
|
||||
text += StringUtil.generateRandomCharset(lowerCaseLimit, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ');
|
||||
text += StringUtil.generateRandomCharset(length - lowerCaseLimit, 'abcdefghijklmnopqrstuvwxyz');
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random string.
|
||||
* @param length If this parameter is not provided the length is set to 8 by default.
|
||||
* @returns random string
|
||||
*/
|
||||
static generateRandomString(length: number = 8): string {
|
||||
return StringUtil.generateRandomCharset(length, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random lowercase string.
|
||||
* @param length If this parameter is not provided the length is set to 8 by default.
|
||||
* @returns random string
|
||||
*/
|
||||
static generateRandomLowercaseString(length: number = 8): string {
|
||||
return StringUtil.generateRandomCharset(length, 'abcdefghijklmnopqrstuvwxyz0123456789');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random email address following the format: abcdef@activiti.test.com
|
||||
* @param domain email domain
|
||||
* @param length email length
|
||||
* @returns email address
|
||||
*/
|
||||
static generateRandomEmail(domain: string, length: number = 5): string {
|
||||
let email = StringUtil.generateRandomCharset(length, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789');
|
||||
email += domain;
|
||||
return email.toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random string - digits only.
|
||||
* @param length If this parameter is not provided the length is set to 8 by default.
|
||||
* @returns random string
|
||||
*/
|
||||
static generateRandomStringDigits(length: number = 8): string {
|
||||
return StringUtil.generateRandomCharset(length, '0123456789');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random string - non-latin characters only.
|
||||
* @param length If this parameter is not provided the length is set to 3 by default.
|
||||
* @returns random string
|
||||
*/
|
||||
static generateRandomStringNonLatin(length: number = 3): string {
|
||||
return StringUtil.generateRandomCharset(length, '密码你好𠮷');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random string.
|
||||
* @param length If this parameter is not provided the length is set to 8 by default.
|
||||
* @param charSet to use
|
||||
* @returns random string
|
||||
*/
|
||||
static generateRandomCharset(length: number = 8, charSet: string): string {
|
||||
let text = '';
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
text += charSet.charAt(Math.floor(Math.random() * charSet.length));
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a sequence of files with name: baseName + index + extension (e.g.) baseName1.txt, baseName2.txt, ...
|
||||
* @param startIndex start index
|
||||
* @param endIndex end index
|
||||
* @param baseName the base name of all files
|
||||
* @param extension the extension of the file
|
||||
* @returns list of file names
|
||||
*/
|
||||
static generateFilesNames(startIndex: number, endIndex: number, baseName: string, extension: string): string[] {
|
||||
const fileNames: string[] = [];
|
||||
for (let i = startIndex; i <= endIndex; i++) {
|
||||
fileNames.push(baseName + i + extension);
|
||||
}
|
||||
return fileNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random name for a process
|
||||
* @param length {int} If this parameter is not provided the length is set to 5 by default.
|
||||
* @returns process name
|
||||
*/
|
||||
static generateProcessName(length: number = 5): string {
|
||||
return 'process_' + StringUtil.generateRandomString(length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random name for a user task
|
||||
* @param length If this parameter is not provided the length is set to 5 by default.
|
||||
* @returns task name
|
||||
*/
|
||||
static generateUserTaskName(length: number = 5): string {
|
||||
return 'userTask_' + StringUtil.generateRandomString(length);
|
||||
}
|
||||
}
|
14
lib/testing/tsconfig.lib.json
Normal file
14
lib/testing/tsconfig.lib.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"paths": {
|
||||
"@alfresco/js-api": ["../../../dist/libs/js-api"],
|
||||
"@alfresco/js-api/*": ["../../../dist/libs/js-api/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "index.ts"]
|
||||
}
|
Reference in New Issue
Block a user