diff --git a/e2e/components/data-table/data-table.ts b/e2e/components/data-table/data-table.ts index 59df39a48..951ce2aa5 100644 --- a/e2e/components/data-table/data-table.ts +++ b/e2e/components/data-table/data-table.ts @@ -34,6 +34,7 @@ export class DataTable extends Component { row: 'tr', selectedRow: 'tr.is-selected', cell: 'td', + nameCell: 'td.app-name-column', emptyListContainer: 'td.adf-no-content-container', emptyFolderDragAndDrop: '.adf-empty-list_template .adf-empty-folder', @@ -113,18 +114,16 @@ export class DataTable extends Component { } // Navigation/selection methods - doubleClickOnRowByContainingText(text: string): promise.Promise { - const row = this.getRowByContainingText(text); - const dblClick = browser.actions().mouseMove(row).click().click(); + doubleClickOnItemName(name: string): promise.Promise { + const locator = by.cssContainingText(DataTable.selectors.nameCell, name); + const dblClick = browser.actions().mouseMove(this.body.element(locator)).click().click(); return dblClick.perform(); } - clickOnRowByContainingText(text: string): promise.Promise { - const row = this.getRowByContainingText(text); - const click = browser.actions().mouseMove(row).click(); - - return click.perform(); + clickOnItemName(name: string): promise.Promise { + const locator = by.cssContainingText(DataTable.selectors.nameCell, name); + return this.body.element(locator).click(); } selectMultipleItems(names: string[]): promise.Promise { diff --git a/e2e/components/menu/menu.ts b/e2e/components/menu/menu.ts index 536e4b05e..d5d9a3bed 100644 --- a/e2e/components/menu/menu.ts +++ b/e2e/components/menu/menu.ts @@ -47,7 +47,11 @@ export class Menu extends Component { return this.getItemByLabel(label).getAttribute('title'); } - clicktNthItem(nth: number): promise.Promise { + getItemsCount(): promise.Promise { + return this.items.count(); + } + + clickNthItem(nth: number): promise.Promise { return this.getNthItem(nth).click(); } diff --git a/e2e/pages/page.ts b/e2e/pages/page.ts index 0867bfa45..7b30f62b8 100644 --- a/e2e/pages/page.ts +++ b/e2e/pages/page.ts @@ -19,7 +19,7 @@ import { browser, element, by, ElementFinder, promise, ExpectedConditions as EC import { BROWSER_WAIT_TIMEOUT } from './../configs'; export abstract class Page { - private static USE_HASH_STRATEGY = false; + private static USE_HASH_STRATEGY = true; private locators = { app: by.css('app-root'), diff --git a/e2e/suites/actions/create-folder.test.ts b/e2e/suites/actions/create-folder.test.ts index f5639049e..1e3dd6c34 100644 --- a/e2e/suites/actions/create-folder.test.ts +++ b/e2e/suites/actions/create-folder.test.ts @@ -87,7 +87,7 @@ describe('Create folder', () => { }); it('option is enabled when having enough permissions', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) + personalFilesPage.dataTable.doubleClickOnItemName(parent) .then(() => personalFilesPage.sidenav.openNewMenu() .then((menu) => { const isEnabled = menu.getItemByLabel('Create folder').getWebElement().isEnabled(); @@ -98,7 +98,7 @@ describe('Create folder', () => { }); it('creates new folder with name', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) + personalFilesPage.dataTable.doubleClickOnItemName(parent) .then(() => openCreateDialog() .then(() => createDialog.enterName(folderName1).clickCreate()) .then(() => createDialog.waitForDialogToClose()) @@ -111,7 +111,7 @@ describe('Create folder', () => { }); it('creates new folder with name and description', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) + personalFilesPage.dataTable.doubleClickOnItemName(parent) .then(() => openCreateDialog() .then(() => { createDialog @@ -133,7 +133,7 @@ describe('Create folder', () => { }); it('enabled option tooltip', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) + personalFilesPage.dataTable.doubleClickOnItemName(parent) .then(() => personalFilesPage.sidenav.openNewMenu() .then(menu => { const action = browser.actions().mouseMove(menu.getItemByLabel('Create folder')); @@ -152,8 +152,8 @@ describe('Create folder', () => { const fileLibrariesPage = new BrowsingPage(APP_ROUTES.FILE_LIBRARIES); fileLibrariesPage.sidenav.navigateToLinkByLabel('File Libraries') - .then(() => fileLibrariesPage.dataTable.doubleClickOnRowByContainingText(siteName)) - .then(() => fileLibrariesPage.dataTable.doubleClickOnRowByContainingText(folderName1)) + .then(() => fileLibrariesPage.dataTable.doubleClickOnItemName(siteName)) + .then(() => fileLibrariesPage.dataTable.doubleClickOnItemName(folderName1)) .then(() => fileLibrariesPage.sidenav.openNewMenu()) .then(menu => { const isEnabled = menu.getItemByLabel('Create folder').getWebElement().isEnabled(); @@ -165,8 +165,8 @@ describe('Create folder', () => { const fileLibrariesPage = new BrowsingPage(APP_ROUTES.FILE_LIBRARIES); fileLibrariesPage.sidenav.navigateToLinkByLabel('File Libraries') - .then(() => fileLibrariesPage.dataTable.doubleClickOnRowByContainingText(siteName)) - .then(() => fileLibrariesPage.dataTable.doubleClickOnRowByContainingText(folderName1)) + .then(() => fileLibrariesPage.dataTable.doubleClickOnItemName(siteName)) + .then(() => fileLibrariesPage.dataTable.doubleClickOnItemName(folderName1)) .then(() => fileLibrariesPage.sidenav.openNewMenu()) .then(menu => { const action = browser.actions().mouseMove(menu.getItemByLabel('Create folder')); @@ -179,7 +179,7 @@ describe('Create folder', () => { }); it('dialog UI elements', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) + personalFilesPage.dataTable.doubleClickOnItemName(parent) .then(() => openCreateDialog().then(() => { const dialogTitle = createDialog.getTitle(); const isFolderNameDisplayed = createDialog.nameInput.getWebElement().isDisplayed(); @@ -197,7 +197,7 @@ describe('Create folder', () => { }); it('with empty folder name', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) + personalFilesPage.dataTable.doubleClickOnItemName(parent) .then(() => openCreateDialog() .then(() => { createDialog.deleteNameWithBackspace(); @@ -213,7 +213,7 @@ describe('Create folder', () => { }); it('with folder name ending with a dot "."', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) + personalFilesPage.dataTable.doubleClickOnItemName(parent) .then(() => openCreateDialog() .then(() => createDialog.enterName('folder-name.')) .then((dialog) => { @@ -229,7 +229,7 @@ describe('Create folder', () => { it('with folder name containing special characters', () => { const namesWithSpecialChars = [ 'a*a', 'a"a', 'aa', `a\\a`, 'a/a', 'a?a', 'a:a', 'a|a' ]; - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) + personalFilesPage.dataTable.doubleClickOnItemName(parent) .then(() => openCreateDialog() .then(() => { namesWithSpecialChars.forEach(name => { @@ -246,7 +246,7 @@ describe('Create folder', () => { }); it('with folder name containing only spaces', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) + personalFilesPage.dataTable.doubleClickOnItemName(parent) .then(() => openCreateDialog() .then(() => createDialog.enterName(' ')) .then((dialog) => { @@ -260,7 +260,7 @@ describe('Create folder', () => { }); it('cancel folder creation', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) + personalFilesPage.dataTable.doubleClickOnItemName(parent) .then(() => openCreateDialog() .then(() => { createDialog @@ -273,7 +273,7 @@ describe('Create folder', () => { }); it('duplicate folder name', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) + personalFilesPage.dataTable.doubleClickOnItemName(parent) .then(() => openCreateDialog() .then(() => createDialog.enterName(duplicateFolderName).clickCreate()) .then(() => { @@ -287,7 +287,7 @@ describe('Create folder', () => { }); it('trim ending spaces from folder name', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) + personalFilesPage.dataTable.doubleClickOnItemName(parent) .then(() => openCreateDialog() .then(() => createDialog.enterName(nameWithSpaces).clickCreate()) .then(() => createDialog.waitForDialogToClose()) diff --git a/e2e/suites/actions/edit-folder.test.ts b/e2e/suites/actions/edit-folder.test.ts index 2e58b4d07..fb830f5c1 100644 --- a/e2e/suites/actions/edit-folder.test.ts +++ b/e2e/suites/actions/edit-folder.test.ts @@ -87,8 +87,8 @@ describe('Edit folder', () => { }); it('dialog UI defaults', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) - .then(() => dataTable.clickOnRowByContainingText(folderName) + personalFilesPage.dataTable.doubleClickOnItemName(parent) + .then(() => dataTable.clickOnItemName(folderName) .then(() => editButton.click()) .then(() => { expect(editDialog.getTitle()).toBe('Edit folder'); @@ -101,8 +101,8 @@ describe('Edit folder', () => { }); it('properties are modified when pressing OK', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) - .then(() => dataTable.clickOnRowByContainingText(folderNameToEdit) + personalFilesPage.dataTable.doubleClickOnItemName(parent) + .then(() => dataTable.clickOnItemName(folderNameToEdit) .then(() => editButton.click()) .then(() => { editDialog @@ -126,8 +126,8 @@ describe('Edit folder', () => { }); it('with empty folder name', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) - .then(() => dataTable.clickOnRowByContainingText(folderName) + personalFilesPage.dataTable.doubleClickOnItemName(parent) + .then(() => dataTable.clickOnItemName(folderName) .then(() => editButton.click()) .then(() => { editDialog.deleteNameWithBackspace(); @@ -142,8 +142,8 @@ describe('Edit folder', () => { it('with name with special characters', () => { const namesWithSpecialChars = [ 'a*a', 'a"a', 'aa', `a\\a`, 'a/a', 'a?a', 'a:a', 'a|a' ]; - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) - .then(() => dataTable.clickOnRowByContainingText(folderName) + personalFilesPage.dataTable.doubleClickOnItemName(parent) + .then(() => dataTable.clickOnItemName(folderName) .then(() => editButton.click()) .then(() => { namesWithSpecialChars.forEach(name => { @@ -157,8 +157,8 @@ describe('Edit folder', () => { }); it('with name ending with a dot', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) - .then(() => dataTable.clickOnRowByContainingText(folderName) + personalFilesPage.dataTable.doubleClickOnItemName(parent) + .then(() => dataTable.clickOnItemName(folderName) .then(() => editButton.click()) .then(() => editDialog.nameInput.sendKeys('.')) .then(() => { @@ -169,8 +169,8 @@ describe('Edit folder', () => { }); it('Cancel button', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) - .then(() => dataTable.clickOnRowByContainingText(folderName) + personalFilesPage.dataTable.doubleClickOnItemName(parent) + .then(() => dataTable.clickOnItemName(folderName) .then(() => editButton.click()) .then(() => editDialog.clickCancel()) .then(() => { @@ -180,8 +180,8 @@ describe('Edit folder', () => { }); it('with duplicate folder name', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) - .then(() => dataTable.clickOnRowByContainingText(folderName) + personalFilesPage.dataTable.doubleClickOnItemName(parent) + .then(() => dataTable.clickOnItemName(folderName) .then(() => editButton.click()) .then(() => editDialog.enterName(duplicateFolderName).clickUpdate()) .then(() => { @@ -195,8 +195,8 @@ describe('Edit folder', () => { }); it('trim ending spaces', () => { - personalFilesPage.dataTable.doubleClickOnRowByContainingText(parent) - .then(() => dataTable.clickOnRowByContainingText(folderName) + personalFilesPage.dataTable.doubleClickOnItemName(parent) + .then(() => dataTable.clickOnItemName(folderName) .then(() => editButton.click()) .then(() => editDialog.nameInput.sendKeys(' ')) .then(() => editDialog.clickUpdate()) diff --git a/e2e/suites/actions/permanently-delete.test.ts b/e2e/suites/actions/permanently-delete.test.ts index e169c3840..cde2d9bec 100644 --- a/e2e/suites/actions/permanently-delete.test.ts +++ b/e2e/suites/actions/permanently-delete.test.ts @@ -25,14 +25,12 @@ describe('Permanently delete from Trash', () => { const username = `user-${Utils.random()}`; const file1 = `file-${Utils.random()}.txt`; - let file1Id; const file2 = `file-${Utils.random()}.txt`; - let file2Id; + let filesIds; const folder1 = `folder-${Utils.random()}`; - let folder1Id; const folder2 = `folder-${Utils.random()}`; - let folder2Id; + let foldersIds; const apis = { admin: new RepoClient(), @@ -47,15 +45,13 @@ describe('Permanently delete from Trash', () => { beforeAll(done => { apis.admin.people.createUser(username) - .then(() => apis.user.nodes.createFiles([ file1 ]).then(resp => file1Id = resp.data.entry.id)) - .then(() => apis.user.nodes.createFiles([ file2 ]).then(resp => file2Id = resp.data.entry.id)) - .then(() => apis.user.nodes.createFolders([ folder1 ]).then(resp => folder1Id = resp.data.entry.id)) - .then(() => apis.user.nodes.createFolders([ folder2 ]).then(resp => folder2Id = resp.data.entry.id)) + .then(() => apis.user.nodes.createFiles([ file1, file2 ])) + .then(resp => filesIds = resp.data.list.entries.map(entries => entries.entry.id)) + .then(() => apis.user.nodes.createFolders([ folder1, folder2 ])) + .then(resp => foldersIds = resp.data.list.entries.map(entries => entries.entry.id)) - .then(() => apis.user.nodes.deleteNodeById(file1Id, false)) - .then(() => apis.user.nodes.deleteNodeById(file2Id, false)) - .then(() => apis.user.nodes.deleteNodeById(folder1Id, false)) - .then(() => apis.user.nodes.deleteNodeById(folder2Id, false)) + .then(() => apis.user.nodes.deleteNodesById(filesIds, false)) + .then(() => apis.user.nodes.deleteNodesById(foldersIds, false)) .then(() => loginPage.load()) .then(() => loginPage.loginWith(username)) @@ -70,21 +66,15 @@ describe('Permanently delete from Trash', () => { afterAll(done => { Promise.all([ - apis.user.trashcan.permanentlyDelete(file1Id), - apis.user.trashcan.permanentlyDelete(file2Id), - apis.user.trashcan.permanentlyDelete(folder1Id), - apis.user.trashcan.permanentlyDelete(folder2Id), + apis.user.trashcan.emptyTrash(), logoutPage.load() ]) .then(done); }); it('delete file', () => { - dataTable.clickOnRowByContainingText(file1) - .then(() => { - const button = toolbar.actions.getButtonByTitleAttribute('Permanently delete'); - button.click(); - }) + dataTable.clickOnItemName(file1) + .then(() => toolbar.actions.getButtonByTitleAttribute('Permanently delete').click()) .then(() => trashPage.getSnackBarMessage()) .then(text => { expect(text).toBe(`${file1} deleted`); @@ -93,11 +83,8 @@ describe('Permanently delete from Trash', () => { }); it('delete folder', () => { - dataTable.clickOnRowByContainingText(folder1) - .then(() => { - const button = toolbar.actions.getButtonByTitleAttribute('Permanently delete'); - button.click(); - }) + dataTable.clickOnItemName(folder1) + .then(() => toolbar.actions.getButtonByTitleAttribute('Permanently delete').click()) .then(() => trashPage.getSnackBarMessage()) .then(text => { expect(text).toBe(`${folder1} deleted`); @@ -107,10 +94,7 @@ describe('Permanently delete from Trash', () => { it('delete multiple items', () => { dataTable.selectMultipleItems([ file2, folder2 ]) - .then(() => { - const button = toolbar.actions.getButtonByTitleAttribute('Permanently delete'); - button.click(); - }) + .then(() => toolbar.actions.getButtonByTitleAttribute('Permanently delete').click()) .then(() => trashPage.getSnackBarMessage()) .then(text => { expect(text).toBe(`2 items deleted`); diff --git a/e2e/suites/actions/restore.test.ts b/e2e/suites/actions/restore.test.ts index dccf13156..7422a9caf 100644 --- a/e2e/suites/actions/restore.test.ts +++ b/e2e/suites/actions/restore.test.ts @@ -25,16 +25,13 @@ describe('Restore from Trash', () => { const username = `user-${Utils.random()}`; const file1 = `file-${Utils.random()}.txt`; - let file1Id; const file2 = `file-${Utils.random()}.txt`; - let file2Id; const file3 = `file-${Utils.random()}.txt`; - let file3Id; + let filesIds; const folder1 = `folder-${Utils.random()}`; - let folder1Id; const folder2 = `folder-${Utils.random()}`; - let folder2Id; + let foldersIds; const apis = { admin: new RepoClient(), @@ -50,17 +47,13 @@ describe('Restore from Trash', () => { beforeAll(done => { apis.admin.people.createUser(username) - .then(() => apis.user.nodes.createFiles([ file1 ]).then(resp => file1Id = resp.data.entry.id)) - .then(() => apis.user.nodes.createFiles([ file2 ]).then(resp => file2Id = resp.data.entry.id)) - .then(() => apis.user.nodes.createFiles([ file3 ]).then(resp => file3Id = resp.data.entry.id)) - .then(() => apis.user.nodes.createFolders([ folder1 ]).then(resp => folder1Id = resp.data.entry.id)) - .then(() => apis.user.nodes.createFolders([ folder2 ]).then(resp => folder2Id = resp.data.entry.id)) + .then(() => apis.user.nodes.createFiles([ file1, file2, file3 ])) + .then(resp => filesIds = resp.data.list.entries.map(entries => entries.entry.id)) + .then(() => apis.user.nodes.createFolders([ folder1, folder2 ])) + .then(resp => foldersIds = resp.data.list.entries.map(entries => entries.entry.id)) - .then(() => apis.user.nodes.deleteNodeById(file1Id, false)) - .then(() => apis.user.nodes.deleteNodeById(file2Id, false)) - .then(() => apis.user.nodes.deleteNodeById(file3Id, false)) - .then(() => apis.user.nodes.deleteNodeById(folder1Id, false)) - .then(() => apis.user.nodes.deleteNodeById(folder2Id, false)) + .then(() => apis.user.nodes.deleteNodesById(filesIds, false)) + .then(() => apis.user.nodes.deleteNodesById(foldersIds, false)) .then(() => loginPage.load()) .then(() => loginPage.loginWith(username)) @@ -75,18 +68,16 @@ describe('Restore from Trash', () => { afterAll(done => { Promise.all([ - apis.user.nodes.deleteNodeById(file1Id), - apis.user.nodes.deleteNodeById(file2Id), - apis.user.nodes.deleteNodeById(file3Id), - apis.user.nodes.deleteNodeById(folder1Id), - apis.user.nodes.deleteNodeById(folder2Id), + apis.user.nodes.deleteNodesById(filesIds), + apis.user.nodes.deleteNodesById(foldersIds), + apis.user.trashcan.emptyTrash(), logoutPage.load() ]) .then(done); }); it('restore file', () => { - dataTable.clickOnRowByContainingText(file1) + dataTable.clickOnItemName(file1) .then(() => toolbar.actions.getButtonByTitleAttribute('Restore').click()) .then(() => trashPage.getSnackBarMessage()) .then(text => { @@ -102,7 +93,7 @@ describe('Restore from Trash', () => { }); it('restore folder', () => { - dataTable.clickOnRowByContainingText(folder1) + dataTable.clickOnItemName(folder1) .then(() => toolbar.actions.getButtonByTitleAttribute('Restore').click()) .then(() => trashPage.getSnackBarMessage()) .then(text => { @@ -136,7 +127,7 @@ describe('Restore from Trash', () => { }); it('View from notification', () => { - dataTable.clickOnRowByContainingText(file3) + dataTable.clickOnItemName(file3) .then(() => toolbar.actions.getButtonByTitleAttribute('Restore').click()) .then(() => trashPage.getSnackBarAction().click()) .then(() => personalFilesPage.dataTable.waitForHeader()) diff --git a/e2e/suites/actions/toolbar-multiple-selection.test.ts b/e2e/suites/actions/toolbar-multiple-selection.test.ts index ca81d85b1..8401919ef 100644 --- a/e2e/suites/actions/toolbar-multiple-selection.test.ts +++ b/e2e/suites/actions/toolbar-multiple-selection.test.ts @@ -189,7 +189,7 @@ describe('Toolbar actions - multiple selection : ', () => { beforeEach(done => { page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES) .then(() => dataTable.waitForHeader()) - .then(() => dataTable.doubleClickOnRowByContainingText(siteName)) + .then(() => dataTable.doubleClickOnItemName(siteName)) .then(() => dataTable.waitForHeader()) .then(done); }); diff --git a/e2e/suites/actions/toolbar-single-selection.test.ts b/e2e/suites/actions/toolbar-single-selection.test.ts index 0e7d331f9..de976bbc4 100644 --- a/e2e/suites/actions/toolbar-single-selection.test.ts +++ b/e2e/suites/actions/toolbar-single-selection.test.ts @@ -49,15 +49,18 @@ describe('Toolbar actions - single selection : ', () => { const loginPage = new LoginPage(); const logoutPage = new LogoutPage(); const page = new BrowsingPage(); - const { dataTable } = page; - const { toolbar } = page; + const { dataTable, toolbar } = page; beforeAll(done => { apis.admin.people.createUser(username) - .then(() => apis.user.nodes.createFiles([ fileUser ]).then(resp => { fileUserId = resp.data.entry.id; })) - .then(() => apis.user.nodes.createFiles([ fileForDelete ]).then(resp => { fileForDeleteId = resp.data.entry.id; })) - .then(() => apis.user.nodes.createFolders([ folderForDelete ]).then((resp) => { folderForDeleteId = resp.data.entry.id; })) - .then(() => apis.user.nodes.createFolders([ folderUser ]).then(resp => { folderUserId = resp.data.entry.id; })) + .then(() => apis.user.nodes.createFiles([ fileUser ])) + .then(resp => fileUserId = resp.data.entry.id) + .then(() => apis.user.nodes.createFiles([ fileForDelete ])) + .then(resp => fileForDeleteId = resp.data.entry.id) + .then(() => apis.user.nodes.createFolders([ folderForDelete ])) + .then(resp => folderForDeleteId = resp.data.entry.id) + .then(() => apis.user.nodes.createFolders([ folderUser ])) + .then(resp => folderUserId = resp.data.entry.id) .then(() => apis.user.shared.shareFileById(fileUserId)) .then(() => apis.user.favorites.addFavoriteById('file', fileUserId)) .then(() => apis.user.favorites.addFavoriteById('folder', folderUserId)) @@ -97,21 +100,21 @@ describe('Toolbar actions - single selection : ', () => { }); it('actions are displayed when a file is selected', () => { - dataTable.clickOnRowByContainingText(fileUser) + dataTable.clickOnItemName(fileUser) .then(() => { expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); }); }); it('actions are displayed when a folder is selected', () => { - dataTable.clickOnRowByContainingText(folderUser) + dataTable.clickOnItemName(folderUser) .then(() => { expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderUser}`); }); }); it('correct actions appear when a file is selected', () => { - dataTable.clickOnRowByContainingText(fileUser) + dataTable.clickOnItemName(fileUser) .then(() => { expect(toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); @@ -128,7 +131,7 @@ describe('Toolbar actions - single selection : ', () => { }); it('correct actions appear when a folder is selected', () => { - dataTable.clickOnRowByContainingText(folderUser) + dataTable.clickOnItemName(folderUser) .then(() => { expect(toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folderUser}`); expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderUser}`); @@ -159,7 +162,7 @@ describe('Toolbar actions - single selection : ', () => { beforeEach(done => { page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FILE_LIBRARIES) .then(() => dataTable.waitForHeader()) - .then(() => dataTable.doubleClickOnRowByContainingText(siteName)) + .then(() => dataTable.doubleClickOnItemName(siteName)) .then(() => dataTable.waitForHeader()) .then(done); }); @@ -186,21 +189,21 @@ describe('Toolbar actions - single selection : ', () => { }); it('actions are displayed when a file is selected', () => { - dataTable.clickOnRowByContainingText(fileAdmin) + dataTable.clickOnItemName(fileAdmin) .then(() => { expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileAdmin}`); }); }); it('actions are displayed when a folder is selected', () => { - dataTable.clickOnRowByContainingText(folderAdmin) + dataTable.clickOnItemName(folderAdmin) .then(() => { expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderAdmin}`); }); }); it('correct actions appear when a file is selected', () => { - dataTable.clickOnRowByContainingText(fileAdmin) + dataTable.clickOnItemName(fileAdmin) .then(() => { expect(toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileAdmin}`); expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileAdmin}`); @@ -217,7 +220,7 @@ describe('Toolbar actions - single selection : ', () => { }); it('correct actions appear when a folder is selected', () => { - dataTable.clickOnRowByContainingText(folderAdmin) + dataTable.clickOnItemName(folderAdmin) .then(() => { expect(toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folderAdmin}`); expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderAdmin}`); @@ -250,21 +253,21 @@ describe('Toolbar actions - single selection : ', () => { }); it('actions are displayed when a file is selected', () => { - dataTable.clickOnRowByContainingText(fileAdmin) + dataTable.clickOnItemName(fileAdmin) .then(() => { expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileAdmin}`); }); }); it('actions are displayed when a folder is selected', () => { - dataTable.clickOnRowByContainingText(folderAdmin) + dataTable.clickOnItemName(folderAdmin) .then(() => { expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderAdmin}`); }); }); it('correct actions appear when a file is selected', () => { - dataTable.clickOnRowByContainingText(fileAdmin) + dataTable.clickOnItemName(fileAdmin) .then(() => { expect(toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileAdmin}`); expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileAdmin}`); @@ -281,7 +284,7 @@ describe('Toolbar actions - single selection : ', () => { }); it('correct actions appear when a folder is selected', () => { - dataTable.clickOnRowByContainingText(folderAdmin) + dataTable.clickOnItemName(folderAdmin) .then(() => { expect(toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folderAdmin}`); expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderAdmin}`); @@ -321,14 +324,14 @@ describe('Toolbar actions - single selection : ', () => { }); it('actions are displayed when a file is selected', () => { - dataTable.clickOnRowByContainingText(fileUser) + dataTable.clickOnItemName(fileUser) .then(() => { expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); }); }); it('correct actions appear when a file is selected', () => { - dataTable.clickOnRowByContainingText(fileUser) + dataTable.clickOnItemName(fileUser) .then(() => { expect(toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); @@ -367,14 +370,14 @@ describe('Toolbar actions - single selection : ', () => { }); it('actions are displayed when a file is selected', () => { - dataTable.clickOnRowByContainingText(fileUser) + dataTable.clickOnItemName(fileUser) .then(() => { expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); }); }); it('correct actions appear when a file is selected', () => { - dataTable.clickOnRowByContainingText(fileUser) + dataTable.clickOnItemName(fileUser) .then(() => { expect(toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); @@ -413,21 +416,21 @@ describe('Toolbar actions - single selection : ', () => { }); it('actions are displayed when a file is selected', () => { - dataTable.clickOnRowByContainingText(fileUser) + dataTable.clickOnItemName(fileUser) .then(() => { expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileUser}`); }); }); it('actions are displayed when a folder is selected', () => { - dataTable.clickOnRowByContainingText(folderUser) + dataTable.clickOnItemName(folderUser) .then(() => { expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderUser}`); }); }); it('correct actions appear when a file is selected', () => { - dataTable.clickOnRowByContainingText(fileUser) + dataTable.clickOnItemName(fileUser) .then(() => { expect(toolbar.actions.isButtonPresent('View')).toBe(true, `View is not displayed for ${fileUser}`); expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not displayed for ${fileUser}`); @@ -444,7 +447,7 @@ describe('Toolbar actions - single selection : ', () => { }); it('correct actions appear when a folder is selected', () => { - dataTable.clickOnRowByContainingText(folderUser) + dataTable.clickOnItemName(folderUser) .then(() => { expect(toolbar.actions.isButtonPresent('View')).toBe(false, `View is displayed for ${folderUser}`); expect(toolbar.actions.isButtonPresent('Download')).toBe(true, `Download is not enabled for ${folderUser}`); @@ -490,21 +493,21 @@ describe('Toolbar actions - single selection : ', () => { }); it('actions are displayed when a file is selected', () => { - dataTable.clickOnRowByContainingText(fileForDelete) + dataTable.clickOnItemName(fileForDelete) .then(() => { expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${fileForDelete}`); }); }); it('actions are displayed when a folder is selected', () => { - dataTable.clickOnRowByContainingText(folderForDelete) + dataTable.clickOnItemName(folderForDelete) .then(() => { expect(toolbar.actions.isEmpty()).toBe(false, `actions not displayed for ${folderForDelete}`); }); }); it('correct actions appear when a file is selected', () => { - dataTable.clickOnRowByContainingText(fileForDelete) + dataTable.clickOnItemName(fileForDelete) .then(() => { expect(toolbar.actions.isButtonPresent('Permanently delete')) .toBe(true, `Permanently delete is not displayed for ${fileForDelete}`); @@ -513,7 +516,7 @@ describe('Toolbar actions - single selection : ', () => { }); it('correct actions appear when a folder is selected', () => { - dataTable.clickOnRowByContainingText(folderForDelete) + dataTable.clickOnItemName(folderForDelete) .then(() => { expect(toolbar.actions.isButtonPresent('Permanently delete')) .toBe(true, `Permanently delete is displayed for ${folderForDelete}`); diff --git a/e2e/suites/list-views/empty-list.test.ts b/e2e/suites/list-views/empty-list.test.ts index 223c8588e..d5c2d0856 100644 --- a/e2e/suites/list-views/empty-list.test.ts +++ b/e2e/suites/list-views/empty-list.test.ts @@ -15,8 +15,6 @@ * limitations under the License. */ -import { browser, by } from 'protractor'; - import { APP_ROUTES, SITE_VISIBILITY, SITE_ROLES, SIDEBAR_LABELS } from '../../configs'; import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; diff --git a/e2e/suites/list-views/personal-files.test.ts b/e2e/suites/list-views/personal-files.test.ts index d2e079c54..232250fbc 100644 --- a/e2e/suites/list-views/personal-files.test.ts +++ b/e2e/suites/list-views/personal-files.test.ts @@ -134,7 +134,7 @@ describe('Personal Files', () => { .then(response => response.data.entry.id); const navigatePromise = dataTable - .doubleClickOnRowByContainingText(userFolder) + .doubleClickOnItemName(userFolder) .then(() => dataTable.waitForHeader()); Promise @@ -158,7 +158,7 @@ describe('Personal Files', () => { const { actions } = personalFilesPage.toolbar; dataTable - .clickOnRowByContainingText(userFolder) + .clickOnItemName(userFolder) .then(() => { expect(actions.isEmpty()).toBe(false, 'Toolbar to be present'); }) diff --git a/e2e/suites/pagination/pagination.test.ts b/e2e/suites/pagination/pagination.test.ts index e510e3973..cbeae1cce 100644 --- a/e2e/suites/pagination/pagination.test.ts +++ b/e2e/suites/pagination/pagination.test.ts @@ -15,9 +15,8 @@ * limitations under the License. */ -import { browser } from 'protractor'; - -import { APP_ROUTES } from '../../configs'; +import { browser, protractor, promise } from 'protractor'; +import { SIDEBAR_LABELS, SITE_VISIBILITY } from '../../configs'; import { LoginPage, LogoutPage, BrowsingPage } from '../../pages/pages'; import { Utils } from '../../utilities/utils'; import { RepoClient, NodeContentTree } from '../../utilities/repo-client/repo-client'; @@ -29,136 +28,640 @@ describe('Pagination', () => { admin: new RepoClient(), user: new RepoClient(username, username) }; + const { + nodes: nodesApi, + trashcan: trashApi, + favorites: favoritesApi, + shared: sharedApi, + sites: sitesApi + } = apis.user; const loginPage = new LoginPage(); const logoutPage = new LogoutPage(); + const page = new BrowsingPage(); + const { dataTable, pagination } = page; + + const parent = `parent-${Utils.random()}`; + const files = Array(101) + .fill('file') + .map((name, index): string => `${name}-${index + 1}.txt`); + let filesIds; + + const filesForDelete = Array(101) + .fill('file') + .map((name, index): string => `${name}-${index + 1}.txt`); + let filesDeletedIds; + + function resetToDefaultPageSize(): promise.Promise { + return pagination.openMaxItemsMenu() + .then(menu => menu.clickMenuItem('25')) + .then(() => dataTable.waitForHeader()); + } + + function resetToDefaultPageNumber(): promise.Promise { + return pagination.openCurrentPageMenu() + .then(menu => menu.clickMenuItem('1')) + .then(() => dataTable.waitForHeader()); + } beforeAll(done => { - apis.admin.people - .createUser(username) + apis.admin.people.createUser(username) + .then(() => nodesApi.createFiles(files, parent)) + .then(resp => filesIds = resp.data.list.entries.map(entries => entries.entry.id)) + + .then(() => sharedApi.shareFilesByIds(filesIds)) + + .then(() => favoritesApi.addFavoritesByIds('file', filesIds)) + + .then(() => nodesApi.createFiles(filesForDelete)) + .then(resp => filesDeletedIds = resp.data.list.entries.map(entries => entries.entry.id)) + .then(() => nodesApi.deleteNodesById(filesDeletedIds, false)) + .then(done); }); + afterAll(done => { + Promise.all([ + nodesApi.deleteNodes([ parent ]), + trashApi.emptyTrash() + ]) + .then(done); + }); + xit(''); describe(`on Personal Files`, () => { - const personalFilesPage = new BrowsingPage(APP_ROUTES.PERSONAL_FILES); - const { dataTable, pagination } = personalFilesPage; - - // Generate files - const content: NodeContentTree = { - name: `user-folder-${Utils.random()}`, - files: Array(101) - .fill('file') - .map((name, index): string => `${name}-${index + 1}.txt`) - }; - - const { nodes: nodesApi } = apis.user; - beforeAll(done => { - nodesApi.createContent(content) - .then(() => loginPage.load() - .then(() => loginPage.loginWith(username)) - .then(done)); + loginPage.load() + .then(() => loginPage.loginWith(username)) + .then(done); }); beforeEach(done => { - personalFilesPage.load() + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.PERSONAL_FILES) .then(() => dataTable.waitForHeader()) - .then(() => dataTable.doubleClickOnRowByContainingText(content.name)) - .then(() => dataTable.sortByColumn('Name')) + .then(() => dataTable.doubleClickOnItemName(parent)) .then(done); }); + afterEach(done => { + browser.actions().sendKeys(protractor.Key.ESCAPE).perform().then(done); + }); + afterAll(done => { - logoutPage.load() - .then(() => nodesApi.deleteNodes([ content.name ])) - .then(done); + logoutPage.load().then(done); }); - it('has default details', () => { - expect(pagination.range.getText()).toContain('1-25 of 101', 'Range'); - expect(pagination.maxItems.getText()).toContain('25', 'Items per page'); - expect(pagination.currentPage.getText()).toContain('Page 1', 'Current page'); - expect(pagination.totalPages.getText()).toContain('of 5', 'Total pages'); - expect(pagination.previousButton.isEnabled()).toBe(false, 'Previous button'); - expect(pagination.nextButton.isEnabled()).toBe(true, 'Next button'); + it('default values', () => { + expect(pagination.range.getText()).toContain('1-25 of 101'); + expect(pagination.maxItems.getText()).toContain('25'); + expect(pagination.currentPage.getText()).toContain('Page 1'); + expect(pagination.totalPages.getText()).toContain('of 5'); + expect(pagination.previousButton.isEnabled()).toBe(false, 'Previous button is enabled'); + expect(pagination.nextButton.isEnabled()).toBe(true, 'Next button is not enabled'); }); - it('has page sizes', () => { + it('page sizes', () => { pagination.openMaxItemsMenu() .then(menu => { const [ first, second, third ] = [1, 2, 3] .map(nth => menu.getNthItem(nth).getText()); - - expect(first).toBe('25', 'Items per page'); - expect(second).toBe('50', 'Items per page'); - expect(third).toBe('100', 'Items per page'); + expect(first).toBe('25'); + expect(second).toBe('50'); + expect(third).toBe('100'); }); }); - it('changes the page size', () => { + it('change the page size', () => { pagination.openMaxItemsMenu() .then(menu => menu.clickMenuItem('50')) .then(() => dataTable.waitForHeader()) .then(() => { - expect(pagination.maxItems.getText()).toContain('50', 'Items per page'); - expect(pagination.totalPages.getText()).toContain('of 3', 'Total pages'); - }); + expect(pagination.maxItems.getText()).toContain('50'); + expect(pagination.totalPages.getText()).toContain('of 3'); + }) + + .then(() => resetToDefaultPageSize()); }); - it('has page items', () => { + it('current page menu items', () => { pagination.openCurrentPageMenu() .then(menu => { - expect(menu.items.count()).toBe(5); + expect(menu.getItemsCount()).toBe(5); }); }); - it('changes the current page from menu', () => { + it('change the current page from menu', () => { pagination.openCurrentPageMenu() - .then(menu => menu.clicktNthItem(3)) + .then(menu => menu.clickNthItem(3)) .then(() => dataTable.waitForHeader()) .then(() => { - expect(pagination.range.getText()).toContain('51-75 of 101', 'Range'); - expect(pagination.currentPage.getText()).toContain('Page 3', 'Current page'); - expect(pagination.previousButton.isEnabled()).toBe(true, 'Previous button'); - expect(pagination.nextButton.isEnabled()).toBe(true, 'Next button'); + expect(pagination.range.getText()).toContain('51-75 of 101'); + expect(pagination.currentPage.getText()).toContain('Page 3'); + expect(pagination.previousButton.isEnabled()).toBe(true, 'Previous button is not enabled'); + expect(pagination.nextButton.isEnabled()).toBe(true, 'Next button is not enabled'); expect(dataTable.getRowByContainingText('file-60.txt').isPresent()) .toBe(true, 'File not found on page'); - }); + }) + + .then(() => resetToDefaultPageNumber()); }); - it('navigates to next page', () => { + it('navigate to next page', () => { pagination.nextButton.click() .then(() => dataTable.waitForHeader()) .then(() => { - expect(pagination.range.getText()).toContain('26-50 of 101', 'Range'); + expect(pagination.range.getText()).toContain('26-50 of 101'); expect(dataTable.getRowByContainingText('file-30.txt').isPresent()) .toBe(true, 'File not found on page'); - }); + }) + + .then(() => resetToDefaultPageNumber()); }); - it('navigates to previous page', () => { + it('navigate to previous page', () => { pagination.openCurrentPageMenu() - .then(menu => menu.clicktNthItem(2)) + .then(menu => menu.clickNthItem(2)) .then(() => dataTable.waitForHeader()) .then(() => pagination.previousButton.click()) .then(() => dataTable.waitForHeader()) .then(() => { - expect(pagination.range.getText()).toContain('1-25 of 101', 'Range'); + expect(pagination.range.getText()).toContain('1-25 of 101'); expect(dataTable.getRowByContainingText('file-12.txt').isPresent()) .toBe(true, 'File not found on page'); + }) + + .then(() => resetToDefaultPageNumber()); + }); + + it('last page', () => { + pagination.openCurrentPageMenu() + .then(menu => menu.clickNthItem(5)) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(dataTable.countRows()).toBe(1, 'Incorrect number of items on the last page'); + expect(pagination.currentPage.getText()).toContain('Page 5'); + expect(pagination.nextButton.isEnabled()).toBe(false, 'Next button is enabled'); + }); + }); + }); + + describe(`on Recent Files`, () => { + beforeAll(done => { + loginPage.load() + .then(() => loginPage.loginWith(username)) + .then(done); + }); + + beforeEach(done => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.RECENT_FILES) + .then(() => dataTable.waitForHeader()) + .then(done); + }); + + afterEach(done => { + browser.actions().sendKeys(protractor.Key.ESCAPE).perform().then(done); + }); + + afterAll(done => { + logoutPage.load().then(done); + }); + + it('default values', () => { + expect(pagination.range.getText()).toContain('1-25 of 101'); + expect(pagination.maxItems.getText()).toContain('25'); + expect(pagination.currentPage.getText()).toContain('Page 1'); + expect(pagination.totalPages.getText()).toContain('of 5'); + expect(pagination.previousButton.isEnabled()).toBe(false, 'Previous button is enabled'); + expect(pagination.nextButton.isEnabled()).toBe(true, 'Next button is not enabled'); + }); + + it('page sizes', () => { + pagination.openMaxItemsMenu() + .then(menu => { + const [ first, second, third ] = [1, 2, 3] + .map(nth => menu.getNthItem(nth).getText()); + expect(first).toBe('25'); + expect(second).toBe('50'); + expect(third).toBe('100'); }); }); - it('has one item on the last page', () => { - pagination.openCurrentPageMenu() - .then(menu => menu.clicktNthItem(5)) + it('change the page size', () => { + pagination.openMaxItemsMenu() + .then(menu => menu.clickMenuItem('50')) .then(() => dataTable.waitForHeader()) .then(() => { - expect(dataTable.countRows()).toBe(1, 'Single item on the last page'); - expect(pagination.currentPage.getText()).toContain('Page 5', 'Last page'); - expect(pagination.nextButton.isEnabled()).toBe(false, 'Next button is not enabled'); + expect(pagination.maxItems.getText()).toContain('50'); + expect(pagination.totalPages.getText()).toContain('of 3'); + }) + + .then(() => resetToDefaultPageSize()); + }); + + it('current page menu items', () => { + pagination.openCurrentPageMenu() + .then(menu => { + expect(menu.getItemsCount()).toBe(5); + }); + }); + + it('change the current page from menu', () => { + pagination.openCurrentPageMenu() + .then(menu => menu.clickNthItem(3)) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(pagination.range.getText()).toContain('51-75 of 101'); + expect(pagination.currentPage.getText()).toContain('Page 3'); + expect(pagination.previousButton.isEnabled()).toBe(true, 'Previous button is not enabled'); + expect(pagination.nextButton.isEnabled()).toBe(true, 'Next button is not enabled'); + expect(dataTable.getRowByContainingText('file-40.txt').isPresent()) + .toBe(true, 'File not found on page'); + }) + + .then(() => resetToDefaultPageNumber()); + }); + + it('navigate to next page', () => { + pagination.nextButton.click() + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(pagination.range.getText()).toContain('26-50 of 101'); + expect(dataTable.getRowByContainingText('file-70.txt').isPresent()) + .toBe(true, 'File not found on page'); + }) + + .then(() => resetToDefaultPageNumber()); + }); + + it('navigate to previous page', () => { + pagination.openCurrentPageMenu() + .then(menu => menu.clickNthItem(2)) + .then(() => dataTable.waitForHeader()) + .then(() => pagination.previousButton.click()) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(pagination.range.getText()).toContain('1-25 of 101'); + expect(dataTable.getRowByContainingText('file-88.txt').isPresent()) + .toBe(true, 'File not found on page'); + }) + + .then(() => resetToDefaultPageNumber()); + }); + + it('last page', () => { + pagination.openCurrentPageMenu() + .then(menu => menu.clickNthItem(5)) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(dataTable.countRows()).toBe(1, 'Incorrect number of items on the last page'); + expect(pagination.currentPage.getText()).toContain('Page 5'); + expect(pagination.nextButton.isEnabled()).toBe(false, 'Next button is enabled'); + }); + }); + }); + + describe(`on Favorites`, () => { + beforeAll(done => { + loginPage.load() + .then(() => loginPage.loginWith(username)) + .then(done); + }); + + beforeEach(done => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.FAVORITES) + .then(() => dataTable.waitForHeader()) + .then(done); + }); + + afterEach(done => { + browser.actions().sendKeys(protractor.Key.ESCAPE).perform().then(done); + }); + + afterAll(done => { + logoutPage.load().then(done); + }); + + it('default values', () => { + expect(pagination.range.getText()).toContain('1-25 of 101'); + expect(pagination.maxItems.getText()).toContain('25'); + expect(pagination.currentPage.getText()).toContain('Page 1'); + expect(pagination.totalPages.getText()).toContain('of 5'); + expect(pagination.previousButton.isEnabled()).toBe(false, 'Previous button is enabled'); + expect(pagination.nextButton.isEnabled()).toBe(true, 'Next button is not enabled'); + }); + + it('page sizes', () => { + pagination.openMaxItemsMenu() + .then(menu => { + const [ first, second, third ] = [1, 2, 3] + .map(nth => menu.getNthItem(nth).getText()); + expect(first).toBe('25'); + expect(second).toBe('50'); + expect(third).toBe('100'); + }); + }); + + it('change the page size', () => { + pagination.openMaxItemsMenu() + .then(menu => menu.clickMenuItem('50')) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(pagination.maxItems.getText()).toContain('50'); + expect(pagination.totalPages.getText()).toContain('of 3'); + }) + + .then(() => resetToDefaultPageSize()); + }); + + it('current page menu items', () => { + pagination.openCurrentPageMenu() + .then(menu => { + expect(menu.getItemsCount()).toBe(5); + }); + }); + + it('change the current page from menu', () => { + pagination.openCurrentPageMenu() + .then(menu => menu.clickNthItem(3)) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(pagination.range.getText()).toContain('51-75 of 101'); + expect(pagination.currentPage.getText()).toContain('Page 3'); + expect(pagination.previousButton.isEnabled()).toBe(true, 'Previous button is not enabled'); + expect(pagination.nextButton.isEnabled()).toBe(true, 'Next button is not enabled'); + expect(dataTable.getRowByContainingText('file-40.txt').isPresent()) + .toBe(true, 'File not found on page'); + }) + + .then(() => resetToDefaultPageNumber()); + }); + + it('navigate to next page', () => { + pagination.nextButton.click() + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(pagination.range.getText()).toContain('26-50 of 101'); + expect(dataTable.getRowByContainingText('file-70.txt').isPresent()) + .toBe(true, 'File not found on page'); + }) + + .then(() => resetToDefaultPageNumber()); + }); + + it('navigate to previous page', () => { + pagination.openCurrentPageMenu() + .then(menu => menu.clickNthItem(2)) + .then(() => dataTable.waitForHeader()) + .then(() => pagination.previousButton.click()) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(pagination.range.getText()).toContain('1-25 of 101'); + expect(dataTable.getRowByContainingText('file-88.txt').isPresent()) + .toBe(true, 'File not found on page'); + }) + + .then(() => resetToDefaultPageNumber()); + }); + + it('last page', () => { + pagination.openCurrentPageMenu() + .then(menu => menu.clickNthItem(5)) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(dataTable.countRows()).toBe(1, 'Incorrect number of items on the last page'); + expect(pagination.currentPage.getText()).toContain('Page 5'); + expect(pagination.nextButton.isEnabled()).toBe(false, 'Next button is enabled'); + }); + }); + }); + + describe(`on Shared Files`, () => { + beforeAll(done => { + loginPage.load() + .then(() => loginPage.loginWith(username)) + .then(done); + }); + + beforeEach(done => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.SHARED_FILES) + .then(() => dataTable.waitForHeader()) + .then(done); + }); + + afterEach(done => { + browser.actions().sendKeys(protractor.Key.ESCAPE).perform().then(done); + }); + + afterAll(done => { + logoutPage.load().then(done); + }); + + it('default values', () => { + expect(pagination.range.getText()).toContain('1-25 of 101'); + expect(pagination.maxItems.getText()).toContain('25'); + expect(pagination.currentPage.getText()).toContain('Page 1'); + expect(pagination.totalPages.getText()).toContain('of 5'); + expect(pagination.previousButton.isEnabled()).toBe(false, 'Previous button is enabled'); + expect(pagination.nextButton.isEnabled()).toBe(true, 'Next button is not enabled'); + }); + + it('page sizes', () => { + pagination.openMaxItemsMenu() + .then(menu => { + const [ first, second, third ] = [1, 2, 3] + .map(nth => menu.getNthItem(nth).getText()); + expect(first).toBe('25'); + expect(second).toBe('50'); + expect(third).toBe('100'); + }); + }); + + it('change the page size', () => { + pagination.openMaxItemsMenu() + .then(menu => menu.clickMenuItem('50')) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(pagination.maxItems.getText()).toContain('50'); + expect(pagination.totalPages.getText()).toContain('of 3'); + }) + + .then(() => resetToDefaultPageSize()); + }); + + it('current page menu items', () => { + pagination.openCurrentPageMenu() + .then(menu => { + expect(menu.getItemsCount()).toBe(5); + }); + }); + + it('change the current page from menu', () => { + pagination.openCurrentPageMenu() + .then(menu => menu.clickNthItem(3)) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(pagination.range.getText()).toContain('51-75 of 101'); + expect(pagination.currentPage.getText()).toContain('Page 3'); + expect(pagination.previousButton.isEnabled()).toBe(true, 'Previous button is not enabled'); + expect(pagination.nextButton.isEnabled()).toBe(true, 'Next button is not enabled'); + expect(dataTable.getRowByContainingText('file-40.txt').isPresent()) + .toBe(true, 'File not found on page'); + }) + + .then(() => resetToDefaultPageNumber()); + }); + + it('navigate to next page', () => { + pagination.nextButton.click() + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(pagination.range.getText()).toContain('26-50 of 101'); + expect(dataTable.getRowByContainingText('file-70.txt').isPresent()) + .toBe(true, 'File not found on page'); + }) + + .then(() => resetToDefaultPageNumber()); + }); + + it('navigate to previous page', () => { + pagination.openCurrentPageMenu() + .then(menu => menu.clickNthItem(2)) + .then(() => dataTable.waitForHeader()) + .then(() => pagination.previousButton.click()) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(pagination.range.getText()).toContain('1-25 of 101'); + expect(dataTable.getRowByContainingText('file-88.txt').isPresent()) + .toBe(true, 'File not found on page'); + }) + + .then(() => resetToDefaultPageNumber()); + }); + + it('last page', () => { + pagination.openCurrentPageMenu() + .then(menu => menu.clickNthItem(5)) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(dataTable.countRows()).toBe(1, 'Incorrect number of items on the last page'); + expect(pagination.currentPage.getText()).toContain('Page 5'); + expect(pagination.nextButton.isEnabled()).toBe(false, 'Next button is enabled'); + }); + }); + }); + + describe(`on Trash`, () => { + beforeAll(done => { + loginPage.load() + .then(() => loginPage.loginWith(username)) + .then(done); + }); + + beforeEach(done => { + page.sidenav.navigateToLinkByLabel(SIDEBAR_LABELS.TRASH) + .then(() => dataTable.waitForHeader()) + .then(done); + }); + + afterEach(done => { + browser.actions().sendKeys(protractor.Key.ESCAPE).perform().then(done); + }); + + afterAll(done => { + logoutPage.load().then(done); + }); + + it('default values', () => { + expect(pagination.range.getText()).toContain('1-25 of 101'); + expect(pagination.maxItems.getText()).toContain('25'); + expect(pagination.currentPage.getText()).toContain('Page 1'); + expect(pagination.totalPages.getText()).toContain('of 5'); + expect(pagination.previousButton.isEnabled()).toBe(false, 'Previous button is enabled'); + expect(pagination.nextButton.isEnabled()).toBe(true, 'Next button is not enabled'); + }); + + it('page sizes', () => { + pagination.openMaxItemsMenu() + .then(menu => { + const [ first, second, third ] = [1, 2, 3] + .map(nth => menu.getNthItem(nth).getText()); + expect(first).toBe('25'); + expect(second).toBe('50'); + expect(third).toBe('100'); + }); + }); + + it('change the page size', () => { + pagination.openMaxItemsMenu() + .then(menu => menu.clickMenuItem('50')) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(pagination.maxItems.getText()).toContain('50'); + expect(pagination.totalPages.getText()).toContain('of 3'); + }) + + .then(() => resetToDefaultPageSize()); + }); + + it('current page menu items', () => { + pagination.openCurrentPageMenu() + .then(menu => { + expect(menu.getItemsCount()).toBe(5); + }); + }); + + it('change the current page from menu', () => { + pagination.openCurrentPageMenu() + .then(menu => menu.clickNthItem(3)) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(pagination.range.getText()).toContain('51-75 of 101'); + expect(pagination.currentPage.getText()).toContain('Page 3'); + expect(pagination.previousButton.isEnabled()).toBe(true, 'Previous button is not enabled'); + expect(pagination.nextButton.isEnabled()).toBe(true, 'Next button is not enabled'); + expect(dataTable.getRowByContainingText('file-40.txt').isPresent()) + .toBe(true, 'File not found on page'); + }) + + .then(() => resetToDefaultPageNumber()); + }); + + it('navigate to next page', () => { + pagination.nextButton.click() + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(pagination.range.getText()).toContain('26-50 of 101'); + expect(dataTable.getRowByContainingText('file-70.txt').isPresent()) + .toBe(true, 'File not found on page'); + }) + + .then(() => resetToDefaultPageNumber()); + }); + + it('navigate to previous page', () => { + pagination.openCurrentPageMenu() + .then(menu => menu.clickNthItem(2)) + .then(() => dataTable.waitForHeader()) + .then(() => pagination.previousButton.click()) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(pagination.range.getText()).toContain('1-25 of 101'); + expect(dataTable.getRowByContainingText('file-88.txt').isPresent()) + .toBe(true, 'File not found on page'); + }) + + .then(() => resetToDefaultPageNumber()); + }); + + it('last page', () => { + pagination.openCurrentPageMenu() + .then(menu => menu.clickNthItem(5)) + .then(() => dataTable.waitForHeader()) + .then(() => { + expect(dataTable.countRows()).toBe(1, 'Incorrect number of items on the last page'); + expect(pagination.currentPage.getText()).toContain('Page 5'); + expect(pagination.nextButton.isEnabled()).toBe(false, 'Next button is enabled'); }); }); }); diff --git a/e2e/utilities/repo-client/apis/favorites/favorites-api.ts b/e2e/utilities/repo-client/apis/favorites/favorites-api.ts index e2b0c4ee0..c8ab9a945 100644 --- a/e2e/utilities/repo-client/apis/favorites/favorites-api.ts +++ b/e2e/utilities/repo-client/apis/favorites/favorites-api.ts @@ -52,6 +52,12 @@ export class FavoritesApi extends RepoApi { .catch(this.handleError); } + addFavoritesByIds(nodeType: string, ids: string[]): Promise { + return ids.reduce((previous, current) => ( + previous.then(() => this.addFavoriteById(nodeType, current)) + ), Promise.resolve()); + } + getFavorite(api: RepoClient, name: string): Promise { return api.nodes.getNodeByPath(name) .then((response) => { diff --git a/e2e/utilities/repo-client/apis/nodes/nodes-api.ts b/e2e/utilities/repo-client/apis/nodes/nodes-api.ts index 9c98c8fb5..90eb669c0 100644 --- a/e2e/utilities/repo-client/apis/nodes/nodes-api.ts +++ b/e2e/utilities/repo-client/apis/nodes/nodes-api.ts @@ -33,6 +33,12 @@ export class NodesApi extends RepoApi { .catch(this.handleError); } + getNodeDescription(name: string): Promise { + return this.getNodeByPath(name) + .then(response => response.data.entry.properties['cm:description']) + .catch(() => Promise.resolve('')); + } + deleteNodeById(id: string, permanent: boolean = true): Promise { return this .delete(`/nodes/${id}?permanent=${permanent}`) @@ -47,19 +53,16 @@ export class NodesApi extends RepoApi { .catch(this.handleError); } - getNodeDescription(name: string): Promise { - return this.getNodeByPath(name) - .then(response => response.data.entry.properties['cm:description']) - .catch(() => Promise.resolve('')); + deleteNodes(names: string[], relativePath: string = '', permanent: boolean = true): Promise { + return names.reduce((previous, current) => ( + previous.then(() => this.deleteNodeByPath(`${relativePath}/${current}`, permanent)) + ), Promise.resolve()); } - deleteNodes(names: string[], relativePath: string = '', permanent: boolean = true): Promise { - const deletions = names - .map((name: string): any => { - return this.deleteNodeByPath(`${relativePath}/${name}`, permanent); - }); - - return Promise.all(deletions); + deleteNodesById(ids: string[], permanent: boolean = true): Promise { + return ids.reduce((previous, current) => ( + previous.then(() => this.deleteNodeById(current, permanent)) + ), Promise.resolve()); } // children diff --git a/e2e/utilities/repo-client/apis/shared-links/shared-links-api.ts b/e2e/utilities/repo-client/apis/shared-links/shared-links-api.ts index b11854c3f..c04cdcfd1 100644 --- a/e2e/utilities/repo-client/apis/shared-links/shared-links-api.ts +++ b/e2e/utilities/repo-client/apis/shared-links/shared-links-api.ts @@ -28,6 +28,12 @@ export class SharedLinksApi extends RepoApi { .catch(this.handleError); } + shareFilesByIds(ids: string[]): Promise { + return ids.reduce((previous, current) => ( + previous.then(() => this.shareFileById(current)) + ), Promise.resolve()); + } + getSharedLinks(): Promise { return this.get(`/shared-links`) .catch(this.handleError); diff --git a/e2e/utilities/repo-client/apis/sites/sites-api.ts b/e2e/utilities/repo-client/apis/sites/sites-api.ts index 05b3f7e6c..cb316daa1 100644 --- a/e2e/utilities/repo-client/apis/sites/sites-api.ts +++ b/e2e/utilities/repo-client/apis/sites/sites-api.ts @@ -35,7 +35,7 @@ export class SitesApi extends RepoApi { .catch(this.handleError); } - createSite(title: string, visibility: string, details?: Site): Promise { + createOrUpdateSite(title: string, visibility: string, details?: Site): Promise { const site: Site = new Site(title, visibility, details); const onSuccess = (response) => response; const onError = (response) => { @@ -50,12 +50,31 @@ export class SitesApi extends RepoApi { .catch(this.handleError); } + createSite(title: string, visibility: string, details?: Site): Promise { + const site: Site = new Site(title, visibility, details); + return this + .post(`/sites`, { data: site }) + .catch(this.handleError); + } + + createSites(titles: string[], visibility: string): Promise { + return titles.reduce((previous, current) => ( + previous.then(() => this.createSite(current, visibility)) + ), Promise.resolve()); + } + deleteSite(id: string, permanent: boolean = true): Promise { return this .delete(`/sites/${id}?permanent=${permanent}`) .catch(this.handleError); } + deleteSites(ids: string[], permanent: boolean = true): Promise { + return ids.reduce((previous, current) => ( + previous.then(() => this.deleteSite(current)) + ), Promise.resolve()); + } + updateSiteMember(siteId: string, userId: string, role: string): Promise { return this .put(`/sites/${siteId}/members/${userId}`, { data: { role } }) diff --git a/e2e/utilities/repo-client/apis/trashcan/trashcan-api.ts b/e2e/utilities/repo-client/apis/trashcan/trashcan-api.ts index cc50b1465..7a0e143e7 100644 --- a/e2e/utilities/repo-client/apis/trashcan/trashcan-api.ts +++ b/e2e/utilities/repo-client/apis/trashcan/trashcan-api.ts @@ -23,4 +23,24 @@ export class TrashcanApi extends RepoApi { .delete(`/deleted-nodes/${id}`) .catch(this.handleError); } + + getDeletedNodes(): Promise { + return this + .get(`/deleted-nodes?maxItems=1000`) + .catch(this.handleError); + } + + emptyTrash(): Promise { + return this.getDeletedNodes() + .then(resp => { + return resp.data.list.entries.map(entries => entries.entry.id); + }) + .then(ids => { + return ids.reduce((previous, current) => ( + previous.then(() => this.permanentlyDelete(current)) + ), Promise.resolve()); + }) + .catch(this.handleError); + } + } diff --git a/package.json b/package.json index a671288e6..276365066 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "build": "npm run server-versions && ng build", "build:prod": "npm run server-versions && ng build --prod", "build:dev": "npm run server-versions && ng build && node postbuild-dev.js", + "build:tomcat": "npm run server-versions && ng build --base-href ./", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e", @@ -15,8 +16,8 @@ }, "private": true, "dependencies": { - "@alfresco/adf-content-services": "2.0.0-beta2", - "@alfresco/adf-core": "2.0.0-beta2", + "@alfresco/adf-content-services": "2.0.0-c968fbcdacb90946dc00bca3d7b663de94d5a9ad", + "@alfresco/adf-core": "2.0.0-c968fbcdacb90946dc00bca3d7b663de94d5a9ad", "@angular/animations": "5.0.0", "@angular/cdk": "5.0.0-rc0", "@angular/common": "5.0.0", @@ -33,6 +34,7 @@ "alfresco-js-api": "2.0.0-beta1", "core-js": "2.4.1", "hammerjs": "2.0.8", + "moment-es6": "1.0.0", "pdfjs-dist": "1.5.404", "rxjs": "5.5.2", "web-animations-js": "^2.3.1", diff --git a/protractor.conf.js b/protractor.conf.js index 9f995ddbb..60ea06d39 100644 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -7,6 +7,9 @@ const jasmineReporters = require('jasmine-reporters'); const projectRoot = path.resolve(__dirname); +const width = 1366; +const height = 768; + exports.config = { allScriptsTimeout: 11000, @@ -55,6 +58,8 @@ exports.config = { project: 'e2e/tsconfig.e2e.json' }); + browser.manage().window().setSize(width, height); + jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); jasmine.getEnv().addReporter(new jasmineReporters.JUnitXmlReporter({ diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 53fadf7c5..f4eecb42d 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -48,6 +48,7 @@ import { LocationLinkComponent } from './components/location-link/location-link. imports: [ BrowserModule, RouterModule.forRoot(APP_ROUTES, { + useHash: true, enableTracing: false // enable for debug only }), AdfModule, diff --git a/src/app/common/directives/node-copy.directive.spec.ts b/src/app/common/directives/node-copy.directive.spec.ts index 9e70237da..933ca54c3 100644 --- a/src/app/common/directives/node-copy.directive.spec.ts +++ b/src/app/common/directives/node-copy.directive.spec.ts @@ -92,7 +92,7 @@ describe('NodeCopyDirective', () => { expect(service.copyNodes).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction).toHaveBeenCalledWith( - 'APP.MESSAGES.INFO.NODE_COPY.SINGULAR', 'Undo', 10000 + 'APP.MESSAGES.INFO.NODE_COPY.SINGULAR', 'APP.ACTIONS.UNDO', 10000 ); }); @@ -112,7 +112,7 @@ describe('NodeCopyDirective', () => { expect(service.copyNodes).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction).toHaveBeenCalledWith( - 'APP.MESSAGES.INFO.NODE_COPY.PLURAL', 'Undo', 10000 + 'APP.MESSAGES.INFO.NODE_COPY.PLURAL', 'APP.ACTIONS.UNDO', 10000 ); }); @@ -131,7 +131,7 @@ describe('NodeCopyDirective', () => { expect(service.copyNodes).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction).toHaveBeenCalledWith( - 'APP.MESSAGES.INFO.NODE_COPY.PARTIAL_SINGULAR', 'Undo', 10000 + 'APP.MESSAGES.INFO.NODE_COPY.PARTIAL_SINGULAR', 'APP.ACTIONS.UNDO', 10000 ); }); @@ -152,7 +152,7 @@ describe('NodeCopyDirective', () => { expect(service.copyNodes).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction).toHaveBeenCalledWith( - 'APP.MESSAGES.INFO.NODE_COPY.PARTIAL_PLURAL', 'Undo', 10000 + 'APP.MESSAGES.INFO.NODE_COPY.PARTIAL_PLURAL', 'APP.ACTIONS.UNDO', 10000 ); }); @@ -255,7 +255,7 @@ describe('NodeCopyDirective', () => { expect(service.copyNodes).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction).toHaveBeenCalledWith( - 'APP.MESSAGES.INFO.NODE_COPY.SINGULAR', 'Undo', 10000 + 'APP.MESSAGES.INFO.NODE_COPY.SINGULAR', 'APP.ACTIONS.UNDO', 10000 ); expect(nodesApiService.deleteNode).toHaveBeenCalledWith(createdItems[0].entry.id, { permanent: true }); @@ -279,7 +279,7 @@ describe('NodeCopyDirective', () => { expect(service.copyNodes).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction).toHaveBeenCalledWith( - 'APP.MESSAGES.INFO.NODE_COPY.PLURAL', 'Undo', 10000 + 'APP.MESSAGES.INFO.NODE_COPY.PLURAL', 'APP.ACTIONS.UNDO', 10000 ); expect(spyOnDeleteNode).toHaveBeenCalled(); @@ -300,7 +300,7 @@ describe('NodeCopyDirective', () => { expect(service.copyNodes).toHaveBeenCalled(); expect(nodesApiService.deleteNode).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction['calls'].allArgs()) - .toEqual([['APP.MESSAGES.INFO.NODE_COPY.SINGULAR', 'Undo', 10000], + .toEqual([['APP.MESSAGES.INFO.NODE_COPY.SINGULAR', 'APP.ACTIONS.UNDO', 10000], ['APP.MESSAGES.ERRORS.GENERIC', '', 3000]]); }); @@ -317,7 +317,7 @@ describe('NodeCopyDirective', () => { expect(service.copyNodes).toHaveBeenCalled(); expect(nodesApiService.deleteNode).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction['calls'].allArgs()) - .toEqual([['APP.MESSAGES.INFO.NODE_COPY.SINGULAR', 'Undo', 10000], + .toEqual([['APP.MESSAGES.INFO.NODE_COPY.SINGULAR', 'APP.ACTIONS.UNDO', 10000], ['APP.MESSAGES.ERRORS.GENERIC', '', 3000]]); }); @@ -334,7 +334,7 @@ describe('NodeCopyDirective', () => { expect(service.copyNodes).toHaveBeenCalled(); expect(nodesApiService.deleteNode).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction['calls'].allArgs()) - .toEqual([['APP.MESSAGES.INFO.NODE_COPY.SINGULAR', 'Undo', 10000], + .toEqual([['APP.MESSAGES.INFO.NODE_COPY.SINGULAR', 'APP.ACTIONS.UNDO', 10000], ['APP.MESSAGES.ERRORS.PERMISSION', '', 3000]]); }); }); diff --git a/src/app/common/directives/node-copy.directive.ts b/src/app/common/directives/node-copy.directive.ts index b1f66aa5b..e6a711afa 100644 --- a/src/app/common/directives/node-copy.directive.ts +++ b/src/app/common/directives/node-copy.directive.ts @@ -96,7 +96,7 @@ export class NodeCopyDirective { } catch (err) { /* Do nothing, keep the original message */ } } - const undo = (numberOfCopiedItems > 0) ? 'Undo' : ''; + const undo = (numberOfCopiedItems > 0) ? this.translation.translate.instant('APP.ACTIONS.UNDO') : ''; const withUndo = (numberOfCopiedItems > 0) ? '_WITH_UNDO' : ''; this.translation.get(i18nMessageString, { success: numberOfCopiedItems, failed: failedItems }).subscribe(message => { diff --git a/src/app/common/directives/node-download.directive.ts b/src/app/common/directives/node-download.directive.ts index ba420b9ad..5107bcc52 100644 --- a/src/app/common/directives/node-download.directive.ts +++ b/src/app/common/directives/node-download.directive.ts @@ -18,7 +18,8 @@ import { Directive, Input, HostListener } from '@angular/core'; import { MatDialog } from '@angular/material'; import { MinimalNodeEntity } from 'alfresco-js-api'; -import { AlfrescoApiService, DownloadZipDialogComponent } from '@alfresco/adf-core'; +import { AlfrescoApiService } from '@alfresco/adf-core'; +import { DownloadZipDialogComponent } from '@alfresco/adf-content-services'; @Directive({ selector: '[app-download-node]' diff --git a/src/app/common/directives/node-move.directive.spec.ts b/src/app/common/directives/node-move.directive.spec.ts index 4a5422df9..9476e6734 100644 --- a/src/app/common/directives/node-move.directive.spec.ts +++ b/src/app/common/directives/node-move.directive.spec.ts @@ -107,7 +107,7 @@ describe('NodeMoveDirective', () => { expect(service.moveNodes).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction).toHaveBeenCalledWith( - 'APP.MESSAGES.INFO.NODE_MOVE.SINGULAR', 'Undo', 10000 + 'APP.MESSAGES.INFO.NODE_MOVE.SINGULAR', 'APP.ACTIONS.UNDO', 10000 ); }); @@ -132,7 +132,7 @@ describe('NodeMoveDirective', () => { expect(service.moveNodes).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction).toHaveBeenCalledWith( - 'APP.MESSAGES.INFO.NODE_MOVE.PLURAL', 'Undo', 10000 + 'APP.MESSAGES.INFO.NODE_MOVE.PLURAL', 'APP.ACTIONS.UNDO', 10000 ); }); @@ -155,7 +155,7 @@ describe('NodeMoveDirective', () => { expect(service.moveNodes).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction).toHaveBeenCalledWith( - 'APP.MESSAGES.INFO.NODE_MOVE.PARTIAL.SINGULAR', 'Undo', 10000 + 'APP.MESSAGES.INFO.NODE_MOVE.PARTIAL.SINGULAR', 'APP.ACTIONS.UNDO', 10000 ); }); @@ -180,7 +180,7 @@ describe('NodeMoveDirective', () => { expect(service.moveNodes).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction).toHaveBeenCalledWith( - 'APP.MESSAGES.INFO.NODE_MOVE.PARTIAL.PLURAL', 'Undo', 10000 + 'APP.MESSAGES.INFO.NODE_MOVE.PARTIAL.PLURAL', 'APP.ACTIONS.UNDO', 10000 ); }); @@ -204,7 +204,7 @@ describe('NodeMoveDirective', () => { expect(service.moveNodes).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction).toHaveBeenCalledWith( - 'APP.MESSAGES.INFO.NODE_MOVE.SINGULAR APP.MESSAGES.INFO.NODE_MOVE.PARTIAL.FAIL', 'Undo', 10000 + 'APP.MESSAGES.INFO.NODE_MOVE.SINGULAR APP.MESSAGES.INFO.NODE_MOVE.PARTIAL.FAIL', 'APP.ACTIONS.UNDO', 10000 ); }); @@ -228,7 +228,7 @@ describe('NodeMoveDirective', () => { expect(service.moveNodes).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction).toHaveBeenCalledWith( - 'APP.MESSAGES.INFO.NODE_MOVE.SINGULAR APP.MESSAGES.INFO.NODE_MOVE.PARTIAL.SINGULAR', 'Undo', 10000 + 'APP.MESSAGES.INFO.NODE_MOVE.SINGULAR APP.MESSAGES.INFO.NODE_MOVE.PARTIAL.SINGULAR', 'APP.ACTIONS.UNDO', 10000 ); }); @@ -348,7 +348,7 @@ describe('NodeMoveDirective', () => { expect(service.moveNodeAction) .toHaveBeenCalledWith(movedItems.succeeded[0].itemMoved.entry, movedItems.succeeded[0].initialParentId); expect(notificationService.openSnackMessageAction) - .toHaveBeenCalledWith('APP.MESSAGES.INFO.NODE_MOVE.SINGULAR', 'Undo', 10000); + .toHaveBeenCalledWith('APP.MESSAGES.INFO.NODE_MOVE.SINGULAR', 'APP.ACTIONS.UNDO', 10000); }); it('should move node back to initial parent, after succeeded move of a single file', () => { @@ -370,7 +370,7 @@ describe('NodeMoveDirective', () => { expect(service.moveNodeAction).toHaveBeenCalledWith(node.entry, initialParent); expect(notificationService.openSnackMessageAction) - .toHaveBeenCalledWith('APP.MESSAGES.INFO.NODE_MOVE.SINGULAR', 'Undo', 10000); + .toHaveBeenCalledWith('APP.MESSAGES.INFO.NODE_MOVE.SINGULAR', 'APP.ACTIONS.UNDO', 10000); }); it('should restore deleted folder back to initial parent, after succeeded moving all its files', () => { @@ -396,7 +396,7 @@ describe('NodeMoveDirective', () => { expect(nodesApiService.restoreNode).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction) - .toHaveBeenCalledWith('APP.MESSAGES.INFO.NODE_MOVE.SINGULAR', 'Undo', 10000); + .toHaveBeenCalledWith('APP.MESSAGES.INFO.NODE_MOVE.SINGULAR', 'APP.ACTIONS.UNDO', 10000); }); it('should notify when error occurs on Undo Move action', () => { @@ -422,7 +422,7 @@ describe('NodeMoveDirective', () => { expect(nodesApiService.restoreNode).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction) - .toHaveBeenCalledWith('APP.MESSAGES.INFO.NODE_MOVE.SINGULAR', 'Undo', 10000); + .toHaveBeenCalledWith('APP.MESSAGES.INFO.NODE_MOVE.SINGULAR', 'APP.ACTIONS.UNDO', 10000); expect(notificationService.openSnackMessage) .toHaveBeenCalledWith('APP.MESSAGES.ERRORS.GENERIC', 3000); }); @@ -449,7 +449,7 @@ describe('NodeMoveDirective', () => { expect(nodesApiService.restoreNode).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction) - .toHaveBeenCalledWith('APP.MESSAGES.INFO.NODE_MOVE.SINGULAR', 'Undo', 10000); + .toHaveBeenCalledWith('APP.MESSAGES.INFO.NODE_MOVE.SINGULAR', 'APP.ACTIONS.UNDO', 10000); expect(notificationService.openSnackMessage) .toHaveBeenCalledWith('APP.MESSAGES.ERRORS.GENERIC', 3000); }); @@ -477,7 +477,7 @@ describe('NodeMoveDirective', () => { expect(service.moveNodes).toHaveBeenCalled(); expect(nodesApiService.restoreNode).toHaveBeenCalled(); expect(notificationService.openSnackMessageAction) - .toHaveBeenCalledWith('APP.MESSAGES.INFO.NODE_MOVE.SINGULAR', 'Undo', 10000); + .toHaveBeenCalledWith('APP.MESSAGES.INFO.NODE_MOVE.SINGULAR', 'APP.ACTIONS.UNDO', 10000); expect(notificationService.openSnackMessage) .toHaveBeenCalledWith('APP.MESSAGES.ERRORS.PERMISSION', 3000); }); diff --git a/src/app/common/directives/node-move.directive.ts b/src/app/common/directives/node-move.directive.ts index 60196eaa9..081f2ae8a 100644 --- a/src/app/common/directives/node-move.directive.ts +++ b/src/app/common/directives/node-move.directive.ts @@ -46,7 +46,7 @@ export class NodeMoveDirective { ) {} moveSelected() { - const permissionForMove: string = 'delete'; + const permissionForMove = '!'; Observable.zip( this.nodeActionsService.moveNodes(this.selection, permissionForMove), @@ -110,7 +110,7 @@ export class NodeMoveDirective { errorMessage = this.getErrorMessage(info); } - const undo = (succeeded + partiallySucceeded > 0) ? 'Undo' : ''; + const undo = (succeeded + partiallySucceeded > 0) ? this.translation.translate.instant('APP.ACTIONS.UNDO') : ''; const withUndo = errorMessage ? '' : '_WITH_UNDO'; failedMessage = errorMessage ? errorMessage : failedMessage; diff --git a/src/app/common/services/node-actions.service.spec.ts b/src/app/common/services/node-actions.service.spec.ts index b8c922ae3..73878032c 100644 --- a/src/app/common/services/node-actions.service.spec.ts +++ b/src/app/common/services/node-actions.service.spec.ts @@ -15,7 +15,6 @@ * limitations under the License. */ - /* import { TestBed, async } from '@angular/core/testing'; import { MatDialog } from '@angular/material'; import { OverlayModule } from '@angular/cdk/overlay'; @@ -23,16 +22,17 @@ import { Observable } from 'rxjs/Rx'; import { CoreModule, AlfrescoApiService, NodesApiService } from '@alfresco/adf-core'; import { DocumentListService } from '@alfresco/adf-content-services'; import { NodeActionsService } from './node-actions.service'; -import { MinimalNodeEntity, MinimalNodeEntryEntity } from 'alfresco-js-api'; +import { MinimalNodeEntryEntity } from 'alfresco-js-api'; -class TestNode extends MinimalNodeEntity { - constructor(id?: string, isFile?: boolean, name?: string, permission?: string[]) { - super(); - this.entry = new MinimalNodeEntryEntity(); +class TestNode { + entry?: MinimalNodeEntryEntity; + + constructor(id?: string, isFile?: boolean, name?: string, permission?: string[], nodeType?: string) { + this.entry = {}; this.entry.id = id || 'node-id'; this.entry.isFile = isFile; this.entry.isFolder = !isFile; - this.entry.nodeType = isFile ? 'content' : 'folder'; + this.entry.nodeType = nodeType ? nodeType : (isFile ? 'content' : 'folder'); this.entry.name = name; if (permission) { this.entry['allowableOperations'] = permission; @@ -241,6 +241,42 @@ describe('NodeActionsService', () => { }); }); + describe('rowFilter', () => { + let fileToCopy; + let folderToCopy; + let testContentNodeSelectorComponentData; + + beforeEach(() => { + fileToCopy = new TestNode(fileId, isFile, 'file-name'); + folderToCopy = new TestNode(); + + spyOn(service, 'getFirstParentId').and.returnValue('parent-id'); + + const dialog = TestBed.get(MatDialog); + spyOn(dialog, 'open').and.callFake((contentNodeSelectorComponent: any, data: any) => { + testContentNodeSelectorComponentData = data; + return {}; + }); + + service.copyNodes([fileToCopy, folderToCopy]); + }); + + it('should filter destination nodes and not show files', () => { + const file = new TestNode('a-file', isFile); + expect(testContentNodeSelectorComponentData.data.rowFilter({node: file})).toBe(false); + }); + + it('should filter destination nodes and not show the symlinks', () => { + const symlinkDestinationFolder = new TestNode('symlink-id', !isFile, 'symlink-name', [], 'app:folderlink'); + expect(testContentNodeSelectorComponentData.data.rowFilter({node: symlinkDestinationFolder})).toBe(false); + }); + + it('should filter destination nodes and show folders', () => { + const destinationFolder = new TestNode(folderDestinationId); + expect(testContentNodeSelectorComponentData.data.rowFilter({node: destinationFolder})).toBe(true); + }); + }); + describe('copyNodes', () => { let fileToCopy; let folderToCopy; @@ -306,7 +342,7 @@ describe('NodeActionsService', () => { expect(spyOnBatchOperation).toHaveBeenCalled(); expect(testContentNodeSelectorComponentData).toBeDefined(); - expect(testContentNodeSelectorComponentData.data.title).toBe('copy entry-name to ...'); + expect(testContentNodeSelectorComponentData.data.title).toBe('copy \'entry-name\' to ...'); }); it('should use the ContentNodeSelectorComponentData object without file name in title, if no name exists', () => { @@ -1181,4 +1217,3 @@ describe('NodeActionsService', () => { }); }); -*/ diff --git a/src/app/common/services/node-actions.service.ts b/src/app/common/services/node-actions.service.ts index 72a41394d..f8612710a 100644 --- a/src/app/common/services/node-actions.service.ts +++ b/src/app/common/services/node-actions.service.ts @@ -170,7 +170,7 @@ export class NodeActionsService { let nodeEntryName = ''; if (contentEntities.length === 1 && contentEntities[0].entry.name) { - nodeEntryName = `${contentEntities[0].entry.name} `; + nodeEntryName = `'${contentEntities[0].entry.name}' `; } const data: ContentNodeSelectorComponentData = { @@ -419,7 +419,7 @@ export class NodeActionsService { // todo: review once 1.10-beta6 is out private rowFilter(row: /*ShareDataRow*/ any): boolean { const node: MinimalNodeEntryEntity = row.node.entry; - return (!node.isFile); + return (!node.isFile && (node.nodeType !== 'app:folderlink')); } // todo: review once 1.10-beta6 is out diff --git a/src/app/components/current-user/current-user.component.html b/src/app/components/current-user/current-user.component.html index 29e77d41d..638f9230b 100644 --- a/src/app/components/current-user/current-user.component.html +++ b/src/app/components/current-user/current-user.component.html @@ -1,13 +1,11 @@
- - {{ userName }} - - {{ userInitials }} - - +
{{ userName }}
+
+ {{ userInitials }} +
diff --git a/src/app/components/current-user/current-user.component.scss b/src/app/components/current-user/current-user.component.scss index 7aae7f531..b301ce7bc 100644 --- a/src/app/components/current-user/current-user.component.scss +++ b/src/app/components/current-user/current-user.component.scss @@ -33,4 +33,17 @@ $am-avatar-dark-bg: rgba(black, .15); background: $am-avatar-dark-bg; } } + + .current-user__full-name { + width: 100px; + text-align: right; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + display:inline-block; + height: 20px; + font-size: 14px; + line-height: 1.43; + letter-spacing: -0.2px; + } } diff --git a/src/app/components/favorites/favorites.component.html b/src/app/components/favorites/favorites.component.html index 0a1fe1a3f..aaf50b5cb 100644 --- a/src/app/components/favorites/favorites.component.html +++ b/src/app/components/favorites/favorites.component.html @@ -23,6 +23,7 @@
diff --git a/src/app/components/favorites/favorites.component.spec.ts b/src/app/components/favorites/favorites.component.spec.ts index 91cba5c34..5c57cfaa9 100644 --- a/src/app/components/favorites/favorites.component.spec.ts +++ b/src/app/components/favorites/favorites.component.spec.ts @@ -179,6 +179,56 @@ describe('Favorites Routed Component', () => { }); }); + describe('edit option', () => { + it('should return false if a file node is selected', () => { + const selection = [ + { + entry: { + isFolder: false, + isFile: true + } + } + ]; + + const result = component.showEditOption(selection); + expect(result).toBe(false); + }); + + it('should return false if multiple nodes are selected', () => { + const selection = [ + { + entry: { + isFolder: true, + isFile: false + } + }, + { + entry: { + isFolder: true, + isFile: false + } + } + ]; + + const result = component.showEditOption(selection); + expect(result).toBe(false); + }); + + it('should return true if selected node is a folder', () => { + const selection = [ + { + entry: { + isFolder: true, + isFile: false + } + } + ]; + + const result = component.showEditOption(selection); + expect(result).toBe(true); + }); + }); + describe('refresh', () => { it('should call document list reload', () => { spyOn(component.documentList, 'reload'); diff --git a/src/app/components/favorites/favorites.component.ts b/src/app/components/favorites/favorites.component.ts index c1bb59778..6216a8e3a 100644 --- a/src/app/components/favorites/favorites.component.ts +++ b/src/app/components/favorites/favorites.component.ts @@ -19,8 +19,8 @@ import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core'; import { Router } from '@angular/router'; import { Subscription } from 'rxjs/Rx'; -import { MinimalNodeEntryEntity, PathElementEntity, PathInfo } from 'alfresco-js-api'; -import { ContentService, NodesApiService } from '@alfresco/adf-core'; +import { MinimalNodeEntryEntity, MinimalNodeEntity, PathElementEntity, PathInfo } from 'alfresco-js-api'; +import { ContentService, NodesApiService, UserPreferencesService } from '@alfresco/adf-core'; import { DocumentListComponent } from '@alfresco/adf-content-services'; import { ContentManagementService } from '../../common/services/content-management.service'; @@ -40,8 +40,9 @@ export class FavoritesComponent extends PageComponent implements OnInit, OnDestr private router: Router, private nodesApi: NodesApiService, private contentService: ContentService, - private content: ContentManagementService) { - super(); + private content: ContentManagementService, + preferences: UserPreferencesService) { + super(preferences); } ngOnInit() { @@ -92,6 +93,10 @@ export class FavoritesComponent extends PageComponent implements OnInit, OnDestr } } + showEditOption(selection: MinimalNodeEntity[]) { + return selection && selection.length === 1 && selection[0].entry.isFolder; + } + refresh(): void { if (this.documentList) { this.documentList.reload(); diff --git a/src/app/components/files/files.component.html b/src/app/components/files/files.component.html index 2341d840a..b2b693d66 100644 --- a/src/app/components/files/files.component.html +++ b/src/app/components/files/files.component.html @@ -84,9 +84,8 @@
+ [parentId]="node?.id" + [disabled]="!canCreateContent(node)"> @@ -145,8 +143,10 @@ + (change)="load(true, $event)" + (changePageSize)="onChangePageSize($event)"> diff --git a/src/app/components/files/files.component.ts b/src/app/components/files/files.component.ts index 651271e02..7b8847f02 100644 --- a/src/app/components/files/files.component.ts +++ b/src/app/components/files/files.component.ts @@ -19,7 +19,10 @@ import { Observable, Subscription } from 'rxjs/Rx'; import { Component, OnInit, OnDestroy, ChangeDetectorRef } from '@angular/core'; import { Router, ActivatedRoute, Params } from '@angular/router'; import { MinimalNodeEntity, MinimalNodeEntryEntity, PathElementEntity, NodePaging, PathElement } from 'alfresco-js-api'; -import { UploadService, FileUploadEvent, NodesApiService, ContentService, AlfrescoApiService } from '@alfresco/adf-core'; +import { + UploadService, FileUploadEvent, NodesApiService, + ContentService, AlfrescoApiService, UserPreferencesService +} from '@alfresco/adf-core'; import { BrowsingFilesService } from '../../common/services/browsing-files.service'; import { ContentManagementService } from '../../common/services/content-management.service'; @@ -47,8 +50,9 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy { private contentManagementService: ContentManagementService, private browsingFilesService: BrowsingFilesService, private contentService: ContentService, - private apiService: AlfrescoApiService) { - super(); + private apiService: AlfrescoApiService, + preferences: UserPreferencesService) { + super(preferences); } ngOnInit() { @@ -79,12 +83,12 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy { this.subscriptions = this.subscriptions.concat([ nodeActionsService.contentCopied.subscribe((nodes) => this.onContentCopied(nodes)), - contentService.folderCreate.subscribe(() => this.load()), - contentService.folderEdit.subscribe(() => this.load()), - contentManagementService.deleteNode.subscribe(() => this.load()), - contentManagementService.moveNode.subscribe(() => this.load()), - contentManagementService.restoreNode.subscribe(() => this.load()), - contentManagementService.toggleFavorite.subscribe(() => this.load()), + contentService.folderCreate.subscribe(() => this.load(false, this.pagination)), + contentService.folderEdit.subscribe(() => this.load(false, this.pagination)), + contentManagementService.deleteNode.subscribe(() => this.load(false, this.pagination)), + contentManagementService.moveNode.subscribe(() => this.load(false, this.pagination)), + contentManagementService.restoreNode.subscribe(() => this.load(false, this.pagination)), + contentManagementService.toggleFavorite.subscribe(() => this.load(false, this.pagination)), uploadService.fileUploadComplete.subscribe(file => this.onFileUploadedEvent(file)), uploadService.fileUploadDeleted.subscribe((file) => this.onFileUploadedEvent(file)) ]); @@ -140,7 +144,7 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy { onFileUploadedEvent(event: FileUploadEvent) { if (event && event.file.options.parentId === this.getParentNodeId()) { - this.load(); + this.load(false, this.pagination); } } @@ -150,7 +154,7 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy { return node && node.entry && node.entry.parentId === this.getParentNodeId(); }); if (newNode) { - this.load(); + this.load(false, this.pagination); } } @@ -167,12 +171,36 @@ export class FilesComponent extends PageComponent implements OnInit, OnDestroy { this.fetchNodes(this.getParentNodeId(), pagination) .subscribe( - (page) => this.onPageLoaded(page), + (page) => { + if (this.isCurrentPageEmpty(page) && this.isNotFirstPage(page)) { + const newSkipCount = pagination.skipCount - pagination.maxItems; + + this.fetchNodes(this.getParentNodeId(), {skipCount: newSkipCount, maxItems: pagination.maxItems}) + .subscribe( + (previousPage) => this.onPageLoaded(previousPage), + error => this.onFetchError(error) + ); + } else { + this.onPageLoaded(page); + } + }, error => this.onFetchError(error), () => this.changeDetector.detectChanges() ); } + isCurrentPageEmpty(page): boolean { + return !this.hasPageEntries(page); + } + + hasPageEntries(page): boolean { + return page && page.list && page.list.entries && page.list.entries.length > 0; + } + + isNotFirstPage(page): boolean { + return (page.list.pagination.skipCount >= page.list.pagination.maxItems); + } + // todo: review this approach once 5.2.3 is out private async updateCurrentNode(node: MinimalNodeEntryEntity) { this.nodePath = null; diff --git a/src/app/components/header/header.component.scss b/src/app/components/header/header.component.scss index 735e23a0c..e1ae95a52 100644 --- a/src/app/components/header/header.component.scss +++ b/src/app/components/header/header.component.scss @@ -4,7 +4,7 @@ &.adf-toolbar { .mat-toolbar { - background-color: #00bcd4; + background-color: #2196F3; font-family: inherit; min-height: $app-menu-height; height: $app-menu-height; @@ -30,7 +30,7 @@ .app-menu__title { width: 100px; - height: $app-menu-height; + height: 50px; margin-left: 5px; display: flex; justify-content: center; diff --git a/src/app/components/layout/layout.component.html b/src/app/components/layout/layout.component.html index 6170b7690..45e9a97f1 100644 --- a/src/app/components/layout/layout.component.html +++ b/src/app/components/layout/layout.component.html @@ -1,8 +1,7 @@
+ [parentId]="node?.id" + [disabled]="!canCreateContent(node)">
diff --git a/src/app/components/libraries/libraries.component.html b/src/app/components/libraries/libraries.component.html index 0de1d3bb0..076fb6d81 100644 --- a/src/app/components/libraries/libraries.component.html +++ b/src/app/components/libraries/libraries.component.html @@ -14,7 +14,6 @@ selectionMode="none" [navigate]="false" [sorting]="[ 'name', 'asc' ]" - [pageSize]="25" [contextMenuActions]="true" [contentActions]="false" (node-dblclick)="onNodeDoubleClick($event)"> @@ -65,5 +64,9 @@ + +
diff --git a/src/app/components/libraries/libraries.component.spec.ts b/src/app/components/libraries/libraries.component.spec.ts index 8e03c27bd..614454851 100644 --- a/src/app/components/libraries/libraries.component.spec.ts +++ b/src/app/components/libraries/libraries.component.spec.ts @@ -21,6 +21,7 @@ import { TestBed, async } from '@angular/core/testing'; import { Observable } from 'rxjs/Rx'; import { CoreModule , NodesApiService, AlfrescoApiService} from '@alfresco/adf-core'; +import { ShareDataTableAdapter } from '@alfresco/adf-content-services'; import { CommonModule } from '../../common/common.module'; import { LibrariesComponent } from './libraries.component'; @@ -101,30 +102,22 @@ describe('Libraries Routed Component', () => { it('sets title with id when duplicate nodes title exists in list', () => { node.title = 'title'; - component.documentList.data = { - page: { - list: { - entries: [{ entry: { id: 'some-id', title: 'title' } }] - } - } - }; + const data = new ShareDataTableAdapter(null); + data.setRows([{ node: { entry: { id: 'some-id', title: 'title' } } }]); + + component.documentList.data = data; const title = component.makeLibraryTitle(node); - expect(title).toContain('nodeId'); }); it('sets title when no duplicate nodes title exists in list', () => { node.title = 'title'; - component.documentList.data = { - page: { - list: { - entries: [{ entry: { id: 'some-id', title: 'title-some-id' } }] - } - } - }; + const data = new ShareDataTableAdapter(null); + data.setRows([{ node: { entry: { id: 'some-id', title: 'title-some-id' } } }]); + component.documentList.data = data; const title = component.makeLibraryTitle(node); expect(title).toBe('title'); diff --git a/src/app/components/libraries/libraries.component.ts b/src/app/components/libraries/libraries.component.ts index 8b34795d8..e6b141bc5 100644 --- a/src/app/components/libraries/libraries.component.ts +++ b/src/app/components/libraries/libraries.component.ts @@ -17,8 +17,8 @@ import { Component, ViewChild } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; -import { NodesApiService } from '@alfresco/adf-core'; -import { DocumentListComponent } from '@alfresco/adf-content-services'; +import { NodesApiService, UserPreferencesService } from '@alfresco/adf-core'; +import { DocumentListComponent, ShareDataRow } from '@alfresco/adf-content-services'; import { PageComponent } from '../page.component'; @@ -33,8 +33,9 @@ export class LibrariesComponent extends PageComponent { constructor( private nodesApi: NodesApiService, private route: ActivatedRoute, - private router: Router) { - super(); + private router: Router, + preferences: UserPreferencesService) { + super(preferences); } makeLibraryTooltip(library: any): string { @@ -44,13 +45,15 @@ export class LibrariesComponent extends PageComponent { } makeLibraryTitle(library: any): string { + const rows = this.documentList.data.getRows(); + const entries = rows.map((r: ShareDataRow) => r.node.entry); const { title, id } = library; - const { entries } = this.documentList.data.page.list; + let isDuplicate = false; if (entries) { isDuplicate = entries - .some(({ entry }: any) => { + .some((entry: any) => { return (entry.id !== id && entry.title === title); }); } diff --git a/src/app/components/location-link/location-link.component.ts b/src/app/components/location-link/location-link.component.ts index e9e83c114..cb09d809a 100644 --- a/src/app/components/location-link/location-link.component.ts +++ b/src/app/components/location-link/location-link.component.ts @@ -50,7 +50,12 @@ export class LocationLinkComponent implements OnInit { const parent = value.elements[value.elements.length - 1]; const area = isLibraryPath ? '/libraries' : '/personal-files'; - this.link = [ area, parent.id ]; + if (!isLibraryPath) { + this.link = [ area, parent.id ]; + } else { + // parent.id could be 'Site' folder or child as 'documentLibrary' + this.link = [ area, (parent.name === 'Sites' ? {} : parent.id) ]; + } } } } @@ -105,7 +110,7 @@ export class LocationLinkComponent implements OnInit { if (elements[0].name === 'Company Home') { elements[0].name = 'Personal Files'; - if (elements.length > 1) { + if (elements.length > 2) { if (elements[1].name === 'Sites') { const fragment = elements[2]; diff --git a/src/app/components/page.component.spec.ts b/src/app/components/page.component.spec.ts index fa45ed439..f8f922dff 100644 --- a/src/app/components/page.component.spec.ts +++ b/src/app/components/page.component.spec.ts @@ -23,7 +23,7 @@ class TestClass extends PageComponent { node: any; constructor() { - super(); + super(null); } fetchNodes(parentNodeId?: string, options?: any) { diff --git a/src/app/components/page.component.ts b/src/app/components/page.component.ts index 133ae97c7..5b19a8f1d 100644 --- a/src/app/components/page.component.ts +++ b/src/app/components/page.component.ts @@ -16,6 +16,7 @@ */ import { MinimalNodeEntity, MinimalNodeEntryEntity, NodePaging, Pagination } from 'alfresco-js-api'; +import { UserPreferencesService } from '@alfresco/adf-core'; export abstract class PageComponent { @@ -31,6 +32,9 @@ export abstract class PageComponent { abstract fetchNodes(parentNodeId?: string, options?: any): void; + constructor(private preferences: UserPreferencesService) { + } + onFetchError(error: any) { this.isLoading = false; } @@ -121,4 +125,8 @@ export abstract class PageComponent { return false; } + + onChangePageSize(event: Pagination): void { + this.preferences.paginationSize = event.maxItems; + } } diff --git a/src/app/components/recent-files/recent-files.component.html b/src/app/components/recent-files/recent-files.component.html index 4607b41c5..e6373f5a3 100644 --- a/src/app/components/recent-files/recent-files.component.html +++ b/src/app/components/recent-files/recent-files.component.html @@ -75,7 +75,6 @@ selectionMode="multiple" [navigate]="false" [sorting]="[ 'modifiedAt', 'desc' ]" - [pageSize]="25" [contextMenuActions]="true" [contentActions]="false" (node-dblclick)="onNodeDoubleClick($event.detail?.node?.entry)"> @@ -123,12 +122,17 @@ + + {{ value | adfTimeAgo }} + + +
diff --git a/src/app/components/recent-files/recent-files.component.ts b/src/app/components/recent-files/recent-files.component.ts index 6a32a6d6c..4444fe829 100644 --- a/src/app/components/recent-files/recent-files.component.ts +++ b/src/app/components/recent-files/recent-files.component.ts @@ -19,6 +19,7 @@ import { Subscription } from 'rxjs/Rx'; import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core'; import { Router } from '@angular/router'; import { MinimalNodeEntryEntity } from 'alfresco-js-api'; +import { UserPreferencesService } from '@alfresco/adf-core'; import { DocumentListComponent } from '@alfresco/adf-content-services'; import { ContentManagementService } from '../../common/services/content-management.service'; @@ -36,8 +37,9 @@ export class RecentFilesComponent extends PageComponent implements OnInit, OnDes constructor( private router: Router, - private content: ContentManagementService) { - super(); + private content: ContentManagementService, + preferences: UserPreferencesService) { + super(preferences); } ngOnInit() { diff --git a/src/app/components/shared-files/shared-files.component.html b/src/app/components/shared-files/shared-files.component.html index 142e62856..898f606ba 100644 --- a/src/app/components/shared-files/shared-files.component.html +++ b/src/app/components/shared-files/shared-files.component.html @@ -73,7 +73,6 @@ currentFolderId="-sharedlinks-" selectionMode="multiple" [sorting]="[ 'modifiedAt', 'desc' ]" - [pageSize]="25" [contextMenuActions]="true" [contentActions]="false" (node-dblclick)="onNodeDoubleClick($event.detail?.node?.entry)"> @@ -113,7 +112,6 @@ - + + {{ value | adfTimeAgo }} + + + diff --git a/src/app/components/shared-files/shared-files.component.ts b/src/app/components/shared-files/shared-files.component.ts index 129c885e7..47079bb0e 100644 --- a/src/app/components/shared-files/shared-files.component.ts +++ b/src/app/components/shared-files/shared-files.component.ts @@ -19,7 +19,7 @@ import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core'; import { Router } from '@angular/router'; import { Subscription } from 'rxjs/Rx'; import { MinimalNodeEntity } from 'alfresco-js-api'; -import { AlfrescoApiService } from '@alfresco/adf-core'; +import { AlfrescoApiService, UserPreferencesService } from '@alfresco/adf-core'; import { DocumentListComponent } from '@alfresco/adf-content-services'; import { ContentManagementService } from '../../common/services/content-management.service'; @@ -38,8 +38,9 @@ export class SharedFilesComponent extends PageComponent implements OnInit, OnDes constructor( private router: Router, private content: ContentManagementService, - private apiService: AlfrescoApiService) { - super(); + private apiService: AlfrescoApiService, + preferences: UserPreferencesService) { + super(preferences); } ngOnInit() { diff --git a/src/app/components/sidenav/sidenav.component.html b/src/app/components/sidenav/sidenav.component.html index 97fba621b..62cbc0269 100644 --- a/src/app/components/sidenav/sidenav.component.html +++ b/src/app/components/sidenav/sidenav.component.html @@ -21,7 +21,6 @@ @@ -81,9 +80,10 @@ + title="APP.DOCUMENT_LIST.COLUMNS.DELETED_ON"> + + {{ value | adfTimeAgo }} + + + diff --git a/src/app/components/trashcan/trashcan.component.spec.ts b/src/app/components/trashcan/trashcan.component.spec.ts index 5c257fc56..6257d8392 100644 --- a/src/app/components/trashcan/trashcan.component.spec.ts +++ b/src/app/components/trashcan/trashcan.component.spec.ts @@ -20,11 +20,13 @@ import { CoreModule, AlfrescoApiService } from '@alfresco/adf-core'; import { TrashcanComponent } from './trashcan.component'; import { CommonModule } from '../../common/common.module'; import { LocationLinkComponent } from '../location-link/location-link.component'; +import { ContentManagementService } from '../../common/services/content-management.service'; describe('TrashcanComponent', () => { let fixture; let component; let alfrescoApi: AlfrescoApiService; + let contentService: ContentManagementService; let page; beforeEach(() => { @@ -45,6 +47,9 @@ describe('TrashcanComponent', () => { declarations: [ LocationLinkComponent, TrashcanComponent + ], + providers: [ + ContentManagementService ] }) .compileComponents() @@ -53,6 +58,7 @@ describe('TrashcanComponent', () => { component = fixture.componentInstance; alfrescoApi = TestBed.get(AlfrescoApiService); + contentService = TestBed.get(ContentManagementService); component.documentList = { loadTrashcan: jasmine.createSpy('loadTrashcan'), @@ -65,6 +71,17 @@ describe('TrashcanComponent', () => { spyOn(alfrescoApi.nodesApi, 'getDeletedNodes').and.returnValue(Promise.resolve(page)); }); + describe('onRestoreNode()', () => { + it('should call refresh()', () => { + spyOn(component, 'refresh'); + fixture.detectChanges(); + + contentService.restoreNode.next(); + + expect(component.refresh).toHaveBeenCalled(); + }); + }); + describe('refresh()', () => { it('calls child component to reload', () => { component.refresh(); diff --git a/src/app/components/trashcan/trashcan.component.ts b/src/app/components/trashcan/trashcan.component.ts index a263f5dfc..1301e41d9 100644 --- a/src/app/components/trashcan/trashcan.component.ts +++ b/src/app/components/trashcan/trashcan.component.ts @@ -15,17 +15,39 @@ * limitations under the License. */ -import { Component, ViewChild } from '@angular/core'; +import { Component, ViewChild, OnInit, OnDestroy } from '@angular/core'; +import { Subscription } from 'rxjs/Rx'; +import { Pagination } from 'alfresco-js-api'; +import { UserPreferencesService } from '@alfresco/adf-core'; import { DocumentListComponent } from '@alfresco/adf-content-services'; +import { ContentManagementService } from '../../common/services/content-management.service'; @Component({ templateUrl: './trashcan.component.html' }) -export class TrashcanComponent { +export class TrashcanComponent implements OnInit, OnDestroy { + private subscriptions: Subscription[] = []; + @ViewChild(DocumentListComponent) documentList; + constructor( + private contentManagementService: ContentManagementService, + private preferences: UserPreferencesService) {} + + ngOnInit() { + this.subscriptions.push(this.contentManagementService.restoreNode.subscribe(() => this.refresh())); + } + refresh(): void { this.documentList.loadTrashcan(); this.documentList.resetSelection(); } + + ngOnDestroy() { + this.subscriptions.forEach(s => s.unsubscribe()); + } + + onChangePageSize(event: Pagination): void { + this.preferences.paginationSize = event.maxItems; + } } diff --git a/src/app/ui/overrides/_alfresco-document-list.scss b/src/app/ui/overrides/_alfresco-document-list.scss index ea0cd2627..718908bf1 100644 --- a/src/app/ui/overrides/_alfresco-document-list.scss +++ b/src/app/ui/overrides/_alfresco-document-list.scss @@ -1,4 +1,4 @@ -@import '../_variables.scss'; +@import '../variables'; adf-document-list { display: flex; @@ -25,6 +25,10 @@ adf-document-list { color: $alfresco-secondary-text-color; } + th.adf-data-table-cell--text:nth-child(n+3) { + text-align: center; + } + th, tr, td { &:focus { outline: none !important; @@ -96,7 +100,7 @@ adf-document-list { color: $alfresco-primary-text-color; &:hover { - color: $alfresco-app-color--default; + color: #2196F3; text-decoration: underline; } } diff --git a/yarn.lock b/yarn.lock index 26479febb..00dd32fe0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,11 +2,11 @@ # yarn lockfile v1 -"@alfresco/adf-content-services@2.0.0-beta2": - version "2.0.0-beta2" - resolved "https://registry.yarnpkg.com/@alfresco/adf-content-services/-/adf-content-services-2.0.0-beta2.tgz#7b587278b7bef852f6d543e1b667f9be5eaf3308" +"@alfresco/adf-content-services@2.0.0-c968fbcdacb90946dc00bca3d7b663de94d5a9ad": + version "2.0.0-c968fbcdacb90946dc00bca3d7b663de94d5a9ad" + resolved "https://registry.yarnpkg.com/@alfresco/adf-content-services/-/adf-content-services-2.0.0-c968fbcdacb90946dc00bca3d7b663de94d5a9ad.tgz#4d189cf2df9ff155c439805ad9ccea92181dc5b1" dependencies: - "@alfresco/adf-core" "2.0.0-beta2" + "@alfresco/adf-core" "2.0.0-c968fbcdacb90946dc00bca3d7b663de94d5a9ad" "@angular/animations" "5.0.0" "@angular/cdk" "5.0.0-rc0" "@angular/common" "5.0.0" @@ -21,7 +21,7 @@ "@angular/platform-browser-dynamic" "5.0.0" "@angular/router" "5.0.0" "@ngx-translate/core" "8.0.0" - alfresco-js-api "2.0.0-beta1" + alfresco-js-api "2.0.0-2e4554a3d3a1265d058795c924d55d570dd46de2" chart.js "2.5.0" core-js "2.4.1" hammerjs "2.0.8" @@ -35,9 +35,9 @@ systemjs "0.19.27" zone.js "0.8.14" -"@alfresco/adf-core@2.0.0-beta2": - version "2.0.0-beta2" - resolved "https://registry.yarnpkg.com/@alfresco/adf-core/-/adf-core-2.0.0-beta2.tgz#3b38b8fa9339d94bde48d8866e797bb0e4d5cc24" +"@alfresco/adf-core@2.0.0-c968fbcdacb90946dc00bca3d7b663de94d5a9ad": + version "2.0.0-c968fbcdacb90946dc00bca3d7b663de94d5a9ad" + resolved "https://registry.yarnpkg.com/@alfresco/adf-core/-/adf-core-2.0.0-c968fbcdacb90946dc00bca3d7b663de94d5a9ad.tgz#bbf289ed254049f7db0406efd94276fa53a9fbf0" dependencies: "@angular/animations" "5.0.0" "@angular/cdk" "5.0.0-rc0" @@ -53,7 +53,7 @@ "@angular/platform-browser-dynamic" "5.0.0" "@angular/router" "5.0.0" "@ngx-translate/core" "8.0.0" - alfresco-js-api "2.0.0-beta1" + alfresco-js-api "2.0.0-2e4554a3d3a1265d058795c924d55d570dd46de2" chart.js "2.5.0" core-js "2.4.1" hammerjs "2.0.8" @@ -76,17 +76,17 @@ typescript "^2.3.3" webpack-sources "^1.0.1" -"@angular-devkit/core@0.0.20": - version "0.0.20" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-0.0.20.tgz#2ad36dd210fccd0e156d01c6499082ad4cd8c2af" +"@angular-devkit/core@0.0.21": + version "0.0.21" + resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-0.0.21.tgz#e2ba4ac0a4e1156f884c083b15eb1c26ddfb2ba8" dependencies: source-map "^0.5.6" "@angular-devkit/schematics@~0.0.34": - version "0.0.36" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-0.0.36.tgz#9f351317759774f930c2d8cef90f87cf02f69cef" + version "0.0.37" + resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-0.0.37.tgz#d86ff93b14a44b766e72bcea1569b5d36063210e" dependencies: - "@angular-devkit/core" "0.0.20" + "@angular-devkit/core" "0.0.21" "@ngtools/json-schema" "^1.1.0" minimist "^1.2.0" rxjs "^5.5.2" @@ -266,10 +266,10 @@ resolved "https://registry.yarnpkg.com/@ngx-translate/core/-/core-8.0.0.tgz#751fd6b512d80f3a748d2de8dfc96dfefa29afe0" "@schematics/angular@~0.1.0": - version "0.1.5" - resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-0.1.5.tgz#501ef8bd7319e1b57486ee2a48720d2f3da7ee13" + version "0.1.7" + resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-0.1.7.tgz#2306aeec118ca185e180882eff54f5116de4ef05" dependencies: - "@angular-devkit/core" "0.0.20" + "@angular-devkit/core" "0.0.21" "@types/jasmine@*", "@types/jasmine@^2.5.53": version "2.8.2" @@ -356,14 +356,21 @@ ajv@^4.9.1: json-stable-stringify "^1.0.1" ajv@^5.0.0, ajv@^5.1.0, ajv@^5.1.5: - version "5.3.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.3.0.tgz#4414ff74a50879c208ee5fdc826e32c303549eda" + version "5.5.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.0.tgz#eb2840746e9dc48bd5e063a36e3fd400c5eab5a9" dependencies: co "^4.6.0" fast-deep-equal "^1.0.0" fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" +alfresco-js-api@2.0.0-2e4554a3d3a1265d058795c924d55d570dd46de2: + version "2.0.0-2e4554a3d3a1265d058795c924d55d570dd46de2" + resolved "https://registry.yarnpkg.com/alfresco-js-api/-/alfresco-js-api-2.0.0-2e4554a3d3a1265d058795c924d55d570dd46de2.tgz#9b4cafb16eef618ed449c2d69055cb032cbe9087" + dependencies: + event-emitter "0.3.4" + superagent "3.7.0" + alfresco-js-api@2.0.0-beta1: version "2.0.0-beta1" resolved "https://registry.yarnpkg.com/alfresco-js-api/-/alfresco-js-api-2.0.0-beta1.tgz#f52f8dfb82e3516efd71868f06cf91c62e4fa71e" @@ -604,7 +611,7 @@ babel-messages@^6.23.0: dependencies: babel-runtime "^6.22.0" -babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: +babel-runtime@^6.22.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" dependencies: @@ -942,8 +949,8 @@ caniuse-api@^1.5.2: lodash.uniq "^4.5.0" caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000766" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000766.tgz#4c911aa3747f01388452fa4b927b78fcf1430680" + version "1.0.30000772" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000772.tgz#51aae891768286eade4a3d8319ea76d6a01b512b" caseless@~0.11.0: version "0.11.0" @@ -1169,15 +1176,15 @@ combined-stream@^1.0.5, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -commander@2.11.x, commander@^2.9.0, commander@~2.11.0: - version "2.11.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" +commander@2.12.x, commander@^2.9.0, commander@~2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.12.1.tgz#468635c4168d06145b9323356d1da84d14ac4a7a" common-tags@^1.3.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.4.0.tgz#1187be4f3d4cf0c0427d43f74eef1f73501614c0" + version "1.5.1" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.5.1.tgz#e2e39931a013cd02253defeed89a1ad615a27f07" dependencies: - babel-runtime "^6.18.0" + babel-runtime "^6.26.0" commondir@^1.0.1: version "1.0.1" @@ -1265,8 +1272,8 @@ content-type@~1.0.4: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" convert-source-map@^1.3.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" + version "1.5.1" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" cookie-signature@1.0.6: version "1.0.6" @@ -1292,17 +1299,15 @@ copy-concurrently@^1.0.0: run-queue "^1.0.0" copy-webpack-plugin@^4.1.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.2.1.tgz#4017366b2d815bd2997e7d6b2b2446f719f486a1" + version "4.2.3" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.2.3.tgz#4a3c61089f3b635777f0f0af346c338b39d63755" dependencies: bluebird "^3.5.1" - fs-extra "^4.0.2" glob "^7.1.2" is-glob "^4.0.0" loader-utils "^0.2.15" lodash "^4.3.0" minimatch "^3.0.4" - node-dir "^0.1.10" core-js@2.4.1: version "2.4.1" @@ -1556,7 +1561,7 @@ debug@*, debug@^3.1.0: dependencies: ms "2.0.0" -debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: +debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.6.6, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -1660,8 +1665,8 @@ detect-indent@^4.0.0: repeating "^2.0.0" detect-libc@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.2.tgz#71ad5d204bf17a6a6ca8f450c61454066ef461e1" + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" detect-node@^2.0.3: version "2.0.3" @@ -1887,8 +1892,8 @@ error-ex@^1.2.0: is-arrayish "^0.2.1" es-abstract@^1.7.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.9.0.tgz#690829a07cae36b222e7fd9b75c0d0573eb25227" + version "1.10.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864" dependencies: es-to-primitive "^1.1.1" function-bind "^1.1.1" @@ -1905,8 +1910,8 @@ es-to-primitive@^1.1.1: is-symbol "^1.0.1" es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.7: - version "0.10.35" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.35.tgz#18ee858ce6a3c45c7d79e91c15fcca9ec568494f" + version "0.10.37" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.37.tgz#0ee741d148b80069ba27d020393756af257defc3" dependencies: es6-iterator "~2.0.1" es6-symbol "~3.1.1" @@ -2260,10 +2265,10 @@ flush-write-stream@^1.0.0: readable-stream "^2.0.4" follow-redirects@>=1.2.0: - version "1.2.5" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.5.tgz#ffd3e14cbdd5eaa72f61b6368c1f68516c2a26cc" + version "1.2.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.6.tgz#4dcdc7e4ab3dd6765a97ff89c3b4c258117c79bf" dependencies: - debug "^2.6.9" + debug "^3.1.0" for-in@^0.1.3: version "0.1.8" @@ -2353,7 +2358,7 @@ fs-extra@^0.26.5: path-is-absolute "^1.0.0" rimraf "^2.2.8" -fs-extra@^4.0.0, fs-extra@^4.0.2: +fs-extra@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.2.tgz#f91704c53d1b461f893452b0c307d9997647ab6b" dependencies: @@ -2708,17 +2713,17 @@ html-entities@^1.2.0: resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" html-minifier@^3.2.3: - version "3.5.6" - resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.6.tgz#7e4e661a09999599c7d8e8a2b8d7fb7430bb5c3e" + version "3.5.7" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.7.tgz#511e69bb5a8e7677d1012ebe03819aa02ca06208" dependencies: camel-case "3.0.x" clean-css "4.1.x" - commander "2.11.x" + commander "2.12.x" he "1.1.x" ncname "1.0.x" param-case "2.1.x" relateurl "0.2.x" - uglify-js "3.1.x" + uglify-js "3.2.x" html-webpack-plugin@^2.29.0: version "2.30.1" @@ -2882,8 +2887,8 @@ inherits@2.0.1: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" ini@^1.3.4, ini@~1.3.0: - version "1.3.4" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" internal-ip@1.2.0: version "1.2.0" @@ -3733,10 +3738,14 @@ mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.16, dependencies: mime-db "~1.30.0" -mime@1.4.1, mime@^1.2.11, mime@^1.3.4, mime@^1.4.1: +mime@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" +mime@^1.2.11, mime@^1.3.4, mime@^1.4.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + mimic-fn@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18" @@ -3795,14 +3804,20 @@ mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkd dependencies: minimist "0.0.8" +moment-es6@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/moment-es6/-/moment-es6-1.0.0.tgz#552fcf405d62565b0a1fe84e6c1e697ac79332df" + dependencies: + moment "*" + +moment@*, moment@^2.10.6: + version "2.19.2" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.2.tgz#8a7f774c95a64550b4c7ebd496683908f9419dbe" + moment@2.15.2: version "2.15.2" resolved "https://registry.yarnpkg.com/moment/-/moment-2.15.2.tgz#1bfdedf6a6e345f322fe956d5df5bd08a8ce84dc" -moment@^2.10.6: - version "2.19.2" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.2.tgz#8a7f774c95a64550b4c7ebd496683908f9419dbe" - moment@~2.18.0: version "2.18.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f" @@ -3835,8 +3850,8 @@ multicast-dns-service-types@^1.1.0: resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" multicast-dns@^6.0.1: - version "6.2.0" - resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.0.tgz#13f22d0c32dc5ee82a32878e3c380d875b3eab22" + version "6.2.1" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.1.tgz#c5035defa9219d30640558a49298067352098060" dependencies: dns-packet "^1.0.1" thunky "^0.1.0" @@ -3867,12 +3882,6 @@ no-case@^2.2.0: dependencies: lower-case "^1.1.1" -node-dir@^0.1.10: - version "0.1.17" - resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" - dependencies: - minimatch "^3.0.2" - node-ensure@^0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/node-ensure/-/node-ensure-0.0.0.tgz#ecae764150de99861ec5c810fd5d096b183932a7" @@ -3956,8 +3965,8 @@ node-rest-client@^3.1.0: xml2js ">=0.2.4" node-sass@^4.3.0: - version "4.7.1" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.7.1.tgz#bec978ab33b5cf56825bf72922323a56ebaf4f66" + version "4.7.2" + resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.7.2.tgz#9366778ba1469eb01438a9e8592f4262bcb6794e" dependencies: async-foreach "^0.1.3" chalk "^1.1.1" @@ -5938,17 +5947,17 @@ typescript@~2.4.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.2.tgz#f8395f85d459276067c988aa41837a8f82870844" uglify-es@^3.1.3: - version "3.1.10" - resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.1.10.tgz#f1840c3b52771d17555a02ce158cf46f689384bd" + version "3.2.0" + resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.2.0.tgz#fbbfb9dc465ec7e5065701b9720d0de977d0bc24" dependencies: - commander "~2.11.0" + commander "~2.12.1" source-map "~0.6.1" -uglify-js@3.1.x: - version "3.1.10" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.1.10.tgz#c4a5f9b5c6276b40cb971c1d97c9eeb26af9509c" +uglify-js@3.2.x: + version "3.2.0" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.2.0.tgz#cb411ee4ca0e0cadbfe3a4e1a1da97e6fa0d19c1" dependencies: - commander "~2.11.0" + commander "~2.12.1" source-map "~0.6.1" uglify-js@^2.6, uglify-js@^2.8.29: @@ -6203,11 +6212,11 @@ webpack-core@^0.6.8: source-map "~0.4.1" webpack-dev-middleware@^1.11.0, webpack-dev-middleware@~1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.0.tgz#d34efefb2edda7e1d3b5dbe07289513219651709" + version "1.12.1" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.1.tgz#338be3ca930973be1c2ce07d84d275e997e1a25a" dependencies: memory-fs "~0.4.1" - mime "^1.3.4" + mime "^1.4.1" path-is-absolute "^1.0.0" range-parser "^1.0.3" time-stamp "^2.0.0" @@ -6251,8 +6260,8 @@ webpack-merge@^4.1.0: lodash "^4.17.4" webpack-sources@^1.0.0, webpack-sources@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.0.2.tgz#d0148ec083b3b5ccef1035a6b3ec16442983b27a" + version "1.1.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" dependencies: source-list-map "^2.0.0" source-map "~0.6.1"