[ACA-1921] automate tests for Create folder from template (#1320)

* automate tests for Create folder from template
added unit test for exclusion of folder links

* fix stupid mistake
This commit is contained in:
Adina Parpalita 2020-01-23 17:47:25 +02:00 committed by Cilibiu Bogdan
parent 1eab186340
commit 2733c69c9a
10 changed files with 597 additions and 85 deletions

View File

@ -33,7 +33,15 @@ export class Menu extends Component {
root: '.mat-menu-panel',
item: '.mat-menu-item',
icon: '.mat-icon',
uploadFiles: 'app-upload-files',
uploadFilesInput: 'app-upload-files',
uploadFile: 'app.create.uploadFile',
uploadFolder: 'app.create.uploadFolder',
createFolder: 'app.create.folder',
createLibrary: 'app.create.library',
createFileFromTemplate: 'app.create.fileFromTemplate',
createFolderFromTemplate: 'app.create.folderFromTemplate',
submenu: 'app-context-menu-item .mat-menu-item',
@ -46,14 +54,20 @@ export class Menu extends Component {
items: ElementArrayFinder = this.component.all(by.css(Menu.selectors.item));
backdrop: ElementFinder = browser.element(by.css('.cdk-overlay-backdrop'));
uploadFiles: ElementFinder = browser.element(by.id(Menu.selectors.uploadFiles));
uploadFilesInput: ElementFinder = browser.element(by.id(Menu.selectors.uploadFilesInput));
submenus: ElementArrayFinder = browser.element.all(by.css(Menu.selectors.submenu));
uploadFileAction: ElementFinder = this.component.element(by.id(Menu.selectors.uploadFile));
uploadFolderAction: ElementFinder = this.component.element(by.id(Menu.selectors.uploadFolder));
createFolderAction: ElementFinder = this.component.element(by.id(Menu.selectors.createFolder));
createLibraryAction: ElementFinder = this.component.element(by.id(Menu.selectors.createLibrary));
createFileFromTemplateAction: ElementFinder = this.component.element(by.id(Menu.selectors.createFileFromTemplate));
createFolderFromTemplateAction: ElementFinder = this.component.element(by.id(Menu.selectors.createFolderFromTemplate));
cancelEditingAction: ElementFinder = this.component.element(by.css(Menu.selectors.cancelEditing));
cancelJoinAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.item, 'Cancel Join'));
copyAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.item, 'Copy'));
createFolderAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.item, 'Create Folder'));
createLibraryAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.item, 'Create Library'));
deleteAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.item, 'Delete'));
downloadAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.item, 'Download'));
editFolderAction: ElementFinder = this.component.element(by.css(Menu.selectors.editFolder));
@ -72,9 +86,6 @@ export class Menu extends Component {
restoreAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.item, 'Restore'));
shareAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.item, 'Share'));
shareEditAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.item, 'Shared Link Settings'));
uploadFileAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.item, 'Upload File'));
uploadFolderAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.item, 'Upload Folder'));
createFileFromTemplateAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.item, 'Create file from template'));
viewAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.item, 'View'));
viewDetailsAction: ElementFinder = this.component.element(by.cssContainingText(Menu.selectors.item, 'View Details'));
@ -235,7 +246,7 @@ export class Menu extends Component {
}
uploadFile(): ElementFinder {
return this.uploadFiles;
return this.uploadFilesInput;
}
async clickEditFolder(): Promise<void> {
@ -364,6 +375,10 @@ export class Menu extends Component {
return (await this.createFileFromTemplateAction.isPresent()) && (await this.createFileFromTemplateAction.isEnabled());
}
async isCreateFolderFromTemplateEnabled(): Promise<boolean> {
return (await this.createFolderFromTemplateAction.isPresent()) && (await this.createFolderFromTemplateAction.isEnabled());
}
async clickCreateFolder(): Promise<void> {
const action = this.createFolderAction;
await action.click();
@ -374,18 +389,13 @@ export class Menu extends Component {
await action.click();
}
async clickUploadFile(): Promise<void> {
const action = this.uploadFileAction;
await action.click();
}
async clickUploadFolder(): Promise<void> {
const action = this.uploadFolderAction;
await action.click();
}
async clickCreateFileFromTemplate(): Promise<void> {
const action = this.createFileFromTemplateAction;
await action.click();
}
async clickCreateFolderFromTemplate(): Promise<void> {
const action = this.createFolderFromTemplateAction;
await action.click();
}
}

View File

@ -55,6 +55,7 @@ export class Sidenav extends Component {
links: ElementArrayFinder = this.component.all(by.css(Sidenav.selectors.link));
activeLink: ElementFinder = this.component.element(by.css(Sidenav.selectors.activeClass));
newButton: ElementArrayFinder = this.component.all(by.css(Sidenav.selectors.newButton));
personalFiles: ElementFinder = this.component.element(by.css(Sidenav.selectors.personalFiles));
@ -90,25 +91,28 @@ export class Sidenav extends Component {
}
async openNewMenu(): Promise<void> {
const { menu, newButton } = this;
await newButton.click();
await menu.waitForMenuToOpen();
await this.newButton.click();
await this.menu.waitForMenuToOpen();
}
async openCreateFolderDialog(): Promise<void> {
await this.openNewMenu();
await this.menu.clickMenuItem('Create Folder');
await this.menu.clickCreateFolder();
}
async openCreateLibraryDialog(): Promise<void> {
await this.openNewMenu();
await this.menu.clickMenuItem('Create Library');
await this.menu.clickCreateLibrary();
}
async openCreateFileFromTemplateDialog(): Promise<void> {
await this.openNewMenu();
await this.menu.clickMenuItem('Create file from template');
await this.menu.clickCreateFileFromTemplate();
}
async openCreateFolderFromTemplateDialog(): Promise<void> {
await this.openNewMenu();
await this.menu.clickCreateFolderFromTemplate();
}
async isActive(name: string): Promise<boolean> {

View File

@ -67,7 +67,7 @@ describe('Create file from template', () => {
title: `file site title`,
description: `file site description`
};
const duplicateFileSite = `duplicate-file-site-${random}`;
const duplicateFileSite = `duplicate-file-site-${random}.txt`;
let docLibUserSite: string;
const userApi = new RepoClient(username, username);
@ -80,7 +80,7 @@ describe('Create file from template', () => {
const createFromTemplateDialog = new CreateFromTemplateDialog();
const { sidenav } = page;
beforeAll( async (done) => {
beforeAll(async () => {
await adminApiActions.createUser({ username });
parentId = (await userApi.nodes.createFolder(parent)).entry.id;
@ -88,17 +88,15 @@ describe('Create file from template', () => {
await userApi.sites.createSite(siteName);
docLibUserSite = await userApi.sites.getDocLibId(siteName);
await userApi.nodes.createFolder(duplicateFileSite, docLibUserSite);
await userApi.nodes.createFile(duplicateFileSite, docLibUserSite);
await loginPage.loginWith(username);
done();
});
afterAll(async (done) => {
afterAll(async () => {
await userApi.nodes.deleteNodeById(parentId);
await userApi.sites.deleteSite(siteName);
await adminApiActions.cleanNodeTemplatesFolder();
done();
await adminApiActions.cleanupNodeTemplatesFolder();
});
beforeEach(async () => {
@ -142,18 +140,16 @@ describe('Create file from template', () => {
};
let link: string;
beforeAll(async (done) => {
beforeAll(async () => {
await adminApiActions.createNodeTemplatesHierarchy(templates);
await adminApiActions.removeUserAccessOnNode(restrictedTemplateFolder);
await adminApiActions.removeUserAccessOnNodeTemplate(restrictedTemplateFolder);
link = (await adminApiActions.createLinkToFileName(template2InRootFolder, await adminApiActions.getNodeTemplatesFolderId())).entry.name;
done();
});
describe('Select Template dialog', () => {
beforeEach(async (done) => {
beforeEach(async () => {
await sidenav.openCreateFileFromTemplateDialog();
await selectTemplateDialog.waitForDialogToOpen();
done();
});
it('Select template - dialog UI - with existing templates - [C325043]', async () => {
@ -230,13 +226,12 @@ describe('Create file from template', () => {
});
describe('Create from template dialog', () => {
beforeEach(async (done) => {
beforeEach(async () => {
await sidenav.openCreateFileFromTemplateDialog();
await selectTemplateDialog.waitForDialogToOpen();
await selectTemplateDialog.dataTable.selectItem(template1InRootFolder);
await selectTemplateDialog.clickNext();
await createFromTemplateDialog.waitForDialogToOpen();
done();
});
it('Create file from template - dialog UI - [C325020]', async () => {
@ -298,7 +293,7 @@ describe('Create file from template', () => {
});
describe('On Personal Files', () => {
beforeEach(async (done) => {
beforeEach(async () => {
await page.clickPersonalFilesAndWait();
await page.dataTable.doubleClickOnRowByName(parent);
await sidenav.openCreateFileFromTemplateDialog();
@ -306,7 +301,6 @@ describe('Create file from template', () => {
await selectTemplateDialog.dataTable.selectItem(template1InRootFolder);
await selectTemplateDialog.clickNext();
await createFromTemplateDialog.waitForDialogToOpen();
done();
});
it('Create a file from a template - with a new Name - [C325030]', async () => {
@ -318,7 +312,7 @@ describe('Create file from template', () => {
expect(await page.dataTable.isItemPresent(file1.name)).toBe(true, 'File not displayed in list view');
});
it('Create a file from a template - with a Name, Title and Description - [C325026]', async (done) => {
it('Create a file from a template - with a Name, Title and Description - [C325026]', async () => {
await createFromTemplateDialog.enterName(file2.name);
await createFromTemplateDialog.enterTitle(file2.title);
await createFromTemplateDialog.enterDescription(file2.description);
@ -331,7 +325,6 @@ describe('Create file from template', () => {
expect(desc).toEqual(file2.description);
const title = await userApi.nodes.getNodeTitle(file2.name, parentId);
expect(title).toEqual(file2.title);
done();
});
it('Create a file with a duplicate name - [C325028]', async () => {
@ -356,14 +349,14 @@ describe('Create file from template', () => {
await createFromTemplateDialog.waitForDialogToClose();
await page.dataTable.waitForHeader();
expect(await page.dataTable.isItemPresent(nameWithSpaces.trim())).toBe(true, 'Folder not displayed in list view');
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 (done) => {
beforeEach(async () => {
await fileLibrariesPage.goToMyLibrariesAndWait();
await page.dataTable.doubleClickOnRowByName(siteName);
await sidenav.openCreateFileFromTemplateDialog();
@ -371,10 +364,9 @@ describe('Create file from template', () => {
await selectTemplateDialog.dataTable.selectItem(template1InRootFolder);
await selectTemplateDialog.clickNext();
await createFromTemplateDialog.waitForDialogToOpen();
done();
});
it('Create a file from a template - with Name, Title and Description - [C325023]', async (done) => {
it('Create a file from a template - with Name, Title and Description - [C325023]', async () => {
await createFromTemplateDialog.enterName(fileSite.name);
await createFromTemplateDialog.enterTitle(fileSite.title);
await createFromTemplateDialog.enterDescription(fileSite.description);
@ -387,7 +379,6 @@ describe('Create file from template', () => {
expect(desc).toEqual(fileSite.description);
const title = await userApi.nodes.getNodeTitle(fileSite.name, docLibUserSite);
expect(title).toEqual(fileSite.title);
done();
});
it('Cancel file creation - [C325024]', async () => {

View File

@ -0,0 +1,387 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* This file is part of the Alfresco Example Content Application.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* The Alfresco Example Content Application is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Alfresco Example Content Application is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { LoginPage, BrowsingPage } from '../../pages/pages';
import { SelectTemplateDialog } from '../../components/dialog/select-template-dialog';
import { CreateFromTemplateDialog } from '../../components/dialog/create-from-template-dialog';
import { Utils } from '../../utilities/utils';
import { AdminActions } from '../../utilities/admin-actions';
import { RepoClient, NodeContentTree } from '../../utilities/repo-client/repo-client';
describe('Create folder from template', () => {
const random = Utils.random();
const username = `user-${random}`;
const restrictedTemplateFolder = `restricted-folder-${random}`;
const fileInRestrictedFolder = `restricted-file-${random}.txt`;
const templateFolder1 = `template-folder1-${random}`;
const fileInFolder1 = `file-1-${random}.txt`;
const templateFolder2 = `template-folder2-${random}`;
const fileInFolder2 = `file-2-${random}.txt`;
const templateSubFolder = `template-sub-folder-${random}`;
const fileInRootFolder = `file-in-root-${random}.txt`;
const folderInRootFolder = `folder-in-root-${random}`;
const parent = `parent-${random}`;
let parentId: string;
const folder1 = {
name: `folder1-${random}`
};
const folder2 = {
name: `folder2-${random}`,
title: `folder2 title`,
description: `folder2 description`
};
const duplicateFolderName = `duplicate-folder-${random}`;
const nameWithSpaces = ` folder-${random} `;
const siteName = `site-${random}`;
const folderSite = {
name: `folder-site-${random}`,
title: `folder site title`,
description: `folder site description`
};
const duplicateFolderSite = `duplicate-folder-site-${random}`;
let docLibUserSite: string;
const templates: NodeContentTree = {
folders: [
{
name: folderInRootFolder
},
{
name: templateFolder1,
files: [fileInFolder1]
},
{
name: templateFolder2,
folders: [
{
name: templateSubFolder
}
],
files: [fileInFolder2]
},
{
name: restrictedTemplateFolder,
files: [fileInRestrictedFolder]
}
],
files: [fileInRootFolder]
};
let folderLink: string;
const userApi = new RepoClient(username, username);
const adminApiActions = new AdminActions();
const loginPage = new LoginPage();
const page = new BrowsingPage();
const selectTemplateDialog = new SelectTemplateDialog();
const createFromTemplateDialog = new CreateFromTemplateDialog();
const { sidenav } = page;
beforeAll(async () => {
await adminApiActions.createUser({ username });
parentId = (await userApi.nodes.createFolder(parent)).entry.id;
await userApi.nodes.createFolder(duplicateFolderName, parentId);
await userApi.sites.createSite(siteName);
docLibUserSite = await userApi.sites.getDocLibId(siteName);
await userApi.nodes.createFolder(duplicateFolderSite, docLibUserSite);
await adminApiActions.createSpaceTemplatesHierarchy(templates);
await adminApiActions.removeUserAccessOnSpaceTemplate(restrictedTemplateFolder);
folderLink = (await adminApiActions.createLinkToFolderName(folderInRootFolder, await adminApiActions.getSpaceTemplatesFolderId())).entry.name;
await loginPage.loginWith(username);
});
afterAll(async () => {
await userApi.nodes.deleteNodeById(parentId);
await userApi.sites.deleteSite(siteName);
await adminApiActions.cleanupSpaceTemplatesFolder();
});
beforeEach(async () => {
await page.closeOpenDialogs();
});
describe('Select Template dialog', () => {
beforeEach(async () => {
await sidenav.openCreateFolderFromTemplateDialog();
await selectTemplateDialog.waitForDialogToOpen();
});
it('Select template - dialog UI - with existing templates - [C325147]', async () => {
expect(await selectTemplateDialog.getTitle()).toEqual('Select a folder template');
expect(await selectTemplateDialog.dataTable.isEmpty()).toBe(false, 'Datatable is empty');
expect(await selectTemplateDialog.dataTable.isItemPresent(templateFolder1)).toBe(true, 'template folder not displayed');
expect(await selectTemplateDialog.dataTable.isItemPresent(templateFolder2)).toBe(true, 'template folder not displayed');
expect(await selectTemplateDialog.dataTable.isItemPresent(fileInRootFolder)).toBe(true, 'file not displayed');
expect(await selectTemplateDialog.breadcrumb.getCurrentFolderName()).toEqual('Space Templates');
expect(await selectTemplateDialog.isNextButtonEnabled()).toBe(false, 'Next button is not disabled');
expect(await selectTemplateDialog.isCancelButtonEnabled()).toBe(true, 'Cancel button is not enabled');
});
it(`Templates don't appear if user doesn't have permissions to see them - [C325148]`, async () => {
expect(await selectTemplateDialog.dataTable.isItemPresent(restrictedTemplateFolder)).toBe(false, 'restricted template folder is displayed');
});
it('Navigate through the templates list with folder hierarchy - [C325149]', async () => {
expect(await selectTemplateDialog.dataTable.isItemPresent(templateFolder2)).toBe(true, 'template folder not displayed');
await selectTemplateDialog.dataTable.doubleClickOnRowByName(templateFolder2);
expect(await selectTemplateDialog.dataTable.isItemPresent(templateSubFolder)).toBe(true, 'template sub-folder not displayed');
expect(await selectTemplateDialog.dataTable.isItemPresent(fileInFolder2)).toBe(true, 'template not displayed');
expect(await selectTemplateDialog.dataTable.isItemPresent(templateFolder1)).toBe(false, 'template folder is displayed');
expect(await selectTemplateDialog.breadcrumb.getCurrentFolderName()).toEqual(templateFolder2);
await selectTemplateDialog.dataTable.doubleClickOnRowByName(templateSubFolder);
expect(await selectTemplateDialog.breadcrumb.getCurrentFolderName()).toEqual(templateSubFolder);
expect(await selectTemplateDialog.dataTable.isEmpty()).toBe(true, 'datatable is not empty');
await selectTemplateDialog.breadcrumb.openPath();
expect(await selectTemplateDialog.breadcrumb.getPathItems()).toEqual([ templateFolder2, 'Space Templates' ]);
});
it(`Templates list doesn't allow multiple selection - [C325150]`, async () => {
expect(await selectTemplateDialog.dataTable.getSelectedRowsCount()).toEqual(0, 'Incorrect number of selected rows');
await selectTemplateDialog.dataTable.selectItem(templateFolder1);
expect(await selectTemplateDialog.dataTable.getSelectedRowsCount()).toEqual(1, 'Incorrect number of selected rows');
expect(await selectTemplateDialog.dataTable.getSelectedRowsNames()).toEqual([ templateFolder1 ], 'Incorrect selected item');
await Utils.pressCmd();
await selectTemplateDialog.dataTable.selectItem(templateFolder2);
await Utils.releaseKeyPressed();
expect(await selectTemplateDialog.dataTable.getSelectedRowsCount()).toEqual(1, 'Incorrect number of selected rows');
expect(await selectTemplateDialog.dataTable.getSelectedRowsNames()).toEqual([ templateFolder2 ], 'Incorrect selected item');
});
it('Links to folders are not displayed - [C325153]', async () => {
expect(await selectTemplateDialog.dataTable.isItemPresent(folderLink)).toBe(false, 'Link to folder is displayed');
});
it('Cancel the Select template dialog - [C325151]', 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('Next button is disabled when selecting a file - [C325139]', async () => {
expect(await selectTemplateDialog.isNextButtonEnabled()).toBe(false, 'Next button is enabled');
await selectTemplateDialog.dataTable.selectItem(fileInRootFolder);
expect(await selectTemplateDialog.isNextButtonEnabled()).toBe(false, 'Next button is enabled');
});
});
describe('Create from template dialog', () => {
beforeEach(async () => {
await sidenav.openCreateFolderFromTemplateDialog();
await selectTemplateDialog.waitForDialogToOpen();
await selectTemplateDialog.dataTable.selectItem(templateFolder1);
await selectTemplateDialog.clickNext();
await createFromTemplateDialog.waitForDialogToOpen();
});
it('Create folder from template - dialog UI - [C325142]', async () => {
expect(await createFromTemplateDialog.getTitle()).toEqual(`Create new folder from '${templateFolder1}'`);
expect(await createFromTemplateDialog.isNameFieldDisplayed()).toBe(true, 'Name field not displayed');
expect(await createFromTemplateDialog.isTitleFieldDisplayed()).toBe(true, 'Title field not displayed');
expect(await createFromTemplateDialog.isDescriptionFieldDisplayed()).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('Folder name is required - [C325143]', async () => {
expect(await createFromTemplateDialog.getName()).toEqual(templateFolder1);
await createFromTemplateDialog.deleteNameWithBackspace();
expect(await createFromTemplateDialog.getValidationMessage()).toEqual('Name is required');
expect(await createFromTemplateDialog.isCreateButtonEnabled()).toBe(false, 'Create button is not disabled');
});
it('Special characters in folder name - [C325144]', async () => {
const namesWithSpecialChars = [ 'a*a', 'a"a', 'a<a', 'a>a', `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('Folder name ending with a dot - [C325145]', async () => {
await createFromTemplateDialog.enterName('folder-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('Folder name containing only spaces - [C325146]', 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('Title too long - [C325141]', 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('Description too long - [C325140]', 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 () => {
await page.clickPersonalFilesAndWait();
await page.dataTable.doubleClickOnRowByName(parent);
await sidenav.openCreateFolderFromTemplateDialog();
await selectTemplateDialog.waitForDialogToOpen();
await selectTemplateDialog.dataTable.selectItem(templateFolder1);
await selectTemplateDialog.clickNext();
await createFromTemplateDialog.waitForDialogToOpen();
});
it('Create a folder from a template - with a new Name - [C325157]', async () => {
await createFromTemplateDialog.enterName(folder1.name);
await createFromTemplateDialog.clickCreate();
await createFromTemplateDialog.waitForDialogToClose();
await page.dataTable.waitForHeader();
expect(await page.dataTable.isItemPresent(folder1.name)).toBe(true, 'Folder not displayed in list view');
});
it('Create a folder from a template - with a Name, Title and Description - [C325154]', async () => {
await createFromTemplateDialog.enterName(folder2.name);
await createFromTemplateDialog.enterTitle(folder2.title);
await createFromTemplateDialog.enterDescription(folder2.description);
await createFromTemplateDialog.clickCreate();
await createFromTemplateDialog.waitForDialogToClose();
await page.dataTable.waitForHeader();
expect(await page.dataTable.isItemPresent(folder2.name)).toBe(true, 'Folder not displayed in list view');
const desc = await userApi.nodes.getNodeDescription(folder2.name, parentId);
expect(desc).toEqual(folder2.description);
const title = await userApi.nodes.getNodeTitle(folder2.name, parentId);
expect(title).toEqual(folder2.title);
});
it('Create a folder with a duplicate name - [C325156]', async () => {
await createFromTemplateDialog.enterName(duplicateFolderName);
await createFromTemplateDialog.clickCreate();
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('Cancel folder creation - [C325155]', 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, 'Folder should not appear in the list');
});
it('Trim spaces from folder Name - [C325158]', async () => {
await createFromTemplateDialog.enterName(nameWithSpaces);
await createFromTemplateDialog.clickCreate();
await createFromTemplateDialog.waitForDialogToClose();
await page.dataTable.waitForHeader();
expect(await page.dataTable.isItemPresent(nameWithSpaces.trim())).toBe(true, 'Folder not displayed in list view');
});
});
describe('On File Libraries', () => {
const fileLibrariesPage = new BrowsingPage();
beforeEach(async () => {
await fileLibrariesPage.goToMyLibrariesAndWait();
await page.dataTable.doubleClickOnRowByName(siteName);
await sidenav.openCreateFolderFromTemplateDialog();
await selectTemplateDialog.waitForDialogToOpen();
await selectTemplateDialog.dataTable.selectItem(templateFolder1);
await selectTemplateDialog.clickNext();
await createFromTemplateDialog.waitForDialogToOpen();
});
it('Create a folder from a template - with Name, Title and Description - [C325161]', async () => {
await createFromTemplateDialog.enterName(folderSite.name);
await createFromTemplateDialog.enterTitle(folderSite.title);
await createFromTemplateDialog.enterDescription(folderSite.description);
await createFromTemplateDialog.clickCreate();
await createFromTemplateDialog.waitForDialogToClose();
await page.dataTable.waitForHeader();
expect(await page.dataTable.isItemPresent(folderSite.name)).toBe(true, 'Folder not displayed in list view');
const desc = await userApi.nodes.getNodeDescription(folderSite.name, docLibUserSite);
expect(desc).toEqual(folderSite.description);
const title = await userApi.nodes.getNodeTitle(folderSite.name, docLibUserSite);
expect(title).toEqual(folderSite.title);
});
it('Cancel folder creation - [C325162]', 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, 'Folder should not appear in the list');
});
it('Create a folder with a duplicate name - [C325163]', async () => {
await createFromTemplateDialog.enterName(duplicateFolderSite);
await createFromTemplateDialog.clickCreate();
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');
});
});
});

View File

@ -77,6 +77,7 @@ describe('New menu', () => {
expect(await menu.isCreateLibraryEnabled()).toBe(true, 'Create Library option not enabled');
expect(await menu.isCreateFileFromTemplateEnabled()).toBe(true, 'Create file from template is not enabled');
expect(await menu.isCreateFolderFromTemplateEnabled()).toBe(true, 'Create folder from template is not enabled');
});
it('Actions in File Libraries - user with enough permissions - [C280393]', async () => {
@ -91,6 +92,7 @@ describe('New menu', () => {
expect(await menu.isCreateLibraryEnabled()).toBe(true, 'Create Library option not enabled');
expect(await menu.isCreateFileFromTemplateEnabled()).toBe(true, 'Create file from template is not enabled');
expect(await menu.isCreateFolderFromTemplateEnabled()).toBe(true, 'Create folder from template is not enabled');
});
it('Actions in File Libraries - user without enough permissions - [C280397]', async () => {
@ -105,6 +107,7 @@ describe('New menu', () => {
expect(await menu.isCreateLibraryEnabled()).toBe(true, 'Create Library option not enabled');
expect(await menu.isCreateFileFromTemplateEnabled()).toBe(false, 'Create file from template is not disabled');
expect(await menu.isCreateFolderFromTemplateEnabled()).toBe(false, 'Create folder from template is not disabled');
});
it('Enabled actions tooltips - [C216342]', async () => {

View File

@ -63,5 +63,7 @@ describe('Upload files', () => {
await dataTable.doubleClickOnRowByName(folder1);
await page.sidenav.openNewMenu();
await page.sidenav.menu.uploadFile().sendKeys(`${__dirname}/create-folder.test.ts`);
expect(await dataTable.isItemPresent('create-folder.test.ts')).toBe(true, 'file not uploaded');
});
});

View File

@ -54,6 +54,10 @@ export class AdminActions {
return await this.adminApi.nodes.getNodeIdFromParent('Node Templates', await this.getDataDictionaryId());
}
async getSpaceTemplatesFolderId(): Promise<string> {
return await this.adminApi.nodes.getNodeIdFromParent('Space Templates', await this.getDataDictionaryId());
}
async createUser(user: PersonModel): Promise<PersonEntry> {
return await this.adminApi.people.createUser(user);
}
@ -68,19 +72,47 @@ export class AdminActions {
return await this.adminApi.nodes.createContent(hierarchy, `Data Dictionary/Node Templates`);
}
async removeUserAccessOnNode(nodeName: string): Promise<NodeEntry> {
async createSpaceTemplate(name: string, title: string = '', description: string = ''): Promise<NodeEntry> {
const templatesRootFolderId: string = await this.getSpaceTemplatesFolderId();
return await this.adminApi.nodes.createFolder(name, templatesRootFolderId, title, description);
}
async createSpaceTemplatesHierarchy(hierarchy: NodeContentTree): Promise<any> {
return await this.adminApi.nodes.createContent(hierarchy, `Data Dictionary/Space Templates`);
}
async removeUserAccessOnNodeTemplate(nodeName: string): Promise<NodeEntry> {
const templatesRootFolderId = await this.getNodeTemplatesFolderId();
const nodeId: string = await this.adminApi.nodes.getNodeIdFromParent(nodeName, templatesRootFolderId);
return await this.adminApi.nodes.setInheritPermissions(nodeId, false);
}
async cleanNodeTemplatesFolder(): Promise<void> {
async removeUserAccessOnSpaceTemplate(nodeName: string): Promise<NodeEntry> {
const templatesRootFolderId = await this.getSpaceTemplatesFolderId();
const nodeId: string = await this.adminApi.nodes.getNodeIdFromParent(nodeName, templatesRootFolderId);
return await this.adminApi.nodes.setInheritPermissions(nodeId, false);
}
async cleanupNodeTemplatesFolder(): Promise<void> {
return await this.adminApi.nodes.deleteNodeChildren(await this.getNodeTemplatesFolderId());
}
async cleanupSpaceTemplatesFolder(): Promise<void> {
const spaceTemplatesNodeId = await this.getSpaceTemplatesFolderId();
// folder links are deleted automatically when original folder is deleted
// Software Engineering Project is the default folder template coming from ACS, should not be deleted
const nodesToDelete = (await this.adminApi.nodes.getNodeChildren(spaceTemplatesNodeId)).list.entries
.filter(node => (node.entry.nodeType !== 'app:folderlink') && (node.entry.name !== 'Software Engineering Project'))
.map(node => node.entry.id);
return await this.adminApi.nodes.deleteNodesById(nodesToDelete);
}
async createLinkToFileId(originalFileId: string, destinationParentId: string): Promise<NodeEntry> {
return await this.adminApi.nodes.createNodeLink(originalFileId, destinationParentId);
return await this.adminApi.nodes.createFileLink(originalFileId, destinationParentId);
}
async createLinkToFileName(originalFileName: string, originalFileParentId: string, destinationParentId?: string): Promise<NodeEntry> {
@ -93,4 +125,18 @@ export class AdminActions {
return await this.createLinkToFileId(nodeId, destinationParentId);
}
async createLinkToFolderId(originalFolderId: string, destinationParentId: string): Promise<NodeEntry> {
return await this.adminApi.nodes.createFolderLink(originalFolderId, destinationParentId);
}
async createLinkToFolderName(originalFolderName: string, originalFolderParentId: string, destinationParentId?: string): Promise<NodeEntry> {
if (!destinationParentId) {
destinationParentId = originalFolderParentId
};
const nodeId = await this.adminApi.nodes.getNodeIdFromParent(originalFolderName, originalFolderParentId);
return await this.createLinkToFolderId(nodeId, destinationParentId);
}
}

View File

@ -179,11 +179,9 @@ export class NodesApi extends RepoApi {
async deleteNodesById(ids: string[], permanent: boolean = true): Promise<void> {
try {
await ids.reduce(async (previous, current) => {
await previous;
const req = await this.deleteNodeById(current, permanent);
return req;
}, Promise.resolve());
for (const id of ids) {
await this.deleteNodeById(id, permanent);
}
} catch (error) {
this.handleError(`${this.constructor.name} ${this.deleteNodesById.name}`, error);
}
@ -202,10 +200,17 @@ export class NodesApi extends RepoApi {
}
}
async deleteNodeChildren(parentId: string): Promise<void> {
async deleteNodeChildren(parentId: string, exceptNodesNamed?: string[]): Promise<void> {
try {
const listEntries = (await this.getNodeChildren(parentId)).list.entries;
const nodeIds = listEntries.map(entries => entries.entry.id);
let nodeIds: string[];
if (exceptNodesNamed) {
nodeIds = listEntries
.filter(entries => !exceptNodesNamed.includes(entries.entry.name))
.map(entries => entries.entry.id);
} else {
nodeIds = listEntries.map(entries => entries.entry.id);
}
await this.deleteNodesById(nodeIds);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.deleteNodeChildren.name}`, error);
@ -225,26 +230,7 @@ export class NodesApi extends RepoApi {
}
}
async createNodeLink(originalNodeId: string, destinationId: string): Promise<NodeEntry|null> {
const name = (await this.getNodeById(originalNodeId)).entry.name;
const nodeBody = {
name: `Link to ${name}.url`,
nodeType: 'app:filelink',
properties: {
'cm:destination': originalNodeId
}
}
try {
await this.apiAuth();
return await this.nodesApi.createNode(destinationId, nodeBody);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.createNode.name}`, error);
return null;
}
}
async createNode(nodeType: string, name: string, parentId: string = '-my-', title: string = '', description: string = '', imageProps: any = null, author: string = '', majorVersion: boolean = true): Promise<NodeEntry|null> {
async createNode(nodeType: string, name: string, parentId: string = '-my-', title: string = '', description: string = '', imageProps: any = null, author: string = '', majorVersion: boolean = true, aspectNames: string[] = null): Promise<NodeEntry|null> {
const nodeBody = {
name,
nodeType,
@ -253,7 +239,7 @@ export class NodesApi extends RepoApi {
'cm:description': description,
'cm:author': author
},
aspectNames: ['cm:versionable'] // workaround for REPO-4772
aspectNames
};
if (imageProps) {
nodeBody.properties = Object.assign(nodeBody.properties, imageProps);
@ -268,9 +254,12 @@ export class NodesApi extends RepoApi {
}
}
async createFile(name: string, parentId: string = '-my-', title: string = '', description: string = '', author: string = '', majorVersion: boolean = true): Promise<NodeEntry> {
async createFile(name: string, parentId: string = '-my-', title: string = '', description: string = '', author: string = '', majorVersion: boolean = true, aspectNames: string[] = null): Promise<NodeEntry> {
if (!aspectNames) {
aspectNames = ['cm:versionable'] // workaround for REPO-4772
}
try {
return await this.createNode('cm:content', name, parentId, title, description, null, author, majorVersion);
return await this.createNode('cm:content', name, parentId, title, description, null, author, majorVersion, aspectNames);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.createFile.name}`, error);
return null;
@ -286,9 +275,9 @@ export class NodesApi extends RepoApi {
}
}
async createFolder(name: string, parentId: string = '-my-', title: string = '', description: string = '', author: string = ''): Promise<NodeEntry|null> {
async createFolder(name: string, parentId: string = '-my-', title: string = '', description: string = '', author: string = '', aspectNames: string[] = null): Promise<NodeEntry|null> {
try {
return await this.createNode('cm:folder', name, parentId, title, description, null, author);
return await this.createNode('cm:folder', name, parentId, title, description, null, author, null, aspectNames);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.createFolder.name}`, error);
return null;
@ -328,6 +317,61 @@ export class NodesApi extends RepoApi {
}
}
async addAspects(nodeId: string, aspectNames: string[]): Promise<NodeEntry> {
try {
await this.apiAuth();
return this.nodesApi.updateNode(nodeId, { aspectNames });
} catch (error) {
this.handleError(`${this.constructor.name} ${this.addAspects.name}`, error);
return null;
}
}
async createFileLink(originalNodeId: string, destinationId: string): Promise<NodeEntry|null> {
const name = (await this.getNodeById(originalNodeId)).entry.name;
const nodeBody = {
name: `Link to ${name}.url`,
nodeType: 'app:filelink',
properties: {
'cm:destination': originalNodeId
}
}
try {
await this.apiAuth();
const link = await this.nodesApi.createNode(destinationId, nodeBody);
await this.addAspects(originalNodeId, ['app:linked']);
return link;
} catch (error) {
this.handleError(`${this.constructor.name} ${this.createFileLink.name}`, error);
return null;
}
}
async createFolderLink(originalNodeId: string, destinationId: string): Promise<NodeEntry|null> {
const name = (await this.getNodeById(originalNodeId)).entry.name;
const nodeBody = {
name: `Link to ${name}.url`,
nodeType: 'app:folderlink',
properties: {
'cm:title': `Link to ${name}.url`,
'cm:destination': originalNodeId,
'cm:description': `Link to ${name}.url`,
'app:icon': 'space-icon-link'
}
}
try {
await this.apiAuth();
const link = await this.nodesApi.createNode(destinationId, nodeBody);
await this.addAspects(originalNodeId, ['app:linked']);
return link;
} catch (error) {
this.handleError(`${this.constructor.name} ${this.createFolderLink.name}`, error);
return null;
}
}
// node content
async getNodeContent(nodeId: string): Promise<any> {
try {

View File

@ -69,6 +69,7 @@ exports.config = {
addRemoveContent: [
'./e2e/suites/actions/new-menu.test.ts',
'./e2e/suites/actions/create-folder.test.ts',
'./e2e/suites/actions/create-folder-from-template.test.ts',
'./e2e/suites/actions/create-library.test.ts',
'./e2e/suites/actions/create-file-from-template.test.ts',
'./e2e/suites/actions/upload-file.test.ts',

View File

@ -150,7 +150,7 @@ describe('NodeTemplateService', () => {
).toBe(true);
});
it('should return false if row is a `link` nodeType', () => {
it('should return false if row is a `filelink` nodeType', () => {
spyOn(
alfrescoApiService.getInstance().nodes,
'getNodeInfo'
@ -174,6 +174,30 @@ describe('NodeTemplateService', () => {
).toBe(false);
});
it('should return false if row is a `folderlink` nodeType', () => {
spyOn(
alfrescoApiService.getInstance().nodes,
'getNodeInfo'
).and.returnValue(
of({
id: 'templates-folder-id',
path: {
elements: [],
name: '/Company Home/Data Dictionary'
}
})
);
spyOn(dialog, 'open');
nodeTemplateService.selectTemplateDialog(fileTemplateConfig);
expect(
dialog.open['calls'].argsFor(0)[1].data.rowFilter({
node: { entry: { nodeType: 'app:folderlink' } }
})
).toBe(false);
});
describe('File templates', () => {
it('should return false if selected node is not a file', () => {
spyOn(