diff --git a/src/app/+admin/admin-sidebar/admin-sidebar.component.spec.ts b/src/app/+admin/admin-sidebar/admin-sidebar.component.spec.ts new file mode 100644 index 0000000000..8b16366fa6 --- /dev/null +++ b/src/app/+admin/admin-sidebar/admin-sidebar.component.spec.ts @@ -0,0 +1,78 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { TranslateModule } from '@ngx-translate/core'; +import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core'; +import { AdminSidebarComponent } from './admin-sidebar.component'; +import { MenuService } from '../../shared/menu/menu.service'; +import { MenuServiceStub } from '../../shared/testing/menu-service-stub'; +import { CSSVariableService } from '../../shared/sass-helper/sass-helper.service'; +import { CSSVariableServiceStub } from '../../shared/testing/css-variable-service-stub'; +import { AuthServiceStub } from '../../shared/testing/auth-service-stub'; +import { AuthService } from '../../core/auth/auth.service'; +import { NgComponentOutlet } from '@angular/common'; +import { MockDirective } from 'ng-mocks'; + +fdescribe('AdminSidebarComponent', () => { + let comp: AdminSidebarComponent; + let fixture: ComponentFixture; + let menuService: AdminSidebarComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), NoopAnimationsModule], + declarations: [AdminSidebarComponent, MockDirective(NgComponentOutlet)], + providers: [ + { provide: Injector, useValue: {} }, + { provide: MenuService, useClass: MenuServiceStub }, + { provide: CSSVariableService, useClass: CSSVariableServiceStub }, + { provide: AuthService, useClass: AuthServiceStub } + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(AdminSidebarComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AdminSidebarComponent); + comp = fixture.componentInstance; // SearchPageComponent test instance + menuService = (comp as any).menuService; + // spyOn(comp as any, 'getSectionDataInjector').and.returnValue(new Map()); + // spyOn(comp as any, 'getSectionComponent').and.returnValue(observableOf(MenuSection)); + fixture.detectChanges(); + }); + + describe('startSlide', () => { + describe('when expanding', () => { + beforeEach(() => { + comp.sidebarClosed = true; + comp.startSlide({ toState: 'expanded' } as any); + }); + + it('should set the sidebarClosed to false', () => { + expect(comp.sidebarClosed).toBeFalsy(); + }) + }); + + describe('when collapsing', () => { + beforeEach(() => { + comp.sidebarClosed = false; + comp.startSlide({ toState: 'collapsed' } as any); + }); + + it('should set the sidebarClosed to false', () => { + expect(comp.sidebarClosed).toBeTruthy(); + }) + }) + }); + + // describe('expand', () => { + // beforeEach(() => { + // spyOn(menuService, 'expandMenu'); + // comp.expand(new Event('click')); + // }); + // it('should trigger the expandMenu function on the menu service', () => { + // expect(menuService.expandMenu).toHaveBeenCalledWith(comp.menuID); + // }) + // }); +}); diff --git a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts index aa31d8f6f2..804680bdf6 100644 --- a/src/app/+admin/admin-sidebar/admin-sidebar.component.ts +++ b/src/app/+admin/admin-sidebar/admin-sidebar.component.ts @@ -84,7 +84,7 @@ export class AdminSidebarComponent extends MenuComponent implements OnInit { /** * Initialize all menu sections and items for this menu */ - createMenu() { + private createMenu() { const menuList = [ /* News */ { diff --git a/src/app/shared/menu/menu-item/link-menu-item.component.spec.ts b/src/app/shared/menu/menu-item/link-menu-item.component.spec.ts new file mode 100644 index 0000000000..6b50ec6788 --- /dev/null +++ b/src/app/shared/menu/menu-item/link-menu-item.component.spec.ts @@ -0,0 +1,50 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; +import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; +import { LinkMenuItemComponent } from './link-menu-item.component'; +import { RouterLinkDirectiveStub } from '../../testing/router-link-directive-stub'; + +describe('LinkMenuItemComponent', () => { + let component: LinkMenuItemComponent; + let fixture: ComponentFixture; + let debugElement: DebugElement; + const text = 'HELLO'; + const link = 'http://google.com'; + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [LinkMenuItemComponent, RouterLinkDirectiveStub], + providers: [ + { provide: 'itemModelProvider', useValue: { text: text, link: link } }, + ], + schemas: [NO_ERRORS_SCHEMA] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LinkMenuItemComponent); + component = fixture.componentInstance; + debugElement = fixture.debugElement; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should contain the correct text', () => { + const textContent = debugElement.query(By.css('a')).nativeElement.textContent; + expect(textContent).toEqual(text); + }); + + it('should have the right routerLink attribute', () => { + const linkDes = fixture.debugElement.queryAll(By.directive(RouterLinkDirectiveStub)); + + const routerLinkQuery = linkDes.map((de) => de.injector.get(RouterLinkDirectiveStub)); + + expect(routerLinkQuery.length).toBe(1); + expect(routerLinkQuery[0].routerLink).toBe(link); + }); +}); diff --git a/src/app/shared/menu/menu-item/link-menu-item.component.ts b/src/app/shared/menu/menu-item/link-menu-item.component.ts index ce2e0558f0..37acabb74b 100644 --- a/src/app/shared/menu/menu-item/link-menu-item.component.ts +++ b/src/app/shared/menu/menu-item/link-menu-item.component.ts @@ -12,8 +12,8 @@ import { rendersMenuItemForType } from '../menu-item.decorator'; }) @rendersMenuItemForType(MenuItemType.LINK) export class LinkMenuItemComponent { - @Input() item: LinkMenuItemModel; - constructor(@Inject('itemModelProvider') item) { + item: LinkMenuItemModel; + constructor(@Inject('itemModelProvider') item: LinkMenuItemModel) { this.item = item; } } diff --git a/src/app/shared/menu/menu-item/text-menu-item.component.spec.ts b/src/app/shared/menu/menu-item/text-menu-item.component.spec.ts new file mode 100644 index 0000000000..46c9b21bd9 --- /dev/null +++ b/src/app/shared/menu/menu-item/text-menu-item.component.spec.ts @@ -0,0 +1,39 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TextMenuItemComponent } from './text-menu-item.component'; +import { TranslateModule } from '@ngx-translate/core'; +import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; +import { By } from '@angular/platform-browser'; + +describe('TextMenuItemComponent', () => { + let component: TextMenuItemComponent; + let fixture: ComponentFixture; + let debugElement: DebugElement; + const text = 'HELLO'; + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [TextMenuItemComponent], + providers: [ + { provide: 'itemModelProvider', useValue: { text: text } }, + ], + schemas: [ NO_ERRORS_SCHEMA ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TextMenuItemComponent); + component = fixture.componentInstance; + debugElement = fixture.debugElement; + fixture.detectChanges(); + }); + + it('should contain the correct text', () => { + expect(component).toBeTruthy(); + }); + + it('should contain the text element', () => { + const textContent = debugElement.query(By.css('span')).nativeElement.textContent; + expect(textContent).toEqual(text); + }); +}); diff --git a/src/app/shared/menu/menu-item/text-menu-item.component.ts b/src/app/shared/menu/menu-item/text-menu-item.component.ts index 00dd146171..f7d3402be0 100644 --- a/src/app/shared/menu/menu-item/text-menu-item.component.ts +++ b/src/app/shared/menu/menu-item/text-menu-item.component.ts @@ -12,8 +12,8 @@ import { rendersMenuItemForType } from '../menu-item.decorator'; }) @rendersMenuItemForType(MenuItemType.TEXT) export class TextMenuItemComponent { - @Input() item: TextMenuItemModel; - constructor(@Inject('itemModelProvider') item) { + item: TextMenuItemModel; + constructor(@Inject('itemModelProvider') item: TextMenuItemModel) { this.item = item; } } diff --git a/src/app/shared/menu/menu-section/menu-section.component.spec.ts b/src/app/shared/menu/menu-section/menu-section.component.spec.ts index d1e8d16237..ebb099fb56 100644 --- a/src/app/shared/menu/menu-section/menu-section.component.spec.ts +++ b/src/app/shared/menu/menu-section/menu-section.component.spec.ts @@ -1,25 +1,76 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { TranslateModule } from '@ngx-translate/core'; +import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core'; import { MenuSectionComponent } from './menu-section.component'; +import { MenuService } from '../menu.service'; +import { MenuServiceStub } from '../../testing/menu-service-stub'; +import { MenuSection } from '../menu.reducer'; +import { of as observableOf } from 'rxjs'; +import { LinkMenuItemComponent } from '../menu-item/link-menu-item.component'; describe('MenuSectionComponent', () => { - let component: MenuSectionComponent; + let comp: MenuSectionComponent; let fixture: ComponentFixture; + let menuService: MenuService; + const dummySection = { + id: 'section', + visible: true, + active: false + } as any; beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ MenuSectionComponent ] - }) - .compileComponents(); + imports: [TranslateModule.forRoot(), NoopAnimationsModule], + declarations: [MenuSectionComponent], + providers: [ + { provide: Injector, useValue: {} }, + { provide: MenuService, useClass: MenuServiceStub }, + { provide: MenuSection, useValue: dummySection }, + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(MenuSectionComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(MenuSectionComponent); - component = fixture.componentInstance; + comp = fixture.componentInstance; + menuService = (comp as any).menuService; + spyOn(comp as any, 'getMenuItemComponent').and.returnValue(LinkMenuItemComponent); + spyOn(comp as any, 'getItemModelInjector').and.returnValue(observableOf({})); fixture.detectChanges(); }); - it('should create', () => { - expect(component).toBeTruthy(); + describe('toggleSection', () => { + beforeEach(() => { + spyOn(menuService, 'toggleActiveSection'); + comp.toggleSection(new Event('click')); + }); + it('should trigger the toggleActiveSection function on the menu service', () => { + expect(menuService.toggleActiveSection).toHaveBeenCalledWith(comp.menuID, dummySection.id); + }) }); + + describe('activateSection', () => { + beforeEach(() => { + spyOn(menuService, 'activateSection'); + comp.activateSection(new Event('click')); + }); + it('should trigger the activateSection function on the menu service', () => { + expect(menuService.activateSection).toHaveBeenCalledWith(comp.menuID, dummySection.id); + }) + }); + + describe('deactivateSection', () => { + beforeEach(() => { + spyOn(menuService, 'deactivateSection'); + comp.deactivateSection(new Event('click')); + }); + it('should trigger the deactivateSection function on the menu service', () => { + expect(menuService.deactivateSection).toHaveBeenCalledWith(comp.menuID, dummySection.id); + }) + }); + }); diff --git a/src/app/shared/menu/menu-section/menu-section.component.ts b/src/app/shared/menu/menu-section/menu-section.component.ts index fcd728a4db..f7e4f7d867 100644 --- a/src/app/shared/menu/menu-section/menu-section.component.ts +++ b/src/app/shared/menu/menu-section/menu-section.component.ts @@ -84,7 +84,7 @@ export class MenuSectionComponent { /** * Method for initializing all injectors and component constructors for the menu items in this section */ - initializeInjectorData() { + private initializeInjectorData() { this.itemInjectors.set(this.section.id, this.getItemModelInjector(this.section.model)); this.itemComponents.set(this.section.id, this.getMenuItemComponent(this.section.model)); this.subSections = this.menuService.getSubSectionsByParentID(this.menuID, this.section.id); @@ -101,7 +101,7 @@ export class MenuSectionComponent { * @param {MenuItemModel} itemModel The given MenuItemModel * @returns {GenericConstructor} Emits the constructor of the Component that should be used to render this menu item model */ - getMenuItemComponent(itemModel?: MenuItemModel) { + private getMenuItemComponent(itemModel?: MenuItemModel) { if (hasNoValue(itemModel)) { itemModel = this.section.model; } @@ -114,7 +114,7 @@ export class MenuSectionComponent { * @param {MenuItemModel} itemModel The given MenuItemModel * @returns {Injector} The Injector that injects the data for this menu item into the item's component */ - getItemModelInjector(itemModel?: MenuItemModel) { + private getItemModelInjector(itemModel?: MenuItemModel) { if (hasNoValue(itemModel)) { itemModel = this.section.model; } diff --git a/src/app/shared/menu/menu.component.spec.ts b/src/app/shared/menu/menu.component.spec.ts new file mode 100644 index 0000000000..f45f5ad391 --- /dev/null +++ b/src/app/shared/menu/menu.component.spec.ts @@ -0,0 +1,88 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { TranslateModule } from '@ngx-translate/core'; +import { ChangeDetectionStrategy, Injector, NO_ERRORS_SCHEMA } from '@angular/core'; +import { MenuService } from './menu.service'; +import { MenuComponent } from './menu.component'; +import { MenuServiceStub } from '../testing/menu-service-stub'; +import { of as observableOf } from 'rxjs'; +import { MenuSection } from './menu.reducer'; + +describe('MenuComponent', () => { + let comp: MenuComponent; + let fixture: ComponentFixture; + let menuService: MenuService; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), NoopAnimationsModule], + declarations: [MenuComponent], + providers: [ + { provide: Injector, useValue: {} }, + { provide: MenuService, useClass: MenuServiceStub }, + ], + schemas: [NO_ERRORS_SCHEMA] + }).overrideComponent(MenuComponent, { + set: { changeDetection: ChangeDetectionStrategy.Default } + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MenuComponent); + comp = fixture.componentInstance; // SearchPageComponent test instance + menuService = (comp as any).menuService; + spyOn(comp as any, 'getSectionDataInjector').and.returnValue(MenuSection); + spyOn(comp as any, 'getSectionComponent').and.returnValue(observableOf({})); + fixture.detectChanges(); + }); + + describe('toggle', () => { + beforeEach(() => { + spyOn(menuService, 'toggleMenu'); + comp.toggle(new Event('click')); + }); + it('should trigger the toggleMenu function on the menu service', () => { + expect(menuService.toggleMenu).toHaveBeenCalledWith(comp.menuID); + }) + }); + + describe('expand', () => { + beforeEach(() => { + spyOn(menuService, 'expandMenu'); + comp.expand(new Event('click')); + }); + it('should trigger the expandMenu function on the menu service', () => { + expect(menuService.expandMenu).toHaveBeenCalledWith(comp.menuID); + }) + }); + + describe('collapse', () => { + beforeEach(() => { + spyOn(menuService, 'collapseMenu'); + comp.collapse(new Event('click')); + }); + it('should trigger the collapseMenu function on the menu service', () => { + expect(menuService.collapseMenu).toHaveBeenCalledWith(comp.menuID); + }) + }); + + describe('expandPreview', () => { + beforeEach(() => { + spyOn(menuService, 'expandMenuPreview'); + comp.expandPreview(new Event('click')); + }); + it('should trigger the expandPreview function on the menu service', () => { + expect(menuService.expandMenuPreview).toHaveBeenCalledWith(comp.menuID); + }) + }); + + describe('collapsePreview', () => { + beforeEach(() => { + spyOn(menuService, 'collapseMenuPreview'); + comp.collapsePreview(new Event('click')); + }); + it('should trigger the collapsePreview function on the menu service', () => { + expect(menuService.collapseMenuPreview).toHaveBeenCalledWith(comp.menuID); + }) + }); +}); diff --git a/src/app/shared/menu/menu.component.ts b/src/app/shared/menu/menu.component.ts index 16474a0df3..98bdcc3d66 100644 --- a/src/app/shared/menu/menu.component.ts +++ b/src/app/shared/menu/menu.component.ts @@ -125,7 +125,7 @@ export class MenuComponent implements OnInit { * @param {MenuSection} section The given MenuSection * @returns {Observable>} Emits the constructor of the Component that should be used to render this object */ - getSectionComponent(section: MenuSection): Observable> { + private getSectionComponent(section: MenuSection): Observable> { return this.menuService.hasSubSections(this.menuID, section.id).pipe( map((expandable: boolean) => { return getComponentForMenu(this.menuID, expandable); @@ -139,7 +139,7 @@ export class MenuComponent implements OnInit { * @param {MenuSection} section The given MenuSection * @returns {Injector} The Injector that injects the data for this menu section into the section's component */ - getSectionDataInjector(section: MenuSection) { + private getSectionDataInjector(section: MenuSection) { return Injector.create({ providers: [{ provide: 'sectionDataProvider', useFactory: () => (section), deps: [] }], parent: this.injector diff --git a/src/app/shared/menu/menu.reducer.spec.ts b/src/app/shared/menu/menu.reducer.spec.ts index 7f24c192d1..c3968d098a 100644 --- a/src/app/shared/menu/menu.reducer.spec.ts +++ b/src/app/shared/menu/menu.reducer.spec.ts @@ -1,13 +1,467 @@ -// import { initialState, reducer } from './menu.reducer'; -// -// describe('Menu Reducer', () => { -// describe('unknown action', () => { -// it('should return the initial state', () => { -// const action = {} as any; -// -// const result = reducer(initialState, action); -// -// expect(result).toBe(initialState); -// }); -// }); -// }); +import * as deepFreeze from 'deep-freeze'; +import { + ActivateMenuSectionAction, + AddMenuSectionAction, + CollapseMenuAction, + CollapseMenuPreviewAction, + DeactivateMenuSectionAction, + ExpandMenuAction, + ExpandMenuPreviewAction, + HideMenuAction, + HideMenuSectionAction, + RemoveMenuSectionAction, + ShowMenuAction, + ShowMenuSectionAction, + ToggleActiveMenuSectionAction, + ToggleMenuAction +} from './menu.actions'; +import { MenuSectionIndex, menusReducer } from './menu.reducer'; +import { initialMenusState, MenuID } from './initial-menus-state'; + +let visibleSection1; +let dummyState; +const menuID = MenuID.ADMIN; +const topSectionID = 'new'; + +class NullAction extends CollapseMenuAction { + type = null; + + constructor() { + super(undefined); + } +} + +describe('menusReducer', () => { + beforeEach(() => { + visibleSection1 = { + id: 'section', + parentID: 'new', + visible: true, + active: false, + index: -1, + }; + + dummyState = { + [MenuID.ADMIN]: { + id: MenuID.ADMIN, + collapsed: true, + previewCollapsed: true, + visible: true, + sections: { + [topSectionID]: { + id: topSectionID, + active: false, + visible: true, + model: { + type: 0, + text: 'admin.sidebar.section.new' + }, + icon: 'plus-circle', + index: 0 + }, + new_item: { + id: 'new_item', + parentID: 'new', + active: false, + visible: true, + model: { + type: 1, + text: 'admin.sidebar.section.new_item', + link: '/items/submission' + } + }, + new_community: { + id: 'new_community', + parentID: 'new', + active: false, + visible: true, + model: { + type: 1, + text: 'admin.sidebar.section.new_community', + link: '/communities/submission' + } + }, + access_control: { + id: 'access_control', + active: false, + visible: true, + model: { + type: 0, + text: 'admin.sidebar.section.access_control' + }, + icon: 'key', + index: 4 + }, + access_control_people: { + id: 'access_control_people', + parentID: 'access_control', + active: false, + visible: true, + model: { + type: 1, + text: 'admin.sidebar.section.access_control_people', + link: '#' + } + }, + access_control_groups: { + id: 'access_control_groups', + parentID: 'access_control', + active: false, + visible: true, + model: { + type: 1, + text: 'admin.sidebar.section.access_control_groups', + link: '#' + } + }, + new_collection: { + id: 'new_collection', + parentID: 'new', + active: false, + visible: true, + model: { + type: 1, + text: 'admin.sidebar.section.new_collection', + link: '/collections/submission' + } + } + }, + sectionToSubsectionIndex: { + access_control: [ + 'access_control_people', + 'access_control_groups', + ], + new: [ + 'new_collection', + 'new_item', + 'new_community' + ] + } + } + } + }); + + it('should return the current state when no valid actions have been made', () => { + const state = dummyState; + const action = new NullAction(); + const newState = menusReducer(state, action); + + expect(newState).toEqual(state); + }); + + it('should start with the initialMenusState', () => { + const state = initialMenusState; + const action = new NullAction(); + const initialState = menusReducer(undefined, action); + + // The search filter starts collapsed + expect(initialState).toEqual(state); + }); + + it('should set collapsed to true for the correct menu in response to the COLLAPSE_MENU action', () => { + dummyState[MenuID.ADMIN].collapsed = false; + const state = dummyState; + const action = new CollapseMenuAction(menuID); + const newState = menusReducer(state, action); + + expect(newState[menuID].collapsed).toEqual(true); + }); + + it('should perform the COLLAPSE_MENU action without affecting the previous state', () => { + dummyState[MenuID.ADMIN].collapsed = false; + const state = dummyState; + deepFreeze([state]); + + const action = new CollapseMenuAction(menuID); + menusReducer(state, action); + + // no expect required, deepFreeze will ensure an exception is thrown if the state + // is mutated, and any uncaught exception will cause the test to fail + }); + + it('should set collapsed to false for the correct menu in response to the EXPAND_MENU action', () => { + dummyState[MenuID.ADMIN].collapsed = true; + const state = dummyState; + const action = new ExpandMenuAction(menuID); + const newState = menusReducer(state, action); + + expect(newState[menuID].collapsed).toEqual(false); + }); + + it('should perform the EXPAND_MENU action without affecting the previous state', () => { + dummyState[MenuID.ADMIN].collapsed = true; + const state = dummyState; + deepFreeze([state]); + + const action = new ExpandMenuAction(menuID); + menusReducer(state, action); + + // no expect required, deepFreeze will ensure an exception is thrown if the state + // is mutated, and any uncaught exception will cause the test to fail + }); + + it('should set collapsed to false for the correct menu in response to the TOGGLE_MENU action when collapsed is true', () => { + dummyState[MenuID.ADMIN].collapsed = true; + const state = dummyState; + const action = new ToggleMenuAction(menuID); + const newState = menusReducer(state, action); + + expect(newState[menuID].collapsed).toEqual(false); + }); + + it('should set collapsed to true for the correct menu in response to the TOGGLE_MENU action when collapsed is false', () => { + dummyState[MenuID.ADMIN].collapsed = true; + const state = dummyState; + const action = new ToggleMenuAction(menuID); + const newState = menusReducer(state, action); + + expect(newState[menuID].collapsed).toEqual(false); + }); + + it('should perform the TOGGLE_MENU action without affecting the previous state', () => { + dummyState[MenuID.ADMIN].collapsed = true; + const state = dummyState; + deepFreeze([state]); + + const action = new ToggleMenuAction(menuID); + menusReducer(state, action); + + // no expect required, deepFreeze will ensure an exception is thrown if the state + // is mutated, and any uncaught exception will cause the test to fail + }); + + it('should set previewCollapsed to true for the correct menu in response to the COLLAPSE_MENU_PREVIEW action', () => { + dummyState[MenuID.ADMIN].previewCollapsed = false; + const state = dummyState; + const action = new CollapseMenuPreviewAction(menuID); + const newState = menusReducer(state, action); + + expect(newState[menuID].previewCollapsed).toEqual(true); + }); + + it('should perform the COLLAPSE_MENU_PREVIEW action without affecting the previous state', () => { + dummyState[MenuID.ADMIN].previewCollapsed = false; + const state = dummyState; + deepFreeze([state]); + + const action = new CollapseMenuPreviewAction(menuID); + menusReducer(state, action); + + // no expect required, deepFreeze will ensure an exception is thrown if the state + // is mutated, and any uncaught exception will cause the test to fail + }); + + it('should set previewCollapsed to false for the correct menu in response to the EXPAND_MENU_PREVIEW action', () => { + dummyState[MenuID.ADMIN].previewCollapsed = true; + const state = dummyState; + const action = new ExpandMenuPreviewAction(menuID); + const newState = menusReducer(state, action); + + expect(newState[menuID].previewCollapsed).toEqual(false); + }); + + it('should perform the EXPAND_MENU_PREVIEW action without affecting the previous state', () => { + dummyState[MenuID.ADMIN].previewCollapsed = true; + const state = dummyState; + deepFreeze([state]); + + const action = new ExpandMenuPreviewAction(menuID); + menusReducer(state, action); + + // no expect required, deepFreeze will ensure an exception is thrown if the state + // is mutated, and any uncaught exception will cause the test to fail + }); + + it('should set visible to true for the correct menu in response to the SHOW_MENU action', () => { + dummyState[MenuID.ADMIN].visible = false; + const state = dummyState; + const action = new ShowMenuAction(menuID); + const newState = menusReducer(state, action); + + expect(newState[menuID].visible).toEqual(true); + }); + + it('should perform the SHOW_MENU action without affecting the previous state', () => { + dummyState[MenuID.ADMIN].visible = false; + const state = dummyState; + deepFreeze([state]); + + const action = new ShowMenuAction(menuID); + menusReducer(state, action); + + // no expect required, deepFreeze will ensure an exception is thrown if the state + // is mutated, and any uncaught exception will cause the test to fail + }); + + it('should set previewCollapsed to false for the correct menu in response to the HIDE_MENU action', () => { + dummyState[MenuID.ADMIN].visible = true; + const state = dummyState; + const action = new HideMenuAction(menuID); + const newState = menusReducer(state, action); + + expect(newState[menuID].visible).toEqual(false); + }); + + it('should perform the HIDE_MENU action without affecting the previous state', () => { + dummyState[MenuID.ADMIN].visible = true; + const state = dummyState; + deepFreeze([state]); + + const action = new HideMenuAction(menuID); + menusReducer(state, action); + + // no expect required, deepFreeze will ensure an exception is thrown if the state + // is mutated, and any uncaught exception will cause the test to fail + }); + + it('should set add a new section for the correct menu in response to the ADD_SECTION action', () => { + const state = dummyState; + const action = new AddMenuSectionAction(menuID, visibleSection1); + const newState = menusReducer(state, action); + expect(Object.values(newState[menuID].sections)).toContain(visibleSection1); + }); + + it('should set add a new section in the right place according to the index for the correct menu in response to the ADD_SECTION action', () => { + const state = dummyState; + const action = new AddMenuSectionAction(menuID, visibleSection1); + const newState = menusReducer(state, action); + expect(Object.values(newState[menuID].sections)[0]).toEqual(visibleSection1); + }); + + it('should add the new section to the sectionToSubsectionIndex when it has a parentID in response to the ADD_SECTION action', () => { + const state = dummyState; + const action = new AddMenuSectionAction(menuID, visibleSection1); + const newState = menusReducer(state, action); + expect(newState[menuID].sectionToSubsectionIndex[visibleSection1.parentID]).toContain(visibleSection1.id) + }); + + it('should perform the ADD_SECTION action without affecting the previous state', () => { + const state = dummyState; + deepFreeze([state]); + + const action = new AddMenuSectionAction(menuID, visibleSection1); + menusReducer(state, action); + + // no expect required, deepFreeze will ensure an exception is thrown if the state + // is mutated, and any uncaught exception will cause the test to fail + }); + + it('should remove a section for the correct menu in response to the REMOVE_SECTION action', () => { + const sectionID = Object.keys(dummyState[menuID].sections)[0]; + const state = dummyState; + const action = new RemoveMenuSectionAction(menuID, sectionID); + const newState = menusReducer(state, action); + expect(Object.keys(newState[menuID].sections)).not.toContain(sectionID); + }); + + it('should remove a section for the correct menu from the sectionToSubsectionIndex in response to the REMOVE_SECTION action', () => { + const index: MenuSectionIndex = dummyState[menuID].sectionToSubsectionIndex; + const parentID: string = Object.keys(index)[0]; + const childID: string = index[parentID][0]; + const state = dummyState; + const action = new RemoveMenuSectionAction(menuID, childID); + const newState = menusReducer(state, action); + expect(newState[menuID].sectionToSubsectionIndex[parentID]).not.toContain(childID); + }); + + it('should set active to true for the correct menu section in response to the ACTIVATE_SECTION action', () => { + dummyState[menuID].sections[topSectionID].active = false; + const state = dummyState; + const action = new ActivateMenuSectionAction(menuID, topSectionID); + const newState = menusReducer(state, action); + + expect(newState[menuID].sections[topSectionID].active).toEqual(true); + }); + + it('should perform the ACTIVATE_SECTION action without affecting the previous state', () => { + dummyState[menuID].sections[topSectionID].active = false; + const state = dummyState; + deepFreeze([state]); + + const action = new ActivateMenuSectionAction(menuID, topSectionID); + menusReducer(state, action); + }); + + it('should set active to false for the correct menu section in response to the DEACTIVATE_SECTION action', () => { + dummyState[menuID].sections[topSectionID].active = true; + const state = dummyState; + const action = new DeactivateMenuSectionAction(menuID, topSectionID); + const newState = menusReducer(state, action); + + expect(newState[menuID].sections[topSectionID].active).toEqual(false); + }); + + it('should perform the DEACTIVATE_SECTION action without affecting the previous state', () => { + dummyState[MenuID.ADMIN].sections[topSectionID].active = false; + const state = dummyState; + deepFreeze([state]); + + const action = new DeactivateMenuSectionAction(menuID, topSectionID); + menusReducer(state, action); + }); + + it('should set active to false for the correct menu in response to the TOGGLE_ACTIVE_SECTION action when active is true', () => { + dummyState[menuID].sections[topSectionID].active = true; + const state = dummyState; + const action = new ToggleActiveMenuSectionAction(menuID, topSectionID); + const newState = menusReducer(state, action); + + expect(newState[menuID].sections[topSectionID].active).toEqual(false); + }); + + it('should set collapsed to true for the correct menu in response to the TOGGLE_ACTIVE_SECTION action when active is false', () => { + dummyState[menuID].sections[topSectionID].active = false; + const state = dummyState; + const action = new ToggleActiveMenuSectionAction(menuID, topSectionID); + const newState = menusReducer(state, action); + + expect(newState[menuID].sections[topSectionID].active).toEqual(true); + }); + + it('should perform the TOGGLE_ACTIVE_SECTION action without affecting the previous state', () => { + dummyState[menuID].sections[topSectionID].active = true; + const state = dummyState; + const action = new ToggleActiveMenuSectionAction(menuID, topSectionID); + deepFreeze([state]); + menusReducer(state, action); + + // no expect required, deepFreeze will ensure an exception is thrown if the state + // is mutated, and any uncaught exception will cause the test to fail + }); + + it('should set visible to true for the correct menu section in response to the SHOW_SECTION action', () => { + dummyState[menuID].sections[topSectionID].visible = false; + const state = dummyState; + const action = new ShowMenuSectionAction(menuID, topSectionID); + const newState = menusReducer(state, action); + + expect(newState[menuID].sections[topSectionID].visible).toEqual(true); + }); + + it('should perform the SHOW_SECTION action without affecting the previous state', () => { + dummyState[menuID].sections[topSectionID].visible = false; + const state = dummyState; + deepFreeze([state]); + + const action = new ShowMenuSectionAction(menuID, topSectionID); + menusReducer(state, action); + }); + + it('should set visible to false for the correct menu section in response to the HIDE_SECTION action', () => { + dummyState[menuID].sections[topSectionID].visible = true; + const state = dummyState; + const action = new HideMenuSectionAction(menuID, topSectionID); + const newState = menusReducer(state, action); + + expect(newState[menuID].sections[topSectionID].visible).toEqual(false); + }); + + it('should perform the HIDE_SECTION action without affecting the previous state', () => { + dummyState[MenuID.ADMIN].sections[topSectionID].visible = false; + const state = dummyState; + deepFreeze([state]); + + const action = new HideMenuSectionAction(menuID, topSectionID); + menusReducer(state, action); + }); +}); diff --git a/src/app/shared/menu/menu.reducer.ts b/src/app/shared/menu/menu.reducer.ts index b5726b8f9a..078343a990 100644 --- a/src/app/shared/menu/menu.reducer.ts +++ b/src/app/shared/menu/menu.reducer.ts @@ -175,9 +175,9 @@ function reorderSections(state: MenusState, action: MenuSectionAction) { function removeSection(state: MenusState, action: RemoveMenuSectionAction) { const menuState: MenuState = state[action.menuID]; const id = action.id; - const newMenuState = Object.assign({}, menuState); - delete newMenuState[id]; const newState = removeFromIndex(state, menuState.sections[action.id], action.menuID); + const newMenuState = Object.assign({}, newState[action.menuID]); + delete newMenuState.sections[id]; return Object.assign({}, newState, { [action.menuID]: newMenuState }); } @@ -195,7 +195,7 @@ function removeFromIndex(state: MenusState, section: MenuSection, menuID: MenuID const menuState: MenuState = state[menuID]; const index = menuState.sectionToSubsectionIndex; const parentIndex = hasValue(index[parentID]) ? index[parentID] : []; - const newIndex = Object.assign({}, index, { [parentID]: parentIndex.filter((id) => id === sectionID) }); + const newIndex = Object.assign({}, index, { [parentID]: parentIndex.filter((id) => id !== sectionID) }); const newMenuState = Object.assign({}, menuState, { sectionToSubsectionIndex: newIndex }); return Object.assign({}, state, { [menuID]: newMenuState }); } diff --git a/src/app/shared/menu/menu.service.spec.ts b/src/app/shared/menu/menu.service.spec.ts index ec24b25de0..b7f955b257 100644 --- a/src/app/shared/menu/menu.service.spec.ts +++ b/src/app/shared/menu/menu.service.spec.ts @@ -5,15 +5,31 @@ import { MenuService } from './menu.service'; import { cold, hot } from 'jasmine-marbles'; import { MenuID } from './initial-menus-state'; import { of as observableOf } from 'rxjs'; +import { + ActivateMenuSectionAction, + AddMenuSectionAction, + CollapseMenuAction, CollapseMenuPreviewAction, DeactivateMenuSectionAction, + ExpandMenuAction, ExpandMenuPreviewAction, HideMenuAction, + RemoveMenuSectionAction, ShowMenuAction, ToggleActiveMenuSectionAction, ToggleMenuAction +} from './menu.actions'; -fdescribe('MenuService', () => { +describe('MenuService', () => { let service: MenuService; let selectSpy; - const store = observableOf({}) as any; - const fakeMenu = { id: MenuID.ADMIN } as any; + const store = Object.assign(observableOf({}), { + dispatch: () => {/***/ + } + }) as any; + const fakeMenu = { + id: MenuID.ADMIN, + collapsed: true, + visible: false, + previewCollapsed: true + } as any; const visibleSection1 = { id: 'section', - visible: true + visible: true, + active: false }; const visibleSection2 = { id: 'section_2', @@ -48,6 +64,7 @@ fdescribe('MenuService', () => { beforeEach(() => { service = new MenuService(store); selectSpy = spyOnProperty(ngrx, 'select'); + spyOn(store, 'dispatch'); }); describe('getMenu', () => { @@ -83,37 +100,297 @@ fdescribe('MenuService', () => { }; }); }); - it('should return only the visible top MenuSections', () => { + it('should return only the visible top MenuSections when mustBeVisible is true', () => { const result = service.getMenuTopSections(MenuID.ADMIN); const expected = cold('b', { b: [visibleSection1, visibleSection2] }); + expect(result).toBeObservable(expected); + }); + + it('should return only the all top MenuSections when mustBeVisible is false', () => { + + const result = service.getMenuTopSections(MenuID.ADMIN, false); + const expected = cold('b', { + b: [visibleSection1, visibleSection2, hiddenSection3] + }); + expect(result).toBeObservable(expected); }) }); - // TODO finish this test describe('getSubSectionsByParentID', () => { + describe('when the subsection list is not empty', () => { + + beforeEach(() => { + spyOn(service, 'getMenuSection').and.returnValue(observableOf(visibleSection1)); + selectSpy.and.callFake(() => { + return () => { + return () => hot('a', { + a: ['id1', 'id2'] + } + ); + }; + }); + }); + it('should return the MenuSections with the given parentID', () => { + + const result = service.getSubSectionsByParentID(MenuID.ADMIN, 'fakeId'); + const expected = cold('b', { + b: [visibleSection1, visibleSection1] + }); + + expect(result).toBeObservable(expected); + }) + }); + describe('when the subsection list is undefined', () => { + + beforeEach(() => { + selectSpy.and.callFake(() => { + return () => { + return () => hot('a', { + a: undefined + } + ); + }; + }); + }); + it('should return an observable that emits nothing', () => { + + const result = service.getSubSectionsByParentID(MenuID.ADMIN, 'fakeId'); + const expected = cold(''); + + expect(result).toBeObservable(expected); + }) + }); + }); + + describe('hasSubSections', () => { + describe('when the subsection list is not empty', () => { + beforeEach(() => { + selectSpy.and.callFake(() => { + return () => { + return () => hot('a', { + a: ['id1', 'id2'] + } + ); + }; + }); + }); + it('should return true', () => { + + const result = service.hasSubSections(MenuID.ADMIN, 'fakeId'); + const expected = cold('b', { + b: true + }); + + expect(result).toBeObservable(expected); + }); + }); + + describe('when the subsection list is empty', () => { + beforeEach(() => { + selectSpy.and.callFake(() => { + return () => { + return () => hot('a', { + a: [] + } + ); + }; + }); + }); + it('should return false', () => { + + const result = service.hasSubSections(MenuID.ADMIN, 'fakeId'); + const expected = cold('b', { + b: false + }); + + expect(result).toBeObservable(expected); + }); + }) + }); + + describe('getMenuSection', () => { beforeEach(() => { selectSpy.and.callFake(() => { return () => { return () => hot('a', { - a: topSections + a: hiddenSection3 } ); }; }); }); - it('should return only the visible top MenuSections', () => { + it('should return false', () => { - const result = service.getMenuTopSections(MenuID.ADMIN); + const result = service.getMenuSection(MenuID.ADMIN, 'fakeId'); const expected = cold('b', { - b: [visibleSection1, visibleSection2] + b: hiddenSection3 }); expect(result).toBeObservable(expected); - }) - }) + }); + }); + + describe('isMenuCollapsed', () => { + beforeEach(() => { + spyOn(service, 'getMenu').and.returnValue(observableOf(fakeMenu)); + }); + it('should return true when the menu is collapsed', () => { + + const result = service.isMenuCollapsed(MenuID.ADMIN); + const expected = cold('(b|)', { + b: fakeMenu.collapsed + }); + + expect(result).toBeObservable(expected); + }); + }); + + describe('isMenuPreviewCollapsed', () => { + beforeEach(() => { + spyOn(service, 'getMenu').and.returnValue(observableOf(fakeMenu)); + }); + it('should return true when the menu\'s preview is collapsed', () => { + + const result = service.isMenuPreviewCollapsed(MenuID.ADMIN); + const expected = cold('(b|)', { + b: fakeMenu.previewCollapsed + }); + + expect(result).toBeObservable(expected); + }); + }); + + describe('isMenuVisible', () => { + beforeEach(() => { + spyOn(service, 'getMenu').and.returnValue(observableOf(fakeMenu)); + + }); + it('should return false when the menu is hidden', () => { + + const result = service.isMenuVisible(MenuID.ADMIN); + const expected = cold('(b|)', { + b: fakeMenu.visible + }); + + expect(result).toBeObservable(expected); + }); + }); + + describe('isSectionActive', () => { + beforeEach(() => { + spyOn(service, 'getMenuSection').and.returnValue(observableOf(visibleSection1)); + }); + + it('should return false when the section is not active', () => { + const result = service.isSectionActive(MenuID.ADMIN, 'fakeID'); + const expected = cold('(b|)', { + b: visibleSection1.active + }); + + expect(result).toBeObservable(expected); + }); + }); + + describe('isSectionVisible', () => { + beforeEach(() => { + spyOn(service, 'getMenuSection').and.returnValue(observableOf(hiddenSection3)); + }); + + it('should return false when the section is hidden', () => { + const result = service.isSectionVisible(MenuID.ADMIN, 'fakeID'); + const expected = cold('(b|)', { + b: hiddenSection3.visible + }); + expect(result).toBeObservable(expected); + }); + }); + + describe('addSection', () => { + it('should dispatch an AddMenuSectionAction with the correct arguments', () => { + service.addSection(MenuID.ADMIN, visibleSection1 as any); + expect(store.dispatch).toHaveBeenCalledWith(new AddMenuSectionAction(MenuID.ADMIN, visibleSection1 as any)); + }); + }); + + describe('removeSection', () => { + it('should dispatch an RemoveMenuSectionAction with the correct arguments', () => { + service.removeSection(MenuID.ADMIN, 'fakeID'); + expect(store.dispatch).toHaveBeenCalledWith(new RemoveMenuSectionAction(MenuID.ADMIN, 'fakeID')); + }); + }); + + describe('expandMenu', () => { + it('should dispatch an ExpandMenuAction with the correct arguments', () => { + service.expandMenu(MenuID.ADMIN); + expect(store.dispatch).toHaveBeenCalledWith(new ExpandMenuAction(MenuID.ADMIN)); + }); + }); + + describe('collapseMenu', () => { + it('should dispatch an CollapseMenuAction with the correct arguments', () => { + service.collapseMenu(MenuID.ADMIN); + expect(store.dispatch).toHaveBeenCalledWith(new CollapseMenuAction(MenuID.ADMIN)); + }); + }); + + describe('expandMenuPreview', () => { + it('should dispatch an ExpandMenuPreviewAction with the correct arguments', () => { + service.expandMenuPreview(MenuID.ADMIN); + expect(store.dispatch).toHaveBeenCalledWith(new ExpandMenuPreviewAction(MenuID.ADMIN)); + }); + }); + + describe('collapseMenuPreview', () => { + it('should dispatch an CollapseMenuPreviewAction with the correct arguments', () => { + service.collapseMenuPreview(MenuID.ADMIN); + expect(store.dispatch).toHaveBeenCalledWith(new CollapseMenuPreviewAction(MenuID.ADMIN)); + }); + }); + + describe('toggleMenu', () => { + it('should dispatch an ToggleMenuAction with the correct arguments', () => { + service.toggleMenu(MenuID.ADMIN); + expect(store.dispatch).toHaveBeenCalledWith(new ToggleMenuAction(MenuID.ADMIN)); + }); + }); + + describe('showMenu', () => { + it('should dispatch an ShowMenuAction with the correct arguments', () => { + service.showMenu(MenuID.ADMIN); + expect(store.dispatch).toHaveBeenCalledWith(new ShowMenuAction(MenuID.ADMIN)); + }); + }); + + describe('hideMenu', () => { + it('should dispatch an HideMenuAction with the correct arguments', () => { + service.hideMenu(MenuID.ADMIN); + expect(store.dispatch).toHaveBeenCalledWith(new HideMenuAction(MenuID.ADMIN)); + }); + }); + + describe('toggleActiveSection', () => { + it('should dispatch an ToggleActiveMenuSectionAction with the correct arguments', () => { + service.toggleActiveSection(MenuID.ADMIN, 'fakeID'); + expect(store.dispatch).toHaveBeenCalledWith(new ToggleActiveMenuSectionAction(MenuID.ADMIN, 'fakeID')); + }); + }); + + describe('activateSection', () => { + it('should dispatch an ActivateMenuSectionAction with the correct arguments', () => { + service.activateSection(MenuID.ADMIN, 'fakeID'); + expect(store.dispatch).toHaveBeenCalledWith(new ActivateMenuSectionAction(MenuID.ADMIN, 'fakeID')); + }); + }); + + describe('deactivateSection', () => { + it('should dispatch an DeactivateMenuSectionAction with the correct arguments', () => { + service.deactivateSection(MenuID.ADMIN, 'fakeID'); + expect(store.dispatch).toHaveBeenCalledWith(new DeactivateMenuSectionAction(MenuID.ADMIN, 'fakeID')); + }); + }); }); diff --git a/src/app/shared/testing/auth-service-stub.ts b/src/app/shared/testing/auth-service-stub.ts index 415d15696b..fa263da31f 100644 --- a/src/app/shared/testing/auth-service-stub.ts +++ b/src/app/shared/testing/auth-service-stub.ts @@ -93,4 +93,8 @@ export class AuthServiceStub { public storeToken(token: AuthTokenInfo) { return; } + + isAuthenticated() { + return observableOf(true); + } } diff --git a/src/app/shared/testing/component-injector-stub.ts b/src/app/shared/testing/component-injector-stub.ts new file mode 100644 index 0000000000..3e3d75d731 --- /dev/null +++ b/src/app/shared/testing/component-injector-stub.ts @@ -0,0 +1,12 @@ +import { ComponentFactory } from '@angular/core'; +import { create } from 'domain'; + +export class ComponentInjectorStub { + resolveComponentFactory(): ComponentFactory { + return { + create() { + return { hostView: {}, viewContainerParent: {}, } + } + } as any; + } +} \ No newline at end of file diff --git a/src/app/shared/testing/css-variable-service-stub.ts b/src/app/shared/testing/css-variable-service-stub.ts new file mode 100644 index 0000000000..32f8349019 --- /dev/null +++ b/src/app/shared/testing/css-variable-service-stub.ts @@ -0,0 +1,8 @@ +import { Observable } from 'rxjs/internal/Observable'; +import { of as observableOf } from 'rxjs'; + +export class CSSVariableServiceStub { + getVariable(name: string): Observable { + return observableOf('500px'); + } +} \ No newline at end of file diff --git a/src/app/shared/testing/menu-service-stub.ts b/src/app/shared/testing/menu-service-stub.ts new file mode 100644 index 0000000000..de71e3483d --- /dev/null +++ b/src/app/shared/testing/menu-service-stub.ts @@ -0,0 +1,93 @@ +import { MenuID } from '../menu/initial-menus-state'; +import { of as observableOf } from 'rxjs'; +import { Observable } from 'rxjs/internal/Observable'; +import { MenuSection } from '../menu/menu.reducer'; + +export class MenuServiceStub { + visibleSection1 = { + id: 'section', + visible: true, + active: false + } as any; + visibleSection2 = { + id: 'section_2', + visible: true + } as any; + hiddenSection3 = { + id: 'section_3', + visible: false + } as any; + subSection4 = { + id: 'section_4', + visible: true, + parentID: 'section1' + } as any; + + toggleMenu(): void { /***/ + }; + + expandMenu(): void { /***/ + }; + + collapseMenu(): void { /***/ + }; + + showMenu(): void { /***/ + }; + + hideMenu(): void { /***/ + }; + + expandMenuPreview(): void { /***/ + }; + + collapseMenuPreview(): void { /***/ + }; + + toggleActiveSection(): void { /***/ + }; + + activateSection(): void { /***/ + }; + + deactivateSection(): void { /***/ + }; + + addSection(): void { /***/ + }; + + removeSection(): void { /***/ + }; + + isMenuVisible(id: MenuID): Observable { + return observableOf(true) + }; + + isMenuCollapsed(id: MenuID): Observable { + return observableOf(false) + }; + + isMenuPreviewCollapsed(id: MenuID): Observable { + return observableOf(true) + }; + + hasSubSections(id: MenuID, sectionID: string): Observable { + return observableOf(true) + }; + + getMenuTopSections(id: MenuID): Observable { + return observableOf([this.visibleSection1, this.visibleSection2]) + }; + + getSubSectionsByParentID(id: MenuID): Observable { + return observableOf([this.subSection4]) + }; + + isSectionActive(id: MenuID, sectionID: string): Observable { + return observableOf(true) + }; + + isSectionVisible(id: MenuID, sectionID: string): Observable { + return observableOf(true) + }; +} diff --git a/src/app/shared/testing/ng-component-outlet-directive-stub.ts b/src/app/shared/testing/ng-component-outlet-directive-stub.ts new file mode 100644 index 0000000000..ee3f65b460 --- /dev/null +++ b/src/app/shared/testing/ng-component-outlet-directive-stub.ts @@ -0,0 +1,10 @@ +import { Directive, Input } from '@angular/core'; + +/* tslint:disable:directive-class-suffix */ +@Directive({ + // tslint:disable-next-line:directive-selector + selector: '[ngComponentOutlet]', +}) +export class NgComponentOutletDirectiveStub { + @Input() ngComponentOutlet: any; +} diff --git a/src/app/shared/testing/router-link-directive-stub.ts b/src/app/shared/testing/router-link-directive-stub.ts new file mode 100644 index 0000000000..ba52602536 --- /dev/null +++ b/src/app/shared/testing/router-link-directive-stub.ts @@ -0,0 +1,10 @@ +import { Directive, Input } from '@angular/core'; + +/* tslint:disable:directive-class-suffix */ +@Directive({ + // tslint:disable-next-line:directive-selector + selector: '[routerLink]', +}) +export class RouterLinkDirectiveStub { + @Input() routerLink: any; +} diff --git a/src/app/shared/testing/test-module.ts b/src/app/shared/testing/test-module.ts index 03d22640d3..d0e5190021 100644 --- a/src/app/shared/testing/test-module.ts +++ b/src/app/shared/testing/test-module.ts @@ -1,5 +1,7 @@ import { NgModule } from '@angular/core'; import { QueryParamsDirectiveStub } from './query-params-directive-stub'; +import { RouterLinkDirectiveStub } from './router-link-directive-stub'; +import { NgComponentOutletDirectiveStub } from './ng-component-outlet-directive-stub'; /** * This module isn't used. It serves to prevent the AoT compiler @@ -9,7 +11,9 @@ import { QueryParamsDirectiveStub } from './query-params-directive-stub'; */ @NgModule({ declarations: [ - QueryParamsDirectiveStub + QueryParamsDirectiveStub, + RouterLinkDirectiveStub, + NgComponentOutletDirectiveStub ] }) export class TestModule {}