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
+**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.
+**searchSubmit**: Emitted when the search form is submitted. The search term is provided in the 'value' property of the returned object.
#### 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)
**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: `
-
+
Authentication failed to ip {{ host }}
@@ -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();
}
diff --git a/ng2-components/ng2-alfresco-search/index.ts b/ng2-components/ng2-alfresco-search/index.ts
index c3d7b740af..c2ce45856f 100644
--- a/ng2-components/ng2-alfresco-search/index.ts
+++ b/ng2-components/ng2-alfresco-search/index.ts
@@ -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
diff --git a/ng2-components/ng2-alfresco-search/karma.conf.js b/ng2-components/ng2-alfresco-search/karma.conf.js
index 2aa3ff5612..7d786304a2 100644
--- a/ng2-components/ng2-alfresco-search/karma.conf.js
+++ b/ng2-components/ng2-alfresco-search/karma.conf.js
@@ -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},
diff --git a/ng2-components/ng2-alfresco-search/src/components/alfresco-search-autocomplete.component.html b/ng2-components/ng2-alfresco-search/src/components/alfresco-search-autocomplete.component.html
index c6d3011623..66fcf83db3 100644
--- a/ng2-components/ng2-alfresco-search/src/components/alfresco-search-autocomplete.component.html
+++ b/ng2-components/ng2-alfresco-search/src/components/alfresco-search-autocomplete.component.html
@@ -21,5 +21,13 @@
+
+
+
+ {{ 'SEARCH.RESULTS.ERROR' | translate:{errorMessage: errorMessage} }} |
+
+
+
{{ 'SEARCH.RESULTS.ERROR' |
translate:{errorMessage: errorMessage} }}
diff --git a/ng2-components/ng2-alfresco-search/src/components/alfresco-search-autocomplete.component.spec.ts b/ng2-components/ng2-alfresco-search/src/components/alfresco-search-autocomplete.component.spec.ts
index 7e0b47e6b6..7694f261e1 100644
--- a/ng2-components/ng2-alfresco-search/src/components/alfresco-search-autocomplete.component.spec.ts
+++ b/ng2-components/ng2-alfresco-search/src/components/alfresco-search-autocomplete.component.spec.ts
@@ -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
, 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('MyDoc');
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 = 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 = 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 = 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();
+ ( 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();
+ ( 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: ''}});
});
});
-*/
diff --git a/ng2-components/ng2-alfresco-search/src/components/alfresco-search-autocomplete.component.ts b/ng2-components/ng2-alfresco-search/src/components/alfresco-search-autocomplete.component.ts
index 65e552d651..69e0859c16 100644
--- a/ng2-components/ng2-alfresco-search/src/components/alfresco-search-autocomplete.component.ts
+++ b/ng2-components/ng2-alfresco-search/src/components/alfresco-search-autocomplete.component.ts
@@ -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 = error;
+ this.errorEmitter.emit(error);
}
);
}
diff --git a/ng2-components/ng2-alfresco-search/src/components/alfresco-search-control.component.html b/ng2-components/ng2-alfresco-search/src/components/alfresco-search-control.component.html
index c38ca8884a..868063b0f5 100644
--- a/ng2-components/ng2-alfresco-search/src/components/alfresco-search-control.component.html
+++ b/ng2-components/ng2-alfresco-search/src/components/alfresco-search-control.component.html
@@ -1,4 +1,4 @@
-
-