mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
Merge pull request #809 from Alfresco/dev-wabson-737
Fix search page navigation from search box
This commit is contained in:
@@ -91,7 +91,8 @@ Also make sure you include these dependencies in your .html page:
|
||||
```html
|
||||
<alfresco-search-control [searchTerm]="searchTerm"
|
||||
inputType="search"
|
||||
(searchChange)="customMethod($event);">
|
||||
(searchChange)="onSearchChange($event);"
|
||||
(searchSubmit)="onSearchSubmit($event);">
|
||||
</alfresco-search-control>
|
||||
```
|
||||
|
||||
@@ -116,7 +117,7 @@ import {
|
||||
@Component({
|
||||
selector: 'alfresco-search-demo',
|
||||
template: `
|
||||
<alfresco-search-control *ngIf="authenticated" [searchTerm]="searchTerm" (searchChange)="searchTermChange($event);">
|
||||
<alfresco-search-control *ngIf="authenticated" [searchTerm]="searchTerm" (searchChange)="onSearchChange($event);" (searchSubmit)="onSearchSubmit($event)">
|
||||
</alfresco-search-control>
|
||||
<div *ngIf="!authenticated">
|
||||
Authentication failed to ip {{ host }}
|
||||
@@ -141,11 +142,15 @@ class SearchDemo implements OnInit {
|
||||
translation.addTranslationFolder();
|
||||
}
|
||||
|
||||
searchTermChange(event) {
|
||||
onSearchChange(event) {
|
||||
console.log('Search term changed', event);
|
||||
this.searchTerm = event.value;
|
||||
}
|
||||
|
||||
onSearchSubmit(event) {
|
||||
console.log('Search submitted', event);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.login();
|
||||
}
|
||||
@@ -165,7 +170,8 @@ bootstrap(SearchDemo, [
|
||||
```
|
||||
#### Events
|
||||
|
||||
**searchChange**: Emitted when the search term is changed and the form submitted, provided that the term is at least three characters in length<br />
|
||||
**searchChange**: Emitted when the search term is changed. The search term is provided in the 'value' property of the returned object. If the term is at less than three characters in length then the term is truncated to an empty string.<br />
|
||||
**searchSubmit**: Emitted when the search form is submitted. The search term is provided in the 'value' property of the returned object.<br />
|
||||
|
||||
#### Options
|
||||
|
||||
@@ -173,6 +179,7 @@ bootstrap(SearchDemo, [
|
||||
**inputType**: {string} (optional) default "text". Type of the input field to render, e.g. "search" or "text" (default)<br />
|
||||
**expandable** {boolean} (optional) default true. Whether to use an expanding search control, if false then a regular input is used.
|
||||
**autocomplete** {boolean} (optional) default true. Whether the browser should offer field auto-completion for the input field to the user.
|
||||
**autocompleteEnabled** {boolean} (optional) default true. Whether find-as-you-type suggestions should be offered for matching content items. Set to false to disable.
|
||||
|
||||
### Search results
|
||||
|
||||
@@ -220,7 +227,7 @@ Also make sure you include these dependencies in your .html page:
|
||||
```
|
||||
|
||||
Example of an component that displays search results, using the Angular2 router to supply a 'q' parameter containing the
|
||||
search term. If no router is present pon the page of if the router does not provide such a parameter then an empty
|
||||
search term. If no router is present on the page of if the router does not provide such a parameter then an empty
|
||||
results page will be shown.
|
||||
|
||||
```ts
|
||||
@@ -242,7 +249,7 @@ import {
|
||||
@Component({
|
||||
selector: 'alfresco-search-demo',
|
||||
template: `
|
||||
<alfresco-search *ngIf="authenticated" [searchTerm]="searchTerm"></alfresco-search>
|
||||
<alfresco-search *ngIf="authenticated"></alfresco-search>
|
||||
<div *ngIf="!authenticated">
|
||||
Authentication failed to ip {{ host }}
|
||||
</div>
|
||||
@@ -266,11 +273,6 @@ class SearchDemo implements OnInit {
|
||||
translation.addTranslationFolder();
|
||||
}
|
||||
|
||||
searchTermChange(event) {
|
||||
console.log('Search term changed', event);
|
||||
this.searchTerm = event.value;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.login();
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
import { NgModule, ModuleWithProviders } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { CoreModule } from 'ng2-alfresco-core';
|
||||
|
||||
import { AlfrescoSearchService } from './src/services/alfresco-search.service';
|
||||
@@ -29,6 +30,7 @@ export * from './src/services/alfresco-search.service';
|
||||
export * from './src/services/alfresco-thumbnail.service';
|
||||
export * from './src/components/alfresco-search.component';
|
||||
export * from './src/components/alfresco-search-control.component';
|
||||
export * from './src/components/alfresco-search-autocomplete.component';
|
||||
|
||||
export const ALFRESCO_SEARCH_DIRECTIVES: [any] = [
|
||||
AlfrescoSearchComponent,
|
||||
@@ -43,7 +45,9 @@ export const ALFRESCO_SEARCH_PROVIDERS: [any] = [
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CoreModule
|
||||
CoreModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule
|
||||
],
|
||||
declarations: [
|
||||
...ALFRESCO_SEARCH_DIRECTIVES
|
||||
|
@@ -34,7 +34,6 @@ module.exports = function (config) {
|
||||
|
||||
'node_modules/alfresco-js-api/dist/alfresco-js-api.js',
|
||||
{pattern: 'node_modules/ng2-translate/**/*.js', included: false, watched: false},
|
||||
{pattern: 'node_modules/ng2-translate/**/*.js.map', included: false, watched: false},
|
||||
|
||||
'karma-test-shim.js',
|
||||
|
||||
@@ -44,18 +43,8 @@ module.exports = function (config) {
|
||||
{pattern: 'dist/**/*.css', included: true, served: true, watched: true},
|
||||
|
||||
// ng2-components
|
||||
{ pattern: 'node_modules/ng2-activiti-form/dist/**/*.js', included: false, served: true, watched: false },
|
||||
{ pattern: 'node_modules/ng2-activiti-processlist/dist/**/*.js', included: false, served: true, watched: false },
|
||||
{ pattern: 'node_modules/ng2-activiti-tasklist/dist/**/*.js', included: false, served: true, watched: false },
|
||||
{ pattern: 'node_modules/ng2-alfresco-core/dist/**/*.js', included: false, served: true, watched: false },
|
||||
{ pattern: 'node_modules/ng2-alfresco-datatable/dist/**/*.js', included: false, served: true, watched: false },
|
||||
{ pattern: 'node_modules/ng2-alfresco-documentlist/dist/**/*.js', included: false, served: true, watched: false },
|
||||
{ pattern: 'node_modules/ng2-alfresco-login/dist/**/*.js', included: false, served: true, watched: false },
|
||||
{ pattern: 'node_modules/ng2-alfresco-search/dist/**/*.js', included: false, served: true, watched: false },
|
||||
{ pattern: 'node_modules/ng2-alfresco-tag/dist/**/*.js', included: false, served: true, watched: false },
|
||||
{ pattern: 'node_modules/ng2-alfresco-upload/dist/**/*.js', included: false, served: true, watched: false },
|
||||
{ pattern: 'node_modules/ng2-alfresco-viewer/dist/**/*.js', included: false, served: true, watched: false },
|
||||
{ pattern: 'node_modules/ng2-alfresco-webscript/dist/**/*.js', included: false, served: true, watched: false },
|
||||
|
||||
// paths to support debugging with source maps in dev tools
|
||||
{pattern: 'src/**/*.ts', included: false, watched: false},
|
||||
|
@@ -21,5 +21,13 @@
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table data-automation-id="autocomplete_error_message" *ngIf="errorMessage"
|
||||
class="mdl-data-table mdl-js-data-table mdl-shadow--2dp full-width">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ 'SEARCH.RESULTS.ERROR' | translate:{errorMessage: errorMessage} }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p data-automation-id="autocomplete_error_message" *ngIf="errorMessage">{{ 'SEARCH.RESULTS.ERROR' |
|
||||
translate:{errorMessage: errorMessage} }}</p>
|
||||
|
@@ -15,10 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
import { it, describe, expect, inject, beforeEachProviders, beforeEach, afterEach } from '@angular/core/testing';
|
||||
import { PLATFORM_PIPES } from '@angular/core';
|
||||
import { TestComponentBuilder } from '@angular/compiler/testing';
|
||||
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||
import { AlfrescoSearchAutocompleteComponent } from './alfresco-search-autocomplete.component';
|
||||
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
|
||||
import { TranslationMock } from './../assets/translation.service.mock';
|
||||
@@ -29,14 +26,13 @@ import {
|
||||
AlfrescoAuthenticationService,
|
||||
AlfrescoContentService,
|
||||
AlfrescoTranslationService,
|
||||
AlfrescoPipeTranslate
|
||||
CoreModule
|
||||
} from 'ng2-alfresco-core';
|
||||
|
||||
declare let jasmine: any;
|
||||
|
||||
describe('AlfrescoSearchAutocompleteComponent', () => {
|
||||
|
||||
let alfrescoSearchComponentFixture, element, component;
|
||||
let fixture: ComponentFixture<AlfrescoSearchAutocompleteComponent>, element: HTMLElement;
|
||||
let component: AlfrescoSearchAutocompleteComponent;
|
||||
|
||||
let result = {
|
||||
list: {
|
||||
@@ -47,7 +43,7 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
|
||||
name: 'MyDoc',
|
||||
isFile : true,
|
||||
content: {
|
||||
mimetype: 'text/plain'
|
||||
mimeType: 'text/plain'
|
||||
},
|
||||
createdByUser: {
|
||||
displayName: 'John Doe'
|
||||
@@ -61,132 +57,235 @@ describe('AlfrescoSearchAutocompleteComponent', () => {
|
||||
}
|
||||
};
|
||||
|
||||
let folderResult = {
|
||||
list: {
|
||||
entries: [
|
||||
{
|
||||
entry: {
|
||||
id: '123',
|
||||
name: 'MyFolder',
|
||||
isFile : false,
|
||||
isFolder : true,
|
||||
createdByUser: {
|
||||
displayName: 'John Doe'
|
||||
},
|
||||
modifiedByUser: {
|
||||
displayName: 'John Doe'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let noResult = {
|
||||
list: {
|
||||
entries: []
|
||||
}
|
||||
};
|
||||
|
||||
beforeEachProviders(() => {
|
||||
return [
|
||||
{ provide: PLATFORM_PIPES, useValue: AlfrescoPipeTranslate, multi: true },
|
||||
{provide: AlfrescoTranslationService, useClass: TranslationMock},
|
||||
AlfrescoThumbnailService,
|
||||
AlfrescoSettingsService,
|
||||
AlfrescoApiService,
|
||||
AlfrescoAuthenticationService,
|
||||
AlfrescoContentService,
|
||||
AlfrescoSearchService
|
||||
];
|
||||
});
|
||||
let errorJson = {
|
||||
error: {
|
||||
errorKey: 'Search failed',
|
||||
statusCode: 400,
|
||||
briefSummary: '08220082 search failed',
|
||||
stackTrace: 'For security reasons the stack trace is no longer displayed, but the property is kept for previous versions.',
|
||||
descriptionURL: 'https://api-explorer.alfresco.com'
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
return tcb
|
||||
.createAsync(AlfrescoSearchAutocompleteComponent)
|
||||
.then(fixture => {
|
||||
jasmine.Ajax.install();
|
||||
alfrescoSearchComponentFixture = fixture;
|
||||
element = alfrescoSearchComponentFixture.nativeElement;
|
||||
component = alfrescoSearchComponentFixture.componentInstance;
|
||||
});
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
CoreModule
|
||||
],
|
||||
declarations: [ AlfrescoSearchAutocompleteComponent ], // declare the test component
|
||||
providers: [
|
||||
{provide: AlfrescoTranslationService, useClass: TranslationMock},
|
||||
AlfrescoThumbnailService,
|
||||
AlfrescoSettingsService,
|
||||
AlfrescoApiService,
|
||||
AlfrescoAuthenticationService,
|
||||
AlfrescoContentService,
|
||||
AlfrescoSearchService
|
||||
]
|
||||
}).compileComponents().then(() => {
|
||||
fixture = TestBed.createComponent(AlfrescoSearchAutocompleteComponent);
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
});
|
||||
}));
|
||||
|
||||
afterEach(() => {
|
||||
jasmine.Ajax.uninstall();
|
||||
});
|
||||
|
||||
it('should setup i18n folder', () => {
|
||||
let translation = jasmine.createSpyObj('AlfrescoTranslationService', [
|
||||
'addTranslationFolder'
|
||||
]);
|
||||
let search = new AlfrescoSearchAutocompleteComponent(null, translation, null);
|
||||
expect(search).toBeDefined();
|
||||
|
||||
let translationService = fixture.debugElement.injector.get(AlfrescoTranslationService);
|
||||
spyOn(translationService, 'addTranslationFolder');
|
||||
fixture.detectChanges();
|
||||
expect(translationService.addTranslationFolder).toHaveBeenCalledWith('node_modules/ng2-alfresco-search/dist/src');
|
||||
});
|
||||
|
||||
it('should display search results when a search term is provided', () => {
|
||||
let searchTerm = { currentValue: 'customSearchTerm', previousValue: ''};
|
||||
spyOn(component, 'displaySearchResults').and.stub();
|
||||
component.searchTerm = searchTerm;
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnChanges({
|
||||
searchTerm: searchTerm
|
||||
});
|
||||
alfrescoSearchComponentFixture.detectChanges();
|
||||
fixture.detectChanges();
|
||||
expect(component.displaySearchResults).toHaveBeenCalledWith(searchTerm.currentValue);
|
||||
});
|
||||
|
||||
it('should display the returned search results', (done) => {
|
||||
it('should clear results straight away when a new search term is entered', (done) => {
|
||||
|
||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||
spyOn(searchService, 'getSearchNodesPromise')
|
||||
.and.returnValue(Promise.resolve(result));
|
||||
|
||||
component.resultsEmitter.subscribe(x => {
|
||||
alfrescoSearchComponentFixture.detectChanges();
|
||||
fixture.detectChanges();
|
||||
component.searchTerm = 'searchTerm2';
|
||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm2', previousValue: 'searchTerm'} });
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelectorAll('table[data-automation-id="autocomplete_results"] tbody tr').length).toBe(0);
|
||||
done();
|
||||
});
|
||||
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''} });
|
||||
});
|
||||
|
||||
it('should display the returned search results', (done) => {
|
||||
|
||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||
spyOn(searchService, 'getSearchNodesPromise')
|
||||
.and.returnValue(Promise.resolve(result));
|
||||
|
||||
component.resultsEmitter.subscribe(x => {
|
||||
fixture.detectChanges();
|
||||
expect( element.querySelector('#result_user_0').innerHTML).toBe('John Doe');
|
||||
expect( element.querySelector('#result_name_0').innerHTML).toBe('<b _ngcontent-a-1="">MyDoc</b>');
|
||||
done();
|
||||
});
|
||||
|
||||
component.searchTerm = { currentValue: 'searchTerm', previousValue: ''};
|
||||
component.ngOnChanges({searchTerm: component.searchTerm });
|
||||
|
||||
jasmine.Ajax.requests.mostRecent().respondWith({
|
||||
status: 200,
|
||||
contentType: 'json',
|
||||
responseText: result
|
||||
});
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''} });
|
||||
});
|
||||
|
||||
it('should display no result if no result are returned', (done) => {
|
||||
component.resultsEmitter.subscribe(x => {
|
||||
alfrescoSearchComponentFixture.detectChanges();
|
||||
expect( element.querySelector('#search_no_result')).not.toBe(null);
|
||||
it('should display the correct thumbnail for result items', (done) => {
|
||||
|
||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||
spyOn(searchService, 'getSearchNodesPromise')
|
||||
.and.returnValue(Promise.resolve(result));
|
||||
|
||||
component.baseComponentPath = 'http://localhost';
|
||||
|
||||
let thumbnailService = fixture.debugElement.injector.get(AlfrescoThumbnailService);
|
||||
spyOn(thumbnailService, 'getMimeTypeIcon').and.returnValue('fake-type-icon.svg');
|
||||
spyOn(thumbnailService, 'getMimeTypeKey').and.returnValue('FAKE_TYPE');
|
||||
|
||||
component.resultsEmitter.subscribe(() => {
|
||||
fixture.detectChanges();
|
||||
let imgEl = <any> element.querySelector('#result_row_0 img');
|
||||
expect(imgEl).not.toBeNull();
|
||||
expect(imgEl.src).toBe('http://localhost/img/fake-type-icon.svg');
|
||||
expect(imgEl.alt).toBe('SEARCH.ICONS.FAKE_TYPE');
|
||||
done();
|
||||
});
|
||||
|
||||
component.searchTerm = { currentValue: 'searchTerm', previousValue: ''};
|
||||
component.ngOnChanges({searchTerm: component.searchTerm});
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''} });
|
||||
});
|
||||
|
||||
jasmine.Ajax.requests.mostRecent().respondWith({
|
||||
status: 200,
|
||||
contentType: 'json',
|
||||
responseText: noResult
|
||||
it('should display no result if no result are returned', (done) => {
|
||||
|
||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||
spyOn(searchService, 'getSearchNodesPromise')
|
||||
.and.returnValue(Promise.resolve(noResult));
|
||||
|
||||
component.resultsEmitter.subscribe(x => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#search_no_result')).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''}});
|
||||
});
|
||||
|
||||
it('should display an error if an error is encountered running the search', (done) => {
|
||||
|
||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||
spyOn(searchService, 'getSearchNodesPromise')
|
||||
.and.returnValue(Promise.reject(errorJson));
|
||||
|
||||
component.errorEmitter.subscribe(() => {
|
||||
fixture.detectChanges();
|
||||
let resultsEl = element.querySelector('[data-automation-id="autocomplete_results"]');
|
||||
let errorEl = <any> element.querySelector('[data-automation-id="autocomplete_error_message"]');
|
||||
expect(resultsEl).toBeNull();
|
||||
expect(errorEl).not.toBeNull();
|
||||
expect(errorEl.innerText.trim()).toBe('SEARCH.RESULTS.ERROR');
|
||||
done();
|
||||
});
|
||||
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''}});
|
||||
});
|
||||
|
||||
it('should clear errors straight away when a new search is performed', (done) => {
|
||||
|
||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||
spyOn(searchService, 'getSearchNodesPromise')
|
||||
.and.returnValue(Promise.reject(errorJson));
|
||||
|
||||
component.errorEmitter.subscribe(() => {
|
||||
fixture.detectChanges();
|
||||
component.searchTerm = 'searchTerm2';
|
||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm2', previousValue: 'searchTerm'} });
|
||||
fixture.detectChanges();
|
||||
let errorEl = <any> element.querySelector('[data-automation-id="autocomplete_error_message"]');
|
||||
expect(errorEl).toBeNull();
|
||||
done();
|
||||
});
|
||||
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''}});
|
||||
});
|
||||
|
||||
it('should emit preview when file item clicked', (done) => {
|
||||
|
||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||
spyOn(searchService, 'getSearchNodesPromise')
|
||||
.and.returnValue(Promise.resolve(result));
|
||||
|
||||
component.resultsEmitter.subscribe(x => {
|
||||
alfrescoSearchComponentFixture.detectChanges();
|
||||
element.querySelector('#result_row_0').click();
|
||||
fixture.detectChanges();
|
||||
(<any> element.querySelector('#result_row_0')).click();
|
||||
});
|
||||
|
||||
component.searchTerm = { currentValue: 'searchTerm', previousValue: ''};
|
||||
component.ngOnChanges({searchTerm: component.searchTerm});
|
||||
|
||||
jasmine.Ajax.requests.mostRecent().respondWith({
|
||||
status: 200,
|
||||
contentType: 'json',
|
||||
responseText: result
|
||||
});
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''}});
|
||||
|
||||
component.preview.subscribe(e => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not emit preview when non-file item is clicked', () => {
|
||||
spyOn(component, 'onItemClick').and.stub();
|
||||
it('should not emit preview if a non-file item is clicked', (done) => {
|
||||
|
||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''} });
|
||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||
spyOn(searchService, 'getSearchNodesPromise')
|
||||
.and.returnValue(Promise.resolve(folderResult));
|
||||
|
||||
component.preview.subscribe(e => {
|
||||
expect(e.value).toBe(component.results[0]);
|
||||
spyOn(component.preview, 'emit');
|
||||
component.resultsEmitter.subscribe(x => {
|
||||
fixture.detectChanges();
|
||||
(<any> element.querySelector('#result_row_0')).click();
|
||||
expect(component.preview.emit).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
|
||||
jasmine.Ajax.requests.mostRecent().respondWith({
|
||||
status: 200,
|
||||
contentType: 'json',
|
||||
responseText: result
|
||||
});
|
||||
|
||||
expect(component.onItemClick).not.toHaveBeenCalled();
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnChanges({searchTerm: { currentValue: 'searchTerm', previousValue: ''}});
|
||||
});
|
||||
|
||||
});
|
||||
*/
|
||||
|
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, OnInit, OnChanges, Output } from '@angular/core';
|
||||
import { AlfrescoSearchService } from './../services/alfresco-search.service';
|
||||
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
|
||||
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
||||
@@ -28,14 +28,14 @@ declare let __moduleName: string;
|
||||
templateUrl: './alfresco-search-autocomplete.component.html',
|
||||
styleUrls: ['./alfresco-search-autocomplete.component.css']
|
||||
})
|
||||
export class AlfrescoSearchAutocompleteComponent implements OnChanges {
|
||||
export class AlfrescoSearchAutocompleteComponent implements OnInit, OnChanges {
|
||||
|
||||
baseComponentPath = __moduleName.replace('/components/alfresco-search-autocomplete.component.js', '');
|
||||
|
||||
@Input()
|
||||
searchTerm: string = '';
|
||||
|
||||
results: any;
|
||||
results: any = null;
|
||||
|
||||
errorMessage;
|
||||
|
||||
@@ -48,17 +48,24 @@ export class AlfrescoSearchAutocompleteComponent implements OnChanges {
|
||||
@Output()
|
||||
resultsEmitter = new EventEmitter();
|
||||
|
||||
@Output()
|
||||
errorEmitter = new EventEmitter();
|
||||
|
||||
constructor(private alfrescoSearchService: AlfrescoSearchService,
|
||||
private translate: AlfrescoTranslationService,
|
||||
private alfrescoThumbnailService: AlfrescoThumbnailService) {
|
||||
if (translate) {
|
||||
translate.addTranslationFolder('node_modules/ng2-alfresco-search/dist/src');
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.translate) {
|
||||
this.translate.addTranslationFolder('node_modules/ng2-alfresco-search/dist/src');
|
||||
}
|
||||
this.results = null;
|
||||
}
|
||||
|
||||
ngOnChanges(changes) {
|
||||
if (changes.searchTerm) {
|
||||
this.results = null;
|
||||
this.errorMessage = null;
|
||||
this.displaySearchResults(changes.searchTerm.currentValue);
|
||||
}
|
||||
}
|
||||
@@ -70,8 +77,8 @@ export class AlfrescoSearchAutocompleteComponent implements OnChanges {
|
||||
public displaySearchResults(searchTerm) {
|
||||
if (searchTerm !== null && searchTerm !== '') {
|
||||
this.alfrescoSearchService
|
||||
.getLiveSearchResults(searchTerm)
|
||||
.subscribe(
|
||||
.getSearchNodesPromise(searchTerm)
|
||||
.then(
|
||||
results => {
|
||||
this.results = results.list.entries;
|
||||
this.errorMessage = null;
|
||||
@@ -80,6 +87,7 @@ export class AlfrescoSearchAutocompleteComponent implements OnChanges {
|
||||
error => {
|
||||
this.results = null;
|
||||
this.errorMessage = <any>error;
|
||||
this.errorEmitter.emit(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<form (submit)="onSearch($event)">
|
||||
<form #f="ngForm" (ngSubmit)="onSearch(f.value)">
|
||||
<div [class]="getTextFieldClassName()">
|
||||
<label *ngIf="expandable" class="mdl-button mdl-js-button mdl-button--icon" for="searchControl">
|
||||
<i mdl-upgrade class="material-icons">search</i>
|
||||
@@ -11,15 +11,17 @@
|
||||
data-automation-id="search_input"
|
||||
#searchInput
|
||||
id="searchControl"
|
||||
ngControl="searchControl"
|
||||
[formControl]="searchControl"
|
||||
[(ngModel)]="searchTerm"
|
||||
name="searchControl"
|
||||
(focus)="onFocus($event)"
|
||||
(blur)="onBlur($event)"
|
||||
(keyup.escape)="onEscape($event)"
|
||||
(keyup.arrowdown)="onArrowDown($event)"
|
||||
aria-labelledby="searchLabel">
|
||||
<label id="searchLabel" class="mdl-textfield__label" for="searchControl">{{'SEARCH.CONTROL.LABEL' | translate}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<alfresco-search-autocomplete [searchTerm]="autocompleteSearchTerm" [ngClass]="{active: searchActive, valid: searchValid}"
|
||||
<alfresco-search-autocomplete *ngIf="autocompleteEnabled"
|
||||
[searchTerm]="autocompleteSearchTerm" [ngClass]="{active: searchActive, valid: searchValid}"
|
||||
(preview)="onFileClicked($event)"></alfresco-search-autocomplete>
|
||||
|
@@ -15,11 +15,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
import { provide, PLATFORM_PIPES } from '@angular/core';
|
||||
import { it, describe, expect, inject, beforeEachProviders, beforeEach } from '@angular/core/testing';
|
||||
import { TestComponentBuilder } from '@angular/compiler/testing';
|
||||
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||
import { AlfrescoSearchControlComponent } from './alfresco-search-control.component';
|
||||
import { AlfrescoSearchAutocompleteComponent } from './alfresco-search-autocomplete.component';
|
||||
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
|
||||
import { TranslationMock } from './../assets/translation.service.mock';
|
||||
import {
|
||||
@@ -28,95 +26,262 @@ import {
|
||||
AlfrescoAuthenticationService,
|
||||
AlfrescoContentService,
|
||||
AlfrescoTranslationService,
|
||||
AlfrescoPipeTranslate
|
||||
CoreModule
|
||||
} from 'ng2-alfresco-core';
|
||||
import { AlfrescoSearchService } from '../services/alfresco-search.service';
|
||||
|
||||
|
||||
describe('AlfrescoSearchControlComponent', () => {
|
||||
|
||||
let alfrescoSearchControlComponentFixture, element, component;
|
||||
let fixture: ComponentFixture<AlfrescoSearchControlComponent>;
|
||||
let component: AlfrescoSearchControlComponent, element: HTMLElement;
|
||||
|
||||
beforeEachProviders(() => {
|
||||
return [
|
||||
{ provide: PLATFORM_PIPES, useValue: AlfrescoPipeTranslate, multi: true },
|
||||
AlfrescoSearchService,
|
||||
provide(AlfrescoTranslationService, {useClass: TranslationMock}),
|
||||
AlfrescoThumbnailService,
|
||||
AlfrescoSettingsService,
|
||||
AlfrescoApiService,
|
||||
AlfrescoAuthenticationService,
|
||||
AlfrescoContentService
|
||||
];
|
||||
});
|
||||
|
||||
beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
return tcb
|
||||
.createAsync(AlfrescoSearchControlComponent)
|
||||
.then(fixture => {
|
||||
alfrescoSearchControlComponentFixture = fixture;
|
||||
element = alfrescoSearchControlComponentFixture.nativeElement;
|
||||
component = alfrescoSearchControlComponentFixture.componentInstance;
|
||||
});
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
CoreModule
|
||||
],
|
||||
declarations: [
|
||||
AlfrescoSearchControlComponent,
|
||||
AlfrescoSearchAutocompleteComponent
|
||||
],
|
||||
providers: [
|
||||
{provide: AlfrescoTranslationService, useClass: TranslationMock},
|
||||
AlfrescoThumbnailService,
|
||||
AlfrescoSettingsService,
|
||||
AlfrescoApiService,
|
||||
AlfrescoAuthenticationService,
|
||||
AlfrescoContentService,
|
||||
AlfrescoSearchService
|
||||
]
|
||||
}).compileComponents().then(() => {
|
||||
fixture = TestBed.createComponent(AlfrescoSearchControlComponent);
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
});
|
||||
}));
|
||||
|
||||
it('should setup i18n folder', () => {
|
||||
let translation = jasmine.createSpyObj('AlfrescoTranslationService', [
|
||||
'addTranslationFolder'
|
||||
]);
|
||||
|
||||
let alfrescoSearchControlComponent = new AlfrescoSearchControlComponent(translation);
|
||||
expect(alfrescoSearchControlComponent).toBeDefined();
|
||||
let translationService = fixture.debugElement.injector.get(AlfrescoTranslationService);
|
||||
spyOn(translationService, 'addTranslationFolder');
|
||||
fixture.detectChanges();
|
||||
expect(translationService.addTranslationFolder)
|
||||
.toHaveBeenCalledWith('node_modules/ng2-alfresco-search/dist/src');
|
||||
});
|
||||
|
||||
it('should emit searchChange when search term changed', () => {
|
||||
alfrescoSearchControlComponentFixture.componentInstance.searchTerm = 'customSearchTerm';
|
||||
alfrescoSearchControlComponentFixture.detectChanges();
|
||||
alfrescoSearchControlComponentFixture.componentInstance.searchChange.subscribe(e => {
|
||||
it('should emit searchChange when search term input changed', (done) => {
|
||||
fixture.componentInstance.searchChange.subscribe(e => {
|
||||
expect(e.value).toBe('customSearchTerm');
|
||||
done();
|
||||
});
|
||||
fixture.detectChanges();
|
||||
fixture.componentInstance.searchTerm = 'customSearchTerm';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should emit searchChange when search term changed by user', (done) => {
|
||||
fixture.detectChanges();
|
||||
fixture.componentInstance.searchChange.subscribe(e => {
|
||||
expect(e.value).toBe('customSearchTerm211');
|
||||
done();
|
||||
});
|
||||
component.searchControl.setValue('customSearchTerm211', true);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
describe('Component rendering', () => {
|
||||
|
||||
it('should display a text input field by default', () => {
|
||||
alfrescoSearchControlComponentFixture.detectChanges();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelectorAll('input[type="text"]').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should display a search input field when specified', () => {
|
||||
alfrescoSearchControlComponentFixture.componentInstance.inputType = 'search';
|
||||
alfrescoSearchControlComponentFixture.detectChanges();
|
||||
fixture.componentInstance.inputType = 'search';
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelectorAll('input[type="search"]').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should set browser autocomplete to off by default', () => {
|
||||
alfrescoSearchControlComponentFixture.detectChanges();
|
||||
fixture.detectChanges();
|
||||
let attr = element.querySelectorAll('input[type="text"]')[0].getAttribute('autocomplete');
|
||||
expect(attr).toBe('off');
|
||||
});
|
||||
|
||||
it('should set browser autocomplete to on when configured', () => {
|
||||
alfrescoSearchControlComponentFixture.componentInstance.autocomplete = true;
|
||||
alfrescoSearchControlComponentFixture.detectChanges();
|
||||
fixture.componentInstance.autocomplete = true;
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelectorAll('input[type="text"]')[0].getAttribute('autocomplete')).toBe('on');
|
||||
});
|
||||
|
||||
it('should show an expanding control by default', () => {
|
||||
alfrescoSearchControlComponentFixture.detectChanges();
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelectorAll('div.mdl-textfield--expandable').length).toBe(1);
|
||||
expect(element.querySelectorAll('div.mdl-textfield__expandable-holder').length).toBe(1);
|
||||
expect(element.querySelectorAll('label.mdl-button--icon').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should show a normal non-expanding control when configured', () => {
|
||||
alfrescoSearchControlComponentFixture.detectChanges();
|
||||
alfrescoSearchControlComponentFixture.componentInstance.expandable = false;
|
||||
alfrescoSearchControlComponentFixture.detectChanges();
|
||||
fixture.detectChanges();
|
||||
fixture.componentInstance.expandable = false;
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelectorAll('div.mdl-textfield--expandable').length).toBe(0);
|
||||
expect(element.querySelectorAll('div.mdl-textfield__expandable-holder').length).toBe(0);
|
||||
expect(element.querySelectorAll('label.mdl-button--icon').length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Find as you type', () => {
|
||||
|
||||
let inputEl: HTMLInputElement;
|
||||
|
||||
beforeEach(() => {
|
||||
inputEl = element.querySelector('input');
|
||||
});
|
||||
|
||||
it('should display a find-as-you-type control by default', () => {
|
||||
fixture.detectChanges();
|
||||
let autocomplete: Element = element.querySelector('alfresco-search-autocomplete');
|
||||
expect(autocomplete).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should make find-as-you-type control hidden initially', () => {
|
||||
fixture.detectChanges();
|
||||
let autocomplete: Element = element.querySelector('alfresco-search-autocomplete');
|
||||
expect(autocomplete.classList.contains('active')).toBe(false);
|
||||
});
|
||||
|
||||
it('should make find-as-you-type control visible when search box has focus', () => {
|
||||
fixture.detectChanges();
|
||||
inputEl.dispatchEvent(new Event('focus'));
|
||||
fixture.detectChanges();
|
||||
let autocomplete: Element = element.querySelector('alfresco-search-autocomplete');
|
||||
expect(autocomplete.classList.contains('active')).toBe(true);
|
||||
});
|
||||
|
||||
it('should hide find-as-you-type results when the search box loses focus', (done) => {
|
||||
fixture.detectChanges();
|
||||
inputEl.dispatchEvent(new Event('focus'));
|
||||
inputEl.dispatchEvent(new Event('blur'));
|
||||
window.setTimeout(() => {
|
||||
fixture.detectChanges();
|
||||
let autocomplete: Element = element.querySelector('alfresco-search-autocomplete');
|
||||
expect(autocomplete.classList.contains('active')).toBe(false);
|
||||
done();
|
||||
}, 250);
|
||||
});
|
||||
|
||||
it('should hide find-as-you-type results when escape key pressed', () => {
|
||||
fixture.detectChanges();
|
||||
inputEl.dispatchEvent(new Event('focus'));
|
||||
inputEl.dispatchEvent(new KeyboardEvent('keyup', {
|
||||
key: 'Escape'
|
||||
}));
|
||||
fixture.detectChanges();
|
||||
let autocomplete: Element = element.querySelector('alfresco-search-autocomplete');
|
||||
expect(autocomplete.classList.contains('active')).toBe(false);
|
||||
});
|
||||
|
||||
it('should make find-as-you-type control visible again when down arrow is pressed', () => {
|
||||
fixture.detectChanges();
|
||||
inputEl.dispatchEvent(new Event('focus'));
|
||||
inputEl.dispatchEvent(new KeyboardEvent('keyup', {
|
||||
key: 'Escape'
|
||||
}));
|
||||
inputEl.dispatchEvent(new KeyboardEvent('keyup', {
|
||||
key: 'ArrowDown'
|
||||
}));
|
||||
fixture.detectChanges();
|
||||
let autocomplete: Element = element.querySelector('alfresco-search-autocomplete');
|
||||
expect(autocomplete.classList.contains('active')).toBe(true);
|
||||
});
|
||||
|
||||
it('should NOT display a find-as-you-type control when configured not to', () => {
|
||||
fixture.componentInstance.autocompleteEnabled = false;
|
||||
fixture.detectChanges();
|
||||
let autocomplete: Element = element.querySelector('alfresco-search-autocomplete');
|
||||
expect(autocomplete).toBeNull();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('search submit', () => {
|
||||
|
||||
it('should fire a search when a term has been entered', () => {
|
||||
spyOn(component.searchSubmit, 'emit');
|
||||
fixture.detectChanges();
|
||||
let formEl: HTMLElement = element.querySelector('form');
|
||||
component.searchTerm = 'searchTerm1';
|
||||
component.searchControl.setValue('searchTerm1', true);
|
||||
fixture.detectChanges();
|
||||
formEl.dispatchEvent(new Event('submit'));
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.searchSubmit.emit).toHaveBeenCalledWith({
|
||||
'value': 'searchTerm1'
|
||||
});
|
||||
});
|
||||
|
||||
it('should not fire a search when no term has been entered', () => {
|
||||
spyOn(component.searchSubmit, 'emit');
|
||||
fixture.detectChanges();
|
||||
let inputEl: HTMLInputElement = <HTMLInputElement> element.querySelector('input[type="text"]');
|
||||
let formEl: HTMLElement = element.querySelector('form');
|
||||
inputEl.value = '';
|
||||
formEl.dispatchEvent(new Event('submit'));
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.searchSubmit.emit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('component focus', () => {
|
||||
|
||||
it('should fire an event when the search box receives focus', () => {
|
||||
spyOn(component.expand, 'emit');
|
||||
let inputEl: HTMLElement = element.querySelector('input');
|
||||
inputEl.dispatchEvent(new Event('focus'));
|
||||
expect(component.expand.emit).toHaveBeenCalledWith({
|
||||
expanded: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should fire an event when the search box loses focus', () => {
|
||||
spyOn(component.expand, 'emit');
|
||||
let inputEl: HTMLElement = element.querySelector('input');
|
||||
inputEl.dispatchEvent(new Event('blur'));
|
||||
expect(component.expand.emit).toHaveBeenCalledWith({
|
||||
expanded: false
|
||||
});
|
||||
});
|
||||
|
||||
it('should NOT fire an event when the search box receives/loses focus but the component is not expandable',
|
||||
() => {
|
||||
spyOn(component.expand, 'emit');
|
||||
component.expandable = false;
|
||||
let inputEl: HTMLElement = element.querySelector('input');
|
||||
inputEl.dispatchEvent(new Event('focus'));
|
||||
inputEl.dispatchEvent(new Event('blur'));
|
||||
expect(component.expand.emit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('file preview', () => {
|
||||
|
||||
it('should emit a preview event when onFileClicked is called', () => {
|
||||
spyOn(component.preview, 'emit');
|
||||
component.onFileClicked({
|
||||
value: 'node12345'
|
||||
});
|
||||
expect(component.preview.emit).toHaveBeenCalledWith({
|
||||
'value': 'node12345'
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
*/
|
||||
|
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { FormControl, Validators } from '@angular/forms';
|
||||
import { Component, Input, Output, ElementRef, EventEmitter, ViewChild } from '@angular/core';
|
||||
import { Component, Input, Output, OnInit, ElementRef, EventEmitter, ViewChild } from '@angular/core';
|
||||
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
||||
import { SearchTermValidator } from './../forms/search-term-validator';
|
||||
|
||||
@@ -28,7 +28,7 @@ declare let __moduleName: string;
|
||||
templateUrl: './alfresco-search-control.component.html',
|
||||
styleUrls: ['./alfresco-search-control.component.css']
|
||||
})
|
||||
export class AlfrescoSearchControlComponent {
|
||||
export class AlfrescoSearchControlComponent implements OnInit {
|
||||
|
||||
@Input()
|
||||
searchTerm = '';
|
||||
@@ -45,6 +45,9 @@ export class AlfrescoSearchControlComponent {
|
||||
@Output()
|
||||
searchChange = new EventEmitter();
|
||||
|
||||
@Output()
|
||||
searchSubmit = new EventEmitter();
|
||||
|
||||
@Output()
|
||||
preview = new EventEmitter();
|
||||
|
||||
@@ -55,6 +58,9 @@ export class AlfrescoSearchControlComponent {
|
||||
|
||||
@ViewChild('searchInput', {}) searchInput: ElementRef;
|
||||
|
||||
@Input()
|
||||
autocompleteEnabled = true;
|
||||
|
||||
@Input()
|
||||
autocompleteSearchTerm = '';
|
||||
|
||||
@@ -68,16 +74,26 @@ export class AlfrescoSearchControlComponent {
|
||||
this.searchTerm,
|
||||
Validators.compose([Validators.required, SearchTermValidator.minAlphanumericChars(3)])
|
||||
);
|
||||
}
|
||||
|
||||
this.searchControl.valueChanges.map(value => this.searchControl.valid ? value : '')
|
||||
.debounceTime(400).distinctUntilChanged().subscribe(
|
||||
(value: string) => {
|
||||
this.autocompleteSearchTerm = value;
|
||||
this.searchValid = this.searchControl.valid;
|
||||
ngOnInit(): void {
|
||||
this.searchControl.valueChanges.debounceTime(400).distinctUntilChanged()
|
||||
.subscribe((value: string) => {
|
||||
this.onSearchTermChange(value);
|
||||
}
|
||||
);
|
||||
this.translate.addTranslationFolder('node_modules/ng2-alfresco-search/dist/src');
|
||||
}
|
||||
|
||||
translate.addTranslationFolder('node_modules/ng2-alfresco-search/dist/src');
|
||||
private onSearchTermChange(value: string): void {
|
||||
this.searchActive = true;
|
||||
this.autocompleteSearchTerm = value;
|
||||
this.searchControl.setValue(value, true);
|
||||
this.searchValid = this.searchControl.valid;
|
||||
this.searchChange.emit({
|
||||
value: value,
|
||||
valid: this.searchValid
|
||||
});
|
||||
}
|
||||
|
||||
getTextFieldClassName(): string {
|
||||
@@ -98,11 +114,9 @@ export class AlfrescoSearchControlComponent {
|
||||
* @param event Submit event that was fired
|
||||
*/
|
||||
onSearch(event): void {
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
this.searchControl.setValue(this.searchTerm, true);
|
||||
if (this.searchControl.valid) {
|
||||
this.searchChange.emit({
|
||||
this.searchSubmit.emit({
|
||||
value: this.searchTerm
|
||||
});
|
||||
this.searchInput.nativeElement.blur();
|
||||
@@ -135,4 +149,12 @@ export class AlfrescoSearchControlComponent {
|
||||
}
|
||||
}
|
||||
|
||||
onEscape(): void {
|
||||
this.searchActive = false;
|
||||
}
|
||||
|
||||
onArrowDown(): void {
|
||||
this.searchActive = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@
|
||||
text-align: left;
|
||||
}
|
||||
:host .mdl-data-table td {
|
||||
max-width: 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
:host .mdl-data-table td.col-mimetype-icon {
|
||||
|
@@ -31,9 +31,7 @@
|
||||
class="mdl-data-table mdl-js-data-table mdl-shadow--2dp full-width">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<div class="truncate"><b> {{ 'SEARCH.RESULTS.NONE' | translate:{searchTerm: searchTerm} }}</b></div>
|
||||
</td>
|
||||
<td class="mdl-data-table__cell--non-numeric">{{ 'SEARCH.RESULTS.NONE' | translate:{searchTerm: searchTerm} }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@@ -15,11 +15,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
import { PLATFORM_PIPES } from '@angular/core';
|
||||
import { it, describe, expect, inject, beforeEachProviders, beforeEach } from '@angular/core/testing';
|
||||
import { TestComponentBuilder } from '@angular/compiler/testing';
|
||||
import { RouteParams } from '@angular/router-deprecated';
|
||||
import { ReflectiveInjector, SimpleChange } from '@angular/core';
|
||||
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Observable } from 'rxjs/Rx';
|
||||
import { AlfrescoSearchComponent } from './alfresco-search.component';
|
||||
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
|
||||
import { TranslationMock } from './../assets/translation.service.mock';
|
||||
@@ -30,14 +29,13 @@ import {
|
||||
AlfrescoAuthenticationService,
|
||||
AlfrescoContentService,
|
||||
AlfrescoTranslationService,
|
||||
AlfrescoPipeTranslate
|
||||
CoreModule
|
||||
} from 'ng2-alfresco-core';
|
||||
|
||||
declare let jasmine: any;
|
||||
|
||||
describe('AlfrescoSearchComponent', () => {
|
||||
|
||||
let alfrescoSearchComponentFixture, element, component;
|
||||
let fixture: ComponentFixture<AlfrescoSearchComponent>, element: HTMLElement;
|
||||
let component: AlfrescoSearchComponent;
|
||||
|
||||
let result = {
|
||||
list: {
|
||||
@@ -48,7 +46,7 @@ describe('AlfrescoSearchComponent', () => {
|
||||
name: 'MyDoc',
|
||||
isFile: true,
|
||||
content: {
|
||||
mimetype: 'text/plain'
|
||||
mimeType: 'text/plain'
|
||||
},
|
||||
createdByUser: {
|
||||
displayName: 'John Doe'
|
||||
@@ -62,40 +60,65 @@ describe('AlfrescoSearchComponent', () => {
|
||||
}
|
||||
};
|
||||
|
||||
let folderResult = {
|
||||
list: {
|
||||
entries: [
|
||||
{
|
||||
entry: {
|
||||
id: '123',
|
||||
name: 'MyFolder',
|
||||
isFile : false,
|
||||
isFolder : true,
|
||||
createdByUser: {
|
||||
displayName: 'John Doe'
|
||||
},
|
||||
modifiedByUser: {
|
||||
displayName: 'John Doe'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let noResult = {
|
||||
list: {
|
||||
entries: []
|
||||
}
|
||||
};
|
||||
|
||||
beforeEachProviders(() => {
|
||||
return [
|
||||
{ provide: PLATFORM_PIPES, useValue: AlfrescoPipeTranslate, multi: true },
|
||||
AlfrescoSearchService,
|
||||
{provide: AlfrescoTranslationService, useClass: TranslationMock},
|
||||
AlfrescoThumbnailService,
|
||||
AlfrescoSettingsService,
|
||||
AlfrescoApiService,
|
||||
AlfrescoAuthenticationService,
|
||||
AlfrescoContentService
|
||||
];
|
||||
});
|
||||
let errorJson = {
|
||||
error: {
|
||||
errorKey: 'Search failed',
|
||||
statusCode: 400,
|
||||
briefSummary: '08220082 search failed',
|
||||
stackTrace: 'For security reasons the stack trace is no longer displayed, but the property is kept for previous versions.',
|
||||
descriptionURL: 'https://api-explorer.alfresco.com'
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
|
||||
return tcb
|
||||
.createAsync(AlfrescoSearchComponent)
|
||||
.then(fixture => {
|
||||
jasmine.Ajax.install();
|
||||
alfrescoSearchComponentFixture = fixture;
|
||||
element = alfrescoSearchComponentFixture.nativeElement;
|
||||
component = alfrescoSearchComponentFixture.componentInstance;
|
||||
});
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
CoreModule
|
||||
],
|
||||
declarations: [ AlfrescoSearchComponent ], // declare the test component
|
||||
providers: [
|
||||
AlfrescoSearchService,
|
||||
{provide: AlfrescoTranslationService, useClass: TranslationMock},
|
||||
AlfrescoThumbnailService,
|
||||
AlfrescoSettingsService,
|
||||
AlfrescoApiService,
|
||||
AlfrescoAuthenticationService,
|
||||
AlfrescoContentService
|
||||
]
|
||||
}).compileComponents().then(() => {
|
||||
fixture = TestBed.createComponent(AlfrescoSearchComponent);
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
});
|
||||
}));
|
||||
|
||||
afterEach(() => {
|
||||
jasmine.Ajax.uninstall();
|
||||
});
|
||||
|
||||
it('should not have a search term by default', () => {
|
||||
let search = new AlfrescoSearchComponent(null, null, null, null);
|
||||
expect(search).toBeDefined();
|
||||
@@ -103,92 +126,148 @@ describe('AlfrescoSearchComponent', () => {
|
||||
});
|
||||
|
||||
it('should take the provided search term from query param provided via RouteParams', () => {
|
||||
let search = new AlfrescoSearchComponent(null, null, null, new RouteParams({q: 'exampleTerm692'}));
|
||||
let injector = ReflectiveInjector.resolveAndCreate([
|
||||
{ provide: ActivatedRoute, useValue: { params: Observable.from([{ q: 'exampleTerm692' }]) } }
|
||||
]);
|
||||
let search = new AlfrescoSearchComponent(null, null, null, injector.get(ActivatedRoute));
|
||||
search.ngOnInit();
|
||||
expect(search.searchTerm).toBe('exampleTerm692');
|
||||
});
|
||||
|
||||
it('should have a null search term if no query param provided via RouteParams', () => {
|
||||
let search = new AlfrescoSearchComponent(null, null, null, new RouteParams({}));
|
||||
let injector = ReflectiveInjector.resolveAndCreate([
|
||||
AlfrescoSearchService,
|
||||
AlfrescoAuthenticationService,
|
||||
AlfrescoSettingsService,
|
||||
AlfrescoApiService,
|
||||
{ provide: ActivatedRoute, useValue: { params: Observable.from([{}]) } }
|
||||
]);
|
||||
let search = new AlfrescoSearchComponent(injector.get(AlfrescoSearchService), null, null, injector.get(ActivatedRoute));
|
||||
search.ngOnInit();
|
||||
expect(search.searchTerm).toBeNull();
|
||||
});
|
||||
|
||||
it('should setup i18n folder', () => {
|
||||
|
||||
let translation = jasmine.createSpyObj('AlfrescoTranslationService', [
|
||||
'addTranslationFolder'
|
||||
]);
|
||||
|
||||
let search = new AlfrescoSearchComponent(null, translation, null, null);
|
||||
expect(search).toBeDefined();
|
||||
expect(translation.addTranslationFolder).toHaveBeenCalledWith('node_modules/ng2-alfresco-search/dist/src');
|
||||
let translationService = fixture.debugElement.injector.get(AlfrescoTranslationService);
|
||||
spyOn(translationService, 'addTranslationFolder');
|
||||
fixture.detectChanges();
|
||||
expect(translationService.addTranslationFolder).toHaveBeenCalledWith('node_modules/ng2-alfresco-search/dist/src');
|
||||
});
|
||||
|
||||
describe('Rendering search results', () => {
|
||||
|
||||
it('should display search results when a search term is provided', (done) => {
|
||||
|
||||
component.searchTerm = { currentValue: 'searchTerm', previousValue: ''};
|
||||
component.ngOnChanges({searchTerm: component.searchTerm});
|
||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||
spyOn(searchService, 'getSearchNodesPromise')
|
||||
.and.returnValue(Promise.resolve(result));
|
||||
|
||||
component.resultsEmitter.subscribe(x => {
|
||||
alfrescoSearchComponentFixture.detectChanges();
|
||||
fixture.detectChanges();
|
||||
expect(searchService.getSearchNodesPromise).toHaveBeenCalled();
|
||||
expect(element.querySelector('#result_user_0')).not.toBeNull();
|
||||
expect(element.querySelector('#result_user_0').innerHTML).toBe('John Doe');
|
||||
expect(element.querySelector('#result_name_0').innerHTML).toBe('MyDoc');
|
||||
done();
|
||||
});
|
||||
|
||||
jasmine.Ajax.requests.mostRecent().respondWith({
|
||||
status: 200,
|
||||
contentType: 'json',
|
||||
responseText: result
|
||||
});
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnInit();
|
||||
});
|
||||
|
||||
|
||||
it('should display no result if no result are returned', (done) => {
|
||||
|
||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||
spyOn(searchService, 'getSearchNodesPromise')
|
||||
.and.returnValue(Promise.resolve(noResult));
|
||||
|
||||
component.resultsEmitter.subscribe(x => {
|
||||
alfrescoSearchComponentFixture.detectChanges();
|
||||
expect(element.querySelector('#search_no_result')).not.toBe(null);
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#search_no_result')).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
|
||||
component.searchTerm = { currentValue: 'searchTerm', previousValue: ''};
|
||||
component.ngOnChanges({searchTerm: component.searchTerm});
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnInit();
|
||||
});
|
||||
|
||||
jasmine.Ajax.requests.mostRecent().respondWith({
|
||||
status: 200,
|
||||
contentType: 'json',
|
||||
responseText: noResult
|
||||
it('should display an error if an error is encountered running the search', (done) => {
|
||||
|
||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||
spyOn(searchService, 'getSearchNodesPromise')
|
||||
.and.returnValue(Promise.reject(errorJson));
|
||||
|
||||
component.errorEmitter.subscribe(() => {
|
||||
fixture.detectChanges();
|
||||
let resultsEl = element.querySelector('[data-automation-id="search_result_table"]');
|
||||
let errorEl = element.querySelector('[data-automation-id="search_error_message"]');
|
||||
expect(resultsEl).toBeNull();
|
||||
expect(errorEl).not.toBeNull();
|
||||
expect((<any>errorEl).innerText).toBe('SEARCH.RESULTS.ERROR');
|
||||
done();
|
||||
});
|
||||
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnInit();
|
||||
});
|
||||
|
||||
it('should update search results when the search term input is changed', (done) => {
|
||||
|
||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||
spyOn(searchService, 'getSearchNodesPromise')
|
||||
.and.returnValue(Promise.resolve(result));
|
||||
|
||||
component.resultsEmitter.subscribe(x => {
|
||||
fixture.detectChanges();
|
||||
expect(searchService.getSearchNodesPromise).toHaveBeenCalledWith('searchTerm2');
|
||||
expect(element.querySelector('#result_user_0')).not.toBeNull();
|
||||
expect(element.querySelector('#result_user_0').innerHTML).toBe('John Doe');
|
||||
expect(element.querySelector('#result_name_0').innerHTML).toBe('MyDoc');
|
||||
done();
|
||||
});
|
||||
|
||||
component.ngOnChanges({searchTerm: new SimpleChange('', 'searchTerm2')});
|
||||
});
|
||||
});
|
||||
|
||||
describe('search result actions', () => {
|
||||
|
||||
it('should emit preview when file item clicked', () => {
|
||||
component.results = [{
|
||||
entry: {
|
||||
id: '123',
|
||||
name: 'MyDoc',
|
||||
content: {
|
||||
mimetype: 'text/plain'
|
||||
},
|
||||
isFile: true
|
||||
}
|
||||
}];
|
||||
it('should emit preview when file item clicked', (done) => {
|
||||
|
||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||
spyOn(searchService, 'getSearchNodesPromise')
|
||||
.and.returnValue(Promise.resolve(result));
|
||||
|
||||
component.resultsEmitter.subscribe(() => {
|
||||
fixture.detectChanges();
|
||||
(<HTMLTableRowElement> element.querySelector('#result_row_0')).click();
|
||||
});
|
||||
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnInit();
|
||||
|
||||
alfrescoSearchComponentFixture.detectChanges(component.results[0]);
|
||||
component.preview.subscribe(e => {
|
||||
expect(e.value).toBe(component.results[0]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not emit preview when non-file item is clicked', (done) => {
|
||||
|
||||
let searchService = fixture.debugElement.injector.get(AlfrescoSearchService);
|
||||
spyOn(searchService, 'getSearchNodesPromise')
|
||||
.and.returnValue(Promise.resolve(folderResult));
|
||||
|
||||
spyOn(component.preview, 'emit');
|
||||
component.resultsEmitter.subscribe(x => {
|
||||
fixture.detectChanges();
|
||||
(<HTMLTableRowElement> element.querySelector('#result_row_0')).click();
|
||||
expect(component.preview.emit).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
|
||||
jasmine.Ajax.requests.mostRecent().respondWith({
|
||||
status: 200,
|
||||
contentType: 'text/plain',
|
||||
responseText: '<div></div>'
|
||||
});
|
||||
component.searchTerm = 'searchTerm';
|
||||
component.ngOnInit();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
*/
|
||||
|
@@ -15,8 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, EventEmitter, Input, Output, OnChanges, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Component, EventEmitter, Input, Output, Optional, OnChanges, SimpleChanges, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
import { AlfrescoSearchService } from './../services/alfresco-search.service';
|
||||
import { AlfrescoThumbnailService } from './../services/alfresco-thumbnail.service';
|
||||
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
|
||||
@@ -42,34 +42,39 @@ export class AlfrescoSearchComponent implements OnChanges, OnInit {
|
||||
@Output()
|
||||
resultsEmitter = new EventEmitter();
|
||||
|
||||
results: any;
|
||||
@Output()
|
||||
errorEmitter = new EventEmitter();
|
||||
|
||||
results: any = null;
|
||||
|
||||
errorMessage;
|
||||
|
||||
route: any[] = [];
|
||||
queryParamName = 'q';
|
||||
|
||||
constructor(private alfrescoSearchService: AlfrescoSearchService,
|
||||
private translate: AlfrescoTranslationService,
|
||||
private _alfrescoThumbnailService: AlfrescoThumbnailService,
|
||||
private activatedRoute: ActivatedRoute) {
|
||||
|
||||
if (translate !== null) {
|
||||
translate.addTranslationFolder('node_modules/ng2-alfresco-search/dist/src');
|
||||
}
|
||||
|
||||
this.results = null;
|
||||
@Optional() private route: ActivatedRoute) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.activatedRoute.params.subscribe(params => {
|
||||
this.searchTerm = params['q'];
|
||||
if (this.translate !== null) {
|
||||
this.translate.addTranslationFolder('node_modules/ng2-alfresco-search/dist/src');
|
||||
}
|
||||
if (this.route) {
|
||||
this.route.params.forEach((params: Params) => {
|
||||
this.searchTerm = params.hasOwnProperty(this.queryParamName) ? params[this.queryParamName] : null;
|
||||
this.displaySearchResults(this.searchTerm);
|
||||
});
|
||||
} else {
|
||||
this.displaySearchResults(this.searchTerm);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes): void {
|
||||
if (changes.searchTerm) {
|
||||
this.displaySearchResults(changes.searchTerm.currentValue);
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes['searchTerm']) {
|
||||
this.searchTerm = changes['searchTerm'].currentValue;
|
||||
this.displaySearchResults(this.searchTerm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +108,7 @@ export class AlfrescoSearchComponent implements OnChanges, OnInit {
|
||||
* @param searchTerm Search query entered by user
|
||||
*/
|
||||
public displaySearchResults(searchTerm): void {
|
||||
if (searchTerm !== null) {
|
||||
if (searchTerm !== null && this.alfrescoSearchService !== null) {
|
||||
this.alfrescoSearchService
|
||||
.getLiveSearchResults(searchTerm)
|
||||
.subscribe(
|
||||
@@ -115,6 +120,7 @@ export class AlfrescoSearchComponent implements OnChanges, OnInit {
|
||||
error => {
|
||||
this.results = null;
|
||||
this.errorMessage = <any>error;
|
||||
this.errorEmitter.emit(error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@@ -15,14 +15,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ReflectiveInjector } from '@angular/core';
|
||||
import { AlfrescoThumbnailService } from './alfresco-thumbnail.service';
|
||||
import { AlfrescoApiService, AlfrescoAuthenticationService, AlfrescoContentService, AlfrescoSettingsService } from 'ng2-alfresco-core';
|
||||
|
||||
describe('AlfrescoThumbnailService', () => {
|
||||
|
||||
let injector: ReflectiveInjector;
|
||||
let service: AlfrescoThumbnailService;
|
||||
|
||||
beforeEach(() => {
|
||||
service = new AlfrescoThumbnailService(null);
|
||||
injector = ReflectiveInjector.resolveAndCreate([
|
||||
AlfrescoApiService,
|
||||
AlfrescoAuthenticationService,
|
||||
AlfrescoContentService,
|
||||
AlfrescoSettingsService,
|
||||
AlfrescoThumbnailService
|
||||
]);
|
||||
|
||||
service = injector.get(AlfrescoThumbnailService);
|
||||
});
|
||||
|
||||
it('should return the correct icon for a plain text file', () => {
|
||||
@@ -41,4 +52,9 @@ describe('AlfrescoThumbnailService', () => {
|
||||
expect(service.getMimeTypeIcon('x-unknown/yyy')).toBe('ft_ic_miscellaneous.svg');
|
||||
});
|
||||
|
||||
it('should return the thumbnail URL for a content item', () => {
|
||||
spyOn(service.contentService, 'getDocumentThumbnailUrl').and.returnValue('/fake-thumbnail.png');
|
||||
expect(service.getDocumentThumbnailUrl({})).toBe('/fake-thumbnail.png');
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -50,7 +50,7 @@ export class AlfrescoThumbnailService {
|
||||
'application/vnd.apple.numbers': 'ft_ic_spreadsheet'
|
||||
};
|
||||
|
||||
constructor(private contentService: AlfrescoContentService) {
|
||||
constructor(public contentService: AlfrescoContentService) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
Reference in New Issue
Block a user