alfresco-ng2-components/lib/content-services/content-node-selector/content-node-selector.component.spec.ts
Vito 141bc0f8b4 [ADF-1805] rebased documentlist pagination removal (#2668)
* [ADF-1805] rebased documentlist pagination removal

* [ADF-1805] fixed some wrong changes]
2017-11-20 10:40:05 +00:00

680 lines
30 KiB
TypeScript

/*!
* @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 { CUSTOM_ELEMENTS_SCHEMA, DebugElement, EventEmitter } from '@angular/core';
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';
import { By } from '@angular/platform-browser';
import { MinimalNodeEntryEntity } from 'alfresco-js-api';
import { ContentService, TranslationService, SearchService, SiteModel, SitesApiService, UserPreferencesService } from '@alfresco/adf-core';
import { DataTableModule } from '@alfresco/adf-core';
import { Observable } from 'rxjs/Rx';
import { MaterialModule } from '../material.module';
import { EmptyFolderContentDirective, DocumentListComponent, DocumentListService } from '../document-list';
import { DropdownSitesComponent } from '../site-dropdown';
import { DropdownBreadcrumbComponent } from '../breadcrumb';
import { ContentNodeSelectorComponent } from './content-node-selector.component';
import { ContentNodeSelectorService } from './content-node-selector.service';
const ONE_FOLDER_RESULT = {
list: {
entries: [
{
entry: {
id: '123', name: 'MyFolder', isFile: false, isFolder: true,
createdByUser: { displayName: 'John Doe' },
modifiedByUser: { displayName: 'John Doe' }
}
}
],
pagination: {
hasMoreItems: true
}
}
};
describe('ContentNodeSelectorComponent', () => {
let component: ContentNodeSelectorComponent;
let fixture: ComponentFixture<ContentNodeSelectorComponent>;
let element: DebugElement;
let data: any;
let searchService: SearchService;
let searchSpy: jasmine.Spy;
let _resolve: Function;
let _reject: Function;
function typeToSearchBox(searchTerm = 'string-to-search') {
let searchInput = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-input"]'));
searchInput.nativeElement.value = searchTerm;
searchInput.triggerEventHandler('input', {});
fixture.detectChanges();
}
function respondWithSearchResults(result) {
_resolve(result);
}
function setupTestbed(plusProviders) {
TestBed.configureTestingModule({
imports: [
DataTableModule,
MaterialModule
],
declarations: [
DocumentListComponent,
EmptyFolderContentDirective,
DropdownSitesComponent,
DropdownBreadcrumbComponent,
ContentNodeSelectorComponent
],
providers: [
ContentService,
SitesApiService,
TranslationService,
DocumentListService,
SearchService,
ContentNodeSelectorService,
UserPreferencesService,
...plusProviders
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
}
afterEach(() => {
fixture.destroy();
TestBed.resetTestingModule();
});
describe('Dialog features', () => {
beforeEach(async(() => {
data = {
title: 'Move along citizen...',
select: new EventEmitter<MinimalNodeEntryEntity>(),
rowFilter: () => {},
imageResolver: () => 'piccolo',
currentFolderId: 'cat-girl-nuku-nuku'
};
setupTestbed([{ provide: MAT_DIALOG_DATA, useValue: data }]);
TestBed.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ContentNodeSelectorComponent);
element = fixture.debugElement;
component = fixture.componentInstance;
fixture.detectChanges();
});
describe('Data injecting with the "Material dialog way"', () => {
it('should show the INJECTED title', () => {
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-title"]'));
expect(titleElement).not.toBeNull();
expect(titleElement.nativeElement.innerText).toBe('Move along citizen...');
});
it('should pass through the injected currentFolderId to the documentlist', () => {
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
expect(documentList).not.toBeNull('Document list should be shown');
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
});
it('should pass through the injected rowFilter to the documentlist', () => {
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
expect(documentList).not.toBeNull('Document list should be shown');
expect(documentList.componentInstance.rowFilter).toBe(data.rowFilter);
});
it('should pass through the injected imageResolver to the documentlist', () => {
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
expect(documentList).not.toBeNull('Document list should be shown');
expect(documentList.componentInstance.imageResolver).toBe(data.imageResolver);
});
it('should trigger the INJECTED select event when selection has been made', (done) => {
const expectedNode = <MinimalNodeEntryEntity> {};
data.select.subscribe((nodes) => {
expect(nodes.length).toBe(1);
expect(nodes[0]).toBe(expectedNode);
done();
});
component.chosenNode = expectedNode;
component.choose();
});
});
describe('Cancel button', () => {
let dummyMdDialogRef;
let fakePreference: UserPreferencesService = <UserPreferencesService> jasmine.createSpyObj('UserPreferencesService', ['paginationSize']);
fakePreference.paginationSize = 10;
beforeEach(() => {
dummyMdDialogRef = <MatDialogRef<ContentNodeSelectorComponent>> { close: () => {} };
});
it('should be shown if dialogRef is injected', () => {
const componentInstance = new ContentNodeSelectorComponent(null, null, fakePreference, data, dummyMdDialogRef);
expect(componentInstance.inDialog).toBeTruthy();
});
it('should should call the close method in the injected dialogRef', () => {
spyOn(dummyMdDialogRef, 'close');
const componentInstance = new ContentNodeSelectorComponent(null, null, fakePreference, data, dummyMdDialogRef);
componentInstance.close();
expect(dummyMdDialogRef.close).toHaveBeenCalled();
});
});
});
describe('General component features', () => {
beforeEach(async(() => {
setupTestbed([]);
TestBed.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ContentNodeSelectorComponent);
element = fixture.debugElement;
component = fixture.componentInstance;
searchService = TestBed.get(SearchService);
searchSpy = spyOn(searchService, 'getQueryNodesPromise').and.callFake(() => {
return new Promise((resolve, reject) => {
_resolve = resolve;
_reject = reject;
});
});
});
describe('Parameters', () => {
it('should show the title', () => {
component.title = 'Move along citizen...';
fixture.detectChanges();
const titleElement = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-title"]'));
expect(titleElement).not.toBeNull();
expect(titleElement.nativeElement.innerText).toBe('Move along citizen...');
});
it('should trigger the select event when selection has been made', (done) => {
const expectedNode = <MinimalNodeEntryEntity> {};
component.select.subscribe((nodes) => {
expect(nodes.length).toBe(1);
expect(nodes[0]).toBe(expectedNode);
done();
});
component.chosenNode = expectedNode;
component.choose();
});
});
describe('Breadcrumbs', () => {
let documentListService,
sitesApiService,
expectedDefaultFolderNode;
beforeEach(() => {
expectedDefaultFolderNode = <MinimalNodeEntryEntity> { path: { elements: [] } };
documentListService = TestBed.get(DocumentListService);
sitesApiService = TestBed.get(SitesApiService);
spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.resolve(expectedDefaultFolderNode));
spyOn(documentListService, 'getFolder').and.returnValue(Observable.throw('No results for test'));
spyOn(sitesApiService, 'getSites').and.returnValue(Observable.of([]));
spyOn(component.documentList, 'loadFolderNodesByFolderNodeId').and.returnValue(Promise.resolve());
component.currentFolderId = 'cat-girl-nuku-nuku';
fixture.detectChanges();
});
it('should show the breadcrumb for the currentFolderId by default', (done) => {
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
expect(breadcrumb).not.toBeNull();
expect(breadcrumb.componentInstance.folderNode).toBe(expectedDefaultFolderNode);
done();
});
});
it('should not show the breadcrumb if search was performed as last action', (done) => {
typeToSearchBox();
respondWithSearchResults(ONE_FOLDER_RESULT);
fixture.whenStable().then(() => {
fixture.detectChanges();
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
expect(breadcrumb).toBeNull();
done();
});
});
it('should show the breadcrumb again on folder navigation in the results list', (done) => {
typeToSearchBox();
respondWithSearchResults(ONE_FOLDER_RESULT);
fixture.whenStable().then(() => {
fixture.detectChanges();
component.onFolderChange();
fixture.detectChanges();
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
expect(breadcrumb).not.toBeNull();
done();
});
});
it('should show the breadcrumb for the selected node when search results are displayed', (done) => {
const alfrescoContentService = TestBed.get(ContentService);
spyOn(alfrescoContentService, 'hasPermission').and.returnValue(true);
typeToSearchBox();
respondWithSearchResults(ONE_FOLDER_RESULT);
fixture.whenStable().then(() => {
fixture.detectChanges();
const chosenNode = <MinimalNodeEntryEntity> { path: { elements: [] } };
component.onNodeSelect({ detail: { node: { entry: chosenNode} } });
fixture.detectChanges();
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
expect(breadcrumb).not.toBeNull();
expect(breadcrumb.componentInstance.folderNode).toBe(chosenNode);
done();
});
});
it('should NOT show the breadcrumb for the selected node when not on search results list', (done) => {
const alfrescoContentService = TestBed.get(ContentService);
spyOn(alfrescoContentService, 'hasPermission').and.returnValue(true);
typeToSearchBox();
respondWithSearchResults(ONE_FOLDER_RESULT);
fixture.whenStable().then(() => {
fixture.detectChanges();
component.onFolderChange();
fixture.detectChanges();
const chosenNode = <MinimalNodeEntryEntity> { path: { elements: [] } };
component.onNodeSelect({ detail: { node: { entry: chosenNode} } });
fixture.detectChanges();
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
expect(breadcrumb).not.toBeNull();
expect(breadcrumb.componentInstance.folderNode).toBe(expectedDefaultFolderNode);
done();
});
});
});
describe('Search functionality', () => {
function defaultSearchOptions(rootNodeId = undefined, skipCount = 0) {
return {
include: ['path', 'allowableOperations'],
skipCount,
rootNodeId,
nodeType: 'cm:folder',
maxItems: 25,
orderBy: null
};
}
beforeEach(() => {
const documentListService = TestBed.get(DocumentListService);
const expectedDefaultFolderNode = <MinimalNodeEntryEntity> { path: { elements: [] } };
spyOn(documentListService, 'getFolderNode').and.returnValue(Promise.resolve(expectedDefaultFolderNode));
spyOn(component.documentList, 'loadFolderNodesByFolderNodeId').and.returnValue(Promise.resolve());
component.currentFolderId = 'cat-girl-nuku-nuku';
fixture.detectChanges();
});
it('should load the results by calling the search api on search change', () => {
typeToSearchBox('kakarot');
expect(searchSpy).toHaveBeenCalledWith('kakarot*', defaultSearchOptions());
});
it('should reset the currently chosen node in case of starting a new search', () => {
component.chosenNode = <MinimalNodeEntryEntity> {};
typeToSearchBox('kakarot');
expect(component.chosenNode).toBeNull();
});
it('should NOT call the search api if the searchTerm length is less than 4 characters', () => {
typeToSearchBox('1');
typeToSearchBox('12');
typeToSearchBox('123');
expect(searchSpy).not.toHaveBeenCalled();
});
xit('should debounce the search call by 500 ms', () => {
});
it('should call the search api on changing the site selectbox\'s value', () => {
typeToSearchBox('vegeta');
expect(searchSpy.calls.count()).toBe(1, 'Search count should be one after only one search');
component.siteChanged(<SiteModel> { guid: 'namek' });
expect(searchSpy.calls.count()).toBe(2, 'Search count should be two after the site change');
expect(searchSpy.calls.argsFor(1)).toEqual(['vegeta*', defaultSearchOptions('namek') ]);
});
it('should show the search icon by default without the X (clear) icon', () => {
fixture.detectChanges();
let searchIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-icon"]'));
let clearIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]'));
expect(searchIcon).not.toBeNull('Search icon should be in the DOM');
expect(clearIcon).toBeNull('Clear icon should NOT be in the DOM');
});
it('should show the X (clear) icon without the search icon when the search contains at least one character', () => {
fixture.detectChanges();
typeToSearchBox('123');
let searchIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-icon"]'));
let clearIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]'));
expect(searchIcon).toBeNull('Search icon should NOT be in the DOM');
expect(clearIcon).not.toBeNull('Clear icon should be in the DOM');
});
it('should clear the search field, nodes and chosenNode when clicking on the X (clear) icon', () => {
component.chosenNode = <MinimalNodeEntryEntity> {};
component.nodes = { list: {
entries: [ { entry: component.chosenNode } ]
} };
component.searchTerm = 'piccolo';
component.showingSearchResults = true;
component.clear();
expect(component.searchTerm).toBe('');
expect(component.nodes).toEqual(null);
expect(component.chosenNode).toBeNull();
expect(component.showingSearchResults).toBeFalsy();
});
it('should show the current folder\'s content instead of search results if search was not performed', () => {
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
expect(documentList).not.toBeNull('Document list should be shown');
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
});
it('should pass through the rowFilter to the documentList', () => {
const filter = () => {};
component.rowFilter = filter;
fixture.detectChanges();
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
expect(documentList).not.toBeNull('Document list should be shown');
expect(documentList.componentInstance.rowFilter).toBe(filter);
});
it('should pass through the imageResolver to the documentList', () => {
const resolver = () => 'piccolo';
component.imageResolver = resolver;
fixture.detectChanges();
let documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
expect(documentList).not.toBeNull('Document list should be shown');
expect(documentList.componentInstance.imageResolver).toBe(resolver);
});
it('should show the result list when search was performed', async(() => {
typeToSearchBox();
respondWithSearchResults(ONE_FOLDER_RESULT);
fixture.whenStable().then(() => {
fixture.detectChanges();
let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
expect(documentList).not.toBeNull('Document list should be shown');
expect(documentList.componentInstance.currentFolderId).toBeUndefined();
});
}));
it('should highlight the results when search was performed in the next timeframe', fakeAsync(() => {
spyOn(component.highlighter, 'highlight');
typeToSearchBox('shenron');
respondWithSearchResults(ONE_FOLDER_RESULT);
expect(component.highlighter.highlight).not.toHaveBeenCalled();
tick();
expect(component.highlighter.highlight).toHaveBeenCalledWith('shenron');
}));
it('should show the default text instead of result list if search was cleared', async(() => {
typeToSearchBox();
respondWithSearchResults(ONE_FOLDER_RESULT);
fixture.whenStable().then(() => {
fixture.detectChanges();
let clearButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]'));
expect(clearButton).not.toBeNull('Clear button should be in DOM');
clearButton.triggerEventHandler('click', {});
fixture.detectChanges();
let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
expect(documentList).not.toBeNull('Document list should be shown');
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
});
}));
it('should reload the original documentlist when clearing the search input', async(() => {
typeToSearchBox('shenron');
respondWithSearchResults(ONE_FOLDER_RESULT);
fixture.whenStable().then(() => {
typeToSearchBox('');
fixture.detectChanges();
let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
});
}));
it('should set the folderIdToShow to the default "currentFolderId" if siteId is undefined', () => {
component.siteChanged(<SiteModel> { guid: 'Kame-Sennin Muten Roshi' });
fixture.detectChanges();
let documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
expect(documentList.componentInstance.currentFolderId).toBe('Kame-Sennin Muten Roshi');
component.siteChanged(<SiteModel> { guid: undefined });
fixture.detectChanges();
documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
});
describe('Pagination "Load more" button', () => {
it('should NOT be shown by default', () => {
fixture.detectChanges();
const pagination = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-button"]'));
expect(pagination).toBeNull();
});
it('should be shown when diplaying search results', async(() => {
typeToSearchBox('shenron');
respondWithSearchResults(ONE_FOLDER_RESULT);
fixture.whenStable().then(() => {
fixture.detectChanges();
const pagination = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-pagination"]'));
expect(pagination).not.toBeNull();
});
}));
it('button\'s callback should load the next batch of results by calling the search api', () => {
const skipCount = 8;
component.searchTerm = 'kakarot';
component.getNextPageOfSearch({skipCount});
expect(searchSpy).toHaveBeenCalledWith('kakarot*', defaultSearchOptions(undefined, skipCount));
});
it('should set its loading state to true after search was started', () => {
component.showingSearchResults = true;
component.pagination = { hasMoreItems: true };
typeToSearchBox('shenron');
fixture.detectChanges();
const spinnerSelector = By.css('[data-automation-id="content-node-selector-search-pagination"] [data-automation-id="adf-infinite-pagination-spinner"]');
const paginationLoading = fixture.debugElement.query(spinnerSelector);
expect(paginationLoading).not.toBeNull();
});
it('should set its loading state to true after search was performed', async(() => {
component.showingSearchResults = true;
component.pagination = { hasMoreItems: true };
typeToSearchBox('shenron');
fixture.detectChanges();
respondWithSearchResults(ONE_FOLDER_RESULT);
fixture.whenStable().then(() => {
fixture.detectChanges();
const spinnerSelector = By.css('[data-automation-id="content-node-selector-search-pagination"] [data-automation-id="adf-infinite-pagination-spinner"]');
const paginationLoading = fixture.debugElement.query(spinnerSelector);
expect(paginationLoading).toBeNull();
});
}));
});
xit('should trigger some kind of error when error happened during search', () => {
});
});
describe('Cancel button', () => {
it('should not be shown if dialogRef is NOT injected', () => {
const closeButton = fixture.debugElement.query(By.css('[content-node-selector-actions-cancel]'));
expect(closeButton).toBeNull();
});
});
describe('Choose button', () => {
const entry: MinimalNodeEntryEntity = <MinimalNodeEntryEntity> {};
let hasPermission;
beforeEach(() => {
const alfrescoContentService = TestBed.get(ContentService);
spyOn(alfrescoContentService, 'hasPermission').and.callFake(() => hasPermission);
});
it('should be disabled by default', () => {
fixture.detectChanges();
let chooseButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
expect(chooseButton.nativeElement.disabled).toBe(true);
});
it('should become enabled after loading node with the necessary permissions', () => {
hasPermission = true;
component.documentList.folderNode = entry;
component.documentList.ready.emit();
fixture.detectChanges();
let chooseButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
expect(chooseButton.nativeElement.disabled).toBe(false);
});
it('should remain disabled after loading node without the necessary permissions', () => {
hasPermission = false;
component.documentList.folderNode = entry;
component.documentList.ready.emit();
fixture.detectChanges();
let chooseButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
expect(chooseButton.nativeElement.disabled).toBe(true);
});
it('should be enabled when clicking on a node (with the right permissions) in the list (onNodeSelect)', () => {
hasPermission = true;
component.onNodeSelect({ detail: { node: { entry } } });
fixture.detectChanges();
let chooseButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
expect(chooseButton.nativeElement.disabled).toBe(false);
});
it('should remain disabled when clicking on a node (with the WRONG permissions) in the list (onNodeSelect)', () => {
hasPermission = false;
component.onNodeSelect({ detail: { node: { entry } } });
fixture.detectChanges();
let chooseButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
expect(chooseButton.nativeElement.disabled).toBe(true);
});
it('should become disabled when clicking on a node (with the WRONG permissions) after previously selecting a right node', () => {
hasPermission = true;
component.onNodeSelect({ detail: { node: { entry } } });
fixture.detectChanges();
hasPermission = false;
component.onNodeSelect({ detail: { node: { entry } } });
fixture.detectChanges();
let chooseButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
expect(chooseButton.nativeElement.disabled).toBe(true);
});
it('should be disabled when resetting the chosen node', () => {
hasPermission = true;
component.onNodeSelect({ detail: { node: { entry: <MinimalNodeEntryEntity> {} } } });
fixture.detectChanges();
component.resetChosenNode();
fixture.detectChanges();
let chooseButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-actions-choose"]'));
expect(chooseButton.nativeElement.disabled).toBe(true);
});
});
});
});