From a2df5d2b0bb486a46ead010b25a21695893fc9c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20=C5=9Awiderski?= Date: Tue, 7 Apr 2026 14:12:45 +0200 Subject: [PATCH] [ACS-11446] Automated last test cases from ACA test execution & various refactoring (#5134) * [ACS-11446] Automated last test cases from ACA test execution & various refactoring * [ACS-11446] sonar cloud fix 1 * [ACS-11446] spinner changes reverted & spinner component added to the base component * [ACS-11446] sonar fix 2 * [ACS-11446] copilot review fixes 1 * [ACS-11446] copilot review fixes 2 * [ACS-11446] copilot review fixes 3 * [ACS=11446] review fixes 1 * [ACS-11446] excluded upload dialog tests --- .../copy-move-actions/src/tests/copy.e2e.ts | 54 +++-- .../copy-move-actions/src/tests/move.e2e.ts | 6 +- .../src/tests/create-library.e2e.ts | 6 +- .../src/tests/create-rules.e2e.ts | 116 ++++++++--- .../src/tests/transform-rules.e2e.ts | 2 +- .../info-drawer/src/tests/edit-mode.e2e.ts | 193 ++++++++++++++++++ .../src/tests/file-folder-properties.e2e.ts | 23 +-- .../src/tests/personal-files.e2e.ts | 2 +- .../src/tests/search-filters-general.e2e.ts | 6 +- .../src/tests/search-filters-logic.e2e.ts | 14 +- .../src/tests/search-filters-tags.e2e.ts | 14 +- .../src/tests/search-highlighting.e2e.ts | 6 +- .../src/tests/search-results-general.e2e.ts | 2 +- .../search/src/tests/search-sorting.e2e.ts | 2 +- .../search/search-results-general.e2e.ts | 2 +- .../exclude.tests.json | 8 +- .../src/tests/viewer-version-actions.e2e.ts | 2 +- .../src/api/nodes-api.ts | 74 +++---- .../src/api/sites-api.ts | 35 ++-- .../aca-playwright-shared/src/api/tags-api.ts | 55 +++-- .../components/adf-info-drawer.component.ts | 8 +- .../page-objects/components/base.component.ts | 20 -- .../dataTable/data-table.component.ts | 22 +- .../dialogs/content-node-selector-dialog.ts | 1 + .../dialogs/link-rules.component.ts | 1 + ...er.component.ts => edit-mode.component.ts} | 29 +-- .../src/page-objects/components/index.ts | 2 +- .../search-filters-location.component.ts | 2 +- .../search-filters-properties.component.ts | 2 +- .../search-filters-tags.component.ts | 4 +- .../search-sorting-picker.components.ts | 2 +- .../src/page-objects/pages/base.page.ts | 6 +- .../src/page-objects/pages/login.page.ts | 2 +- .../page-objects/pages/personal-files.page.ts | 4 +- .../src/page-objects/pages/search.page.ts | 2 +- .../src/page-objects/playwright-base.ts | 10 +- .../aca-playwright-shared/src/utils/logger.ts | 80 ++------ 37 files changed, 503 insertions(+), 316 deletions(-) create mode 100755 e2e/playwright/info-drawer/src/tests/edit-mode.e2e.ts rename projects/aca-playwright-shared/src/page-objects/components/{spinner.component.ts => edit-mode.component.ts} (53%) diff --git a/e2e/playwright/copy-move-actions/src/tests/copy.e2e.ts b/e2e/playwright/copy-move-actions/src/tests/copy.e2e.ts index 1d1942554..6a319ecf7 100644 --- a/e2e/playwright/copy-move-actions/src/tests/copy.e2e.ts +++ b/e2e/playwright/copy-move-actions/src/tests/copy.e2e.ts @@ -82,25 +82,29 @@ test.describe('Copy actions', () => { } }; + const copyFolderAndVerifyContent = async (personalFilesPage: PersonalFilesPage) => { + await Utils.reloadPageIfRowNotVisible(personalFilesPage, sourceFolder); + await copyContentInPersonalFiles(personalFilesPage, [sourceFolder], destinationFolder); + expect.soft(await personalFilesPage.dataTable.isItemPresent(sourceFolder)).toBe(true); + await personalFilesPage.dataTable.performClickFolderOrFileToOpen(destinationFolder); + await personalFilesPage.spinnerWaitForReload(); + expect.soft(await personalFilesPage.dataTable.isItemPresent(sourceFolder)).toBe(true); + await personalFilesPage.dataTable.performClickFolderOrFileToOpen(sourceFolder); + await personalFilesPage.spinnerWaitForReload(); + expect(await personalFilesPage.dataTable.isItemPresent(sourceFileInsideFolder)).toBe(true); + }; + test('[XAT-4941] Copy a file', async ({ personalFiles }) => { await Utils.reloadPageIfRowNotVisible(personalFiles, sourceFile); await copyContentInPersonalFiles(personalFiles, [sourceFile], destinationFolder); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFile)).toBe(true); await personalFiles.dataTable.performClickFolderOrFileToOpen(destinationFolder); - await personalFiles.spinner.waitForReload(); + await personalFiles.spinnerWaitForReload(); expect(await personalFiles.dataTable.isItemPresent(sourceFile)).toBe(true); }); test('[XAT-4942] Copy a folder with content', async ({ personalFiles }) => { - await Utils.reloadPageIfRowNotVisible(personalFiles, sourceFolder); - await copyContentInPersonalFiles(personalFiles, [sourceFolder], destinationFolder); - expect.soft(await personalFiles.dataTable.isItemPresent(sourceFolder)).toBe(true); - await personalFiles.dataTable.performClickFolderOrFileToOpen(destinationFolder); - await personalFiles.spinner.waitForReload(); - expect.soft(await personalFiles.dataTable.isItemPresent(sourceFolder)).toBe(true); - await personalFiles.dataTable.performClickFolderOrFileToOpen(sourceFolder); - await personalFiles.spinner.waitForReload(); - expect(await personalFiles.dataTable.isItemPresent(sourceFileInsideFolder)).toBe(true); + await copyFolderAndVerifyContent(personalFiles); }); test('[XAT-4943] Copy multiple items', async ({ personalFiles }) => { @@ -109,7 +113,7 @@ test.describe('Copy actions', () => { expect.soft(await personalFiles.dataTable.isItemPresent(sourceFolder)).toBe(true); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFile)).toBe(true); await personalFiles.dataTable.performClickFolderOrFileToOpen(destinationFolder); - await personalFiles.spinner.waitForReload(); + await personalFiles.spinnerWaitForReload(); await Utils.reloadPageIfRowNotVisible(personalFiles, sourceFolder); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFolder)).toBe(true); expect(await personalFiles.dataTable.isItemPresent(sourceFile)).toBe(true); @@ -122,7 +126,7 @@ test.describe('Copy actions', () => { await copyContentInPersonalFiles(personalFiles, [sourceFile], destinationFolder); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFile)).toBe(true); await personalFiles.dataTable.performClickFolderOrFileToOpen(destinationFolder); - await personalFiles.spinner.waitForReload(); + await personalFiles.spinnerWaitForReload(); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFile)).toBe(true); expect(await personalFiles.dataTable.isItemPresent(expectedNameForCopiedFile)).toBe(true); }); @@ -135,10 +139,10 @@ test.describe('Copy actions', () => { await copyContentInPersonalFiles(personalFiles, [sourceFolder], destinationFolder); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFolder)).toBe(true); await personalFiles.dataTable.performClickFolderOrFileToOpen(destinationFolder); - await personalFiles.spinner.waitForReload(); + await personalFiles.spinnerWaitForReload(); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFolder)).toBe(true); await personalFiles.dataTable.performClickFolderOrFileToOpen(sourceFolder); - await personalFiles.spinner.waitForReload(); + await personalFiles.spinnerWaitForReload(); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFileInsideFolder)).toBe(true); expect(await personalFiles.dataTable.isItemPresent(expectedNameForCopiedFile)).toBe(true); }); @@ -150,22 +154,14 @@ test.describe('Copy actions', () => { await copyContentInPersonalFiles(personalFiles, [sourceFile], destinationFolder); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFile)).toBe(true); await personalFiles.dataTable.performClickFolderOrFileToOpen(destinationFolder); - await personalFiles.spinner.waitForReload(); + await personalFiles.spinnerWaitForReload(); expect(await personalFiles.dataTable.isItemPresent(sourceFile)).toBe(true); }); test('[XAT-4948] Copy folder that contains locked file', async ({ personalFiles }) => { const lockType = 'ALLOW_OWNER_CHANGES'; await nodesApi.lockNodes([sourceFileInsideFolderId], lockType); - await Utils.reloadPageIfRowNotVisible(personalFiles, sourceFolder); - await copyContentInPersonalFiles(personalFiles, [sourceFolder], destinationFolder); - expect.soft(await personalFiles.dataTable.isItemPresent(sourceFolder)).toBe(true); - await personalFiles.dataTable.performClickFolderOrFileToOpen(destinationFolder); - await personalFiles.spinner.waitForReload(); - expect.soft(await personalFiles.dataTable.isItemPresent(sourceFolder)).toBe(true); - await personalFiles.dataTable.performClickFolderOrFileToOpen(sourceFolder); - await personalFiles.spinner.waitForReload(); - expect(await personalFiles.dataTable.isItemPresent(sourceFileInsideFolder)).toBe(true); + await copyFolderAndVerifyContent(personalFiles); }); test('[XAT-4949] Undo copy of files', async ({ personalFiles }) => { @@ -174,7 +170,7 @@ test.describe('Copy actions', () => { await personalFiles.snackBar.actionButton.click(); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFile)).toBe(true); await personalFiles.dataTable.performClickFolderOrFileToOpen(destinationFolder); - await personalFiles.spinner.waitForReload(); + await personalFiles.spinnerWaitForReload(); expect(await personalFiles.dataTable.isItemPresent(sourceFile)).toBe(false); }); @@ -184,7 +180,7 @@ test.describe('Copy actions', () => { await personalFiles.snackBar.actionButton.click(); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFolder)).toBe(true); await personalFiles.dataTable.performClickFolderOrFileToOpen(destinationFolder); - await personalFiles.spinner.waitForReload(); + await personalFiles.spinnerWaitForReload(); expect(await personalFiles.dataTable.isItemPresent(sourceFolder)).toBe(false); }); @@ -196,7 +192,7 @@ test.describe('Copy actions', () => { await personalFiles.snackBar.actionButton.click(); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFile)).toBe(true); await personalFiles.dataTable.performClickFolderOrFileToOpen(destinationFolder); - await personalFiles.spinner.waitForReload(); + await personalFiles.spinnerWaitForReload(); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFile)).toBe(true); expect(await personalFiles.dataTable.isItemPresent(expectedNameForCopiedFile)).toBe(false); }); @@ -210,10 +206,10 @@ test.describe('Copy actions', () => { await personalFiles.snackBar.actionButton.click(); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFolder)).toBe(true); await personalFiles.dataTable.performClickFolderOrFileToOpen(destinationFolder); - await personalFiles.spinner.waitForReload(); + await personalFiles.spinnerWaitForReload(); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFolder)).toBe(true); await personalFiles.dataTable.performClickFolderOrFileToOpen(sourceFolder); - await personalFiles.spinner.waitForReload(); + await personalFiles.spinnerWaitForReload(); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFileInsideFolder)).toBe(true); expect(await personalFiles.dataTable.isItemPresent(expectedNameForCopiedFile)).toBe(false); }); diff --git a/e2e/playwright/copy-move-actions/src/tests/move.e2e.ts b/e2e/playwright/copy-move-actions/src/tests/move.e2e.ts index be1a04946..f19825513 100644 --- a/e2e/playwright/copy-move-actions/src/tests/move.e2e.ts +++ b/e2e/playwright/copy-move-actions/src/tests/move.e2e.ts @@ -71,7 +71,7 @@ test.describe('Move actions', () => { const moveContentInPersonalFiles = async (personalFilesPage: PersonalFilesPage, sourceFileList: string[], destinationName: string) => { await personalFilesPage.copyOrMoveContentInDatatable(sourceFileList, destinationName, 'Move'); - await personalFilesPage.spinner.waitForReload(); + await personalFilesPage.spinnerWaitForReload(); }; test('[XAT-4996] Move a file', async ({ personalFiles }) => { @@ -146,7 +146,7 @@ test.describe('Move actions', () => { await Utils.reloadPageIfRowNotVisible(personalFiles, sourceFile); await moveContentInPersonalFiles(personalFiles, [sourceFile], destinationFolder); await personalFiles.snackBar.actionButton.click(); - await personalFiles.spinner.waitForReload(); + await personalFiles.spinnerWaitForReload(); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFile)).toBe(true); await personalFiles.dataTable.performClickFolderOrFileToOpen(destinationFolder); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFile)).toBe(false); @@ -158,7 +158,7 @@ test.describe('Move actions', () => { await Utils.reloadPageIfRowNotVisible(personalFiles, sourceFolder); await moveContentInPersonalFiles(personalFiles, [sourceFolder], destinationFolder); await personalFiles.snackBar.actionButton.click(); - await personalFiles.spinner.waitForReload(); + await personalFiles.spinnerWaitForReload(); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFolder)).toBe(true); await personalFiles.dataTable.performClickFolderOrFileToOpen(destinationFolder); expect.soft(await personalFiles.dataTable.isItemPresent(sourceFolder)).toBe(false); diff --git a/e2e/playwright/create-actions/src/tests/create-library.e2e.ts b/e2e/playwright/create-actions/src/tests/create-library.e2e.ts index 869a1db66..16992f0d1 100644 --- a/e2e/playwright/create-actions/src/tests/create-library.e2e.ts +++ b/e2e/playwright/create-actions/src/tests/create-library.e2e.ts @@ -129,7 +129,7 @@ test.describe('Create Libraries ', () => { }); test('[XAT-5120] Create a moderated library', async ({ myLibrariesPage }) => { - await libraryDialog.createLibraryWithNameAndId(randomLibraryName, randomLibraryId, null, moderatedVisibility); + await libraryDialog.createLibraryWithNameAndId(randomLibraryName, randomLibraryId, undefined, moderatedVisibility); await expect(libraryBreadcrumb.getItemByTitle(randomLibraryName)).toBeVisible(); await myLibrariesPage.navigate(); @@ -140,7 +140,7 @@ test.describe('Create Libraries ', () => { }); test('[XAT-5121] Create a private library', async ({ myLibrariesPage }) => { - await libraryDialog.createLibraryWithNameAndId(randomLibraryName, randomLibraryId, null, privateVisibility); + await libraryDialog.createLibraryWithNameAndId(randomLibraryName, randomLibraryId, undefined, privateVisibility); await expect(libraryBreadcrumb.getItemByTitle(randomLibraryName)).toBeVisible(); await myLibrariesPage.navigate(); @@ -149,7 +149,7 @@ test.describe('Create Libraries ', () => { createdLibrariesIds.push(randomLibraryId); }); - test('[XAT-5122] Create a library with a given ID and description', async ({ myLibrariesPage }) => { + test('[XAT-5122] [XAT-863] Create a library with a given ID and description', async ({ myLibrariesPage }) => { const libraryViewDetails = myLibrariesPage.acaHeader.viewDetails; const libraryDetails = myLibrariesPage.libraryDetails; diff --git a/e2e/playwright/folder-rules/src/tests/create-rules.e2e.ts b/e2e/playwright/folder-rules/src/tests/create-rules.e2e.ts index a8b432aca..548e67db7 100644 --- a/e2e/playwright/folder-rules/src/tests/create-rules.e2e.ts +++ b/e2e/playwright/folder-rules/src/tests/create-rules.e2e.ts @@ -32,18 +32,48 @@ test.describe('Folder Rules Actions', () => { let nodesApi: NodesApi; let trashcanApi: TrashcanApi; const username = `user-e2e-${Utils.random()}`; - const randomFolderName1 = `folder-name-${Utils.random()}`; - const randomFolderName2 = `folder-name-2-${Utils.random()}`; - const randomFolderName3 = `folder-name-3-${Utils.random()}`; + const folder883 = `folder-883-${Utils.random()}`; + const folder884 = `folder-884-${Utils.random()}`; + const folder884Second = `folder-884-2-${Utils.random()}`; + const folder885 = `folder-885-${Utils.random()}`; + const folder885Child = `folder-885-child-${Utils.random()}`; + const folder886 = `folder-886-${Utils.random()}`; + const folder887 = `folder-887-${Utils.random()}`; + const folder888 = `folder-888-${Utils.random()}`; + const folder889 = `folder-889-${Utils.random()}`; + const folder889Second = `folder-889-2-${Utils.random()}`; + const folder890 = `folder-890-${Utils.random()}`; + const folder891 = `folder-891-${Utils.random()}`; + const folder892 = `folder-892-${Utils.random()}`; + const folder893 = `folder-893-${Utils.random()}`; + const folder894 = `folder-894-${Utils.random()}`; + const folder895 = `folder-895-${Utils.random()}`; + const folder896 = `folder-896-${Utils.random()}`; + const folder910 = `folder-910-${Utils.random()}`; let randomRuleName: string; const copyFileName = `copy-file-${Utils.random()}`; const specialChars = '!@£$%^&*()~#/'; const testString = '"!@£$%^&*()_+{}|:""?><,/.\';][=-`~"'; - let randomFolderName1Id: string; - let randomFolderName2Id: string; - let randomFolderName3Id: string; + let folder883Id: string; + let folder884Id: string; + let folder884SecondId: string; + let folder885Id: string; + let folder885ChildId: string; + let folder886Id: string; + let folder887Id: string; + let folder888Id: string; + let folder889Id: string; + let folder889SecondId: string; + let folder890Id: string; + let folder891Id: string; + let folder892Id: string; + let folder893Id: string; + let folder894Id: string; + let folder895Id: string; + let folder896Id: string; + let folder910Id: string; test.beforeAll(async () => { try { @@ -53,12 +83,28 @@ test.describe('Folder Rules Actions', () => { trashcanApi = await TrashcanApi.initialize(username, username); } catch (error) { console.error(`beforeAll failed : ${error}`); + throw error; } - randomFolderName1Id = (await nodesApi.createFolder(randomFolderName1)).entry.id; - randomFolderName2Id = (await nodesApi.createFolder(randomFolderName2)).entry.id; - randomFolderName3Id = (await nodesApi.createFolder(randomFolderName3, randomFolderName1Id)).entry.id; - await nodesApi.createFile(copyFileName, randomFolderName1Id); + folder883Id = (await nodesApi.createFolder(folder883)).entry.id; + folder884Id = (await nodesApi.createFolder(folder884)).entry.id; + folder884SecondId = (await nodesApi.createFolder(folder884Second)).entry.id; + folder885Id = (await nodesApi.createFolder(folder885)).entry.id; + folder885ChildId = (await nodesApi.createFolder(folder885Child, folder885Id)).entry.id; + folder886Id = (await nodesApi.createFolder(folder886)).entry.id; + folder887Id = (await nodesApi.createFolder(folder887)).entry.id; + folder888Id = (await nodesApi.createFolder(folder888)).entry.id; + folder889Id = (await nodesApi.createFolder(folder889)).entry.id; + folder889SecondId = (await nodesApi.createFolder(folder889Second)).entry.id; + folder890Id = (await nodesApi.createFolder(folder890)).entry.id; + folder891Id = (await nodesApi.createFolder(folder891)).entry.id; + folder892Id = (await nodesApi.createFolder(folder892)).entry.id; + folder893Id = (await nodesApi.createFolder(folder893)).entry.id; + folder894Id = (await nodesApi.createFolder(folder894)).entry.id; + folder895Id = (await nodesApi.createFolder(folder895)).entry.id; + folder896Id = (await nodesApi.createFolder(folder896)).entry.id; + folder910Id = (await nodesApi.createFolder(folder910)).entry.id; + await nodesApi.createFile(copyFileName, folder889Id); }); test.beforeEach(async ({ loginPage }) => { @@ -71,7 +117,7 @@ test.describe('Folder Rules Actions', () => { }); test('[XAT-883] Create a rule with symbols in its name and description', async ({ personalFiles, nodesPage }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder883Id}/rules` }); await nodesPage.rulesToolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(testString); await nodesPage.manageRulesDialog.ruleDescriptionInputLocator.fill(testString); @@ -83,37 +129,37 @@ test.describe('Folder Rules Actions', () => { }); test('[XAT-884] Create a rule and link it to an existing folder', async ({ personalFiles, nodesPage }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder884Id}/rules` }); await nodesPage.rulesToolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.actionsDropdown.selectAction(ActionType.IncrementCounter, 0); await nodesPage.manageRulesDialog.createRuleButton.click(); - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName2Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder884SecondId}/rules` }); await nodesPage.rulesToolbar.clickLinkRulesButton(); await nodesPage.linkRulesDialog.waitForLinkRules(); await nodesPage.linkRulesDialog.getFolderIcon.click(); await nodesPage.linkRulesDialog.getOptionLocator(username).click(); - await nodesPage.linkRulesDialog.selectDestination(randomFolderName1); + await nodesPage.linkRulesDialog.selectDestination(folder884); await nodesPage.linkRulesDialog.selectFolderButton.click(); await nodesPage.manageRules.checkIfRuleIsOnTheList(randomRuleName); }); test('[XAT-885] Create a rule in a folder and inherit it in a subfolder (Rule applies to subfolders)', async ({ personalFiles, nodesPage }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder885Id}/rules` }); await nodesPage.rulesToolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.actionsDropdown.selectAction(ActionType.IncrementCounter, 0); await nodesPage.manageRulesDialog.ruleSubfoldersCheckbox.click(); await nodesPage.manageRulesDialog.createRuleButton.click(); - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName3Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder885ChildId}/rules` }); await nodesPage.manageRules.checkIfRuleIsOnTheList(randomRuleName); }); test('[XAT-886] Create a rule and press cancel', async ({ personalFiles, nodesPage }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder886Id}/rules` }); await nodesPage.rulesToolbar.clickCreateRuleButton(); await expect(nodesPage.manageRulesDialog.createRuleButton).toBeDisabled(); await nodesPage.manageRulesDialog.cancelRuleButton.click(); @@ -121,7 +167,7 @@ test.describe('Folder Rules Actions', () => { }); test('[XAT-887] Create a disabled rule', async ({ personalFiles, nodesPage }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder887Id}/rules` }); await nodesPage.rulesToolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.actionsDropdown.selectAction(ActionType.IncrementCounter, 0); @@ -136,7 +182,7 @@ test.describe('Folder Rules Actions', () => { const specialiseTypeValue = 'Action Base Type [act:actionbase]'; const simpleWorkFlow = 'accept reject'; - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder888Id}/rules` }); await nodesPage.rulesToolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); @@ -156,7 +202,7 @@ test.describe('Folder Rules Actions', () => { }); test('[XAT-889] Create a rule which runs when items are deleted or leave a folder', async ({ nodesPage, personalFiles }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder889Id}/rules` }); await nodesPage.rulesToolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.manageRulesDialog.whenCreatedCheckbox.click(); @@ -165,23 +211,23 @@ test.describe('Folder Rules Actions', () => { await nodesPage.manageRulesDialog.destinationFolderButton.click(); await nodesPage.contentNodeSelectorDialog.getFolderIcon.click(); await nodesPage.contentNodeSelectorDialog.getOptionLocator(username).click(); - await nodesPage.contentNodeSelectorDialog.selectDestination(randomFolderName2); + await nodesPage.contentNodeSelectorDialog.selectDestination(folder889Second); await nodesPage.contentNodeSelectorDialog.actionButton.click(); await nodesPage.manageRulesDialog.createRuleButton.click(); await nodesPage.manageRules.checkIfRuleIsOnTheList(randomRuleName); - await personalFiles.navigate({ remoteUrl: `#/personal-files/${randomFolderName1Id}` }); + await personalFiles.navigate({ remoteUrl: `#/personal-files/${folder889Id}` }); await personalFiles.dataTable.selectItems(copyFileName); await personalFiles.acaHeader.clickMoreActions(); await personalFiles.acaHeader.matMenu.clickMenuItem('Delete'); await personalFiles.snackBar.message.waitFor({ state: 'visible' }); - await personalFiles.navigate({ remoteUrl: `#/personal-files/${randomFolderName2Id}` }); + await personalFiles.navigate({ remoteUrl: `#/personal-files/${folder889SecondId}` }); await expect(personalFiles.dataTable.getRowByName(copyFileName)).toBeVisible(); }); test('[XAT-890] Create a rule which adds multiple aspects when its ran', async ({ nodesPage, personalFiles }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder890Id}/rules` }); await nodesPage.rulesToolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.actionsDropdown.selectAction(ActionType.AddAspect, 0); @@ -205,7 +251,7 @@ test.describe('Folder Rules Actions', () => { }); test('[XAT-891] Prevent rule creation after clicking on cancel during selecting destination folder', async ({ nodesPage, personalFiles }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder891Id}/rules` }); await nodesPage.rulesToolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.actionsDropdown.selectAction(ActionType.AddAspect, 0); @@ -218,7 +264,7 @@ test.describe('Folder Rules Actions', () => { }); test('[XAT-892] Prevent rule creation when missing any required field for action', async ({ nodesPage, personalFiles }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder892Id}/rules` }); await nodesPage.rulesToolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.actionsDropdown.selectAction(ActionType.AddAspect, 0); @@ -229,7 +275,7 @@ test.describe('Folder Rules Actions', () => { }); test('[XAT-893] Removing values from required fields should restore disabled state for Create button', async ({ nodesPage, personalFiles }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder893Id}/rules` }); await nodesPage.rulesToolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.actionsDropdown.selectAction(ActionType.AddAspect, 0); @@ -240,7 +286,7 @@ test.describe('Folder Rules Actions', () => { }); test('[XAT-894] Create rule with filled required fields and empty optional fields', async ({ nodesPage, personalFiles }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder894Id}/rules` }); await nodesPage.rulesToolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.actionsDropdown.selectAction(ActionType.AddAspect, 0); @@ -254,7 +300,7 @@ test.describe('Folder Rules Actions', () => { personalFiles, nodesPage }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder895Id}/rules` }); await nodesPage.rulesToolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.conditionsDropdown.addCondition(Field.Size, specialChars, 0, Comparator.Equals); @@ -269,7 +315,7 @@ test.describe('Folder Rules Actions', () => { }); test('[XAT-896] Create a rule with multiple groups utilising all available comparators and conditions', async ({ personalFiles, nodesPage }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder896Id}/rules` }); await nodesPage.rulesToolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.conditionsDropdown.addConditionGroup(Field.Size, specialChars, 0, Comparator.Equals); @@ -282,4 +328,14 @@ test.describe('Folder Rules Actions', () => { await nodesPage.manageRules.checkIfRuleIsOnTheList(randomRuleName); }); + + test('[XAT-910] Create a rule and disable it', async ({ personalFiles, nodesPage }) => { + await personalFiles.navigate({ remoteUrl: `#/nodes/${folder910Id}/rules` }); + await nodesPage.rulesToolbar.clickCreateRuleButton(); + await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); + await nodesPage.actionsDropdown.selectAction(ActionType.IncrementCounter, 0); + await nodesPage.manageRulesDialog.createRuleButton.click(); + await nodesPage.manageRules.ruleToggle.click(); + await expect(nodesPage.manageRules.ruleToggleFalse).toBeVisible(); + }); }); diff --git a/e2e/playwright/folder-rules/src/tests/transform-rules.e2e.ts b/e2e/playwright/folder-rules/src/tests/transform-rules.e2e.ts index 71f5d70cb..b42119a82 100644 --- a/e2e/playwright/folder-rules/src/tests/transform-rules.e2e.ts +++ b/e2e/playwright/folder-rules/src/tests/transform-rules.e2e.ts @@ -97,7 +97,7 @@ async function verifyTransformation( await personalFiles.navigate(); await personalFiles.dataTable.performClickFolderOrFileToOpen(destinationFolderName); - await personalFiles.spinner.waitForReload(); + await personalFiles.spinnerWaitForReload(); for (const file of files) { const transformedFileName = `${file.name}.${expectedExtension}`; const exists = await personalFiles.dataTable.isItemPresent(transformedFileName); diff --git a/e2e/playwright/info-drawer/src/tests/edit-mode.e2e.ts b/e2e/playwright/info-drawer/src/tests/edit-mode.e2e.ts new file mode 100755 index 000000000..5f06e54ac --- /dev/null +++ b/e2e/playwright/info-drawer/src/tests/edit-mode.e2e.ts @@ -0,0 +1,193 @@ +/*! + * Copyright © 2005-2025 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, Utils, test, TrashcanApi, NodesApi, FileActionsApi, TagsApi, PersonalFilesPage } from '@alfresco/aca-playwright-shared'; + +async function navigateAndOpenExpandedInfoDrawer(personalFiles: PersonalFilesPage, folderName: string): Promise { + await personalFiles.navigate(); + await Utils.reloadPageIfRowNotVisible(personalFiles, folderName); + await expect(personalFiles.dataTable.getRowByName(folderName)).toBeVisible(); + await personalFiles.dataTable.getRowByName(folderName).click(); + await personalFiles.acaHeader.viewDetails.click(); + await personalFiles.infoDrawer.expandDetailsButton.click(); +} + +async function openTagsEditMode(personalFiles: PersonalFilesPage): Promise { + await personalFiles.nodeInfoEditMode.tagsAccordionPenButton.scrollIntoViewIfNeeded(); + await personalFiles.nodeInfoEditMode.tagsAccordionPenButton.click(); +} + +test.describe('Edit Mode - Tags and Categories', () => { + const apiClientFactory = new ApiClientFactory(); + let nodesApi: NodesApi; + let trashcanApi: TrashcanApi; + let fileActionsApi: FileActionsApi; + let tagsApi: TagsApi; + let folder919: string; + let folder938: string; + let folder939: string; + let folder942: string; + const testRunId = Utils.random(); + const tag938 = `xat-938-${Utils.random()}`; + const tag939SharedName = `xat-939-${testRunId}`; + const tag939One = `${tag939SharedName}-1-${Utils.random()}`; + const tag939Two = `${tag939SharedName}-2-${Utils.random()}`; + const tag939Three = `${tag939SharedName}-3-${Utils.random()}`; + const tag942 = `xat-942-${Utils.random()}`; + const username = `edit-mode-${Utils.random()}`; + + test.beforeAll(async () => { + try { + await apiClientFactory.setUpAcaBackend('admin'); + await apiClientFactory.createUser({ username }); + nodesApi = await NodesApi.initialize(username, username); + trashcanApi = await TrashcanApi.initialize(username, username); + fileActionsApi = await FileActionsApi.initialize(username, username); + tagsApi = await TagsApi.initialize('admin'); + } catch (error) { + console.error(`beforeAll failed : ${error}`); + throw error; + } + }); + + test.beforeEach(async ({ loginPage }) => { + await Utils.tryLoginUser(loginPage, username, username, 'beforeEach failed'); + }); + + test.afterAll(async () => { + await Utils.deleteNodesSitesEmptyTrashcan(nodesApi, trashcanApi, 'afterAll failed'); + for (const tag of [tag938, tag939One, tag939Two, tag939Three]) { + await tagsApi.deleteTagByTagName(tag); + } + }); + + test.describe('Edit Mode - XAT-919', () => { + test.beforeAll(async () => { + try { + folder919 = `folder-919-${Utils.random()}`; + await nodesApi.createFolder(folder919); + + await fileActionsApi.waitForNodes(folder919, { expect: 1 }); + } catch (error) { + console.error(`beforeAll failed : ${error}`); + throw error; + } + }); + + test('[XAT-919] Input fields in edit mode', async ({ personalFiles }) => { + await navigateAndOpenExpandedInfoDrawer(personalFiles, folder919); + await personalFiles.nodeInfoEditMode.categoriesAccordionPenButton.scrollIntoViewIfNeeded(); + await personalFiles.nodeInfoEditMode.categoriesAccordionPenButton.click(); + await expect(personalFiles.nodeInfoEditMode.categoriesInput).toBeEnabled(); + await openTagsEditMode(personalFiles); + await expect(personalFiles.nodeInfoEditMode.tagsInput).toBeEnabled(); + }); + }); + + test.describe('Edit Mode - XAT-938', () => { + test.beforeAll(async () => { + try { + folder938 = `folder-938-${Utils.random()}`; + await nodesApi.createFolder(folder938); + await tagsApi.createTags(tag938); + + await fileActionsApi.waitForNodes(folder938, { expect: 1 }); + } catch (error) { + console.error(`beforeAll failed : ${error}`); + throw error; + } + }); + + test.afterAll(async () => { + await tagsApi.deleteTagByTagName(tag938); + }); + + test('[XAT-938] Select an existing tag', async ({ personalFiles }) => { + await navigateAndOpenExpandedInfoDrawer(personalFiles, folder938); + await openTagsEditMode(personalFiles); + await personalFiles.nodeInfoEditMode.tagsInput.fill(tag938); + await personalFiles.nodeInfoEditMode.existingTags.getByText(tag938).click(); + await personalFiles.nodeInfoEditMode.tagsAccordionConfirmButton.click(); + await expect(personalFiles.nodeInfoEditMode.tagsChips.getByText(tag938)).toBeVisible(); + }); + }); + + test.describe('Edit Mode - XAT-939', () => { + test.beforeAll(async () => { + try { + folder939 = `folder-939-${Utils.random()}`; + await nodesApi.createFolder(folder939); + await tagsApi.createTags(tag939One, tag939Two, tag939Three); + + await fileActionsApi.waitForNodes(folder939, { expect: 1 }); + } catch (error) { + console.error(`beforeAll failed : ${error}`); + throw error; + } + }); + + test.afterAll(async () => { + for (const tag of [tag939One, tag939Two, tag939Three]) { + await tagsApi.deleteTagByTagName(tag); + } + }); + + test('[XAT-939] List of tags ready to be assigned', async ({ personalFiles }) => { + await navigateAndOpenExpandedInfoDrawer(personalFiles, folder939); + await openTagsEditMode(personalFiles); + await personalFiles.nodeInfoEditMode.tagsInput.fill(tag939SharedName); + await personalFiles.nodeInfoEditMode.existingTags.first().waitFor(); + expect(await personalFiles.nodeInfoEditMode.existingTags.count()).toEqual(3); + }); + }); + + test.describe('Edit Mode - XAT-942', () => { + test.beforeAll(async () => { + try { + folder942 = `folder-942-${Utils.random()}`; + await nodesApi.createFolder(folder942); + + await fileActionsApi.waitForNodes(folder942, { expect: 1 }); + } catch (error) { + console.error(`beforeAll failed : ${error}`); + throw error; + } + }); + + test.afterAll(async () => { + await tagsApi.deleteTagByTagName(tag942); + }); + + test('[XAT-942] Creating tags in edit mode', async ({ personalFiles }) => { + await navigateAndOpenExpandedInfoDrawer(personalFiles, folder942); + await openTagsEditMode(personalFiles); + await personalFiles.nodeInfoEditMode.tagsInput.fill(tag942); + await personalFiles.nodeInfoEditMode.createTagButton.click(); + await personalFiles.nodeInfoEditMode.tagsChips.getByText(tag942).click(); + await personalFiles.nodeInfoEditMode.tagsAccordionConfirmButton.click(); + await expect(personalFiles.nodeInfoEditMode.tagsChips.getByText(tag942)).toBeVisible(); + }); + }); +}); diff --git a/e2e/playwright/info-drawer/src/tests/file-folder-properties.e2e.ts b/e2e/playwright/info-drawer/src/tests/file-folder-properties.e2e.ts index 64f227aad..9a8692daa 100755 --- a/e2e/playwright/info-drawer/src/tests/file-folder-properties.e2e.ts +++ b/e2e/playwright/info-drawer/src/tests/file-folder-properties.e2e.ts @@ -44,7 +44,6 @@ test.describe('Info Drawer - file folder Properties', () => { let tagsApi: TagsApi; let categoriesApi: CategoriesApi; let responseCategoryId: string; - let responseTagsId: string; let folder17240Id: string; let folder17242Id: string; let folder5513Id: string; @@ -80,19 +79,13 @@ test.describe('Info Drawer - file folder Properties', () => { if ('entry' in createdCategory) { return createdCategory.entry.id; } else { - console.error('Unexpected response format:', createdCategory); - return null; + throw new Error(`Unexpected response format: ${JSON.stringify(createdCategory)}`); } } async function createTagGetId(): Promise { - const createdTag = await tagsApi.createTags([tagBody]); - if ('entry' in createdTag) { - return (createdTag as { entry: { id: string } }).entry.id; - } else { - console.error('Unexpected response format:', createdTag); - return null; - } + const [createdTag] = await tagsApi.createTags(tagBody.tag); + return createdTag.entry.id; } async function checkNodeFields(personalFiles: PersonalFilesPage, isFolder?: 'isFolder'): Promise { @@ -135,11 +128,7 @@ test.describe('Info Drawer - file folder Properties', () => { tagsApi = await TagsApi.initialize('admin'); categoriesApi = await CategoriesApi.initialize('admin'); responseCategoryId = await createCategoryGetId(); - responseTagsId = await createTagGetId(); - - if (responseCategoryId === null || responseTagsId === null) { - throw new Error('Failed to create category or tag - check API manually'); - } + await createTagGetId(); await nodesApi.createFolder(folder5512); folder5513Id = (await nodesApi.createFolder(folder5513)).entry.id; @@ -180,7 +169,7 @@ test.describe('Info Drawer - file folder Properties', () => { test.afterAll(async () => { await Utils.deleteNodesSitesEmptyTrashcan(nodesApi, trashcanApi, 'afterAll failed'); await categoriesApi.deleteCategory(responseCategoryId); - await tagsApi.deleteTagsByTagName(tagsPhraseForDeletion); + await tagsApi.deleteTagByTagName(tagsPhraseForDeletion); }); test('[XAT-5512] View properties - Default tabs', async ({ personalFiles }) => { @@ -295,7 +284,7 @@ test.describe('Info Drawer - file folder Properties', () => { await fileActionsApi.waitForNodes(folder17240, { expect: 1 }); await tagsApi.assignTagToNode(folder17240Id, tagBody); await expect(async () => { - expect((await tagsApi.listTagsForNode(folder17240Id)).list.entries.length).toEqual(1); + expect((await tagsApi.listTagsForNode(folder17240Id)).list?.entries.length).toEqual(1); }).toPass({ intervals: [1_000], timeout: timeouts.large diff --git a/e2e/playwright/list-views/src/tests/personal-files.e2e.ts b/e2e/playwright/list-views/src/tests/personal-files.e2e.ts index 98e7ad757..b3d4fc64b 100644 --- a/e2e/playwright/list-views/src/tests/personal-files.e2e.ts +++ b/e2e/playwright/list-views/src/tests/personal-files.e2e.ts @@ -139,7 +139,7 @@ test.describe('Personal Files', () => { await personalFiles.pagination.clickOnNextPage(); expect(await personalFiles.pagination.getCurrentPage()).toBe('Page 2'); await personalFiles.breadcrumb.clickItem(abbreviateFolderMain); - await personalFiles.spinner.spinnerWaitForReload(); + await personalFiles.spinnerWaitForReload(); expect(await personalFiles.pagination.getRange()).toContain('Showing 1-1'); }); }); diff --git a/e2e/playwright/search/src/tests/search-filters-general.e2e.ts b/e2e/playwright/search/src/tests/search-filters-general.e2e.ts index fd96c52bb..e61daaf88 100644 --- a/e2e/playwright/search/src/tests/search-filters-general.e2e.ts +++ b/e2e/playwright/search/src/tests/search-filters-general.e2e.ts @@ -89,7 +89,7 @@ test.describe('Search - Filters - General', () => { await searchPage.searchFilters.propertiesFilter.click(); await searchPage.searchFiltersProperties.fileSizeInput.fill('1000'); await searchPage.searchMenuCard.menuCardApply.click(); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); const filterTextAfter = await searchPage.searchFilters.propertiesFilter.textContent(); await searchPage.searchFilters.propertiesFilter.click(); @@ -106,14 +106,14 @@ test.describe('Search - Filters - General', () => { await searchPage.searchFilters.propertiesFilter.click(); await searchPage.searchFiltersProperties.fileSizeInput.fill('1000'); await searchPage.searchMenuCard.menuCardApply.click(); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); const propertiesFilterTextAfter = await searchPage.searchFilters.propertiesFilter.textContent(); const logicFilterTextBefore = await searchPage.searchFilters.logicFilter.textContent(); await searchPage.searchFilters.logicFilter.click(); await searchPage.searchFiltersLogic.matchAllInput.fill('test'); await searchPage.searchMenuCard.menuCardApply.click(); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); const logicFilterTextAfter = await searchPage.searchFilters.logicFilter.textContent(); await searchPage.searchFilters.resetButton.click(); diff --git a/e2e/playwright/search/src/tests/search-filters-logic.e2e.ts b/e2e/playwright/search/src/tests/search-filters-logic.e2e.ts index 0e35ada81..db87f8bdf 100644 --- a/e2e/playwright/search/src/tests/search-filters-logic.e2e.ts +++ b/e2e/playwright/search/src/tests/search-filters-logic.e2e.ts @@ -81,7 +81,7 @@ test.describe('Search - Filters - Logic', () => { `${logicFile1NameSplit[0]} ${logicFile1NameSplit[1]} ${logicFile1TitleSplit[1]} ${logicFile1DescriptionSplit[1]}` ); await searchPage.searchMenuCard.menuCardApply.click(); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); await expect(searchPage.dataTable.getRowByName(logicFile1.name)).toBeVisible(); await expect(searchPage.dataTable.getRowByName(logicFile2.name)).toBeHidden(); @@ -93,7 +93,7 @@ test.describe('Search - Filters - Logic', () => { `${logicFile1NameSplit[2]}-${logicFile1NameSplit[3]} ${logicFile1TitleSplit[0]} ${logicFile1DescriptionSplit[0]}` ); await searchPage.searchMenuCard.menuCardApply.click(); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); expect(await searchPage.dataTable.getRowsCount()).toBe(2); await expect(searchPage.dataTable.getRowByName(logicFile1.name)).toBeVisible(); @@ -107,7 +107,7 @@ test.describe('Search - Filters - Logic', () => { ); await searchPage.searchFiltersLogic.excludeInput.fill(`${logicFile1DescriptionSplit[1]}`); await searchPage.searchMenuCard.menuCardApply.click(); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); expect(await searchPage.dataTable.getRowsCount()).toBe(1); await expect(searchPage.dataTable.getRowByName(logicFile1.name)).toBeHidden(); @@ -118,7 +118,7 @@ test.describe('Search - Filters - Logic', () => { await searchPage.searchFilters.logicFilter.click(); await searchPage.searchFiltersLogic.matchExactInput.fill(logicFile1.name); await searchPage.searchMenuCard.menuCardApply.click(); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); expect(await searchPage.dataTable.getRowsCount()).toBe(1); await expect(searchPage.dataTable.getRowByName(logicFile2.name)).toBeHidden(); @@ -127,7 +127,7 @@ test.describe('Search - Filters - Logic', () => { await searchPage.searchFilters.logicFilter.click(); await searchPage.searchFiltersLogic.matchExactInput.fill(logicFile1.title); await searchPage.searchMenuCard.menuCardApply.click(); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); expect(await searchPage.dataTable.getRowsCount()).toBe(1); await expect(searchPage.dataTable.getRowByName(logicFile2.name)).toBeHidden(); @@ -136,7 +136,7 @@ test.describe('Search - Filters - Logic', () => { await searchPage.searchFilters.logicFilter.click(); await searchPage.searchFiltersLogic.matchExactInput.fill(logicFile1.description); await searchPage.searchMenuCard.menuCardApply.click(); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); expect(await searchPage.dataTable.getRowsCount()).toBe(1); await expect(searchPage.dataTable.getRowByName(logicFile2.name)).toBeHidden(); @@ -152,7 +152,7 @@ test.describe('Search - Filters - Logic', () => { await searchPage.searchFiltersLogic.excludeInput.fill(`${logicFile1NameSplit[3]}`); await searchPage.searchFiltersLogic.matchExactInput.fill(`${logicFile2NameSplit[1]}-${logicFile2NameSplit[2]}-${logicFile2NameSplit[3]}`); await searchPage.searchMenuCard.menuCardApply.click(); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); expect(await searchPage.dataTable.getRowsCount()).toBe(1); await expect(searchPage.dataTable.getRowByName(logicFile1.name)).toBeHidden(); diff --git a/e2e/playwright/search/src/tests/search-filters-tags.e2e.ts b/e2e/playwright/search/src/tests/search-filters-tags.e2e.ts index 5225cbb47..da7ca17f3 100644 --- a/e2e/playwright/search/src/tests/search-filters-tags.e2e.ts +++ b/e2e/playwright/search/src/tests/search-filters-tags.e2e.ts @@ -24,7 +24,7 @@ import { expect } from '@playwright/test'; import { ApiClientFactory, Utils, test, NodesApi, TrashcanApi, TagsApi, FileActionsApi } from '@alfresco/aca-playwright-shared'; -import { TagPaging } from '@alfresco/js-api'; +import { TagEntry } from '@alfresco/js-api'; test.describe('Search - Filters - Tags', () => { let nodesApi: NodesApi; @@ -33,7 +33,7 @@ test.describe('Search - Filters - Tags', () => { let fileActionsApi: FileActionsApi; let file1Id: string; let file2Id: string; - let tagPaging: TagPaging; + let createdTags: TagEntry[]; const random = Utils.random(); const username = `user1-${random}`; @@ -60,7 +60,7 @@ test.describe('Search - Filters - Tags', () => { file2Id = node2.entry.id; await fileActionsApi.waitForNodes(file1, { expect: 1 }); await fileActionsApi.waitForNodes(file2, { expect: 1 }); - tagPaging = (await tagsApiAdmin.createTags(tags)) as TagPaging; + createdTags = await tagsApiAdmin.createTags(...tags.map((t) => t.tag)); await tagsApiAdmin.assignTagToNode(file1Id, tags[0]); await tagsApiAdmin.assignTagToNode(file2Id, tags[1]); } catch (error) { @@ -70,19 +70,19 @@ test.describe('Search - Filters - Tags', () => { test.afterAll(async () => { await Utils.deleteNodesSitesEmptyTrashcan(nodesApi, trashcanApi, 'afterAll failed'); - await tagsApiAdmin.deleteTags([`${tagPaging.list.entries[0].entry.id}`]); - await tagsApiAdmin.deleteTags([`${tagPaging.list.entries[1].entry.id}`]); + await tagsApiAdmin.deleteTags({ id: createdTags[0].entry.id, tag: createdTags[0].entry.tag }); + await tagsApiAdmin.deleteTags({ id: createdTags[1].entry.id, tag: createdTags[1].entry.tag }); }); test('[XAT-5581] user able to search with tags facet', async ({ searchPage }) => { await searchPage.searchWithin(random, 'files'); - await searchPage.searchFiltersTags.filterByTag(searchPage, `${tagPaging.list.entries[0].entry.tag}`); + await searchPage.searchFiltersTags.filterByTag(searchPage, createdTags[0].entry.tag); await expect(searchPage.dataTable.getRowByName(file1)).toBeVisible(); await expect(searchPage.dataTable.getRowByName(file2)).toBeHidden(); await searchPage.searchFiltersTags.clearTagFilter(searchPage); - await searchPage.searchFiltersTags.filterByTag(searchPage, `${tagPaging.list.entries[1].entry.tag}`); + await searchPage.searchFiltersTags.filterByTag(searchPage, createdTags[1].entry.tag); await expect(searchPage.dataTable.getRowByName(file1)).toBeHidden(); await expect(searchPage.dataTable.getRowByName(file2)).toBeVisible(); diff --git a/e2e/playwright/search/src/tests/search-highlighting.e2e.ts b/e2e/playwright/search/src/tests/search-highlighting.e2e.ts index f71136661..f31f4c523 100644 --- a/e2e/playwright/search/src/tests/search-highlighting.e2e.ts +++ b/e2e/playwright/search/src/tests/search-highlighting.e2e.ts @@ -67,13 +67,13 @@ test.describe('Search Highlighting', () => { test('[XAT-17119] Matching phrases should be highlighted in the file name for search results', async ({ searchPage }) => { await searchPage.searchWithin(fileNameHighlight, 'files'); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); expect(await searchPage.dataTable.hasHighlightedText('name')).toBe(true); }); test('[XAT-17120] Matching phrases should be highlighted in the file description for search results', async ({ searchPage }) => { await searchPage.searchWithin(fileDescription, 'files'); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); expect(await searchPage.dataTable.hasHighlightedText('description')).toBe(true); expect(await searchPage.dataTable.hasHighlightedText('name')).toBe(false); }); @@ -81,7 +81,7 @@ test.describe('Search Highlighting', () => { test('[XAT-17121] Matching phrases should be highlighted in the file content for search results', async ({ searchPage, personalFiles }) => { await personalFiles.navigate(); await searchPage.searchWithin(fileContent, 'files'); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); expect(await searchPage.dataTable.hasHighlightedText('content')).toBe(true); expect(await searchPage.dataTable.hasHighlightedText('name')).toBe(false); }); diff --git a/e2e/playwright/search/src/tests/search-results-general.e2e.ts b/e2e/playwright/search/src/tests/search-results-general.e2e.ts index 2b3e8e324..9389080bb 100644 --- a/e2e/playwright/search/src/tests/search-results-general.e2e.ts +++ b/e2e/playwright/search/src/tests/search-results-general.e2e.ts @@ -97,7 +97,7 @@ test.describe('Search Results - General', () => { await personalFiles.navigate(); await personalFiles.page.goto(url); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); expect(await searchPage.dataTable.isItemPresent(site)).toBe(true); }); diff --git a/e2e/playwright/search/src/tests/search-sorting.e2e.ts b/e2e/playwright/search/src/tests/search-sorting.e2e.ts index e7727a153..9d5b99325 100644 --- a/e2e/playwright/search/src/tests/search-sorting.e2e.ts +++ b/e2e/playwright/search/src/tests/search-sorting.e2e.ts @@ -122,7 +122,7 @@ test.describe('Search sorting', () => { await searchPage.searchWithin(`*${random}*`, 'files'); } await searchPage.searchSortingPicker.sortBy(sortBy, sortOrder); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); expect(await searchPage.dataTable.getNthRow(0).textContent()).toContain(expectedFirstFile); expect(await searchPage.dataTable.getNthRow(1).textContent()).toContain(expectedSecondFile); } diff --git a/e2e/playwright/smoke-test/src/tests/search/search-results-general.e2e.ts b/e2e/playwright/smoke-test/src/tests/search/search-results-general.e2e.ts index b848c2816..783ff1970 100644 --- a/e2e/playwright/smoke-test/src/tests/search/search-results-general.e2e.ts +++ b/e2e/playwright/smoke-test/src/tests/search/search-results-general.e2e.ts @@ -77,7 +77,7 @@ test.describe('Search Results - General', () => { await personalFiles.navigate(); await personalFiles.page.goto(url); - await searchPage.dataTable.progressBarWaitForReload(); + await searchPage.dataTable.spinnerWaitForReload(); expect(await searchPage.dataTable.isItemPresent(site)).toBe(true); }); diff --git a/e2e/playwright/upload-download-actions/exclude.tests.json b/e2e/playwright/upload-download-actions/exclude.tests.json index 19ead4630..6a17ee8c9 100644 --- a/e2e/playwright/upload-download-actions/exclude.tests.json +++ b/e2e/playwright/upload-download-actions/exclude.tests.json @@ -1,3 +1,9 @@ { - "all": {} + "all": { + "XAT-5353": "https://hyland.atlassian.net/browse/ACS-11471", + "XAT-5354": "https://hyland.atlassian.net/browse/ACS-11471", + "XAT-5327": "https://hyland.atlassian.net/browse/ACS-11471", + "XAT-5328": "https://hyland.atlassian.net/browse/ACS-11471", + "XAT-5329": "https://hyland.atlassian.net/browse/ACS-11471" + } } \ No newline at end of file diff --git a/e2e/playwright/viewer/src/tests/viewer-version-actions.e2e.ts b/e2e/playwright/viewer/src/tests/viewer-version-actions.e2e.ts index 6b074dab5..48c871093 100644 --- a/e2e/playwright/viewer/src/tests/viewer-version-actions.e2e.ts +++ b/e2e/playwright/viewer/src/tests/viewer-version-actions.e2e.ts @@ -98,7 +98,7 @@ test.describe('Version actions', () => { await Utils.tryLoginUser(loginPage, username, username, 'beforeEach failed'); await personalFiles.navigate(); await personalFiles.dataTable.performClickFolderOrFileToOpen(parentFolder); - await personalFiles.dataTable.progressBarWaitForReload(); + await personalFiles.dataTable.spinnerWaitForReload(); await viewFirstFileVersion(personalFiles); }); diff --git a/projects/aca-playwright-shared/src/api/nodes-api.ts b/projects/aca-playwright-shared/src/api/nodes-api.ts index ead138497..7fd806535 100755 --- a/projects/aca-playwright-shared/src/api/nodes-api.ts +++ b/projects/aca-playwright-shared/src/api/nodes-api.ts @@ -46,13 +46,12 @@ export class NodesApi { title: string = '', description: string = '', author: string = '', - aspectNames: string[] = null - ): Promise { + aspectNames: string[] = [] + ): Promise { try { - return await this.createNode('cm:folder', name, parentId, title, description, null, author, null, aspectNames); + return await this.createNode('cm:folder', name, parentId, title, description, null, author, true, aspectNames); } catch (error) { - console.error(`${this.constructor.name} ${this.createFolder.name}`, error); - return null; + throw new Error(`${this.constructor.name} ${this.createFolder.name}: ${error}`); } } @@ -63,13 +62,12 @@ export class NodesApi { description: string = '', author: string = '', majorVersion: boolean = true, - aspectNames: string[] = null + aspectNames: string[] = [] ): Promise { try { return await this.createNode('cm:content', name, parentId, title, description, null, author, majorVersion, aspectNames); } catch (error) { - console.error(`${this.constructor.name} ${this.createFile.name}`, error); - return null; + throw new Error(`${this.constructor.name} ${this.createFile.name}: ${error}`); } } @@ -77,8 +75,7 @@ export class NodesApi { try { return await this.createContent({ files: names }, relativePath); } catch (error) { - console.error(`${this.constructor.name} ${this.createFiles.name}: ${error}`); - return null; + throw new Error(`${this.constructor.name} ${this.createFiles.name}: ${error}`); } } @@ -97,8 +94,7 @@ export class NodesApi { try { return await this.createContent({ folders: names }, relativePath); } catch (error) { - console.error(`${this.constructor.name} ${this.createFolders.name}: ${error}`); - return null; + throw new Error(`${this.constructor.name} ${this.createFolders.name}: ${error}`); } } @@ -119,11 +115,8 @@ export class NodesApi { imageProps: any = null, author: string = '', majorVersion: boolean = true, - aspectNames: string[] = null - ): Promise { - if (!aspectNames) { - aspectNames = ['cm:versionable']; // workaround for REPO-4772 - } + aspectNames: string[] = [] + ): Promise { const nodeBody = { name, nodeType, @@ -144,8 +137,7 @@ export class NodesApi { majorVersion }); } catch (error) { - console.error(`${this.constructor.name} ${this.createNode.name}`, error); - return null; + throw new Error(`${this.constructor.name} ${this.createNode.name}: ${error}`); } } @@ -186,7 +178,7 @@ export class NodesApi { */ async deleteCurrentUserNodes(): Promise { try { - const userNodes = (await this.getNodeChildren('-my-')).list.entries; + const userNodes = (await this.getNodeChildren('-my-'))?.list?.entries ?? []; const userNodesIds = userNodes.map((nodeChild) => nodeChild.entry.id); await this.deleteNodes(userNodesIds); } catch (error) { @@ -218,24 +210,22 @@ export class NodesApi { try { return this.apiService.nodes.createNode('-my-', flattenNodeContentTree(content, relativePath) as any); } catch (error) { - console.error(`${this.constructor.name} ${this.createContent.name}`, error); - return null; + throw new Error(`${this.constructor.name} ${this.createContent.name}: ${error}`); } } - async getNodeById(id: string): Promise { + async getNodeById(id: string): Promise { try { return this.apiService.nodes.getNode(id); } catch (error) { - console.error(`${this.constructor.name} ${this.getNodeById.name}`, error); - return null; + throw new Error(`${this.constructor.name} ${this.getNodeById.name}: ${error}`); } } async getNodeIdFromParent(name: string, parentId: string): Promise { try { - const children = (await this.getNodeChildren(parentId)).list.entries; - return children.find((elem) => elem.entry.name === name).entry.id || ''; + const children = (await this.getNodeChildren(parentId))?.list?.entries ?? []; + return children.find((elem) => elem.entry.name === name)?.entry.id ?? ''; } catch (error) { console.error(`${this.constructor.name} ${this.getNodeIdFromParent.name}`, error); return ''; @@ -332,7 +322,7 @@ export class NodesApi { } } - async removeUserAccessOnNodeTemplate(nodeName: string): Promise { + async removeUserAccessOnNodeTemplate(nodeName: string): Promise { try { const templatesRootFolderId = await this.getNodeTemplatesFolderId(); const nodeId: string = await this.getNodeIdFromParent(nodeName, templatesRootFolderId); @@ -344,7 +334,7 @@ export class NodesApi { } } - async removeUserAccessOnSpaceTemplate(nodeName: string): Promise { + async removeUserAccessOnSpaceTemplate(nodeName: string): Promise { try { const templatesRootFolderId = await this.getSpaceTemplatesFolderId(); const nodeId: string = await this.getNodeIdFromParent(nodeName, templatesRootFolderId); @@ -371,7 +361,7 @@ export class NodesApi { } } - private async addAspects(nodeId: string, aspectNames: string[]): Promise { + private async addAspects(nodeId: string, aspectNames: string[]): Promise { try { return this.apiService.nodes.updateNode(nodeId, { aspectNames }); } catch (error) { @@ -423,7 +413,7 @@ export class NodesApi { } } - async createLinkToFileName(originalFileName: string, originalFileParentId: string, destinationParentId?: string): Promise { + async createLinkToFileName(originalFileName: string, originalFileParentId: string, destinationParentId?: string): Promise { destinationParentId ??= originalFileParentId; try { @@ -431,30 +421,27 @@ export class NodesApi { return this.createFileLink(nodeId, destinationParentId); } catch (error) { - console.error('Admin Actions - createLinkToFileName failed : ', error); - return null; + throw new Error(`Admin Actions - createLinkToFileName failed : ${error}`); } } - async createLinkToFolderName(originalFolderName: string, originalFolderParentId: string, destinationParentId?: string): Promise { + async createLinkToFolderName(originalFolderName: string, originalFolderParentId: string, destinationParentId?: string): Promise { destinationParentId ??= originalFolderParentId; try { const nodeId = await this.getNodeIdFromParent(originalFolderName, originalFolderParentId); return this.createFolderLink(nodeId, destinationParentId); } catch (error) { - console.error('Admin Actions - createLinkToFolderName failed : ', error); - return null; + throw new Error(`Admin Actions - createLinkToFolderName failed : ${error}`); } } async getNodeProperty(nodeId: string, property: string): Promise { try { const node = await this.getNodeById(nodeId); - return node.entry.properties?.[property] || ''; + return node.entry.properties?.[property] ?? ''; } catch (error) { - console.error(`${this.constructor.name} ${this.getNodeProperty.name}`, error); - return ''; + throw new Error(`${this.constructor.name} ${this.getNodeProperty.name}: ${error}`); } } @@ -463,8 +450,7 @@ export class NodesApi { const sharedId = await this.getNodeProperty(nodeId, 'qshare:sharedId'); return sharedId !== ''; } catch (error) { - console.error(`${this.constructor.name} ${this.isFileShared.name}`, error); - return null; + throw new Error(`${this.constructor.name} ${this.isFileShared.name}: ${error}`); } } @@ -473,8 +459,7 @@ export class NodesApi { const lockType = await this.getNodeProperty(nodeId, 'cm:lockType'); return lockType || ''; } catch (error) { - console.error(`${this.constructor.name} ${this.getLockType.name}`, error); - return ''; + throw new Error(`${this.constructor.name} ${this.getLockType.name}: ${error}`); } } @@ -482,8 +467,7 @@ export class NodesApi { try { return (await this.getLockType(nodeId)) === 'WRITE_LOCK'; } catch (error) { - console.error(`${this.constructor.name} ${this.isFileLockedWrite.name}`, error); - return null; + throw new Error(`${this.constructor.name} ${this.isFileLockedWrite.name}: ${error}`); } } } diff --git a/projects/aca-playwright-shared/src/api/sites-api.ts b/projects/aca-playwright-shared/src/api/sites-api.ts index 677381834..b1946331d 100755 --- a/projects/aca-playwright-shared/src/api/sites-api.ts +++ b/projects/aca-playwright-shared/src/api/sites-api.ts @@ -46,7 +46,7 @@ export class SitesApi { return classObj; } - async createSite(title: string, visibility?: string, description?: string, siteId?: string): Promise { + async createSite(title: string, visibility?: string, description?: string, siteId?: string): Promise { const site = { title, visibility: visibility || Site.VisibilityEnum.PUBLIC, @@ -57,17 +57,19 @@ export class SitesApi { try { return this.apiService.sites.createSite(site); } catch (error) { - console.error(`SitesApi createSite : catch : `, error); - return null; + throw new Error(`SitesApi ${this.createSite.name}: ${error}`); } } async getDocLibId(siteId: string): Promise { try { - return (await this.apiService.sites.listSiteContainers(siteId)).list.entries[0].entry.id; + const id = (await this.apiService.sites.listSiteContainers(siteId)).list?.entries?.[0]?.entry?.id; + if (!id) { + throw new Error(`Document library not found for site ${siteId}`); + } + return id; } catch (error) { - console.error(`SitesApi getDocLibId : catch : `, error); - return null; + throw new Error(`Failed to get document library ID for site ${siteId}: ${error}`); } } @@ -110,12 +112,10 @@ export class SitesApi { try { return this.apiService.sites.createSiteMembership(siteId, memberBody); } catch (error) { - if (error.status === 409) { + if (String(error).includes('409')) { return this.updateSiteMember(siteId, userId, role); - } else { - console.error(`SitesApi addSiteMember : catch : `, error); - return new SiteMemberEntry(); } + throw error; } } @@ -127,8 +127,7 @@ export class SitesApi { try { return this.apiService.sites.createSiteMembershipRequestForPerson(personId, body); } catch (error) { - console.error(`SitesApi createSiteMembershipRequestForPerson : catch : `, error); - return null; + throw new Error(`Failed to create site membership request for person ${personId} and site ${siteId}: ${error}`); } } @@ -136,18 +135,17 @@ export class SitesApi { try { return this.apiService.sites.approveSiteMembershipRequest(siteId, inviteeId); } catch (error) { - console.error(`SitesApi approveSiteMembershipRequest : catch : `, error); - return null; + throw new Error(`Failed to approve site membership request for invitee ${inviteeId} and site ${siteId}: ${error}`); } } async hasMembershipRequest(personId: string, siteId: string): Promise { try { - const requests = (await this.apiService.sites.listSiteMembershipRequestsForPerson(personId)).list.entries.map((e) => e.entry.id); + const entries = (await this.apiService.sites.listSiteMembershipRequestsForPerson(personId)).list?.entries ?? []; + const requests = entries.map((e) => e.entry?.id).filter((id): id is string => !!id); return requests.includes(siteId); } catch (error) { - console.error(`SitesApi hasMembershipRequest : catch : `, error); - return null; + throw new Error(`Failed to check site membership request for person ${personId} and site ${siteId}: ${error}`); } } @@ -163,8 +161,7 @@ export class SitesApi { try { return this.apiService.sites.getSite(siteId); } catch (error) { - console.error(`SitesApi getSite : catch : `, error); - return null; + throw new Error(`Failed to get site ${siteId}: ${error}`); } } } diff --git a/projects/aca-playwright-shared/src/api/tags-api.ts b/projects/aca-playwright-shared/src/api/tags-api.ts index 5d693b94d..c212a104f 100644 --- a/projects/aca-playwright-shared/src/api/tags-api.ts +++ b/projects/aca-playwright-shared/src/api/tags-api.ts @@ -22,8 +22,9 @@ * from Hyland Software. If not, see . */ -import { TagBody, TagEntry, TagPaging } from '@alfresco/js-api'; +import { TagBody, TagEntry, TagPaging, Tag } from '@alfresco/js-api'; import { ApiClientFactory } from './api-client-factory'; +import { logger } from '../utils'; export class TagsApi { private readonly apiService: ApiClientFactory; @@ -38,12 +39,29 @@ export class TagsApi { return classObj; } - async createTags(tags: TagBody[]): Promise { + async createTags(...tagNames: string[]): Promise { try { - return this.apiService.tagsApi.createTags(tags); + const results: TagEntry[] = []; + for (const tag of tagNames) { + const result = await this.apiService.tagsApi.createTags([{ tag }]); + let created: TagEntry; + if ('entry' in result) { + created = result as TagEntry; + } else if ('list' in result) { + const firstEntry = result.list?.entries?.[0]; + if (!firstEntry) { + throw new Error(`createTags returned a paging result with no entries for tag "${tag}"`); + } + created = firstEntry; + } else { + throw new Error(`createTags returned an unexpected response format for tag "${tag}"`); + } + logger.info(`Tag created: "${created.entry.tag}" (id: ${created.entry.id})`); + results.push(created); + } + return results; } catch (error) { - console.error(error); - return null; + throw new Error(`Failed to create tags: ${error}`); } } @@ -51,18 +69,19 @@ export class TagsApi { try { return this.apiService.tagsApi.assignTagToNode(nodeId, tag); } catch (error) { - console.error(error); - return null; + throw new Error(`Failed to assign tag to node: ${error}`); } } - async deleteTags(tagIds: string[]): Promise { + async deleteTags(...tags: Tag[]): Promise { try { - for (const tagId of tagIds) { - await this.apiService.tagsApi.deleteTag(tagId); + for (const { id, tag } of tags) { + await this.apiService.tagsApi.deleteTag(id); + const tagLabel = tag ? `"${tag}" ` : ''; + logger.info(`Tag deleted: ${tagLabel}(id: ${id})`); } } catch (error) { - console.error(error); + throw new Error(`Failed to delete tags: ${error}`); } } @@ -70,8 +89,7 @@ export class TagsApi { try { return this.apiService.tagsApi.listTagsForNode(nodeId); } catch (error) { - console.error(error); - return null; + throw new Error(`Failed to list tags for node: ${error}`); } } @@ -79,18 +97,17 @@ export class TagsApi { try { return this.apiService.tagsApi.listTags(params); } catch (error) { - console.error(error); - return null; + throw new Error(`Failed to list tags: ${error}`); } } - async deleteTagsByTagName(tagName: string): Promise { + async deleteTagByTagName(tagName: string): Promise { try { const response = await this.listTags({ tag: tagName, matching: true }); - const tagIds = response.list.entries.map((entry) => entry.entry.id); - await this.deleteTags(tagIds); + const tags = response.list?.entries.map((entry) => entry.entry) || []; + await this.deleteTags(...tags); } catch (error) { - console.error(error); + throw new Error(`Failed to delete tags by tag name: ${error}`); } } } diff --git a/projects/aca-playwright-shared/src/page-objects/components/adf-info-drawer.component.ts b/projects/aca-playwright-shared/src/page-objects/components/adf-info-drawer.component.ts index aa3e79eb4..14c18f455 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/adf-info-drawer.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/adf-info-drawer.component.ts @@ -102,10 +102,9 @@ export class AdfInfoDrawerComponent extends BaseComponent { async checkCommentsHeaderCount(): Promise { const commentsCountTextContent = await this.commentsHeader.textContent(); - const commentsCountString = commentsCountTextContent.match(/\d+/g)[0]; - return parseInt(commentsCountString, 10); + const commentsCountString = commentsCountTextContent?.match(/\d+/g)?.[0]; + return parseInt(commentsCountString ?? '0', 10); } - async getCommentsCountFromList(): Promise { return this.commentsList.count(); } @@ -121,9 +120,8 @@ export class AdfInfoDrawerComponent extends BaseComponent { } async getHeaderTitle(): Promise { - return this.headerTitle.textContent(); + return (await this.headerTitle.textContent()) ?? ''; } - async getTabsCount(): Promise { return this.infoDrawerTabs.count(); } diff --git a/projects/aca-playwright-shared/src/page-objects/components/base.component.ts b/projects/aca-playwright-shared/src/page-objects/components/base.component.ts index 2631fc506..74470a14a 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/base.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/base.component.ts @@ -24,11 +24,9 @@ import { Locator, Page } from '@playwright/test'; import { PlaywrightBase } from '../playwright-base'; -import { timeouts } from '../../utils'; export abstract class BaseComponent extends PlaywrightBase { private readonly rootElement: string; - private readonly progressBar = this.page.locator('[role="progressbar"]'); protected constructor(page: Page, rootElement: string) { super(page); @@ -46,22 +44,4 @@ export abstract class BaseComponent extends PlaywrightBase { getChild(cssLocator: string, options?: { hasText?: string | RegExp; has?: Locator }): Locator { return this.page.locator(`${this.rootElement} ${cssLocator}`, options); } - - async spinnerWaitForReload(): Promise { - try { - await this.page.locator('[role="progressbar"]').waitFor({ state: 'attached', timeout: timeouts.medium }); - await this.page.locator('[role="progressbar"]').waitFor({ state: 'detached', timeout: timeouts.normal }); - } catch (e) { - this.logger.info('Spinner was not present'); - } - } - - async progressBarWaitForReload(): Promise { - try { - await this.progressBar.waitFor({ state: 'visible', timeout: timeouts.medium }); - await this.progressBar.waitFor({ state: 'hidden', timeout: timeouts.normal }); - } catch (e) { - this.logger.info('Progress bar was not present'); - } - } } 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 b172a0238..44427692a 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 @@ -153,16 +153,16 @@ export class DataTableComponent extends BaseComponent { async goThroughPagesLookingForRowWithName(name: string | number): Promise { await this.spinnerWaitForReload(); if (await this.getRowByName(name).isVisible()) { - return null; + return; } if (await this.pagination.currentPageLocator.isVisible()) { if ((await this.pagination.currentPageLocator.textContent()) === ' of 1 ') { - return null; + return; } } if (await this.pagination.totalPageLocator.isVisible()) { - const maxPages = (await this.pagination.totalPageLocator?.textContent())?.match(/\d/)[0]; + const maxPages = (await this.pagination.totalPageLocator?.textContent())?.match(/\d+/)?.[0]; for (let page = 1; page <= Number(maxPages); page++) { if (await this.getRowByName(name).isVisible()) { break; @@ -216,7 +216,7 @@ export class DataTableComponent extends BaseComponent { async getItemLocationTooltip(name: string): Promise { const location = this.getItemLocationEl(name); await location.hover(); - return location.locator('a').getAttribute('title', { timeout: timeouts.normal }); + return (await location.locator('a').getAttribute('title', { timeout: timeouts.normal })) ?? ''; } async clickItemLocation(name: string): Promise { @@ -224,7 +224,7 @@ export class DataTableComponent extends BaseComponent { } async getSortingOrder(): Promise { - const str = await this.sortedColumnHeader.locator('../..').getAttribute('class'); + const str = (await this.sortedColumnHeader.locator('../..').getAttribute('class')) ?? ''; if (str.includes('asc')) { return 'asc'; } else if (str.includes('desc')) { @@ -275,10 +275,8 @@ export class DataTableComponent extends BaseComponent { const rowsCount = await this.sitesName.count(); const sitesInfo: { [siteName: string]: string } = {}; for (let i = 0; i < rowsCount; i++) { - let siteVisibilityText = await this.sitesVisibility.nth(i).textContent(); - let siteNameText = await this.sitesName.nth(i).textContent(); - siteVisibilityText = siteVisibilityText.trim().toUpperCase(); - siteNameText = siteNameText.trim(); + const siteVisibilityText = ((await this.sitesVisibility.nth(i).textContent()) ?? '').trim().toUpperCase(); + const siteNameText = ((await this.sitesName.nth(i).textContent()) ?? '').trim(); sitesInfo[siteNameText] = siteVisibilityText; } return sitesInfo; @@ -293,10 +291,8 @@ export class DataTableComponent extends BaseComponent { const rowsCount = await this.sitesName.count(); const sitesInfo: { [siteName: string]: string } = {}; for (let i = 0; i < rowsCount; i++) { - let siteNameText = await this.sitesName.nth(i).textContent(); - let siteRoleText = await this.sitesRole.nth(i).textContent(); - siteNameText = siteNameText.trim(); - siteRoleText = siteRoleText.trim(); + const siteNameText = ((await this.sitesName.nth(i).textContent()) ?? '').trim(); + const siteRoleText = ((await this.sitesRole.nth(i).textContent()) ?? '').trim(); sitesInfo[siteNameText] = siteRoleText; } return sitesInfo; diff --git a/projects/aca-playwright-shared/src/page-objects/components/dialogs/content-node-selector-dialog.ts b/projects/aca-playwright-shared/src/page-objects/components/dialogs/content-node-selector-dialog.ts index 6a7cf1d6d..af4002090 100755 --- a/projects/aca-playwright-shared/src/page-objects/components/dialogs/content-node-selector-dialog.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/dialogs/content-node-selector-dialog.ts @@ -60,6 +60,7 @@ export class ContentNodeSelectorDialog extends BaseComponent { async selectDestination(folderName: string): Promise { const row = this.getRowByName(folderName); + await row.scrollIntoViewIfNeeded(); await expect(row).toBeVisible(); await row.click({ trial: true }); diff --git a/projects/aca-playwright-shared/src/page-objects/components/dialogs/link-rules.component.ts b/projects/aca-playwright-shared/src/page-objects/components/dialogs/link-rules.component.ts index ac7d25f0e..2c8cf4668 100755 --- a/projects/aca-playwright-shared/src/page-objects/components/dialogs/link-rules.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/dialogs/link-rules.component.ts @@ -40,6 +40,7 @@ export class LinkRulesDialog extends BaseComponent { async selectDestination(folderName: string): Promise { const row = this.getRowByName(folderName); + await row.scrollIntoViewIfNeeded(); await expect(row).toBeVisible(); await row.click({ trial: true }); diff --git a/projects/aca-playwright-shared/src/page-objects/components/spinner.component.ts b/projects/aca-playwright-shared/src/page-objects/components/edit-mode.component.ts similarity index 53% rename from projects/aca-playwright-shared/src/page-objects/components/spinner.component.ts rename to projects/aca-playwright-shared/src/page-objects/components/edit-mode.component.ts index 6943995dc..067096ad6 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/spinner.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/edit-mode.component.ts @@ -22,22 +22,25 @@ * from Hyland Software. If not, see . */ -import { Page } from '@playwright/test'; import { BaseComponent } from './base.component'; +import { Page } from '@playwright/test'; -export class SpinnerComponent extends BaseComponent { - private static readonly rootElement = '[role="progressbar"]'; +export class EditModeComponent extends BaseComponent { + private static readonly rootElement = '.aca-details-container'; - constructor(page: Page, rootElement = SpinnerComponent.rootElement) { - super(page, rootElement); - } + public tagsAccordion = this.page.locator('[data-automation-id="adf-content-metadata-tags-panel"]'); + public tagsAccordionPenButton = this.tagsAccordion.locator('[data-automation-id="showing-tag-input-button"]'); + public tagsInput = this.tagsAccordion.locator('input'); + public tagsChips = this.tagsAccordion.locator('[role="listitem"]'); + public tagsAccordionConfirmButton = this.getChild('[data-automation-id="save-tags-metadata"]'); + public createTagButton = this.tagsAccordion.locator('.adf-create-tag-label'); + public existingTags = this.tagsAccordion.locator('.adf-tag'); - async waitForReload(): Promise { - try { - await this.getChild('').waitFor({ state: 'attached', timeout: 2000 }); - await this.getChild('').waitFor({ state: 'detached', timeout: 2000 }); - } catch (e) { - this.logger.info('Spinner was not present'); - } + public categoriesAccordion = this.page.locator('[data-automation-id="adf-content-metadata-categories-panel"]'); + public categoriesAccordionPenButton = this.categoriesAccordion.locator('[data-automation-id="meta-data-categories-edit"]'); + public categoriesInput = this.categoriesAccordion.locator('input'); + + constructor(page: Page) { + super(page, EditModeComponent.rootElement); } } 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 b8c4543f7..544cac71c 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/index.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/index.ts @@ -27,7 +27,6 @@ export * from './dataTable'; export * from './dialogs'; export * from './manageRules'; export * from './base.component'; -export * from './spinner.component'; export * from './actions-dropdown.component'; export * from './conditions.component'; export * from './pagination.component'; @@ -39,3 +38,4 @@ export * from './sidenav.component'; export * from './aca-header.component'; export * from './error.component'; export * from './datetime-picker/datetime-picker.component'; +export * from './edit-mode.component'; diff --git a/projects/aca-playwright-shared/src/page-objects/components/search/search-filters/search-filters-location.component.ts b/projects/aca-playwright-shared/src/page-objects/components/search/search-filters/search-filters-location.component.ts index be855455f..7e9ad25f4 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/search/search-filters/search-filters-location.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/search/search-filters/search-filters-location.component.ts @@ -44,6 +44,6 @@ export class SearchFiltersLocation extends BaseComponent { await page.searchFiltersLocation.addOptionInput.fill(location); await page.searchFiltersLocation.searchOption(location).click(); await page.searchMenuCard.menuCardApply.click(); - await page.dataTable.progressBarWaitForReload(); + await page.dataTable.spinnerWaitForReload(); } } diff --git a/projects/aca-playwright-shared/src/page-objects/components/search/search-filters/search-filters-properties.component.ts b/projects/aca-playwright-shared/src/page-objects/components/search/search-filters/search-filters-properties.component.ts index 1ddea7b78..650b9710f 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/search/search-filters/search-filters-properties.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/search/search-filters/search-filters-properties.component.ts @@ -95,6 +95,6 @@ export class SearchFiltersProperties extends BaseComponent { } await page.searchMenuCard.menuCardApply.click(); - await page.dataTable.progressBarWaitForReload(); + await page.dataTable.spinnerWaitForReload(); } } diff --git a/projects/aca-playwright-shared/src/page-objects/components/search/search-filters/search-filters-tags.component.ts b/projects/aca-playwright-shared/src/page-objects/components/search/search-filters/search-filters-tags.component.ts index 9f53656f3..b3fcb223b 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/search/search-filters/search-filters-tags.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/search/search-filters/search-filters-tags.component.ts @@ -44,12 +44,12 @@ export class SearchFiltersTags extends BaseComponent { await page.searchFiltersTags.addOptionInput.fill(tag); await this.searchOption(tag).click(); await page.searchMenuCard.menuCardApply.click(); - await page.dataTable.progressBarWaitForReload(); + await page.dataTable.spinnerWaitForReload(); } async clearTagFilter(page: SearchPage): Promise { await page.searchFilters.tagsFilter.click(); await page.searchMenuCard.menuCardClear.click(); - await page.dataTable.progressBarWaitForReload(); + await page.dataTable.spinnerWaitForReload(); } } diff --git a/projects/aca-playwright-shared/src/page-objects/components/search/search-sorting-picker.components.ts b/projects/aca-playwright-shared/src/page-objects/components/search/search-sorting-picker.components.ts index ede100a38..991fd083b 100755 --- a/projects/aca-playwright-shared/src/page-objects/components/search/search-sorting-picker.components.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/search/search-sorting-picker.components.ts @@ -80,6 +80,6 @@ export class SearchSortingPicker extends BaseComponent { await elem.click(); const directionSortElement = this.page.locator(`[id="${optionId}-${direction.toLocaleLowerCase()}"]`); await directionSortElement.click(); - await this.progressBarWaitForReload(); + await this.spinnerWaitForReload(); } } diff --git a/projects/aca-playwright-shared/src/page-objects/pages/base.page.ts b/projects/aca-playwright-shared/src/page-objects/pages/base.page.ts index 0459af610..5d4d1c09c 100644 --- a/projects/aca-playwright-shared/src/page-objects/pages/base.page.ts +++ b/projects/aca-playwright-shared/src/page-objects/pages/base.page.ts @@ -24,7 +24,7 @@ import { Page } from '@playwright/test'; import { PlaywrightBase } from '../playwright-base'; -import { SnackBarComponent, SpinnerComponent } from '../components'; +import { SnackBarComponent } from '../components'; export interface NavigateOptions { query?: string; @@ -36,14 +36,12 @@ export abstract class BasePage extends PlaywrightBase { private readonly pageUrl: string; private readonly urlRequest: RegExp; public snackBar: SnackBarComponent; - public spinner: SpinnerComponent; protected constructor(page: Page, pageUrl: string, urlRequest?: RegExp) { super(page); this.pageUrl = pageUrl; this.urlRequest = urlRequest; this.snackBar = new SnackBarComponent(this.page); - this.spinner = new SpinnerComponent(this.page); } /** @@ -81,7 +79,7 @@ export abstract class BasePage extends PlaywrightBase { timeout: 60000 }); } - await this.spinner.waitForReload(); + await this.spinnerWaitForReload(); } async reload(options?: Pick): Promise { diff --git a/projects/aca-playwright-shared/src/page-objects/pages/login.page.ts b/projects/aca-playwright-shared/src/page-objects/pages/login.page.ts index 5cbc0290f..f9d325621 100644 --- a/projects/aca-playwright-shared/src/page-objects/pages/login.page.ts +++ b/projects/aca-playwright-shared/src/page-objects/pages/login.page.ts @@ -53,7 +53,7 @@ export class LoginPage extends BasePage { await this.submitButton.click(); if (options?.waitForLoading) { - await Promise.all([this.page.waitForLoadState('domcontentloaded'), this.spinner.waitForReload()]); + await Promise.all([this.page.waitForLoadState('domcontentloaded'), this.spinnerWaitForReload()]); } } 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 6222aca02..6e70e2377 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 @@ -46,7 +46,8 @@ import { UploadDialog, SnackBarComponent, EditDialog, - FolderInformationDialogComponent + FolderInformationDialogComponent, + EditModeComponent } from '../components'; export class PersonalFilesPage extends BasePage { @@ -72,6 +73,7 @@ export class PersonalFilesPage extends BasePage { public shareDialog = new ShareDialogComponent(this.page); public confirmDialog = new AdfConfirmDialogComponent(this.page); public infoDrawer = new AdfInfoDrawerComponent(this.page); + public nodeInfoEditMode = new EditModeComponent(this.page); public uploadNewVersionDialog = new UploadNewVersionDialog(this.page); public manageVersionsDialog = new ManageVersionsDialog(this.page); public uploadDialog = new UploadDialog(this.page); diff --git a/projects/aca-playwright-shared/src/page-objects/pages/search.page.ts b/projects/aca-playwright-shared/src/page-objects/pages/search.page.ts index cd930a1be..eaacdcac2 100644 --- a/projects/aca-playwright-shared/src/page-objects/pages/search.page.ts +++ b/projects/aca-playwright-shared/src/page-objects/pages/search.page.ts @@ -100,7 +100,7 @@ export class SearchPage extends BasePage { await this.searchInDialog.applyButton.click(); await this.clickSearchButton(); await this.searchInputComponent.searchFor(searchText); - await this.dataTable.progressBarWaitForReload(); + await this.dataTable.spinnerWaitForReload(); } async clickSearchButton() { diff --git a/projects/aca-playwright-shared/src/page-objects/playwright-base.ts b/projects/aca-playwright-shared/src/page-objects/playwright-base.ts index 847722e20..f2461e24f 100644 --- a/projects/aca-playwright-shared/src/page-objects/playwright-base.ts +++ b/projects/aca-playwright-shared/src/page-objects/playwright-base.ts @@ -23,7 +23,7 @@ */ import { Page } from '@playwright/test'; -import { GenericLogger, LoggerLike } from '../utils'; +import { logger, LoggerLike, timeouts } from '../utils'; export abstract class PlaywrightBase { public page: Page; @@ -31,6 +31,12 @@ export abstract class PlaywrightBase { protected constructor(page: Page) { this.page = page; - this.logger = new GenericLogger(process.env.PLAYWRIGHT_CUSTOM_LOG_LEVEL); + this.logger = logger; + } + + async spinnerWaitForReload(): Promise { + const spinner = this.page.locator('[role="progressbar"]'); + await spinner.waitFor({ state: 'attached', timeout: timeouts.medium }).catch(() => {}); + await spinner.waitFor({ state: 'detached', timeout: timeouts.normal }).catch(() => {}); } } diff --git a/projects/aca-playwright-shared/src/utils/logger.ts b/projects/aca-playwright-shared/src/utils/logger.ts index 44180687c..3120ecb97 100644 --- a/projects/aca-playwright-shared/src/utils/logger.ts +++ b/projects/aca-playwright-shared/src/utils/logger.ts @@ -24,65 +24,29 @@ /* eslint-disable @typescript-eslint/naming-convention */ -export const infoColor = '\x1b[36m%s\x1b[0m'; -export const logColor = '\x1b[35m%s\x1b[0m'; -export const warnColor = '\x1b[33m%s\x1b[0m'; -export const errorColor = '\x1b[31m%s\x1b[0m'; - -export type LOG_LEVEL = 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'SILENT'; - -export class LogLevelsEnum extends Number { - public static readonly TRACE: number = 5; - public static readonly DEBUG: number = 4; - public static readonly INFO: number = 3; - public static readonly WARN: number = 2; - public static readonly ERROR: number = 1; - public static readonly SILENT: number = 0; -} - -export const logLevels: { level: LogLevelsEnum; name: LOG_LEVEL }[] = [ - { level: LogLevelsEnum.TRACE, name: 'TRACE' }, - { level: LogLevelsEnum.DEBUG, name: 'DEBUG' }, - { level: LogLevelsEnum.INFO, name: 'INFO' }, - { level: LogLevelsEnum.WARN, name: 'WARN' }, - { level: LogLevelsEnum.ERROR, name: 'ERROR' }, - { level: LogLevelsEnum.SILENT, name: 'SILENT' } -]; - export interface LoggerLike { - info(...messages: string[]): void; - log(...messages: string[]): void; - warn(...messages: string[]): void; - error(...messages: string[]): void; + info(message: string): void; + log(message: string): void; + warn(message: string): void; + error(message: string): void; + table(message: object): void; } -/* eslint-disable no-console */ -export class GenericLogger implements LoggerLike { - private readonly level: LogLevelsEnum; +const reset = '\x1b[0m'; +const red = '\x1b[31m'; +const yellow = '\x1b[33m'; +const blue = '\x1b[34m'; +const cyan = '\x1b[36m'; - constructor(logLevel: string) { - this.level = logLevels.find(({ name }) => name === logLevel)?.level || LogLevelsEnum.ERROR; - } - - info(...messages: string[]): void { - if (Number(this.level) >= LogLevelsEnum.INFO) { - console.log(infoColor, messages.join('')); - } - } - - log(...messages: string[]): void { - if (Number(this.level) >= LogLevelsEnum.TRACE) { - console.log(logColor, messages.join('')); - } - } - - warn(...messages: string[]): void { - if (Number(this.level) >= LogLevelsEnum.WARN) { - console.log(warnColor, messages.join('')); - } - } - - error(...messages: string[]): void { - console.log(errorColor, messages.join('')); - } -} +export const logger: LoggerLike = { + // eslint-disable-next-line no-console,no-restricted-syntax + info: (message: string) => console.info(`${blue}[INFO]${reset}`, message), + // eslint-disable-next-line no-console,no-restricted-syntax + log: (message: string) => console.log(`${cyan}[LOG]${reset}`, message), + // eslint-disable-next-line no-console,no-restricted-syntax + error: (message: string) => console.error(`${red}[ERROR]${reset}`, message), + // eslint-disable-next-line no-console,no-restricted-syntax + warn: (message: string) => console.warn(`${yellow}[WARN]${reset}`, message), + // eslint-disable-next-line no-console,no-restricted-syntax + table: (message: object) => console.table(message) +};