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/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/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(); }); 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..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: { @@ -50,25 +50,40 @@ export class FavoritesPageApi { return await this.apiService.favorites.createFavorite('-me-', data); } - private async getFavorites(username: string) { + 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): 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 () => { @@ -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..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 { NodeEntry } from '@alfresco/js-api'; +import { NodeBodyCreate, NodeEntry, ResultSetPaging } 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 + } + } as NodeBodyCreate; + + const opts = { + name: newName, + nodeType: 'cm:content' + }; + + try { + return await this.apiService.upload.uploadFile(file, '', parentId, nodeProps, 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) { @@ -111,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}*\"`, @@ -124,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/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 6fb68f96f6..f242c61ad9 100755 --- a/projects/aca-playwright-shared/src/api/nodes-api.ts +++ b/projects/aca-playwright-shared/src/api/nodes-api.ts @@ -223,6 +223,27 @@ 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; + } + } + async removeUserAccessOnSpaceTemplate(nodeName: string): Promise { try { const templatesRootFolderId = await this.getSpaceTemplatesFolderId(); @@ -295,5 +316,4 @@ export class NodesApi { 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..ecc90b2b30 --- /dev/null +++ b/projects/aca-playwright-shared/src/api/search-api.ts @@ -0,0 +1,84 @@ +/*! + * 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'; +import { ResultSetPaging } from '@alfresco/js-api'; + +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 querySearchFiles(username: string): Promise { + 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 new ResultSetPaging; + } + } + + async getTotalItems(username: string): Promise { + return (await this.querySearchFiles(username)).list.pagination.totalItems; + } + + async waitForApi(username: string, data: { expect: number }) { + try { + const searchFiles = async () => { + const totalItems = await this.getTotalItems(username); + if (totalItems !== data.expect) { + return Promise.reject(totalItems); + } else { + return Promise.resolve(totalItems); + } + }; + + 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 95310f7444..c90089c6d9 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 new SharedLinkPaging; + } + } + + 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..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 } 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 { @@ -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): Promise { + 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 new SiteMemberEntry; + } + } + + async addSiteMember(siteId: string, userId: string, role: string): Promise { + 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 new SiteMemberEntry; + } + } + } } 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 46bbe2d9b8..cc9881b83a 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'; @@ -51,4 +52,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/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(); } 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 0000000000..eb6bf4a457 Binary files /dev/null and b/projects/aca-playwright-shared/src/resources/test-files/file-jpg.jpg differ diff --git a/projects/aca-playwright-shared/src/resources/test-files/file2-docx.docx b/projects/aca-playwright-shared/src/resources/test-files/file2-docx.docx new file mode 100644 index 0000000000..c0d0614a84 Binary files /dev/null and b/projects/aca-playwright-shared/src/resources/test-files/file2-docx.docx differ 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 0000000000..3039a7cdc8 Binary files /dev/null and b/projects/aca-playwright-shared/src/resources/test-files/file2-xlsx.xlsx differ 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 0000000000..d0d083f114 Binary files /dev/null and b/projects/aca-playwright-shared/src/resources/test-files/protected.pdf differ