diff --git a/ng2-components/ng2-alfresco-documentlist/src/assets/alfresco.service.mock.ts b/ng2-components/ng2-alfresco-documentlist/src/assets/alfresco.service.mock.ts index 96419bfdcd..95ea8be10d 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/assets/alfresco.service.mock.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/assets/alfresco.service.mock.ts @@ -25,7 +25,9 @@ import { export class AlfrescoServiceMock extends AlfrescoService { - _folderToReturn: any = {}; + folderToReturn: any = {}; + getFolderReject: boolean = false; + getFolderRejectError: string = 'Error'; constructor( settings?: AlfrescoSettingsService, @@ -36,8 +38,11 @@ export class AlfrescoServiceMock extends AlfrescoService { } getFolder(folder: string) { + if (this.getFolderReject) { + return Observable.throw(this.getFolderRejectError); + } return Observable.create(observer => { - observer.next(this._folderToReturn); + observer.next(this.folderToReturn); observer.complete(); }); } diff --git a/ng2-components/ng2-alfresco-documentlist/src/assets/document-library.model.mock.ts b/ng2-components/ng2-alfresco-documentlist/src/assets/document-library.model.mock.ts index 37c454366a..f29c1f8121 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/assets/document-library.model.mock.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/assets/document-library.model.mock.ts @@ -20,12 +20,15 @@ import { MinimalNodeEntity, MinimalNodeEntryEntity, PathInfoEntity, - ContentInfo + ContentInfo, + NodePagingList } from '../models/document-library.model'; export class PageNode extends NodePaging { - constructor() { + constructor(entries?: MinimalNodeEntity[]) { super(); + this.list = new NodePagingList(); + this.list.entries = entries || []; } } diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-action-list.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-action-list.spec.ts new file mode 100644 index 0000000000..647e8d0586 --- /dev/null +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-action-list.spec.ts @@ -0,0 +1,67 @@ +/*! + * @license + * Copyright 2016 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + it, + describe, + expect, + beforeEach +} from '@angular/core/testing'; + +import { DocumentList } from './document-list'; +import { AlfrescoServiceMock } from '../assets/alfresco.service.mock'; +import { ContentActionModel } from './../models/content-action.model'; +import { ContentActionList } from './content-action-list'; + +describe('ContentColumnList', () => { + + let documentList: DocumentList; + let actionList: ContentActionList; + + beforeEach(() => { + let alfrescoServiceMock = new AlfrescoServiceMock(); + documentList = new DocumentList(alfrescoServiceMock, null); + actionList = new ContentActionList(documentList); + }); + + it('should register action', () => { + spyOn(documentList.actions, 'push').and.callThrough(); + + let action = new ContentActionModel(); + let result = actionList.registerAction(action); + + expect(result).toBeTruthy(); + expect(documentList.actions.push).toHaveBeenCalledWith(action); + }); + + it('should require document list instance to register action', () => { + actionList = new ContentActionList(null); + let action = new ContentActionModel(); + expect(actionList.registerAction(action)).toBeFalsy(); + }); + + it('should require action instance to register', () => { + spyOn(documentList.actions, 'push').and.callThrough(); + let result = actionList.registerAction(null); + + expect(result).toBeFalsy(); + expect(documentList.actions.push).not.toHaveBeenCalled(); + }); + +}); + + diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-action-list.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-action-list.ts index 7f8cef1d1b..2dd1f15ff4 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/content-action-list.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-action-list.ts @@ -33,9 +33,11 @@ export class ContentActionList { * Registers action handler within the parent document list component. * @param action Action model to register. */ - registerAction(action: ContentActionModel): void { + registerAction(action: ContentActionModel): boolean { if (this.documentList && action) { this.documentList.actions.push(action); + return true; } + return false; } } diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-action.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-action.spec.ts index 08d260980c..0f42649e01 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/content-action.spec.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-action.spec.ts @@ -30,6 +30,7 @@ import { ContentAction } from './content-action'; import { DocumentActionsService } from '../services/document-actions.service'; import { FolderActionsService } from '../services/folder-actions.service'; import { ContentActionHandler } from '../models/content-action.model'; +import { FileNode } from '../assets/document-library.model.mock'; describe('ContentAction', () => { @@ -225,4 +226,47 @@ describe('ContentAction', () => { expect(documentActions.getHandler).not.toHaveBeenCalled(); }); + + it('should wire model with custom event handler', (done) => { + let action = new ContentAction(actionList, documentActions, folderActions); + let file = new FileNode(); + + let handler = new EventEmitter(); + handler.subscribe((e) => { + expect(e.value).toBe(file); + done(); + }); + + action.execute = handler; + + action.ngOnInit(); + action.model.handler(file); + }); + + it('should allow registering model without handler', () => { + let action = new ContentAction(actionList, documentActions, folderActions); + + spyOn(actionList, 'registerAction').and.callThrough(); + action.execute = null; + action.ngOnInit(); + + expect(action.model.handler).toBeUndefined(); + expect(actionList.registerAction).toHaveBeenCalledWith(action.model); + }); + + it('should register on init', () => { + let action = new ContentAction(actionList, null, null); + spyOn(action, 'register').and.callThrough(); + + action.ngOnInit(); + expect(action.register).toHaveBeenCalled(); + }); + + it('should require action list to register action with', () => { + let action = new ContentAction(actionList, null, null); + expect(action.register()).toBeTruthy(); + + action = new ContentAction(null, null, null); + expect(action.register()).toBeFalsy(); + }); }); diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-action.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-action.ts index 33164fdea5..2c0f2859a6 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/content-action.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-action.ts @@ -73,7 +73,14 @@ export class ContentAction implements OnInit, OnChanges { }; } - this.list.registerAction(this.model); + this.register(); + } + + register(): boolean { + if (this.list) { + return this.list.registerAction(this.model); + } + return false; } ngOnChanges(changes) { diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.spec.ts index 00f9947c0f..0d6b52e8e5 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.spec.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.spec.ts @@ -41,9 +41,24 @@ describe('ContentColumnList', () => { it('should register column within parent document list', () => { expect(documentList.columns.length).toBe(0); - columnList.registerColumn(new ContentColumnModel()); + let result = columnList.registerColumn(new ContentColumnModel()); + expect(result).toBeTruthy(); expect(documentList.columns.length).toBe(1); }); + it('should require document list instance to register action', () => { + columnList = new ContentColumnList(null); + let col = new ContentColumnModel(); + expect(columnList.registerColumn(col)).toBeFalsy(); + }); + + it('should require action instance to register', () => { + spyOn(documentList.actions, 'push').and.callThrough(); + let result = columnList.registerColumn(null); + + expect(result).toBeFalsy(); + expect(documentList.actions.push).not.toHaveBeenCalled(); + }); + }); diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.ts index 73afe4fccd..b6b126227f 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-column-list.ts @@ -33,9 +33,11 @@ export class ContentColumnList { * Registers column model within the parent document list component. * @param column Column definition model to register. */ - registerColumn(column: ContentColumnModel): void { + registerColumn(column: ContentColumnModel): boolean { if (this.documentList && column) { this.documentList.columns.push(column); + return true; } + return false; } } diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-column.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-column.spec.ts index 9f335ec6a3..f44502c9f8 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/content-column.spec.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-column.spec.ts @@ -93,4 +93,20 @@ describe('ContentColumn', () => { expect(column.model.title).toBe('title2'); }); + it('should register on init', () => { + let column = new ContentColumn(columnList); + spyOn(column, 'register').and.callThrough(); + + column.ngOnInit(); + expect(column.register).toHaveBeenCalled(); + }); + + it('should require action list to register action with', () => { + let column = new ContentColumn(columnList); + expect(column.register()).toBeTruthy(); + + column = new ContentColumn(null); + expect(column.register()).toBeFalsy(); + }); + }); diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/content-column.ts b/ng2-components/ng2-alfresco-documentlist/src/components/content-column.ts index 1599d765f6..3b0c9d40ff 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/content-column.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/content-column.ts @@ -66,9 +66,14 @@ export class ContentColumn implements OnInit, OnChanges { this.model.srTitle = 'Thumbnail'; } + this.register(); + } + + register(): boolean { if (this.list) { - this.list.registerColumn(this.model); + return this.list.registerColumn(this.model); } + return false; } ngOnChanges(change) { diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list-breadcrumb.component.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/components/document-list-breadcrumb.component.spec.ts index 9b1ba0fde1..47a688a106 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list-breadcrumb.component.spec.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list-breadcrumb.component.spec.ts @@ -106,4 +106,27 @@ describe('DocumentListBreadcrumb', () => { expect(documentList.currentFolderPath).toBe(node.path); }); + it('should do nothing for same path', () => { + let called = 0; + + component.pathChanged.subscribe(() => called++); + + component.currentFolderPath = '/'; + component.currentFolderPath = '/'; + + expect(called).toBe(0); + }); + + it('should emit path changed event', (done) => { + let path = '/some/path'; + + component.pathChanged.subscribe(e => { + expect(e.value).toBe(path); + expect(e.route).toBe(component.route); + done(); + }); + + component.currentFolderPath = path; + }); + }); diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list-breadcrumb.component.ts b/ng2-components/ng2-alfresco-documentlist/src/components/document-list-breadcrumb.component.ts index 5bc4990259..40e1c13308 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list-breadcrumb.component.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list-breadcrumb.component.ts @@ -49,6 +49,10 @@ export class DocumentListBreadcrumb { this._currentFolderPath = this.rootFolder.path; this.route = [ this.rootFolder ]; } + this.pathChanged.emit({ + value: this._currentFolderPath, + route: this.route + }); } } @@ -65,6 +69,9 @@ export class DocumentListBreadcrumb { @Output() navigate: EventEmitter = new EventEmitter(); + @Output() + pathChanged: EventEmitter = new EventEmitter(); + onRoutePathClick(route: PathNode, e?: Event) { if (e) { e.preventDefault(); diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.spec.ts b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.spec.ts index c9d89495d0..67b9d6cc42 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.spec.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.spec.ts @@ -86,7 +86,7 @@ describe('DocumentList', () => { let folder = { 'nodeRef': 'workspace://SpacesStore/8bb36efb-c26d-4d2b-9199-ab6922f53c28' }; - alfrescoServiceMock._folderToReturn = folder; + alfrescoServiceMock.folderToReturn = folder; documentList.ngOnInit(); expect(documentList.folder).toBe(folder); @@ -672,7 +672,6 @@ describe('DocumentList', () => { let value = documentList.getCellValue(file, col); expect(value).toBe(dateValue); - }); it('should return date value as string', () => { @@ -703,4 +702,204 @@ describe('DocumentList', () => { expect(value).toBe(url); }); + it('should require path to display folder content', () => { + spyOn(alfrescoServiceMock, 'getFolder').and.callThrough(); + + documentList.displayFolderContent(null); + documentList.displayFolderContent(''); + + expect(alfrescoServiceMock.getFolder).not.toHaveBeenCalled(); + }); + + it('should require node to resolve context menu actions', () => { + expect(documentList.getContextActions(null)).toBeNull(); + + let file = new FileNode(); + file.entry = null; + + expect(documentList.getContextActions(file)).toBeNull(); + }); + + it('should fetch context menu actions for a file node', () => { + let actionModel = {}; + spyOn(documentList, 'getContentActions').and.returnValue([actionModel]); + + let file = new FileNode(); + let actions = documentList.getContextActions(file); + + expect(documentList.getContentActions).toHaveBeenCalledWith('document', 'menu'); + expect(actions.length).toBe(1); + expect(actions[0].model).toBe(actionModel); + expect(actions[0].node).toBe(file); + expect(actions[0].subject).toBe(documentList.contextActionHandler); + }); + + it('should fetch context menu actions for a folder node', () => { + let actionModel = {}; + spyOn(documentList, 'getContentActions').and.returnValue([actionModel]); + + let folder = new FolderNode(); + let actions = documentList.getContextActions(folder); + + expect(documentList.getContentActions).toHaveBeenCalledWith('folder', 'menu'); + expect(actions.length).toBe(1); + expect(actions[0].model).toBe(actionModel); + expect(actions[0].node).toBe(folder); + expect(actions[0].subject).toBe(documentList.contextActionHandler); + }); + + it('should fetch no context menu actions for unknown type', () => { + spyOn(documentList, 'getContentActions').and.stub(); + + let node = new FileNode(); + node.entry.isFile = false; + node.entry.isFolder = false; + + let actions = documentList.getContextActions(node); + + expect(documentList.getContentActions).not.toHaveBeenCalled(); + expect(actions).toBeNull(); + }); + + it('should return null value when no content actions found', () => { + spyOn(documentList, 'getContentActions').and.returnValue([]); + + let file = new FileNode(); + let actions = documentList.getContextActions(file); + + expect(actions).toBeNull(); + expect(documentList.getContentActions).toHaveBeenCalled(); + }); + + it('should update error message when folder content display fails', () => { + let error = 'My Error'; + alfrescoServiceMock.getFolderReject = true; + alfrescoServiceMock.getFolderRejectError = error; + + documentList.displayFolderContent('/some/path'); + expect(documentList.errorMessage).toBe(error); + }); + + it('should get object value via property path', () => { + let obj = { + name: { + firstName: '' + } + }; + + expect(documentList.getObjectValue(obj, 'name.firstName')).toBe(''); + }); + + it('should not get object value via invalid path', () => { + expect(documentList.getObjectValue({}, 'some.missing.path')).toBeUndefined(); + }); + + it('should log error when having date conversion issues', () => { + + let value = ''; + let file = new FileNode(); + file.entry.createdAt = value; + + let col = new ContentColumnModel({ + source: 'createdAt', + type: 'date', + format: 'medium' + }); + + spyOn(console, 'error').and.stub(); + + let result = documentList.getCellValue(file, col); + + expect(result).toBe(value); + expect(console.error).toHaveBeenCalledWith(`DocumentList: error parsing date ${value} to format ${col.format}`); + }); + + it('should convert thumbnail if column source defined', () => { + let file = new FileNode(); + let col = new ContentColumnModel({ + source: 'name', + type: 'image' + }); + + expect(documentList.getCellValue(file, col)).toBe(file.entry.name); + }); + + it('should require current folder path to reload', () => { + + // Redefine 'currentFolderPath' to disable native setter validation + Object.defineProperty(documentList, 'currentFolderPath', { + value: null + }); + expect(documentList.currentFolderPath).toBeNull(); + + spyOn(documentList, 'displayFolderContent').and.stub(); + + documentList.reload(); + + expect(documentList.displayFolderContent).not.toHaveBeenCalled(); + }); + + it('should not sort empty page', () => { + let page = new PageNode(); + spyOn(page.list.entries, 'sort').and.stub(); + + documentList.sort(page, null); + expect(page.list.entries.sort).not.toHaveBeenCalled(); + }); + + it('should put folders to top on sort', () => { + let folder = new FolderNode(); + let file1 = new FileNode('file1'); + let file2 = new FileNode('file2'); + let page = new PageNode([file1, file2, folder]); + + // asc + documentList.sort(page, new ColumnSortingModel({ + key: 'name', + direction: 'asc' + })); + + expect(page.list.entries[0]).toBe(folder); + expect(page.list.entries[1]).toBe(file1); + expect(page.list.entries[2]).toBe(file2); + + // desc + documentList.sort(page, new ColumnSortingModel({ + key: 'name', + direction: 'desc' + })); + + expect(page.list.entries[0]).toBe(folder); + expect(page.list.entries[1]).toBe(file2); + expect(page.list.entries[2]).toBe(file1); + }); + + it('should sort by dates up to ms', () => { + let file1 = new FileNode(); + file1.entry['dateProp'] = new Date(2016, 6, 30, 13, 14, 1); + + let file2 = new FileNode(); + file2.entry['dateProp'] = new Date(2016, 6, 30, 13, 14, 2); + + let page = new PageNode([file1, file2]); + + // desc + documentList.sort(page, new ColumnSortingModel({ + key: 'dateProp', + direction: 'desc' + })); + + expect(page.list.entries[0]).toBe(file2); + expect(page.list.entries[1]).toBe(file1); + + // asc + documentList.sort(page, new ColumnSortingModel({ + key: 'dateProp', + direction: 'asc' + })); + + expect(page.list.entries[0]).toBe(file1); + expect(page.list.entries[1]).toBe(file2); + }); + }); diff --git a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.ts b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.ts index 9c993ec516..f9f9d53a13 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/components/document-list.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/components/document-list.ts @@ -319,7 +319,7 @@ export class DocumentList implements OnInit, AfterViewChecked, AfterContentInit, } displayFolderContent(path: string) { - if (path !== null) { + if (path) { this.alfrescoService .getFolder(path) .subscribe( diff --git a/ng2-components/ng2-alfresco-documentlist/src/models/column-sorting.model.ts b/ng2-components/ng2-alfresco-documentlist/src/models/column-sorting.model.ts index 10c0dc9123..6f1f3b6dd5 100644 --- a/ng2-components/ng2-alfresco-documentlist/src/models/column-sorting.model.ts +++ b/ng2-components/ng2-alfresco-documentlist/src/models/column-sorting.model.ts @@ -16,6 +16,16 @@ */ export class ColumnSortingModel { + + static DEFAULT_DIRECTION: string = 'asc'; + key: string; - direction: string = 'asc'; + direction: string = ColumnSortingModel.DEFAULT_DIRECTION; + + constructor(opts?: any) { + if (opts) { + this.key = opts.key; + this.direction = opts.direction || ColumnSortingModel.DEFAULT_DIRECTION; + } + } }