diff --git a/.eslintrc.json b/.eslintrc.json index ef1204f45..f4daca6c9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -354,7 +354,7 @@ "playwright/no-useless-not": "warn", "playwright/no-wait-for-selector": "warn", "playwright/no-wait-for-timeout": "off", - "playwright/prefer-web-first-assertions": "warn", + "playwright/prefer-web-first-assertions": "off", "playwright/valid-describe-callback": "warn", "playwright/valid-expect": "warn", "playwright/valid-expect-in-promise": "off", diff --git a/e2e/playwright/folder-rules/exclude.tests.json b/e2e/playwright/folder-rules/exclude.tests.json index c5d26040d..495a42bc8 100644 --- a/e2e/playwright/folder-rules/exclude.tests.json +++ b/e2e/playwright/folder-rules/exclude.tests.json @@ -1,3 +1,3 @@ { - "C691642": "https://hyland.atlassian.net/browse/ACS-4894" + "XAT-897": "https://hyland.atlassian.net/browse/ACS-5503" } 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 9a3117e25..cb1caff55 100644 --- a/e2e/playwright/folder-rules/src/tests/create-rules.e2e.ts +++ b/e2e/playwright/folder-rules/src/tests/create-rules.e2e.ts @@ -31,18 +31,18 @@ test.describe('Folder Rules Actions', () => { let nodesApi: NodesApi; let trashcanApi: TrashcanApi; const username = `user-e2e-${Utils.random()}`; - const folderName883 = `folder-XAT-883-${Utils.random()}`; - const folderName883_2 = `folder-XAT-883-2-${Utils.random()}`; - const folderName883_3 = `folder-XAT-883-3-${Utils.random()}`; + const randomFolderName1 = `folder-name-${Utils.random()}`; + const randomFolderName2 = `folder-name-2-${Utils.random()}`; + const randomFolderName3 = `folder-name-3-${Utils.random()}`; let randomRuleName: string; - const copyFileName = `copy-file-XAT-888-${Utils.random()}`; + const copyFileName = `copy-file-${Utils.random()}`; const specialChars = '!@£$%^&*()~#/'; const testString = '"!@£$%^&*()_+{}|:""?><,/.\';][=-`~"'; - let folderName883Id: string; - let folderName883Id2: string; - let folderName883Id3: string; + let randomFolderName1Id: string; + let randomFolderName2Id: string; + let randomFolderName3Id: string; test.beforeAll(async () => { try { @@ -54,10 +54,10 @@ test.describe('Folder Rules Actions', () => { console.error(`beforeAll failed : ${error}`); } - folderName883Id = (await nodesApi.createFolder(folderName883)).entry.id; - folderName883Id2 = (await nodesApi.createFolder(folderName883_2)).entry.id; - folderName883Id3 = (await nodesApi.createFolder(folderName883_3, folderName883Id)).entry.id; - await nodesApi.createFile(copyFileName, folderName883Id); + 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); }); test.beforeEach(async ({ loginPage }) => { @@ -70,7 +70,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/${folderName883Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); await nodesPage.toolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(testString); await nodesPage.manageRulesDialog.ruleDescriptionInputLocator.fill(testString); @@ -82,37 +82,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/${folderName883Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); await nodesPage.toolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.actionsDropdown.selectAction(ActionType.IncrementCounter, 0); await nodesPage.manageRulesDialog.createRuleButton.click(); - await personalFiles.navigate({ remoteUrl: `#/nodes/${folderName883Id2}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName2Id}/rules` }); await nodesPage.toolbar.clickLinkRulesButton(); await nodesPage.linkRulesDialog.waitForLinkRules(); await nodesPage.linkRulesDialog.getFolderIcon.click(); await nodesPage.linkRulesDialog.getOptionLocator(username).click(); - await nodesPage.linkRulesDialog.selectDestination(folderName883); + await nodesPage.linkRulesDialog.selectDestination(randomFolderName1); 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/${folderName883Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); await nodesPage.toolbar.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/${folderName883Id3}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName3Id}/rules` }); await nodesPage.manageRules.checkIfRuleIsOnTheList(randomRuleName); }); test('[XAT-886] Create a rule and press cancel', async ({ personalFiles, nodesPage }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${folderName883Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); await nodesPage.toolbar.clickCreateRuleButton(); await expect(nodesPage.manageRulesDialog.createRuleButton).toBeDisabled(); await nodesPage.manageRulesDialog.cancelRuleButton.click(); @@ -120,7 +120,7 @@ test.describe('Folder Rules Actions', () => { }); test('[XAT-887] Create a disabled rule', async ({ personalFiles, nodesPage }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${folderName883Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); await nodesPage.toolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.actionsDropdown.selectAction(ActionType.IncrementCounter, 0); @@ -135,7 +135,7 @@ test.describe('Folder Rules Actions', () => { const autoDeclareOptionsValue = 'For all major and minor versions [ALL]'; const simpleWorkFlow = 'accept reject'; - await personalFiles.navigate({ remoteUrl: `#/nodes/${folderName883Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); await nodesPage.toolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); @@ -156,7 +156,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/${folderName883Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); await nodesPage.toolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.manageRulesDialog.whenCreatedCheckbox.click(); @@ -165,23 +165,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(folderName883_2); + await nodesPage.contentNodeSelectorDialog.selectDestination(randomFolderName2); await nodesPage.contentNodeSelectorDialog.actionButton.click(); await nodesPage.manageRulesDialog.createRuleButton.click(); await nodesPage.manageRules.checkIfRuleIsOnTheList(randomRuleName); - await personalFiles.navigate({ remoteUrl: `#/personal-files/${folderName883Id}` }); + await personalFiles.navigate({ remoteUrl: `#/personal-files/${randomFolderName1Id}` }); await personalFiles.dataTable.selectItem(copyFileName); await personalFiles.acaHeader.clickMoreActions(); await personalFiles.acaHeader.matMenu.clickMenuItem('Delete'); await personalFiles.snackBar.message.waitFor({ state: 'visible' }); - await personalFiles.navigate({ remoteUrl: `#/personal-files/${folderName883Id2}` }); + await personalFiles.navigate({ remoteUrl: `#/personal-files/${randomFolderName2Id}` }); 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/${folderName883Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); await nodesPage.toolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.actionsDropdown.selectAction(ActionType.AddAspect, 0); @@ -200,7 +200,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/${folderName883Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); await nodesPage.toolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.actionsDropdown.selectAction(ActionType.AddAspect, 0); @@ -213,7 +213,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/${folderName883Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); await nodesPage.toolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.actionsDropdown.selectAction(ActionType.AddAspect, 0); @@ -224,7 +224,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/${folderName883Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); await nodesPage.toolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.actionsDropdown.selectAction(ActionType.AddAspect, 0); @@ -235,7 +235,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/${folderName883Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); await nodesPage.toolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.actionsDropdown.selectAction(ActionType.AddAspect, 0); @@ -249,7 +249,7 @@ test.describe('Folder Rules Actions', () => { personalFiles, nodesPage }) => { - await personalFiles.navigate({ remoteUrl: `#/nodes/${folderName883Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); await nodesPage.toolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.conditionsDropdown.addCondition(Field.Size, specialChars, 0, Comparator.Equals); @@ -264,7 +264,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/${folderName883Id}/rules` }); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); await nodesPage.toolbar.clickCreateRuleButton(); await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(randomRuleName); await nodesPage.conditionsDropdown.addConditionGroup(Field.Size, specialChars, 0, Comparator.Equals); diff --git a/e2e/playwright/folder-rules/src/tests/manage-rules.e2e.ts b/e2e/playwright/folder-rules/src/tests/manage-rules.e2e.ts deleted file mode 100644 index bb914471e..000000000 --- a/e2e/playwright/folder-rules/src/tests/manage-rules.e2e.ts +++ /dev/null @@ -1,56 +0,0 @@ -/*! - * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. - * - * Alfresco Example Content Application - * - * This file is part of the Alfresco Example Content Application. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * The Alfresco Example Content Application is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * The Alfresco Example Content Application is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * from Hyland Software. If not, see . - */ - -import { ApiClientFactory, getUserState, test, Utils } from '@alfresco/playwright-shared'; -import { expect } from '@playwright/test'; - -test.use({ storageState: getUserState('hruser') }); -test.describe('Rules - Manage Rules', () => { - const apiClientFactory = new ApiClientFactory(); - const randomName = `folder-rules-manage-rules-${Utils.random()}`; - const randomRuleName = `playwright-rule-${Utils.random()}`; - - let folderId: string; - - test.beforeAll(async () => { - await apiClientFactory.setUpAcaBackend('hruser'); - const node = await apiClientFactory.nodes.createNode('-my-', { name: randomName, nodeType: 'cm:folder' }); - folderId = node.entry.id; - await apiClientFactory.createRandomRule(folderId, randomRuleName); - }); - - test.beforeEach(async ({ personalFiles }) => { - await personalFiles.navigate(); - }); - - test.afterAll(async () => { - await apiClientFactory.nodes.deleteNode(folderId, { permanent: true }); - }); - - test('[C691642] Create a rule and disable it', async ({ personalFiles, nodesPage }) => { - await personalFiles.dataTable.performActionFromExpandableMenu(randomName, 'Manage rules'); - await nodesPage.manageRules.ruleToggle.click(); - await expect(nodesPage.manageRules.ruleToggleFalse).toBeVisible(); - }); -}); diff --git a/e2e/playwright/folder-rules/src/tests/update-rules.e2e.ts b/e2e/playwright/folder-rules/src/tests/update-rules.e2e.ts new file mode 100644 index 000000000..7780d0000 --- /dev/null +++ b/e2e/playwright/folder-rules/src/tests/update-rules.e2e.ts @@ -0,0 +1,159 @@ +/*! + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Alfresco Example Content Application + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * from Hyland Software. If not, see . + */ + +import { ApiClientFactory, test, Utils, NodesApi, TrashcanApi, ActionType, RulesApi, Field, Comparator } from '@alfresco/playwright-shared'; +import { expect } from '@playwright/test'; + +test.use({ launchOptions: { slowMo: 300 } }); +test.describe('Rules - Manage Rules', () => { + const apiClientFactory = new ApiClientFactory(); + let nodesApi: NodesApi; + let trashcanApi: TrashcanApi; + let rulesApi: RulesApi; + const username = `user-e2e-${Utils.random()}`; + const folderName897_1 = `folder-XAT-897-1-${Utils.random()}`; + const folderName897_2 = `folder-XAT-897-2-${Utils.random()}`; + const randomFolderName1 = `folder-name-${Utils.random()}`; + const randomRuleName897 = `playwright-rule-897-${Utils.random()}`; + const randomRuleName898 = `playwright-rule-898-${Utils.random()}`; + const randomRuleName899 = `playwright-rule-899-${Utils.random()}`; + const randomRuleName900 = `playwright-rule-900-${Utils.random()}`; + const randomRuleName901 = `playwright-rule-901-${Utils.random()}`; + const randomRuleName902 = `playwright-rule-902-${Utils.random()}`; + const randomRuleName903 = `playwright-rule-903-${Utils.random()}`; + + let folderName897Id1: string; + let folderName897Id2: string; + let randomFolderId: string; + + test.beforeAll(async () => { + try { + await apiClientFactory.setUpAcaBackend('admin'); + await apiClientFactory.createUser({ username }); + nodesApi = await NodesApi.initialize(username, username); + trashcanApi = await TrashcanApi.initialize(username, username); + rulesApi = await RulesApi.initialize(username, username); + } catch (error) { + console.error(`beforeAll failed : ${error}`); + } + + await apiClientFactory.setUpAcaBackend(username, username); + folderName897Id1 = (await nodesApi.createFolder(folderName897_1)).entry.id; + folderName897Id2 = (await nodesApi.createFolder(folderName897_2)).entry.id; + randomFolderId = (await nodesApi.createFolder(randomFolderName1)).entry.id; + + await rulesApi.createRuleWithDestinationFolder(folderName897Id1, randomRuleName897, 'move', folderName897Id2); + await rulesApi.createRandomRule(randomFolderId, randomRuleName898); + await rulesApi.createRandomRuleWithMultipleActions(randomFolderId, randomRuleName899, 3); + await rulesApi.createRuleWithRandomAspects(randomFolderId, randomRuleName900); + await rulesApi.createRandomRuleWithMultipleActions(randomFolderId, randomRuleName901, 3); + await rulesApi.createRandomRule(randomFolderId, randomRuleName902); + await rulesApi.createRandomRuleWithMultipleConditions(randomFolderId, randomRuleName903, 3); + + await nodesApi.deleteNodeById(folderName897Id2); + }); + + test.beforeEach(async ({ personalFiles, loginPage }) => { + await Utils.tryLoginUser(loginPage, username, username, 'beforeEach failed'); + await personalFiles.navigate(); + }); + + test.afterAll(async () => { + await Utils.deleteNodesSitesEmptyTrashcan(nodesApi, trashcanApi, 'afterAll failed'); + }); + + test('[XAT-897] Update a rule which has an error in its action', async ({ personalFiles, nodesPage }) => { + await personalFiles.dataTable.performActionFromExpandableMenu(folderName897_1, 'Manage rules'); + await nodesPage.manageRules.turnOffRuleToggle(); + await nodesPage.manageRules.ruleDetailsEditButton.click(); + await expect(nodesPage.manageRulesDialog.ruleDisableCheckbox).toBeChecked(); + }); + + test('[XAT-898] Cancel updating rule dialog', async ({ personalFiles, nodesPage }) => { + await personalFiles.dataTable.performActionFromExpandableMenu(randomFolderName1, 'Manage rules'); + await nodesPage.manageRules.ruleDetailsEditButton.click(); + await nodesPage.manageRulesDialog.ruleNameInputLocator.fill('new name'); + await nodesPage.manageRulesDialog.cancelRuleButton.click(); + await expect(nodesPage.manageRules.getGroupsList(randomRuleName898)).toBeVisible(); + }); + + test('[XAT-899] Update rule by removing existing actions', async ({ personalFiles, nodesPage }) => { + await personalFiles.dataTable.performActionFromExpandableMenu(randomFolderName1, 'Manage rules'); + await nodesPage.manageRules.getGroupsList(randomRuleName899).click(); + await nodesPage.manageRules.ruleDetailsEditButton.click(); + await nodesPage.manageRulesDialog.deleteActions(2); + await nodesPage.actionsDropdown.selectAction(ActionType.IncrementCounter, 1); + await nodesPage.manageRulesDialog.createRuleButton.click(); + await nodesPage.manageRulesDialog.createRuleButton.waitFor({ state: 'hidden' }); + await nodesPage.manageRules.getGroupsList(randomRuleName899).click(); + + expect(await nodesPage.manageRules.ruleActions.count()).toEqual(2); + }); + + test('[XAT-900] Update a rule by removing existing aspects', async ({ personalFiles, nodesPage }) => { + await personalFiles.dataTable.performActionFromExpandableMenu(randomFolderName1, 'Manage rules'); + await nodesPage.manageRules.getGroupsList(randomRuleName900).click(); + await nodesPage.manageRules.ruleDetailsEditButton.click(); + await nodesPage.manageRulesDialog.deleteActions(2); + await nodesPage.actionsDropdown.selectAction(ActionType.AddAspect, 1); + await nodesPage.actionsDropdown.insertAddAspectActionValues('History', 1); + await nodesPage.manageRulesDialog.createRuleButton.click(); + await nodesPage.manageRulesDialog.createRuleButton.waitFor({ state: 'hidden' }); + await nodesPage.manageRules.getGroupsList(randomRuleName900).click(); + + expect(await nodesPage.manageRules.ruleActions.count()).toEqual(2); + }); + + test('[XAT-901] Prevent rule updating after clicking on cancel during selecting destination folder', async ({ personalFiles, nodesPage }) => { + await personalFiles.dataTable.performActionFromExpandableMenu(randomFolderName1, 'Manage rules'); + await nodesPage.manageRules.getGroupsList(randomRuleName901).click(); + await nodesPage.manageRules.ruleDetailsEditButton.click(); + await nodesPage.manageRulesDialog.deleteActions(2); + await nodesPage.actionsDropdown.selectAction(ActionType.Copy, 1); + await nodesPage.manageRulesDialog.destinationFolderButton.click(); + await nodesPage.contentNodeSelectorDialog.cancelButton.click(); + await nodesPage.contentNodeSelectorDialog.cancelButton.waitFor({ state: 'hidden' }); + await expect(nodesPage.manageRulesDialog.createRuleButton).toBeDisabled(); + }); + + test('[XAT-902] Prevent rule updating when required fields are empty', async ({ personalFiles, nodesPage }) => { + await personalFiles.dataTable.performActionFromExpandableMenu(randomFolderName1, 'Manage rules'); + await nodesPage.manageRules.getGroupsList(randomRuleName902).click(); + await nodesPage.manageRules.ruleDetailsEditButton.click(); + await nodesPage.actionsDropdown.selectAction(ActionType.AddAspect, 1); + await expect(nodesPage.manageRulesDialog.createRuleButton).toBeDisabled(); + }); + + test('[XAT-903] [XAT-904] Edit existing conditions', async ({ personalFiles, nodesPage }) => { + await personalFiles.dataTable.performActionFromExpandableMenu(randomFolderName1, 'Manage rules'); + await nodesPage.manageRules.getGroupsList(randomRuleName903).click(); + await nodesPage.manageRules.ruleDetailsEditButton.click(); + await nodesPage.manageRulesDialog.deleteConditions(2); + await nodesPage.conditionsDropdown.addCondition(Field.Size, 'XAT-903', 1, Comparator.Equals); + await nodesPage.manageRulesDialog.createRuleButton.click(); + await nodesPage.manageRulesDialog.createRuleButton.waitFor({ state: 'hidden' }); + await nodesPage.manageRules.getGroupsList(randomRuleName903).click(); + expect(await nodesPage.manageRules.countConditionsInGroup()).toEqual(2); + }); +}); diff --git a/e2e/playwright/navigation/src/tests/breadcrumb.e2e.ts b/e2e/playwright/navigation/src/tests/breadcrumb.e2e.ts index c1bcb6cf7..25d92ac0a 100644 --- a/e2e/playwright/navigation/src/tests/breadcrumb.e2e.ts +++ b/e2e/playwright/navigation/src/tests/breadcrumb.e2e.ts @@ -117,8 +117,7 @@ test.describe('viewer action file', () => { test('[C213237] Tooltip appears on hover on a step in breadcrumb', async ({ personalFiles }) => { await personalFiles.navigate({ remoteUrl: `#/personal-files/${subFolder2Id}` }); const item = personalFiles.breadcrumb.items.nth(2); - const title = await item.getAttribute('title'); - expect(title).toEqual(subFolder1); + await expect(item).toHaveAttribute('title', subFolder1); }); test('[C213238] Breadcrumb updates correctly when folder is renamed', async ({ personalFiles, nodesApiAction }) => { 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 b78cb8726..2933010a9 100644 --- a/e2e/playwright/search/src/tests/search-filters-general.e2e.ts +++ b/e2e/playwright/search/src/tests/search-filters-general.e2e.ts @@ -85,7 +85,7 @@ test.describe('Search - Filters - General', () => { }); test('[C704283] Facets filters can be cleared', async ({ searchPage }) => { - const filterTextBefore = searchPage.searchFilters.propertiesFilter; + const filterTextBefore = await searchPage.searchFilters.propertiesFilter.textContent(); await searchPage.searchFilters.propertiesFilter.click(); await searchPage.searchFiltersProperties.fileSizeInput.fill('1000'); await searchPage.searchFilters.menuCardApply.click(); @@ -97,12 +97,12 @@ test.describe('Search - Filters - General', () => { await searchPage.searchFiltersProperties.fileSizeInput.waitFor({ state: 'hidden' }); const filterTextCleared = await searchPage.searchFilters.propertiesFilter.textContent(); - await expect(filterTextBefore).toHaveText(filterTextCleared); + expect(filterTextBefore).toEqual(filterTextCleared); expect(filterTextAfter).not.toEqual(filterTextCleared); }); test('[C699499] All filters can be reset with reset button', async ({ searchPage }) => { - const propertiesFilterTextBefore = searchPage.searchFilters.propertiesFilter; + const propertiesFilterTextBefore = await searchPage.searchFilters.propertiesFilter.textContent(); await searchPage.searchFilters.propertiesFilter.click(); await searchPage.searchFiltersProperties.fileSizeInput.fill('1000'); await searchPage.searchFilters.menuCardApply.click(); @@ -120,9 +120,9 @@ test.describe('Search - Filters - General', () => { const propertiesFilterTextCleared = await searchPage.searchFilters.propertiesFilter.textContent(); const logicFilterTextCleared = await searchPage.searchFilters.logicFilter.textContent(); - await expect(propertiesFilterTextBefore).toHaveText(propertiesFilterTextCleared); - expect(logicFilterTextBefore).toEqual(logicFilterTextCleared); - expect(propertiesFilterTextCleared).not.toEqual(propertiesFilterTextAfter); - expect(logicFilterTextCleared).not.toEqual(logicFilterTextAfter); + await expect(propertiesFilterTextBefore).toEqual(propertiesFilterTextCleared); + await expect(logicFilterTextBefore).toEqual(logicFilterTextCleared); + await expect(propertiesFilterTextCleared).not.toEqual(propertiesFilterTextAfter); + await expect(logicFilterTextCleared).not.toEqual(logicFilterTextAfter); }); }); diff --git a/projects/aca-playwright-shared/src/api/api-client-factory.ts b/projects/aca-playwright-shared/src/api/api-client-factory.ts index b512517d3..17e6ef0ad 100644 --- a/projects/aca-playwright-shared/src/api/api-client-factory.ts +++ b/projects/aca-playwright-shared/src/api/api-client-factory.ts @@ -44,7 +44,6 @@ import { CategoriesApi, TagsApi } from '@alfresco/js-api'; -import { ActionTypes, Rule } from './rules-api'; import { users } from '../base-config'; import { Person, PersonModel } from './people-api-models'; @@ -90,7 +89,6 @@ export class ApiClientFactory { public queriesApi: QueriesApi; public categoriesApi: CategoriesApi; public tagsApi: TagsApi; - constructor() { this.alfrescoApi = new AlfrescoApi(config); } @@ -125,26 +123,6 @@ export class ApiClientFactory { return this; } - private callApi(path: string, httpMethod: string, body: object = {}): Promise { - // APIs used by this service are still private and not yet available for public use - const params = [{}, {}, {}, {}, body, ['application/json'], ['application/json']]; - return this.alfrescoApi.contentPrivateClient.callApi(path, httpMethod, ...params); - } - - async createRule(nodeId: string, rule: Partial, ruleSetId: string = '-default-'): Promise { - const response = await this.callApi(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules`, 'POST', { ...rule }); - return response.entry; - } - - async createRandomRule(folderId: string, ruleName: string): Promise { - const response = await this.createRule(folderId, { - name: ruleName, - isEnabled: true, - actions: [ActionTypes.ADDFEATURES.value, ActionTypes.CHECKIN.value] - }); - return response; - } - async login(userName: string, password?: string) { const predefinedUserKey = Object.keys(users).find((user) => user === userName || users[user].username === userName); const userToLog = predefinedUserKey ? users[predefinedUserKey] : undefined; diff --git a/projects/aca-playwright-shared/src/api/rules-api.ts b/projects/aca-playwright-shared/src/api/rules-api.ts index 1ad2f0679..e8197ddd8 100644 --- a/projects/aca-playwright-shared/src/api/rules-api.ts +++ b/projects/aca-playwright-shared/src/api/rules-api.ts @@ -22,21 +22,135 @@ * from Hyland Software. If not, see . */ +import { ApiClientFactory } from './api-client-factory'; +import { AcaFolderRulesModule } from '@alfresco/aca-content/folder-rules'; +import * as crypto from 'crypto'; + +export class RulesApi { + private apiService: ApiClientFactory; + + constructor() { + this.apiService = new ApiClientFactory(); + } + + static async initialize(userName: string, password?: string): Promise { + const classObj = new RulesApi(); + await classObj.apiService.setUpAcaBackend(userName, password); + return classObj; + } + + private callApi(path: string, httpMethod: string, body: object = {}): Promise { + // APIs used by this service are still private and not yet available for public use + const params = [{}, {}, {}, {}, body, ['application/json'], ['application/json']]; + return this.apiService.alfrescoApi.contentPrivateClient.callApi(path, httpMethod, ...params); + } + + async createRule(nodeId: string, rule: Partial, ruleSetId: string = '-default-'): Promise { + const response = await this.callApi(`/nodes/${nodeId}/rule-sets/${ruleSetId}/rules`, 'POST', { ...rule }); + return response.entry; + } + + async createRandomRule(folderId: string, ruleName: string): Promise { + const randomActionsIndex = crypto.randomInt(0, ActionTypes.actions.length); + const randomAction = ActionTypes.actions[randomActionsIndex]; + return await this.createRule(folderId, { + name: ruleName, + isEnabled: true, + actions: [randomAction] + }); + } + + async createRandomRuleWithMultipleConditions(folderId: string, ruleName: string, numOfConditions: number): Promise { + if (numOfConditions > ConditionsTypes.conditions.length) { + numOfConditions = ConditionsTypes.conditions.length; + } + const randomActionsIndex = crypto.randomInt(0, ActionTypes.actions.length); + const randomAction = ActionTypes.actions[randomActionsIndex]; + let conditionsArray = []; + for (let i = 0; i < numOfConditions; i++) { + const randomIndex = crypto.randomInt(0, ConditionsTypes.conditions.length); + const randomCondition = ConditionsTypes.conditions[randomIndex]; + conditionsArray.push(randomCondition); + } + return await this.createRule(folderId, { + name: ruleName, + isEnabled: true, + actions: [randomAction], + conditions: { + inverted: false, + booleanMode: 'and', + compositeConditions: conditionsArray + } + }); + } + + async createRandomRuleWithMultipleActions(folderId: string, ruleName: string, numOfActions: number): Promise { + if (numOfActions > ActionTypes.actions.length) { + numOfActions = ActionTypes.actions.length; + } + let actionsArray = []; + for (let i = 0; i < numOfActions; i++) { + const randomIndex = crypto.randomInt(0, ActionTypes.actions.length); + const randomAction = ActionTypes.actions[randomIndex]; + actionsArray.push(randomAction); + } + return await this.createRule(folderId, { + name: ruleName, + isEnabled: true, + actions: actionsArray + }); + } + + async createRuleWithRandomAspects(folderId: string, ruleName: string): Promise { + return await this.createRule(folderId, { + name: ruleName, + isEnabled: true, + actions: [ + { + actionDefinitionId: 'add-features', + params: { + 'aspect-name': 'sc:controlsAreClearance' + } + }, + { + actionDefinitionId: 'add-features', + params: { + 'aspect-name': 'sfdc:objectModel' + } + }, + { + actionDefinitionId: 'add-features', + params: { + 'aspect-name': 'sfdc:folder' + } + } + ] + }); + } + + async createRuleWithDestinationFolder( + folderId: string, + ruleName: string, + actionType: 'move' | 'copy' | 'import', + destinationFolderId: string + ): Promise { + return await this.createRule(folderId, { + name: ruleName, + isEnabled: true, + actions: [ + { + actionDefinitionId: actionType, + params: { + 'destination-folder': destinationFolderId + } + } + ] + }); + } +} + type RuleTrigger = 'inbound' | 'update' | 'outbound'; -export interface RuleCompositeCondition { - inverted: boolean; - booleanMode: 'and' | 'or'; - compositeConditions: RuleCompositeCondition[]; - simpleConditions: RuleSimpleCondition[]; -} - -export interface RuleSimpleCondition { - field: string; - comparator: string; - parameter: string; -} - export interface Rule { id: string; name: string; @@ -47,7 +161,7 @@ export interface Rule { errorScript: string; isShared: boolean; triggers: RuleTrigger[]; - conditions: RuleCompositeCondition; + conditions: AcaFolderRulesModule; actions: RuleAction[]; } @@ -57,16 +171,84 @@ export interface RuleAction { } export class ActionTypes { - static ADDFEATURES = new ActionTypes('ADDFEATURES', { + static readonly ADDFEATURES = new ActionTypes('ADDFEATURES', { actionDefinitionId: 'add-features', params: { 'aspect-name': 'cm:thumbnailed' } }); - static CHECKIN = new ActionTypes('CHECKIN', { + static readonly CHECKIN = new ActionTypes('CHECKIN', { actionDefinitionId: 'check-in', params: { description: 'test', minorChange: true } }); + static readonly SPECIALISETYPE = new ActionTypes('SPECIALISETYPE', { + actionDefinitionId: 'specialise-type', + params: { 'type-name': 'sys:base' } + }); + static readonly RECORDABLEVERSION = new ActionTypes('RECORDABLEVERSION', { + actionDefinitionId: 'recordable-version-config', + params: { version: 'ALL' } + }); + static readonly SETPROPERTYVALUE = new ActionTypes('SETPROPERTYVALUE', { + actionDefinitionId: 'set-property-value', + params: { property: 'dl:ganttPercentComplete', value: 'test' } + }); + static readonly actions = [ + ActionTypes.ADDFEATURES.value, + ActionTypes.CHECKIN.value, + ActionTypes.RECORDABLEVERSION.value, + ActionTypes.SPECIALISETYPE.value, + ActionTypes.SETPROPERTYVALUE.value + ]; constructor(public key: string, public value: RuleAction) {} } + +export class ConditionsTypes { + static readonly MIMETYPE = new ConditionsTypes('MIMETYPE', { + inverted: false, + booleanMode: 'and', + simpleConditions: [ + { + field: 'mimetype', + comparator: 'equals', + parameter: 'video/3gpp' + } + ] + }); + static readonly CMNAME = new ConditionsTypes('CM:NAME', { + inverted: false, + booleanMode: 'and', + simpleConditions: [ + { + field: 'cm:name', + comparator: 'equals', + parameter: 'testname' + } + ] + }); + static readonly SIZE = new ConditionsTypes('SIZE', { + inverted: false, + booleanMode: 'and', + simpleConditions: [ + { + field: 'size', + comparator: 'equals', + parameter: '345' + } + ] + }); + static readonly TAG = new ConditionsTypes('TAG', { + inverted: false, + booleanMode: 'and', + simpleConditions: [ + { + field: 'tag', + comparator: 'equals', + parameter: 'testtag' + } + ] + }); + static readonly conditions = [ConditionsTypes.MIMETYPE.value, ConditionsTypes.CMNAME.value, ConditionsTypes.SIZE.value, ConditionsTypes.TAG.value]; + constructor(public key: string, public value: AcaFolderRulesModule) {} +} diff --git a/projects/aca-playwright-shared/src/page-objects/components/actions-dropdown.component.ts b/projects/aca-playwright-shared/src/page-objects/components/actions-dropdown.component.ts index d1144691b..89f72ff69 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/actions-dropdown.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/actions-dropdown.component.ts @@ -52,7 +52,8 @@ export enum ActionType { export class ActionsDropdownComponent extends BaseComponent { private static rootElement = 'aca-edit-rule-dialog aca-rule-action-list'; - private getOptionLocator = (optionName: string): Locator => this.page.locator('.mat-select-panel .mat-option-text', { hasText: optionName }).first(); + private getOptionLocator = (optionName: string): Locator => + this.page.locator('.mat-select-panel .mat-option-text', { hasText: optionName }).first(); private ruleActionLocator = this.getChild('aca-rule-action'); private addActionButtonLocator = this.getChild('[data-automation-id="rule-action-list-add-action-button"]'); private actionDropdownLocator = this.getChild('[data-automation-id="rule-action-select"]'); @@ -74,6 +75,7 @@ export class ActionsDropdownComponent extends BaseComponent { if (index > 0) { await this.addActionButtonLocator.click(); } + await this.actionDropdownLocator.nth(index).hover({ timeout: 1000 }); await this.actionDropdownLocator.nth(index).click(); const option = this.getOptionLocator(action); await option.click(); diff --git a/projects/aca-playwright-shared/src/page-objects/components/conditions.component.ts b/projects/aca-playwright-shared/src/page-objects/components/conditions.component.ts index 957d34ae1..6dcc03f60 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/conditions.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/conditions.component.ts @@ -58,7 +58,7 @@ export class ConditionComponent extends ManageRulesDialogComponent { } async addCondition(fields: Partial, value: string, index: number, comparators?: Partial): Promise { - await this.addConditionButton.click(); + await this.addConditionButton.first().click(); await this.selectField(fields, index); if (comparators) { await this.selectComparator(comparators, index); diff --git a/projects/aca-playwright-shared/src/page-objects/components/manageRules/manage-rules-dialog.component.ts b/projects/aca-playwright-shared/src/page-objects/components/manageRules/manage-rules-dialog.component.ts index d6c542216..6d3f96006 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/manageRules/manage-rules-dialog.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/manageRules/manage-rules-dialog.component.ts @@ -44,8 +44,24 @@ export class ManageRulesDialogComponent extends BaseComponent { public ruleInBackgroundCheckbox = this.getChild('[data-automation-id="rule-option-checkbox-asynchronous"]'); public ruleSubfoldersCheckbox = this.getChild('[data-automation-id="rule-option-checkbox-inheritable"]'); public ruleDisableCheckbox = this.getChild('[data-automation-id="rule-option-checkbox-disabled"]'); + public actionsEllipsisButtons = this.getChild('[data-automation-id="rule-action-list-action-menu"]'); + public actionsEllipsisDelete = this.page.locator('[data-automation-id="rule-action-list-remove-action-button"]'); + public conditionsEllipsisButtons = this.getChild('[data-automation-id="condition-actions-button"]'); + public conditionsEllipsisDelete = this.page.locator('button[title="Remove"]'); constructor(page: Page) { super(page, ManageRulesDialogComponent.rootElement); } + + async deleteActions(noActions: number): Promise { + for(let i = 0; i < noActions; i++) { + await this.actionsEllipsisButtons.first().click(); + await this.actionsEllipsisDelete.click(); + }} + + async deleteConditions(noConditions: number): Promise { + for(let i = 0; i < noConditions; i++) { + await this.conditionsEllipsisButtons.first().click(); + await this.conditionsEllipsisDelete.click(); + }} } diff --git a/projects/aca-playwright-shared/src/page-objects/components/manageRules/manage-rules.component.ts b/projects/aca-playwright-shared/src/page-objects/components/manageRules/manage-rules.component.ts index 10268e2ef..980917b2a 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/manageRules/manage-rules.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/manageRules/manage-rules.component.ts @@ -37,6 +37,8 @@ export class ManageRules extends BaseComponent { public ruleDetailsWhenText = this.getChild('[data-automation-id="rule-details-triggers-component"]'); public ruleDetailsPerformActionsDiv = this.getChild('adf-card-view-textitem mat-form-field input'); public rulesEmptyListTitle = this.getChild('.adf-empty-content__title'); + public ruleActions = this.getChild('aca-rule-action'); + public ruleConditionsInGroup = this.getChild('aca-rule-composite-condition aca-rule-simple-condition'); constructor(page: Page) { super(page, ManageRules.rootElement); @@ -56,4 +58,19 @@ export class ManageRules extends BaseComponent { async checkIfRuleIsOnTheList(ruleName: string): Promise { await expect(this.getGroupsList(ruleName)).toBeVisible({ timeout: 5000 }); } + + async countConditionsInGroup(): Promise { + return await this.ruleConditionsInGroup.count(); + } + + async turnOffRuleToggle(): Promise { + await expect(async () => { + await this.ruleToggle.hover({ timeout: 1000 }); + await this.ruleToggle.click(); + await expect(this.ruleToggleFalse).toBeVisible(); + }).toPass({ + intervals: [2_000, 2_000, 2_000, 2_000, 2_000, 2_000, 2_000], + timeout: 20_000 + }); + } }