diff --git a/e2e/playwright/actions/src/tests/create-file-from-template.spec.ts b/e2e/playwright/actions/src/tests/create-file-from-template.spec.ts new file mode 100644 index 0000000000..1b8391b3c0 --- /dev/null +++ b/e2e/playwright/actions/src/tests/create-file-from-template.spec.ts @@ -0,0 +1,402 @@ +/*! + * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Alfresco Example Content Application + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * from Hyland Software. If not, see . + */ + +import { expect } from '@playwright/test'; +import { + AcaHeader, + ContentNodeSelectorDialog, + CreateFromTemplateDialogComponent, + DataTableComponent, + NodeContentTree, + NodesApi, + Utils, + errorStrings, + getUserState, + test +} from '@alfresco/playwright-shared'; + +test.use({ storageState: getUserState('hruser') }); +test.describe('Create file from template', () => { + let selectFileTemplateDialog: ContentNodeSelectorDialog; + let createFileFromTemplateDialog: CreateFromTemplateDialogComponent; + let dataTable: DataTableComponent; + let toolbar: AcaHeader; + let randomFileName: string; + let randomFileTitle: string; + let randomFileDescription: string; + let fileLink: string; + let fileId: string; + const selectDialogTitle = 'Select a document template'; + const dialogBreadcrumb = 'Node Templates'; + const nameLabel = 'Name *'; + const titleLabel = 'Title'; + const descriptionLabel = 'Description'; + const emptyString = ''; + const tabKeyString = 'Tab'; + const dotString = '.'; + const spaceString = ' '; + const commandKey = 'Meta'; + const random = Utils.random(); + + const restrictedTemplateFolder = `restricted-templates-${random}`; + const templateInRestrictedFolder = `template-restricted-${random}.txt`; + + const templatesFolder1 = `templates-folder1-${random}`; + const template1InFolder1 = `template1-1-${random}.txt`; + const template2InFolder1 = `template1-2-${random}.txt`; + + const templatesFolder2 = `templates-folder2-${random}`; + const template1InFolder2 = `template2-1-${random}.txt`; + const templatesSubFolder = `template-subFolder-${random}`; + + const template1InRoot = `template3-${random}.txt`; + const template2InRoot = `template4-${random}.txt`; + + const createDialogTitle = `Create new document from '${template1InRoot}'`; + const commonFileName = `playwright-file-${Utils.random()}`; + + const templates: NodeContentTree = { + folders: [ + { + name: templatesFolder1, + folders: [ + { + name: templatesSubFolder, + files: [template1InFolder1, template2InFolder1] + } + ] + }, + { + name: templatesFolder2, + files: [template1InFolder2] + }, + { + name: restrictedTemplateFolder, + files: [templateInRestrictedFolder] + } + ], + files: [template1InRoot, template2InRoot] + }; + + test.beforeAll(async ({ nodesApiAction }) => { + await nodesApiAction.createContent(templates, `Data Dictionary/Node Templates`); + await nodesApiAction.removeUserAccessOnNodeTemplate(restrictedTemplateFolder); + fileLink = (await nodesApiAction.createLinkToFileName(template2InRoot, await nodesApiAction.getNodeTemplatesFolderId())).entry.name; + }); + + test.beforeEach(async ({ personalFiles }) => { + await personalFiles.navigate(); + }); + + test.afterAll(async ({ nodesApiAction }) => { + await nodesApiAction.cleanupNodeTemplatesItems([templatesFolder1, templatesFolder2, restrictedTemplateFolder, template1InRoot, template2InRoot]); + }); + + test.describe('Personal Files page', () => { + test.beforeEach(async ({ personalFiles }) => { + selectFileTemplateDialog = personalFiles.contentNodeSelector; + dataTable = personalFiles.dataTable; + toolbar = personalFiles.acaHeader; + await toolbar.clickCreateFileFromTemplate(); + }); + + test.describe('Select Template dialog', () => { + test('[C325043] Select template - dialog UI - with existing templates', async () => { + await expect.soft(selectFileTemplateDialog.getDialogTitle(selectDialogTitle)).toBeVisible(); + await expect.soft(selectFileTemplateDialog.getBreadcrumb(dialogBreadcrumb)).toBeVisible(); + await expect.soft(dataTable.getRowByName(templatesFolder1)).not.toBeEmpty(); + await expect.soft(dataTable.getRowByName(templatesFolder1)).toBeVisible(); + await expect.soft(dataTable.getRowByName(templatesFolder2)).toBeVisible(); + await expect.soft(dataTable.getRowByName(template1InRoot)).toBeVisible(); + await expect.soft(selectFileTemplateDialog.cancelButton).toBeEnabled(); + await expect(selectFileTemplateDialog.actionButton).toBeDisabled(); + }); + + test(`[C325044] Templates don't appear if user doesn't have permissions to see them`, async () => { + await expect(selectFileTemplateDialog.getDialogTitle(selectDialogTitle)).toBeVisible(); + await expect(dataTable.getRowByName(restrictedTemplateFolder)).not.toBeVisible(); + await expect(dataTable.getRowByName(templateInRestrictedFolder)).not.toBeVisible(); + }); + + test(`[C325045] Navigate through the templates list with folder hierarchy`, async () => { + await expect(selectFileTemplateDialog.getBreadcrumb(dialogBreadcrumb)).toBeVisible(); + await expect(dataTable.getRowByName(templatesFolder1)).toBeVisible(); + await expect(dataTable.getRowByName(templatesFolder2)).toBeVisible(); + await expect(dataTable.getRowByName(template1InRoot)).toBeVisible(); + + await dataTable.getRowByName(templatesFolder1).dblclick(); + await expect(selectFileTemplateDialog.getBreadcrumb(templatesFolder1)).toBeVisible(); + await expect(dataTable.getRowByName(templatesSubFolder)).toBeVisible(); + + await dataTable.getRowByName(templatesSubFolder).dblclick(); + await expect(selectFileTemplateDialog.getBreadcrumb(templatesSubFolder)).toBeVisible(); + await expect(dataTable.getRowByName(template1InFolder1)).toBeVisible(); + await expect(dataTable.getRowByName(template2InFolder1)).toBeVisible(); + + await selectFileTemplateDialog.getFolderIcon.click(); + await expect(selectFileTemplateDialog.getOptionLocator(templatesFolder1)).toBeVisible(); + await expect(selectFileTemplateDialog.getOptionLocator(dialogBreadcrumb)).toBeVisible(); + }); + + test(`[C325047] Templates list doesn't allow multiple selection`, async () => { + await expect(dataTable.getSelectedRow).toHaveCount(0); + + await dataTable.getRowByName(template1InRoot).click({ modifiers: [commandKey] }); + await expect(dataTable.getSelectedRow).toHaveCount(1); + await expect(dataTable.getSelectedRow).toContainText(template1InRoot); + + await dataTable.getRowByName(template2InRoot).click({ modifiers: [commandKey] }); + await expect(dataTable.getSelectedRow).not.toHaveCount(2); + await expect(dataTable.getSelectedRow).toHaveCount(1); + await expect(dataTable.getSelectedRow).toContainText(template2InRoot); + }); + + test('[C325050] Links to files are not displayed', async () => { + await expect(dataTable.getRowByName(template1InRoot)).toBeVisible(); + await expect(dataTable.getRowByName(template2InRoot)).toBeVisible(); + await expect(dataTable.getRowByName(fileLink)).not.toBeVisible(); + }); + + test('[C325048] Cancel the Select template dialog', async () => { + await expect(selectFileTemplateDialog.getDialogTitle(selectDialogTitle)).toBeVisible(); + + await selectFileTemplateDialog.cancelButton.click(); + await expect(selectFileTemplateDialog.getDialogTitle(selectDialogTitle)).toBeHidden(); + }); + + test('[C216339] Next button is disabled when selecting a folder', async () => { + await expect(dataTable.getRowByName(templatesFolder1)).toBeVisible(); + await expect(selectFileTemplateDialog.actionButton).toBeDisabled(); + + await dataTable.getRowByName(templatesFolder1).click(); + await expect(selectFileTemplateDialog.actionButton).toBeDisabled(); + }); + }); + + test.describe('Create document from template dialog', () => { + test.beforeAll(async () => { + const nodesApi = await NodesApi.initialize('hruser'); + fileId = (await nodesApi.createFolder(commonFileName)).entry.id; + }); + + test.beforeEach(async ({ personalFiles }) => { + createFileFromTemplateDialog = personalFiles.createFromTemplateDialogComponent; + await dataTable.getRowByName(template1InRoot).click(); + await selectFileTemplateDialog.actionButton.click(); + }); + + test.afterAll(async ({ nodesApiAction }) => { + await nodesApiAction.deleteNodeById(fileId); + }); + + test('[C325020] Create file from template - dialog UI', async () => { + await expect.soft(createFileFromTemplateDialog.getDialogTitle(createDialogTitle)).toBeVisible(); + await expect.soft(createFileFromTemplateDialog.getDialogLabel(nameLabel)).toBeVisible(); + await expect.soft(createFileFromTemplateDialog.getDialogLabel(nameLabel)).toHaveValue(template1InRoot); + await expect.soft(createFileFromTemplateDialog.getDialogLabel(titleLabel)).toBeVisible(); + await expect.soft(createFileFromTemplateDialog.getDialogLabel(titleLabel)).toHaveValue(emptyString); + await expect.soft(createFileFromTemplateDialog.getDialogLabel(descriptionLabel)).toBeVisible(); + await expect.soft(createFileFromTemplateDialog.getDialogLabel(descriptionLabel)).toHaveValue(emptyString); + await expect.soft(createFileFromTemplateDialog.cancelButton).toBeEnabled(); + await expect(createFileFromTemplateDialog.createButton).toBeEnabled(); + }); + + test('[C325031] File name is required', async () => { + await expect(createFileFromTemplateDialog.getDialogLabel(nameLabel)).toHaveValue(template1InRoot); + + await createFileFromTemplateDialog.getDialogLabel(nameLabel).clear(); + await createFileFromTemplateDialog.page.keyboard.press(tabKeyString); + await expect(createFileFromTemplateDialog.getDialogLabel(nameLabel)).toHaveValue(emptyString); + expect + .soft(await createFileFromTemplateDialog.isErrorMessageDisplayed(errorStrings.nameIsRequiredError), errorStrings.errorMessageNotPresent) + .toBe(true); + await expect(createFileFromTemplateDialog.createButton).toBeDisabled(); + }); + + test('[C325032] Special characters in file name', async () => { + const nameWithSpecialChars = ['a*a', 'a"a', 'aa', `a\\a`, 'a/a', 'a?a', 'a:a', 'a|a']; + + for (const specialFileName of nameWithSpecialChars) { + await createFileFromTemplateDialog.getDialogLabel(nameLabel).fill(specialFileName); + await createFileFromTemplateDialog.page.keyboard.press(tabKeyString); + await expect(createFileFromTemplateDialog.getDialogLabel(nameLabel)).toHaveValue(specialFileName); + expect + .soft( + await createFileFromTemplateDialog.isErrorMessageDisplayed(errorStrings.nameWithSpecialCharactersError), + errorStrings.errorMessageNotPresent + ) + .toBe(true); + await expect(createFileFromTemplateDialog.createButton).toBeDisabled(); + } + }); + + test('[C325033] File name ending with a dot', async () => { + await createFileFromTemplateDialog.getDialogLabel(nameLabel).type(dotString); + await createFileFromTemplateDialog.page.keyboard.press(tabKeyString); + await expect(createFileFromTemplateDialog.getDialogLabel(nameLabel)).toHaveValue(template1InRoot + dotString); + expect + .soft(await createFileFromTemplateDialog.isErrorMessageDisplayed(errorStrings.nameEndWithDotError), errorStrings.errorMessageNotPresent) + .toBe(true); + await expect(createFileFromTemplateDialog.createButton).toBeDisabled(); + }); + + test('[C325034] File name containing only spaces', async () => { + await createFileFromTemplateDialog.getDialogLabel(nameLabel).clear(); + await createFileFromTemplateDialog.getDialogLabel(nameLabel).fill(spaceString); + await createFileFromTemplateDialog.page.keyboard.press(tabKeyString); + await expect(createFileFromTemplateDialog.getDialogLabel(nameLabel)).toHaveValue(spaceString); + expect + .soft( + await createFileFromTemplateDialog.isErrorMessageDisplayed(errorStrings.nameContainOnlySpacesError), + errorStrings.errorMessageNotPresent + ) + .toBe(true); + await expect(createFileFromTemplateDialog.createButton).toBeDisabled(); + }); + + test('[C290146] File title too long', async () => { + await createFileFromTemplateDialog.getDialogLabel(titleLabel).fill(Utils.string257Long); + await createFileFromTemplateDialog.page.keyboard.press(tabKeyString); + await expect(createFileFromTemplateDialog.getDialogLabel(titleLabel)).toHaveValue(Utils.string257Long); + expect + .soft(await createFileFromTemplateDialog.isErrorMessageDisplayed(errorStrings.titleLengthLimitError), errorStrings.errorMessageNotPresent) + .toBe(true); + await expect(createFileFromTemplateDialog.createButton).toBeDisabled(); + }); + + test('[C290142] Description too long', async () => { + await createFileFromTemplateDialog.getDialogLabel(descriptionLabel).fill(Utils.string513Long); + await createFileFromTemplateDialog.page.keyboard.press(tabKeyString); + await expect(createFileFromTemplateDialog.getDialogLabel(descriptionLabel)).toHaveValue(Utils.string513Long); + expect + .soft( + await createFileFromTemplateDialog.isErrorMessageDisplayed(errorStrings.descriptionLengthLimitError), + errorStrings.errorMessageNotPresent + ) + .toBe(true); + await expect(createFileFromTemplateDialog.createButton).toBeDisabled(); + }); + + test('[C325028] Create a file with a duplicate name', async ({ personalFiles }) => { + const snackBar = personalFiles.snackBar; + + await createFileFromTemplateDialog.createFromTemplateAction(commonFileName); + await expect(snackBar.getByMessageLocator(errorStrings.nameAlreadyUsedError)).toBeVisible(); + await expect(createFileFromTemplateDialog.getDialogTitle(createDialogTitle)).toBeVisible(); + }); + + test('[C325027] Cancel file creation', async () => { + await expect(createFileFromTemplateDialog.getDialogTitle(createDialogTitle)).toBeVisible(); + await createFileFromTemplateDialog.cancelButton.click(); + await expect(createFileFromTemplateDialog.getDialogTitle(createDialogTitle)).not.toBeVisible(); + }); + }); + + test.describe('File created from template on Personal Files', () => { + test.beforeEach(async ({ personalFiles }) => { + randomFileName = `playwright-file-${Utils.random()}`; + randomFileTitle = `file-title-${Utils.random()}`; + randomFileDescription = `file-description-${Utils.random()}`; + createFileFromTemplateDialog = personalFiles.createFromTemplateDialogComponent; + await dataTable.getRowByName(template1InRoot).click(); + await selectFileTemplateDialog.actionButton.click(); + }); + + test.afterEach(async () => { + const nodesApi = await NodesApi.initialize('hruser'); + fileId = await nodesApi.getNodeIdFromParent(randomFileName, '-my-'); + await nodesApi.deleteNodeById(fileId); + }); + + test('[C325030] Create a file from a template - with a new Name', async () => { + await createFileFromTemplateDialog.createFromTemplateAction(randomFileName); + await dataTable.goThroughPagesLookingForRowWithName(randomFileName); + await expect(dataTable.getRowByName(randomFileName)).toBeVisible(); + }); + + test('[C325026] Create a file from a template - with a Name, Title and Description', async () => { + await createFileFromTemplateDialog.createFromTemplateAction(randomFileName, randomFileTitle, randomFileDescription); + await dataTable.goThroughPagesLookingForRowWithName(randomFileName); + await expect(dataTable.getCellLinkByName(randomFileName)).toHaveAttribute(titleLabel, randomFileTitle + `\n` + randomFileDescription); + }); + + test('[C325042] Trim spaces from file Name', async () => { + await createFileFromTemplateDialog.createFromTemplateAction(' ' + randomFileName + ' '); + await dataTable.goThroughPagesLookingForRowWithName(randomFileName); + await expect(dataTable.getRowByName(randomFileName)).toBeVisible(); + }); + }); + }); + + test.describe('File created from template on Personal Files Libraries', () => { + const randomLibraryName = `playwright-library-${Utils.random()}`; + + test.beforeAll(async ({ sitesApiAction, nodesApiAction }) => { + await sitesApiAction.createSite(randomLibraryName); + const libraryGuId = await sitesApiAction.getDocLibId(randomLibraryName); + await nodesApiAction.createFolder(commonFileName, libraryGuId); + }); + + test.beforeEach(async ({ myLibrariesPage }) => { + randomFileName = `playwright-file-${Utils.random()}`; + randomFileTitle = `file-title-${Utils.random()}`; + randomFileDescription = `file-description-${Utils.random()}`; + await myLibrariesPage.navigate(); + selectFileTemplateDialog = myLibrariesPage.contentNodeSelector; + createFileFromTemplateDialog = myLibrariesPage.createFromTemplateDialogComponent; + dataTable = myLibrariesPage.dataTable; + toolbar = myLibrariesPage.acaHeader; + await dataTable.goThroughPagesLookingForRowWithName(randomLibraryName); + await dataTable.getRowByName(randomLibraryName).dblclick(); + await dataTable.spinnerWaitForReload(); + await toolbar.clickCreateFileFromTemplate(); + await dataTable.getRowByName(template1InRoot).click(); + await selectFileTemplateDialog.actionButton.click(); + }); + + test.afterAll(async ({ sitesApiAction }) => { + await sitesApiAction.deleteSites([randomLibraryName]); + }); + + test('[C325023] Create a file from a template from library - with Name, Title and Description', async () => { + await createFileFromTemplateDialog.createFromTemplateAction(randomFileName, randomFileTitle, randomFileDescription); + await expect(dataTable.getCellLinkByName(randomFileName)).toHaveAttribute(titleLabel, randomFileTitle + `\n` + randomFileDescription); + }); + + test('[C325024] Cancel file creation in a library', async () => { + await expect(createFileFromTemplateDialog.getDialogTitle(createDialogTitle)).toBeVisible(); + await createFileFromTemplateDialog.cancelButton.click(); + await expect(createFileFromTemplateDialog.getDialogTitle(createDialogTitle)).not.toBeVisible(); + await expect(dataTable.getRowByName(randomFileName)).not.toBeVisible(); + }); + + test('[C325025] Create a file with a duplicate name in a library', async ({ myLibrariesPage }) => { + const snackBar = myLibrariesPage.snackBar; + + await createFileFromTemplateDialog.createFromTemplateAction(commonFileName); + await expect(snackBar.getByMessageLocator(errorStrings.nameAlreadyUsedError)).toBeVisible(); + await expect(createFileFromTemplateDialog.getDialogTitle(createDialogTitle)).toBeVisible(); + }); + }); +}); diff --git a/e2e/playwright/actions/src/tests/create-folder-from-template.spec.ts b/e2e/playwright/actions/src/tests/create-folder-from-template.spec.ts index 67c792f948..c5965f0f1f 100644 --- a/e2e/playwright/actions/src/tests/create-folder-from-template.spec.ts +++ b/e2e/playwright/actions/src/tests/create-folder-from-template.spec.ts @@ -305,7 +305,7 @@ test.describe('Create folder from template', () => { test('[C325156] Create a folder with a duplicate name', async ({ personalFiles }) => { const snackBar = personalFiles.snackBar; - await createFolderFromTemplateDialog.createNewFolderFromTemplate(commonFolderName); + await createFolderFromTemplateDialog.createFromTemplateAction(commonFolderName); await expect(snackBar.getByMessageLocator(errorStrings.nameAlreadyUsedError)).toBeVisible(); await expect(createFolderFromTemplateDialog.getDialogTitle(createDialogTitle)).toBeVisible(); }); @@ -334,7 +334,7 @@ test.describe('Create folder from template', () => { }); test('[C325157] Create a folder from a template - with a new Name', async () => { - await createFolderFromTemplateDialog.createNewFolderFromTemplate(randomFolderName); + await createFolderFromTemplateDialog.createFromTemplateAction(randomFolderName); await dataTable.goThroughPagesLookingForRowWithName(randomFolderName); await expect(dataTable.getRowByName(randomFolderName)).toBeVisible(); @@ -344,13 +344,13 @@ test.describe('Create folder from template', () => { }); test('[C325154] Create a folder from a template - with a Name, Title and Description', async () => { - await createFolderFromTemplateDialog.createNewFolderFromTemplate(randomFolderName, randomFolderTitle, randomFolderDescription); + await createFolderFromTemplateDialog.createFromTemplateAction(randomFolderName, randomFolderTitle, randomFolderDescription); await dataTable.goThroughPagesLookingForRowWithName(randomFolderName); await expect(dataTable.getCellLinkByName(randomFolderName)).toHaveAttribute(titleLabel, randomFolderTitle + `\n` + randomFolderDescription); }); test('[C325158] Trim spaces from folder Name', async () => { - await createFolderFromTemplateDialog.createNewFolderFromTemplate(' ' + randomFolderName + ' '); + await createFolderFromTemplateDialog.createFromTemplateAction(' ' + randomFolderName + ' '); await dataTable.goThroughPagesLookingForRowWithName(randomFolderName); await expect(dataTable.getRowByName(randomFolderName)).toBeVisible(); }); @@ -388,7 +388,7 @@ test.describe('Create folder from template', () => { }); test('[C325161] Create a folder from a template from library - with Name, Title and Description', async () => { - await createFolderFromTemplateDialog.createNewFolderFromTemplate(randomFolderName, randomFolderTitle, randomFolderDescription); + await createFolderFromTemplateDialog.createFromTemplateAction(randomFolderName, randomFolderTitle, randomFolderDescription); await expect .soft(dataTable.getCellLinkByName(randomFolderName)) .toHaveAttribute(titleLabel, randomFolderTitle + `\n` + randomFolderDescription); @@ -408,7 +408,7 @@ test.describe('Create folder from template', () => { test('[C325163] Create a folder with a duplicate name in a library', async ({ myLibrariesPage }) => { const snackBar = myLibrariesPage.snackBar; - await createFolderFromTemplateDialog.createNewFolderFromTemplate(commonFolderName); + await createFolderFromTemplateDialog.createFromTemplateAction(commonFolderName); await expect(snackBar.getByMessageLocator(errorStrings.nameAlreadyUsedError)).toBeVisible(); await expect(createFolderFromTemplateDialog.getDialogTitle(createDialogTitle)).toBeVisible(); }); diff --git a/e2e/protractor/suites/actions/create/create-file-from-template.test.ts b/e2e/protractor/suites/actions/create/create-file-from-template.test.ts deleted file mode 100755 index efeebdb9ff..0000000000 --- a/e2e/protractor/suites/actions/create/create-file-from-template.test.ts +++ /dev/null @@ -1,422 +0,0 @@ -/*! - * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved. - * - * Alfresco Example Content Application - * - * This file is part of the Alfresco Example Content Application. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * The Alfresco Example Content Application is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * The Alfresco Example Content Application is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * from Hyland Software. If not, see . - */ - -import { - LoginPage, - BrowsingPage, - SelectTemplateDialog, - CreateFromTemplateDialog, - Utils, - clearTextWithBackspace, - AdminActions, - RepoClient, - NodeContentTree, - UserActions -} from '@alfresco/aca-testing-shared'; -import { BrowserActions, Logger } from '@alfresco/adf-testing'; - -describe('Create file from template', () => { - const random = Utils.random(); - - const username = `user-${random}`; - - const restrictedTemplateFolder = `restricted-templates-${random}`; - const templateInRestrictedFolder = `template-restricted-${random}.txt`; - - const templatesFolder1 = `templates-folder1-${random}`; - const template1InFolder1 = `template1-1-${random}.txt`; - const template2InFolder1 = `template1-2-${random}.txt`; - - const templatesFolder2 = `templates-folder2-${random}`; - const template1InFolder2 = `template2-1-${random}.txt`; - const templatesSubFolder = `template-subFolder-${random}`; - - const template1InRootFolder = `template3-${random}.txt`; - const template2InRootFolder = `template4-${random}.txt`; - - const parent = `parent-${random}`; - let parentId: string; - const file1 = { - name: `file1-${random}.txt` - }; - const file2 = { - name: `file2-${random}.txt`, - title: `file2 title`, - description: `file2 description` - }; - const duplicateFileName = `duplicate-file-${random}.txt`; - const nameWithSpaces = ` file-${random}.txt `; - - const siteName = `site-${random}`; - const fileSite = { - name: `file-site-${random}.txt`, - title: `file site title`, - description: `file site description` - }; - const duplicateFileSite = `duplicate-file-site-${random}.txt`; - let docLibUserSite: string; - - const userApi = new RepoClient(username, username); - - const adminApiActions = new AdminActions(); - const userActions = new UserActions(); - - const loginPage = new LoginPage(); - const page = new BrowsingPage(); - const selectTemplateDialog = new SelectTemplateDialog(); - const createFromTemplateDialog = new CreateFromTemplateDialog(); - const { toolbar } = page; - - const templates: NodeContentTree = { - folders: [ - { - name: templatesFolder1, - files: [template1InFolder1, template2InFolder1] - }, - { - name: templatesFolder2, - folders: [ - { - name: templatesSubFolder - } - ], - files: [template1InFolder2] - }, - { - name: restrictedTemplateFolder, - files: [templateInRestrictedFolder] - } - ], - files: [template1InRootFolder, template2InRootFolder] - }; - let link: string; - - beforeAll(async () => { - await adminApiActions.createUser({ username }); - await userActions.login(username, username); - - parentId = await userApi.createFolder(parent); - await userApi.nodes.createFile(duplicateFileName, parentId); - - await userApi.sites.createSite(siteName); - docLibUserSite = await userApi.sites.getDocLibId(siteName); - await userApi.createFile(duplicateFileSite, docLibUserSite); - - await loginPage.loginWith(username); - }); - - afterAll(async () => { - await userActions.login(username, username); - await userActions.deleteNodes([parentId]); - await userActions.deleteSites([siteName]); - - await adminApiActions.login(); - await adminApiActions.cleanupNodeTemplatesItems([ - templatesFolder1, - templatesFolder2, - restrictedTemplateFolder, - template1InRootFolder, - template2InRootFolder - ]); - }); - - beforeEach(async () => { - await page.closeOpenDialogs(); - }); - - describe('with existing templates', () => { - beforeAll(async () => { - await adminApiActions.login(); - await adminApiActions.createNodeTemplatesHierarchy(templates); - await adminApiActions.removeUserAccessOnNodeTemplate(restrictedTemplateFolder); - link = (await adminApiActions.createLinkToFileName(template2InRootFolder, await adminApiActions.getNodeTemplatesFolderId())).entry.name; - }); - - describe('Select Template dialog', () => { - beforeEach(async () => { - await toolbar.openCreateFileFromTemplateDialog(); - await selectTemplateDialog.waitForDialogToOpen(); - }); - - it('[C325043] Select template - dialog UI - with existing templates', async () => { - expect(await selectTemplateDialog.getDialogTitle()).toEqual('Select a document template'); - expect(await selectTemplateDialog.dataTable.isEmpty()).toBe(false, 'Datatable is empty'); - expect(await selectTemplateDialog.dataTable.isItemPresent(templatesFolder1)).toBe(true, 'template folder not displayed'); - expect(await selectTemplateDialog.dataTable.isItemPresent(templatesFolder2)).toBe(true, 'template folder not displayed'); - expect(await selectTemplateDialog.dataTable.isItemPresent(template1InRootFolder)).toBe(true, 'template not displayed'); - expect(await selectTemplateDialog.dataTable.isItemPresent(template2InRootFolder)).toBe(true, 'template not displayed'); - expect(await selectTemplateDialog.breadcrumb.currentFolder.getText()).toEqual('Node Templates'); - expect(await selectTemplateDialog.isNextButtonEnabled()).toBe(false, 'Next button is not disabled'); - expect(await selectTemplateDialog.isCancelButtonEnabled()).toBe(true, 'Cancel button is not enabled'); - }); - - it(`[C325044] Templates don't appear if user doesn't have permissions to see them`, async () => { - expect(await selectTemplateDialog.dataTable.isItemPresent(restrictedTemplateFolder)).toBe(false, 'restricted templates folder is displayed'); - }); - - it('[C325045] Navigate through the templates list with folder hierarchy', async () => { - expect(await selectTemplateDialog.dataTable.isItemPresent(templatesFolder2)).toBe(true, 'template folder not displayed'); - - await selectTemplateDialog.dataTable.doubleClickOnRowByName(templatesFolder2); - - expect(await selectTemplateDialog.dataTable.isItemPresent(templatesSubFolder)).toBe(true, 'template sub-folder not displayed'); - expect(await selectTemplateDialog.dataTable.isItemPresent(template1InFolder2)).toBe(true, 'template not displayed'); - expect(await selectTemplateDialog.dataTable.isItemPresent(template1InRootFolder)).toBe(false, 'template is displayed'); - expect(await selectTemplateDialog.dataTable.isItemPresent(template2InRootFolder)).toBe(false, 'template is displayed'); - expect(await selectTemplateDialog.breadcrumb.currentFolder.getText()).toEqual(templatesFolder2); - - await selectTemplateDialog.dataTable.doubleClickOnRowByName(templatesSubFolder); - - expect(await selectTemplateDialog.breadcrumb.currentFolder.getText()).toEqual(templatesSubFolder); - expect(await selectTemplateDialog.dataTable.isEmpty()).toBe(true, 'datatable is not empty'); - - await selectTemplateDialog.breadcrumb.openPath(); - - expect(await selectTemplateDialog.breadcrumb.getPathItems()).toEqual([templatesFolder2, 'Node Templates']); - }); - - it(`[C325047] Templates list doesn't allow multiple selection`, async () => { - expect(await selectTemplateDialog.dataTable.getSelectedRowsCount()).toEqual(0, 'Incorrect number of selected rows'); - - await selectTemplateDialog.dataTable.selectItem(template1InRootFolder); - expect(await selectTemplateDialog.dataTable.getSelectedRowsCount()).toEqual(1, 'Incorrect number of selected rows'); - expect(await selectTemplateDialog.dataTable.getSelectedRowsNames()).toEqual([template1InRootFolder], 'Incorrect selected item'); - - await Utils.pressCmd(); - await selectTemplateDialog.dataTable.selectItem(template2InRootFolder); - await Utils.releaseKeyPressed(); - - expect(await selectTemplateDialog.dataTable.getSelectedRowsCount()).toEqual(1, 'Incorrect number of selected rows'); - expect(await selectTemplateDialog.dataTable.getSelectedRowsNames()).toEqual([template2InRootFolder], 'Incorrect selected item'); - }); - - it('[C325050] Links to files are not displayed', async () => { - expect(await selectTemplateDialog.dataTable.isItemPresent(link)).toBe(false, 'Link to file is displayed'); - }); - - it('[C325048] Cancel the Select template dialog', async () => { - expect(await selectTemplateDialog.isCancelButtonEnabled()).toBe(true, 'Cancel button is not enabled'); - - await selectTemplateDialog.clickCancel(); - - expect(await selectTemplateDialog.isDialogOpen()).toBe(false, 'Select Template dialog is open'); - }); - - it('[C216339] Next button is disabled when selecting a folder', async () => { - expect(await selectTemplateDialog.isNextButtonEnabled()).toBe(false, 'Next button is enabled'); - - await selectTemplateDialog.dataTable.selectItem(templatesFolder1); - - expect(await selectTemplateDialog.isNextButtonEnabled()).toBe(false, 'Next button is enabled'); - }); - }); - - describe('Create from template dialog', () => { - beforeEach(async () => { - try { - await toolbar.openCreateFileFromTemplateDialog(); - await selectTemplateDialog.waitForDialogToOpen(); - await selectTemplateDialog.dataTable.selectItem(template1InRootFolder); - await selectTemplateDialog.clickNext(); - await createFromTemplateDialog.waitForDialogToOpen(); - } catch (error) { - Logger.error(`----- beforeEach failed : ${error}`); - } - }); - - it('[C325020] Create file from template - dialog UI', async () => { - expect(await createFromTemplateDialog.getDialogTitle()).toEqual(`Create new document from '${template1InRootFolder}'`); - expect(await createFromTemplateDialog.nameInput.isDisplayed()).toBe(true, 'Name field not displayed'); - expect(await createFromTemplateDialog.titleInput.isDisplayed()).toBe(true, 'Title field not displayed'); - expect(await createFromTemplateDialog.descriptionTextArea.isDisplayed()).toBe(true, 'Description field not displayed'); - expect(await createFromTemplateDialog.isCancelButtonEnabled()).toBe(true, 'Cancel button is not enabled'); - expect(await createFromTemplateDialog.isCreateButtonEnabled()).toBe(true, 'Create button is not enabled'); - }); - - it('[C325031] File name is required', async () => { - expect(await createFromTemplateDialog.getNameInputValue()).toEqual(template1InRootFolder); - await clearTextWithBackspace(createFromTemplateDialog.nameInput); - - expect(await createFromTemplateDialog.getValidationMessage()).toEqual('Name is required'); - expect(await createFromTemplateDialog.isCreateButtonEnabled()).toBe(false, 'Create button is not disabled'); - }); - - it('[C325032] Special characters in file name', async () => { - const namesWithSpecialChars = ['a*a', 'a"a', 'aa', `a\\a`, 'a/a', 'a?a', 'a:a', 'a|a']; - - for (const name of namesWithSpecialChars) { - await createFromTemplateDialog.enterName(name); - expect(await createFromTemplateDialog.isCreateButtonEnabled()).toBe(false, 'Create button is not disabled'); - expect(await createFromTemplateDialog.getValidationMessage()).toContain(`Name can't contain these characters`); - } - }); - - it('[C325033] File name ending with a dot', async () => { - await createFromTemplateDialog.enterName('file-name.'); - - expect(await createFromTemplateDialog.isCreateButtonEnabled()).toBe(false, 'Create button is not disabled'); - expect(await createFromTemplateDialog.getValidationMessage()).toMatch(`Name can't end with a period .`); - }); - - it('[C325034] File name containing only spaces', async () => { - await createFromTemplateDialog.enterName(' '); - - expect(await createFromTemplateDialog.isCreateButtonEnabled()).toBe(false, 'Create button is not disabled'); - expect(await createFromTemplateDialog.getValidationMessage()).toMatch(`Name can't contain only spaces`); - }); - - it('[C290146] Title too long', async () => { - await createFromTemplateDialog.enterTitle(Utils.string257); - await Utils.pressTab(); - - expect(await createFromTemplateDialog.isCreateButtonEnabled()).toBe(false, 'Create button is not disabled'); - expect(await createFromTemplateDialog.getValidationMessage()).toMatch(`Use 256 characters or less for title`); - }); - - it('[C290142] Description too long', async () => { - await createFromTemplateDialog.enterDescription(Utils.string513); - await Utils.pressTab(); - - expect(await createFromTemplateDialog.isCreateButtonEnabled()).toBe(false, 'Create button is not disabled'); - expect(await createFromTemplateDialog.getValidationMessage()).toMatch(`Use 512 characters or less for description`); - }); - }); - - describe('On Personal Files', () => { - beforeEach(async () => { - try { - await page.clickPersonalFilesAndWait(); - await page.dataTable.doubleClickOnRowByName(parent); - await toolbar.openCreateFileFromTemplateDialog(); - await selectTemplateDialog.waitForDialogToOpen(); - await selectTemplateDialog.dataTable.selectItem(template1InRootFolder); - await selectTemplateDialog.clickNext(); - await createFromTemplateDialog.waitForDialogToOpen(); - } catch (error) { - Logger.error(`----- beforeEach failed : ${error}`); - } - }); - - it('[C325030] Create a file from a template - with a new Name', async () => { - await createFromTemplateDialog.enterName(file1.name); - await BrowserActions.click(createFromTemplateDialog.createButton); - await createFromTemplateDialog.waitForDialogToClose(); - await page.dataTable.waitForHeader(); - - expect(await page.dataTable.isItemPresent(file1.name)).toBe(true, 'File not displayed in list view'); - }); - - it('[C325026] Create a file from a template - with a Name, Title and Description', async () => { - await createFromTemplateDialog.enterName(file2.name); - await createFromTemplateDialog.enterTitle(file2.title); - await createFromTemplateDialog.enterDescription(file2.description); - - await BrowserActions.click(createFromTemplateDialog.createButton); - await createFromTemplateDialog.waitForDialogToClose(); - await page.dataTable.waitForHeader(); - - expect(await page.dataTable.isItemPresent(file2.name)).toBe(true, 'File not displayed in list view'); - const desc = await userApi.nodes.getNodeDescription(file2.name, parentId); - expect(desc).toEqual(file2.description); - const title = await userApi.nodes.getNodeTitle(file2.name, parentId); - expect(title).toEqual(file2.title); - }); - - it('[C325028] Create a file with a duplicate name', async () => { - await createFromTemplateDialog.enterName(duplicateFileName); - await BrowserActions.click(createFromTemplateDialog.createButton); - - expect(await page.getSnackBarMessage()).toEqual(`This name is already in use, try a different name.`); - expect(await createFromTemplateDialog.isDialogOpen()).toBe(true, 'dialog is not present'); - }); - - it('[C325027] Cancel file creation', async () => { - await createFromTemplateDialog.enterName('test'); - await createFromTemplateDialog.clickCancel(); - - expect(await createFromTemplateDialog.isDialogOpen()).not.toBe(true, 'dialog is not closed'); - expect(await page.dataTable.isItemPresent('test')).toBe(false, 'File should not appear in the list'); - }); - - it('[C325042] Trim spaces from file Name', async () => { - await createFromTemplateDialog.enterName(nameWithSpaces); - await BrowserActions.click(createFromTemplateDialog.createButton); - await createFromTemplateDialog.waitForDialogToClose(); - await page.dataTable.waitForHeader(); - - expect(await page.dataTable.isItemPresent(nameWithSpaces.trim())).toBe(true, 'File not displayed in list view'); - }); - }); - - describe('On File Libraries', () => { - const fileLibrariesPage = new BrowsingPage(); - - beforeEach(async () => { - try { - await fileLibrariesPage.goToMyLibrariesAndWait(); - await page.dataTable.doubleClickOnRowByName(siteName); - await toolbar.openCreateFileFromTemplateDialog(); - await selectTemplateDialog.waitForDialogToOpen(); - await selectTemplateDialog.dataTable.selectItem(template1InRootFolder); - await selectTemplateDialog.clickNext(); - await createFromTemplateDialog.waitForDialogToOpen(); - } catch (error) { - Logger.error(`----- beforeEach failed : ${error}`); - } - }); - - it('[C325023] Create a file from a template - with Name, Title and Description', async () => { - await createFromTemplateDialog.enterName(fileSite.name); - await createFromTemplateDialog.enterTitle(fileSite.title); - await createFromTemplateDialog.enterDescription(fileSite.description); - - await BrowserActions.click(createFromTemplateDialog.createButton); - await createFromTemplateDialog.waitForDialogToClose(); - await page.dataTable.waitForHeader(); - - expect(await page.dataTable.isItemPresent(fileSite.name)).toBe(true, 'File not displayed in list view'); - const desc = await userApi.nodes.getNodeDescription(fileSite.name, docLibUserSite); - expect(desc).toEqual(fileSite.description); - const title = await userApi.nodes.getNodeTitle(fileSite.name, docLibUserSite); - expect(title).toEqual(fileSite.title); - }); - - it('[C325024] Cancel file creation', async () => { - await createFromTemplateDialog.enterName('test'); - await createFromTemplateDialog.clickCancel(); - - expect(await createFromTemplateDialog.isDialogOpen()).not.toBe(true, 'dialog is not closed'); - expect(await page.dataTable.isItemPresent('test')).toBe(false, 'File should not appear in the list'); - }); - - it('[C325025] Create a file with a duplicate name', async () => { - await createFromTemplateDialog.enterName(duplicateFileSite); - await BrowserActions.click(createFromTemplateDialog.createButton); - - expect(await page.getSnackBarMessage()).toEqual(`This name is already in use, try a different name.`); - expect(await createFromTemplateDialog.isDialogOpen()).toBe(true, 'dialog is not present'); - }); - }); - }); -}); diff --git a/projects/aca-content/src/lib/dialogs/node-template/create-from-template.dialog.html b/projects/aca-content/src/lib/dialogs/node-template/create-from-template.dialog.html index e3f007132b..a2f375368f 100644 --- a/projects/aca-content/src/lib/dialogs/node-template/create-from-template.dialog.html +++ b/projects/aca-content/src/lib/dialogs/node-template/create-from-template.dialog.html @@ -42,7 +42,7 @@

- diff --git a/projects/aca-playwright-shared/src/api/nodes-api.ts b/projects/aca-playwright-shared/src/api/nodes-api.ts index 8cb7d3837f..fd9455c48b 100755 --- a/projects/aca-playwright-shared/src/api/nodes-api.ts +++ b/projects/aca-playwright-shared/src/api/nodes-api.ts @@ -193,6 +193,18 @@ export class NodesApi { } } + async cleanupNodeTemplatesItems(nodeNames: string[]): Promise { + try { + const templatesFolderId = await this.getNodeTemplatesFolderId(); + for (const nodeName of nodeNames) { + const nodeId = await this.getNodeIdFromParent(nodeName, templatesFolderId); + await this.deleteNodeById(nodeId); + } + } catch (error) { + logger.error('Admin Actions - cleanupNodeTemplatesItems failed : ', error); + } + } + async cleanupSpaceTemplatesItems(nodeNames: string[]): Promise { try { const spaceTemplatesNodeId = await this.getSpaceTemplatesFolderId(); @@ -205,6 +217,15 @@ export class NodesApi { } } + async getNodeTemplatesFolderId(): Promise { + try { + return this.getNodeIdFromParent('Node Templates', await this.getDataDictionaryId()); + } catch (error) { + logger.error('Admin Actions - getNodeTemplatesFolderId failed : ', error); + return ''; + } + } + async getSpaceTemplatesFolderId(): Promise { try { return this.getNodeIdFromParent('Space Templates', await this.getDataDictionaryId()); @@ -243,6 +264,18 @@ export class NodesApi { } } + async removeUserAccessOnNodeTemplate(nodeName: string): Promise { + try { + const templatesRootFolderId = await this.getNodeTemplatesFolderId(); + const nodeId: string = await this.getNodeIdFromParent(nodeName, templatesRootFolderId); + + return this.setInheritPermissions(nodeId, false); + } catch (error) { + logger.error('Admin Actions - removeUserAccessOnNodeTemplate failed : ', error); + return null; + } + } + async removeUserAccessOnSpaceTemplate(nodeName: string): Promise { try { const templatesRootFolderId = await this.getSpaceTemplatesFolderId(); @@ -279,6 +312,26 @@ export class NodesApi { } } + async createFileLink(originalNodeId: string, destinationId: string): Promise { + const name = (await this.getNodeById(originalNodeId)).entry.name; + const nodeBody = { + name: `Link to ${name}.url`, + nodeType: 'app:filelink', + properties: { + 'cm:destination': originalNodeId + } + }; + + try { + const link = await this.apiService.nodes.createNode(destinationId, nodeBody); + await this.addAspects(originalNodeId, ['app:linked']); + return link; + } catch (error) { + logger.error(`${this.constructor.name} ${this.createFileLink.name}`, error); + return null; + } + } + async createFolderLink(originalNodeId: string, destinationId: string): Promise { const name = (await this.getNodeById(originalNodeId)).entry.name; const nodeBody = { @@ -302,10 +355,21 @@ export class NodesApi { } } - async createLinkToFolderName(originalFolderName: string, originalFolderParentId: string, destinationParentId?: string): Promise { - if (!destinationParentId) { - destinationParentId = originalFolderParentId; + async createLinkToFileName(originalFileName: string, originalFileParentId: string, destinationParentId?: string): Promise { + destinationParentId = destinationParentId ?? originalFileParentId; + + try { + const nodeId = await this.getNodeIdFromParent(originalFileName, originalFileParentId); + + return this.createFileLink(nodeId, destinationParentId); + } catch (error) { + logger.error('Admin Actions - createLinkToFileName failed : ', error); + return null; } + } + + async createLinkToFolderName(originalFolderName: string, originalFolderParentId: string, destinationParentId?: string): Promise { + destinationParentId = destinationParentId ?? originalFolderParentId; try { const nodeId = await this.getNodeIdFromParent(originalFolderName, originalFolderParentId); diff --git a/projects/aca-playwright-shared/src/page-objects/components/aca-header.component.ts b/projects/aca-playwright-shared/src/page-objects/components/aca-header.component.ts index f8b791d485..34dadd3bb4 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/aca-header.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/aca-header.component.ts @@ -50,4 +50,9 @@ export class AcaHeader extends BaseComponent { await this.createButton.click(); await this.matMenu.createFolderFromTemplate.click(); } + + async clickCreateFileFromTemplate(): Promise { + await this.createButton.click(); + await this.matMenu.createFileFromTemplate.click(); + } } diff --git a/projects/aca-playwright-shared/src/page-objects/components/dataTable/mat-menu.component.ts b/projects/aca-playwright-shared/src/page-objects/components/dataTable/mat-menu.component.ts index d9e5085f96..97c4edbbdc 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/dataTable/mat-menu.component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/dataTable/mat-menu.component.ts @@ -36,6 +36,7 @@ export class MatMenuComponent extends BaseComponent { public getMenuItemTextLocator = this.getChild('[data-automation-id="menu-item-title"]'); public createFolder = this.getChild('[id="app.create.folder"]'); public createFolderFromTemplate = this.getChild('[id="app.create.folderFromTemplate"]'); + public createFileFromTemplate = this.getChild('[id="app.create.fileFromTemplate"]'); public createLibrary = this.getChild('[id="app.create.library"]'); public getButtonByText = (text: string) => this.getChild('button', { hasText: text }); diff --git a/projects/aca-playwright-shared/src/page-objects/components/dialogs/create-from-template-dialog-component.ts b/projects/aca-playwright-shared/src/page-objects/components/dialogs/create-from-template-dialog-component.ts index 9b40e6998d..c17b8a67f9 100644 --- a/projects/aca-playwright-shared/src/page-objects/components/dialogs/create-from-template-dialog-component.ts +++ b/projects/aca-playwright-shared/src/page-objects/components/dialogs/create-from-template-dialog-component.ts @@ -33,8 +33,8 @@ export class CreateFromTemplateDialogComponent extends BaseComponent { super(page, CreateFromTemplateDialogComponent.rootElement); } - cancelButton = this.getChild('[data-automation-id="cancel-folder-template-button"]'); - createButton = this.getChild('[data-automation-id="create-folder-template-button"]'); + cancelButton = this.getChild('[data-automation-id="create-from-template-dialog-cancel-button"]'); + createButton = this.getChild('[data-automation-id="create-from-template-dialog-create-button"]'); getDialogTitle = (text: string) => this.getChild('.mat-dialog-title', { hasText: text }); getDialogLabel = (text: string) => this.getChild('label', { hasText: text }); getErrorByText = (text: string): Locator => this.page.locator('mat-error', {hasText: text}); @@ -46,9 +46,9 @@ export class CreateFromTemplateDialogComponent extends BaseComponent { } /** - * This method is used when we want to fill in Create new folder from template dialog and choose Create button + * This method is used when we want to fill in Create new folder/document/file from template dialog and choose Create button */ - async createNewFolderFromTemplate( nameInput: string, titleInput?: string, descriptionInput?: string): Promise { + async createFromTemplateAction( nameInput: string, titleInput?: string, descriptionInput?: string): Promise { await this.getDialogLabel('Name *').fill(nameInput); if (titleInput) { await this.getDialogLabel('Title').fill(titleInput); } if (descriptionInput) { await this.getDialogLabel('Description').fill(descriptionInput); }