From 35f662bee95fce9d286efd6b7a0e0db1659241dc Mon Sep 17 00:00:00 2001 From: Rakesh Ghosal Date: Wed, 3 Dec 2025 13:01:51 +0530 Subject: [PATCH] ACS-9871: Add transformation rule tests and supporting methods (#4921) https://hyland.atlassian.net/browse/ACS-9871 This pull request introduces a new end-to-end test for folder rules that transform supported file types to PDF, and enhances the shared Playwright testing utilities to support MIME type selection and destination folder specification for transformation actions. The changes primarily add test coverage for the "Transform and Copy Content" rule and extend the reusable test components to support these scenarios. Enhancements to Playwright shared utilities: Added a new MimeType enum to actions-dropdown.component.ts to standardize MIME type selection for transformation actions. Introduced the selectMimeType method in ActionsDropdownComponent for selecting a target MIME type (e.g., PDF) in rule configuration. Added the selectDestinationFolderTransformAndCopyContent method in ActionsDropdownComponent to automate picking a destination folder for transformed files. Declared new locators in ActionsDropdownComponent to support MIME type dropdown and destination folder selection. New end-to-end test: Created transform-rules.e2e.ts to verify that DOCX, XLSX, and PPTX files are correctly transformed to PDF and copied to the specified folder when a folder rule is configured. --- .../src/tests/transform-rules.e2e.ts | 105 ++++++++++++++++++ .../components/actions-dropdown.component.ts | 66 +++++++++++ 2 files changed, 171 insertions(+) create mode 100644 e2e/playwright/folder-rules/src/tests/transform-rules.e2e.ts diff --git a/e2e/playwright/folder-rules/src/tests/transform-rules.e2e.ts b/e2e/playwright/folder-rules/src/tests/transform-rules.e2e.ts new file mode 100644 index 000000000..2c38dc1d3 --- /dev/null +++ b/e2e/playwright/folder-rules/src/tests/transform-rules.e2e.ts @@ -0,0 +1,105 @@ +/*! + * 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 { + ActionType, + ApiClientFactory, + test, + Utils, + TrashcanApi, + NodesApi, + MimeType, + FileActionsApi, + TEST_FILES +} from '@alfresco/aca-playwright-shared'; + +test.use({ launchOptions: { slowMo: 300 } }); +test.describe('Folder Rules Actions', () => { + const apiClientFactory = new ApiClientFactory(); + let nodesApi: NodesApi; + let trashcanApi: TrashcanApi; + let fileActionApi: FileActionsApi; + const username = `user-e2e-${Utils.random()}`; + + const randomFolderName1 = `folder-name-${Utils.random()}`; + const randomDocxName = `${TEST_FILES.DOCX.name}-${Utils.random()}`; + const randomXLSXName = `${TEST_FILES.XLSX.name}-${Utils.random()}`; + const randomPPTXName = `${TEST_FILES.PPTX_FILE.name}-${Utils.random()}`; + + const copyFileName = `copy-file-${Utils.random()}`; + const testString = '"!@£$%^&*()_+{}|:""?><,/.\';][=-`~"'; + + let randomFolderName1Id: 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); + fileActionApi = await FileActionsApi.initialize(username, username); + randomFolderName1Id = (await nodesApi.createFolder(randomFolderName1)).entry.id; + await nodesApi.createFile(copyFileName, randomFolderName1Id); + } catch (error) { + console.error(`beforeAll failed : ${error}`); + } + }); + + test.beforeEach(async ({ loginPage }) => { + await Utils.tryLoginUser(loginPage, username, username, 'beforeEach failed'); + }); + + test.afterAll(async () => { + await Utils.deleteNodesSitesEmptyTrashcan(nodesApi, trashcanApi, 'afterAll failed'); + }); + + test('[XAT-8050] Supported types transformation to PDF', async ({ personalFiles, nodesPage, loginPage }) => { + const toFolderName = 'TO_PDF'; + await nodesApi.createFolder(toFolderName); + await personalFiles.navigate({ remoteUrl: `#/nodes/${randomFolderName1Id}/rules` }); + await nodesPage.toolbar.clickCreateRuleButton(); + await nodesPage.manageRulesDialog.ruleNameInputLocator.fill(testString); + await nodesPage.manageRulesDialog.ruleDescriptionInputLocator.fill(testString); + await nodesPage.actionsDropdown.selectAction(ActionType.TransformAndCopyContent, 0); + await nodesPage.actionsDropdown.selectMimeType(MimeType.AdobePDFDocument, 0); + await nodesPage.actionsDropdown.selectDestinationFolderTransformAndCopyContent(0, toFolderName); + await nodesPage.manageRulesDialog.createRuleButton.click(); + await expect(nodesPage.manageRules.getGroupsList(testString)).toBeVisible(); + await fileActionApi.uploadFile(TEST_FILES.DOCX.path, randomDocxName, randomFolderName1Id); + await fileActionApi.uploadFile(TEST_FILES.XLSX.path, randomXLSXName, randomFolderName1Id); + await fileActionApi.uploadFile(TEST_FILES.PPTX_FILE.path, randomPPTXName, randomFolderName1Id); + await loginPage.logoutUser(); + await expect(loginPage.username, 'User name was not visible').toBeVisible(); + await Utils.tryLoginUser(loginPage, username, username, 'beforeEach failed'); + await personalFiles.dataTable.performClickFolderOrFileToOpen(toFolderName); + await personalFiles.spinner.waitForReload(); + const docxToPDF = `${randomDocxName}.pdf`; + const xlsxToPDF = `${randomXLSXName}.pdf`; + const pptxToPDF = `${randomPPTXName}.pdf`; + expect(await personalFiles.dataTable.isItemPresent(docxToPDF), `Converted PDF from DOCX ${docxToPDF} was not present in data table`).toBe(true); + expect(await personalFiles.dataTable.isItemPresent(pptxToPDF), `Converted PDF from PPTX ${pptxToPDF} was not present in data table`).toBe(true); + expect(await personalFiles.dataTable.isItemPresent(xlsxToPDF), `Converted PDF from XLSX ${xlsxToPDF} was not present in data table`).toBe(true); + }); +}); 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 02769d5f8..5f902f660 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 @@ -24,6 +24,7 @@ import { Locator, Page } from '@playwright/test'; import { BaseComponent } from './base.component'; +import { timeouts } from '../../public-api'; export enum ActionType { AddAspect = 'Add aspect', @@ -49,6 +50,10 @@ export enum ActionType { TransformAndCopyImage = 'Transform and copy image' } +export enum MimeType { + AdobePDFDocument = 'Adobe PDF Document [application/pdf]' +} + export class ActionsDropdownComponent extends BaseComponent { private static rootElement = 'aca-edit-rule-dialog aca-rule-action-list'; @@ -67,6 +72,9 @@ export class ActionsDropdownComponent extends BaseComponent { private actionSimpleWorkflowLabelApproveLocator = `[data-automation-id="card-boolean-label-approve-move"]`; private actionSimpleWorkflowSRejectStepLocator = '[data-automation-id="header-reject-step"] input'; private actionSimpleWorkflowRejectFolderLocator = `[data-automation-id="header-reject-folder"] input`; + private readonly mimeTypeDropdownLocator = this.getChild('[data-automation-class="select-box"][aria-label="Mimetype *"]'); + private readonly actionTransformAndCopyContentDestinationFolderLocator = '[data-automation-id="card-textitem-value-destination-folder"]'; + private readonly contentNodeSelectorSearchInput = '[data-automation-id="content-node-selector-search-input"]'; constructor(page: Page) { super(page, ActionsDropdownComponent.rootElement); @@ -112,4 +120,62 @@ export class ActionsDropdownComponent extends BaseComponent { await this.ruleActionLocator.nth(index).locator(this.actionSimpleWorkflowRejectFolderLocator).click(); await this.page.locator(this.actionSimpleWorkflowActionChoiceLocator).click(); } + + /** + * Selects a MIME type from the dropdown for content transformation actions. + * This method is typically used when configuring folder rules with transform actions + * (e.g., Transform and Copy Content) to specify the target format for file conversion. + * + * @param mimeType - The MIME type to select (e.g., MimeType.AdobePDFDocument for PDF conversion) + * @param index - The zero-based index of the action dropdown (use 0 for the first action, 1 for the second, etc.) + * + * @example + * ```typescript + * // Select PDF as the target format for the first transform action + * await actionsDropdown.selectMimeType(MimeType.AdobePDFDocument, 0); + * ``` + */ + async selectMimeType(mimeType: Partial, index: number): Promise { + await this.mimeTypeDropdownLocator.nth(index).hover({ timeout: timeouts.short }); + await this.mimeTypeDropdownLocator.nth(index).click(); + const option = this.getOptionLocator(mimeType); + await option.click(); + } + + /** + * Selects a destination folder for the Transform and Copy Content action. + * This method opens the folder picker dialog, searches for a folder by name, + * and selects it as the destination where transformed files will be copied. + * + * The method performs the following steps: + * 1. Clicks the destination folder field to open the content node selector dialog + * 2. Fills the search input with the provided search term + * 3. Waits for search results to load + * 4. Clicks on the first matching folder from the results + * 5. Confirms the selection by clicking the Choose button + * + * @param index - The zero-based index of the action dropdown (use 0 for the first action, 1 for the second, etc.) + * @param searchTerm - The name or partial name of the destination folder to search for (e.g., 'TO_PDF') + * + * @example + * ```typescript + * // Select 'TO_PDF' folder as the destination for transformed files + * await actionsDropdown.selectDestinationFolderTransformAndCopyContent(0, 'TO_PDF'); + * ``` + */ + async selectDestinationFolderTransformAndCopyContent(index: number, searchTerm: string): Promise { + // Click on the destination folder input field to open the folder picker dialog + await this.ruleActionLocator.nth(index).locator(this.actionTransformAndCopyContentDestinationFolderLocator).click(); + + // Locate and fill the search input field within the content node selector + const searchInput = this.page.locator(this.contentNodeSelectorSearchInput); + await searchInput.fill(searchTerm); + + // Click on the folder from the search results (first match if multiple) + const folderRow = this.page.locator('.adf-datatable-body .adf-name-location-cell-name', { hasText: searchTerm }).first(); + await folderRow.click(); + + // Click the Choose button to confirm selection + await this.page.locator(this.actionSimpleWorkflowActionChoiceLocator).click(); + } }