diff --git a/e2e/playwright/actions/src/tests/share/share-file.spec.ts b/e2e/playwright/actions/src/tests/share/share-file.spec.ts new file mode 100644 index 0000000000..e6e040b78e --- /dev/null +++ b/e2e/playwright/actions/src/tests/share/share-file.spec.ts @@ -0,0 +1,246 @@ +/*! + * 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 { ApiClientFactory, NodesApi, test, timeouts, Utils, SharedLinksApi } from '@alfresco/playwright-shared'; +import { expect } from '@playwright/test'; + +test.describe('Share a file', () => { + const random = Utils.random(); + + const username = `user-${random}`; + const parent = `parent-${random}`; + let parentId: string; + + const file3 = `file3-${random}.txt`; + const file4 = `file4-${random}.txt`; + const file5 = `file5-${random}.txt`; + const file6 = `file6-${random}.txt`; + const file7 = `file7-${random}.txt`; + const file8 = `file8-${random}.txt`; + const file9 = `file9-${random}.txt`; + + const shareLinkPreUrl = `/#/preview/s/`; + + const apiClientFactory = new ApiClientFactory(); + + test.beforeAll(async () => { + await apiClientFactory.setUpAcaBackend('admin'); + await apiClientFactory.createUser({ username }); + const nodesApi = await NodesApi.initialize(username, username); + + parentId = (await nodesApi.createFolder(parent)).entry.id; + }); + + test.afterAll(async () => { + await apiClientFactory.nodes.deleteNodes([parentId]); + }); + + test.describe('when logged out', () => { + let file6SharedLink: string; + let file6Id: string; + + test.beforeAll(async () => { + const nodesApi = await NodesApi.initialize(username, username); + const shareApi = await SharedLinksApi.initialize(username, username); + + file6Id = (await nodesApi.createFile(file6, parentId))?.entry.id; + + const sharedId = (await shareApi.shareFileById(file6Id)).entry.id; + file6SharedLink = `${shareLinkPreUrl}${sharedId}`; + await shareApi.waitForFilesToBeShared([file6Id]); + }); + + test.afterAll(async () => { + await apiClientFactory.nodes.deleteNodes([file6Id]); + }); + + test('[C286326] A non-logged user can download the shared file from the viewer', async ({ personalFiles, page }) => { + await page.goto(file6SharedLink); + await personalFiles.viewer.waitForViewerToOpen(); + + const downloadPromise = personalFiles.page.waitForEvent('download'); + await personalFiles.viewer.toolbar.sharedDownloadButton.click(); + const download = await downloadPromise; + expect(download.suggestedFilename()).toBe(file6); + }); + }); + + test.describe('when logged in', () => { + const expiryDateObj = new Date(); + expiryDateObj.setFullYear(expiryDateObj.getFullYear() + 1); + const expiryDate: any = expiryDateObj.toISOString().replace('Z', '+0000'); + + test.describe('from Personal Files', () => { + let file3Id: string; + let file4Id: string; + let file5Id: string; + let file6Id: string; + let file7Id: string; + let file8Id: string; + let file9Id: string; + + test.beforeAll(async () => { + test.setTimeout(timeouts.extendedTest); + const nodesApi = await NodesApi.initialize(username, username); + const shareApi = await SharedLinksApi.initialize(username, username); + + file3Id = (await nodesApi.createFile(file3, parentId))?.entry.id; + file4Id = (await nodesApi.createFile(file4, parentId))?.entry.id; + file5Id = (await nodesApi.createFile(file5, parentId))?.entry.id; + file6Id = (await nodesApi.createFile(file6, parentId))?.entry.id; + file7Id = (await nodesApi.createFile(file7, parentId))?.entry.id; + file8Id = (await nodesApi.createFile(file8, parentId))?.entry.id; + file9Id = (await nodesApi.createFile(file9, parentId))?.entry.id; + + await shareApi.shareFilesByIds([file6Id, file7Id], expiryDate); + await shareApi.waitForFilesToBeShared([file6Id, file7Id]); + }); + + test.beforeEach(async ({ loginPage, personalFiles, page }) => { + await loginPage.navigate(); + await loginPage.loginUser({ username: username, password: username }); + + await personalFiles.waitForPageLoad(); + await personalFiles.dataTable.getCellByColumnNameAndRowItem(parent, 'Size').dblclick(); + await page.waitForTimeout(timeouts.tiny); + }); + + test.afterAll(async () => { + const nodesApi = await NodesApi.initialize(username, username); + await nodesApi.deleteNodes([file3Id, file4Id, file5Id, file6Id, file7Id, file8Id, file9Id]); + }); + + test('[C286327] Share dialog default values', async ({ personalFiles }) => { + expect(await personalFiles.dataTable.performActionFromExpandableMenu(file3, 'Share')); + const labels = await personalFiles.shareDialog.getLabels(); + expect(await personalFiles.shareDialog.getDialogTitle()).toEqual(`Share ${file3}`); + expect(labels[0].trim()).toBe(`Share ${file3}`); + expect(await personalFiles.shareDialog.getInfoText()).toEqual('Share Link'); + expect(await personalFiles.shareDialog.getLinkUrl()).toContain(shareLinkPreUrl); + expect(await personalFiles.shareDialog.isUrlReadOnly()).toBe(true); + expect(await personalFiles.shareDialog.isShareToggleChecked()).toBe(true); + expect(labels[1].trim()).toBe('Link Expiry Date'); + expect(await personalFiles.shareDialog.isExpireToggleEnabled()).toBe(false); + expect(await personalFiles.shareDialog.isCloseEnabled()).toBe(true); + }); + + test('[C286329] Share a file', async ({ personalFiles, nodesApiAction }) => { + expect(await personalFiles.dataTable.performActionFromExpandableMenu(file3, 'Share')); + + const url = await personalFiles.shareDialog.getLinkUrl(); + await personalFiles.shareDialog.clickClose(); + + const sharedId = await nodesApiAction.getSharedId(file3Id); + expect(url).toContain(sharedId); + }); + + test('[C286330] Copy shared file URL', async ({ personalFiles, page }) => { + expect(await personalFiles.dataTable.performActionFromExpandableMenu(file4, 'Share')); + + const url = await personalFiles.shareDialog.getLinkUrl(); + expect(url).toContain(shareLinkPreUrl); + + await personalFiles.shareDialog.urlAction.click(); + + const shareSnackBar = personalFiles.snackBar; + await expect(shareSnackBar.getByMessageLocator('Link copied to the clipboard')).toBeVisible(); + + await page.goto(url); + await personalFiles.viewer.waitForViewerToOpen(); + + const downloadPromise = personalFiles.page.waitForEvent('download'); + await personalFiles.viewer.toolbar.sharedDownloadButton.click(); + + const download = await downloadPromise; + expect(download.suggestedFilename()).toBe(file4); + }); + + test('[C286332] Share a file with expiration date', async ({ personalFiles, nodesApiAction, page }) => { + expect(await personalFiles.dataTable.performActionFromExpandableMenu(file5, 'Share')); + + await personalFiles.shareDialog.expireToggle.click(); + expect(await personalFiles.shareDialog.isExpireToggleEnabled()).toBe(true); + + await personalFiles.shareDialog.datetimePickerButton.click(); + expect(await personalFiles.shareDialog.dateTimePicker.isCalendarOpen()).toBe(true); + + await personalFiles.shareDialog.dateTimePicker.pickDateTime(); + + const inputDate = await personalFiles.shareDialog.getExpireDate(); + + await page.waitForTimeout(timeouts.normal); + const expireDateProperty = await nodesApiAction.getSharedExpiryDate(file5Id); + + expect(Utils.formatDate(expireDateProperty)).toEqual(Utils.formatDate(inputDate)); + }); + + test('[C286337] Expire date is displayed correctly', async ({ personalFiles, nodesApiAction }) => { + expect(await personalFiles.dataTable.performActionFromExpandableMenu(file6, 'Share')); + const expireProperty = await nodesApiAction.getSharedExpiryDate(file6Id); + + expect(expireProperty).toEqual(expiryDate); + expect(await personalFiles.shareDialog.isExpireToggleEnabled()).toBe(true); + expect(Utils.formatDate(await personalFiles.shareDialog.getExpireDate())).toEqual(Utils.formatDate(expiryDate)); + }); + + test('[C286333] Disable the share link expiration', async ({ personalFiles, nodesApiAction, page }) => { + expect(await personalFiles.dataTable.performActionFromExpandableMenu(file7, 'Share')); + + expect(await personalFiles.shareDialog.isExpireToggleEnabled()).toBe(true); + expect(await personalFiles.shareDialog.getExpireDate()).not.toBe(''); + + await personalFiles.shareDialog.expireToggle.click(); + expect(await personalFiles.shareDialog.isExpireToggleEnabled()).toBe(false); + + await page.waitForTimeout(timeouts.tiny); + await personalFiles.shareDialog.clickClose(); + expect(await nodesApiAction.getSharedExpiryDate(file7Id)).toBe(''); + }); + + test('[C286335] Shared file URL is not changed when Share dialog is closed and opened again', async ({ personalFiles }) => { + expect(await personalFiles.dataTable.performActionFromExpandableMenu(file8, 'Share')); + + const url1 = await personalFiles.shareDialog.getLinkUrl(); + await personalFiles.shareDialog.clickClose(); + + await personalFiles.dataTable.selectItem(file8); + await personalFiles.acaHeader.shareButton.click(); + const url2 = await personalFiles.shareDialog.getLinkUrl(); + + expect(url1).toEqual(url2); + }); + + test('[C286345] Share a file from the context menu', async ({ personalFiles, nodesApiAction }) => { + expect(await personalFiles.dataTable.performActionFromExpandableMenu(file9, 'Share')); + + const url = await personalFiles.shareDialog.getLinkUrl(); + await personalFiles.shareDialog.clickClose(); + + const sharedId = await nodesApiAction.getSharedId(file9Id); + expect(await nodesApiAction.isFileShared(file9Id)).toBe(true); + expect(url).toContain(sharedId); + }); + }); + }); +}); diff --git a/e2e/playwright/list-views/src/tests/empty-list.spec.ts b/e2e/playwright/list-views/src/tests/empty-list.spec.ts new file mode 100755 index 0000000000..6a4236d0b4 --- /dev/null +++ b/e2e/playwright/list-views/src/tests/empty-list.spec.ts @@ -0,0 +1,78 @@ +/*! + * 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, LoginPage, Utils, test } from '@alfresco/playwright-shared'; + +test.describe('Empty list views', () => { + const username = `user-${Utils.random()}`; + + test.beforeAll(async () => { + const apiClientFactory = new ApiClientFactory(); + await apiClientFactory.setUpAcaBackend('admin'); + await apiClientFactory.createUser({ username }); + }); + + test.beforeEach(async ({ page }) => { + const loginPage = new LoginPage(page); + await loginPage.loginUser( + { username, password: username }, + { + withNavigation: true, + waitForLoading: true + } + ); + }); + + test('[C217099] empty My Libraries', async ({ myLibrariesPage }) => { + await myLibrariesPage.navigate(); + expect(await myLibrariesPage.dataTable.isEmpty(), 'list is not empty').toBe(true); + expect(await myLibrariesPage.dataTable.getEmptyStateTitle()).toContain(`You aren't a member of any File Libraries yet`); + expect(await myLibrariesPage.dataTable.getEmptyStateSubtitle()).toContain('Join libraries to upload, view, and share files.'); + }); + + test('[C280134] [C280120] Empty Trash - pagination controls not displayed', async ({ trashPage }) => { + await trashPage.navigate(); + expect(await trashPage.dataTable.isEmpty(), 'list is not empty').toBe(true); + expect(await trashPage.dataTable.getEmptyStateTitle()).toContain('Trash is empty'); + expect(await trashPage.dataTable.getEmptyListText()).toContain('Items you delete are moved to the Trash.'); + expect(await trashPage.dataTable.getEmptyListText()).toContain('Empty Trash to permanently delete items.'); + expect(await trashPage.pagination.isRangePresent(), 'Range is present').toBe(false); + expect(await trashPage.pagination.isMaxItemsPresent(), 'Max items is present').toBe(false); + }); + + test('[C290123] [C290031] Empty Search results - pagination controls not displayed', async ({ personalFiles, searchPage }) => { + await personalFiles.acaHeader.searchButton.click(); + await searchPage.searchInput.searchButton.click(); + await searchPage.searchOverlay.checkFilesAndFolders(); + await searchPage.searchOverlay.searchFor('InvalidText'); + await searchPage.reload({ waitUntil: 'domcontentloaded' }); + await searchPage.dataTable.spinnerWaitForReload(); + + expect(await personalFiles.pagination.isRangePresent(), 'Range is present').toBe(false); + expect(await personalFiles.pagination.isMaxItemsPresent(), 'Max items is present').toBe(false); + expect(await personalFiles.dataTable.isEmpty(), 'list is not empty').toBe(true); + expect(await personalFiles.dataTable.emptySearchText.innerText()).toContain('Your search returned 0 results'); + }); +}); diff --git a/e2e/playwright/list-views/src/tests/generic-errors.spec.ts b/e2e/playwright/list-views/src/tests/generic-errors.spec.ts new file mode 100755 index 0000000000..616726344b --- /dev/null +++ b/e2e/playwright/list-views/src/tests/generic-errors.spec.ts @@ -0,0 +1,99 @@ +/*! + * 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, LoginPage, NodesApi, TrashcanApi, Utils, test } from '@alfresco/playwright-shared'; + +test.describe('Generic errors', () => { + const username = `user-${Utils.random()}`; + const username2 = `user2-${Utils.random()}`; + let parentId: string; + let file1Id: string; + let file2Id: string; + let actionUser: NodesApi; + + test.beforeAll(async () => { + try { + const parent = `folder-${Utils.random()}`; + const file1 = `file1-${Utils.random()}.txt`; + const file2 = `file2-${Utils.random()}.txt`; + const apiClientFactory = new ApiClientFactory(); + await apiClientFactory.setUpAcaBackend('admin'); + await apiClientFactory.createUser({ username }); + await apiClientFactory.createUser({ username: username2 }); + + actionUser = await NodesApi.initialize(username, username); + parentId = (await actionUser.createFolder(parent)).entry.id; + file1Id = (await actionUser.createFile(file1, parentId)).entry.id; + file2Id = (await actionUser.createFile(file2, parentId)).entry.id; + } catch (error) { + console.error(`----- beforeAll failed : ${error}`); + } + }); + + test.beforeEach(async ({ page }) => { + const loginPage = new LoginPage(page); + await loginPage.loginUser( + { username, password: username }, + { + withNavigation: true, + waitForLoading: true + } + ); + }); + + test.afterAll(async () => { + actionUser = await NodesApi.initialize(username, username); + const trashcanApi = await TrashcanApi.initialize(username, username); + await actionUser.deleteNodeById(parentId); + await trashcanApi.emptyTrashcan(); + }); + + test('[C217313] File / folder not found', async ({ personalFiles }) => { + await actionUser.deleteNodeById(file1Id, false); + await personalFiles.navigate({ remoteUrl: `#/personal-files/${file1Id}` }); + + expect(await personalFiles.errorDialog.genericError.isVisible(), 'Generic error page not displayed').toBe(true); + expect(await personalFiles.errorDialog.genericErrorTitle.innerText()).toContain( + `This item no longer exists or you don't have permission to view it.` + ); + }); + + test('[C217314] Permission denied', async ({ personalFiles, loginPage }) => { + await loginPage.logoutUser(); + await loginPage.loginUser( + { username: username2, password: username2 }, + { + withNavigation: true, + waitForLoading: true + } + ); + await personalFiles.navigate({ remoteUrl: `#/personal-files/${file2Id}` }); + + expect(await personalFiles.errorDialog.genericError.isVisible(), 'Generic error page not displayed').toBe(true); + expect(await personalFiles.errorDialog.genericErrorTitle.innerText()).toContain( + `This item no longer exists or you don't have permission to view it.` + ); + }); +}); diff --git a/e2e/playwright/list-views/src/tests/permissions.spec.ts b/e2e/playwright/list-views/src/tests/permissions.spec.ts new file mode 100755 index 0000000000..5f610bb889 --- /dev/null +++ b/e2e/playwright/list-views/src/tests/permissions.spec.ts @@ -0,0 +1,205 @@ +/*! + * 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, + FavoritesPageApi, + FileActionsApi, + LoginPage, + NodesApi, + SharedLinksApi, + SitesApi, + Utils, + test, + timeouts +} from '@alfresco/playwright-shared'; +import { Site } from '@alfresco/js-api'; + +test.describe('Special permissions', () => { + const username = `userPermissions-${Utils.random()}`; + let siteApiAdmin: SitesApi; + test.beforeAll(async () => { + const apiClientFactory = new ApiClientFactory(); + await apiClientFactory.setUpAcaBackend('admin'); + await apiClientFactory.createUser({ username }); + }); + + test.describe('file not displayed if user no longer has permissions on it', () => { + const sitePrivate = `private-${Utils.random()}`; + const fileName = `file-${Utils.random()}.txt`; + + test.beforeAll(async () => { + test.setTimeout(timeouts.webServer); + const userFavoritesApi = await FavoritesPageApi.initialize(username, username); + const userFileActionApi = await FileActionsApi.initialize(username, username); + siteApiAdmin = await SitesApi.initialize('admin'); + const nodeApiAdmin = await NodesApi.initialize('admin'); + const shareApiAdmin = await SharedLinksApi.initialize('admin'); + const shareApiUser = await SharedLinksApi.initialize(username, username); + + await siteApiAdmin.createSite(sitePrivate, Site.VisibilityEnum.PRIVATE); + await siteApiAdmin.addSiteMember(sitePrivate, username, Site.RoleEnum.SiteCollaborator); + const docLibId = await siteApiAdmin.getDocLibId(sitePrivate); + const fileId = (await nodeApiAdmin.createFile(fileName, docLibId)).entry.id; + await shareApiAdmin.shareFileById(fileId); + await userFavoritesApi.addFavoriteById('file', fileId); + + await userFileActionApi.updateNodeContent(fileId, 'edited by user'); + + await userFileActionApi.waitForNodes(username, { expect: 1 }); + + await shareApiAdmin.waitForFilesToBeShared([fileId]); + await shareApiUser.waitForFilesToBeShared([fileId]); + }); + + test.beforeEach(async ({ page }) => { + const loginPage = new LoginPage(page); + await loginPage.loginUser( + { username, password: username }, + { + withNavigation: true, + waitForLoading: true + } + ); + }); + + test.afterEach(async () => { + await siteApiAdmin.addSiteMember(sitePrivate, username, Site.RoleEnum.SiteCollaborator); + }); + + test.afterAll(async () => { + await siteApiAdmin.deleteSites([sitePrivate]); + }); + + test('[C213173] on Recent Files', async ({ recentFilesPage }) => { + await recentFilesPage.navigate(); + expect(await recentFilesPage.dataTable.getRowsCount(), 'Incorrect number of items').toBe(2); + await siteApiAdmin.deleteSiteMember(sitePrivate, username); + await recentFilesPage.reload(); + expect(await recentFilesPage.dataTable.isEmpty(), 'Items are still displayed').toBe(true); + }); + + test('[C213227] on Favorites', async ({ favoritePage }) => { + await favoritePage.navigate(); + expect(await favoritePage.dataTable.getRowsCount(), 'Incorrect number of items').toBe(2); + await siteApiAdmin.deleteSiteMember(sitePrivate, username); + await favoritePage.reload(); + expect(await favoritePage.dataTable.isEmpty(), 'Items are still displayed').toBe(true); + }); + + test('[C213116] on Shared Files', async ({ sharedPage }) => { + await sharedPage.navigate(); + expect(await sharedPage.dataTable.getRowsCount(), 'Incorrect number of items').toBe(2); + await siteApiAdmin.deleteSiteMember(sitePrivate, username); + await sharedPage.reload(); + expect(await sharedPage.dataTable.getRowsCount(), 'Incorrect number of items').toBe(0); + }); + + test('[C290122] on Search Results', async ({ personalFiles, searchPage }) => { + await personalFiles.acaHeader.searchButton.click(); + await searchPage.searchInput.searchButton.click(); + await searchPage.searchOverlay.checkFilesAndFolders(); + await searchPage.searchOverlay.searchFor(fileName); + await searchPage.dataTable.spinnerWaitForReload(); + + expect(await searchPage.dataTable.getRowsCount(), 'Incorrect number of items').toBe(2); + + await siteApiAdmin.deleteSiteMember(sitePrivate, username); + + await searchPage.reload(); + await searchPage.dataTable.spinnerWaitForReload(); + + expect(await searchPage.dataTable.getRowsCount(), 'Incorrect number of items').toBe(0); + }); + }); + + test.describe(`Location column is empty if user doesn't have permissions on the file's parent folder`, () => { + const sitePrivate = `private-${Utils.random()}`; + const fileName = `file-${Utils.random()}.txt`; + let adminSiteApiActions: SitesApi; + + test.beforeAll(async () => { + test.setTimeout(timeouts.webServer); + const userFavoritesApi = await FavoritesPageApi.initialize(username, username); + const userShareActionApi = await SharedLinksApi.initialize(username, username); + const userNodeActionApi = await NodesApi.initialize(username, username); + adminSiteApiActions = await SitesApi.initialize('admin'); + + await adminSiteApiActions.createSite(sitePrivate, Site.VisibilityEnum.PRIVATE); + await adminSiteApiActions.addSiteMember(sitePrivate, username, Site.RoleEnum.SiteCollaborator); + const docLibId = await adminSiteApiActions.getDocLibId(sitePrivate); + const fileId = (await userNodeActionApi.createFile(fileName, docLibId)).entry.id; + await userFavoritesApi.addFavoriteById('file', fileId); + + await userShareActionApi.shareFileById(fileId); + await userShareActionApi.waitForFilesToBeShared([fileId]); + await adminSiteApiActions.deleteSiteMember(sitePrivate, username); + }); + + test.beforeEach(async ({ page }) => { + const loginPage = new LoginPage(page); + await loginPage.loginUser( + { username, password: username }, + { + withNavigation: true, + waitForLoading: true + } + ); + }); + + test.afterAll(async () => { + await adminSiteApiActions.deleteSites([sitePrivate]); + }); + + test('[C213178] on Recent Files', async ({ recentFilesPage }) => { + await recentFilesPage.navigate(); + expect(await recentFilesPage.dataTable.getRowsCount(), 'Incorrect number of items').toBe(2); + expect(await recentFilesPage.dataTable.getItemLocationText(fileName)).toEqual('Unknown'); + }); + + test('[C213672] on Favorites', async ({ favoritePage }) => { + await favoritePage.navigate(); + expect(await favoritePage.dataTable.getRowsCount(), 'Incorrect number of items').toBe(2); + expect(await favoritePage.dataTable.getItemLocationText(fileName)).toEqual('Unknown'); + }); + + test(`[C213668] on Shared Files`, async ({ sharedPage }) => { + await sharedPage.navigate(); + expect(await sharedPage.dataTable.getRowsCount(), 'Incorrect number of items').toBe(2); + expect(await sharedPage.dataTable.getItemLocationText(fileName)).toEqual('Unknown'); + }); + + test('[C306868] on Search results', async ({ personalFiles, searchPage }) => { + await personalFiles.acaHeader.searchButton.click(); + await searchPage.searchInput.searchButton.click(); + await searchPage.searchOverlay.checkFilesAndFolders(); + await searchPage.searchOverlay.searchFor(fileName); + await searchPage.dataTable.spinnerWaitForReload(); + + expect(await searchPage.dataTable.getRowsCount(), 'Incorrect number of items').toBe(2); + expect(await searchPage.dataTable.getItemLocationText(fileName)).toEqual('Unknown'); + }); + }); +}); diff --git a/e2e/playwright/list-views/src/tests/sort-list.spec.ts b/e2e/playwright/list-views/src/tests/sort-list.spec.ts new file mode 100644 index 0000000000..ac61d43280 --- /dev/null +++ b/e2e/playwright/list-views/src/tests/sort-list.spec.ts @@ -0,0 +1,265 @@ +/*! + * 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, + FavoritesPageApi, + FileActionsApi, + NodesApi, + PersonalFilesPage, + TEST_FILES, + Utils, + test, + timeouts +} from '@alfresco/playwright-shared'; + +async function getSortState(myPersonalFiles: PersonalFilesPage): Promise<{ [key: string]: string }> { + return { + sortingColumn: await myPersonalFiles.dataTable.getSortedColumnHeaderText(), + sortingOrder: await myPersonalFiles.dataTable.getSortingOrder(), + firstElement: await myPersonalFiles.dataTable.getFirstElementDetail('Name') + }; +} + +test.describe('Remember sorting', () => { + const user1 = `userSort1-${Utils.random()}`; + const user2 = `userSort2-${Utils.random()}`; + const pdfFileNames = [...new Array(14).fill(100)].map((v, i) => `file-${v + i}.pdf`); + const jpgFileNames = [...new Array(12).fill(114)].map((v, i) => `file-${v + i}.jpg`); + const folderToMove = `folder1`; + const folderToContain = `folder2`; + const uiCreatedFolder = `folder3`; + + const testData = { + user1: { + files: { + jpg: jpgFileNames, + pdf: pdfFileNames + } + }, + user2: { + files: [pdfFileNames[0], jpgFileNames[0]] + } + }; + + let initialSortState: { [key: string]: string }; + let nodeActionUser1: NodesApi; + + test.beforeAll(async () => { + test.setTimeout(timeouts.extendedTest); + const apiClientFactory = new ApiClientFactory(); + await apiClientFactory.setUpAcaBackend('admin'); + await apiClientFactory.createUser({ username: user1 }); + await apiClientFactory.createUser({ username: user2 }); + const fileActionUser1 = await FileActionsApi.initialize(user1, user1); + const fileActionUser2 = await FileActionsApi.initialize(user2, user2); + const favoritesActions = await FavoritesPageApi.initialize(user1, user1); + nodeActionUser1 = await NodesApi.initialize(user1, user1); + const filesIdsUser1: { [key: string]: string } = {}; + const filesIdsUser2: { [key: string]: string } = {}; + await Promise.all( + testData.user1.files.pdf.map( + async (i) => (filesIdsUser1[i] = (await fileActionUser1.uploadFileWithRename(TEST_FILES.PDF.path, i, '-my-')).entry.id) + ) + ); + await Promise.all( + testData.user1.files.jpg.map( + async (i) => (filesIdsUser1[i] = (await fileActionUser1.uploadFileWithRename(TEST_FILES.JPG_FILE.path, i, '-my-')).entry.id) + ) + ); + await Promise.all( + testData.user2.files.map( + async (i) => (filesIdsUser2[i] = (await fileActionUser2.uploadFileWithRename(TEST_FILES.PDF.path, i, '-my-')).entry.id) + ) + ); + await favoritesActions.addFavoritesByIds('file', [filesIdsUser1[pdfFileNames[0]], filesIdsUser1[pdfFileNames[1]]]); + }); + + test.beforeEach(async ({ loginPage, personalFiles }) => { + await NodesApi.initialize(user1, user1); + await loginPage.loginUser( + { username: user1, password: user1 }, + { + withNavigation: true, + waitForLoading: true + } + ); + await personalFiles.dataTable.sortBy('Name', 'asc'); + await personalFiles.dataTable.spinnerWaitForReload(); + initialSortState = await getSortState(personalFiles); + }); + + test.afterAll(async () => { + nodeActionUser1 = await NodesApi.initialize(user1, user1); + const nodeActionUser2 = await NodesApi.initialize(user2, user2); + await nodeActionUser1.deleteCurrentUserNodes(); + await nodeActionUser2.deleteCurrentUserNodes(); + }); + + test('[C261136] Sort order is retained when navigating to another part of the app', async ({ personalFiles, favoritePage }) => { + await personalFiles.dataTable.sortBy('Name', 'desc'); + await personalFiles.dataTable.spinnerWaitForReload(); + + const expectedSortData = await getSortState(personalFiles); + expect(expectedSortData).not.toEqual(initialSortState); + + await favoritePage.navigate(); + await personalFiles.navigate(); + + const actualSortData = await getSortState(personalFiles); + expect(actualSortData).toEqual(expectedSortData); + }); + + test('[C589205] Size sort order is retained after viewing a file and closing the viewer', async ({ personalFiles }) => { + await personalFiles.dataTable.sortBy('Size', 'desc'); + await personalFiles.dataTable.spinnerWaitForReload(); + const expectedSortData = await getSortState(personalFiles); + + await personalFiles.dataTable.performClickFolderOrFileToOpen(expectedSortData.firstElement); + await personalFiles.viewer.closeButtonLocator.click(); + await personalFiles.waitForPageLoad(); + + const actualSortData = await getSortState(personalFiles); + expect(actualSortData).toEqual(expectedSortData); + }); + + test('[C261153] Sort order should be remembered separately on each list view', async ({ personalFiles, favoritePage }) => { + await personalFiles.dataTable.sortBy('Size', 'desc'); + await personalFiles.dataTable.spinnerWaitForReload(); + const personalFilesSortData = await getSortState(personalFiles); + + await favoritePage.navigate(); + await favoritePage.dataTable.sortBy('Name', 'asc'); + await favoritePage.dataTable.spinnerWaitForReload(); + + const favouritesSortData = await getSortState(personalFiles); + expect(favouritesSortData).not.toEqual(personalFilesSortData); + + await personalFiles.navigate(); + const personalFilesSortDataAfterFavSort = await getSortState(personalFiles); + expect(personalFilesSortDataAfterFavSort).toEqual(personalFilesSortData); + }); + + test('[C261147] Sort order is retained when user changes the page from pagination', async ({ personalFiles }) => { + const lastFileInArray = testData.user1.files.jpg.slice(-1).pop(); + const firstFileInArray = testData.user1.files.pdf[0]; + + await personalFiles.pagination.clickOnNextPage(); + await personalFiles.dataTable.spinnerWaitForReload(); + + let expectedPersonalFilesSortDataPage2 = { + sortingColumn: 'Name', + sortingOrder: 'asc', + firstElement: lastFileInArray + }; + + let currentPersonalFilesSortDataPage2 = await getSortState(personalFiles); + expect(currentPersonalFilesSortDataPage2).toEqual(expectedPersonalFilesSortDataPage2); + + await personalFiles.dataTable.sortBy('Name', 'desc'); + await personalFiles.dataTable.spinnerWaitForReload(); + expectedPersonalFilesSortDataPage2 = { + sortingColumn: 'Name', + sortingOrder: 'desc', + firstElement: firstFileInArray + }; + + currentPersonalFilesSortDataPage2 = await getSortState(personalFiles); + expect(expectedPersonalFilesSortDataPage2).toEqual(currentPersonalFilesSortDataPage2); + }); + + test.describe('Folder actions', () => { + test.beforeAll(async () => { + const folderIds: { [key: string]: string } = {}; + folderIds[folderToContain] = (await nodeActionUser1.createFolder(folderToContain)).entry.id; + folderIds[folderToMove] = (await nodeActionUser1.createFolder(folderToMove)).entry.id; + }); + + test('[C261138] Sort order is retained when creating a new folder', async ({ personalFiles }) => { + await personalFiles.dataTable.sortBy('Name', 'desc'); + await personalFiles.dataTable.spinnerWaitForReload(); + + const expectedSortData = { + sortingColumn: await personalFiles.dataTable.getSortedColumnHeaderText(), + sortingOrder: await personalFiles.dataTable.getSortingOrder(), + firstElement: uiCreatedFolder + }; + + await personalFiles.selectCreateFolder(); + await personalFiles.folderDialog.createNewFolderDialog(uiCreatedFolder); + await personalFiles.dataTable.isItemPresent(uiCreatedFolder); + + const actualSortData = await getSortState(personalFiles); + expect(actualSortData).toEqual(expectedSortData); + }); + + test('[C261139] Sort order is retained when moving a file', async ({ personalFiles }) => { + const expectedSortData = { + sortingColumn: await personalFiles.dataTable.getSortedColumnHeaderText(), + sortingOrder: await personalFiles.dataTable.getSortingOrder(), + firstElement: folderToContain + }; + await personalFiles.copyOrMoveContentInDatatable([folderToMove], folderToContain, 'Move'); + await personalFiles.dataTable.spinnerWaitForReload(); + const actualSortData = await getSortState(personalFiles); + expect(actualSortData).toEqual(expectedSortData); + }); + }); + + test.describe('User Tests', () => { + test('[C261137] Size sort order is retained when user logs out and logs back in', async ({ personalFiles, loginPage }) => { + await personalFiles.dataTable.sortBy('Name', 'desc'); + await personalFiles.dataTable.spinnerWaitForReload(); + const expectedSortData = await getSortState(personalFiles); + expect(expectedSortData).not.toEqual(initialSortState); + + await loginPage.logoutUser(); + await FileActionsApi.initialize(user1, user1); + await loginPage.loginUser({ username: user1, password: user1 }, { withNavigation: true, waitForLoading: true }); + + const actualSortData = await getSortState(personalFiles); + expect(actualSortData).toEqual(expectedSortData); + }); + + test('[C261150] Sort order is not retained between different users', async ({ personalFiles, loginPage }) => { + await personalFiles.dataTable.sortBy('Size', 'asc'); + await personalFiles.dataTable.spinnerWaitForReload(); + const expectedSortData = await getSortState(personalFiles); + + await loginPage.logoutUser(); + await FileActionsApi.initialize(user2, user2); + await loginPage.loginUser( + { username: user2, password: user2 }, + { + withNavigation: true, + waitForLoading: true + } + ); + + const actualSortData = await getSortState(personalFiles); + expect(actualSortData).not.toEqual(expectedSortData); + }); + }); +}); diff --git a/e2e/playwright/list-views/src/tests/trash-admin.spec.ts b/e2e/playwright/list-views/src/tests/trash-admin.spec.ts new file mode 100755 index 0000000000..b84eda52e1 --- /dev/null +++ b/e2e/playwright/list-views/src/tests/trash-admin.spec.ts @@ -0,0 +1,63 @@ +/*! + * 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, NodesApi, Utils, getUserState, test } from '@alfresco/playwright-shared'; + +test.use({ storageState: getUserState('admin') }); +test.describe('Trash admin', () => { + const folderAdmin = `deleteFolder-${Utils.random()}`; + let folderAdminId: string; + let adminApiActions: NodesApi; + + test.beforeAll(async () => { + try { + const apiClientFactory = new ApiClientFactory(); + await apiClientFactory.setUpAcaBackend('admin'); + adminApiActions = await NodesApi.initialize('admin'); + folderAdminId = (await adminApiActions.createFolder(folderAdmin)).entry.id; + await adminApiActions.deleteNodeById(folderAdminId, false); + } catch (error) { + console.error(`----- beforeAll failed : ${error}`); + } + }); + + test.afterAll(async () => { + try { + await adminApiActions.deleteDeletedNode(folderAdminId); + } catch (error) { + console.error(`----- afterAll failed : ${error}`); + } + }); + + test.describe('as admin', () => { + test('[C213217] has the correct columns', async ({ trashPage }) => { + await trashPage.navigate(); + const expectedColumns = ['Name', 'Location', 'Size', 'Deleted', 'Deleted by']; + const actualColumns = await trashPage.dataTable.getColumnHeaders(); + + expect(actualColumns).toEqual(expectedColumns); + }); + }); +}); diff --git a/e2e/protractor/protractor.excludes.json b/e2e/protractor/protractor.excludes.json index c96b1052fa..3e61b1fd1b 100644 --- a/e2e/protractor/protractor.excludes.json +++ b/e2e/protractor/protractor.excludes.json @@ -1,5 +1,4 @@ { - "C589205": "https://alfresco.atlassian.net/browse/ACA-4353", "C261153": "https://alfresco.atlassian.net/browse/AAE-7517", "C306959": "https://alfresco.atlassian.net/browse/ACA-4620", "C213134": "temp, see https://alfresco.atlassian.net/browse/ACS-5189", diff --git a/e2e/protractor/suites/actions/share/share-file.test.ts b/e2e/protractor/suites/actions/share/share-file.test.ts deleted file mode 100755 index ea6a4dd41c..0000000000 --- a/e2e/protractor/suites/actions/share/share-file.test.ts +++ /dev/null @@ -1,268 +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 { AdminActions, UserActions, LoginPage, BrowsingPage, RepoClient, ShareDialog, Viewer, Utils } from '@alfresco/aca-testing-shared'; -import { BrowserActions } from '@alfresco/adf-testing'; - -describe('Share a file', () => { - const username = `user-${Utils.random()}`; - const parent = `parent-${Utils.random()}`; - let parentId: string; - - const file3 = `file3-${Utils.random()}.txt`; - const file4 = `file4-${Utils.random()}.txt`; - const file5 = `file5-${Utils.random()}.txt`; - const file6 = `file6-${Utils.random()}.txt`; - const file7 = `file7-${Utils.random()}.txt`; - const file8 = `file8-${Utils.random()}.txt`; - const file9 = `file9-${Utils.random()}.txt`; - - const viewer = new Viewer(); - const page = new BrowsingPage(); - const { dataTable, toolbar } = page; - const shareLinkPreUrl = `/#/preview/s/`; - - const apis = { - user: new RepoClient(username, username) - }; - - const adminApiActions = new AdminActions(); - const userActions = new UserActions(); - - beforeAll(async () => { - await adminApiActions.createUser({ username }); - await userActions.login(username, username); - - parentId = await apis.user.createFolder(parent); - }); - - afterAll(async () => { - await userActions.deleteNodes([parentId]); - }); - - describe('when logged out', () => { - let file6SharedLink: string; - let file6Id: string; - - beforeAll(async () => { - file6Id = await apis.user.createFile(file6, parentId); - - const sharedId = (await apis.user.shared.shareFileById(file6Id)).entry.id; - file6SharedLink = `${shareLinkPreUrl}${sharedId}`; - await apis.user.shared.waitForFilesToBeShared([file6Id]); - }); - - afterAll(async () => { - await userActions.deleteNodes([file6Id]); - }); - - it('[C286326] A non-logged user can download the shared file from the viewer', async () => { - await browser.get(file6SharedLink); - await viewer.waitForTxtViewerToLoad(); - - expect(await viewer.getFileTitle()).toEqual(file6); - - await BrowserActions.click(viewer.toolbar.downloadButton); - expect(await Utils.fileExistsOnOS(file6)).toBe(true, 'File not found in download location'); - }); - }); - - describe('when logged in', () => { - const expiryDateObj: Date = new Date(); - expiryDateObj.setFullYear(expiryDateObj.getFullYear() + 1); - const expiryDate: any = expiryDateObj.toISOString().replace('Z', '+0000'); - - const loginPage = new LoginPage(); - const shareDialog = new ShareDialog(); - const contextMenu = dataTable.menu; - - beforeAll(async () => { - await loginPage.loginWith(username); - }); - - describe('from Personal Files', () => { - let file3Id: string; - let file4Id: string; - let file5Id: string; - let file6Id: string; - let file7Id: string; - let file8Id: string; - let file9Id: string; - - beforeAll(async () => { - file3Id = await apis.user.createFile(file3, parentId); - file4Id = await apis.user.createFile(file4, parentId); - file5Id = await apis.user.createFile(file5, parentId); - file6Id = await apis.user.createFile(file6, parentId); - file7Id = await apis.user.createFile(file7, parentId); - file8Id = await apis.user.createFile(file8, parentId); - file9Id = await apis.user.createFile(file9, parentId); - - await userActions.login(username, username); - await userActions.shareNodes([file6Id, file7Id], expiryDate); - await apis.user.shared.waitForFilesToBeShared([file6Id, file7Id]); - }); - - beforeEach(async () => { - await page.clickPersonalFilesAndWait(); - await dataTable.doubleClickOnRowByName(parent); - await dataTable.waitForHeader(); - }); - - afterEach(async () => { - await Utils.pressEscape(); - await page.closeOpenDialogs(); - }); - - afterAll(async () => { - await userActions.deleteNodes([file3Id, file4Id, file5Id, file6Id, file7Id, file8Id, file9Id]); - }); - - it('[C286327] Share dialog default values', async () => { - await dataTable.selectItem(file3); - await toolbar.shareButton.click(); - await shareDialog.waitForDialogToOpen(); - - expect(await shareDialog.getDialogTitle()).toEqual(`Share ${file3}`); - expect(await shareDialog.getInfoText()).toEqual('Share Link'); - expect(await shareDialog.labels.get(0).getText()).toEqual(`Share ${file3}`); - expect(await shareDialog.getLinkUrl()).toContain(shareLinkPreUrl); - expect(await shareDialog.isUrlReadOnly()).toBe(true, 'url is not readonly'); - expect(await shareDialog.isShareToggleChecked()).toBe(true, 'Share toggle not checked'); - expect(await shareDialog.labels.get(1).getText()).toEqual('Link Expiry Date'); - expect(await shareDialog.isExpireToggleEnabled()).toBe(false, 'Expire toggle is checked'); - expect(await shareDialog.isCloseEnabled()).toBe(true, 'Close button is not enabled'); - }); - - it('[C286329] Share a file', async () => { - await dataTable.selectItem(file3); - await toolbar.shareButton.click(); - await shareDialog.waitForDialogToOpen(); - - const url = await shareDialog.getLinkUrl(); - await shareDialog.clickClose(); - - const sharedId = await apis.user.nodes.getSharedId(file3Id); - expect(url).toContain(sharedId); - }); - - it('[C286330] Copy shared file URL', async () => { - await dataTable.selectItem(file4); - await toolbar.shareButton.click(); - await shareDialog.waitForDialogToOpen(); - - const url = await shareDialog.getLinkUrl(); - expect(url).toContain(shareLinkPreUrl); - - await BrowserActions.click(shareDialog.urlAction); - expect(await page.getSnackBarMessage()).toBe('Link copied to the clipboard'); - - await browser.get(url); - - await viewer.waitForViewerToOpen(); - expect(await viewer.getFileTitle()).toEqual(file4); - - await page.load(); - }); - - it('[C286332] Share a file with expiration date', async () => { - await dataTable.selectItem(file5); - await toolbar.shareButton.click(); - await shareDialog.waitForDialogToOpen(); - - await BrowserActions.click(shareDialog.expireToggle); - expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expire toggle not checked'); - - await BrowserActions.click(shareDialog.datetimePickerButton); - expect(await shareDialog.dateTimePicker.isCalendarOpen()).toBe(true, 'Calendar not opened'); - await shareDialog.dateTimePicker.pickDateTime(); - await shareDialog.dateTimePicker.waitForDateTimePickerToClose(); - - const inputDate = await shareDialog.getExpireDate(); - const expireDateProperty = await apis.user.nodes.getSharedExpiryDate(file5Id); - - expect(Utils.formatDate(expireDateProperty)).toEqual(Utils.formatDate(inputDate)); - }); - - it('[C286337] Expire date is displayed correctly', async () => { - await dataTable.selectItem(file6); - await BrowserActions.click(toolbar.shareButton); - await shareDialog.waitForDialogToOpen(); - - const expireProperty = await apis.user.nodes.getSharedExpiryDate(file6Id); - expect(expireProperty).toEqual(expiryDate); - expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); - expect(Utils.formatDate(await shareDialog.getExpireDate())).toEqual(Utils.formatDate(expiryDate)); - }); - - it('[C286333] Disable the share link expiration', async () => { - await dataTable.selectItem(file7); - await BrowserActions.click(toolbar.shareButton); - await shareDialog.waitForDialogToOpen(); - - expect(await shareDialog.isExpireToggleEnabled()).toBe(true, 'Expiration is not checked'); - expect(await shareDialog.getExpireDate()).not.toBe('', 'Expire date input is empty'); - - await BrowserActions.click(shareDialog.expireToggle); - expect(await shareDialog.isExpireToggleEnabled()).toBe(false, 'Expiration is checked'); - expect(await shareDialog.expireInput.isDisplayed()).toBe(false, 'Expire date input is not empty'); - - await shareDialog.clickClose(); - expect(await apis.user.nodes.getSharedExpiryDate(file7Id)).toBe('', `${file7} link still has expiration`); - }); - - it('[C286335] Shared file URL is not changed when Share dialog is closed and opened again', async () => { - await dataTable.selectItem(file8); - await toolbar.shareButton.click(); - await shareDialog.waitForDialogToOpen(); - - const url1 = await shareDialog.getLinkUrl(); - await shareDialog.clickClose(); - - await page.dataTable.clearSelection(); - await dataTable.selectItem(file8); - await BrowserActions.click(toolbar.shareButton); - await shareDialog.waitForDialogToOpen(); - const url2 = await shareDialog.getLinkUrl(); - - expect(url1).toEqual(url2); - }); - - it('[C286345] Share a file from the context menu', async () => { - await dataTable.rightClickOnItem(file9); - await contextMenu.waitForMenuToOpen(); - await contextMenu.shareAction.click(); - await shareDialog.waitForDialogToOpen(); - - const url = await shareDialog.getLinkUrl(); - await shareDialog.clickClose(); - - const sharedId = await apis.user.nodes.getSharedId(file9Id); - expect(await apis.user.nodes.isFileShared(file9Id)).toBe(true, `${file9} is not shared`); - expect(url).toContain(sharedId); - }); - }); - }); -}); diff --git a/e2e/protractor/suites/list-views/empty-list.test.ts b/e2e/protractor/suites/list-views/empty-list.test.ts index cdcc0a09d2..e620c61cb4 100755 --- a/e2e/protractor/suites/list-views/empty-list.test.ts +++ b/e2e/protractor/suites/list-views/empty-list.test.ts @@ -37,13 +37,6 @@ describe('Empty list views', () => { await loginPage.loginWith(username); }); - it('[C217099] empty My Libraries', async () => { - await page.goToMyLibraries(); - expect(await dataTable.isEmpty()).toBe(true, 'list is not empty'); - expect(await dataTable.getEmptyStateTitle()).toContain(`You aren't a member of any File Libraries yet`); - expect(await dataTable.getEmptyStateSubtitle()).toContain('Join libraries to upload, view, and share files.'); - }); - it('[C289911] empty Favorite Libraries', async () => { await page.goToFavoriteLibraries(); expect(await dataTable.isEmpty()).toBe(true, 'list is not empty'); @@ -65,14 +58,6 @@ describe('Empty list views', () => { expect(await dataTable.getEmptyStateSubtitle()).toContain('Favorite items that you want to easily find later.'); }); - it('[C280134] empty Trash', async () => { - await page.clickTrash(); - expect(await dataTable.isEmpty()).toBe(true, 'list is not empty'); - expect(await dataTable.getEmptyStateTitle()).toContain('Trash is empty'); - expect(await dataTable.getEmptyListText()).toContain('Items you delete are moved to the Trash.'); - expect(await dataTable.getEmptyListText()).toContain('Empty Trash to permanently delete items.'); - }); - it('[C280111] Favorites - pagination controls not displayed', async () => { await page.clickFavorites(); expect(await pagination.isRangePresent()).toBe(false, 'Range is present'); @@ -133,21 +118,6 @@ describe('Empty list views', () => { expect(await pagination.isNextButtonPresent()).toBe(false, 'Next button is present'); }); - it('[C290123] Search results - pagination controls not displayed', async () => { - await toolbar.clickSearchIconButton(); - await searchInput.clickSearchButton(); - /* cspell:disable-next-line */ - await searchInput.searchFor('qwertyuiop'); - await dataTable.waitForBody(); - - expect(await pagination.isRangePresent()).toBe(false, 'Range is present'); - expect(await pagination.isMaxItemsPresent()).toBe(false, 'Max items is present'); - expect(await pagination.isCurrentPagePresent()).toBe(false, 'Current page is present'); - expect(await pagination.isTotalPagesPresent()).toBe(false, 'Total pages is present'); - expect(await pagination.isPreviousButtonPresent()).toBe(false, 'Previous button is present'); - expect(await pagination.isNextButtonPresent()).toBe(false, 'Next button is present'); - }); - it('[C290020] Empty Search results - Libraries', async () => { await page.goToMyLibraries(); await toolbar.clickSearchIconButton(); @@ -160,17 +130,4 @@ describe('Empty list views', () => { expect(await dataTable.isEmpty()).toBe(true, 'list is not empty'); expect(await dataTable.emptySearchText.getText()).toContain('Your search returned 0 results'); }); - - it('[C290031] Empty Search results - Files / Folders', async () => { - await page.clickPersonalFiles(); - await toolbar.clickSearchIconButton(); - await searchInput.clickSearchButton(); - await searchInput.checkFilesAndFolders(); - /* cspell:disable-next-line */ - await searchInput.searchFor('qwertyuiop'); - await dataTable.waitForBody(); - - expect(await dataTable.isEmpty()).toBe(true, 'list is not empty'); - expect(await dataTable.emptySearchText.getText()).toContain('Your search returned 0 results'); - }); }); diff --git a/e2e/protractor/suites/list-views/generic-errors.test.ts b/e2e/protractor/suites/list-views/generic-errors.test.ts deleted file mode 100755 index 71b6eead7b..0000000000 --- a/e2e/protractor/suites/list-views/generic-errors.test.ts +++ /dev/null @@ -1,104 +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 { AdminActions, UserActions, LoginPage, BrowsingPage, Utils, RepoClient } from '@alfresco/aca-testing-shared'; -import { Logger } from '@alfresco/adf-testing'; - -describe('Generic errors', () => { - const username = `user-${Utils.random()}`; - const username2 = `user2-${Utils.random()}`; - - const parent = `folder-${Utils.random()}`; - let parentId: string; - const file1 = `file1-${Utils.random()}.txt`; - let file1Id: string; - const file2 = `file2-${Utils.random()}.txt`; - - const apis = { - user: new RepoClient(username, username) - }; - - const loginPage = new LoginPage(); - const page = new BrowsingPage(); - const { dataTable } = page; - - const adminApiActions = new AdminActions(); - const userActions = new UserActions(); - - beforeAll(async () => { - try { - await adminApiActions.createUser({ username }); - await adminApiActions.createUser({ username: username2 }); - await userActions.login(username, username); - - parentId = await apis.user.createFolder(parent); - file1Id = await apis.user.createFile(file1, parentId); - await apis.user.nodes.createFile(file2, parentId); - - await loginPage.loginWith(username); - } catch (error) { - Logger.error(`----- beforeAll failed : ${error}`); - } - }); - - afterAll(async () => { - await userActions.login(username, username); - await userActions.deleteNodes([parentId]); - await userActions.emptyTrashcan(); - }); - - it('[C217313] File / folder not found', async () => { - await page.clickPersonalFilesAndWait(); - await dataTable.doubleClickOnRowByName(parent); - await dataTable.doubleClickOnRowByName(file1); - const URL = await browser.getCurrentUrl(); - await userActions.deleteNodes([file1Id], false); - await browser.get(URL); - - expect(await page.genericError.isDisplayed()).toBe(true, 'Generic error page not displayed'); - expect(await page.genericErrorTitle.getText()).toContain(`This item no longer exists or you don't have permission to view it.`); - }); - - it('[C217315] Invalid URL', async () => { - await page.load('/invalid page'); - - expect(await page.genericError.isDisplayed()).toBe(true, 'Generic error page not displayed'); - expect(await page.genericErrorTitle.getText()).toContain(`This item no longer exists or you don't have permission to view it.`); - }); - - it('[C217314] Permission denied', async () => { - await page.clickPersonalFilesAndWait(); - await dataTable.doubleClickOnRowByName(parent); - await dataTable.doubleClickOnRowByName(file2); - const URL = await browser.getCurrentUrl(); - await loginPage.loginWith(username2); - await browser.get(URL); - - expect(await page.genericError.isDisplayed()).toBe(true, 'Generic error page not displayed'); - expect(await page.genericErrorTitle.getText()).toContain(`This item no longer exists or you don't have permission to view it.`); - - await loginPage.loginWith(username); - }); -}); diff --git a/e2e/protractor/suites/list-views/permissions.test.ts b/e2e/protractor/suites/list-views/permissions.test.ts deleted file mode 100755 index b79ed7001b..0000000000 --- a/e2e/protractor/suites/list-views/permissions.test.ts +++ /dev/null @@ -1,177 +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, UserActions, SITE_VISIBILITY, SITE_ROLES, LoginPage, BrowsingPage, Utils, RepoClient } from '@alfresco/aca-testing-shared'; - -describe('Special permissions', () => { - const username = `user-${Utils.random()}`; - - const apis = { - user: new RepoClient(username, username) - }; - - const loginPage = new LoginPage(); - const page = new BrowsingPage(); - const { dataTable, toolbar } = page; - const { searchInput } = page.pageLayoutHeader; - - const adminApiActions = new AdminActions(); - const userActions = new UserActions(); - - beforeAll(async () => { - await adminApiActions.createUser({ username }); - }); - - describe('file not displayed if user no longer has permissions on it', () => { - const sitePrivate = `private-${Utils.random()}`; - const fileName = `file-${Utils.random()}.txt`; - let fileId: string; - - beforeAll(async () => { - await adminApiActions.login(); - await adminApiActions.sites.createSite(sitePrivate, SITE_VISIBILITY.PRIVATE); - await adminApiActions.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_COLLABORATOR.ROLE); - const docLibId = await adminApiActions.sites.getDocLibId(sitePrivate); - fileId = (await adminApiActions.nodes.createFile(fileName, docLibId)).entry.id; - await apis.user.favorites.addFavoriteById('file', fileId); - - await adminApiActions.login(); - await adminApiActions.shareNodes([fileId]); - await apis.user.nodes.updateNodeContent(fileId, 'edited by user'); - - await apis.user.search.waitForApi(username, { expect: 1 }); - - await adminApiActions.login(); - await adminApiActions.shared.waitForFilesToBeShared([fileId]); - - await loginPage.loginWith(username); - }); - - afterEach(async () => { - await adminApiActions.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_COLLABORATOR.ROLE); - }); - - afterAll(async () => { - await adminApiActions.sites.deleteSite(sitePrivate); - }); - - it('[C213173] on Recent Files', async () => { - await page.clickRecentFilesAndWait(); - expect(await dataTable.getRowsCount()).toBe(1, 'Incorrect number of items'); - await adminApiActions.sites.deleteSiteMember(sitePrivate, username); - await page.refresh(); - expect(await dataTable.isEmpty()).toBe(true, 'Items are still displayed'); - }); - - it('[C213227] on Favorites', async () => { - await page.clickFavoritesAndWait(); - expect(await dataTable.getRowsCount()).toBe(1, 'Incorrect number of items'); - await adminApiActions.sites.deleteSiteMember(sitePrivate, username); - await page.refresh(); - expect(await dataTable.isEmpty()).toBe(true, 'Items are still displayed'); - }); - - it('[C213116] on Shared Files', async () => { - await page.clickSharedFilesAndWait(); - expect(await dataTable.isItemPresent(fileName)).toBe(true, `${fileName} not displayed`); - await adminApiActions.sites.deleteSiteMember(sitePrivate, username); - await page.refresh(); - expect(await dataTable.isItemPresent(fileName)).toBe(false, `${fileName} is displayed`); - }); - - it('[C290122] on Search Results', async () => { - await toolbar.clickSearchIconButton(); - await searchInput.clickSearchButton(); - await searchInput.checkFilesAndFolders(); - await searchInput.searchFor(fileName); - await dataTable.waitForBody(); - - expect(await dataTable.isItemPresent(fileName)).toBe(true, `${fileName} is not displayed`); - - await adminApiActions.sites.deleteSiteMember(sitePrivate, username); - - await searchInput.clickSearchButton(); - await searchInput.checkFilesAndFolders(); - await searchInput.searchFor(fileName); - await dataTable.waitForBody(); - - expect(await dataTable.isItemPresent(fileName)).toBe(false, `${fileName} is displayed`); - }); - }); - - describe(`Location column is empty if user doesn't have permissions on the file's parent folder`, () => { - const sitePrivate = `private-${Utils.random()}`; - const fileName = `file-${Utils.random()}.txt`; - let fileId; - - beforeAll(async () => { - await adminApiActions.sites.createSite(sitePrivate, SITE_VISIBILITY.PRIVATE); - await adminApiActions.sites.addSiteMember(sitePrivate, username, SITE_ROLES.SITE_COLLABORATOR.ROLE); - const docLibId = await adminApiActions.sites.getDocLibId(sitePrivate); - fileId = (await apis.user.nodes.createFile(fileName, docLibId)).entry.id; - await apis.user.favorites.addFavoriteById('file', fileId); - - await userActions.login(username, username); - await userActions.shareNodes([fileId]); - await apis.user.shared.waitForFilesToBeShared([fileId]); - - await apis.user.search.waitForApi(username, { expect: 1 }); - await adminApiActions.sites.deleteSiteMember(sitePrivate, username); - await loginPage.loginWith(username); - }); - - afterAll(async () => { - await adminApiActions.sites.deleteSite(sitePrivate); - }); - - it('[C213178] on Recent Files', async () => { - await page.clickRecentFilesAndWait(); - expect(await dataTable.getRowsCount()).toBe(1, 'Incorrect number of items'); - expect(await dataTable.getItemLocation(fileName)).toEqual('Unknown'); - }); - - it('[C213672] on Favorites', async () => { - await page.clickFavoritesAndWait(); - expect(await dataTable.getRowsCount()).toBe(1, 'Incorrect number of items'); - expect(await dataTable.getItemLocation(fileName)).toEqual('Unknown'); - }); - - it(`[C213668] on Shared Files`, async () => { - await page.clickSharedFilesAndWait(); - expect(await dataTable.isItemPresent(fileName)).toBe(true, `${fileName} not displayed`); - expect(await dataTable.getItemLocation(fileName)).toEqual('Unknown'); - }); - - it('[C306868] on Search results', async () => { - await toolbar.clickSearchIconButton(); - await searchInput.clickSearchButton(); - await searchInput.checkFilesAndFolders(); - await searchInput.searchFor(fileName); - await dataTable.waitForBody(); - - expect(await dataTable.isItemPresent(fileName)).toBe(true, `${fileName} is not displayed`); - expect(await dataTable.getItemLocation(fileName)).toEqual('Unknown'); - }); - }); -}); diff --git a/e2e/protractor/suites/list-views/sort-list.test.ts b/e2e/protractor/suites/list-views/sort-list.test.ts deleted file mode 100644 index d826938b17..0000000000 --- a/e2e/protractor/suites/list-views/sort-list.test.ts +++ /dev/null @@ -1,348 +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, RepoClient, FILES, BrowsingPage, DataTable, CreateOrEditFolderDialog } from '@alfresco/aca-testing-shared'; -import { BrowserActions, ContentNodeSelectorDialogPage, DocumentListPage, PaginationPage, ViewerPage } from '@alfresco/adf-testing'; - -describe('Remember sorting', () => { - interface NodesIds { - [index: string]: string; - } - - const timestamp = new Date().getTime(); - const user1 = `user1-${timestamp}`; - const user2 = `user2-${timestamp}`; - const pdfFileNames = [...new Array(14).fill(100)].map((v, i) => `file-${v + i}.pdf`); - const jpgFileNames = [...new Array(12).fill(114)].map((v, i) => `file-${v + i}.jpg`); - const folderToMove = `folder1`; - const folderToContain = `folder2`; - const uiCreatedFolder = `folder3`; - const filesIdsUser1: NodesIds = {}; - const filesIdsUser2: NodesIds = {}; - const folderIds: NodesIds = {}; - - const testData = { - user1: { - files: { - jpg: jpgFileNames, - pdf: pdfFileNames - } - }, - user2: { - files: [pdfFileNames[0], jpgFileNames[0]] - } - }; - - let initialSortState: { - sortingColumn: string; - sortingOrder: string; - firstElement: string; - }; - - const apis = { - user1: new RepoClient(user1, user1), - user2: new RepoClient(user2, user2) - }; - - const loginPage = new LoginPage(); - const browsingPage = new BrowsingPage(); - const dataTable = new DataTable(); - const documentListPage = new DocumentListPage(); - const viewerPage = new ViewerPage(); - const adminApiActions = new AdminActions(); - const createDialog = new CreateOrEditFolderDialog(); - const contentNodeSelectorDialogPage = new ContentNodeSelectorDialogPage(); - const paginationPage = new PaginationPage(); - - beforeAll(async () => { - await adminApiActions.createUser({ username: user1 }); - await adminApiActions.createUser({ username: user2 }); - await Promise.all( - testData.user1.files.pdf.map( - async (i) => (filesIdsUser1[i] = (await apis.user1.upload.uploadFileWithRename(FILES.pdfFile, '-my-', i)).entry.id) - ) - ); - await Promise.all( - testData.user1.files.jpg.map( - async (i) => (filesIdsUser1[i] = (await apis.user1.upload.uploadFileWithRename(FILES.jpgFile, '-my-', i)).entry.id) - ) - ); - await Promise.all( - testData.user2.files.map(async (i) => (filesIdsUser2[i] = (await apis.user2.upload.uploadFileWithRename(FILES.pdfFile, '-my-', i)).entry.id)) - ); - await apis.user1.favorites.addFavoritesByIds('file', [filesIdsUser1[pdfFileNames[0]], filesIdsUser1[pdfFileNames[1]]]); - await loginPage.loginWith(user1); - }); - - beforeEach(async () => { - await browsingPage.clickPersonalFilesAndWait(); - await dataTable.sortBy('Name', 'asc'); - await dataTable.waitForBody(); - initialSortState = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - }); - - afterAll(async () => { - await Promise.all(Object.keys(filesIdsUser1).map((i) => apis.user1.nodes.deleteNodeById(filesIdsUser1[i]))); - await Promise.all(Object.keys(filesIdsUser2).map((i) => apis.user2.nodes.deleteNodeById(filesIdsUser2[i]))); - }); - - it('[C261136] Sort order is retained when navigating to another part of the app', async () => { - await dataTable.sortBy('Name', 'desc'); - await dataTable.waitForFirstElementToChange(initialSortState.firstElement); - await dataTable.waitForBody(); - - const expectedSortData = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - - expect(expectedSortData).not.toEqual(initialSortState); - - await browsingPage.clickFavorites(); - await browsingPage.clickPersonalFilesAndWait(); - - const actualSortData = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - - expect(actualSortData).toEqual(expectedSortData); - }); - - it('[C261137] Size sort order is retained when user logs out and logs back in', async () => { - await dataTable.sortBy('Name', 'desc'); - await dataTable.waitForFirstElementToChange(initialSortState.firstElement); - await dataTable.waitForBody(); - - const expectedSortData = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - - expect(expectedSortData).not.toEqual(initialSortState); - - await browsingPage.signOut(); - await loginPage.loginWith(user1); - - const actualSortData = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - - expect(actualSortData).toEqual(expectedSortData); - }); - - describe('Folder actions', () => { - beforeAll(async () => { - folderIds[folderToContain] = (await apis.user1.nodes.createFolder(folderToContain)).entry.id; - folderIds[folderToMove] = (await apis.user1.nodes.createFolder(folderToMove)).entry.id; - }); - - afterAll(async () => { - folderIds[uiCreatedFolder] = await apis.user1.nodes.getNodeIdFromParent(uiCreatedFolder, '-my-'); - await Promise.all(Object.keys(folderIds).map((i) => apis.user1.nodes.deleteNodeById(folderIds[i]))); - }); - - it('[C261138] Sort order is retained when creating a new folder', async () => { - await dataTable.sortBy('Name', 'desc'); - await dataTable.waitForFirstElementToChange(initialSortState.firstElement); - await dataTable.waitForBody(); - - const expectedSortData = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: uiCreatedFolder - }; - - await browsingPage.sidenav.openCreateFolderDialog(); - await createDialog.waitForDialogToOpen(); - await createDialog.enterName(uiCreatedFolder); - await BrowserActions.click(createDialog.createButton); - await createDialog.waitForDialogToClose(); - await documentListPage.dataTable.checkRowContentIsDisplayed(uiCreatedFolder); - - const actualSortData = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - - expect(actualSortData).toEqual(expectedSortData); - }); - - it('[C261139] Sort order is retained when moving a file', async () => { - const expectedSortData = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: folderToContain - }; - - await browsingPage.dataTable.rightClickOnItem(folderToMove); - await dataTable.menu.clickMenuItem('Move'); - await contentNodeSelectorDialogPage.clickContentNodeSelectorResult(folderToContain); - await contentNodeSelectorDialogPage.clickMoveCopyButton(); - await documentListPage.dataTable.checkRowContentIsNotDisplayed(folderToMove); - - const actualSortData = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - - expect(actualSortData).toEqual(expectedSortData); - }); - }); - - it('[C589205] Size sort order is retained after viewing a file and closing the viewer', async () => { - await dataTable.sortBy('Size', 'desc'); - await dataTable.waitForFirstElementToChange(initialSortState.firstElement); - await dataTable.waitForBody(); - - const expectedSortData = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - - await dataTable.doubleClickOnRowByName(expectedSortData.firstElement); - await viewerPage.clickCloseButton(); - await browsingPage.clickPersonalFilesAndWait(); - - const actualSortData = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - - expect(actualSortData).toEqual(expectedSortData); - }); - - it('[C261153] Sort order should be remembered separately on each list view', async () => { - await dataTable.sortBy('Size', 'desc'); - await dataTable.waitForFirstElementToChange(initialSortState.firstElement); - await dataTable.waitForBody(); - - const personalFilesSortData = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - - await browsingPage.clickFavoritesAndWait(); - await dataTable.sortBy('Name', 'asc'); - await dataTable.waitForFirstElementToChange(personalFilesSortData.firstElement); - await dataTable.waitForBody(); - - const favouritesSortData = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - - expect(favouritesSortData).not.toEqual(personalFilesSortData); - - await browsingPage.clickPersonalFilesAndWait(); - - const personalFilesSortDataAfterFavSort = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - - expect(personalFilesSortDataAfterFavSort).toEqual(personalFilesSortData); - }); - - it('[C261147] Sort order is retained when user changes the page from pagination', async () => { - const lastFileInArray = testData.user1.files.jpg.slice(-1).pop(); - const firstFileInArray = testData.user1.files.pdf[0]; - - await paginationPage.clickOnNextPage(); - await dataTable.waitForBody(); - - let expectedPersonalFilesSortDataPage2 = { - sortingColumn: 'Name', - sortingOrder: 'asc', - firstElement: lastFileInArray - }; - - let currentPersonalFilesSortDataPage2 = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - - expect(currentPersonalFilesSortDataPage2).toEqual(expectedPersonalFilesSortDataPage2); - - await dataTable.sortBy('Name', 'desc'); - await dataTable.waitForFirstElementToChange(currentPersonalFilesSortDataPage2.firstElement); - await dataTable.waitForBody(); - - expectedPersonalFilesSortDataPage2 = { - sortingColumn: 'Name', - sortingOrder: 'desc', - firstElement: firstFileInArray - }; - - currentPersonalFilesSortDataPage2 = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - - expect(expectedPersonalFilesSortDataPage2).toEqual(currentPersonalFilesSortDataPage2); - }); - - it('[C261150] Sort order is not retained between different users', async () => { - await dataTable.sortBy('Size', 'asc'); - await dataTable.waitForFirstElementToChange(initialSortState.firstElement); - await dataTable.waitForBody(); - - const expectedSortData = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - - await browsingPage.signOut(); - await loginPage.loginWith(user2); - - const actualSortData = { - sortingColumn: await dataTable.getSortedColumnHeaderText(), - sortingOrder: await dataTable.getSortingOrder(), - firstElement: await documentListPage.dataTable.getFirstElementDetail('Name') - }; - - expect(actualSortData).not.toEqual(expectedSortData); - - await browsingPage.signOut(); - }); -}); diff --git a/e2e/protractor/suites/list-views/trash.test.ts b/e2e/protractor/suites/list-views/trash.test.ts deleted file mode 100755 index b6cae2a866..0000000000 --- a/e2e/protractor/suites/list-views/trash.test.ts +++ /dev/null @@ -1,123 +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, UserActions, SITE_VISIBILITY, SITE_ROLES, LoginPage, BrowsingPage, Utils, RepoClient } from '@alfresco/aca-testing-shared'; -import { Logger } from '@alfresco/adf-testing'; - -describe('Trash', () => { - const username = `user-${Utils.random()}`; - - const siteName = `site-${Utils.random()}`; - const fileSite = `file-${Utils.random()}.txt`; - let fileSiteId: string; - - const folderAdmin = `folder-${Utils.random()}`; - let folderAdminId: string; - const fileAdmin = `file-${Utils.random()}.txt`; - let fileAdminId: string; - - const folderUser = `folder-${Utils.random()}`; - let folderUserId: string; - const fileUser = `file-${Utils.random()}.txt`; - let fileUserId: string; - - const folderDeleted = `folder-${Utils.random()}`; - let folderDeletedId: string; - const fileDeleted = `file-${Utils.random()}.txt`; - let fileDeletedId: string; - - const folderNotDeleted = `folder-${Utils.random()}`; - let folderNotDeletedId: string; - const fileInFolder = `file-${Utils.random()}.txt`; - let fileInFolderId: string; - - const apis = { - user: new RepoClient(username, username) - }; - - const loginPage = new LoginPage(); - const page = new BrowsingPage(); - const { dataTable } = page; - - const adminApiActions = new AdminActions(); - const userActions = new UserActions(); - - beforeAll(async () => { - try { - await adminApiActions.createUser({ username }); - - fileAdminId = (await adminApiActions.nodes.createFiles([fileAdmin])).entry.id; - folderAdminId = (await adminApiActions.nodes.createFolders([folderAdmin])).entry.id; - await adminApiActions.sites.createSite(siteName, SITE_VISIBILITY.PUBLIC); - await adminApiActions.sites.addSiteMember(siteName, username, SITE_ROLES.SITE_MANAGER.ROLE); - const docLibId = await adminApiActions.sites.getDocLibId(siteName); - fileSiteId = (await adminApiActions.nodes.createFile(fileSite, docLibId)).entry.id; - - await adminApiActions.nodes.deleteNodesById([fileAdminId, folderAdminId], false); - - fileUserId = (await apis.user.nodes.createFiles([fileUser])).entry.id; - folderUserId = (await apis.user.nodes.createFolders([folderUser])).entry.id; - folderDeletedId = (await apis.user.nodes.createFolder(folderDeleted)).entry.id; - fileDeletedId = (await apis.user.nodes.createFiles([fileDeleted], folderDeleted)).entry.id; - folderNotDeletedId = (await apis.user.nodes.createFolder(folderNotDeleted)).entry.id; - fileInFolderId = (await apis.user.nodes.createFiles([fileInFolder], folderNotDeleted)).entry.id; - - await apis.user.nodes.deleteNodesById([fileSiteId, fileUserId, folderUserId, fileInFolderId, fileDeletedId, folderDeletedId], false); - } catch (error) { - Logger.error(`----- beforeAll failed : ${error}`); - } - }); - - afterAll(async () => { - try { - await adminApiActions.login(); - await adminApiActions.deleteSites([siteName]); - await adminApiActions.trashcanApi.deleteDeletedNode(fileAdminId); - await adminApiActions.trashcanApi.deleteDeletedNode(folderAdminId); - - await userActions.login(username, username); - await userActions.deleteNodes([folderNotDeletedId]); - await userActions.emptyTrashcan(); - } catch (error) { - Logger.error(`----- afterAll failed : ${error}`); - } - }); - - describe('as admin', () => { - beforeAll(async () => { - await loginPage.loginWithAdmin(); - }); - - beforeEach(async () => { - await page.clickTrashAndWait(); - }); - - it('[C213217] has the correct columns', async () => { - const expectedColumns = ['Name', 'Location', 'Size', 'Deleted', 'Deleted by']; - const actualColumns = await dataTable.getColumnHeadersText(); - - expect(actualColumns).toEqual(expectedColumns); - }); - }); -}); diff --git a/projects/aca-content/folder-rules/assets/i18n/cs.json b/projects/aca-content/folder-rules/assets/i18n/cs.json index af2ebe11b9..d25b3baf04 100644 --- a/projects/aca-content/folder-rules/assets/i18n/cs.json +++ b/projects/aca-content/folder-rules/assets/i18n/cs.json @@ -8,15 +8,15 @@ "CREATE": "Vytvořit", "CREATE_TITLE": "Vytvořit pravidlo", "UPDATE": "Aktualizovat", - "UPDATE_TITLE": "Edit a rule" + "UPDATE_TITLE": "Upravit pravidlo" }, "RULE_DETAILS": { "LABEL": { "NAME": "Název", "DESCRIPTION": "Popis", "WHEN": "Když", - "PERFORM_ACTIONS": "Perform actions", - "OPTIONS": "Other options" + "PERFORM_ACTIONS": "Provádějte akce", + "OPTIONS": "Jiné možnosti" }, "PLACEHOLDER": { "NAME": "Zadejte název pravidla", @@ -36,10 +36,10 @@ "OUTBOUND": "Položky jsou odstraněny nebo opouští tuto složku" }, "OPTIONS": { - "IS_INHERITABLE": "Rule applies to subfolders", - "IS_ASYNCHRONOUS": "Run rule in the background", - "DISABLE_RULE": "Disable rule", - "ERROR_SCRIPT": "If errors occur run script", + "IS_INHERITABLE": "Pravidlo platí pro podsložky", + "IS_ASYNCHRONOUS": "Spustit pravidlo na pozadí", + "DISABLE_RULE": "Zakázat pravidlo", + "ERROR_SCRIPT": "Pokud se vyskytnou chyby, spusťte skript", "NO_SCRIPT": "Žádný" }, "COMPARATORS": { @@ -74,8 +74,8 @@ "OR": "Nebo" }, "CONDITION_BUTTONS": { - "ADD_CONDITION": "Add condition", - "ADD_GROUP": "Add condition group", + "ADD_CONDITION": "Přidat podmínku", + "ADD_GROUP": "Přidat skupinu podmínek", "REMOVE": "Odebrat" }, "NO_CONDITIONS": "Žádné podmínky", @@ -84,7 +84,7 @@ "ADD_ACTION": "Přidat akci", "REMOVE": "Odebrat" }, - "ACTION_SELECT_PLACEHOLDER": "Select an action" + "ACTION_SELECT_PLACEHOLDER": "Vyberte akci" }, "MANAGE_RULES": { "TOOLBAR": { @@ -141,4 +141,4 @@ "DELETE_RULE_SET_LINK_FAILED": "Vyskytla se chyba při pokusu o smazání odkazu z nastaveného pravidla" } } -} \ No newline at end of file +} diff --git a/projects/aca-playwright-shared/src/api/file-actions.ts b/projects/aca-playwright-shared/src/api/file-actions.ts index f2883c5383..1e67b1957c 100644 --- a/projects/aca-playwright-shared/src/api/file-actions.ts +++ b/projects/aca-playwright-shared/src/api/file-actions.ts @@ -41,7 +41,7 @@ export class FileActionsApi { return classObj; } - async uploadFile(fileLocation: string, fileName: string, parentFolderId: string): Promise { + async uploadFile(fileLocation: string, fileName: string, parentFolderId: string): Promise { const file = fs.createReadStream(fileLocation); return this.apiService.upload.uploadFile(file, '', parentFolderId, null, { name: fileName, @@ -50,7 +50,13 @@ export class FileActionsApi { }); } - async uploadFileWithRename(fileLocation: string, newName: string, parentId: string = '-my-', title: string = '', description: string = '') { + async uploadFileWithRename( + fileLocation: string, + newName: string, + parentId: string = '-my-', + title: string = '', + description: string = '' + ): Promise { const file = fs.createReadStream(fileLocation); const nodeProps = { properties: { @@ -68,10 +74,11 @@ export class FileActionsApi { return await this.apiService.upload.uploadFile(file, '', parentId, nodeProps, opts); } catch (error) { Logger.error(`${this.constructor.name} ${this.uploadFileWithRename.name}`, error); + return Promise.reject(error); } } - async lockNodes(nodeIds: string[], lockType: string = 'ALLOW_OWNER_CHANGES') { + async lockNodes(nodeIds: string[], lockType: string = 'ALLOW_OWNER_CHANGES'): Promise { try { for (const nodeId of nodeIds) { await this.apiService.nodes.lockNode(nodeId, { type: lockType }); @@ -93,7 +100,7 @@ export class FileActionsApi { async getNodeProperty(nodeId: string, property: string): Promise { try { const node = await this.getNodeById(nodeId); - return (node.entry.properties?.[property]) || ''; + return node.entry.properties?.[property] || ''; } catch (error) { Logger.error(`${this.constructor.name} ${this.getNodeProperty.name}`, error); return ''; @@ -145,7 +152,7 @@ export class FileActionsApi { return this.apiService.search.search(data); } catch (error) { Logger.error(`SearchApi queryNodesNames : catch : `, error); - return new ResultSetPaging; + return new ResultSetPaging(); } } @@ -167,4 +174,18 @@ export class FileActionsApi { Logger.error(`\tExpected: ${data.expect} items, but found ${error}`); } } + + async updateNodeContent(nodeId: string, content: string, majorVersion: boolean = true, comment?: string, newName?: string): Promise { + try { + const opts: { [key: string]: string | boolean } = { + majorVersion: majorVersion, + comment: comment, + name: newName + }; + return await this.apiService.nodes.updateNodeContent(nodeId, content, opts); + } catch (error) { + console.error(`${this.constructor.name} ${this.updateNodeContent.name}`, error); + return Promise.reject(error); + } + } } diff --git a/projects/aca-playwright-shared/src/api/nodes-api.ts b/projects/aca-playwright-shared/src/api/nodes-api.ts index c83f563888..3ef94b0883 100755 --- a/projects/aca-playwright-shared/src/api/nodes-api.ts +++ b/projects/aca-playwright-shared/src/api/nodes-api.ts @@ -81,6 +81,14 @@ export class NodesApi { } } + async deleteDeletedNode(name: string): Promise { + try { + await this.apiService.trashCan.deleteDeletedNode(name); + } catch (error) { + console.error(`${this.constructor.name} ${this.deleteDeletedNode.name}: ${error}`); + } + } + private async createNode( nodeType: string, name: string, @@ -401,4 +409,44 @@ export class NodesApi { return null; } } + + async getNodeProperty(nodeId: string, property: string): Promise { + try { + const node = await this.getNodeById(nodeId); + return node.entry.properties?.[property] || ''; + } catch (error) { + console.error(`${this.constructor.name} ${this.getNodeProperty.name}`, error); + return ''; + } + } + + async getSharedId(nodeId: string): Promise { + try { + const sharedId = await this.getNodeProperty(nodeId, 'qshare:sharedId'); + return sharedId || ''; + } catch (error) { + console.error(`${this.constructor.name} ${this.getSharedId.name}`, error); + return ''; + } + } + + async getSharedExpiryDate(nodeId: string): Promise { + try { + const expiryDate = await this.getNodeProperty(nodeId, 'qshare:expiryDate'); + return expiryDate || ''; + } catch (error) { + console.error(`${this.constructor.name} ${this.getSharedExpiryDate.name}`, error); + return ''; + } + } + + async isFileShared(nodeId: string): Promise { + try { + const sharedId = await this.getSharedId(nodeId); + return sharedId !== ''; + } catch (error) { + console.error(`${this.constructor.name} ${this.isFileShared.name}`, error); + return null; + } + } } 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 b7f995a32f..0a551213e4 100755 --- a/projects/aca-playwright-shared/src/api/shared-links-api.ts +++ b/projects/aca-playwright-shared/src/api/shared-links-api.ts @@ -50,12 +50,12 @@ export class SharedLinksApi { } } - async shareFilesByIds(ids: string[]): Promise { + async shareFilesByIds(ids: string[], expireDate?: Date): Promise { const sharedLinks: SharedLinkEntry[] = []; try { if (ids && ids.length > 0) { for (const id of ids) { - const sharedLink = await this.shareFileById(id); + const sharedLink = await this.shareFileById(id, expireDate); sharedLinks.push(sharedLink); } } diff --git a/projects/aca-playwright-shared/src/api/sites-api.ts b/projects/aca-playwright-shared/src/api/sites-api.ts index 1b33218104..a2e27f2b3c 100755 --- a/projects/aca-playwright-shared/src/api/sites-api.ts +++ b/projects/aca-playwright-shared/src/api/sites-api.ts @@ -23,7 +23,16 @@ */ import { ApiClientFactory } from './api-client-factory'; -import { Site, SiteBodyCreate, SiteEntry, SiteMemberEntry, SiteMembershipBodyCreate, SiteMembershipBodyUpdate, SiteMembershipRequestBodyCreate, SiteMembershipRequestEntry } from '@alfresco/js-api'; +import { + Site, + SiteBodyCreate, + SiteEntry, + SiteMemberEntry, + SiteMembershipBodyCreate, + SiteMembershipBodyUpdate, + SiteMembershipRequestBodyCreate, + SiteMembershipRequestEntry +} from '@alfresco/js-api'; export class SitesApi { private apiService: ApiClientFactory; @@ -88,7 +97,7 @@ export class SitesApi { return await this.apiService.sites.updateSiteMembership(siteId, userId, siteRole); } catch (error) { console.error(`SitesApi updateSiteMember : catch : `, error); - return new SiteMemberEntry; + return new SiteMemberEntry(); } } @@ -105,7 +114,7 @@ export class SitesApi { return this.updateSiteMember(siteId, userId, role); } else { console.error(`SitesApi addSiteMember : catch : `, error); - return new SiteMemberEntry; + return new SiteMemberEntry(); } } } @@ -141,4 +150,12 @@ export class SitesApi { return null; } } + + async deleteSiteMember(siteId: string, userId: string) { + try { + return await this.apiService.sites.deleteSiteMembership(siteId, userId); + } catch (error) { + console.error(`SitesApi deleteSiteMember : catch : `, error); + } + } } 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 2e0252b67d..9f9eceefb9 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 @@ -37,6 +37,7 @@ export class AcaHeader extends BaseComponent { public fullScreenButton = 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"]'); + public sharedDownloadButton = this.getChild('button[id="app.viewer.shared.download"]'); constructor(page: Page) { super(page, AcaHeader.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 430c3de7ae..95df52d4be 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 @@ -44,6 +44,11 @@ export class DataTableComponent extends BaseComponent { sortedColumnHeader = this.getChild(`.adf-datatable__header--sorted-asc .adf-datatable-cell-header-content .adf-datatable-cell-value, .adf-datatable__header--sorted-desc .adf-datatable-cell-header-content .adf-datatable-cell-value`); columnHeaders = this.getChild('.adf-datatable-row .adf-datatable-cell-header .adf-datatable-cell-value'); + emptyList = this.getChild('div.adf-no-content-container'); + emptyListTitle = this.getChild('.adf-empty-content__title'); + emptyListSubtitle = this.getChild('.adf-empty-content__subtitle'); + emptySearchText = this.getChild('.empty-search__text'); + emptyListTest = this.getChild('adf-custom-empty-content-template'); /** Locator for row (or rows) */ getRowLocator = this.getChild(`adf-datatable-row`); @@ -123,17 +128,21 @@ export class DataTableComponent extends BaseComponent { getSearchResultLinkByName = (name: string): Locator => this.getChild('.aca-search-results-row span[role="link"]', { hasText: name }); - async sortBy(columnTitle: string, order: 'Ascending' | 'Descending'): Promise { - const columnHeaderLocator = this.getColumnHeaderByTitleLocator(columnTitle); - await this.spinnerWaitForReload(); - await columnHeaderLocator.click(); + getColumnValuesByName = (name: string): Locator => this.getChild(`div[title="${name}"] span`); - const sortAttribute = await columnHeaderLocator.getAttribute('aria-sort'); - if (sortAttribute !== order) { - await columnHeaderLocator.click(); - } + getColumnHeaderByName = (headerTitle: string): Locator => + this.getChild('.adf-datatable-row .adf-datatable-cell-header .adf-datatable-cell-value', { hasText: headerTitle }); - await this.spinnerWaitForReload(); + async sortBy(label: string, order: 'asc' | 'desc'): Promise { + const sortColumn = await this.getSortedColumnHeaderText(); + let sortOrder = await this.getSortingOrder(); + if (sortColumn !== label) { + await this.getColumnHeaderByName(label).click({ force: true }); + sortOrder = await this.getSortingOrder(); + } + if (sortOrder !== order) { + await this.getChild('span[class*="adf-datatable__header--sorted"]').click(); + } } /** @@ -281,4 +290,29 @@ export class DataTableComponent extends BaseComponent { async getRowAllInnerTexts(name: string): Promise { return (await this.getRowByName(name).locator('span').allInnerTexts()).toString(); } + + async getFirstElementDetail(name: string): Promise { + const firstNode = this.getColumnValuesByName(name).first(); + return firstNode.innerText(); + } + + async isEmpty(): Promise { + return this.emptyList.isVisible(); + } + + async getEmptyStateTitle(): Promise { + return (await this.isEmpty()) ? this.emptyListTitle.innerText() : ''; + } + + async getEmptyStateSubtitle(): Promise { + return (await this.isEmpty()) ? this.emptyListSubtitle.innerText() : ''; + } + + async getEmptyListText(): Promise { + return (await this.isEmpty()) ? this.emptyListTest.innerText() : ''; + } + + async getRowsCount(): Promise { + return this.getRowLocator.count(); + } } diff --git a/projects/aca-playwright-shared/src/page-objects/components/datetime-picker/datetime-picker.component.ts b/projects/aca-playwright-shared/src/page-objects/components/datetime-picker/datetime-picker.component.ts new file mode 100644 index 0000000000..60dc634754 --- /dev/null +++ b/projects/aca-playwright-shared/src/page-objects/components/datetime-picker/datetime-picker.component.ts @@ -0,0 +1,58 @@ +/*! + * 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 { Page } from '@playwright/test'; +import { BaseComponent } from '../base.component'; + +export class DateTimePicker extends BaseComponent { + private static rootElement = '.mat-calendar'; + calendar = this.getChild('.mat-datepicker-popup'); + dayPicker = this.getChild('mat-month-view'); + nextMonthBtn = this.getChild('.mat-calendar-next-button'); + rootElemLocator = this.page.locator('.mat-datepicker-popup'); + + constructor(page: Page) { + super(page, DateTimePicker.rootElement); + } + + async isCalendarOpen(): Promise { + const element = this.rootElemLocator; + return element.isVisible(); + } + + async pickDateTime(): Promise { + const today = new Date(); + const nextAvailableDay = new Date(); + nextAvailableDay.setDate(today.getDate() + 2); + if (nextAvailableDay.getMonth() !== today.getMonth()) { + await this.nextMonthBtn.click(); + } + await this.selectDay(nextAvailableDay.getDate()); + } + + async selectDay(day: number): Promise { + const firstActiveDayElem = this.dayPicker.locator(`text=${day}`).first(); + await firstActiveDayElem.click(); + } +} diff --git a/projects/aca-playwright-shared/src/page-objects/components/dialogs/index.ts b/projects/aca-playwright-shared/src/page-objects/components/dialogs/index.ts index c1a913f24d..091bd03320 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/dialogs/index.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/dialogs/index.ts @@ -29,3 +29,4 @@ export * from './viewer-overlay-dialog.component'; export * from './content-node-selector-dialog'; export * from './create-from-template-dialog-component'; export * from './adf-confirm-dialog.component'; +export * from './share-dialog.component'; diff --git a/projects/aca-playwright-shared/src/page-objects/components/dialogs/share-dialog.component.ts b/projects/aca-playwright-shared/src/page-objects/components/dialogs/share-dialog.component.ts new file mode 100644 index 0000000000..fde0b404dc --- /dev/null +++ b/projects/aca-playwright-shared/src/page-objects/components/dialogs/share-dialog.component.ts @@ -0,0 +1,101 @@ +/*! + * 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 { ElementHandle, Locator, Page } from '@playwright/test'; +import { BaseComponent } from '../base.component'; +import { timeouts } from '../../../utils'; +import { DateTimePicker } from '../datetime-picker/datetime-picker.component'; + +export class ShareDialogComponent extends BaseComponent { + private static rootElement = 'adf-share-dialog'; + + constructor(page: Page) { + super(page, ShareDialogComponent.rootElement); + } + + closeButton = this.getChild('[data-automation-id="adf-share-dialog-close"]'); + dialogTitle = this.getChild('[data-automation-id="adf-share-dialog-title"]'); + infoText = this.getChild('.adf-share-link__info').first(); + labels = '.adf-share-link__label'; + shareToggle = this.getChild(`[data-automation-id='adf-share-toggle']`); + url = this.getChild(`[data-automation-id='adf-share-link']`); + urlAction = this.getChild('.adf-input-action'); + expireToggle = this.getChild(`[data-automation-id='adf-expire-toggle']`); + expireInput = this.getChild('input[formcontrolname="time"]'); + datetimePickerButton = this.getChild('.mat-datepicker-toggle'); + + dateTimePicker = new DateTimePicker(this.page); + + getDialogLabel = () => this.getChild('label').innerText(); + getErrorByText = (text: string): Locator => this.page.locator('mat-error', { hasText: text }); + + async getLabels(): Promise> { + return await this.page.$$eval('.adf-share-link__label', (elements) => elements.map((element) => element.textContent)); + } + + async getDialogTitle(): Promise { + return await this.dialogTitle.innerText(); + } + + async getInfoText(): Promise { + return await this.infoText.innerText(); + } + + async getLinkUrl(): Promise { + return await this.url.inputValue(); + } + + async isUrlReadOnly(): Promise { + const urlAttr = await this.url.getAttribute('readonly'); + return urlAttr === 'true'; + } + + async isCloseEnabled(): Promise { + return await this.closeButton.isEnabled(); + } + + async clickClose(): Promise { + await this.page.waitForTimeout(timeouts.tiny); + await this.closeButton.click(); + } + + async isToggleStatus(toggle: ElementHandle, status: string): Promise { + const toggleClass = await toggle.getAttribute('class'); + return toggleClass.includes(status); + } + + async isShareToggleChecked(): Promise { + const shareToggleElement = await this.shareToggle.elementHandle(); + return this.isToggleStatus(shareToggleElement, 'checked'); + } + + async isExpireToggleEnabled(): Promise { + const expireToggleElement = await this.expireToggle.elementHandle(); + return this.isToggleStatus(expireToggleElement, 'checked'); + } + + async getExpireDate(): Promise { + return await this.expireInput.inputValue(); + } +} diff --git a/projects/aca-playwright-shared/src/page-objects/components/error.component.ts b/projects/aca-playwright-shared/src/page-objects/components/error.component.ts new file mode 100644 index 0000000000..cbdcd16656 --- /dev/null +++ b/projects/aca-playwright-shared/src/page-objects/components/error.component.ts @@ -0,0 +1,36 @@ +/*! + * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Alfresco Example Content Application + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * from Hyland Software. If not, see . + */ + +import { Page } from '@playwright/test'; +import { BaseComponent } from './base.component'; + +export class ErrorComponent extends BaseComponent { + private static rootElement = 'aca-page-layout'; + genericError = this.getChild('aca-generic-error'); + genericErrorTitle = this.getChild('.generic-error__title'); + + constructor(page: Page) { + super(page, ErrorComponent.rootElement); + } +} 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 3083c6a25c..74c2a84a56 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/index.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/index.ts @@ -38,3 +38,5 @@ export * from './search/search-overlay.components'; export * from './breadcrumb/breadcrumb.component'; export * from './sidenav.component'; export * from './aca-header.component'; +export * from './error.component'; +export * from './datetime-picker/datetime-picker.component'; diff --git a/projects/aca-playwright-shared/src/page-objects/components/pagination.component.ts b/projects/aca-playwright-shared/src/page-objects/components/pagination.component.ts index cafbcb512c..ff58672902 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/pagination.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/pagination.component.ts @@ -105,7 +105,7 @@ export class PaginationComponent extends BaseComponent { if (await this.isNextEnabled()) { await this.nextButton.click(); } - } catch(error) { + } catch (error) { throw new Error(`Failed on previous click: ${error}`); } } @@ -115,7 +115,7 @@ export class PaginationComponent extends BaseComponent { if (await this.isPreviousEnabled()) { await this.previousButton.click(); } - } catch(error) { + } catch (error) { throw new Error(`Failed on previous click: ${error}`); } } @@ -166,4 +166,12 @@ export class PaginationComponent extends BaseComponent { async closeMenu(): Promise { await this.page.keyboard.press('Escape'); } + + async isRangePresent(): Promise { + return this.range.isVisible(); + } + + async isMaxItemsPresent(): Promise { + return this.maxItems.isVisible(); + } } 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 af46e02ef8..d9f43da3f8 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 @@ -36,7 +36,9 @@ import { MatMenuComponent, ViewerComponent, SidenavComponent, - PaginationComponent + PaginationComponent, + ErrorComponent, + ShareDialogComponent } from '../components'; export class PersonalFilesPage extends BasePage { @@ -58,6 +60,8 @@ export class PersonalFilesPage extends BasePage { public sidenav = new SidenavComponent(this.page); public createFromTemplateDialogComponent = new CreateFromTemplateDialogComponent(this.page); public pagination = new PaginationComponent(this.page); + public errorDialog = new ErrorComponent(this.page); + public shareDialog= new ShareDialogComponent(this.page); async selectCreateFolder(): Promise { await this.acaHeader.createButton.click(); 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 09a57e123e..848f5367c3 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, SidenavComponent, Breadcrumb } from '../components'; +import { DataTableComponent, MatMenuComponent, ViewerComponent, SidenavComponent, Breadcrumb, PaginationComponent } from '../components'; import { AcaHeader } from '../components/aca-header.component'; import { AdfFolderDialogComponent, ViewerOverlayDialogComponent } from '../components/dialogs'; @@ -43,4 +43,5 @@ export class TrashPage extends BasePage { public viewerDialog = new ViewerOverlayDialogComponent(this.page); public sidenav = new SidenavComponent(this.page); public breadcrumb = new Breadcrumb(this.page); + public pagination = new PaginationComponent(this.page); } diff --git a/projects/aca-playwright-shared/src/resources/test-data/test-data-files-folders.ts b/projects/aca-playwright-shared/src/resources/test-data/test-data-files-folders.ts index 97c76b84ee..bcbe9b0a9d 100644 --- a/projects/aca-playwright-shared/src/resources/test-data/test-data-files-folders.ts +++ b/projects/aca-playwright-shared/src/resources/test-data/test-data-files-folders.ts @@ -51,8 +51,8 @@ export const folderFavFile = { // ---- multiple selection --- -const multipleSelContextMenu = ['Download', 'Favorite', 'Move', 'Copy', 'Delete', 'Permissions']; -const multipleSelToolbarMore = ['Favorite', 'Move', 'Copy', 'Delete', 'Permissions']; +const multipleSelContextMenu = ['Download', 'Favorite', 'Move', 'Copy', 'Delete']; +const multipleSelToolbarMore = ['Favorite', 'Move', 'Copy', 'Delete']; export const multipleSelFile = { contextMenu: multipleSelContextMenu, diff --git a/projects/aca-playwright-shared/src/utils/utils.ts b/projects/aca-playwright-shared/src/utils/utils.ts index d11ba3ed3f..3bc69e54ee 100644 --- a/projects/aca-playwright-shared/src/utils/utils.ts +++ b/projects/aca-playwright-shared/src/utils/utils.ts @@ -42,4 +42,8 @@ export class Utils { return run(retry); } + + static formatDate(date: string): string { + return new Date(date).toLocaleDateString('en-US'); + } } diff --git a/projects/aca-shared/rules/src/app.rules.spec.ts b/projects/aca-shared/rules/src/app.rules.spec.ts index 4f3a848a60..7aac081b48 100644 --- a/projects/aca-shared/rules/src/app.rules.spec.ts +++ b/projects/aca-shared/rules/src/app.rules.spec.ts @@ -849,12 +849,17 @@ describe('app.evaluators', () => { expect(app.canManagePermissions(context)).toBe(false); }); + it('should return false if many nodes are selected', () => { + context.selection.count = 2; + expect(app.canManagePermissions(context)).toBe(false); + }); + it('should return false if the selected node is a smart folder', () => { context.selection.first = { entry: { aspectNames: ['smf:customConfigSmartFolder'], isFolder: true } } as NodeEntry; expect(app.canManagePermissions(context)).toBe(false); }); - it('should return true if user can update the selected node and it is not a trashcan nor smart folder', () => { + it('should return true if user can update the selected node and it is not a trashcan nor smart folder nor multiselect', () => { expect(app.canManagePermissions(context)).toBe(true); }); }); diff --git a/projects/aca-shared/rules/src/app.rules.ts b/projects/aca-shared/rules/src/app.rules.ts index c73796b16c..482178e245 100644 --- a/projects/aca-shared/rules/src/app.rules.ts +++ b/projects/aca-shared/rules/src/app.rules.ts @@ -505,7 +505,7 @@ export const canEditAspects = (context: RuleContext): boolean => * @param context Rule execution context */ export const canManagePermissions = (context: RuleContext): boolean => - [canUpdateSelectedNode(context), navigation.isNotTrashcan(context), !isSmartFolder(context)].every(Boolean); + [canUpdateSelectedNode(context), navigation.isNotTrashcan(context), !isSmartFolder(context), !isMultiselection(context)].every(Boolean); /** * Checks if user can toggle **Edit Offline** mode for selected node.