mirror of
https://github.com/Alfresco/alfresco-content-app.git
synced 2026-09-09 18:02:54 +00:00
[ACS-11446] Automated last test cases from ACA test execution & various refactoring (#5134)
* [ACS-11446] Automated last test cases from ACA test execution & various refactoring * [ACS-11446] sonar cloud fix 1 * [ACS-11446] spinner changes reverted & spinner component added to the base component * [ACS-11446] sonar fix 2 * [ACS-11446] copilot review fixes 1 * [ACS-11446] copilot review fixes 2 * [ACS-11446] copilot review fixes 3 * [ACS=11446] review fixes 1 * [ACS-11446] excluded upload dialog tests
This commit is contained in:
@@ -46,13 +46,12 @@ export class NodesApi {
|
||||
title: string = '',
|
||||
description: string = '',
|
||||
author: string = '',
|
||||
aspectNames: string[] = null
|
||||
): Promise<NodeEntry | null> {
|
||||
aspectNames: string[] = []
|
||||
): Promise<NodeEntry> {
|
||||
try {
|
||||
return await this.createNode('cm:folder', name, parentId, title, description, null, author, null, aspectNames);
|
||||
return await this.createNode('cm:folder', name, parentId, title, description, null, author, true, aspectNames);
|
||||
} catch (error) {
|
||||
console.error(`${this.constructor.name} ${this.createFolder.name}`, error);
|
||||
return null;
|
||||
throw new Error(`${this.constructor.name} ${this.createFolder.name}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,13 +62,12 @@ export class NodesApi {
|
||||
description: string = '',
|
||||
author: string = '',
|
||||
majorVersion: boolean = true,
|
||||
aspectNames: string[] = null
|
||||
aspectNames: string[] = []
|
||||
): Promise<NodeEntry> {
|
||||
try {
|
||||
return await this.createNode('cm:content', name, parentId, title, description, null, author, majorVersion, aspectNames);
|
||||
} catch (error) {
|
||||
console.error(`${this.constructor.name} ${this.createFile.name}`, error);
|
||||
return null;
|
||||
throw new Error(`${this.constructor.name} ${this.createFile.name}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,8 +75,7 @@ export class NodesApi {
|
||||
try {
|
||||
return await this.createContent({ files: names }, relativePath);
|
||||
} catch (error) {
|
||||
console.error(`${this.constructor.name} ${this.createFiles.name}: ${error}`);
|
||||
return null;
|
||||
throw new Error(`${this.constructor.name} ${this.createFiles.name}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,8 +94,7 @@ export class NodesApi {
|
||||
try {
|
||||
return await this.createContent({ folders: names }, relativePath);
|
||||
} catch (error) {
|
||||
console.error(`${this.constructor.name} ${this.createFolders.name}: ${error}`);
|
||||
return null;
|
||||
throw new Error(`${this.constructor.name} ${this.createFolders.name}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,11 +115,8 @@ export class NodesApi {
|
||||
imageProps: any = null,
|
||||
author: string = '',
|
||||
majorVersion: boolean = true,
|
||||
aspectNames: string[] = null
|
||||
): Promise<NodeEntry | null> {
|
||||
if (!aspectNames) {
|
||||
aspectNames = ['cm:versionable']; // workaround for REPO-4772
|
||||
}
|
||||
aspectNames: string[] = []
|
||||
): Promise<NodeEntry> {
|
||||
const nodeBody = {
|
||||
name,
|
||||
nodeType,
|
||||
@@ -144,8 +137,7 @@ export class NodesApi {
|
||||
majorVersion
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`${this.constructor.name} ${this.createNode.name}`, error);
|
||||
return null;
|
||||
throw new Error(`${this.constructor.name} ${this.createNode.name}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +178,7 @@ export class NodesApi {
|
||||
*/
|
||||
async deleteCurrentUserNodes(): Promise<void> {
|
||||
try {
|
||||
const userNodes = (await this.getNodeChildren('-my-')).list.entries;
|
||||
const userNodes = (await this.getNodeChildren('-my-'))?.list?.entries ?? [];
|
||||
const userNodesIds = userNodes.map((nodeChild) => nodeChild.entry.id);
|
||||
await this.deleteNodes(userNodesIds);
|
||||
} catch (error) {
|
||||
@@ -218,24 +210,22 @@ export class NodesApi {
|
||||
try {
|
||||
return this.apiService.nodes.createNode('-my-', flattenNodeContentTree(content, relativePath) as any);
|
||||
} catch (error) {
|
||||
console.error(`${this.constructor.name} ${this.createContent.name}`, error);
|
||||
return null;
|
||||
throw new Error(`${this.constructor.name} ${this.createContent.name}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
async getNodeById(id: string): Promise<NodeEntry | null> {
|
||||
async getNodeById(id: string): Promise<NodeEntry> {
|
||||
try {
|
||||
return this.apiService.nodes.getNode(id);
|
||||
} catch (error) {
|
||||
console.error(`${this.constructor.name} ${this.getNodeById.name}`, error);
|
||||
return null;
|
||||
throw new Error(`${this.constructor.name} ${this.getNodeById.name}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
async getNodeIdFromParent(name: string, parentId: string): Promise<string> {
|
||||
try {
|
||||
const children = (await this.getNodeChildren(parentId)).list.entries;
|
||||
return children.find((elem) => elem.entry.name === name).entry.id || '';
|
||||
const children = (await this.getNodeChildren(parentId))?.list?.entries ?? [];
|
||||
return children.find((elem) => elem.entry.name === name)?.entry.id ?? '';
|
||||
} catch (error) {
|
||||
console.error(`${this.constructor.name} ${this.getNodeIdFromParent.name}`, error);
|
||||
return '';
|
||||
@@ -332,7 +322,7 @@ export class NodesApi {
|
||||
}
|
||||
}
|
||||
|
||||
async removeUserAccessOnNodeTemplate(nodeName: string): Promise<NodeEntry> {
|
||||
async removeUserAccessOnNodeTemplate(nodeName: string): Promise<NodeEntry | null> {
|
||||
try {
|
||||
const templatesRootFolderId = await this.getNodeTemplatesFolderId();
|
||||
const nodeId: string = await this.getNodeIdFromParent(nodeName, templatesRootFolderId);
|
||||
@@ -344,7 +334,7 @@ export class NodesApi {
|
||||
}
|
||||
}
|
||||
|
||||
async removeUserAccessOnSpaceTemplate(nodeName: string): Promise<NodeEntry> {
|
||||
async removeUserAccessOnSpaceTemplate(nodeName: string): Promise<NodeEntry | null> {
|
||||
try {
|
||||
const templatesRootFolderId = await this.getSpaceTemplatesFolderId();
|
||||
const nodeId: string = await this.getNodeIdFromParent(nodeName, templatesRootFolderId);
|
||||
@@ -371,7 +361,7 @@ export class NodesApi {
|
||||
}
|
||||
}
|
||||
|
||||
private async addAspects(nodeId: string, aspectNames: string[]): Promise<NodeEntry> {
|
||||
private async addAspects(nodeId: string, aspectNames: string[]): Promise<NodeEntry | null> {
|
||||
try {
|
||||
return this.apiService.nodes.updateNode(nodeId, { aspectNames });
|
||||
} catch (error) {
|
||||
@@ -423,7 +413,7 @@ export class NodesApi {
|
||||
}
|
||||
}
|
||||
|
||||
async createLinkToFileName(originalFileName: string, originalFileParentId: string, destinationParentId?: string): Promise<NodeEntry> {
|
||||
async createLinkToFileName(originalFileName: string, originalFileParentId: string, destinationParentId?: string): Promise<NodeEntry | null> {
|
||||
destinationParentId ??= originalFileParentId;
|
||||
|
||||
try {
|
||||
@@ -431,30 +421,27 @@ export class NodesApi {
|
||||
|
||||
return this.createFileLink(nodeId, destinationParentId);
|
||||
} catch (error) {
|
||||
console.error('Admin Actions - createLinkToFileName failed : ', error);
|
||||
return null;
|
||||
throw new Error(`Admin Actions - createLinkToFileName failed : ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
async createLinkToFolderName(originalFolderName: string, originalFolderParentId: string, destinationParentId?: string): Promise<NodeEntry> {
|
||||
async createLinkToFolderName(originalFolderName: string, originalFolderParentId: string, destinationParentId?: string): Promise<NodeEntry | null> {
|
||||
destinationParentId ??= originalFolderParentId;
|
||||
|
||||
try {
|
||||
const nodeId = await this.getNodeIdFromParent(originalFolderName, originalFolderParentId);
|
||||
return this.createFolderLink(nodeId, destinationParentId);
|
||||
} catch (error) {
|
||||
console.error('Admin Actions - createLinkToFolderName failed : ', error);
|
||||
return null;
|
||||
throw new Error(`Admin Actions - createLinkToFolderName failed : ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
async getNodeProperty(nodeId: string, property: string): Promise<string> {
|
||||
try {
|
||||
const node = await this.getNodeById(nodeId);
|
||||
return node.entry.properties?.[property] || '';
|
||||
return node.entry.properties?.[property] ?? '';
|
||||
} catch (error) {
|
||||
console.error(`${this.constructor.name} ${this.getNodeProperty.name}`, error);
|
||||
return '';
|
||||
throw new Error(`${this.constructor.name} ${this.getNodeProperty.name}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,8 +450,7 @@ export class NodesApi {
|
||||
const sharedId = await this.getNodeProperty(nodeId, 'qshare:sharedId');
|
||||
return sharedId !== '';
|
||||
} catch (error) {
|
||||
console.error(`${this.constructor.name} ${this.isFileShared.name}`, error);
|
||||
return null;
|
||||
throw new Error(`${this.constructor.name} ${this.isFileShared.name}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -473,8 +459,7 @@ export class NodesApi {
|
||||
const lockType = await this.getNodeProperty(nodeId, 'cm:lockType');
|
||||
return lockType || '';
|
||||
} catch (error) {
|
||||
console.error(`${this.constructor.name} ${this.getLockType.name}`, error);
|
||||
return '';
|
||||
throw new Error(`${this.constructor.name} ${this.getLockType.name}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -482,8 +467,7 @@ export class NodesApi {
|
||||
try {
|
||||
return (await this.getLockType(nodeId)) === 'WRITE_LOCK';
|
||||
} catch (error) {
|
||||
console.error(`${this.constructor.name} ${this.isFileLockedWrite.name}`, error);
|
||||
return null;
|
||||
throw new Error(`${this.constructor.name} ${this.isFileLockedWrite.name}: ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export class SitesApi {
|
||||
return classObj;
|
||||
}
|
||||
|
||||
async createSite(title: string, visibility?: string, description?: string, siteId?: string): Promise<SiteEntry | null> {
|
||||
async createSite(title: string, visibility?: string, description?: string, siteId?: string): Promise<SiteEntry> {
|
||||
const site = {
|
||||
title,
|
||||
visibility: visibility || Site.VisibilityEnum.PUBLIC,
|
||||
@@ -57,17 +57,19 @@ export class SitesApi {
|
||||
try {
|
||||
return this.apiService.sites.createSite(site);
|
||||
} catch (error) {
|
||||
console.error(`SitesApi createSite : catch : `, error);
|
||||
return null;
|
||||
throw new Error(`SitesApi ${this.createSite.name}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
async getDocLibId(siteId: string): Promise<string> {
|
||||
try {
|
||||
return (await this.apiService.sites.listSiteContainers(siteId)).list.entries[0].entry.id;
|
||||
const id = (await this.apiService.sites.listSiteContainers(siteId)).list?.entries?.[0]?.entry?.id;
|
||||
if (!id) {
|
||||
throw new Error(`Document library not found for site ${siteId}`);
|
||||
}
|
||||
return id;
|
||||
} catch (error) {
|
||||
console.error(`SitesApi getDocLibId : catch : `, error);
|
||||
return null;
|
||||
throw new Error(`Failed to get document library ID for site ${siteId}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,12 +112,10 @@ export class SitesApi {
|
||||
try {
|
||||
return this.apiService.sites.createSiteMembership(siteId, memberBody);
|
||||
} catch (error) {
|
||||
if (error.status === 409) {
|
||||
if (String(error).includes('409')) {
|
||||
return this.updateSiteMember(siteId, userId, role);
|
||||
} else {
|
||||
console.error(`SitesApi addSiteMember : catch : `, error);
|
||||
return new SiteMemberEntry();
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,8 +127,7 @@ export class SitesApi {
|
||||
try {
|
||||
return this.apiService.sites.createSiteMembershipRequestForPerson(personId, body);
|
||||
} catch (error) {
|
||||
console.error(`SitesApi createSiteMembershipRequestForPerson : catch : `, error);
|
||||
return null;
|
||||
throw new Error(`Failed to create site membership request for person ${personId} and site ${siteId}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,18 +135,17 @@ export class SitesApi {
|
||||
try {
|
||||
return this.apiService.sites.approveSiteMembershipRequest(siteId, inviteeId);
|
||||
} catch (error) {
|
||||
console.error(`SitesApi approveSiteMembershipRequest : catch : `, error);
|
||||
return null;
|
||||
throw new Error(`Failed to approve site membership request for invitee ${inviteeId} and site ${siteId}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
async hasMembershipRequest(personId: string, siteId: string): Promise<boolean> {
|
||||
try {
|
||||
const requests = (await this.apiService.sites.listSiteMembershipRequestsForPerson(personId)).list.entries.map((e) => e.entry.id);
|
||||
const entries = (await this.apiService.sites.listSiteMembershipRequestsForPerson(personId)).list?.entries ?? [];
|
||||
const requests = entries.map((e) => e.entry?.id).filter((id): id is string => !!id);
|
||||
return requests.includes(siteId);
|
||||
} catch (error) {
|
||||
console.error(`SitesApi hasMembershipRequest : catch : `, error);
|
||||
return null;
|
||||
throw new Error(`Failed to check site membership request for person ${personId} and site ${siteId}: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,8 +161,7 @@ export class SitesApi {
|
||||
try {
|
||||
return this.apiService.sites.getSite(siteId);
|
||||
} catch (error) {
|
||||
console.error(`SitesApi getSite : catch : `, error);
|
||||
return null;
|
||||
throw new Error(`Failed to get site ${siteId}: ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,9 @@
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { TagBody, TagEntry, TagPaging } from '@alfresco/js-api';
|
||||
import { TagBody, TagEntry, TagPaging, Tag } from '@alfresco/js-api';
|
||||
import { ApiClientFactory } from './api-client-factory';
|
||||
import { logger } from '../utils';
|
||||
|
||||
export class TagsApi {
|
||||
private readonly apiService: ApiClientFactory;
|
||||
@@ -38,12 +39,29 @@ export class TagsApi {
|
||||
return classObj;
|
||||
}
|
||||
|
||||
async createTags(tags: TagBody[]): Promise<TagEntry | TagPaging> {
|
||||
async createTags(...tagNames: string[]): Promise<TagEntry[]> {
|
||||
try {
|
||||
return this.apiService.tagsApi.createTags(tags);
|
||||
const results: TagEntry[] = [];
|
||||
for (const tag of tagNames) {
|
||||
const result = await this.apiService.tagsApi.createTags([{ tag }]);
|
||||
let created: TagEntry;
|
||||
if ('entry' in result) {
|
||||
created = result as TagEntry;
|
||||
} else if ('list' in result) {
|
||||
const firstEntry = result.list?.entries?.[0];
|
||||
if (!firstEntry) {
|
||||
throw new Error(`createTags returned a paging result with no entries for tag "${tag}"`);
|
||||
}
|
||||
created = firstEntry;
|
||||
} else {
|
||||
throw new Error(`createTags returned an unexpected response format for tag "${tag}"`);
|
||||
}
|
||||
logger.info(`Tag created: "${created.entry.tag}" (id: ${created.entry.id})`);
|
||||
results.push(created);
|
||||
}
|
||||
return results;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return null;
|
||||
throw new Error(`Failed to create tags: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,18 +69,19 @@ export class TagsApi {
|
||||
try {
|
||||
return this.apiService.tagsApi.assignTagToNode(nodeId, tag);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return null;
|
||||
throw new Error(`Failed to assign tag to node: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteTags(tagIds: string[]): Promise<void> {
|
||||
async deleteTags(...tags: Tag[]): Promise<void> {
|
||||
try {
|
||||
for (const tagId of tagIds) {
|
||||
await this.apiService.tagsApi.deleteTag(tagId);
|
||||
for (const { id, tag } of tags) {
|
||||
await this.apiService.tagsApi.deleteTag(id);
|
||||
const tagLabel = tag ? `"${tag}" ` : '';
|
||||
logger.info(`Tag deleted: ${tagLabel}(id: ${id})`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw new Error(`Failed to delete tags: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +89,7 @@ export class TagsApi {
|
||||
try {
|
||||
return this.apiService.tagsApi.listTagsForNode(nodeId);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return null;
|
||||
throw new Error(`Failed to list tags for node: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,18 +97,17 @@ export class TagsApi {
|
||||
try {
|
||||
return this.apiService.tagsApi.listTags(params);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return null;
|
||||
throw new Error(`Failed to list tags: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
async deleteTagsByTagName(tagName: string): Promise<void> {
|
||||
async deleteTagByTagName(tagName: string): Promise<void> {
|
||||
try {
|
||||
const response = await this.listTags({ tag: tagName, matching: true });
|
||||
const tagIds = response.list.entries.map((entry) => entry.entry.id);
|
||||
await this.deleteTags(tagIds);
|
||||
const tags = response.list?.entries.map((entry) => entry.entry) || [];
|
||||
await this.deleteTags(...tags);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw new Error(`Failed to delete tags by tag name: ${error}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -102,10 +102,9 @@ export class AdfInfoDrawerComponent extends BaseComponent {
|
||||
|
||||
async checkCommentsHeaderCount(): Promise<number> {
|
||||
const commentsCountTextContent = await this.commentsHeader.textContent();
|
||||
const commentsCountString = commentsCountTextContent.match(/\d+/g)[0];
|
||||
return parseInt(commentsCountString, 10);
|
||||
const commentsCountString = commentsCountTextContent?.match(/\d+/g)?.[0];
|
||||
return parseInt(commentsCountString ?? '0', 10);
|
||||
}
|
||||
|
||||
async getCommentsCountFromList(): Promise<number> {
|
||||
return this.commentsList.count();
|
||||
}
|
||||
@@ -121,9 +120,8 @@ export class AdfInfoDrawerComponent extends BaseComponent {
|
||||
}
|
||||
|
||||
async getHeaderTitle(): Promise<string> {
|
||||
return this.headerTitle.textContent();
|
||||
return (await this.headerTitle.textContent()) ?? '';
|
||||
}
|
||||
|
||||
async getTabsCount(): Promise<number> {
|
||||
return this.infoDrawerTabs.count();
|
||||
}
|
||||
|
||||
@@ -24,11 +24,9 @@
|
||||
|
||||
import { Locator, Page } from '@playwright/test';
|
||||
import { PlaywrightBase } from '../playwright-base';
|
||||
import { timeouts } from '../../utils';
|
||||
|
||||
export abstract class BaseComponent extends PlaywrightBase {
|
||||
private readonly rootElement: string;
|
||||
private readonly progressBar = this.page.locator('[role="progressbar"]');
|
||||
|
||||
protected constructor(page: Page, rootElement: string) {
|
||||
super(page);
|
||||
@@ -46,22 +44,4 @@ export abstract class BaseComponent extends PlaywrightBase {
|
||||
getChild(cssLocator: string, options?: { hasText?: string | RegExp; has?: Locator }): Locator {
|
||||
return this.page.locator(`${this.rootElement} ${cssLocator}`, options);
|
||||
}
|
||||
|
||||
async spinnerWaitForReload(): Promise<void> {
|
||||
try {
|
||||
await this.page.locator('[role="progressbar"]').waitFor({ state: 'attached', timeout: timeouts.medium });
|
||||
await this.page.locator('[role="progressbar"]').waitFor({ state: 'detached', timeout: timeouts.normal });
|
||||
} catch (e) {
|
||||
this.logger.info('Spinner was not present');
|
||||
}
|
||||
}
|
||||
|
||||
async progressBarWaitForReload(): Promise<void> {
|
||||
try {
|
||||
await this.progressBar.waitFor({ state: 'visible', timeout: timeouts.medium });
|
||||
await this.progressBar.waitFor({ state: 'hidden', timeout: timeouts.normal });
|
||||
} catch (e) {
|
||||
this.logger.info('Progress bar was not present');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-13
@@ -153,16 +153,16 @@ export class DataTableComponent extends BaseComponent {
|
||||
async goThroughPagesLookingForRowWithName(name: string | number): Promise<void> {
|
||||
await this.spinnerWaitForReload();
|
||||
if (await this.getRowByName(name).isVisible()) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (await this.pagination.currentPageLocator.isVisible()) {
|
||||
if ((await this.pagination.currentPageLocator.textContent()) === ' of 1 ') {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (await this.pagination.totalPageLocator.isVisible()) {
|
||||
const maxPages = (await this.pagination.totalPageLocator?.textContent())?.match(/\d/)[0];
|
||||
const maxPages = (await this.pagination.totalPageLocator?.textContent())?.match(/\d+/)?.[0];
|
||||
for (let page = 1; page <= Number(maxPages); page++) {
|
||||
if (await this.getRowByName(name).isVisible()) {
|
||||
break;
|
||||
@@ -216,7 +216,7 @@ export class DataTableComponent extends BaseComponent {
|
||||
async getItemLocationTooltip(name: string): Promise<string> {
|
||||
const location = this.getItemLocationEl(name);
|
||||
await location.hover();
|
||||
return location.locator('a').getAttribute('title', { timeout: timeouts.normal });
|
||||
return (await location.locator('a').getAttribute('title', { timeout: timeouts.normal })) ?? '';
|
||||
}
|
||||
|
||||
async clickItemLocation(name: string): Promise<void> {
|
||||
@@ -224,7 +224,7 @@ export class DataTableComponent extends BaseComponent {
|
||||
}
|
||||
|
||||
async getSortingOrder(): Promise<string> {
|
||||
const str = await this.sortedColumnHeader.locator('../..').getAttribute('class');
|
||||
const str = (await this.sortedColumnHeader.locator('../..').getAttribute('class')) ?? '';
|
||||
if (str.includes('asc')) {
|
||||
return 'asc';
|
||||
} else if (str.includes('desc')) {
|
||||
@@ -275,10 +275,8 @@ export class DataTableComponent extends BaseComponent {
|
||||
const rowsCount = await this.sitesName.count();
|
||||
const sitesInfo: { [siteName: string]: string } = {};
|
||||
for (let i = 0; i < rowsCount; i++) {
|
||||
let siteVisibilityText = await this.sitesVisibility.nth(i).textContent();
|
||||
let siteNameText = await this.sitesName.nth(i).textContent();
|
||||
siteVisibilityText = siteVisibilityText.trim().toUpperCase();
|
||||
siteNameText = siteNameText.trim();
|
||||
const siteVisibilityText = ((await this.sitesVisibility.nth(i).textContent()) ?? '').trim().toUpperCase();
|
||||
const siteNameText = ((await this.sitesName.nth(i).textContent()) ?? '').trim();
|
||||
sitesInfo[siteNameText] = siteVisibilityText;
|
||||
}
|
||||
return sitesInfo;
|
||||
@@ -293,10 +291,8 @@ export class DataTableComponent extends BaseComponent {
|
||||
const rowsCount = await this.sitesName.count();
|
||||
const sitesInfo: { [siteName: string]: string } = {};
|
||||
for (let i = 0; i < rowsCount; i++) {
|
||||
let siteNameText = await this.sitesName.nth(i).textContent();
|
||||
let siteRoleText = await this.sitesRole.nth(i).textContent();
|
||||
siteNameText = siteNameText.trim();
|
||||
siteRoleText = siteRoleText.trim();
|
||||
const siteNameText = ((await this.sitesName.nth(i).textContent()) ?? '').trim();
|
||||
const siteRoleText = ((await this.sitesRole.nth(i).textContent()) ?? '').trim();
|
||||
sitesInfo[siteNameText] = siteRoleText;
|
||||
}
|
||||
return sitesInfo;
|
||||
|
||||
+1
@@ -60,6 +60,7 @@ export class ContentNodeSelectorDialog extends BaseComponent {
|
||||
|
||||
async selectDestination(folderName: string): Promise<void> {
|
||||
const row = this.getRowByName(folderName);
|
||||
await row.scrollIntoViewIfNeeded();
|
||||
|
||||
await expect(row).toBeVisible();
|
||||
await row.click({ trial: true });
|
||||
|
||||
+1
@@ -40,6 +40,7 @@ export class LinkRulesDialog extends BaseComponent {
|
||||
|
||||
async selectDestination(folderName: string): Promise<void> {
|
||||
const row = this.getRowByName(folderName);
|
||||
await row.scrollIntoViewIfNeeded();
|
||||
|
||||
await expect(row).toBeVisible();
|
||||
await row.click({ trial: true });
|
||||
|
||||
+16
-13
@@ -22,22 +22,25 @@
|
||||
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { Page } from '@playwright/test';
|
||||
import { BaseComponent } from './base.component';
|
||||
import { Page } from '@playwright/test';
|
||||
|
||||
export class SpinnerComponent extends BaseComponent {
|
||||
private static readonly rootElement = '[role="progressbar"]';
|
||||
export class EditModeComponent extends BaseComponent {
|
||||
private static readonly rootElement = '.aca-details-container';
|
||||
|
||||
constructor(page: Page, rootElement = SpinnerComponent.rootElement) {
|
||||
super(page, rootElement);
|
||||
}
|
||||
public tagsAccordion = this.page.locator('[data-automation-id="adf-content-metadata-tags-panel"]');
|
||||
public tagsAccordionPenButton = this.tagsAccordion.locator('[data-automation-id="showing-tag-input-button"]');
|
||||
public tagsInput = this.tagsAccordion.locator('input');
|
||||
public tagsChips = this.tagsAccordion.locator('[role="listitem"]');
|
||||
public tagsAccordionConfirmButton = this.getChild('[data-automation-id="save-tags-metadata"]');
|
||||
public createTagButton = this.tagsAccordion.locator('.adf-create-tag-label');
|
||||
public existingTags = this.tagsAccordion.locator('.adf-tag');
|
||||
|
||||
async waitForReload(): Promise<void> {
|
||||
try {
|
||||
await this.getChild('').waitFor({ state: 'attached', timeout: 2000 });
|
||||
await this.getChild('').waitFor({ state: 'detached', timeout: 2000 });
|
||||
} catch (e) {
|
||||
this.logger.info('Spinner was not present');
|
||||
}
|
||||
public categoriesAccordion = this.page.locator('[data-automation-id="adf-content-metadata-categories-panel"]');
|
||||
public categoriesAccordionPenButton = this.categoriesAccordion.locator('[data-automation-id="meta-data-categories-edit"]');
|
||||
public categoriesInput = this.categoriesAccordion.locator('input');
|
||||
|
||||
constructor(page: Page) {
|
||||
super(page, EditModeComponent.rootElement);
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@ export * from './dataTable';
|
||||
export * from './dialogs';
|
||||
export * from './manageRules';
|
||||
export * from './base.component';
|
||||
export * from './spinner.component';
|
||||
export * from './actions-dropdown.component';
|
||||
export * from './conditions.component';
|
||||
export * from './pagination.component';
|
||||
@@ -39,3 +38,4 @@ export * from './sidenav.component';
|
||||
export * from './aca-header.component';
|
||||
export * from './error.component';
|
||||
export * from './datetime-picker/datetime-picker.component';
|
||||
export * from './edit-mode.component';
|
||||
|
||||
+1
-1
@@ -44,6 +44,6 @@ export class SearchFiltersLocation extends BaseComponent {
|
||||
await page.searchFiltersLocation.addOptionInput.fill(location);
|
||||
await page.searchFiltersLocation.searchOption(location).click();
|
||||
await page.searchMenuCard.menuCardApply.click();
|
||||
await page.dataTable.progressBarWaitForReload();
|
||||
await page.dataTable.spinnerWaitForReload();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -95,6 +95,6 @@ export class SearchFiltersProperties extends BaseComponent {
|
||||
}
|
||||
|
||||
await page.searchMenuCard.menuCardApply.click();
|
||||
await page.dataTable.progressBarWaitForReload();
|
||||
await page.dataTable.spinnerWaitForReload();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -44,12 +44,12 @@ export class SearchFiltersTags extends BaseComponent {
|
||||
await page.searchFiltersTags.addOptionInput.fill(tag);
|
||||
await this.searchOption(tag).click();
|
||||
await page.searchMenuCard.menuCardApply.click();
|
||||
await page.dataTable.progressBarWaitForReload();
|
||||
await page.dataTable.spinnerWaitForReload();
|
||||
}
|
||||
|
||||
async clearTagFilter(page: SearchPage): Promise<void> {
|
||||
await page.searchFilters.tagsFilter.click();
|
||||
await page.searchMenuCard.menuCardClear.click();
|
||||
await page.dataTable.progressBarWaitForReload();
|
||||
await page.dataTable.spinnerWaitForReload();
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -80,6 +80,6 @@ export class SearchSortingPicker extends BaseComponent {
|
||||
await elem.click();
|
||||
const directionSortElement = this.page.locator(`[id="${optionId}-${direction.toLocaleLowerCase()}"]`);
|
||||
await directionSortElement.click();
|
||||
await this.progressBarWaitForReload();
|
||||
await this.spinnerWaitForReload();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
import { Page } from '@playwright/test';
|
||||
import { PlaywrightBase } from '../playwright-base';
|
||||
import { SnackBarComponent, SpinnerComponent } from '../components';
|
||||
import { SnackBarComponent } from '../components';
|
||||
|
||||
export interface NavigateOptions {
|
||||
query?: string;
|
||||
@@ -36,14 +36,12 @@ export abstract class BasePage extends PlaywrightBase {
|
||||
private readonly pageUrl: string;
|
||||
private readonly urlRequest: RegExp;
|
||||
public snackBar: SnackBarComponent;
|
||||
public spinner: SpinnerComponent;
|
||||
|
||||
protected constructor(page: Page, pageUrl: string, urlRequest?: RegExp) {
|
||||
super(page);
|
||||
this.pageUrl = pageUrl;
|
||||
this.urlRequest = urlRequest;
|
||||
this.snackBar = new SnackBarComponent(this.page);
|
||||
this.spinner = new SpinnerComponent(this.page);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,7 +79,7 @@ export abstract class BasePage extends PlaywrightBase {
|
||||
timeout: 60000
|
||||
});
|
||||
}
|
||||
await this.spinner.waitForReload();
|
||||
await this.spinnerWaitForReload();
|
||||
}
|
||||
|
||||
async reload(options?: Pick<NavigateOptions, 'waitUntil'>): Promise<void> {
|
||||
|
||||
@@ -53,7 +53,7 @@ export class LoginPage extends BasePage {
|
||||
await this.submitButton.click();
|
||||
|
||||
if (options?.waitForLoading) {
|
||||
await Promise.all([this.page.waitForLoadState('domcontentloaded'), this.spinner.waitForReload()]);
|
||||
await Promise.all([this.page.waitForLoadState('domcontentloaded'), this.spinnerWaitForReload()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,8 @@ import {
|
||||
UploadDialog,
|
||||
SnackBarComponent,
|
||||
EditDialog,
|
||||
FolderInformationDialogComponent
|
||||
FolderInformationDialogComponent,
|
||||
EditModeComponent
|
||||
} from '../components';
|
||||
|
||||
export class PersonalFilesPage extends BasePage {
|
||||
@@ -72,6 +73,7 @@ export class PersonalFilesPage extends BasePage {
|
||||
public shareDialog = new ShareDialogComponent(this.page);
|
||||
public confirmDialog = new AdfConfirmDialogComponent(this.page);
|
||||
public infoDrawer = new AdfInfoDrawerComponent(this.page);
|
||||
public nodeInfoEditMode = new EditModeComponent(this.page);
|
||||
public uploadNewVersionDialog = new UploadNewVersionDialog(this.page);
|
||||
public manageVersionsDialog = new ManageVersionsDialog(this.page);
|
||||
public uploadDialog = new UploadDialog(this.page);
|
||||
|
||||
@@ -100,7 +100,7 @@ export class SearchPage extends BasePage {
|
||||
await this.searchInDialog.applyButton.click();
|
||||
await this.clickSearchButton();
|
||||
await this.searchInputComponent.searchFor(searchText);
|
||||
await this.dataTable.progressBarWaitForReload();
|
||||
await this.dataTable.spinnerWaitForReload();
|
||||
}
|
||||
|
||||
async clickSearchButton() {
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
*/
|
||||
|
||||
import { Page } from '@playwright/test';
|
||||
import { GenericLogger, LoggerLike } from '../utils';
|
||||
import { logger, LoggerLike, timeouts } from '../utils';
|
||||
|
||||
export abstract class PlaywrightBase {
|
||||
public page: Page;
|
||||
@@ -31,6 +31,12 @@ export abstract class PlaywrightBase {
|
||||
|
||||
protected constructor(page: Page) {
|
||||
this.page = page;
|
||||
this.logger = new GenericLogger(process.env.PLAYWRIGHT_CUSTOM_LOG_LEVEL);
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
async spinnerWaitForReload(): Promise<void> {
|
||||
const spinner = this.page.locator('[role="progressbar"]');
|
||||
await spinner.waitFor({ state: 'attached', timeout: timeouts.medium }).catch(() => {});
|
||||
await spinner.waitFor({ state: 'detached', timeout: timeouts.normal }).catch(() => {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,65 +24,29 @@
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
|
||||
export const infoColor = '\x1b[36m%s\x1b[0m';
|
||||
export const logColor = '\x1b[35m%s\x1b[0m';
|
||||
export const warnColor = '\x1b[33m%s\x1b[0m';
|
||||
export const errorColor = '\x1b[31m%s\x1b[0m';
|
||||
|
||||
export type LOG_LEVEL = 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'SILENT';
|
||||
|
||||
export class LogLevelsEnum extends Number {
|
||||
public static readonly TRACE: number = 5;
|
||||
public static readonly DEBUG: number = 4;
|
||||
public static readonly INFO: number = 3;
|
||||
public static readonly WARN: number = 2;
|
||||
public static readonly ERROR: number = 1;
|
||||
public static readonly SILENT: number = 0;
|
||||
}
|
||||
|
||||
export const logLevels: { level: LogLevelsEnum; name: LOG_LEVEL }[] = [
|
||||
{ level: LogLevelsEnum.TRACE, name: 'TRACE' },
|
||||
{ level: LogLevelsEnum.DEBUG, name: 'DEBUG' },
|
||||
{ level: LogLevelsEnum.INFO, name: 'INFO' },
|
||||
{ level: LogLevelsEnum.WARN, name: 'WARN' },
|
||||
{ level: LogLevelsEnum.ERROR, name: 'ERROR' },
|
||||
{ level: LogLevelsEnum.SILENT, name: 'SILENT' }
|
||||
];
|
||||
|
||||
export interface LoggerLike {
|
||||
info(...messages: string[]): void;
|
||||
log(...messages: string[]): void;
|
||||
warn(...messages: string[]): void;
|
||||
error(...messages: string[]): void;
|
||||
info(message: string): void;
|
||||
log(message: string): void;
|
||||
warn(message: string): void;
|
||||
error(message: string): void;
|
||||
table(message: object): void;
|
||||
}
|
||||
|
||||
/* eslint-disable no-console */
|
||||
export class GenericLogger implements LoggerLike {
|
||||
private readonly level: LogLevelsEnum;
|
||||
const reset = '\x1b[0m';
|
||||
const red = '\x1b[31m';
|
||||
const yellow = '\x1b[33m';
|
||||
const blue = '\x1b[34m';
|
||||
const cyan = '\x1b[36m';
|
||||
|
||||
constructor(logLevel: string) {
|
||||
this.level = logLevels.find(({ name }) => name === logLevel)?.level || LogLevelsEnum.ERROR;
|
||||
}
|
||||
|
||||
info(...messages: string[]): void {
|
||||
if (Number(this.level) >= LogLevelsEnum.INFO) {
|
||||
console.log(infoColor, messages.join(''));
|
||||
}
|
||||
}
|
||||
|
||||
log(...messages: string[]): void {
|
||||
if (Number(this.level) >= LogLevelsEnum.TRACE) {
|
||||
console.log(logColor, messages.join(''));
|
||||
}
|
||||
}
|
||||
|
||||
warn(...messages: string[]): void {
|
||||
if (Number(this.level) >= LogLevelsEnum.WARN) {
|
||||
console.log(warnColor, messages.join(''));
|
||||
}
|
||||
}
|
||||
|
||||
error(...messages: string[]): void {
|
||||
console.log(errorColor, messages.join(''));
|
||||
}
|
||||
}
|
||||
export const logger: LoggerLike = {
|
||||
// eslint-disable-next-line no-console,no-restricted-syntax
|
||||
info: (message: string) => console.info(`${blue}[INFO]${reset}`, message),
|
||||
// eslint-disable-next-line no-console,no-restricted-syntax
|
||||
log: (message: string) => console.log(`${cyan}[LOG]${reset}`, message),
|
||||
// eslint-disable-next-line no-console,no-restricted-syntax
|
||||
error: (message: string) => console.error(`${red}[ERROR]${reset}`, message),
|
||||
// eslint-disable-next-line no-console,no-restricted-syntax
|
||||
warn: (message: string) => console.warn(`${yellow}[WARN]${reset}`, message),
|
||||
// eslint-disable-next-line no-console,no-restricted-syntax
|
||||
table: (message: object) => console.table(message)
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user