diff --git a/projects/aca-content/src/lib/components/favorites/favorites.component.spec.ts b/projects/aca-content/src/lib/components/favorites/favorites.component.spec.ts index b1b51b1262..a025b9c823 100644 --- a/projects/aca-content/src/lib/components/favorites/favorites.component.spec.ts +++ b/projects/aca-content/src/lib/components/favorites/favorites.component.spec.ts @@ -31,6 +31,7 @@ import { AppTestingModule } from '../../testing/app-testing.module'; import { AppService, ContentApiService } from '@alfresco/aca-shared'; import { getTitleElementText } from '../../testing/test-utils'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { testHeader } from '../../testing/document-base-page-utils'; describe('FavoritesComponent', () => { let fixture: ComponentFixture; @@ -140,4 +141,6 @@ describe('FavoritesComponent', () => { fixture.detectChanges(); expect(getTitleElementText(fixture)).toBe('APP.HEADER.SELECTED'); }); + + testHeader(FavoritesComponent); }); diff --git a/projects/aca-content/src/lib/components/files/files.component.spec.ts b/projects/aca-content/src/lib/components/files/files.component.spec.ts index aecf55b6ca..fd8daea467 100644 --- a/projects/aca-content/src/lib/components/files/files.component.spec.ts +++ b/projects/aca-content/src/lib/components/files/files.component.spec.ts @@ -35,6 +35,7 @@ import { By } from '@angular/platform-browser'; import { NodeEntry, NodePaging, Node, PathElement } from '@alfresco/js-api'; import { DocumentListPresetRef } from '@alfresco/adf-extensions'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { testHeader } from '../../testing/document-base-page-utils'; describe('FilesComponent', () => { let node; @@ -473,4 +474,6 @@ describe('FilesComponent', () => { expect(resetNewFolderPaginationSpy).not.toHaveBeenCalled(); }); }); + + testHeader(FilesComponent); }); diff --git a/projects/aca-content/src/lib/components/recent-files/recent-files.component.spec.ts b/projects/aca-content/src/lib/components/recent-files/recent-files.component.spec.ts index c5ae08ba7a..bc3bd6b889 100644 --- a/projects/aca-content/src/lib/components/recent-files/recent-files.component.spec.ts +++ b/projects/aca-content/src/lib/components/recent-files/recent-files.component.spec.ts @@ -31,6 +31,7 @@ import { NodePaging, SearchApi } from '@alfresco/js-api'; import { of } from 'rxjs'; import { getTitleElementText } from '../../testing/test-utils'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { testHeader } from '../../testing/document-base-page-utils'; describe('RecentFilesComponent', () => { let fixture: ComponentFixture; @@ -110,4 +111,6 @@ describe('RecentFilesComponent', () => { fixture.detectChanges(); expect(getTitleElementText(fixture)).toBe('APP.HEADER.SELECTED'); }); + + testHeader(RecentFilesComponent); }); diff --git a/projects/aca-content/src/lib/components/search/search-results/search-results.component.spec.ts b/projects/aca-content/src/lib/components/search/search-results/search-results.component.spec.ts index 274a971bd1..841025d0b3 100644 --- a/projects/aca-content/src/lib/components/search/search-results/search-results.component.spec.ts +++ b/projects/aca-content/src/lib/components/search/search-results/search-results.component.spec.ts @@ -34,6 +34,7 @@ import { BehaviorSubject, Subject } from 'rxjs'; import { AppTestingModule } from '../../../testing/app-testing.module'; import { AppService } from '@alfresco/aca-shared'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { testHeader } from '../../../testing/document-base-page-utils'; describe('SearchComponent', () => { let component: SearchResultsComponent; @@ -279,4 +280,6 @@ describe('SearchComponent', () => { expect(queryBuilder.userQuery).toBe(`((=cm:tag:"orange"))`); expect(queryBuilder.update).toHaveBeenCalled(); }); + + testHeader(SearchResultsComponent, false); }); diff --git a/projects/aca-content/src/lib/components/shared-files/shared-files.component.spec.ts b/projects/aca-content/src/lib/components/shared-files/shared-files.component.spec.ts index 2b873f3c75..7109e85735 100644 --- a/projects/aca-content/src/lib/components/shared-files/shared-files.component.spec.ts +++ b/projects/aca-content/src/lib/components/shared-files/shared-files.component.spec.ts @@ -31,8 +31,9 @@ import { By } from '@angular/platform-browser'; import { SharedLinkPaging } from '@alfresco/js-api'; import { AppService } from '@alfresco/aca-shared'; import { getTitleElementText } from '../../testing/test-utils'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute, NavigationStart, Router } from '@angular/router'; import { MatSnackBarModule } from '@angular/material/snack-bar'; +import { testHeader } from '../../testing/document-base-page-utils'; describe('SharedFilesComponent', () => { let fixture: ComponentFixture; @@ -40,7 +41,8 @@ describe('SharedFilesComponent', () => { let component: SharedFilesComponent; const routerMock = { routerState: { root: '' }, - url: 'shared-files' + url: 'shared-files', + events: new Subject() }; const route = { snapshot: { @@ -104,4 +106,6 @@ describe('SharedFilesComponent', () => { const pagination = fixture.debugElement.query(By.css('.adf-pagination')); expect(pagination).toBeNull(); }); + + testHeader(SharedFilesComponent); }); diff --git a/projects/aca-content/src/lib/store/effects/search-ai.effects.spec.ts b/projects/aca-content/src/lib/store/effects/search-ai.effects.spec.ts new file mode 100644 index 0000000000..cb187dc128 --- /dev/null +++ b/projects/aca-content/src/lib/store/effects/search-ai.effects.spec.ts @@ -0,0 +1,77 @@ +/*! + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Alfresco Example Content Application + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * from Hyland Software. If not, see . + */ + +import { TestBed } from '@angular/core/testing'; +import { EffectsModule } from '@ngrx/effects'; +import { SearchAiEffects } from './search-ai.effects'; +import { Store } from '@ngrx/store'; +import { AppTestingModule } from '../../testing/app-testing.module'; +import { SearchAiNavigationService } from '../../services/search-ai-navigation.service'; +import { AppStore, SearchByTermAiAction, ToggleAISearchInput } from '@alfresco/aca-shared/store'; +import { SearchAiService } from '@alfresco/adf-content-services'; + +describe('SearchAiEffects', () => { + let store: Store; + + const agentId = '1'; + const searchTerm = 'test'; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [AppTestingModule, EffectsModule.forRoot([SearchAiEffects])] + }); + store = TestBed.inject(Store); + }); + + describe('searchByTerm$', () => { + it('should call navigateToSearchAi on SearchAiNavigationService', () => { + const searchAiNavigationService = TestBed.inject(SearchAiNavigationService); + spyOn(searchAiNavigationService, 'navigateToSearchAi'); + + store.dispatch( + new SearchByTermAiAction({ + searchTerm, + agentId + }) + ); + expect(searchAiNavigationService.navigateToSearchAi).toHaveBeenCalledWith({ + query: searchTerm, + agentId + }); + }); + }); + describe('toggleAISearchInput$', () => { + it('should call updateSearchAiInputState on SearchAiService', () => { + const searchAiService = TestBed.inject(SearchAiService); + spyOn(searchAiService, 'updateSearchAiInputState'); + + store.dispatch(new ToggleAISearchInput(agentId, searchTerm)); + expect(searchAiService.updateSearchAiInputState).toHaveBeenCalledWith({ + active: true, + selectedAgentId: agentId, + searchTerm + }); + }); + }); +}); diff --git a/projects/aca-content/src/lib/testing/document-base-page-utils.ts b/projects/aca-content/src/lib/testing/document-base-page-utils.ts new file mode 100644 index 0000000000..761db8e558 --- /dev/null +++ b/projects/aca-content/src/lib/testing/document-base-page-utils.ts @@ -0,0 +1,103 @@ +/*! + * Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Alfresco Example Content Application + * + * This file is part of the Alfresco Example Content Application. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * The Alfresco Example Content Application is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Alfresco Example Content Application is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * from Hyland Software. If not, see . + */ + +import { BehaviorSubject, Subject } from 'rxjs'; +import { AgentService, SearchAiInputState, SearchAiService } from '@alfresco/adf-content-services'; +import { DebugElement, Type } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { SearchAiInputContainerComponent } from '../components/knowledge-retrieval/search-ai/search-ai-input-container/search-ai-input-container.component'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { PageComponent } from '@alfresco/aca-shared'; +import { Agent } from '@alfresco/js-api/typings'; + +export const testHeader = (component: Type, checkHeaderVisibility = true) => { + describe('Header', () => { + let fixture: ComponentFixture; + let toggleSearchAiInput$: BehaviorSubject; + + const getSearchAiInputElement = (): DebugElement => fixture.debugElement.query(By.directive(SearchAiInputContainerComponent)); + + const getHeaderElement = (): DebugElement => fixture.debugElement.query(By.css('.aca-header-container')); + + beforeEach(() => { + fixture = TestBed.createComponent(component); + toggleSearchAiInput$ = new BehaviorSubject({ + searchTerm: '' + } as SearchAiInputState); + TestBed.inject(SearchAiService).toggleSearchAiInput$ = toggleSearchAiInput$; + spyOn(TestBed.inject(AgentService), 'getAgents').and.returnValue(new Subject()); + fixture.detectChanges(); + }); + + it('should display ai input if input is active', () => { + toggleSearchAiInput$.next({ + active: true, + searchTerm: '' + }); + + fixture.detectChanges(); + expect(getSearchAiInputElement()).not.toBeNull(); + }); + + it('should not display ai input if input is not active', () => { + toggleSearchAiInput$.next({ + active: false, + searchTerm: '' + }); + + fixture.detectChanges(); + expect(getSearchAiInputElement()).toBeNull(); + }); + + it('should not display ai input by default', () => { + expect(getSearchAiInputElement()).toBeNull(); + }); + + if (checkHeaderVisibility) { + it('should not display header if input is active', () => { + toggleSearchAiInput$.next({ + active: true, + searchTerm: '' + }); + + fixture.detectChanges(); + expect(getHeaderElement()).toBeNull(); + }); + + it('should display header if input is not active', () => { + toggleSearchAiInput$.next({ + active: false, + searchTerm: '' + }); + + fixture.detectChanges(); + expect(getHeaderElement()).not.toBeNull(); + }); + + it('should display header by default', () => { + expect(getHeaderElement()).not.toBeNull(); + }); + } + }); +}; diff --git a/projects/aca-shared/rules/src/app.rules.spec.ts b/projects/aca-shared/rules/src/app.rules.spec.ts index d5fe674a96..e59ef4e478 100644 --- a/projects/aca-shared/rules/src/app.rules.spec.ts +++ b/projects/aca-shared/rules/src/app.rules.spec.ts @@ -26,6 +26,7 @@ import * as app from './app.rules'; import { TestRuleContext } from './test-rule-context'; import { NodeEntry, RepositoryInfo, StatusInfo } from '@alfresco/js-api'; import { getFileExtension } from './app.rules'; +import { AppConfigService } from '@alfresco/adf-core'; describe('app.evaluators', () => { let context: TestRuleContext; @@ -540,44 +541,117 @@ describe('app.evaluators', () => { }); }); - describe('isKnowledgeRetrievalEnabled', () => { - it('should call context.appConfig.get with correct parameters', () => { - context.appConfig = { get: jasmine.createSpy() } as any; + describe('canDisplayKnowledgeRetrievalButton', () => { + const testCanDisplayKnowledgeRetrievalButton = (testTitle: string, url: string, knowledgeRetrievalEnabled: boolean, expected: boolean) => { + it(testTitle, () => { + context.appConfig = jasmine.createSpyObj({ + get: knowledgeRetrievalEnabled + }); + context.navigation.url = url; + expect(app.canDisplayKnowledgeRetrievalButton(context)).toBe(expected); + }); + }; - app.canDisplayKnowledgeRetrievalButton(context); - expect(context.appConfig.get).toHaveBeenCalledWith('plugins.knowledgeRetrievalEnabled', true); - }); + [ + { + pageName: 'personal files', + pageUrl: '/personal-files' + }, + { + pageName: 'shared files', + pageUrl: '/shared' + }, + { + pageName: 'recent files', + pageUrl: '/recent-files' + }, + { + pageName: 'favorites', + pageUrl: '/favorites' + }, + { + pageName: 'library content', + pageUrl: '/libraries/some-id' + } + ].forEach((testCase) => { + testCanDisplayKnowledgeRetrievalButton( + `should return false if get from appConfig returns false and navigation is ${testCase.pageName}`, + testCase.pageUrl, + false, + false + ); + + testCanDisplayKnowledgeRetrievalButton( + `should return true if get from appConfig returns true and navigation is ${testCase.pageName}`, + testCase.pageUrl, + true, + true + ); + }); + + testCanDisplayKnowledgeRetrievalButton( + 'should return false if get from appConfig returns false and navigation is search results but not for libraries', + '/search', + false, + false + ); + + testCanDisplayKnowledgeRetrievalButton( + 'should return true if get from appConfig returns true and navigation is search results but not for libraries', + '/search', + true, + true + ); + + testCanDisplayKnowledgeRetrievalButton( + 'should return false if get from appConfig returns false and navigation is search results for libraries', + '/search/libraries', + false, + false + ); + + testCanDisplayKnowledgeRetrievalButton( + 'should return false if get from appConfig returns true and navigation is search results for libraries', + '/search/libraries', + true, + false + ); + + testCanDisplayKnowledgeRetrievalButton( + 'should return false if get from appConfig returns false and navigation is libraries', + '/libraries', + false, + false + ); + + testCanDisplayKnowledgeRetrievalButton( + 'should return false if get from appConfig returns true and navigation is libraries', + '/libraries', + true, + false + ); + + testCanDisplayKnowledgeRetrievalButton( + 'should return false if get from appConfig returns false and navigation is incorrect', + '/my-special-files', + false, + false + ); + + testCanDisplayKnowledgeRetrievalButton( + 'should return false if get from appConfig returns true but navigation is incorrect', + '/my-special-files', + true, + false + ); + + it('should call get on context.appConfig with correct parameters', () => { + context.appConfig = jasmine.createSpyObj({ + get: false + }); - it('should return false if get from appConfig returns false', () => { - expect( - app.canDisplayKnowledgeRetrievalButton({ - appConfig: { - get: () => false - } - } as any) - ).toBeFalse(); - }); - - it('should return true if get from appConfig returns true and navigation is correct', () => { - expect( - app.canDisplayKnowledgeRetrievalButton({ - navigation: { url: '/personal-files' }, - appConfig: { - get: () => true - } - } as any) - ).toBeTrue(); - }); - - it('should return false if get from appConfig returns true, but navigation is not correct', () => { - expect( - app.canDisplayKnowledgeRetrievalButton({ - navigation: { url: '/my-special-files' }, - appConfig: { - get: () => true - } - } as any) - ).toBeFalse(); + app.canDisplayKnowledgeRetrievalButton(context); + expect(context.appConfig.get).toHaveBeenCalledWith('plugins.knowledgeRetrievalEnabled', false); }); }); diff --git a/projects/aca-shared/src/lib/components/document-base-page/document-base-page.spec.ts b/projects/aca-shared/src/lib/components/document-base-page/document-base-page.spec.ts index 8d6264154b..8dde3db5f0 100644 --- a/projects/aca-shared/src/lib/components/document-base-page/document-base-page.spec.ts +++ b/projects/aca-shared/src/lib/components/document-base-page/document-base-page.spec.ts @@ -30,7 +30,7 @@ import { NodeEntry, NodePaging } from '@alfresco/js-api'; import { DocumentBasePageService } from './document-base-page.service'; import { Store } from '@ngrx/store'; import { Component } from '@angular/core'; -import { DiscoveryApiService, DocumentListComponent, DocumentListService, SearchAiService } from '@alfresco/adf-content-services'; +import { DiscoveryApiService, DocumentListComponent, DocumentListService, SearchAiInputState, SearchAiService } from '@alfresco/adf-content-services'; import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { AuthModule, UserPreferencesService } from '@alfresco/adf-core'; import { of, Subscription } from 'rxjs'; @@ -278,6 +278,22 @@ describe('PageComponent', () => { expect(searchAiService.updateSearchAiInputState).not.toHaveBeenCalled(); }); }); + + describe('SearchAiService toggleSearchAiInput$ event handler', () => { + it('should set searchAiInputState', () => { + const initialSearchAiInputState = component.searchAiInputState; + const searchAiInputState: SearchAiInputState = { + active: true + }; + searchAiService.toggleSearchAiInput$ = of(searchAiInputState); + + component.ngOnInit(); + expect(component.searchAiInputState).toBe(searchAiInputState); + expect(initialSearchAiInputState).toEqual({ + active: false + }); + }); + }); }); describe('Info Drawer state', () => {