[ACS-6510] playwright List View e2e test (#3566)

* [ACS-6435] playwright e2e for list views personal files

* e2e test for trash page

* e2e test for trash page

* e2e test for file-libraries page

* e2e test for file-libraries page fix

* e2e test for file-libraries page fix

* e2e test shared recent  page

* e2e test shared recent  page fix

* e2e test review comment fix

* e2e test review fix flaky test fix

* e2e test fail test fix

* e2e test fail  fix

* test code fix

* protractor list-view test enable

* [ACS-6510] listview playwright e2e test

* code fix

* code fix

* code fix

* code fix

* code fix

* code fix

* code fix for review

* code fix for review
This commit is contained in:
Akash Rathod
2023-12-22 09:36:32 +01:00
committed by GitHub
parent 1d0752ce8f
commit 7465bbbf6d
20 changed files with 859 additions and 817 deletions

View File

@@ -41,7 +41,7 @@ export class FileActionsApi {
return classObj;
}
async uploadFile(fileLocation: string, fileName: string, parentFolderId: string): Promise<any> {
async uploadFile(fileLocation: string, fileName: string, parentFolderId: string): Promise<NodeEntry> {
const file = fs.createReadStream(fileLocation);
return this.apiService.upload.uploadFile(file, '', parentFolderId, null, {
name: fileName,
@@ -50,7 +50,13 @@ export class FileActionsApi {
});
}
async uploadFileWithRename(fileLocation: string, newName: string, parentId: string = '-my-', title: string = '', description: string = '') {
async uploadFileWithRename(
fileLocation: string,
newName: string,
parentId: string = '-my-',
title: string = '',
description: string = ''
): Promise<NodeEntry> {
const file = fs.createReadStream(fileLocation);
const nodeProps = {
properties: {
@@ -68,10 +74,11 @@ export class FileActionsApi {
return await this.apiService.upload.uploadFile(file, '', parentId, nodeProps, opts);
} catch (error) {
Logger.error(`${this.constructor.name} ${this.uploadFileWithRename.name}`, error);
return Promise.reject(error);
}
}
async lockNodes(nodeIds: string[], lockType: string = 'ALLOW_OWNER_CHANGES') {
async lockNodes(nodeIds: string[], lockType: string = 'ALLOW_OWNER_CHANGES'): Promise<void> {
try {
for (const nodeId of nodeIds) {
await this.apiService.nodes.lockNode(nodeId, { type: lockType });
@@ -93,7 +100,7 @@ export class FileActionsApi {
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) {
Logger.error(`${this.constructor.name} ${this.getNodeProperty.name}`, error);
return '';
@@ -145,7 +152,7 @@ export class FileActionsApi {
return this.apiService.search.search(data);
} catch (error) {
Logger.error(`SearchApi queryNodesNames : catch : `, error);
return new ResultSetPaging;
return new ResultSetPaging();
}
}
@@ -167,4 +174,18 @@ export class FileActionsApi {
Logger.error(`\tExpected: ${data.expect} items, but found ${error}`);
}
}
async updateNodeContent(nodeId: string, content: string, majorVersion: boolean = true, comment?: string, newName?: string): Promise<NodeEntry> {
try {
const opts: { [key: string]: string | boolean } = {
majorVersion: majorVersion,
comment: comment,
name: newName
};
return await this.apiService.nodes.updateNodeContent(nodeId, content, opts);
} catch (error) {
console.error(`${this.constructor.name} ${this.updateNodeContent.name}`, error);
return Promise.reject(error);
}
}
}

View File

@@ -81,6 +81,14 @@ export class NodesApi {
}
}
async deleteDeletedNode(name: string): Promise<void> {
try {
await this.apiService.trashCan.deleteDeletedNode(name);
} catch (error) {
console.error(`${this.constructor.name} ${this.deleteDeletedNode.name}: ${error}`);
}
}
private async createNode(
nodeType: string,
name: string,

View File

@@ -23,7 +23,16 @@
*/
import { ApiClientFactory } from './api-client-factory';
import { Site, SiteBodyCreate, SiteEntry, SiteMemberEntry, SiteMembershipBodyCreate, SiteMembershipBodyUpdate, SiteMembershipRequestBodyCreate, SiteMembershipRequestEntry } from '@alfresco/js-api';
import {
Site,
SiteBodyCreate,
SiteEntry,
SiteMemberEntry,
SiteMembershipBodyCreate,
SiteMembershipBodyUpdate,
SiteMembershipRequestBodyCreate,
SiteMembershipRequestEntry
} from '@alfresco/js-api';
export class SitesApi {
private apiService: ApiClientFactory;
@@ -88,7 +97,7 @@ export class SitesApi {
return await this.apiService.sites.updateSiteMembership(siteId, userId, siteRole);
} catch (error) {
console.error(`SitesApi updateSiteMember : catch : `, error);
return new SiteMemberEntry;
return new SiteMemberEntry();
}
}
@@ -105,7 +114,7 @@ export class SitesApi {
return this.updateSiteMember(siteId, userId, role);
} else {
console.error(`SitesApi addSiteMember : catch : `, error);
return new SiteMemberEntry;
return new SiteMemberEntry();
}
}
}
@@ -141,4 +150,12 @@ export class SitesApi {
return null;
}
}
async deleteSiteMember(siteId: string, userId: string) {
try {
return await this.apiService.sites.deleteSiteMembership(siteId, userId);
} catch (error) {
console.error(`SitesApi deleteSiteMember : catch : `, error);
}
}
}

View File

@@ -44,6 +44,11 @@ export class DataTableComponent extends BaseComponent {
sortedColumnHeader = this.getChild(`.adf-datatable__header--sorted-asc .adf-datatable-cell-header-content .adf-datatable-cell-value,
.adf-datatable__header--sorted-desc .adf-datatable-cell-header-content .adf-datatable-cell-value`);
columnHeaders = this.getChild('.adf-datatable-row .adf-datatable-cell-header .adf-datatable-cell-value');
emptyList = this.getChild('div.adf-no-content-container');
emptyListTitle = this.getChild('.adf-empty-content__title');
emptyListSubtitle = this.getChild('.adf-empty-content__subtitle');
emptySearchText = this.getChild('.empty-search__text');
emptyListTest = this.getChild('adf-custom-empty-content-template');
/** Locator for row (or rows) */
getRowLocator = this.getChild(`adf-datatable-row`);
@@ -123,17 +128,21 @@ export class DataTableComponent extends BaseComponent {
getSearchResultLinkByName = (name: string): Locator => this.getChild('.aca-search-results-row span[role="link"]', { hasText: name });
async sortBy(columnTitle: string, order: 'Ascending' | 'Descending'): Promise<void> {
const columnHeaderLocator = this.getColumnHeaderByTitleLocator(columnTitle);
await this.spinnerWaitForReload();
await columnHeaderLocator.click();
getColumnValuesByName = (name: string): Locator => this.getChild(`div[title="${name}"] span`);
const sortAttribute = await columnHeaderLocator.getAttribute('aria-sort');
if (sortAttribute !== order) {
await columnHeaderLocator.click();
getColumnHeaderByName = (headerTitle: string): Locator =>
this.getChild('.adf-datatable-row .adf-datatable-cell-header .adf-datatable-cell-value', { hasText: headerTitle });
async sortBy(label: string, order: 'asc' | 'desc'): Promise<void> {
const sortColumn = await this.getSortedColumnHeaderText();
let sortOrder = await this.getSortingOrder();
if (sortColumn !== label) {
await this.getColumnHeaderByName(label).click({ force: true });
sortOrder = await this.getSortingOrder();
}
if (sortOrder !== order) {
await this.getChild('span[class*="adf-datatable__header--sorted"]').click();
}
await this.spinnerWaitForReload();
}
/**
@@ -281,4 +290,29 @@ export class DataTableComponent extends BaseComponent {
async getRowAllInnerTexts(name: string): Promise<string> {
return (await this.getRowByName(name).locator('span').allInnerTexts()).toString();
}
async getFirstElementDetail(name: string): Promise<string> {
const firstNode = this.getColumnValuesByName(name).first();
return firstNode.innerText();
}
async isEmpty(): Promise<boolean> {
return this.emptyList.isVisible();
}
async getEmptyStateTitle(): Promise<string> {
return (await this.isEmpty()) ? this.emptyListTitle.innerText() : '';
}
async getEmptyStateSubtitle(): Promise<string> {
return (await this.isEmpty()) ? this.emptyListSubtitle.innerText() : '';
}
async getEmptyListText(): Promise<string> {
return (await this.isEmpty()) ? this.emptyListTest.innerText() : '';
}
async getRowsCount(): Promise<number> {
return this.getRowLocator.count();
}
}

View File

@@ -0,0 +1,36 @@
/*!
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Alfresco Example Content Application
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* from Hyland Software. If not, see <http://www.gnu.org/licenses/>.
*/
import { Page } from '@playwright/test';
import { BaseComponent } from './base.component';
export class ErrorComponent extends BaseComponent {
private static rootElement = 'aca-page-layout';
genericError = this.getChild('aca-generic-error');
genericErrorTitle = this.getChild('.generic-error__title');
constructor(page: Page) {
super(page, ErrorComponent.rootElement);
}
}

View File

@@ -38,3 +38,4 @@ export * from './search/search-overlay.components';
export * from './breadcrumb/breadcrumb.component';
export * from './sidenav.component';
export * from './aca-header.component';
export * from './error.component';

View File

@@ -105,7 +105,7 @@ export class PaginationComponent extends BaseComponent {
if (await this.isNextEnabled()) {
await this.nextButton.click();
}
} catch(error) {
} catch (error) {
throw new Error(`Failed on previous click: ${error}`);
}
}
@@ -115,7 +115,7 @@ export class PaginationComponent extends BaseComponent {
if (await this.isPreviousEnabled()) {
await this.previousButton.click();
}
} catch(error) {
} catch (error) {
throw new Error(`Failed on previous click: ${error}`);
}
}
@@ -166,4 +166,12 @@ export class PaginationComponent extends BaseComponent {
async closeMenu(): Promise<void> {
await this.page.keyboard.press('Escape');
}
async isRangePresent(): Promise<boolean> {
return this.range.isVisible();
}
async isMaxItemsPresent(): Promise<boolean> {
return this.maxItems.isVisible();
}
}

View File

@@ -36,7 +36,8 @@ import {
MatMenuComponent,
ViewerComponent,
SidenavComponent,
PaginationComponent
PaginationComponent,
ErrorComponent
} from '../components';
export class PersonalFilesPage extends BasePage {
@@ -58,6 +59,7 @@ export class PersonalFilesPage extends BasePage {
public sidenav = new SidenavComponent(this.page);
public createFromTemplateDialogComponent = new CreateFromTemplateDialogComponent(this.page);
public pagination = new PaginationComponent(this.page);
public errorDialog = new ErrorComponent(this.page);
async selectCreateFolder(): Promise<void> {
await this.acaHeader.createButton.click();

View File

@@ -24,7 +24,7 @@
import { Page } from '@playwright/test';
import { BasePage } from './base.page';
import { DataTableComponent, MatMenuComponent, ViewerComponent, SidenavComponent, Breadcrumb } from '../components';
import { DataTableComponent, MatMenuComponent, ViewerComponent, SidenavComponent, Breadcrumb, PaginationComponent } from '../components';
import { AcaHeader } from '../components/aca-header.component';
import { AdfFolderDialogComponent, ViewerOverlayDialogComponent } from '../components/dialogs';
@@ -43,4 +43,5 @@ export class TrashPage extends BasePage {
public viewerDialog = new ViewerOverlayDialogComponent(this.page);
public sidenav = new SidenavComponent(this.page);
public breadcrumb = new Breadcrumb(this.page);
public pagination = new PaginationComponent(this.page);
}