From 335c5ba85c367d662c3c99e436cb781c665716b6 Mon Sep 17 00:00:00 2001 From: "akash.rathod@hyland.com" Date: Wed, 20 Sep 2023 11:43:58 +0200 Subject: [PATCH 01/10] [ACS-5923] sidenav and singleclick test --- .../src/tests/breadcrumb-admin.spec.ts | 57 ++++++++ .../navigation/src/tests/sidebar.spec.ts | 125 ++++++++++++++++++ .../navigation/src/tests/single-click.spec.ts | 98 ++++++++++++++ .../breadcrumb/breadcrumb.component.ts | 3 +- .../dataTable/data-table.component.ts | 2 + .../src/page-objects/components/index.ts | 1 + .../search/search-input.component.ts | 4 +- .../components/sidenav.component.ts | 98 ++++++++++++++ .../pages/favorites-libraries.page.ts | 45 +++++++ .../src/page-objects/pages/favorites.page.ts | 3 +- .../src/page-objects/pages/index.ts | 1 + .../page-objects/pages/my-libraries.page.ts | 4 +- .../page-objects/pages/personal-files.page.ts | 3 +- .../page-objects/pages/recent-files.page.ts | 4 +- .../src/page-objects/pages/search.page.ts | 3 +- .../src/page-objects/pages/shared.page.ts | 3 +- .../src/page-objects/pages/trash.page.ts | 3 +- .../aca-playwright-shared/src/utils/config.ts | 45 +++++++ .../aca-playwright-shared/src/utils/index.ts | 1 + 19 files changed, 492 insertions(+), 11 deletions(-) create mode 100644 e2e/playwright/navigation/src/tests/breadcrumb-admin.spec.ts create mode 100644 e2e/playwright/navigation/src/tests/sidebar.spec.ts create mode 100644 e2e/playwright/navigation/src/tests/single-click.spec.ts create mode 100644 projects/aca-playwright-shared/src/page-objects/components/sidenav.component.ts create mode 100644 projects/aca-playwright-shared/src/page-objects/pages/favorites-libraries.page.ts create mode 100644 projects/aca-playwright-shared/src/utils/config.ts diff --git a/e2e/playwright/navigation/src/tests/breadcrumb-admin.spec.ts b/e2e/playwright/navigation/src/tests/breadcrumb-admin.spec.ts new file mode 100644 index 0000000000..704ccda9d8 --- /dev/null +++ b/e2e/playwright/navigation/src/tests/breadcrumb-admin.spec.ts @@ -0,0 +1,57 @@ +/*! + * 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 . + */ + +import { expect } from '@playwright/test'; +import { ApiClientFactory, getUserState, NodesApi, test, Utils } from '@alfresco/playwright-shared'; + +test.use({ storageState: getUserState('admin') }); +test.describe('as admin', () => { + const apiClientFactory = new ApiClientFactory(); + const userFolder = `userFolder-${Utils.random()}`; + const username = `userAdmin-${Utils.random()}`; + let userFolderId: string; + let nodesApi: NodesApi; + + test.beforeAll(async () => { + await apiClientFactory.setUpAcaBackend('admin'); + await apiClientFactory.createUser({ username }); + nodesApi = await NodesApi.initialize(username, username); + const node = await nodesApi.createFolder(userFolder); + userFolderId = node.entry.id; + }); + + test.beforeEach(async ({ personalFiles }) => { + await personalFiles.navigate({ remoteUrl: `#/personal-files}` }); + }); + + test.afterAll(async () => { + await apiClientFactory.nodes.deleteNode(userFolderId, { permanent: true }); + }); + + test(`[C260970] Breadcrumb on navigation to a user's home`, async ({ personalFiles }) => { + await personalFiles.navigate({ remoteUrl: `#/personal-files/${userFolderId}` }); + personalFiles.breadcrumb.getItemByTitle(username).waitFor({ state: 'attached' }); + expect(await personalFiles.breadcrumb.getAllItems()).toEqual(['Personal Files', 'User Homes', username, userFolder]); + }); +}); diff --git a/e2e/playwright/navigation/src/tests/sidebar.spec.ts b/e2e/playwright/navigation/src/tests/sidebar.spec.ts new file mode 100644 index 0000000000..846d938975 --- /dev/null +++ b/e2e/playwright/navigation/src/tests/sidebar.spec.ts @@ -0,0 +1,125 @@ +/*! + * 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 . + */ + +import { expect } from '@playwright/test'; +import { ApiClientFactory, APP_ROUTES, getUserState, SIDEBAR_LABELS, test } from '@alfresco/playwright-shared'; + +test.use({ storageState: getUserState('hruser') }); +test.describe('Sidebar', () => { + const apiClientFactory = new ApiClientFactory(); + + test.beforeAll(async () => { + await apiClientFactory.setUpAcaBackend('hruser'); + }); + + test('[C289902] navigate to Favorite Libraries', async ({ personalFiles, favoritePage }) => { + await personalFiles.navigate(); + await personalFiles.sidenav.openPanel('Favorite Libraries'); + await personalFiles.dataTable.spinnerWaitForReload(); + expect(favoritePage.page.url()).toContain(APP_ROUTES.FAVORITE_LIBRARIES); + expect(await favoritePage.sidenav.isActive(SIDEBAR_LABELS.FAVORITE_LIBRARIES), 'Favorite Libraries link not active').toBe(true); + }); + + test('[C289901] navigate to My Libraries', async ({ personalFiles, myLibrariesPage }) => { + await personalFiles.navigate(); + await personalFiles.sidenav.openPanel('My Libraries'); + await personalFiles.dataTable.spinnerWaitForReload(); + expect(myLibrariesPage.page.url()).toContain(APP_ROUTES.MY_LIBRARIES); + expect(await myLibrariesPage.sidenav.isActive(SIDEBAR_LABELS.MY_LIBRARIES), 'My Libraries link not active').toBe(true); + }); + + test('[C213110] navigates to "Shared Files"', async ({ personalFiles, sharedPage }) => { + await personalFiles.navigate(); + await personalFiles.sidenav.openPanel('Shared'); + await personalFiles.dataTable.spinnerWaitForReload(); + expect(sharedPage.page.url()).toContain(APP_ROUTES.SHARED_FILES); + expect(await sharedPage.sidenav.isActive(SIDEBAR_LABELS.SHARED_FILES), 'Shared Files link not active').toBe(true); + }); + + test('[C213166] navigates to "Recent Files"', async ({ personalFiles, recentFilesPage }) => { + await personalFiles.navigate(); + await personalFiles.sidenav.openPanel('Recent Files'); + await personalFiles.dataTable.spinnerWaitForReload(); + expect(recentFilesPage.page.url()).toContain(APP_ROUTES.RECENT_FILES); + expect(await recentFilesPage.sidenav.isActive(SIDEBAR_LABELS.RECENT_FILES), 'Recent Files link not active').toBe(true); + }); + + test('[C213225] navigates to "Favorites"', async ({ personalFiles, favoritePage }) => { + await personalFiles.navigate(); + await personalFiles.sidenav.openPanel('Favorites'); + await personalFiles.dataTable.spinnerWaitForReload(); + expect(favoritePage.page.url()).toContain(APP_ROUTES.FAVORITES); + expect(await favoritePage.sidenav.isActive(SIDEBAR_LABELS.FAVORITES), 'Favorites link not active').toBe(true); + }); + + test('[C213216] navigates to "Trash"', async ({ personalFiles, trashPage }) => { + await personalFiles.navigate(); + await personalFiles.sidenav.openPanel('Trash'); + await personalFiles.dataTable.spinnerWaitForReload(); + expect(trashPage.page.url()).toContain(APP_ROUTES.TRASHCAN); + expect(await trashPage.sidenav.isActive(SIDEBAR_LABELS.TRASH), 'Trash link not active').toBe(true); + }); + + test('[C280409] navigates to "Personal Files"', async ({ personalFiles, trashPage }) => { + await trashPage.navigate(); + await personalFiles.sidenav.openPanel('Personal Files'); + await personalFiles.dataTable.spinnerWaitForReload(); + expect(personalFiles.page.url()).toContain(APP_ROUTES.PERSONAL_FILES); + expect(await personalFiles.sidenav.isActive(SIDEBAR_LABELS.PERSONAL_FILES), 'Personal Files link not active').toBe(true); + }); + + test('[C277230] sidenav can be expanded when search results page is displayed', async ({ personalFiles }) => { + await personalFiles.navigate({ remoteUrl: `#/search;q=test` }); + expect(await personalFiles.sidenav.isSidenavExpanded(), 'Sidebar expanded').toBe(false); + await personalFiles.sidenav.expandSideNav(); + expect(await personalFiles.sidenav.isSidenavExpanded(), 'Sidebar not expanded').toBe(true); + }); + + test('[C269100] sidebar state is preserved on page refresh', async ({ personalFiles }) => { + await personalFiles.navigate(); + expect(await personalFiles.sidenav.isSidenavExpanded(), 'Sidebar expanded').toBe(true); + await personalFiles.reload(); + expect(await personalFiles.sidenav.isSidenavExpanded(), 'Sidebar expanded').toBe(true); + + await personalFiles.sidenav.collapseSideNav(); + + expect(await personalFiles.sidenav.isSidenavExpanded(), 'Sidebar not expanded').toBe(false); + await personalFiles.reload(); + expect(await personalFiles.sidenav.isSidenavExpanded(), 'Sidebar not expanded').toBe(false); + }); + + test('[C269096] sidebar toggle', async ({ personalFiles }) => { + await personalFiles.navigate(); + await personalFiles.sidenav.collapseSideNav(); + expect(await personalFiles.sidenav.isSidenavExpanded(), 'Sidebar not expanded').toBe(false); + await personalFiles.sidenav.expandSideNav(); + expect(await personalFiles.sidenav.isSidenavExpanded(), 'Sidebar not expanded').toBe(true); + }); + + test('[C277224] sidenav returns to the default state when navigating away from the Search Results page', async ({ personalFiles, searchPage }) => { + await personalFiles.navigate({ remoteUrl: `#/search;q=test` }); + await searchPage.searchInput.getIconByName('close').click(); + expect(await personalFiles.sidenav.isSidenavExpanded(), 'Sidebar not expanded').toBe(true); + }); +}); diff --git a/e2e/playwright/navigation/src/tests/single-click.spec.ts b/e2e/playwright/navigation/src/tests/single-click.spec.ts new file mode 100644 index 0000000000..5b3505bc49 --- /dev/null +++ b/e2e/playwright/navigation/src/tests/single-click.spec.ts @@ -0,0 +1,98 @@ +/*! + * 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 . + */ + +import { expect } from '@playwright/test'; +import { ApiClientFactory, getUserState, test, Utils } from '@alfresco/playwright-shared'; + +test.use({ storageState: getUserState('hruser') }); +test.describe('Single click on item name', () => { + const apiClientFactory = new ApiClientFactory(); + + const folder1 = `folder1-${Utils.random()}`; + let folder1Id: string; + const folderSearch = `folder1-${Utils.random()}`; + let folderSearchId: string; + + const deletedFile1 = `file1-${Utils.random()}.txt`; + let deletedFile1Id: string; + const deletedFolder1 = `folder1-${Utils.random()}`; + let deletedFolder1Id: string; + + const siteName = `site-${Utils.random()}`; + const fileSite = `fileSite-${Utils.random()}.txt`; + + test.beforeAll(async ({ nodesApiAction, sitesApiAction }) => { + await apiClientFactory.setUpAcaBackend('hruser'); + const node = await apiClientFactory.nodes.createNode('-my-', { name: folder1, nodeType: 'cm:folder', relativePath: '/' }); + folder1Id = node.entry.id; + folderSearchId = (await nodesApiAction.createFolder(folderSearch)).entry.id; + deletedFile1Id = (await nodesApiAction.createFile(deletedFile1)).entry.id; + deletedFolder1Id = (await nodesApiAction.createFolder(deletedFolder1)).entry.id; + + await sitesApiAction.createSite(siteName); + const docLibId = await sitesApiAction.getDocLibId(siteName); + await nodesApiAction.createFile(fileSite, docLibId); + }); + + test.afterAll(async ({ nodesApiAction }) => { + await nodesApiAction.deleteNodes([deletedFolder1Id, deletedFile1Id], true); + }); + + test('[C284899] Hyperlink does not appear for items in the Trash', async ({ trashPage }) => { + await trashPage.navigate(); + expect(await trashPage.dataTable.getCellLinkByName(deletedFile1).isVisible(), 'Link on name is present').toBe(false); + expect(await trashPage.dataTable.getCellLinkByName(deletedFolder1).isVisible(), 'Link on name is present').toBe(false); + }); + + test.describe('on Personal Files', () => { + test.afterAll(async ({ nodesApiAction }) => { + await nodesApiAction.deleteNodes([folder1Id, folderSearchId], true); + }); + + test('[C280034] Navigate inside the folder when clicking the hyperlink on Personal Files', async ({ personalFiles }) => { + await personalFiles.navigate(); + await personalFiles.dataTable.getCellLinkByName(folder1).click(); + await personalFiles.dataTable.spinnerWaitForReload(); + expect(await personalFiles.breadcrumb.currentItem.innerText()).toBe(folder1); + }); + + test('[C306990] Navigate inside the folder when clicking the hyperlink on Search Results', async ({ personalFiles }) => { + await personalFiles.navigate({ remoteUrl: `#/search;q=${folderSearch}` }); + await personalFiles.reload(); + await personalFiles.dataTable.spinnerWaitForReload(); + await personalFiles.dataTable.getSearchResultLinkByName(folderSearch).click(); + await personalFiles.dataTable.spinnerWaitForReload(); + expect(await personalFiles.breadcrumb.currentItem.innerText()).toBe(folderSearch); + }); + }); + + test('[C284902] Navigate inside the library when clicking the hyperlink on File Libraries', async ({ myLibrariesPage }) => { + await myLibrariesPage.navigate(); + await myLibrariesPage.dataTable.goThroughPagesLookingForRowWithName(siteName); + await myLibrariesPage.dataTable.getCellLinkByName(siteName).click(); + await myLibrariesPage.dataTable.spinnerWaitForReload(); + expect(await myLibrariesPage.breadcrumb.currentItem.innerText()).toBe(siteName); + expect(await myLibrariesPage.dataTable.getCellLinkByName(fileSite).isVisible(), `${fileSite} not displayed`).toBe(true); + }); +}); diff --git a/projects/aca-playwright-shared/src/page-objects/components/breadcrumb/breadcrumb.component.ts b/projects/aca-playwright-shared/src/page-objects/components/breadcrumb/breadcrumb.component.ts index 970cf984b4..4723cfadc8 100755 --- a/projects/aca-playwright-shared/src/page-objects/components/breadcrumb/breadcrumb.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/breadcrumb/breadcrumb.component.ts @@ -23,11 +23,12 @@ */ import { BaseComponent } from '.././base.component'; -import { Page } from '@playwright/test'; +import { Locator, Page } from '@playwright/test'; export class Breadcrumb extends BaseComponent { private static rootElement = 'adf-breadcrumb'; public items = this.getChild('.adf-breadcrumb-item'); public currentItem = this.getChild('.adf-breadcrumb-item-current'); + getItemByTitle = (name: string): Locator => this.getChild(`.adf-breadcrumb-item[title=${name}]`); constructor(page: Page) { super(page, Breadcrumb.rootElement); diff --git a/projects/aca-playwright-shared/src/page-objects/components/dataTable/data-table.component.ts b/projects/aca-playwright-shared/src/page-objects/components/dataTable/data-table.component.ts index f32c908c81..eb66e89699 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/dataTable/data-table.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/dataTable/data-table.component.ts @@ -113,6 +113,8 @@ export class DataTableComponent extends BaseComponent { getColumnHeaderByTitleLocator = (headerTitle: string): Locator => this.getChild('[role="columnheader"]', { hasText: headerTitle }); + getSearchResultLinkByName = (name: string): Locator => this.getChild('.aca-search-results-row span[role="link"]', { hasText: name }); + async sortBy(columnTitle: string, order: 'Ascending' | 'Descending'): Promise { const columnHeaderLocator = this.getColumnHeaderByTitleLocator(columnTitle); await this.spinnerWaitForReload(); diff --git a/projects/aca-playwright-shared/src/page-objects/components/index.ts b/projects/aca-playwright-shared/src/page-objects/components/index.ts index 5812c570dd..567c0403f3 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/index.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/index.ts @@ -36,3 +36,4 @@ export * from './viewer.component'; export * from './search/search-input.component'; export * from './search/search-overlay.components'; export * from './breadcrumb/breadcrumb.component'; +export * from './sidenav.component'; diff --git a/projects/aca-playwright-shared/src/page-objects/components/search/search-input.component.ts b/projects/aca-playwright-shared/src/page-objects/components/search/search-input.component.ts index 1f0d182ca5..78ca1fa60d 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/search/search-input.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/search/search-input.component.ts @@ -29,6 +29,7 @@ import { timeouts } from '../../../utils'; export class SearchInputComponent extends BaseComponent { private static rootElement = 'aca-page-layout'; public searchButton = this.getChild('aca-search-input .app-search-button'); + getIconByName = (name: string): Locator => this.getChild('.mat-icon[role="img"]', { hasText: name }); /** * Method used in cases where user have possibility to navigate "inside" the element (it's clickable and has link attribute). @@ -43,9 +44,8 @@ export class SearchInputComponent extends BaseComponent { } async performDoubleClickFolderOrFileToOpen(name: string): Promise { - await this.getCellLinkByName(name).waitFor({ state:'visible', timeout: timeouts.normal }); + await this.getCellLinkByName(name).waitFor({ state: 'visible', timeout: timeouts.normal }); await this.getCellLinkByName(name).dblclick(); await this.spinnerWaitForReload(); } - } diff --git a/projects/aca-playwright-shared/src/page-objects/components/sidenav.component.ts b/projects/aca-playwright-shared/src/page-objects/components/sidenav.component.ts new file mode 100644 index 0000000000..94cd7374d1 --- /dev/null +++ b/projects/aca-playwright-shared/src/page-objects/components/sidenav.component.ts @@ -0,0 +1,98 @@ +/*! + * 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 . + */ + +import { BaseComponent } from './base.component'; +import { Locator, Page } from '@playwright/test'; + +export class SidenavComponent extends BaseComponent { + private static rootElement = 'app-sidenav'; + + private personalFiles = this.getChild(`[data-automation-id='app.navbar.personalFiles']`); + private fileLibraries = this.getChild(`[data-automation-id='app.navbar.libraries.menu']`); + private myLibraries = this.getChild(`[data-automation-id='app.navbar.libraries.files']`); + private favoriteLibraries = this.getChild(`[data-automation-id='app.navbar.libraries.favorite']`); + private shared = this.getChild(`[data-automation-id='app.navbar.shared']`); + private recentFiles = this.getChild(`[data-automation-id='app.navbar.recentFiles']`); + private favorites = this.getChild(`[data-automation-id='app.navbar.favorites']`); + private trash = this.getChild(`[data-automation-id='app.navbar.trashcan']`); + private sidenavToggle = this.getChild(`.sidenav-header-title-logo`); + private sidenavExpand = this.page.getByTitle('Expand navigation menu'); + private expandedSidenav = this.page.locator(`[data-automation-id='expanded']`); + + constructor(page: Page) { + super(page, SidenavComponent.rootElement); + } + + async isActive(name: string): Promise { + const cssClass = await this.getLinkLabel(name).getAttribute('class'); + return cssClass.includes('action-button--active'); + } + + async openPanel(name: string): Promise { + await this.getLinkLabel(name).click(); + } + + private getLinkLabel(name: string): Locator { + switch (name) { + case 'Personal Files': + return this.personalFiles; + case 'File Libraries': + return this.fileLibraries; + case 'My Libraries': + return this.myLibraries; + case 'Favorite Libraries': + return this.favoriteLibraries; + case 'Shared': + return this.shared; + case 'Recent Files': + return this.recentFiles; + case 'Favorites': + return this.favorites; + case 'Trash': + return this.trash; + default: + return this.personalFiles; + } + } + + async isSidenavExpanded(): Promise { + return this.expandedSidenav.isVisible(); + } + + async expandSideNav(): Promise { + const expanded = await this.isSidenavExpanded(); + if (!expanded) { + await this.sidenavExpand.click(); + await this.expandedSidenav.waitFor({ state: 'attached' }); + } + } + + async collapseSideNav(): Promise { + const expanded = await this.isSidenavExpanded(); + if (expanded) { + await this.sidenavToggle.click(); + await this.expandedSidenav.waitFor({ state: 'detached' }); + } + } +} diff --git a/projects/aca-playwright-shared/src/page-objects/pages/favorites-libraries.page.ts b/projects/aca-playwright-shared/src/page-objects/pages/favorites-libraries.page.ts new file mode 100644 index 0000000000..c6d57bb9ee --- /dev/null +++ b/projects/aca-playwright-shared/src/page-objects/pages/favorites-libraries.page.ts @@ -0,0 +1,45 @@ +/*! + * 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 . + */ + +import { Page } from '@playwright/test'; +import { BasePage } from './base.page'; +import { DataTableComponent, MatMenuComponent, SidenavComponent, ViewerComponent } from '../components'; +import { AcaHeader } from '../components/aca-header.component'; +import { AdfFolderDialogComponent, ViewerOverlayDialogComponent } from '../components/dialogs'; + +export class FavoritesLibrariesPage extends BasePage { + private static pageUrl = 'favorite/libraries'; + + constructor(page: Page) { + super(page, FavoritesLibrariesPage.pageUrl); + } + + public acaHeader = new AcaHeader(this.page); + public matMenu = new MatMenuComponent(this.page); + public folderDialog = new AdfFolderDialogComponent(this.page); + public dataTable = new DataTableComponent(this.page); + public viewer = new ViewerComponent(this.page); + public viewerDialog = new ViewerOverlayDialogComponent(this.page); + public sidenav = new SidenavComponent(this.page); +} diff --git a/projects/aca-playwright-shared/src/page-objects/pages/favorites.page.ts b/projects/aca-playwright-shared/src/page-objects/pages/favorites.page.ts index 179aeed990..7b1ef705d8 100644 --- a/projects/aca-playwright-shared/src/page-objects/pages/favorites.page.ts +++ b/projects/aca-playwright-shared/src/page-objects/pages/favorites.page.ts @@ -24,7 +24,7 @@ import { Page } from '@playwright/test'; import { BasePage } from './base.page'; -import { DataTableComponent, MatMenuComponent, ViewerComponent } from '../components'; +import { DataTableComponent, MatMenuComponent, ViewerComponent, SidenavComponent } from '../components'; import { AcaHeader } from '../components/aca-header.component'; import { AdfFolderDialogComponent, ViewerOverlayDialogComponent } from '../components/dialogs'; @@ -41,4 +41,5 @@ export class FavoritesPage extends BasePage { public dataTable = new DataTableComponent(this.page); public viewer = new ViewerComponent(this.page); public viewerDialog = new ViewerOverlayDialogComponent(this.page); + public sidenav = new SidenavComponent(this.page); } diff --git a/projects/aca-playwright-shared/src/page-objects/pages/index.ts b/projects/aca-playwright-shared/src/page-objects/pages/index.ts index fcee9ac8e6..d0cb866042 100644 --- a/projects/aca-playwright-shared/src/page-objects/pages/index.ts +++ b/projects/aca-playwright-shared/src/page-objects/pages/index.ts @@ -32,3 +32,4 @@ export * from './shared.page'; export * from './search.page'; export * from './favorites.page'; export * from './trash.page'; +export * from './favorites-libraries.page'; diff --git a/projects/aca-playwright-shared/src/page-objects/pages/my-libraries.page.ts b/projects/aca-playwright-shared/src/page-objects/pages/my-libraries.page.ts index 747a8250ec..b051482080 100644 --- a/projects/aca-playwright-shared/src/page-objects/pages/my-libraries.page.ts +++ b/projects/aca-playwright-shared/src/page-objects/pages/my-libraries.page.ts @@ -33,7 +33,8 @@ import { ViewerComponent, ViewerOverlayDialogComponent, ContentNodeSelectorDialog, - Breadcrumb + Breadcrumb, + SidenavComponent } from '../components'; export class MyLibrariesPage extends BasePage { @@ -51,6 +52,7 @@ export class MyLibrariesPage extends BasePage { public viewerDialog = new ViewerOverlayDialogComponent(this.page); public copyMoveDialog = new ContentNodeSelectorDialog(this.page); public breadcrumb = new Breadcrumb(this.page); + public sidenav = new SidenavComponent(this.page); async selectCreateLibrary(): Promise { await this.acaHeader.createButton.click(); diff --git a/projects/aca-playwright-shared/src/page-objects/pages/personal-files.page.ts b/projects/aca-playwright-shared/src/page-objects/pages/personal-files.page.ts index 46e3bb7982..9bae8882ba 100644 --- a/projects/aca-playwright-shared/src/page-objects/pages/personal-files.page.ts +++ b/projects/aca-playwright-shared/src/page-objects/pages/personal-files.page.ts @@ -24,7 +24,7 @@ import { Page } from '@playwright/test'; import { BasePage } from './base.page'; -import { Breadcrumb, DataTableComponent, MatMenuComponent, ViewerComponent } from '../components'; +import { Breadcrumb, DataTableComponent, MatMenuComponent, ViewerComponent, SidenavComponent } from '../components'; import { AcaHeader } from '../components/aca-header.component'; import { AdfFolderDialogComponent, PasswordOverlayDialogComponent, ViewerOverlayDialogComponent } from '../components/dialogs'; @@ -43,6 +43,7 @@ export class PersonalFilesPage extends BasePage { public passwordDialog = new PasswordOverlayDialogComponent(this.page); public viewerDialog = new ViewerOverlayDialogComponent(this.page); public breadcrumb = new Breadcrumb(this.page); + public sidenav = new SidenavComponent(this.page); async selectCreateFolder(): Promise { await this.acaHeader.createButton.click(); diff --git a/projects/aca-playwright-shared/src/page-objects/pages/recent-files.page.ts b/projects/aca-playwright-shared/src/page-objects/pages/recent-files.page.ts index e971c89213..2fedf41f6d 100644 --- a/projects/aca-playwright-shared/src/page-objects/pages/recent-files.page.ts +++ b/projects/aca-playwright-shared/src/page-objects/pages/recent-files.page.ts @@ -22,10 +22,9 @@ * from Hyland Software. If not, see . */ - import { Page } from '@playwright/test'; import { BasePage } from './base.page'; -import { DataTableComponent, MatMenuComponent, ViewerComponent } from '../components'; +import { DataTableComponent, MatMenuComponent, ViewerComponent, SidenavComponent } from '../components'; import { AcaHeader } from '../components/aca-header.component'; import { AdfFolderDialogComponent } from '../components/dialogs'; @@ -41,4 +40,5 @@ export class RecentFilesPage extends BasePage { public folderDialog = new AdfFolderDialogComponent(this.page); public dataTable = new DataTableComponent(this.page); public viewer = new ViewerComponent(this.page); + public sidenav = new SidenavComponent(this.page); } diff --git a/projects/aca-playwright-shared/src/page-objects/pages/search.page.ts b/projects/aca-playwright-shared/src/page-objects/pages/search.page.ts index b195a5b77c..1e72c1fcf6 100644 --- a/projects/aca-playwright-shared/src/page-objects/pages/search.page.ts +++ b/projects/aca-playwright-shared/src/page-objects/pages/search.page.ts @@ -24,7 +24,7 @@ import { Page } from '@playwright/test'; import { BasePage } from './base.page'; -import { DataTableComponent, MatMenuComponent, ViewerComponent, SearchInputComponent, SearchOverlayComponent } from '../components'; +import { DataTableComponent, MatMenuComponent, ViewerComponent, SearchInputComponent, SearchOverlayComponent, SidenavComponent } from '../components'; import { AcaHeader } from '../components/aca-header.component'; import { AdfFolderDialogComponent } from '../components/dialogs'; @@ -42,4 +42,5 @@ export class SearchPage extends BasePage { public viewer = new ViewerComponent(this.page); public searchInput = new SearchInputComponent(this.page); public searchOverlay = new SearchOverlayComponent(this.page); + public sidenav = new SidenavComponent(this.page); } diff --git a/projects/aca-playwright-shared/src/page-objects/pages/shared.page.ts b/projects/aca-playwright-shared/src/page-objects/pages/shared.page.ts index 891f3336bf..88372b5e9b 100644 --- a/projects/aca-playwright-shared/src/page-objects/pages/shared.page.ts +++ b/projects/aca-playwright-shared/src/page-objects/pages/shared.page.ts @@ -24,7 +24,7 @@ import { Page } from '@playwright/test'; import { BasePage } from './base.page'; -import { DataTableComponent, MatMenuComponent, ViewerComponent } from '../components'; +import { DataTableComponent, MatMenuComponent, ViewerComponent, SidenavComponent } from '../components'; import { AcaHeader } from '../components/aca-header.component'; import { AdfFolderDialogComponent, ViewerOverlayDialogComponent } from '../components/dialogs'; @@ -41,4 +41,5 @@ export class SharedPage extends BasePage { public dataTable = new DataTableComponent(this.page); public viewer = new ViewerComponent(this.page); public viewerDialog = new ViewerOverlayDialogComponent(this.page); + public sidenav = new SidenavComponent(this.page); } diff --git a/projects/aca-playwright-shared/src/page-objects/pages/trash.page.ts b/projects/aca-playwright-shared/src/page-objects/pages/trash.page.ts index 5927404c46..b6a512a8de 100644 --- a/projects/aca-playwright-shared/src/page-objects/pages/trash.page.ts +++ b/projects/aca-playwright-shared/src/page-objects/pages/trash.page.ts @@ -24,7 +24,7 @@ import { Page } from '@playwright/test'; import { BasePage } from './base.page'; -import { DataTableComponent, MatMenuComponent, ViewerComponent } from '../components'; +import { DataTableComponent, MatMenuComponent, ViewerComponent, SidenavComponent } from '../components'; import { AcaHeader } from '../components/aca-header.component'; import { AdfFolderDialogComponent, ViewerOverlayDialogComponent } from '../components/dialogs'; @@ -41,4 +41,5 @@ export class TrashPage extends BasePage { public dataTable = new DataTableComponent(this.page); public viewer = new ViewerComponent(this.page); public viewerDialog = new ViewerOverlayDialogComponent(this.page); + public sidenav = new SidenavComponent(this.page); } diff --git a/projects/aca-playwright-shared/src/utils/config.ts b/projects/aca-playwright-shared/src/utils/config.ts new file mode 100644 index 0000000000..f85982bae7 --- /dev/null +++ b/projects/aca-playwright-shared/src/utils/config.ts @@ -0,0 +1,45 @@ +/*! + * 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 + * along with Alfresco. If not, see . + */ + +export const APP_ROUTES = { + FAVORITES: '/favorites', + MY_LIBRARIES: '/libraries', + FAVORITE_LIBRARIES: '/favorite/libraries', + LOGIN: '/login', + LOGOUT: '/logout', + PERSONAL_FILES: '/personal-files', + RECENT_FILES: '/recent-files', + SHARED_FILES: '/shared', + TRASHCAN: '/trashcan' +}; + +export const SIDEBAR_LABELS = { + PERSONAL_FILES: 'Personal Files', + MY_LIBRARIES: 'My Libraries', + FAVORITE_LIBRARIES: 'Favorite Libraries', + SHARED_FILES: 'Shared', + RECENT_FILES: 'Recent Files', + FAVORITES: 'Favorites', + TRASH: 'Trash' +}; diff --git a/projects/aca-playwright-shared/src/utils/index.ts b/projects/aca-playwright-shared/src/utils/index.ts index d6ac3086e5..0178ddfe69 100644 --- a/projects/aca-playwright-shared/src/utils/index.ts +++ b/projects/aca-playwright-shared/src/utils/index.ts @@ -29,3 +29,4 @@ export * from './state-helper'; export * from './folder-errors'; export * from './utils'; export * from './library-errors'; +export * from './config'; From c10fa499b29d637817650b85a86dcd764311b649 Mon Sep 17 00:00:00 2001 From: "akash.rathod@hyland.com" Date: Wed, 20 Sep 2023 12:24:33 +0200 Subject: [PATCH 02/10] remove protractor test and fix flaky test --- .../navigation/src/tests/sidebar.spec.ts | 1 + .../navigation/src/tests/single-click.spec.ts | 9 - .../suites/navigation/sidebar.test.ts | 193 ------------------ .../suites/navigation/single-click.test.ts | 90 -------- .../components/sidenav.component.ts | 2 +- 5 files changed, 2 insertions(+), 293 deletions(-) delete mode 100755 e2e/protractor/suites/navigation/sidebar.test.ts diff --git a/e2e/playwright/navigation/src/tests/sidebar.spec.ts b/e2e/playwright/navigation/src/tests/sidebar.spec.ts index 846d938975..d43d31c098 100644 --- a/e2e/playwright/navigation/src/tests/sidebar.spec.ts +++ b/e2e/playwright/navigation/src/tests/sidebar.spec.ts @@ -120,6 +120,7 @@ test.describe('Sidebar', () => { test('[C277224] sidenav returns to the default state when navigating away from the Search Results page', async ({ personalFiles, searchPage }) => { await personalFiles.navigate({ remoteUrl: `#/search;q=test` }); await searchPage.searchInput.getIconByName('close').click(); + await searchPage.sidenav.expandedSidenav.waitFor({ state: 'attached' }); expect(await personalFiles.sidenav.isSidenavExpanded(), 'Sidebar not expanded').toBe(true); }); }); diff --git a/e2e/playwright/navigation/src/tests/single-click.spec.ts b/e2e/playwright/navigation/src/tests/single-click.spec.ts index 5b3505bc49..6d48acc27c 100644 --- a/e2e/playwright/navigation/src/tests/single-click.spec.ts +++ b/e2e/playwright/navigation/src/tests/single-click.spec.ts @@ -76,15 +76,6 @@ test.describe('Single click on item name', () => { await personalFiles.dataTable.spinnerWaitForReload(); expect(await personalFiles.breadcrumb.currentItem.innerText()).toBe(folder1); }); - - test('[C306990] Navigate inside the folder when clicking the hyperlink on Search Results', async ({ personalFiles }) => { - await personalFiles.navigate({ remoteUrl: `#/search;q=${folderSearch}` }); - await personalFiles.reload(); - await personalFiles.dataTable.spinnerWaitForReload(); - await personalFiles.dataTable.getSearchResultLinkByName(folderSearch).click(); - await personalFiles.dataTable.spinnerWaitForReload(); - expect(await personalFiles.breadcrumb.currentItem.innerText()).toBe(folderSearch); - }); }); test('[C284902] Navigate inside the library when clicking the hyperlink on File Libraries', async ({ myLibrariesPage }) => { diff --git a/e2e/protractor/suites/navigation/sidebar.test.ts b/e2e/protractor/suites/navigation/sidebar.test.ts deleted file mode 100755 index 04b3475f87..0000000000 --- a/e2e/protractor/suites/navigation/sidebar.test.ts +++ /dev/null @@ -1,193 +0,0 @@ -/*! - * 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 . - */ - -import { browser } from 'protractor'; -import { APP_ROUTES, SIDEBAR_LABELS, LoginPage, BrowsingPage, SearchResultsPage, Utils } from '@alfresco/aca-testing-shared'; - -describe('Sidebar', () => { - const loginPage = new LoginPage(); - const page = new BrowsingPage(); - const { sidenav, toolbar } = page; - const searchResultsPage = new SearchResultsPage(); - const { searchInput } = searchResultsPage.pageLayoutHeader; - - beforeAll(async () => { - await loginPage.loginWithAdmin(); - }); - - beforeEach(async () => { - await Utils.pressEscape(); - await sidenav.expandSideNav(); - }); - - afterEach(async () => { - await Utils.pressEscape(); - await page.clickPersonalFiles(); - }); - - it('[C217149] has "Personal Files" as default', async () => { - expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES); - expect(await sidenav.isActive(SIDEBAR_LABELS.PERSONAL_FILES)).toBe(true, 'Default active link'); - }); - - it('[C289902] navigate to Favorite Libraries', async () => { - await page.goToFavoriteLibraries(); - expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.FAVORITE_LIBRARIES); - expect(await sidenav.isActive(SIDEBAR_LABELS.FAVORITE_LIBRARIES)).toBe(true, 'Favorite Libraries link not active'); - }); - - it('[C289901] navigate to My Libraries', async () => { - await page.goToMyLibraries(); - expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.MY_LIBRARIES); - expect(await sidenav.isActive(SIDEBAR_LABELS.MY_LIBRARIES)).toBe(true, 'My Libraries link not active'); - }); - - it('[C213110] navigates to "Shared Files"', async () => { - await page.clickSharedFiles(); - expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.SHARED_FILES); - expect(await sidenav.isActive(SIDEBAR_LABELS.SHARED_FILES)).toBe(true, 'Shared Files link not active'); - }); - - it('[C213166] navigates to "Recent Files"', async () => { - await page.clickRecentFiles(); - expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.RECENT_FILES); - expect(await sidenav.isActive(SIDEBAR_LABELS.RECENT_FILES)).toBe(true, 'Recent Files link not active'); - }); - - it('[C213225] navigates to "Favorites"', async () => { - await page.clickFavorites(); - expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.FAVORITES); - expect(await sidenav.isActive(SIDEBAR_LABELS.FAVORITES)).toBe(true, 'Favorites link not active'); - }); - - it('[C213216] navigates to "Trash"', async () => { - await page.clickTrash(); - expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.TRASHCAN); - expect(await sidenav.isActive(SIDEBAR_LABELS.TRASH)).toBe(true, 'Trash link not active'); - }); - - it('[C280409] navigates to "Personal Files"', async () => { - await page.clickPersonalFiles(); - expect(await browser.getCurrentUrl()).toContain(APP_ROUTES.PERSONAL_FILES); - expect(await sidenav.isActive(SIDEBAR_LABELS.PERSONAL_FILES)).toBe(true, 'Personal Files link not active'); - }); - - it('[C217151] Personal Files tooltip', async () => { - await page.clickPersonalFiles(); - expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.PERSONAL_FILES)).toContain('View your Personal Files'); - }); - - it('[C213111] Shared Files tooltip', async () => { - await page.clickSharedFiles(); - expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.SHARED_FILES)).toContain('View files that have been shared'); - }); - - it('[C213167] Recent Files tooltip', async () => { - await page.clickRecentFiles(); - expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.RECENT_FILES)).toContain('View files you recently edited'); - }); - - it('[C217153] Favorites tooltip', async () => { - await page.clickFavorites(); - expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.FAVORITES)).toContain('View your favorite files and folders'); - }); - - it('[C217154] Trash tooltip', async () => { - await page.clickTrash(); - expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.TRASH)).toContain('View deleted files in the trash'); - }); - - it('[C289916] My Libraries tooltip', async () => { - await page.goToMyLibraries(); - expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.MY_LIBRARIES)).toContain('Access my libraries'); - }); - - it('[C289917] Favorite Libraries tooltip', async () => { - await page.goToFavoriteLibraries(); - expect(await sidenav.getLinkTooltip(SIDEBAR_LABELS.FAVORITE_LIBRARIES)).toContain('Access my favorite libraries'); - }); - - it('[C269095] default state is expanded', async () => { - expect(await sidenav.isSidenavExpanded()).toBe(true, 'Sidebar not expanded'); - }); - - it('[C269096] sidebar toggle', async () => { - await sidenav.collapseSideNav(); - expect(await sidenav.isSidenavExpanded()).toBe(false, 'Sidebar not collapsed'); - - await sidenav.expandSideNav(); - expect(await sidenav.isSidenavExpanded()).toBe(true, 'Sidebar not expanded'); - }); - - it('[C269100] sidebar state is preserved on page refresh', async () => { - expect(await sidenav.isSidenavExpanded()).toBe(true, 'Sidebar not expanded'); - await page.refresh(); - expect(await sidenav.isSidenavExpanded()).toBe(true, 'Sidebar not expanded'); - - await sidenav.collapseSideNav(); - expect(await sidenav.isSidenavExpanded()).toBe(false, 'Sidebar not collapsed'); - await page.refresh(); - expect(await sidenav.isSidenavExpanded()).toBe(false, 'Sidebar not collapsed'); - }); - - it('[C269102] sidebar state is preserved after logout / login', async () => { - await sidenav.collapseSideNav(); - await page.signOut(); - await loginPage.loginWithAdmin(); - - expect(await sidenav.isSidenavExpanded()).toBe(false, 'Sidebar not collapsed'); - }); - - it('[C277223] sidebar is collapsed automatically when Search Results opens', async () => { - await toolbar.clickSearchIconButton(); - await searchInput.clickSearchButton(); - /* cspell:disable-next-line */ - await searchInput.searchFor('qwertyuiop'); - await searchResultsPage.waitForResults(); - - expect(await sidenav.isSidenavExpanded()).toBe(false, 'Sidebar not collapsed'); - }); - - it('[C277224] sidenav returns to the default state when navigating away from the Search Results page', async () => { - await toolbar.clickSearchIconButton(); - await searchInput.clickSearchButton(); - /* cspell:disable-next-line */ - await searchInput.searchFor('qwertyuiop'); - await searchResultsPage.waitForResults(); - await page.clickFavorites(); - - expect(await sidenav.isSidenavExpanded()).toBe(true, 'Sidebar not expanded'); - }); - - it('[C277230] sidenav can be expanded when search results page is displayed', async () => { - await toolbar.clickSearchIconButton(); - await searchInput.clickSearchButton(); - /* cspell:disable-next-line */ - await searchInput.searchFor('qwertyuiop'); - await searchResultsPage.waitForResults(); - await sidenav.expandSideNav(); - - expect(await sidenav.isSidenavExpanded()).toBe(true, 'Sidebar not expanded'); - }); -}); diff --git a/e2e/protractor/suites/navigation/single-click.test.ts b/e2e/protractor/suites/navigation/single-click.test.ts index b048c48a37..a91b82fbd8 100755 --- a/e2e/protractor/suites/navigation/single-click.test.ts +++ b/e2e/protractor/suites/navigation/single-click.test.ts @@ -83,96 +83,6 @@ describe('Single click on item name', () => { await userActions.emptyTrashcan(); }); - it('[C284899] Hyperlink does not appear for items in the Trash', async () => { - await page.clickTrashAndWait(); - - expect(await dataTable.hasLinkOnName(deletedFile1)).toBe(false, 'Link on name is present'); - expect(await dataTable.hasLinkOnName(deletedFolder1)).toBe(false, 'Link on name is present'); - }); - - describe('on Personal Files', () => { - beforeEach(async () => { - await page.clickPersonalFilesAndWait(); - }); - - it('[C280032] Hyperlink appears when mouse over a file/folder', async () => { - expect(await dataTable.hasLinkOnName(file1)).toBe(true, 'Link on name is missing'); - }); - - it('[C280033] File preview opens when clicking the hyperlink', async () => { - await dataTable.clickNameLink(file1); - - expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); - - await Utils.pressEscape(); - }); - - it('[C280034] Navigate inside the folder when clicking the hyperlink', async () => { - await dataTable.clickNameLink(folder1); - - expect(await breadcrumb.currentItem.getText()).toBe(folder1); - }); - }); - - describe('on File Libraries', () => { - beforeEach(async () => { - await page.goToMyLibrariesAndWait(); - }); - - it('[C284901] Hyperlink appears when mouse over a library', async () => { - expect(await dataTable.hasLinkOnName(siteName)).toBe(true, 'Link on site name is missing'); - }); - - it('[C284902] Navigate inside the library when clicking the hyperlink', async () => { - await dataTable.clickNameLink(siteName); - - expect(await breadcrumb.currentItem.getText()).toBe(siteName); - expect(await dataTable.isItemPresent(fileSite)).toBe(true, `${fileSite} not displayed`); - }); - }); - - describe('on Shared Files', () => { - beforeAll(async () => { - await userActions.login(username, username); - await userActions.shareNodes([file1Id]); - await apis.user.shared.waitForFilesToBeShared([file1Id]); - }); - - beforeEach(async () => { - await page.clickSharedFilesAndWait(); - }); - - it('[C284905] Hyperlink appears when mouse over a file', async () => { - expect(await dataTable.hasLinkOnName(file1)).toBe(true, 'Link on name is missing'); - }); - - it('[C284906] File preview opens when clicking the hyperlink', async () => { - await dataTable.clickNameLink(file1); - - expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); - - await Utils.pressEscape(); - }); - }); - - describe('on Recent Files', () => { - beforeEach(async () => { - await page.clickRecentFilesAndWait(); - }); - - it('[C284907] Hyperlink appears when mouse over a file', async () => { - expect(await dataTable.hasLinkOnName(file1)).toBe(true, 'Link on name is missing'); - }); - - it('[C284908] File preview opens when clicking the hyperlink', async () => { - await dataTable.clickNameLink(file1); - - expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); - - await Utils.pressEscape(); - }); - }); - describe('on Favorites', () => { beforeAll(async () => { const initialFavoriteTotalItems = await apis.user.favorites.getFavoritesTotalItems(); diff --git a/projects/aca-playwright-shared/src/page-objects/components/sidenav.component.ts b/projects/aca-playwright-shared/src/page-objects/components/sidenav.component.ts index 94cd7374d1..523779283e 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/sidenav.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/sidenav.component.ts @@ -38,7 +38,7 @@ export class SidenavComponent extends BaseComponent { private trash = this.getChild(`[data-automation-id='app.navbar.trashcan']`); private sidenavToggle = this.getChild(`.sidenav-header-title-logo`); private sidenavExpand = this.page.getByTitle('Expand navigation menu'); - private expandedSidenav = this.page.locator(`[data-automation-id='expanded']`); + public expandedSidenav = this.page.locator(`[data-automation-id='expanded']`); constructor(page: Page) { super(page, SidenavComponent.rootElement); From f867ed2a51d41502d3a0babffcc699c56fcef7d5 Mon Sep 17 00:00:00 2001 From: "akash.rathod@hyland.com" Date: Thu, 21 Sep 2023 14:51:26 +0200 Subject: [PATCH 03/10] test fix --- .../navigation/src/tests/sidebar.spec.ts | 50 +------------------ .../search/search-input.component.ts | 2 +- .../components/sidenav.component.ts | 17 +++---- 3 files changed, 10 insertions(+), 59 deletions(-) diff --git a/e2e/playwright/navigation/src/tests/sidebar.spec.ts b/e2e/playwright/navigation/src/tests/sidebar.spec.ts index d43d31c098..e64a8835e5 100644 --- a/e2e/playwright/navigation/src/tests/sidebar.spec.ts +++ b/e2e/playwright/navigation/src/tests/sidebar.spec.ts @@ -33,62 +33,14 @@ test.describe('Sidebar', () => { await apiClientFactory.setUpAcaBackend('hruser'); }); - test('[C289902] navigate to Favorite Libraries', async ({ personalFiles, favoritePage }) => { - await personalFiles.navigate(); - await personalFiles.sidenav.openPanel('Favorite Libraries'); - await personalFiles.dataTable.spinnerWaitForReload(); - expect(favoritePage.page.url()).toContain(APP_ROUTES.FAVORITE_LIBRARIES); - expect(await favoritePage.sidenav.isActive(SIDEBAR_LABELS.FAVORITE_LIBRARIES), 'Favorite Libraries link not active').toBe(true); - }); - test('[C289901] navigate to My Libraries', async ({ personalFiles, myLibrariesPage }) => { await personalFiles.navigate(); - await personalFiles.sidenav.openPanel('My Libraries'); + await personalFiles.sidenav.openPanel(SIDEBAR_LABELS.MY_LIBRARIES); await personalFiles.dataTable.spinnerWaitForReload(); expect(myLibrariesPage.page.url()).toContain(APP_ROUTES.MY_LIBRARIES); expect(await myLibrariesPage.sidenav.isActive(SIDEBAR_LABELS.MY_LIBRARIES), 'My Libraries link not active').toBe(true); }); - test('[C213110] navigates to "Shared Files"', async ({ personalFiles, sharedPage }) => { - await personalFiles.navigate(); - await personalFiles.sidenav.openPanel('Shared'); - await personalFiles.dataTable.spinnerWaitForReload(); - expect(sharedPage.page.url()).toContain(APP_ROUTES.SHARED_FILES); - expect(await sharedPage.sidenav.isActive(SIDEBAR_LABELS.SHARED_FILES), 'Shared Files link not active').toBe(true); - }); - - test('[C213166] navigates to "Recent Files"', async ({ personalFiles, recentFilesPage }) => { - await personalFiles.navigate(); - await personalFiles.sidenav.openPanel('Recent Files'); - await personalFiles.dataTable.spinnerWaitForReload(); - expect(recentFilesPage.page.url()).toContain(APP_ROUTES.RECENT_FILES); - expect(await recentFilesPage.sidenav.isActive(SIDEBAR_LABELS.RECENT_FILES), 'Recent Files link not active').toBe(true); - }); - - test('[C213225] navigates to "Favorites"', async ({ personalFiles, favoritePage }) => { - await personalFiles.navigate(); - await personalFiles.sidenav.openPanel('Favorites'); - await personalFiles.dataTable.spinnerWaitForReload(); - expect(favoritePage.page.url()).toContain(APP_ROUTES.FAVORITES); - expect(await favoritePage.sidenav.isActive(SIDEBAR_LABELS.FAVORITES), 'Favorites link not active').toBe(true); - }); - - test('[C213216] navigates to "Trash"', async ({ personalFiles, trashPage }) => { - await personalFiles.navigate(); - await personalFiles.sidenav.openPanel('Trash'); - await personalFiles.dataTable.spinnerWaitForReload(); - expect(trashPage.page.url()).toContain(APP_ROUTES.TRASHCAN); - expect(await trashPage.sidenav.isActive(SIDEBAR_LABELS.TRASH), 'Trash link not active').toBe(true); - }); - - test('[C280409] navigates to "Personal Files"', async ({ personalFiles, trashPage }) => { - await trashPage.navigate(); - await personalFiles.sidenav.openPanel('Personal Files'); - await personalFiles.dataTable.spinnerWaitForReload(); - expect(personalFiles.page.url()).toContain(APP_ROUTES.PERSONAL_FILES); - expect(await personalFiles.sidenav.isActive(SIDEBAR_LABELS.PERSONAL_FILES), 'Personal Files link not active').toBe(true); - }); - test('[C277230] sidenav can be expanded when search results page is displayed', async ({ personalFiles }) => { await personalFiles.navigate({ remoteUrl: `#/search;q=test` }); expect(await personalFiles.sidenav.isSidenavExpanded(), 'Sidebar expanded').toBe(false); diff --git a/projects/aca-playwright-shared/src/page-objects/components/search/search-input.component.ts b/projects/aca-playwright-shared/src/page-objects/components/search/search-input.component.ts index 78ca1fa60d..c575f423a5 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/search/search-input.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/search/search-input.component.ts @@ -29,7 +29,7 @@ import { timeouts } from '../../../utils'; export class SearchInputComponent extends BaseComponent { private static rootElement = 'aca-page-layout'; public searchButton = this.getChild('aca-search-input .app-search-button'); - getIconByName = (name: string): Locator => this.getChild('.mat-icon[role="img"]', { hasText: name }); + getIconByName = (name: string): Locator => this.getChild('.mat-icon', { hasText: name }); /** * Method used in cases where user have possibility to navigate "inside" the element (it's clickable and has link attribute). diff --git a/projects/aca-playwright-shared/src/page-objects/components/sidenav.component.ts b/projects/aca-playwright-shared/src/page-objects/components/sidenav.component.ts index 523779283e..1886414aec 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/sidenav.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/sidenav.component.ts @@ -22,6 +22,7 @@ * from Hyland Software. If not, see . */ +import { SIDEBAR_LABELS } from '../../utils'; import { BaseComponent } from './base.component'; import { Locator, Page } from '@playwright/test'; @@ -55,21 +56,19 @@ export class SidenavComponent extends BaseComponent { private getLinkLabel(name: string): Locator { switch (name) { - case 'Personal Files': + case SIDEBAR_LABELS.PERSONAL_FILES: return this.personalFiles; - case 'File Libraries': - return this.fileLibraries; - case 'My Libraries': + case SIDEBAR_LABELS.MY_LIBRARIES: return this.myLibraries; - case 'Favorite Libraries': + case SIDEBAR_LABELS.FAVORITE_LIBRARIES: return this.favoriteLibraries; - case 'Shared': + case SIDEBAR_LABELS.SHARED_FILES: return this.shared; - case 'Recent Files': + case SIDEBAR_LABELS.RECENT_FILES: return this.recentFiles; - case 'Favorites': + case SIDEBAR_LABELS.FAVORITES: return this.favorites; - case 'Trash': + case SIDEBAR_LABELS.TRASH: return this.trash; default: return this.personalFiles; From 49816b8276defb4b8d32ff07046b34cf99e9344c Mon Sep 17 00:00:00 2001 From: "akash.rathod@hyland.com" Date: Fri, 22 Sep 2023 17:52:09 +0200 Subject: [PATCH 04/10] [ACS-5639] fix exclude test in viewer --- e2e/playwright/viewer/exclude.tests.json | 10 +- .../viewer/src/tests/viewer-action.spec.ts | 25 ++-- .../viewer/src/tests/viewer.spec.ts | 31 +--- e2e/protractor/protractor.excludes.json | 8 +- .../suites/viewer/viewer-actions.test.ts | 135 ------------------ .../src/api/favorites-api.ts | 36 +++++ .../src/api/file-actions.ts | 98 ++++++++++--- .../src/api/shared-links-api.ts | 21 +-- .../components/aca-header.component.ts | 2 +- .../components/viewer.component.ts | 1 + .../aca-playwright-shared/src/utils/utils.ts | 10 +- 11 files changed, 152 insertions(+), 225 deletions(-) diff --git a/e2e/playwright/viewer/exclude.tests.json b/e2e/playwright/viewer/exclude.tests.json index 9c00daf8b7..0967ef424b 100644 --- a/e2e/playwright/viewer/exclude.tests.json +++ b/e2e/playwright/viewer/exclude.tests.json @@ -1,9 +1 @@ -{ - "C284636" : "this have Protractor test Enabled https://alfresco.atlassian.net/browse/ACS-5639", - "C284635" : "this have Protractor test Enabled https://alfresco.atlassian.net/browse/ACS-5639", - "C279175" : "this have Protractor test Enabled https://alfresco.atlassian.net/browse/ACS-5639", - "C284634" : "this have Protractor test Enabled https://alfresco.atlassian.net/browse/ACS-5639", - "C297585" : "this have Protractor test Enabled https://alfresco.atlassian.net/browse/ACS-5639", - "C286379" : "this have Protractor test Enabled https://alfresco.atlassian.net/browse/ACS-5639", - "C286395" : "this have Protractor test Enabled https://alfresco.atlassian.net/browse/ACS-5639" -} +{} diff --git a/e2e/playwright/viewer/src/tests/viewer-action.spec.ts b/e2e/playwright/viewer/src/tests/viewer-action.spec.ts index 2b7dad58b4..5a778a24ab 100644 --- a/e2e/playwright/viewer/src/tests/viewer-action.spec.ts +++ b/e2e/playwright/viewer/src/tests/viewer-action.spec.ts @@ -37,19 +37,26 @@ test.describe('viewer action file', () => { const fileForCancelEditing = `playwright-file2-${Utils.random()}.docx`; let folderId: string; let fileDocxShareId: string; + let randomDocxNameFavoriteId: string; + let fileForCancelEditingId: string; - test.beforeAll(async ({ fileAction, shareAction }) => { + test.beforeAll(async ({ fileAction, favoritesPageAction, shareAction }) => { await apiClientFactory.setUpAcaBackend('hruser'); const node = await apiClientFactory.nodes.createNode('-my-', { name: randomFolderName, nodeType: 'cm:folder', relativePath: '/' }); folderId = node.entry.id; - await fileAction.uploadFile(TEST_FILES.DOCX.path, fileForCancelEditing, folderId); + + fileDocxShareId = (await fileAction.uploadFile(TEST_FILES.DOCX.path, randomDocxNameShare, folderId)).entry.id; + await shareAction.shareFileById(fileDocxShareId); + fileForCancelEditingId = (await fileAction.uploadFile(TEST_FILES.DOCX.path, fileForCancelEditing, folderId)).entry.id; + await fileAction.lockNodes([fileForCancelEditingId]); await fileAction.uploadFile(TEST_FILES.DOCX.path, randomDocxName, folderId); await fileAction.uploadFile(TEST_FILES.DOCX.path, randomDocxDelete, folderId); - const fileDocShare = await fileAction.uploadFile(TEST_FILES.DOCX.path, randomDocxNameShare, folderId); - fileDocxShareId = fileDocShare.entry.id; - await fileAction.uploadFile(TEST_FILES.DOCX.path, randomDocxNameFavorite, folderId); - await shareAction.shareFileById(fileDocxShareId); + const fileFavoritesNode = await fileAction.uploadFile(TEST_FILES.DOCX.path, randomDocxNameFavorite, folderId); + randomDocxNameFavoriteId = fileFavoritesNode.entry.id; await fileAction.uploadFile(TEST_FILES.DOCX.path, fileForEditOffline, folderId); + await favoritesPageAction.addFavoriteById('file', randomDocxNameFavoriteId); + await favoritesPageAction.isFavoriteWithRetry('hruser', randomDocxNameFavoriteId, { expect: true }); + await fileAction.isFileLockedWriteWithRetry(fileForCancelEditingId, true); }); test.beforeEach(async ({ personalFiles }) => { @@ -140,6 +147,7 @@ test.describe('viewer action file', () => { await favoritePage.viewerDialog.favoriteMenuButton.waitFor({ state: 'detached', timeout: timeouts.normal }); await sharedPage.acaHeader.clickViewerMoreActions(); + await favoritePage.viewerDialog.removeFavoriteMenuButton.waitFor({ state: 'attached', timeout: timeouts.normal }); expect(await sharedPage.viewerDialog.removeFavoriteMenuButton.isVisible(), 'Item should be remove favorite').toBe(true); await sharedPage.page.keyboard.press('Escape'); await favoritePage.navigate({ waitUntil: 'domcontentloaded' }); @@ -150,8 +158,9 @@ test.describe('viewer action file', () => { await favoritePage.navigate({ waitUntil: 'domcontentloaded' }); await favoritePage.dataTable.performClickFolderOrFileToOpen(randomDocxNameFavorite); expect(await favoritePage.viewer.isViewerOpened(), 'Viewer should be opened').toBe(true); - - await favoritePage.acaHeader.shareButton.click(); + await favoritePage.viewer.shareButton.waitFor({ state: 'attached', timeout: timeouts.normal }); + await favoritePage.viewer.shareButton.click(); + await favoritePage.viewerDialog.shareDialogTitle.waitFor({ state: 'attached', timeout: timeouts.normal }); expect(await favoritePage.viewerDialog.shareDialogTitle.isVisible(), 'Share dialog should be open').toBe(true); await favoritePage.viewerDialog.shareDialogClose.click(); await favoritePage.viewerDialog.shareDialogClose.waitFor({ state: 'detached', timeout: timeouts.large }); diff --git a/e2e/playwright/viewer/src/tests/viewer.spec.ts b/e2e/playwright/viewer/src/tests/viewer.spec.ts index 7a2d96889f..4cf12c2d1d 100644 --- a/e2e/playwright/viewer/src/tests/viewer.spec.ts +++ b/e2e/playwright/viewer/src/tests/viewer.spec.ts @@ -31,12 +31,17 @@ test.describe('viewer file', () => { const randomFolderName = `playwright-folder-${Utils.random()}`; const randomDocxName = `${TEST_FILES.DOCX.name}-${Utils.random()}`; let folderId: string; + let fileDocxId: string; - test.beforeAll(async ({ fileAction }) => { + test.beforeAll(async ({ fileAction, shareAction, favoritesPageAction }) => { await apiClientFactory.setUpAcaBackend('hruser'); const node = await apiClientFactory.nodes.createNode('-my-', { name: randomFolderName, nodeType: 'cm:folder', relativePath: '/' }); folderId = node.entry.id; - await fileAction.uploadFile(TEST_FILES.DOCX.path, randomDocxName, folderId); + const fileDoc = await fileAction.uploadFile(TEST_FILES.DOCX.path, randomDocxName, folderId); + fileDocxId = fileDoc.entry.id; + await shareAction.shareFileById(fileDocxId); + await favoritesPageAction.addFavoriteById('file', fileDocxId); + await favoritesPageAction.isFavoriteWithRetry('hruser', fileDocxId, { expect: true }); }); test.beforeEach(async ({ personalFiles }) => { @@ -98,28 +103,6 @@ test.describe('viewer file', () => { expect(await searchPage.viewer.isCloseButtonDisplayed(), 'Close button is not displayed').toBe(true); expect(await searchPage.viewer.isFileTitleDisplayed(), 'File title is not displayed').toBe(true); }); -}); - -test.describe('viewer file', () => { - const apiClientFactory = new ApiClientFactory(); - const randomFolderName = `playwright-folder-${Utils.random()}`; - const randomDocxName = `$(TEST_FILES.DOCX.name)-${Utils.random()}`; - let folderId: string; - let fileDocxId: string; - - test.beforeAll(async ({ fileAction, shareAction, favoritesPageAction: favoritesPageAction }) => { - await apiClientFactory.setUpAcaBackend('hruser'); - const node = await apiClientFactory.nodes.createNode('-my-', { name: randomFolderName, nodeType: 'cm:folder', relativePath: '/' }); - folderId = node.entry.id; - const fileDoc = await fileAction.uploadFile(TEST_FILES.DOCX.path, randomDocxName, folderId); - fileDocxId = fileDoc.entry.id; - await shareAction.shareFileById(fileDocxId); - await favoritesPageAction.addFavoriteById('file', fileDocxId); - }); - - test.afterAll(async () => { - await apiClientFactory.nodes.deleteNode(folderId, { permanent: true }); - }); test('[C279285] Viewer opens when accessing the preview URL for a file', async ({ personalFiles }) => { const previewURL = `#/personal-files/${folderId}/(viewer:view/${fileDocxId})`; diff --git a/e2e/protractor/protractor.excludes.json b/e2e/protractor/protractor.excludes.json index 99183958b9..2deec9cf3a 100644 --- a/e2e/protractor/protractor.excludes.json +++ b/e2e/protractor/protractor.excludes.json @@ -22,11 +22,5 @@ "C268958" : "test migrated to playwright https://alfresco.atlassian.net/browse/ACS-5604", "C268959" : "test migrated to playwright https://alfresco.atlassian.net/browse/ACS-5604", "C268960" : "test migrated to playwright https://alfresco.atlassian.net/browse/ACS-5604", - "C268961" : "test migrated to playwright https://alfresco.atlassian.net/browse/ACS-5604", - - "C286314" : "test migrated to playwright https://alfresco.atlassian.net/browse/ACS-5650", - "C279282" : "test migrated to playwright https://alfresco.atlassian.net/browse/ACS-5650", - "C297584" : "test migrated to playwright https://alfresco.atlassian.net/browse/ACS-5650", - "C268133" : "test migrated to playwright https://alfresco.atlassian.net/browse/ACS-5650", - "C268129" : "test migrated to playwright https://alfresco.atlassian.net/browse/ACS-5650" + "C268961" : "test migrated to playwright https://alfresco.atlassian.net/browse/ACS-5604" } diff --git a/e2e/protractor/suites/viewer/viewer-actions.test.ts b/e2e/protractor/suites/viewer/viewer-actions.test.ts index 6fdfbafd5a..31802f0546 100755 --- a/e2e/protractor/suites/viewer/viewer-actions.test.ts +++ b/e2e/protractor/suites/viewer/viewer-actions.test.ts @@ -64,7 +64,6 @@ describe('Viewer actions', () => { const userActions = new UserActions(); const uploadFilesDialog = new UploadFilesDialog(); - const downloadButton = element(By.css(`button[id='app.viewer.download']`)); const shareButton = element(By.css(`adf-viewer [data-automation-id="share-action-button"]`)); beforeAll(async () => { @@ -85,8 +84,6 @@ describe('Viewer actions', () => { const filePersonalFiles = docxFile2; let filePersonalFilesId: string; - const fileForEditOffline = `file1-${Utils.random()}.docx`; - let fileForEditOfflineId: string; const fileForCancelEditing = `file2-${Utils.random()}.docx`; let fileForCancelEditingId: string; const fileForUploadNewVersion = `file3-${Utils.random()}.docx`; @@ -105,7 +102,6 @@ describe('Viewer actions', () => { await apis.user.upload.uploadFileWithRename(xlsxFileForMove, parentId, xlsxPersonalFiles); await apis.user.upload.uploadFileWithRename(pdfFileForDelete, parentId, pdfPersonalFiles); - fileForEditOfflineId = (await apis.user.upload.uploadFileWithRename(docxFile, parentId, fileForEditOffline)).entry.id; fileForCancelEditingId = (await apis.user.upload.uploadFileWithRename(docxFile, parentId, fileForCancelEditing)).entry.id; fileForUploadNewVersionId = (await apis.user.upload.uploadFileWithRename(docxFile, parentId, fileForUploadNewVersion)).entry.id; fileForUploadNewVersionId2 = (await apis.user.upload.uploadFileWithRename(docxFile, parentId, fileForUploadNewVersion2)).entry.id; @@ -142,37 +138,6 @@ describe('Viewer actions', () => { } }); - it('[C268129] Download action', async () => { - await dataTable.doubleClickOnRowByName(docxPersonalFiles); - await viewer.waitForViewerToOpen(); - - await downloadButton.click(); - - expect(await Utils.fileExistsOnOS(docxPersonalFiles)).toBe(true, 'File not found in download location'); - }); - - it('[C268133] Delete action', async () => { - await dataTable.doubleClickOnRowByName(pdfPersonalFiles); - await viewer.waitForViewerToOpen(); - - await toolbar.clickMoreActionsDelete(); - expect(await page.getSnackBarMessage()).toContain(`${pdfPersonalFiles} deleted`); - expect(await viewer.isViewerOpened()).toBe(false, 'Viewer is opened'); - await Utils.pressEscape(); - await page.clickTrashAndWait(); - expect(await dataTable.isItemPresent(pdfPersonalFiles)).toBe(true, 'Item is not present in Trash'); - }); - - it('[C297584] Edit Offline action', async () => { - await dataTable.doubleClickOnRowByName(fileForEditOffline); - await viewer.waitForViewerToOpen(); - await toolbar.clickMoreActionsEditOffline(); - - expect(await Utils.fileExistsOnOS(fileForEditOffline)).toBe(true, 'File not found in download location'); - expect(await apis.user.nodes.isFileLockedWrite(fileForEditOfflineId)).toBe(true, `${fileForEditOffline} is not locked`); - expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not open'); - }); - it('[C297585] Cancel Editing action', async () => { await dataTable.doubleClickOnRowByName(fileForCancelEditing); await viewer.waitForViewerToOpen(); @@ -216,106 +181,6 @@ describe('Viewer actions', () => { expect(await toolbar.menu.cancelEditingAction.isPresent()).toBe(false, `'Cancel Editing' button shouldn't be shown`); expect(await toolbar.menu.editOfflineAction.isPresent()).toBe(true, `'Edit Offline' should be shown`); }); - - it('[C279282] Full screen action', async () => { - await dataTable.doubleClickOnRowByName(docxPersonalFiles); - await viewer.waitForViewerToOpen(); - - await toolbar.fullScreenButton.click(); - expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is closed after pressing Full screen'); - }); - - it('[C286314] Pressing ESC in the viewer closes only the action dialog', async () => { - await dataTable.doubleClickOnRowByName(docxPersonalFiles); - await viewer.waitForViewerToOpen(); - - await toolbar.clickMoreActionsCopy(); - expect(await copyMoveDialog.isDialogOpen()).toBe(true, 'Dialog is not open'); - await Utils.pressEscape(); - expect(await shareDialog.isDialogOpen()).toBe(false, 'Dialog is still open'); - expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); - }); - }); - - describe('from File Libraries', () => { - const siteName = `site-${Utils.random()}`; - const destination = `destFL-${Utils.random()}`; - let destinationId: string; - - const xlsxLibraries = `xlsxFL-${Utils.random()}.xlsx`; - const pdfLibraries = `pdfFL-${Utils.random()}.pdf`; - - const fileForEditOffline = `file1-${Utils.random()}.docx`; - const fileForCancelEditing = `file2-${Utils.random()}.docx`; - let fileForCancelEditingId: string; - const fileForUploadNewVersion = `file3-${Utils.random()}.docx`; - let fileForUploadNewVersionId: string; - - beforeAll(async () => { - try { - await apis.user.sites.createSite(siteName); - const docLibId = await apis.user.sites.getDocLibId(siteName); - destinationId = await apis.user.createFolder(destination); - - await apis.user.upload.uploadFile(docxFile2, docLibId); - - await apis.user.upload.uploadFileWithRename(xlsxFileForMove, docLibId, xlsxLibraries); - await apis.user.upload.uploadFileWithRename(pdfFileForDelete, docLibId, pdfLibraries); - - await apis.user.upload.uploadFileWithRename(docxFile, docLibId, fileForEditOffline); - fileForCancelEditingId = (await apis.user.upload.uploadFileWithRename(docxFile, docLibId, fileForCancelEditing)).entry.id; - fileForUploadNewVersionId = (await apis.user.upload.uploadFileWithRename(docxFile, docLibId, fileForUploadNewVersion)).entry.id; - - await userActions.lockNodes([fileForCancelEditingId, fileForUploadNewVersionId]); - - await loginPage.loginWith(username); - } catch (error) { - Logger.error(`----- beforeAll failed : ${error}`); - } - }); - - beforeEach(async () => { - try { - await page.goToMyLibrariesAndWait(); - await dataTable.doubleClickOnRowByName(siteName); - await dataTable.waitForHeader(); - } catch (error) { - Logger.error(`----- beforeEach failed : ${error}`); - } - }); - - afterEach(async () => { - await Utils.pressEscape(); - await uploadFilesDialog.closeUploadDialog(); - }); - - afterAll(async () => { - try { - await userActions.login(username, username); - await userActions.deleteSites([siteName]); - await userActions.deleteNodes([destinationId]); - await userActions.emptyTrashcan(); - } catch (error) { - Logger.error(`----- afterAll failed : ${error}`); - } - }); - - it('[C286371] Move action', async () => { - await dataTable.doubleClickOnRowByName(xlsxLibraries); - expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); - - await toolbar.clickMoreActionsMove(); - expect(await copyMoveDialog.isDialogOpen()).toBe(true, 'Dialog is not open'); - await copyMoveDialog.selectLocation('Personal Files'); - await copyMoveDialog.selectDestination(destination); - await copyMoveDialog.moveButton.click(); - expect(await page.getSnackBarMessage()).toContain('Moved 1 item'); - await viewer.closeButton.click(); - expect(await dataTable.isItemPresent(xlsxLibraries)).toBe(false, 'Item was not moved'); - await page.clickPersonalFilesAndWait(); - await dataTable.doubleClickOnRowByName(destination); - expect(await dataTable.isItemPresent(xlsxLibraries)).toBe(true, 'Item is not present in destination'); - }); }); describe('from Recent Files', () => { diff --git a/projects/aca-playwright-shared/src/api/favorites-api.ts b/projects/aca-playwright-shared/src/api/favorites-api.ts index 21cae7b456..7233617447 100755 --- a/projects/aca-playwright-shared/src/api/favorites-api.ts +++ b/projects/aca-playwright-shared/src/api/favorites-api.ts @@ -24,6 +24,8 @@ import { ApiClientFactory } from './api-client-factory'; import { FavoriteEntry } from '@alfresco/js-api'; +import { Logger } from '@alfresco/adf-testing'; +import { Utils } from '../utils'; export class FavoritesPageApi { private apiService: ApiClientFactory; @@ -47,4 +49,38 @@ export class FavoritesPageApi { }; return await this.apiService.favorites.createFavorite('-me-', data); } + + private async getFavorites(username: string) { + try { + return await this.apiService.favorites.listFavorites(username); + } catch (error) { + Logger.error(`FavoritesApi getFavorites : catch : `, error); + return null; + } + } + + async isFavorite(username: string, nodeId: string) { + try { + return JSON.stringify((await this.getFavorites(username)).list.entries).includes(nodeId); + } catch (error) { + Logger.error(`FavoritesApi isFavorite : catch : `, error); + return null; + } + } + + async isFavoriteWithRetry(username: string, nodeId: string, data: { expect: boolean }) { + let isFavorite = false; + try { + const favorite = async () => { + isFavorite = await this.isFavorite(username, nodeId); + if (isFavorite !== data.expect) { + return Promise.reject(isFavorite); + } else { + return Promise.resolve(isFavorite); + } + }; + return await Utils.retryCall(favorite); + } catch (error) {} + return isFavorite; + } } diff --git a/projects/aca-playwright-shared/src/api/file-actions.ts b/projects/aca-playwright-shared/src/api/file-actions.ts index b4614522ac..615dec6286 100644 --- a/projects/aca-playwright-shared/src/api/file-actions.ts +++ b/projects/aca-playwright-shared/src/api/file-actions.ts @@ -24,32 +24,90 @@ import * as fs from 'fs'; import { ApiClientFactory } from './api-client-factory'; +import { Utils } from '../utils'; +import { logger } from '@alfresco/adf-cli/scripts/logger'; +import { NodeEntry } from '@alfresco/js-api'; export class FileActionsApi { - private apiService: ApiClientFactory; + private apiService: ApiClientFactory; - constructor() { - this.apiService = new ApiClientFactory(); + constructor() { + this.apiService = new ApiClientFactory(); + } + + static async initialize(userName: string, password?: string): Promise { + const classObj = new FileActionsApi(); + await classObj.apiService.setUpAcaBackend(userName, password); + return classObj; + } + + async uploadFile(fileLocation: string, fileName: string, parentFolderId: string): Promise { + const file = fs.createReadStream(fileLocation); + return this.apiService.upload.uploadFile(file, '', parentFolderId, null, { + name: fileName, + nodeType: 'cm:content', + renditions: 'doclib' + }); + } + + async lockNodes(nodeIds: string[], lockType: string = 'ALLOW_OWNER_CHANGES') { + try { + for (const nodeId of nodeIds) { + await this.apiService.nodes.lockNode(nodeId, { type: lockType }); + } + } catch (error) { + logger.error(`${this.constructor.name} ${this.lockNodes.name}`, error); + } + } + + async getNodeById(id: string): Promise { + try { + return await this.apiService.nodes.getNode(id); + } catch (error) { + logger.error(`${this.constructor.name} ${this.getNodeById.name}`, error); + return null; + } + } + + async getNodeProperty(nodeId: string, property: string): Promise { + try { + const node = await this.getNodeById(nodeId); + return (node.entry.properties && node.entry.properties[property]) || ''; + } catch (error) { + logger.error(`${this.constructor.name} ${this.getNodeProperty.name}`, error); + return ''; } + } - static async initialize(userName: string, password?: string): Promise { - const classObj = new FileActionsApi(); - await classObj.apiService.setUpAcaBackend(userName, password); - return classObj; + private async getLockType(nodeId: string): Promise { + try { + const lockType = await this.getNodeProperty(nodeId, 'cm:lockType'); + return lockType || ''; + } catch (error) { + logger.error(`${this.constructor.name} ${this.getLockType.name}`, error); + return ''; } + } - async uploadFile(fileLocation: string, fileName: string, parentFolderId: string): Promise { - const file = fs.createReadStream(fileLocation); - return this.apiService.upload.uploadFile( - file, - '', - parentFolderId, - null, - { - name: fileName, - nodeType: 'cm:content', - renditions: 'doclib' - } - ); + async isFileLockedWriteWithRetry(nodeId: string, expect: boolean): Promise { + const data = { + expect: expect, + retry: 5 + }; + let isLocked = false; + try { + const locked = async () => { + isLocked = (await this.getLockType(nodeId)) === 'WRITE_LOCK'; + if (isLocked !== data.expect) { + return Promise.reject(isLocked); + } else { + return Promise.resolve(isLocked); + } + }; + return await Utils.retryCall(locked, data.retry); + } catch (error) { + logger.error(`${this.constructor.name} ${this.isFileLockedWriteWithRetry.name}`, error); } + return isLocked; + } } diff --git a/projects/aca-playwright-shared/src/api/shared-links-api.ts b/projects/aca-playwright-shared/src/api/shared-links-api.ts index 188abd3d69..95310f7444 100755 --- a/projects/aca-playwright-shared/src/api/shared-links-api.ts +++ b/projects/aca-playwright-shared/src/api/shared-links-api.ts @@ -23,8 +23,7 @@ */ import { ApiClientFactory } from './api-client-factory'; -import { FavoritePaging, SharedLinkEntry } from '@alfresco/js-api'; -import { logger } from '@alfresco/adf-cli/scripts/logger'; +import { SharedLinkEntry } from '@alfresco/js-api'; export class SharedLinksApi { private apiService: ApiClientFactory; @@ -49,22 +48,4 @@ export class SharedLinksApi { return null; } } - - private async getFavorites(userName: string): Promise { - try { - return await this.apiService.favorites.listFavorites(userName); - } catch (error) { - logger.error(`\n--- Error while fetching favourites list ${error} :`); - return null; - } - } - - async isFavorite(nodeId: string, userName: string): Promise { - try { - return JSON.stringify((await this.getFavorites(userName)).list.entries).includes(nodeId); - } catch (error) { - logger.error(`\n--- Error while checking favourite node ${error} ${error} :`); - return null; - } - } } diff --git a/projects/aca-playwright-shared/src/page-objects/components/aca-header.component.ts b/projects/aca-playwright-shared/src/page-objects/components/aca-header.component.ts index 5b8ab0ddf3..bbb1d9355c 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/aca-header.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/aca-header.component.ts @@ -32,7 +32,7 @@ export class AcaHeader extends BaseComponent { public viewButton = this.getChild('button[title="View"]'); public searchButton = this.getChild('button[title="Search"]'); public fullScreenButton = this.getChild('button[id="app.viewer.fullscreen"]'); - public shareButton = this.getChild('button[id="app.viewer.fullscreen"]'); + public shareButton = this.getChild('button[id="share-action-button"]'); public downloadButton = this.getChild('button[id="app.viewer.download"]'); constructor(page: Page) { diff --git a/projects/aca-playwright-shared/src/page-objects/components/viewer.component.ts b/projects/aca-playwright-shared/src/page-objects/components/viewer.component.ts index 16b7ad4093..601899620c 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/viewer.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/viewer.component.ts @@ -34,6 +34,7 @@ export class ViewerComponent extends BaseComponent { public closeButtonLocator = this.getChild('.adf-viewer-close-button'); public fileTitleButtonLocator = this.getChild('.adf-viewer__file-title'); public pdfViewerContentPages = this.getChild('.adf-pdf-viewer__content .page'); + public shareButton = this.getChild('button[id="share-action-button"]'); toolbar = new AcaHeader(this.page); diff --git a/projects/aca-playwright-shared/src/utils/utils.ts b/projects/aca-playwright-shared/src/utils/utils.ts index 575fb9f8d4..c23779b4c5 100644 --- a/projects/aca-playwright-shared/src/utils/utils.ts +++ b/projects/aca-playwright-shared/src/utils/utils.ts @@ -25,9 +25,17 @@ const crypto = require('crypto'); export class Utils { - static random(): string { return crypto.getRandomValues(new Uint32Array(1))[0].toString(36).substring(0, 5).toLowerCase(); } + static retryCall(fn: () => Promise, retry: number = 30, delay: number = 1500): Promise { + const pause = (duration: number) => new Promise((res) => setTimeout(res, duration)); + + const run = (retries: number): Promise => { + return fn().catch((err) => (retries > 1 ? pause(delay).then(() => run(retries - 1)) : Promise.reject(err))); + }; + + return run(retry); + } } From d51f742b1ef6615f0811ed3f9090dd9cb506c0b7 Mon Sep 17 00:00:00 2001 From: "akash.rathod@hyland.com" Date: Fri, 22 Sep 2023 20:30:37 +0200 Subject: [PATCH 05/10] remove exclude test and fix test --- .../viewer/src/tests/viewer.spec.ts | 1 + e2e/protractor/protractor.excludes.json | 7 +- .../suites/viewer/viewer-general.test.ts | 35 +----- .../viewer/viewer-protected-file.test.ts | 109 ------------------ .../src/api/file-actions.ts | 47 +++++++- 5 files changed, 44 insertions(+), 155 deletions(-) delete mode 100755 e2e/protractor/suites/viewer/viewer-protected-file.test.ts diff --git a/e2e/playwright/viewer/src/tests/viewer.spec.ts b/e2e/playwright/viewer/src/tests/viewer.spec.ts index 4cf12c2d1d..4e5f06202d 100644 --- a/e2e/playwright/viewer/src/tests/viewer.spec.ts +++ b/e2e/playwright/viewer/src/tests/viewer.spec.ts @@ -42,6 +42,7 @@ test.describe('viewer file', () => { await shareAction.shareFileById(fileDocxId); await favoritesPageAction.addFavoriteById('file', fileDocxId); await favoritesPageAction.isFavoriteWithRetry('hruser', fileDocxId, { expect: true }); + await fileAction.waitForNodes(randomDocxName, { expect: 1 }); }); test.beforeEach(async ({ personalFiles }) => { diff --git a/e2e/protractor/protractor.excludes.json b/e2e/protractor/protractor.excludes.json index 2deec9cf3a..effe33632b 100644 --- a/e2e/protractor/protractor.excludes.json +++ b/e2e/protractor/protractor.excludes.json @@ -17,10 +17,5 @@ "C279220": "will be fixed after protractor to playwright migration, see https://alfresco.atlassian.net/browse/ACS-5007", "C279221": "will be fixed after protractor to playwright migration, see https://alfresco.atlassian.net/browse/ACS-5007", "C325006": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007", - "C213097": "https://alfresco.atlassian.net/browse/ACS-5479", - - "C268958" : "test migrated to playwright https://alfresco.atlassian.net/browse/ACS-5604", - "C268959" : "test migrated to playwright https://alfresco.atlassian.net/browse/ACS-5604", - "C268960" : "test migrated to playwright https://alfresco.atlassian.net/browse/ACS-5604", - "C268961" : "test migrated to playwright https://alfresco.atlassian.net/browse/ACS-5604" + "C213097": "https://alfresco.atlassian.net/browse/ACS-5479" } diff --git a/e2e/protractor/suites/viewer/viewer-general.test.ts b/e2e/protractor/suites/viewer/viewer-general.test.ts index 0685d5fcdc..65fd6909fe 100755 --- a/e2e/protractor/suites/viewer/viewer-general.test.ts +++ b/e2e/protractor/suites/viewer/viewer-general.test.ts @@ -48,9 +48,8 @@ describe('Viewer general', () => { const loginPage = new LoginPage(); const page = new BrowsingPage(); - const { dataTable, toolbar } = page; + const { dataTable } = page; const viewer = new Viewer(); - const { searchInput } = page.pageLayoutHeader; const adminApiActions = new AdminActions(); const userActions = new UserActions(); @@ -114,24 +113,6 @@ describe('Viewer general', () => { expect(await viewer.isFileTitleDisplayed()).toBe(true, 'File title is not displayed'); }); - it('[C284636] Viewer opens for a file from Recent Files', async () => { - await page.clickRecentFilesAndWait(); - await dataTable.doubleClickOnRowByName(xlsxFile); - expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); - expect(await viewer.isViewerToolbarDisplayed()).toBe(true, 'Toolbar not displayed'); - expect(await viewer.isCloseButtonDisplayed()).toBe(true, 'Close button is not displayed'); - expect(await viewer.isFileTitleDisplayed()).toBe(true, 'File title is not displayed'); - }); - - it('[C284635] Viewer opens for a file from Shared Files', async () => { - await page.clickSharedFilesAndWait(); - await dataTable.doubleClickOnRowByName(xlsxFile); - expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); - expect(await viewer.isViewerToolbarDisplayed()).toBe(true, 'Toolbar not displayed'); - expect(await viewer.isCloseButtonDisplayed()).toBe(true, 'Close button is not displayed'); - expect(await viewer.isFileTitleDisplayed()).toBe(true, 'File title is not displayed'); - }); - it('[C284634] Viewer opens for a file from Favorites', async () => { await page.clickFavoritesAndWait(); await dataTable.doubleClickOnRowByName(xlsxFile); @@ -140,18 +121,4 @@ describe('Viewer general', () => { expect(await viewer.isCloseButtonDisplayed()).toBe(true, 'Close button is not displayed'); expect(await viewer.isFileTitleDisplayed()).toBe(true, 'File title is not displayed'); }); - - it('[C279175] Viewer opens for a file from Search Results', async () => { - await toolbar.clickSearchIconButton(); - await searchInput.clickSearchButton(); - await searchInput.checkFilesAndFolders(); - await searchInput.searchFor(xlsxFile); - await dataTable.waitForBody(); - - await dataTable.doubleClickOnRowByName(xlsxFile); - expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); - expect(await viewer.isViewerToolbarDisplayed()).toBe(true, 'Toolbar not displayed'); - expect(await viewer.isCloseButtonDisplayed()).toBe(true, 'Close button is not displayed'); - expect(await viewer.isFileTitleDisplayed()).toBe(true, 'File title is not displayed'); - }); }); diff --git a/e2e/protractor/suites/viewer/viewer-protected-file.test.ts b/e2e/protractor/suites/viewer/viewer-protected-file.test.ts deleted file mode 100755 index 095e63f738..0000000000 --- a/e2e/protractor/suites/viewer/viewer-protected-file.test.ts +++ /dev/null @@ -1,109 +0,0 @@ -/*! - * 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 . - */ - -import { AdminActions, LoginPage, BrowsingPage, FILES, RepoClient, Utils, Viewer, PasswordDialog } from '@alfresco/aca-testing-shared'; -import { BrowserActions } from '@alfresco/adf-testing'; - -describe('Viewer - password protected file', () => { - const username = `user-${Utils.random()}`; - - const parent = `parent-${Utils.random()}`; - let parentId: string; - - const protectedFile = FILES.protectedFile; - - const apis = { - user: new RepoClient(username, username) - }; - - const loginPage = new LoginPage(); - const page = new BrowsingPage(); - const { dataTable } = page; - const viewer = new Viewer(); - const passwordDialog = new PasswordDialog(); - const adminApiActions = new AdminActions(); - - beforeAll(async () => { - await adminApiActions.createUser({ username }); - parentId = (await apis.user.nodes.createFolder(parent)).entry.id; - await apis.user.upload.uploadFile(protectedFile.name, parentId); - - await loginPage.loginWith(username); - }); - - beforeEach(async () => { - await page.header.expandSideNav(); - await page.clickPersonalFilesAndWait(); - await dataTable.doubleClickOnRowByName(parent); - await dataTable.waitForHeader(); - await dataTable.doubleClickOnRowByName(protectedFile.name); - await viewer.waitForViewerToOpen(); - await passwordDialog.waitForDialogToOpen(); - }); - - afterEach(async () => { - await page.closeOpenDialogs(); - await Utils.pressEscape(); - }); - - afterAll(async () => { - await apis.user.nodes.deleteNodeById(parentId); - }); - - it('[C268958] Password dialog appears when opening a protected file', async () => { - expect(await passwordDialog.isDialogOpen()).toBe(true, 'Password dialog not open'); - expect(await passwordDialog.isPasswordInputDisplayed()).toBe(true, 'Password input not displayed'); - expect(await passwordDialog.isSubmitEnabled()).toBe(false, 'Submit button not disabled'); - expect(await passwordDialog.isCloseEnabled()).toBe(true, 'Close button not enabled'); - expect(await viewer.isPdfViewerContentDisplayed()).toBe(false, 'file content is displayed'); - }); - - it('[C268959] File content is displayed when entering the correct password', async () => { - await passwordDialog.enterPassword(protectedFile.password); - expect(await passwordDialog.isSubmitEnabled()).toBe(true, 'Submit button not enabled'); - - await BrowserActions.click(passwordDialog.submitButton); - await passwordDialog.waitForDialogToClose(); - - expect(await viewer.isPdfViewerContentDisplayed()).toBe(true, 'file content not displayed'); - }); - - it('[C268960] Error appears when entering an incorrect password', async () => { - await passwordDialog.enterPassword('incorrect'); - expect(await passwordDialog.isSubmitEnabled()).toBe(true, 'Submit button not enabled'); - await BrowserActions.click(passwordDialog.submitButton); - - expect(await passwordDialog.getErrorMessage()).toBe('Password is wrong'); - expect(await viewer.isPdfViewerContentDisplayed()).toBe(false, 'file content is displayed'); - }); - - it('[C268961] Refresh the page while Password dialog is open', async () => { - await passwordDialog.enterPassword(protectedFile.password); - await page.refresh(); - await viewer.waitForViewerToOpen(); - - expect(await viewer.isPdfViewerContentDisplayed()).toBe(false, 'file content is displayed'); - expect(await passwordDialog.isDialogOpen()).toBe(true, 'Password dialog not open'); - }); -}); diff --git a/projects/aca-playwright-shared/src/api/file-actions.ts b/projects/aca-playwright-shared/src/api/file-actions.ts index 615dec6286..865f74cb4e 100644 --- a/projects/aca-playwright-shared/src/api/file-actions.ts +++ b/projects/aca-playwright-shared/src/api/file-actions.ts @@ -25,7 +25,7 @@ import * as fs from 'fs'; import { ApiClientFactory } from './api-client-factory'; import { Utils } from '../utils'; -import { logger } from '@alfresco/adf-cli/scripts/logger'; +import { ApiUtil, Logger } from '@alfresco/adf-testing'; import { NodeEntry } from '@alfresco/js-api'; export class FileActionsApi { @@ -56,7 +56,7 @@ export class FileActionsApi { await this.apiService.nodes.lockNode(nodeId, { type: lockType }); } } catch (error) { - logger.error(`${this.constructor.name} ${this.lockNodes.name}`, error); + Logger.error(`${this.constructor.name} ${this.lockNodes.name}`, error); } } @@ -64,7 +64,7 @@ export class FileActionsApi { try { return await this.apiService.nodes.getNode(id); } catch (error) { - logger.error(`${this.constructor.name} ${this.getNodeById.name}`, error); + Logger.error(`${this.constructor.name} ${this.getNodeById.name}`, error); return null; } } @@ -74,7 +74,7 @@ export class FileActionsApi { const node = await this.getNodeById(nodeId); return (node.entry.properties && node.entry.properties[property]) || ''; } catch (error) { - logger.error(`${this.constructor.name} ${this.getNodeProperty.name}`, error); + Logger.error(`${this.constructor.name} ${this.getNodeProperty.name}`, error); return ''; } } @@ -84,7 +84,7 @@ export class FileActionsApi { const lockType = await this.getNodeProperty(nodeId, 'cm:lockType'); return lockType || ''; } catch (error) { - logger.error(`${this.constructor.name} ${this.getLockType.name}`, error); + Logger.error(`${this.constructor.name} ${this.getLockType.name}`, error); return ''; } } @@ -106,8 +106,43 @@ export class FileActionsApi { }; return await Utils.retryCall(locked, data.retry); } catch (error) { - logger.error(`${this.constructor.name} ${this.isFileLockedWriteWithRetry.name}`, error); + Logger.error(`${this.constructor.name} ${this.isFileLockedWriteWithRetry.name}`, error); } return isLocked; } + + private async queryNodesNames(searchTerm: string) { + const data = { + query: { + query: `cm:name:\"${searchTerm}*\"`, + language: 'afts' + }, + filterQueries: [{ query: `+TYPE:'cm:folder' OR +TYPE:'cm:content'` }] + }; + + try { + return this.apiService.search.search(data); + } catch (error) { + Logger.error(`SearchApi queryNodesNames : catch : `, error); + return null; + } + } + async waitForNodes(searchTerm: string, data: { expect: number }) { + const predicate = (totalItems: number) => totalItems === data.expect; + + const apiCall = async () => { + try { + return (await this.queryNodesNames(searchTerm)).list.pagination.totalItems; + } catch (error) { + return 0; + } + }; + + try { + await ApiUtil.waitForApi(apiCall, predicate, 30, 2500); + } catch (error) { + Logger.error(`SearchApi waitForNodes : catch : `); + Logger.error(`\tExpected: ${data.expect} items, but found ${error}`); + } + } } From 92c07eeadfb514c754794de7554ce824dd7a2cc1 Mon Sep 17 00:00:00 2001 From: "akash.rathod@hyland.com" Date: Thu, 28 Sep 2023 22:19:49 +0200 Subject: [PATCH 06/10] [ACS-6066] viewer special permissions playwright test --- .../.eslintrc.json | 26 + .../exclude.tests.json | 1 + .../playwright.config.ts | 44 ++ .../project.json | 24 + .../tests/special-permissions-actions.test.ts | 190 ++++++++ .../src/tests/viewer.ts | 456 ++++++++++++++++++ .../tsconfig.e2e.adf.json | 15 + .../tsconfig.e2e.json | 12 + .../suites/viewer/viewer-general.test.ts | 9 - .../src/api/favorites-api.ts | 41 ++ .../src/api/file-actions.ts | 23 +- .../aca-playwright-shared/src/api/index.ts | 1 + .../src/api/nodes-api.ts | 20 + .../src/api/search-api.ts | 88 ++++ .../src/api/shared-links-api.ts | 50 +- .../src/api/sites-api.ts | 53 +- projects/aca-playwright-shared/src/index.ts | 1 + .../dataTable/mat-menu.component.ts | 15 + .../components/viewer.component.ts | 21 + .../src/resources/test-data/index.ts | 25 + .../test-data/test-data-permissions.ts | 385 +++++++++++++++ .../src/resources/test-files/file-jpg.jpg | Bin 0 -> 22812 bytes .../src/resources/test-files/file2-docx.docx | Bin 0 -> 11523 bytes .../src/resources/test-files/file2-xlsx.xlsx | Bin 0 -> 97629 bytes .../resources/test-files/file_unsupported.3DS | 1 + .../src/resources/test-files/index.ts | 25 + .../src/resources/test-files/protected.pdf | Bin 0 -> 18052 bytes 27 files changed, 1504 insertions(+), 22 deletions(-) create mode 100644 e2e/playwright/special-permissions-actions-available/.eslintrc.json create mode 100644 e2e/playwright/special-permissions-actions-available/exclude.tests.json create mode 100644 e2e/playwright/special-permissions-actions-available/playwright.config.ts create mode 100644 e2e/playwright/special-permissions-actions-available/project.json create mode 100644 e2e/playwright/special-permissions-actions-available/src/tests/special-permissions-actions.test.ts create mode 100644 e2e/playwright/special-permissions-actions-available/src/tests/viewer.ts create mode 100644 e2e/playwright/special-permissions-actions-available/tsconfig.e2e.adf.json create mode 100755 e2e/playwright/special-permissions-actions-available/tsconfig.e2e.json create mode 100755 projects/aca-playwright-shared/src/api/search-api.ts create mode 100644 projects/aca-playwright-shared/src/resources/test-data/index.ts create mode 100644 projects/aca-playwright-shared/src/resources/test-data/test-data-permissions.ts create mode 100644 projects/aca-playwright-shared/src/resources/test-files/file-jpg.jpg create mode 100644 projects/aca-playwright-shared/src/resources/test-files/file2-docx.docx create mode 100644 projects/aca-playwright-shared/src/resources/test-files/file2-xlsx.xlsx create mode 100755 projects/aca-playwright-shared/src/resources/test-files/file_unsupported.3DS create mode 100644 projects/aca-playwright-shared/src/resources/test-files/protected.pdf diff --git a/e2e/playwright/special-permissions-actions-available/.eslintrc.json b/e2e/playwright/special-permissions-actions-available/.eslintrc.json new file mode 100644 index 0000000000..c72703a550 --- /dev/null +++ b/e2e/playwright/special-permissions-actions-available/.eslintrc.json @@ -0,0 +1,26 @@ +{ + "extends": "../../../.eslintrc.json", + "ignorePatterns": [ + "!**/*" + ], + "overrides": [ + { + "files": [ + "*.ts" + ], + "parserOptions": { + "project": [ + "e2e/playwright/special-permissions-actions-available/tsconfig.e2e.json" + ], + "createDefaultProgram": true + }, + "plugins": [ + "rxjs", + "unicorn" + ], + "rules": { + "@typescript-eslint/no-floating-promises": "off" + } + } + ] +} diff --git a/e2e/playwright/special-permissions-actions-available/exclude.tests.json b/e2e/playwright/special-permissions-actions-available/exclude.tests.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/e2e/playwright/special-permissions-actions-available/exclude.tests.json @@ -0,0 +1 @@ +{} diff --git a/e2e/playwright/special-permissions-actions-available/playwright.config.ts b/e2e/playwright/special-permissions-actions-available/playwright.config.ts new file mode 100644 index 0000000000..f906d6fc0c --- /dev/null +++ b/e2e/playwright/special-permissions-actions-available/playwright.config.ts @@ -0,0 +1,44 @@ +/*! + * 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 . + */ + +import { PlaywrightTestConfig } from '@playwright/test'; +import { CustomConfig, getGlobalConfig, getExcludedTestsRegExpArray } from '@alfresco/playwright-shared'; +import EXCLUDED_JSON from './exclude.tests.json'; + +const config: PlaywrightTestConfig = { + ...getGlobalConfig, + + grepInvert: getExcludedTestsRegExpArray(EXCLUDED_JSON, 'special-permissions'), + projects: [ + { + name: 'special-permissions', + testDir: './src/tests', + use: { + users: ['hruser', 'admin'] + } + } + ] +}; + +export default config; diff --git a/e2e/playwright/special-permissions-actions-available/project.json b/e2e/playwright/special-permissions-actions-available/project.json new file mode 100644 index 0000000000..dfd6cbb985 --- /dev/null +++ b/e2e/playwright/special-permissions-actions-available/project.json @@ -0,0 +1,24 @@ +{ + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "name": "special-permissions-e2e", + "sourceRoot": "e2e/playwright/special-permissions-actions-available/src", + "projectType": "application", + "targets": { + "e2e": { + "executor": "nx:run-commands", + "options": { + "commands": [ + "npx playwright test --config=e2e/playwright/special-permissions-actions-available/playwright.config.ts" + ] + }, + "configurations": { + "production": { + "devServerTarget": "content-ce:serve:production" + } + } + }, + "lint": { + "executor": "@angular-eslint/builder:lint" + } + } +} diff --git a/e2e/playwright/special-permissions-actions-available/src/tests/special-permissions-actions.test.ts b/e2e/playwright/special-permissions-actions-available/src/tests/special-permissions-actions.test.ts new file mode 100644 index 0000000000..b04b18e865 --- /dev/null +++ b/e2e/playwright/special-permissions-actions-available/src/tests/special-permissions-actions.test.ts @@ -0,0 +1,190 @@ +/*! + * 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 . + */ + +import * as testData from '@alfresco/playwright-shared'; +import { viewerTests } from './viewer'; +import { + ApiClientFactory, + FavoritesPageApi, + FileActionsApi, + TEST_FILES, + NodesApi, + SitesApi, + test, + SharedLinksApi, + SearchPageApi +} from '@alfresco/playwright-shared'; +import { Site } from '@alfresco/js-api'; + +test.describe('Special permissions : ', () => { + const apiClientFactory = new ApiClientFactory(); + const random = testData.random; + let docLibId: string; + + const sitePrivate = `site-private-${random}`; + + const userManager = `manager-${random}`; + const userConsumer = `consumer-${random}`; + const userCollaborator = `collaborator-${random}`; + const userDemoted = `demoted-${random}`; + + let fileDocxFavId: string; + let fileFavId: string; + let fileDocxSharedId: string; + let fileDocxSharedFavId: string; + let fileSharedId: string; + let fileSharedFavId: string; + let fileLockedId: string; + let fileFavLockedId: string; + let fileSharedLockedId: string; + let fileSharedFavLockedId: string; + let fileGranularPermissionId: string; + let fileLockedByUserId: string; + let folderFavId: string; + let folderFav2Id: string; + + let managerNodeActions: NodesApi; + let demotedUserActions: NodesApi; + let consumerFavoritesActions: FavoritesPageApi; + let managerFavoritesActions: FavoritesPageApi; + let collaboratorFavoritesActions: FavoritesPageApi; + let demotedUserFavoritesActions: FavoritesPageApi; + let managerUserShareActions: SharedLinksApi; + let demotedUserShareActions: SharedLinksApi; + let consumerShareActions: SharedLinksApi; + let managerSiteActions: SitesApi; + let managerFileActions: FileActionsApi; + let managerSearchActions: SearchPageApi; + + test.beforeAll(async () => { + test.setTimeout(120000); + await apiClientFactory.setUpAcaBackend('admin'); + await apiClientFactory.createUser({ username: userManager }); + await apiClientFactory.createUser({ username: userConsumer }); + await apiClientFactory.createUser({ username: userCollaborator }); + await apiClientFactory.createUser({ username: userDemoted }); + + managerNodeActions = await NodesApi.initialize(userManager, userManager); + demotedUserActions = await NodesApi.initialize(userDemoted, userDemoted); + consumerFavoritesActions = await FavoritesPageApi.initialize(userConsumer, userConsumer); + collaboratorFavoritesActions = await FavoritesPageApi.initialize(userCollaborator, userCollaborator); + demotedUserFavoritesActions = await FavoritesPageApi.initialize(userDemoted, userDemoted); + managerFavoritesActions = await FavoritesPageApi.initialize(userManager, userManager); + managerSearchActions = await SearchPageApi.initialize(userManager, userManager); + managerSiteActions = await SitesApi.initialize(userManager, userManager); + managerFileActions = await FileActionsApi.initialize(userManager, userManager); + managerUserShareActions = await SharedLinksApi.initialize(userManager, userManager); + demotedUserShareActions = await SharedLinksApi.initialize(userDemoted, userDemoted); + consumerShareActions = await SharedLinksApi.initialize(userConsumer, userConsumer); + + const consumerFavoritesTotalItems = await consumerFavoritesActions.getFavoritesTotalItems(userConsumer); + const managerSearchTotalItems = await managerSearchActions.getTotalItems(userManager); + const collaboratorFavoritesTotalItems = await collaboratorFavoritesActions.getFavoritesTotalItems(userCollaborator); + + await managerSiteActions.createSite(sitePrivate, Site.VisibilityEnum.PRIVATE); + docLibId = await managerSiteActions.getDocLibId(sitePrivate); + await managerSiteActions.addSiteMember(sitePrivate, userConsumer, Site.RoleEnum.SiteConsumer); + await managerSiteActions.addSiteMember(sitePrivate, userCollaborator, Site.RoleEnum.SiteCollaborator); + await managerSiteActions.addSiteMember(sitePrivate, userDemoted, Site.RoleEnum.SiteManager); + + await managerFileActions.uploadFileWithRename(TEST_FILES.DOCX.path, docLibId, testData.fileDocx.name); + fileDocxFavId = (await managerFileActions.uploadFileWithRename(TEST_FILES.DOCX.path, docLibId, testData.fileDocxFav.name)).entry.id; + await managerNodeActions.createFile(testData.file.name, docLibId); + fileFavId = (await managerNodeActions.createFile(testData.fileFav.name, docLibId)).entry.id; + fileDocxSharedId = (await managerFileActions.uploadFileWithRename(TEST_FILES.DOCX.path, docLibId, testData.fileDocxShared.name)).entry.id; + fileDocxSharedFavId = (await managerFileActions.uploadFileWithRename(TEST_FILES.DOCX.path, docLibId, testData.fileDocxSharedFav.name)).entry.id; + fileSharedId = (await managerNodeActions.createFile(testData.fileShared.name, docLibId)).entry.id; + fileSharedFavId = (await managerNodeActions.createFile(testData.fileSharedFav.name, docLibId)).entry.id; + fileLockedId = (await managerNodeActions.createFile(testData.fileLocked.name, docLibId)).entry.id; + fileFavLockedId = (await managerNodeActions.createFile(testData.fileFavLocked.name, docLibId)).entry.id; + fileSharedLockedId = (await managerNodeActions.createFile(testData.fileSharedLocked.name, docLibId)).entry.id; + fileSharedFavLockedId = (await managerNodeActions.createFile(testData.fileSharedFavLocked.name, docLibId)).entry.id; + fileGranularPermissionId = (await managerNodeActions.createFile(testData.fileGranularPermission, docLibId)).entry.id; + + fileLockedByUserId = (await managerNodeActions.createFile(testData.fileLockedByUser, docLibId)).entry.id; + await demotedUserActions.lockNodes([fileLockedByUserId]); + await demotedUserFavoritesActions.addFavoriteById('file', fileLockedByUserId); + await demotedUserShareActions.shareFileById(fileLockedByUserId); + await managerSiteActions.updateSiteMember(sitePrivate, userDemoted, Site.RoleEnum.SiteConsumer); + + await managerNodeActions.createFolder(testData.folder.name, docLibId); + folderFavId = (await managerNodeActions.createFolder(testData.folderFav.name, docLibId)).entry.id; + folderFav2Id = (await managerNodeActions.createFolder(testData.folderFav2.name, docLibId)).entry.id; + await consumerFavoritesActions.addFavoritesByIds('folder', [folderFavId, folderFav2Id]); + + await consumerFavoritesActions.addFavoritesByIds('file', [ + fileDocxFavId, + fileFavId, + fileDocxSharedFavId, + fileSharedFavId, + fileFavLockedId, + fileSharedFavLockedId, + fileGranularPermissionId + ]); + + await consumerShareActions.shareFilesByIds([ + fileDocxSharedId, + fileDocxSharedFavId, + fileSharedId, + fileSharedFavId, + fileSharedLockedId, + fileSharedFavLockedId, + fileGranularPermissionId + ]); + + await collaboratorFavoritesActions.addFavoritesByIds('file', [fileDocxSharedFavId, fileSharedFavId]); + + await managerNodeActions.lockNodes([fileLockedId, fileFavLockedId, fileSharedLockedId, fileSharedFavLockedId]); + + await managerNodeActions.setGranularPermission(fileGranularPermissionId, false, userConsumer, Site.RoleEnum.SiteManager); + + await managerFavoritesActions.addFavoriteById('file', fileLockedByUserId); + + await Promise.all([ + consumerFavoritesActions.waitForApi(userConsumer, { expect: consumerFavoritesTotalItems + 9 }), + managerUserShareActions.waitForFilesToBeShared([ + fileDocxSharedId, + fileDocxSharedFavId, + fileSharedId, + fileSharedFavId, + fileSharedLockedId, + fileSharedFavLockedId, + fileGranularPermissionId, + fileLockedByUserId + ]), + managerSearchActions.waitForApi(userManager, { expect: managerSearchTotalItems + 14 }), + collaboratorFavoritesActions.waitForApi(userCollaborator, { expect: collaboratorFavoritesTotalItems + 2 }) + ]); + }); + + test.afterAll(async () => { + await managerSiteActions.deleteSites([sitePrivate]); + }); + + test.describe('Consumer', () => { + test.describe('on Viewer', () => { + viewerTests(userConsumer, sitePrivate); + }); + }); +}); diff --git a/e2e/playwright/special-permissions-actions-available/src/tests/viewer.ts b/e2e/playwright/special-permissions-actions-available/src/tests/viewer.ts new file mode 100644 index 0000000000..67f5e05a13 --- /dev/null +++ b/e2e/playwright/special-permissions-actions-available/src/tests/viewer.ts @@ -0,0 +1,456 @@ +/*! + * 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 . + */ + +import { expect } from '@playwright/test'; +import { FavoritesPage, LoginPage, MyLibrariesPage, SearchPage, SharedPage, test } from '@alfresco/playwright-shared'; +import * as testData from '@alfresco/playwright-shared'; + +export function viewerTests(userConsumer: string, siteName: string) { + test.describe('Consumer available actions : ', () => { + test.describe('file opened from File Libraries', () => { + async function checkViewerActions( + loginPage: LoginPage, + myLibrariesPage: MyLibrariesPage, + item: string, + expectedToolbarPrimary: string[], + expectedToolbarMore: string[] + ): Promise { + await loginPage.navigate(); + await loginPage.loginUser({ username: userConsumer, password: userConsumer }); + await myLibrariesPage.navigate(); + await myLibrariesPage.dataTable.performClickFolderOrFileToOpen(siteName); + await myLibrariesPage.dataTable.performClickFolderOrFileToOpen(item); + expect(await myLibrariesPage.viewer.isViewerOpened(), 'Viewer is not opened').toBe(true); + await myLibrariesPage.viewer.verifyViewerPrimaryActions(expectedToolbarPrimary); + await myLibrariesPage.acaHeader.clickViewerMoreActions(); + await myLibrariesPage.matMenu.verifyActualMoreActions(expectedToolbarMore); + } + + test('File Office - [C326622]', async ({ loginPage, myLibrariesPage }) => { + await checkViewerActions( + loginPage, + myLibrariesPage, + testData.fileDocx.name, + testData.fileDocx.viewerToolbarPrimary, + testData.fileDocx.viewerToolbarMore + ); + }); + + test('File Office, favorite - [C326623]', async ({ loginPage, myLibrariesPage }) => { + await checkViewerActions( + loginPage, + myLibrariesPage, + testData.fileDocxFav.name, + testData.fileDocxFav.viewerToolbarPrimary, + testData.fileDocxFav.viewerToolbarMore + ); + }); + + test('File simple - [C326624]', async ({ loginPage, myLibrariesPage }) => { + await checkViewerActions(loginPage, myLibrariesPage, testData.file.name, testData.file.viewerToolbarPrimary, testData.file.viewerToolbarMore); + }); + + test('File favorite - [C326625]', async ({ loginPage, myLibrariesPage }) => { + await checkViewerActions( + loginPage, + myLibrariesPage, + testData.fileFav.name, + testData.fileFav.viewerToolbarPrimary, + testData.fileFav.viewerToolbarMore + ); + }); + + test('File Office, shared - [C326637]', async ({ loginPage, myLibrariesPage }) => { + await checkViewerActions( + loginPage, + myLibrariesPage, + testData.fileDocxShared.name, + testData.fileDocxShared.viewerToolbarPrimary, + testData.fileDocxShared.viewerToolbarMore + ); + }); + + test('File Office, shared, favorite - [C326638]', async ({ loginPage, myLibrariesPage }) => { + await checkViewerActions( + loginPage, + myLibrariesPage, + testData.fileDocxSharedFav.name, + testData.fileDocxSharedFav.viewerToolbarPrimary, + testData.fileDocxSharedFav.viewerToolbarMore + ); + }); + + test('File shared - [C326648]', async ({ loginPage, myLibrariesPage }) => { + await checkViewerActions( + loginPage, + myLibrariesPage, + testData.fileShared.name, + testData.fileShared.viewerToolbarPrimary, + testData.fileShared.viewerToolbarMore + ); + }); + + test('File shared, favorite - [C326649]', async ({ loginPage, myLibrariesPage }) => { + await checkViewerActions( + loginPage, + myLibrariesPage, + testData.fileSharedFav.name, + testData.fileSharedFav.viewerToolbarPrimary, + testData.fileSharedFav.viewerToolbarMore + ); + }); + + test('File locked - [C326630]', async ({ loginPage, myLibrariesPage }) => { + await checkViewerActions( + loginPage, + myLibrariesPage, + testData.fileLocked.name, + testData.fileLocked.viewerToolbarPrimary, + testData.fileLocked.viewerToolbarMore + ); + }); + + test('File favorite, locked - [C326633]', async ({ loginPage, myLibrariesPage }) => { + await checkViewerActions( + loginPage, + myLibrariesPage, + testData.fileFavLocked.name, + testData.fileFavLocked.viewerToolbarPrimary, + testData.fileFavLocked.viewerToolbarMore + ); + }); + + test('File shared, locked - [C326650]', async ({ loginPage, myLibrariesPage }) => { + await checkViewerActions( + loginPage, + myLibrariesPage, + testData.fileSharedLocked.name, + testData.fileSharedLocked.viewerToolbarPrimary, + testData.fileSharedLocked.viewerToolbarMore + ); + }); + + test('File shared, favorite, locked - [C326651]', async ({ loginPage, myLibrariesPage }) => { + await checkViewerActions( + loginPage, + myLibrariesPage, + testData.fileSharedFavLocked.name, + testData.fileSharedFavLocked.viewerToolbarPrimary, + testData.fileSharedFavLocked.viewerToolbarMore + ); + }); + }); + + test.describe('file opened from Favorites', () => { + async function checkViewerActions( + loginPage: LoginPage, + favoritePage: FavoritesPage, + item: string, + expectedToolbarPrimary: string[], + expectedToolbarMore: string[] + ): Promise { + await loginPage.navigate(); + await loginPage.loginUser({ username: userConsumer, password: userConsumer }); + await favoritePage.navigate(); + await favoritePage.dataTable.performClickFolderOrFileToOpen(item); + expect(await favoritePage.viewer.isViewerOpened(), 'Viewer is not opened').toBe(true); + await favoritePage.viewer.verifyViewerPrimaryActions(expectedToolbarPrimary); + await favoritePage.acaHeader.clickViewerMoreActions(); + await favoritePage.matMenu.verifyActualMoreActions(expectedToolbarMore); + } + + test('File Office, favorite - [C326652]', async ({ loginPage, favoritePage }) => { + await checkViewerActions( + loginPage, + favoritePage, + testData.fileDocxFav.name, + testData.fileDocxFav.viewerToolbarPrimary, + testData.fileDocxFav.viewerToolbarMore + ); + }); + + test('File favorite - [C326653]', async ({ loginPage, favoritePage }) => { + await checkViewerActions( + loginPage, + favoritePage, + testData.fileFav.name, + testData.fileFav.viewerToolbarPrimary, + testData.fileFav.viewerToolbarMore + ); + }); + + test('File Office, shared, favorite - [C326655]', async ({ loginPage, favoritePage }) => { + await checkViewerActions( + loginPage, + favoritePage, + testData.fileDocxSharedFav.name, + testData.fileDocxSharedFav.viewerToolbarPrimary, + testData.fileDocxSharedFav.viewerToolbarMore + ); + }); + + test('File shared, favorite - [C326656]', async ({ loginPage, favoritePage }) => { + await checkViewerActions( + loginPage, + favoritePage, + testData.fileSharedFav.name, + testData.fileSharedFav.viewerToolbarPrimary, + testData.fileSharedFav.viewerToolbarMore + ); + }); + + test('File favorite, locked - [C326654]', async ({ loginPage, favoritePage }) => { + await checkViewerActions( + loginPage, + favoritePage, + testData.fileFavLocked.name, + testData.fileFavLocked.viewerToolbarPrimary, + testData.fileFavLocked.viewerToolbarMore + ); + }); + + test('File shared, favorite, locked - [C326657]', async ({ loginPage, favoritePage }) => { + await checkViewerActions( + loginPage, + favoritePage, + testData.fileSharedFavLocked.name, + testData.fileSharedFavLocked.viewerToolbarPrimary, + testData.fileSharedFavLocked.viewerToolbarMore + ); + }); + }); + + test.describe('file opened from Shared Files', () => { + async function checkViewerActions( + loginPage: LoginPage, + sharedPage: SharedPage, + item: string, + expectedToolbarPrimary: string[], + expectedToolbarMore: string[] + ): Promise { + await loginPage.navigate(); + await loginPage.loginUser({ username: userConsumer, password: userConsumer }); + await sharedPage.navigate(); + await sharedPage.dataTable.performClickFolderOrFileToOpen(item); + expect(await sharedPage.viewer.isViewerOpened(), 'Viewer is not opened').toBe(true); + await sharedPage.viewer.verifyViewerPrimaryActions(expectedToolbarPrimary); + await sharedPage.acaHeader.clickViewerMoreActions(); + await sharedPage.matMenu.verifyActualMoreActions(expectedToolbarMore); + } + + test('File Office, shared - [C326658]', async ({ loginPage, sharedPage }) => { + await checkViewerActions( + loginPage, + sharedPage, + testData.fileDocxShared.name, + testData.fileDocxShared.viewerToolbarPrimary, + testData.fileDocxShared.viewerToolbarMore + ); + }); + + test('File Office, shared, favorite - [C326659]', async ({ loginPage, sharedPage }) => { + await checkViewerActions( + loginPage, + sharedPage, + testData.fileDocxSharedFav.name, + testData.fileDocxSharedFav.viewerToolbarPrimary, + testData.fileDocxSharedFav.viewerToolbarMore + ); + }); + + test('File shared - [C326660]', async ({ loginPage, sharedPage }) => { + await checkViewerActions( + loginPage, + sharedPage, + testData.fileShared.name, + testData.fileShared.viewerToolbarPrimary, + testData.fileShared.viewerToolbarMore + ); + }); + + test('File shared, favorite - [C326661]', async ({ loginPage, sharedPage }) => { + await checkViewerActions( + loginPage, + sharedPage, + testData.fileSharedFav.name, + testData.fileSharedFav.viewerToolbarPrimary, + testData.fileSharedFav.viewerToolbarMore + ); + }); + + test('File shared, locked - [C326662]', async ({ loginPage, sharedPage }) => { + await checkViewerActions( + loginPage, + sharedPage, + testData.fileSharedLocked.name, + testData.fileSharedLocked.viewerToolbarPrimary, + testData.fileSharedLocked.viewerToolbarMore + ); + }); + + test('File shared, favorite, locked - [C326663]', async ({ loginPage, sharedPage }) => { + await checkViewerActions( + loginPage, + sharedPage, + testData.fileSharedFavLocked.name, + testData.fileSharedFavLocked.viewerToolbarPrimary, + testData.fileSharedFavLocked.viewerToolbarMore + ); + }); + }); + + test.describe('file opened from Search Results', () => { + async function checkViewerActions( + loginPage: LoginPage, + searchPage: SearchPage, + item: string, + expectedToolbarPrimary: string[], + expectedToolbarMore: string[] + ): Promise { + await loginPage.navigate(); + await loginPage.loginUser({ username: userConsumer, password: userConsumer }); + await searchPage.navigate({ remoteUrl: `#/search;q=${item}` }); + await searchPage.searchInput.performDoubleClickFolderOrFileToOpen(item); + expect(await searchPage.viewer.isViewerOpened(), 'Viewer is not opened').toBe(true); + await searchPage.viewer.verifyViewerPrimaryActions(expectedToolbarPrimary); + await searchPage.acaHeader.clickViewerMoreActions(); + await searchPage.matMenu.verifyActualMoreActions(expectedToolbarMore); + } + + test('File Office - [C326664]', async ({ loginPage, searchPage }) => { + await checkViewerActions( + loginPage, + searchPage, + testData.fileDocx.name, + testData.fileDocx.viewerToolbarPrimary, + testData.fileDocx.viewerToolbarMore + ); + }); + + test('File Office, favorite - [C326665]', async ({ loginPage, searchPage }) => { + await checkViewerActions( + loginPage, + searchPage, + testData.fileDocxFav.name, + testData.fileDocxFav.viewerToolbarPrimary, + testData.fileDocxFav.viewerToolbarMore + ); + }); + + test('File simple - [C326666]', async ({ loginPage, searchPage }) => { + await checkViewerActions(loginPage, searchPage, testData.file.name, testData.file.viewerToolbarPrimary, testData.file.viewerToolbarMore); + }); + + test('File favorite - [C326667]', async ({ loginPage, searchPage }) => { + await checkViewerActions( + loginPage, + searchPage, + testData.fileFav.name, + testData.fileFav.viewerToolbarPrimary, + testData.fileFav.viewerToolbarMore + ); + }); + + test('File Office, shared - [C326670]', async ({ loginPage, searchPage }) => { + await checkViewerActions( + loginPage, + searchPage, + testData.fileDocxShared.name, + testData.fileDocxShared.viewerToolbarPrimary, + testData.fileDocxShared.viewerToolbarMore + ); + }); + + test('File Office, shared, favorite - [C326671]', async ({ loginPage, searchPage }) => { + await checkViewerActions( + loginPage, + searchPage, + testData.fileDocxSharedFav.name, + testData.fileDocxSharedFav.viewerToolbarPrimary, + testData.fileDocxSharedFav.viewerToolbarMore + ); + }); + + test('File shared - [C326672]', async ({ loginPage, searchPage }) => { + await checkViewerActions( + loginPage, + searchPage, + testData.fileShared.name, + testData.fileShared.viewerToolbarPrimary, + testData.fileShared.viewerToolbarMore + ); + }); + + test('File shared, favorite - [C326673]', async ({ loginPage, searchPage }) => { + await checkViewerActions( + loginPage, + searchPage, + testData.fileSharedFav.name, + testData.fileSharedFav.viewerToolbarPrimary, + testData.fileSharedFav.viewerToolbarMore + ); + }); + + test('File locked - [C326668]', async ({ loginPage, searchPage }) => { + await checkViewerActions( + loginPage, + searchPage, + testData.fileLocked.name, + testData.fileLocked.viewerToolbarPrimary, + testData.fileLocked.viewerToolbarMore + ); + }); + + test('File favorite, locked - [C326669]', async ({ loginPage, searchPage }) => { + await checkViewerActions( + loginPage, + searchPage, + testData.fileFavLocked.name, + testData.fileFavLocked.viewerToolbarPrimary, + testData.fileFavLocked.viewerToolbarMore + ); + }); + + test('File shared, locked - [C326674]', async ({ loginPage, searchPage }) => { + await checkViewerActions( + loginPage, + searchPage, + testData.fileSharedLocked.name, + testData.fileSharedLocked.viewerToolbarPrimary, + testData.fileSharedLocked.viewerToolbarMore + ); + }); + + test('File shared, favorite, locked - [C326675]', async ({ loginPage, searchPage }) => { + await checkViewerActions( + loginPage, + searchPage, + testData.fileSharedFavLocked.name, + testData.fileSharedFavLocked.viewerToolbarPrimary, + testData.fileSharedFavLocked.viewerToolbarMore + ); + }); + }); + }); +} diff --git a/e2e/playwright/special-permissions-actions-available/tsconfig.e2e.adf.json b/e2e/playwright/special-permissions-actions-available/tsconfig.e2e.adf.json new file mode 100644 index 0000000000..87cbcf775a --- /dev/null +++ b/e2e/playwright/special-permissions-actions-available/tsconfig.e2e.adf.json @@ -0,0 +1,15 @@ +{ + "extends": "../../../tsconfig.adf.json", + "compilerOptions": { + "outDir": "../../out-tsc/e2e", + "baseUrl": "./", + "module": "commonjs", + "target": "es2017", + "types": ["jasmine", "jasminewd2", "node"], + "skipLibCheck": true, + "paths": { + "@alfresco/playwright-shared": ["../../../projects/aca-playwright-shared/src/index.ts"] + } + }, + "exclude": ["node_modules"] +} diff --git a/e2e/playwright/special-permissions-actions-available/tsconfig.e2e.json b/e2e/playwright/special-permissions-actions-available/tsconfig.e2e.json new file mode 100755 index 0000000000..d1d140fba4 --- /dev/null +++ b/e2e/playwright/special-permissions-actions-available/tsconfig.e2e.json @@ -0,0 +1,12 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "../../out-tsc/e2e", + "baseUrl": "./", + "module": "commonjs", + "target": "es2017", + "types": ["jasmine", "jasminewd2", "node", "@playwright/test"], + "skipLibCheck": true, + }, + "exclude": ["node_modules"] +} diff --git a/e2e/protractor/suites/viewer/viewer-general.test.ts b/e2e/protractor/suites/viewer/viewer-general.test.ts index 65fd6909fe..37f12450d1 100755 --- a/e2e/protractor/suites/viewer/viewer-general.test.ts +++ b/e2e/protractor/suites/viewer/viewer-general.test.ts @@ -112,13 +112,4 @@ describe('Viewer general', () => { expect(await viewer.isCloseButtonDisplayed()).toBe(true, 'Close button is not displayed'); expect(await viewer.isFileTitleDisplayed()).toBe(true, 'File title is not displayed'); }); - - it('[C284634] Viewer opens for a file from Favorites', async () => { - await page.clickFavoritesAndWait(); - await dataTable.doubleClickOnRowByName(xlsxFile); - expect(await viewer.isViewerOpened()).toBe(true, 'Viewer is not opened'); - expect(await viewer.isViewerToolbarDisplayed()).toBe(true, 'Toolbar not displayed'); - expect(await viewer.isCloseButtonDisplayed()).toBe(true, 'Close button is not displayed'); - expect(await viewer.isFileTitleDisplayed()).toBe(true, 'File title is not displayed'); - }); }); diff --git a/projects/aca-playwright-shared/src/api/favorites-api.ts b/projects/aca-playwright-shared/src/api/favorites-api.ts index 7233617447..8567904996 100755 --- a/projects/aca-playwright-shared/src/api/favorites-api.ts +++ b/projects/aca-playwright-shared/src/api/favorites-api.ts @@ -50,6 +50,21 @@ export class FavoritesPageApi { return await this.apiService.favorites.createFavorite('-me-', data); } + async addFavoritesByIds(nodeType: 'file' | 'folder' | 'site', ids: string[]): Promise { + const favorites: FavoriteEntry[] = []; + try { + if (ids && ids.length > 0) { + for (const id of ids) { + const favorite = await this.addFavoriteById(nodeType, id); + favorites.push(favorite); + } + } + } catch (error) { + Logger.error(`FavoritesApi addFavoritesByIds : catch : `, error); + } + return favorites; + } + private async getFavorites(username: string) { try { return await this.apiService.favorites.listFavorites(username); @@ -83,4 +98,30 @@ export class FavoritesPageApi { } catch (error) {} return isFavorite; } + + async getFavoritesTotalItems(username: string): Promise { + try { + return (await this.apiService.favorites.listFavorites(username)).list.pagination.totalItems; + } catch (error) { + Logger.error(`FavoritesApi getFavoritesTotalItems : catch : `, error); + return -1; + } + } + + async waitForApi(username: string, data: { expect: number }) { + try { + const favoriteFiles = async () => { + const totalItems = await this.getFavoritesTotalItems(username); + if (totalItems !== data.expect) { + return Promise.reject(totalItems); + } else { + return Promise.resolve(totalItems); + } + }; + return await Utils.retryCall(favoriteFiles); + } catch (error) { + Logger.error(`FavoritesApi waitForApi : catch : `); + Logger.error(`\tExpected: ${data.expect} items, but found ${error}`); + } + } } diff --git a/projects/aca-playwright-shared/src/api/file-actions.ts b/projects/aca-playwright-shared/src/api/file-actions.ts index 865f74cb4e..f0c75b6742 100644 --- a/projects/aca-playwright-shared/src/api/file-actions.ts +++ b/projects/aca-playwright-shared/src/api/file-actions.ts @@ -26,7 +26,7 @@ import * as fs from 'fs'; import { ApiClientFactory } from './api-client-factory'; import { Utils } from '../utils'; import { ApiUtil, Logger } from '@alfresco/adf-testing'; -import { NodeEntry } from '@alfresco/js-api'; +import { NodeBodyCreate, NodeEntry } from '@alfresco/js-api'; export class FileActionsApi { private apiService: ApiClientFactory; @@ -50,6 +50,27 @@ export class FileActionsApi { }); } + async uploadFileWithRename(fileLocation: string, parentId: string = '-my-', newName: string, title: string = '', description: string = '') { + const file = fs.createReadStream(fileLocation); + const nodeProps = { + properties: { + 'cm:title': title, + 'cm:description': description + } + }; + + const opts = { + name: newName, + nodeType: 'cm:content' + }; + + try { + return await this.apiService.upload.uploadFile(file, '', parentId, nodeProps as NodeBodyCreate, opts); + } catch (error) { + Logger.error(`${this.constructor.name} ${this.uploadFileWithRename.name}`, error); + } + } + async lockNodes(nodeIds: string[], lockType: string = 'ALLOW_OWNER_CHANGES') { try { for (const nodeId of nodeIds) { diff --git a/projects/aca-playwright-shared/src/api/index.ts b/projects/aca-playwright-shared/src/api/index.ts index 61942ccbca..0d7a98d447 100644 --- a/projects/aca-playwright-shared/src/api/index.ts +++ b/projects/aca-playwright-shared/src/api/index.ts @@ -31,3 +31,4 @@ export * from './people-api-models'; export * from './nodes-api'; export * from './sites-api'; export * from './node-content-tree'; +export * from './search-api'; diff --git a/projects/aca-playwright-shared/src/api/nodes-api.ts b/projects/aca-playwright-shared/src/api/nodes-api.ts index 090e27d724..1e98ad1955 100755 --- a/projects/aca-playwright-shared/src/api/nodes-api.ts +++ b/projects/aca-playwright-shared/src/api/nodes-api.ts @@ -214,4 +214,24 @@ export class NodesApi { } } + async setGranularPermission(nodeId: string, inheritPermissions: boolean = false, username: string, role: string): Promise { + const data = { + permissions: { + isInheritanceEnabled: inheritPermissions, + locallySet: [ + { + authorityId: username, + name: role + } + ] + } + }; + + try { + return await this.apiService.nodes.updateNode(nodeId, data); + } catch (error) { + logger.error(`${this.constructor.name} ${this.setGranularPermission.name}`, error); + return null; + } + } } diff --git a/projects/aca-playwright-shared/src/api/search-api.ts b/projects/aca-playwright-shared/src/api/search-api.ts new file mode 100755 index 0000000000..3a2aae5771 --- /dev/null +++ b/projects/aca-playwright-shared/src/api/search-api.ts @@ -0,0 +1,88 @@ +/*! + * 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 + * along with Alfresco. If not, see . + */ + +import { ApiClientFactory } from './api-client-factory'; +import { Logger } from '@alfresco/adf-testing'; +import { Utils } from '../utils'; + +export class SearchPageApi { + private apiService: ApiClientFactory; + + constructor() { + this.apiService = new ApiClientFactory(); + } + static async initialize(userName: string, password?: string): Promise { + const classObj = new SearchPageApi(); + await classObj.apiService.setUpAcaBackend(userName, password); + return classObj; + } + + private async queryRecentFiles(username: string) { + const data = { + query: { + query: '*', + language: 'afts' + }, + filterQueries: [ + { query: `cm:modified:[NOW/DAY-30DAYS TO NOW/DAY+1DAY]` }, + { query: `cm:modifier:${username} OR cm:creator:${username}` }, + { query: `TYPE:"content" AND -TYPE:"app:filelink" AND -TYPE:"fm:post"` } + ] + }; + + try { + return this.apiService.search.search(data); + } catch (error) { + Logger.error(`SearchApi queryRecentFiles : catch : `, error); + return null; + } + } + + async getTotalItems(username: string): Promise { + try { + return (await this.queryRecentFiles(username)).list.pagination.totalItems; + } catch (error) { + Logger.error(`SearchApi getTotalItems : catch : `, error); + return -1; + } + } + + async waitForApi(username: string, data: { expect: number }) { + try { + const recentFiles = async () => { + const totalItems = await this.getTotalItems(username); + if (totalItems !== data.expect) { + return Promise.reject(totalItems); + } else { + return Promise.resolve(totalItems); + } + }; + + return await Utils.retryCall(recentFiles); + } catch (error) { + Logger.error(`SearchApi waitForApi : catch : `); + Logger.error(`\tExpected: ${data.expect} items, but found ${error}`); + } + } +} diff --git a/projects/aca-playwright-shared/src/api/shared-links-api.ts b/projects/aca-playwright-shared/src/api/shared-links-api.ts index 95310f7444..2419c45100 100755 --- a/projects/aca-playwright-shared/src/api/shared-links-api.ts +++ b/projects/aca-playwright-shared/src/api/shared-links-api.ts @@ -22,8 +22,10 @@ * along with Alfresco. If not, see . */ +import { logger } from '@alfresco/adf-cli/scripts/logger'; import { ApiClientFactory } from './api-client-factory'; -import { SharedLinkEntry } from '@alfresco/js-api'; +import { SharedLinkEntry, SharedLinkPaging } from '@alfresco/js-api'; +import { Utils } from '../utils'; export class SharedLinksApi { private apiService: ApiClientFactory; @@ -48,4 +50,50 @@ export class SharedLinksApi { return null; } } + + async shareFilesByIds(ids: string[]): Promise { + const sharedLinks: SharedLinkEntry[] = []; + try { + if (ids && ids.length > 0) { + for (const id of ids) { + const sharedLink = await this.shareFileById(id); + sharedLinks.push(sharedLink); + } + } + } catch (error) { + logger.error(`SharedLinksApi shareFilesByIds : catch : `, error); + } + return sharedLinks; + } + + private async getSharedLinks(maxItems: number = 250): Promise { + try { + const opts = { + maxItems + }; + return await this.apiService.share.listSharedLinks(opts); + } catch (error) { + logger.error(`SharedLinksApi getSharedLinks : catch : `, error); + return null; + } + } + + async waitForFilesToBeShared(filesIds: string[]): Promise { + try { + const sharedFile = async () => { + const sharedFiles = (await this.getSharedLinks()).list.entries.map((link) => link.entry.nodeId); + const foundItems = filesIds.every((id) => sharedFiles.includes(id)); + if (foundItems) { + return Promise.resolve(foundItems); + } else { + return Promise.reject(foundItems); + } + }; + + return Utils.retryCall(sharedFile); + } catch (error) { + logger.error(`SharedLinksApi waitForFilesToBeShared : catch : ${error}`); + logger.error(`\tWait timeout reached waiting for files to be shared`); + } + } } diff --git a/projects/aca-playwright-shared/src/api/sites-api.ts b/projects/aca-playwright-shared/src/api/sites-api.ts index 226a70ba7f..d761b5edd1 100755 --- a/projects/aca-playwright-shared/src/api/sites-api.ts +++ b/projects/aca-playwright-shared/src/api/sites-api.ts @@ -23,7 +23,7 @@ */ import { ApiClientFactory } from './api-client-factory'; -import { Site, SiteBodyCreate, SiteEntry } from '@alfresco/js-api'; +import { Site, SiteBodyCreate, SiteEntry, SiteMembershipBodyCreate, SiteMembershipBodyUpdate } from '@alfresco/js-api'; import { logger } from '@alfresco/adf-cli/scripts/logger'; export class SitesApi { @@ -63,20 +63,51 @@ export class SitesApi { } } - /** + /** * Delete multiple sites/libraries. * @param siteIds The list of the site/library IDs to delete. * @param permanent Delete permanently, without moving to the trashcan? (default: true) */ - async deleteSites(siteIds: string[], permanent: boolean = true) { - try { - if (siteIds && siteIds.length > 0) { - for (const siteId of siteIds) { - await this.apiService.sites.deleteSite(siteId, { permanent }); - } - } - } catch (error) { - logger.error(`${this.constructor.name} ${this.deleteSites.name}`, error); + async deleteSites(siteIds: string[], permanent: boolean = true) { + try { + if (siteIds && siteIds.length > 0) { + for (const siteId of siteIds) { + await this.apiService.sites.deleteSite(siteId, { permanent }); } } + } catch (error) { + logger.error(`${this.constructor.name} ${this.deleteSites.name}`, error); + } + } + + async updateSiteMember(siteId: string, userId: string, role: string) { + const siteRole = { + role: role + } as SiteMembershipBodyUpdate; + + try { + return await this.apiService.sites.updateSiteMembership(siteId, userId, siteRole); + } catch (error) { + logger.error(`SitesApi updateSiteMember : catch : `, error); + return null; + } + } + + async addSiteMember(siteId: string, userId: string, role: string) { + const memberBody = { + id: userId, + role: role + } as SiteMembershipBodyCreate; + + try { + return await this.apiService.sites.createSiteMembership(siteId, memberBody); + } catch (error) { + if (error.status === 409) { + return this.updateSiteMember(siteId, userId, role); + } else { + logger.error(`SitesApi addSiteMember : catch : `, error); + return null; + } + } + } } diff --git a/projects/aca-playwright-shared/src/index.ts b/projects/aca-playwright-shared/src/index.ts index 356316797e..37fc9fe8d3 100644 --- a/projects/aca-playwright-shared/src/index.ts +++ b/projects/aca-playwright-shared/src/index.ts @@ -29,3 +29,4 @@ export * from './page-objects'; export * from './fixtures/page-initialization'; export * from './utils'; export * from './resources/test-files'; +export * from './resources/test-data'; diff --git a/projects/aca-playwright-shared/src/page-objects/components/dataTable/mat-menu.component.ts b/projects/aca-playwright-shared/src/page-objects/components/dataTable/mat-menu.component.ts index 1f48038a6c..8e96c0c2a4 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/dataTable/mat-menu.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/dataTable/mat-menu.component.ts @@ -24,6 +24,7 @@ import { Page } from '@playwright/test'; import { BaseComponent } from '../base.component'; +import { expect } from '@playwright/test'; export class MatMenuComponent extends BaseComponent { private static rootElement = '.mat-menu-content'; @@ -50,4 +51,18 @@ export class MatMenuComponent extends BaseComponent { await menuElement.waitFor({ state: 'attached' }); return await menuElement.isVisible(); } + + async verifyActualMoreActions(expectedToolbarMore: string[]): Promise { + await this.page.locator('.mat-menu-content').waitFor({ state: 'attached' }); + let menus = await this.page.$$('.mat-menu-content .mat-menu-item'); + let actualMoreActions: string[] = await Promise.all( + menus.map(async (button) => { + const title = await (await button.$('span')).innerText(); + return title || ''; + }) + ); + for (const action of expectedToolbarMore) { + expect(actualMoreActions.includes(action), `Expected to contain ${action} ${actualMoreActions}`).toBe(true); + } + } } diff --git a/projects/aca-playwright-shared/src/page-objects/components/viewer.component.ts b/projects/aca-playwright-shared/src/page-objects/components/viewer.component.ts index 601899620c..dc3dd5613c 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/viewer.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/viewer.component.ts @@ -26,6 +26,7 @@ import { Page } from '@playwright/test'; import { BaseComponent } from './base.component'; import { AcaHeader } from './aca-header.component'; import { timeouts } from '../../utils'; +import { expect } from '@playwright/test'; export class ViewerComponent extends BaseComponent { private static rootElement = 'adf-viewer'; @@ -35,6 +36,7 @@ export class ViewerComponent extends BaseComponent { public fileTitleButtonLocator = this.getChild('.adf-viewer__file-title'); public pdfViewerContentPages = this.getChild('.adf-pdf-viewer__content .page'); public shareButton = this.getChild('button[id="share-action-button"]'); + public allButtons = this.getChild('button'); toolbar = new AcaHeader(this.page); @@ -70,4 +72,23 @@ export class ViewerComponent extends BaseComponent { await this.closeButtonLocator.waitFor({ state: 'visible', timeout: timeouts.normal }); return await this.closeButtonLocator.getAttribute('title'); } + + async verifyViewerPrimaryActions(expectedToolbarPrimary: string[]): Promise { + const toRemove = ['Close', 'Previous File', 'Next File', 'View details']; + const removeClosePreviousNextOldInfo = (actions: string[]): string[] => actions.filter((elem) => !toRemove.includes(elem)); + + let buttons = await this.page.$$('adf-viewer button'); + let actualPrimaryActions: string[] = await Promise.all( + buttons.map(async (button) => { + const title = await button.getAttribute('title'); + return title || ''; + }) + ); + + actualPrimaryActions = removeClosePreviousNextOldInfo(actualPrimaryActions); + + for (const action of expectedToolbarPrimary) { + expect(actualPrimaryActions.includes(action), `Expected to contain ${action}`).toBe(true); + } + } } diff --git a/projects/aca-playwright-shared/src/resources/test-data/index.ts b/projects/aca-playwright-shared/src/resources/test-data/index.ts new file mode 100644 index 0000000000..9d04786adc --- /dev/null +++ b/projects/aca-playwright-shared/src/resources/test-data/index.ts @@ -0,0 +1,25 @@ +/*! + * 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 . + */ + +export * from './test-data-permissions'; diff --git a/projects/aca-playwright-shared/src/resources/test-data/test-data-permissions.ts b/projects/aca-playwright-shared/src/resources/test-data/test-data-permissions.ts new file mode 100644 index 0000000000..90e2ea6040 --- /dev/null +++ b/projects/aca-playwright-shared/src/resources/test-data/test-data-permissions.ts @@ -0,0 +1,385 @@ +/*! + * 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 . + */ + +import { Utils } from '@alfresco/playwright-shared'; + +export const random = Utils.random(); + +// ----- files ----- + +const consumerContextMenu = ['Share', 'Download', 'View', 'Favorite', 'Copy', 'Manage Versions']; +const consumerFavContextMenu = ['Share', 'Download', 'View', 'Remove Favorite', 'Copy', 'Manage Versions']; +const consumerSharedContextMenu = ['Shared Link Settings', 'Download', 'View', 'Favorite', 'Copy', 'Manage Versions']; +const consumerSharedFavContextMenu = ['Shared Link Settings', 'Download', 'View', 'Remove Favorite', 'Copy', 'Manage Versions']; + +const consumerToolbarPrimary = ['Share', 'Download', 'View', 'View Details', 'More Actions']; +const consumerSharedToolbarPrimary = ['Shared Link Settings', 'Download', 'View', 'View Details', 'More Actions']; + +const searchConsumerToolbarPrimary = ['Share', 'Download', 'View', 'View Details', 'More Actions']; +const searchConsumerSharedToolbarPrimary = ['Shared Link Settings', 'Download', 'View', 'View Details', 'More Actions']; + +const consumerToolbarMore = ['Favorite', 'Copy', 'Manage Versions']; +const consumerFavToolbarMore = ['Remove Favorite', 'Copy', 'Manage Versions']; + +// ---- VIEWER ---- + +const consumerViewerSharedToolbarPrimary = ['Activate full-screen mode', 'Shared Link Settings', 'Download', 'Print', 'View Details', 'More Actions']; +const consumerViewerToolbarPrimary = ['Activate full-screen mode', 'Share', 'Download', 'Print', 'View Details', 'More Actions']; +const consumerViewerFavToolbarMore = ['Remove Favorite', 'Copy', 'Manage Versions']; +const consumerViewerToolbarMore = ['Favorite', 'Copy', 'Manage Versions']; + +// ---- FAVORITES workarounds ---- + +const favoritesConsumerToolbarMore = ['Upload New Version', 'Remove Favorite', 'Move', 'Copy', 'Delete', 'Manage Versions']; + +const favoritesConsumerContextMenu = [ + 'Share', + 'Download', + 'View', + 'Upload New Version', + 'Remove Favorite', + 'Move', + 'Copy', + 'Delete', + 'Manage Versions' +]; + +const favoritesConsumerSharedContextMenu = [ + 'Shared Link Settings', + 'Download', + 'View', + 'Upload New Version', + 'Remove Favorite', + 'Move', + 'Copy', + 'Delete', + 'Manage Versions' +]; + +// ---- SHARED FILES workaround ---- + +const sharedConsumerToolbarMore = ['Upload New Version', 'Favorite', 'Copy', 'Manage Versions']; +const sharedConsumerLockedToolbarMore = ['Cancel Editing', 'Upload New Version', 'Favorite', 'Copy', 'Manage Versions']; +const sharedConsumerFavToolbarMore = ['Upload New Version', 'Remove Favorite', 'Copy', 'Manage Versions']; +const sharedConsumerFavLockedToolbarMore = ['Cancel Editing', 'Upload New Version', 'Remove Favorite', 'Copy', 'Manage Versions']; +const sharedConsumerContextMenu = ['Shared Link Settings', 'Download', 'View', 'Upload New Version', 'Favorite', 'Copy', 'Manage Versions']; +const sharedConsumerLockedContextMenu = [ + 'Shared Link Settings', + 'Download', + 'View', + 'Cancel Editing', + 'Upload New Version', + 'Favorite', + 'Copy', + 'Manage Versions' +]; + +const sharedConsumerFavContextMenu = ['Shared Link Settings', 'Download', 'View', 'Upload New Version', 'Remove Favorite', 'Copy', 'Manage Versions']; + +const sharedConsumerFavLockedContextMenu = [ + 'Shared Link Settings', + 'Download', + 'View', + 'Cancel Editing', + 'Upload New Version', + 'Remove Favorite', + 'Copy', + 'Manage Versions' +]; + +export const fileDocx = { + name: `file-${random}-docx.docx`, + description: 'file not shared, not fav, office, not locked', + + contextMenu: consumerContextMenu, + toolbarPrimary: consumerToolbarPrimary, + toolbarMore: consumerToolbarMore, + viewerToolbarPrimary: consumerViewerToolbarPrimary, + viewerToolbarMore: consumerViewerToolbarMore, + + searchToolbarPrimary: searchConsumerToolbarPrimary +}; + +export const fileDocxFav = { + name: `file-${random}-docx-fav.docx`, + description: 'file not shared, fav, office, not locked', + + contextMenu: consumerFavContextMenu, + toolbarPrimary: consumerToolbarPrimary, + toolbarMore: consumerFavToolbarMore, + viewerToolbarPrimary: consumerViewerToolbarPrimary, + viewerToolbarMore: consumerViewerFavToolbarMore, + + favoritesToolbarMore: favoritesConsumerToolbarMore, + favoritesContextMenu: favoritesConsumerContextMenu, + + searchToolbarPrimary: searchConsumerToolbarPrimary +}; + +export const file = { + name: `file-${random}.txt`, + description: 'file not shared, not fav, not office, not locked', + + contextMenu: consumerContextMenu, + toolbarPrimary: consumerToolbarPrimary, + toolbarMore: consumerToolbarMore, + viewerToolbarPrimary: consumerViewerToolbarPrimary, + viewerToolbarMore: consumerViewerToolbarMore, + + searchToolbarPrimary: searchConsumerToolbarPrimary +}; + +export const fileFav = { + name: `file-${random}-fav.txt`, + description: 'file not shared, fav, not office, not locked', + + contextMenu: consumerFavContextMenu, + toolbarPrimary: consumerToolbarPrimary, + toolbarMore: consumerFavToolbarMore, + viewerToolbarPrimary: consumerViewerToolbarPrimary, + viewerToolbarMore: consumerViewerFavToolbarMore, + + favoritesToolbarMore: favoritesConsumerToolbarMore, + favoritesContextMenu: favoritesConsumerContextMenu, + + searchToolbarPrimary: searchConsumerToolbarPrimary +}; + +export const fileDocxShared = { + name: `file-${random}-docx-shared.docx`, + description: 'file shared, not fav, office, not locked', + + contextMenu: consumerSharedContextMenu, + toolbarPrimary: consumerSharedToolbarPrimary, + toolbarMore: consumerToolbarMore, + viewerToolbarPrimary: consumerViewerSharedToolbarPrimary, + viewerToolbarMore: consumerViewerToolbarMore, + + sharedToolbarMore: sharedConsumerToolbarMore, + sharedContextMenu: sharedConsumerContextMenu, + + searchToolbarPrimary: searchConsumerSharedToolbarPrimary +}; + +export const fileDocxSharedFav = { + name: `file-${random}-docx-shared-fav.docx`, + description: 'file shared, fav, office, not locked', + + contextMenu: consumerSharedFavContextMenu, + toolbarPrimary: consumerSharedToolbarPrimary, + toolbarMore: consumerFavToolbarMore, + viewerToolbarPrimary: consumerViewerSharedToolbarPrimary, + viewerToolbarMore: consumerViewerFavToolbarMore, + + favoritesToolbarMore: favoritesConsumerToolbarMore, + favoritesContextMenu: favoritesConsumerSharedContextMenu, + + sharedToolbarMore: sharedConsumerFavToolbarMore, + sharedContextMenu: sharedConsumerFavContextMenu, + + searchToolbarPrimary: searchConsumerSharedToolbarPrimary +}; + +export const fileShared = { + name: `file-${random}-shared.txt`, + description: 'file shared, not fav, not office, not locked', + + contextMenu: consumerSharedContextMenu, + toolbarPrimary: consumerSharedToolbarPrimary, + toolbarMore: consumerToolbarMore, + viewerToolbarPrimary: consumerViewerSharedToolbarPrimary, + viewerToolbarMore: consumerViewerToolbarMore, + + sharedToolbarMore: sharedConsumerToolbarMore, + sharedContextMenu: sharedConsumerContextMenu, + + searchToolbarPrimary: searchConsumerSharedToolbarPrimary +}; + +export const fileSharedFav = { + name: `file-${random}-shared-fav.txt`, + description: 'file shared, fav, not office, not locked', + + contextMenu: consumerSharedFavContextMenu, + toolbarPrimary: consumerSharedToolbarPrimary, + toolbarMore: consumerFavToolbarMore, + viewerToolbarPrimary: consumerViewerSharedToolbarPrimary, + viewerToolbarMore: consumerViewerFavToolbarMore, + + favoritesToolbarMore: favoritesConsumerToolbarMore, + favoritesContextMenu: favoritesConsumerSharedContextMenu, + + sharedToolbarMore: sharedConsumerFavToolbarMore, + sharedContextMenu: sharedConsumerFavContextMenu, + + searchToolbarPrimary: searchConsumerSharedToolbarPrimary +}; + +export const fileLocked = { + name: `file-${random}-locked.txt`, + description: 'file not shared, not fav, not office, locked', + + contextMenu: consumerContextMenu, + toolbarPrimary: consumerToolbarPrimary, + toolbarMore: consumerToolbarMore, + viewerToolbarPrimary: consumerViewerToolbarPrimary, + viewerToolbarMore: consumerViewerToolbarMore, + + searchToolbarPrimary: searchConsumerToolbarPrimary +}; + +export const fileFavLocked = { + name: `file-${random}-fav-locked.txt`, + description: 'file not shared, fav, not office, locked', + + contextMenu: consumerFavContextMenu, + toolbarPrimary: consumerToolbarPrimary, + toolbarMore: consumerFavToolbarMore, + viewerToolbarPrimary: consumerViewerToolbarPrimary, + viewerToolbarMore: consumerViewerFavToolbarMore, + + favoritesToolbarMore: favoritesConsumerToolbarMore, + favoritesContextMenu: favoritesConsumerContextMenu, + + searchToolbarPrimary: searchConsumerToolbarPrimary +}; + +export const fileSharedLocked = { + name: `file-${random}-shared-locked.txt`, + description: 'file shared, not fav, not office, locked', + + contextMenu: consumerSharedContextMenu, + toolbarPrimary: consumerSharedToolbarPrimary, + toolbarMore: consumerToolbarMore, + viewerToolbarPrimary: consumerViewerSharedToolbarPrimary, + viewerToolbarMore: consumerViewerToolbarMore, + + sharedToolbarMore: sharedConsumerLockedToolbarMore, + sharedContextMenu: sharedConsumerLockedContextMenu, + + searchToolbarPrimary: searchConsumerSharedToolbarPrimary +}; + +export const fileSharedFavLocked = { + name: `file-${random}-shared-fav-locked.txt`, + description: 'file shared, fav, not office, locked', + + contextMenu: consumerSharedFavContextMenu, + toolbarPrimary: consumerSharedToolbarPrimary, + toolbarMore: consumerFavToolbarMore, + viewerToolbarPrimary: consumerViewerSharedToolbarPrimary, + viewerToolbarMore: consumerViewerFavToolbarMore, + + favoritesToolbarMore: favoritesConsumerToolbarMore, + favoritesContextMenu: favoritesConsumerSharedContextMenu, + + sharedToolbarMore: sharedConsumerFavLockedToolbarMore, + sharedContextMenu: sharedConsumerFavLockedContextMenu, + + searchToolbarPrimary: searchConsumerSharedToolbarPrimary +}; + +export const fileGranularPermission = `file-${random}-granular.txt`; +export const fileLockedByUser = `file-${random}-my-locked.txt`; + +// ---- folders --- + +const consumerFolderContextMenu = ['Download', 'Favorite', 'Copy']; +const consumerFolderToolbarPrimary = ['Download', 'View Details', 'More Actions']; +const consumerFolderToolbarMore = ['Favorite', 'Copy']; +const searchConsumerFolderToolbarPrimary = ['Download', 'View Details', 'More Actions']; +const consumerFolderFavContextMenu = ['Download', 'Remove Favorite', 'Copy']; +const consumerFolderFavToolbarMore = ['Remove Favorite', 'Copy']; + +// ---- FAVORITES workarounds ---- + +const favoritesConsumerFolderContextMenu = ['Download', 'Edit', 'Remove Favorite', 'Move', 'Copy', 'Delete']; + +const favoritesConsumerFolderToolbarMore = ['Edit', 'Remove Favorite', 'Move', 'Copy', 'Delete']; + +export const folder = { + name: `folder-${random}`, + description: 'folder not favorite', + contextMenu: consumerFolderContextMenu, + toolbarPrimary: consumerFolderToolbarPrimary, + toolbarMore: consumerFolderToolbarMore, + + searchToolbarPrimary: searchConsumerFolderToolbarPrimary +}; + +export const folderFav = { + name: `folder-fav-${random}`, + description: 'folder favorite', + contextMenu: consumerFolderFavContextMenu, + toolbarPrimary: consumerFolderToolbarPrimary, + toolbarMore: consumerFolderFavToolbarMore, + + favoritesContextMenu: favoritesConsumerFolderContextMenu, + favoritesToolbarMore: favoritesConsumerFolderToolbarMore, + + searchToolbarPrimary: searchConsumerFolderToolbarPrimary +}; + +export const folderFav2 = { + name: `folder-fav-2-${random}`, + description: 'folder 2 favorite' +}; + +// ---- multiple selection --- + +const multipleSelContextMenu = ['Download', 'Favorite', 'Copy']; +const multipleSelAllFavContextMenu = ['Download', 'Remove Favorite', 'Copy']; +const multipleSelToolbarPrimary = ['Download', 'View Details', 'More Actions']; +const multipleSelToolbarMore = ['Favorite', 'Copy']; +const multipleSelAllFavToolbarMore = ['Remove Favorite', 'Copy']; +const searchMultipleSelToolbarPrimary = ['Download', 'View Details', 'More Actions']; + +// ---- FAVORITES workarounds ---- + +const favoritesMultipleSelContextMenu = ['Download', 'Favorite', 'Move', 'Copy', 'Delete']; +const favoritesMultipleSelToolbarMore = ['Favorite', 'Move', 'Copy', 'Delete']; +const favoritesMultipleSelAllFavContextMenu = ['Download', 'Remove Favorite', 'Move', 'Copy', 'Delete']; +const favoritesMultipleSelAllFavToolbarMore = ['Remove Favorite', 'Move', 'Copy', 'Delete']; + +export const multipleSel = { + contextMenu: multipleSelContextMenu, + toolbarPrimary: multipleSelToolbarPrimary, + toolbarMore: multipleSelToolbarMore, + + favoritesContextMenu: favoritesMultipleSelContextMenu, + favoritesToolbarMore: favoritesMultipleSelToolbarMore, + + searchToolbarPrimary: searchMultipleSelToolbarPrimary +}; + +export const multipleSelAllFav = { + contextMenu: multipleSelAllFavContextMenu, + toolbarPrimary: multipleSelToolbarPrimary, + toolbarMore: multipleSelAllFavToolbarMore, + + favoritesContextMenu: favoritesMultipleSelAllFavContextMenu, + favoritesToolbarMore: favoritesMultipleSelAllFavToolbarMore, + + searchToolbarPrimary: searchMultipleSelToolbarPrimary +}; diff --git a/projects/aca-playwright-shared/src/resources/test-files/file-jpg.jpg b/projects/aca-playwright-shared/src/resources/test-files/file-jpg.jpg new file mode 100644 index 0000000000000000000000000000000000000000..eb6bf4a4570fa8112b945e44f6af249a47450b41 GIT binary patch literal 22812 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 literal 0 HcmV?d00001 diff --git a/projects/aca-playwright-shared/src/resources/test-files/file2-xlsx.xlsx b/projects/aca-playwright-shared/src/resources/test-files/file2-xlsx.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..3039a7cdc8579121f821af3dbb78590f00ba19f6 GIT binary patch literal 97629 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 literal 0 HcmV?d00001 diff --git a/projects/aca-playwright-shared/src/resources/test-files/file_unsupported.3DS b/projects/aca-playwright-shared/src/resources/test-files/file_unsupported.3DS new file mode 100755 index 0000000000..be21d11425 --- /dev/null +++ b/projects/aca-playwright-shared/src/resources/test-files/file_unsupported.3DS @@ -0,0 +1 @@ +™™ \ No newline at end of file diff --git a/projects/aca-playwright-shared/src/resources/test-files/index.ts b/projects/aca-playwright-shared/src/resources/test-files/index.ts index 297040f2a0..867a739077 100644 --- a/projects/aca-playwright-shared/src/resources/test-files/index.ts +++ b/projects/aca-playwright-shared/src/resources/test-files/index.ts @@ -30,11 +30,21 @@ export const TEST_FILES = { name: 'file-docx', data: 'Lorem ipsum dolor sit amet' }, + DOCX2: { + path: resolve(__dirname, 'file2-docx.docx'), + name: 'file-docx', + data: 'Lorem ipsum dolor sit amet' + }, PDF: { path: resolve(__dirname, 'file-pdf.pdf'), name: 'file-pdf', data: 'Lorem ipsum dolor sit amet' }, + FILE_UNSUPPORTED: { + path: resolve(__dirname, 'file_unsupported.3DS'), + name: 'file-3DS', + data: 'Lorem ipsum dolor sit amet' + }, PDF_PROTECTED: { path: resolve(__dirname, 'file-pdf-protected.pdf'), name: 'file-pdf-protected', @@ -45,5 +55,20 @@ export const TEST_FILES = { path: resolve(__dirname, 'file-xlsx.xlsx'), name: 'file-xlsx', data: 'Lorem ipsum dolor sit amet' + }, + XLSX2: { + path: resolve(__dirname, 'file2-xlsx.xlsx'), + name: 'file-xlsx', + data: 'Lorem ipsum dolor sit amet' + }, + JPG_FILE: { + path: resolve(__dirname, 'file-jpg.jpg'), + name: 'file-jpg' + }, + PDF_PROTECTED2: { + path: resolve(__dirname, 'protected.pdf'), + name: 'file-protected', + data: 'Lorem ipsum dolor sit amet', + password: '0000' } }; diff --git a/projects/aca-playwright-shared/src/resources/test-files/protected.pdf b/projects/aca-playwright-shared/src/resources/test-files/protected.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d0d083f114898f160cad4b07fdb57046a46cfdf8 GIT binary patch literal 18052 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�n$D2q#UC*S2dlsX7xdxaRXh03)Na4ql`!)% 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 literal 0 HcmV?d00001 From 436ef5960631fe940446a4cd85d703979a7a4f60 Mon Sep 17 00:00:00 2001 From: "akash.rathod@hyland.com" Date: Thu, 28 Sep 2023 23:17:39 +0200 Subject: [PATCH 07/10] [ACS-6066] viewer protractor test remove --- .github/workflows/pull-request.yml | 2 ++ .../special-permissions/special-permissions-actions.test.ts | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 20f7d81381..c0f6a5fc9b 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -182,6 +182,8 @@ jobs: id: 4 - name: "navigation" id: 5 + - name: "special-permissions" + id: 6 steps: - name: Checkout uses: actions/checkout@v3 diff --git a/e2e/protractor/suites/actions-available/special-permissions/special-permissions-actions.test.ts b/e2e/protractor/suites/actions-available/special-permissions/special-permissions-actions.test.ts index 3fc5d0298d..85d0f413f5 100644 --- a/e2e/protractor/suites/actions-available/special-permissions/special-permissions-actions.test.ts +++ b/e2e/protractor/suites/actions-available/special-permissions/special-permissions-actions.test.ts @@ -27,7 +27,6 @@ import * as testData from './test-data-permissions'; import { librariesTests } from './my-libraries'; import { favoritesTests } from './favorites'; import { searchResultsTests } from './search-results'; -import { viewerTests } from './viewer'; import { sharedFilesTests } from './shared-files'; import { collaboratorTests, filesLockedByCurrentUser, filesLockedByOtherUser } from './other-permissions'; @@ -181,10 +180,6 @@ describe('Special permissions : ', () => { searchResultsTests(); }); - describe('on Viewer', () => { - viewerTests(sitePrivate); - }); - describe('on Shared Files', () => { sharedFilesTests(); }); From af7896acfbbe97f3917e1f56027d6f3310bd57cb Mon Sep 17 00:00:00 2001 From: "akash.rathod@hyland.com" Date: Fri, 29 Sep 2023 09:56:59 +0200 Subject: [PATCH 08/10] [ACS-6066] viewer failed test fix --- .../page-objects/components/search/search-input.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/aca-playwright-shared/src/page-objects/components/search/search-input.component.ts b/projects/aca-playwright-shared/src/page-objects/components/search/search-input.component.ts index c575f423a5..6adf0d7388 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/search/search-input.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/search/search-input.component.ts @@ -44,7 +44,7 @@ export class SearchInputComponent extends BaseComponent { } async performDoubleClickFolderOrFileToOpen(name: string): Promise { - await this.getCellLinkByName(name).waitFor({ state: 'visible', timeout: timeouts.normal }); + await this.getCellLinkByName(name).waitFor({ state: 'visible', timeout: timeouts.medium }); await this.getCellLinkByName(name).dblclick(); await this.spinnerWaitForReload(); } From 54d00dcf94fa3c983014d2fb57d84cb70616e833 Mon Sep 17 00:00:00 2001 From: Akash Rathod <41251473+akashrathod28@users.noreply.github.com> Date: Mon, 2 Oct 2023 10:13:54 +0000 Subject: [PATCH 09/10] review changes added --- .../src/api/favorites-api.ts | 14 +++++------ .../src/api/file-actions.ts | 23 ++++++------------- .../src/api/search-api.ts | 16 +++++-------- .../src/api/shared-links-api.ts | 6 ++--- .../src/api/sites-api.ts | 10 ++++---- 5 files changed, 28 insertions(+), 41 deletions(-) diff --git a/projects/aca-playwright-shared/src/api/favorites-api.ts b/projects/aca-playwright-shared/src/api/favorites-api.ts index 8567904996..2e061690af 100755 --- a/projects/aca-playwright-shared/src/api/favorites-api.ts +++ b/projects/aca-playwright-shared/src/api/favorites-api.ts @@ -23,7 +23,7 @@ */ import { ApiClientFactory } from './api-client-factory'; -import { FavoriteEntry } from '@alfresco/js-api'; +import { FavoriteEntry, FavoritePaging } from '@alfresco/js-api'; import { Logger } from '@alfresco/adf-testing'; import { Utils } from '../utils'; @@ -38,7 +38,7 @@ export class FavoritesPageApi { await classObj.apiService.setUpAcaBackend(userName, password); return classObj; } - async addFavoriteById(nodeType: 'file' | 'folder' | 'site', id: string): Promise { + async addFavoriteById(nodeType: 'file' | 'folder' | 'site', id: string): Promise { let guid = nodeType === 'site' ? (await this.apiService.sites.getSite(id)).entry.guid : id; const data = { target: { @@ -65,25 +65,25 @@ export class FavoritesPageApi { return favorites; } - private async getFavorites(username: string) { + private async getFavorites(username: string): Promise { try { return await this.apiService.favorites.listFavorites(username); } catch (error) { Logger.error(`FavoritesApi getFavorites : catch : `, error); - return null; + return new FavoritePaging; } } - async isFavorite(username: string, nodeId: string) { + async isFavorite(username: string, nodeId: string): Promise { try { return JSON.stringify((await this.getFavorites(username)).list.entries).includes(nodeId); } catch (error) { Logger.error(`FavoritesApi isFavorite : catch : `, error); - return null; + return false; } } - async isFavoriteWithRetry(username: string, nodeId: string, data: { expect: boolean }) { + async isFavoriteWithRetry(username: string, nodeId: string, data: { expect: boolean }): Promise { let isFavorite = false; try { const favorite = async () => { diff --git a/projects/aca-playwright-shared/src/api/file-actions.ts b/projects/aca-playwright-shared/src/api/file-actions.ts index 4b698e39ca..6f6c254ffe 100644 --- a/projects/aca-playwright-shared/src/api/file-actions.ts +++ b/projects/aca-playwright-shared/src/api/file-actions.ts @@ -26,7 +26,7 @@ import * as fs from 'fs'; import { ApiClientFactory } from './api-client-factory'; import { Utils } from '../utils'; import { ApiUtil, Logger } from '@alfresco/adf-testing'; -import { NodeBodyCreate, NodeEntry } from '@alfresco/js-api'; +import { NodeBodyCreate, NodeEntry, ResultSetPaging } from '@alfresco/js-api'; export class FileActionsApi { private apiService: ApiClientFactory; @@ -57,7 +57,7 @@ export class FileActionsApi { 'cm:title': title, 'cm:description': description } - }; + } as NodeBodyCreate; const opts = { name: newName, @@ -65,7 +65,7 @@ export class FileActionsApi { }; try { - return await this.apiService.upload.uploadFile(file, '', parentId, nodeProps as NodeBodyCreate, opts); + return await this.apiService.upload.uploadFile(file, '', parentId, nodeProps, opts); } catch (error) { Logger.error(`${this.constructor.name} ${this.uploadFileWithRename.name}`, error); } @@ -110,16 +110,6 @@ export class FileActionsApi { } } - private async getLockType(nodeId: string): Promise { - try { - const lockType = await this.getNodeProperty(nodeId, 'cm:lockType'); - return lockType || ''; - } catch (error) { - Logger.error(`${this.constructor.name} ${this.getLockType.name}`, error); - return ''; - } - } - async isFileLockedWriteWithRetry(nodeId: string, expect: boolean): Promise { const data = { expect: expect, @@ -142,7 +132,7 @@ export class FileActionsApi { return isLocked; } - private async queryNodesNames(searchTerm: string) { + private async queryNodesNames(searchTerm: string): Promise { const data = { query: { query: `cm:name:\"${searchTerm}*\"`, @@ -155,10 +145,11 @@ export class FileActionsApi { return this.apiService.search.search(data); } catch (error) { Logger.error(`SearchApi queryNodesNames : catch : `, error); - return null; + return new ResultSetPaging; } } - async waitForNodes(searchTerm: string, data: { expect: number }) { + + async waitForNodes(searchTerm: string, data: { expect: number }): Promise { const predicate = (totalItems: number) => totalItems === data.expect; const apiCall = async () => { diff --git a/projects/aca-playwright-shared/src/api/search-api.ts b/projects/aca-playwright-shared/src/api/search-api.ts index 3a2aae5771..ecc90b2b30 100755 --- a/projects/aca-playwright-shared/src/api/search-api.ts +++ b/projects/aca-playwright-shared/src/api/search-api.ts @@ -25,6 +25,7 @@ import { ApiClientFactory } from './api-client-factory'; import { Logger } from '@alfresco/adf-testing'; import { Utils } from '../utils'; +import { ResultSetPaging } from '@alfresco/js-api'; export class SearchPageApi { private apiService: ApiClientFactory; @@ -38,7 +39,7 @@ export class SearchPageApi { return classObj; } - private async queryRecentFiles(username: string) { + private async querySearchFiles(username: string): Promise { const data = { query: { query: '*', @@ -55,22 +56,17 @@ export class SearchPageApi { return this.apiService.search.search(data); } catch (error) { Logger.error(`SearchApi queryRecentFiles : catch : `, error); - return null; + return new ResultSetPaging; } } async getTotalItems(username: string): Promise { - try { - return (await this.queryRecentFiles(username)).list.pagination.totalItems; - } catch (error) { - Logger.error(`SearchApi getTotalItems : catch : `, error); - return -1; - } + return (await this.querySearchFiles(username)).list.pagination.totalItems; } async waitForApi(username: string, data: { expect: number }) { try { - const recentFiles = async () => { + const searchFiles = async () => { const totalItems = await this.getTotalItems(username); if (totalItems !== data.expect) { return Promise.reject(totalItems); @@ -79,7 +75,7 @@ export class SearchPageApi { } }; - return await Utils.retryCall(recentFiles); + return await Utils.retryCall(searchFiles); } catch (error) { Logger.error(`SearchApi waitForApi : catch : `); Logger.error(`\tExpected: ${data.expect} items, but found ${error}`); diff --git a/projects/aca-playwright-shared/src/api/shared-links-api.ts b/projects/aca-playwright-shared/src/api/shared-links-api.ts index 2419c45100..c90089c6d9 100755 --- a/projects/aca-playwright-shared/src/api/shared-links-api.ts +++ b/projects/aca-playwright-shared/src/api/shared-links-api.ts @@ -66,7 +66,7 @@ export class SharedLinksApi { return sharedLinks; } - private async getSharedLinks(maxItems: number = 250): Promise { + private async getSharedLinks(maxItems: number = 250): Promise { try { const opts = { maxItems @@ -74,11 +74,11 @@ export class SharedLinksApi { return await this.apiService.share.listSharedLinks(opts); } catch (error) { logger.error(`SharedLinksApi getSharedLinks : catch : `, error); - return null; + return new SharedLinkPaging; } } - async waitForFilesToBeShared(filesIds: string[]): Promise { + async waitForFilesToBeShared(filesIds: string[]): Promise { try { const sharedFile = async () => { const sharedFiles = (await this.getSharedLinks()).list.entries.map((link) => link.entry.nodeId); diff --git a/projects/aca-playwright-shared/src/api/sites-api.ts b/projects/aca-playwright-shared/src/api/sites-api.ts index d761b5edd1..15625fb415 100755 --- a/projects/aca-playwright-shared/src/api/sites-api.ts +++ b/projects/aca-playwright-shared/src/api/sites-api.ts @@ -23,7 +23,7 @@ */ import { ApiClientFactory } from './api-client-factory'; -import { Site, SiteBodyCreate, SiteEntry, SiteMembershipBodyCreate, SiteMembershipBodyUpdate } from '@alfresco/js-api'; +import { Site, SiteBodyCreate, SiteEntry, SiteMemberEntry, SiteMembershipBodyCreate, SiteMembershipBodyUpdate } from '@alfresco/js-api'; import { logger } from '@alfresco/adf-cli/scripts/logger'; export class SitesApi { @@ -80,7 +80,7 @@ export class SitesApi { } } - async updateSiteMember(siteId: string, userId: string, role: string) { + async updateSiteMember(siteId: string, userId: string, role: string): Promise { const siteRole = { role: role } as SiteMembershipBodyUpdate; @@ -89,11 +89,11 @@ export class SitesApi { return await this.apiService.sites.updateSiteMembership(siteId, userId, siteRole); } catch (error) { logger.error(`SitesApi updateSiteMember : catch : `, error); - return null; + return new SiteMemberEntry; } } - async addSiteMember(siteId: string, userId: string, role: string) { + async addSiteMember(siteId: string, userId: string, role: string): Promise { const memberBody = { id: userId, role: role @@ -106,7 +106,7 @@ export class SitesApi { return this.updateSiteMember(siteId, userId, role); } else { logger.error(`SitesApi addSiteMember : catch : `, error); - return null; + return new SiteMemberEntry; } } } From af4dd443771e40c01dd5888978a71694aeb1696e Mon Sep 17 00:00:00 2001 From: "akash.rathod@hyland.com" Date: Tue, 3 Oct 2023 10:42:53 +0200 Subject: [PATCH 10/10] fix error in script --- projects/aca-playwright-shared/src/api/nodes-api.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/projects/aca-playwright-shared/src/api/nodes-api.ts b/projects/aca-playwright-shared/src/api/nodes-api.ts index 508d43575e..f242c61ad9 100755 --- a/projects/aca-playwright-shared/src/api/nodes-api.ts +++ b/projects/aca-playwright-shared/src/api/nodes-api.ts @@ -232,6 +232,18 @@ export class NodesApi { authorityId: username, name: role } + ] + } + }; + + try { + return await this.apiService.nodes.updateNode(nodeId, data); + } catch (error) { + logger.error(`${this.constructor.name} ${this.setGranularPermission.name}`, error); + return null; + } + } + async removeUserAccessOnSpaceTemplate(nodeName: string): Promise { try { const templatesRootFolderId = await this.getSpaceTemplatesFolderId();