Fix tests pt. 1

This commit is contained in:
Yury Bondarenko
2025-02-07 17:57:42 +01:00
parent fbffcca945
commit 3fecbc245b
8 changed files with 77 additions and 105 deletions

View File

@@ -79,17 +79,14 @@ describe('ExpandableAdminSidebarSectionComponent', () => {
describe('when there are no subsections', () => { describe('when there are no subsections', () => {
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [NoopAnimationsModule, TranslateModule.forRoot()], imports: [NoopAnimationsModule, TranslateModule.forRoot(), ExpandableAdminSidebarSectionComponent, TestComponent],
declarations: [ExpandableAdminSidebarSectionComponent, TestComponent],
providers: [ providers: [
{ provide: 'sectionDataProvider', useValue: { icon: iconString, model: {} } }, { provide: 'sectionDataProvider', useValue: { icon: iconString, model: {} } },
{ provide: MenuService, useValue: menuService }, { provide: MenuService, useValue: menuService },
{ provide: CSSVariableService, useClass: CSSVariableServiceStub }, { provide: CSSVariableService, useClass: CSSVariableServiceStub },
{ provide: Router, useValue: new RouterStub() }, { provide: Router, useValue: new RouterStub() },
], ],
}).overrideComponent(ExpandableAdminSidebarSectionComponent, { }).compileComponents();
})
.compileComponents();
})); }));
beforeEach(() => { beforeEach(() => {

View File

@@ -74,8 +74,7 @@ describe('DsoEditMenuExpandableSectionComponent', () => {
describe('when there are no subsections', () => { describe('when there are no subsections', () => {
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()], imports: [TranslateModule.forRoot(), DsoEditMenuExpandableSectionComponent, TestComponent],
declarations: [DsoEditMenuExpandableSectionComponent, TestComponent],
providers: [ providers: [
{ provide: 'sectionDataProvider', useValue: dummySection }, { provide: 'sectionDataProvider', useValue: dummySection },
{ provide: MenuService, useValue: menuService }, { provide: MenuService, useValue: menuService },

View File

@@ -23,6 +23,7 @@ import { AbstractMenuSectionComponent } from './abstract-menu-section.component'
@Component({ @Component({
selector: 'ds-some-menu-section', selector: 'ds-some-menu-section',
template: '', template: '',
standalone: true,
}) })
class SomeMenuSectionComponent extends AbstractMenuSectionComponent { class SomeMenuSectionComponent extends AbstractMenuSectionComponent {
constructor( constructor(
@@ -47,8 +48,7 @@ describe('MenuSectionComponent', () => {
active: false, active: false,
} as any; } as any;
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), NoopAnimationsModule, SomeMenuSectionComponent], imports: [TranslateModule.forRoot(), NoopAnimationsModule, SomeMenuSectionComponent, AbstractMenuSectionComponent],
declarations: [AbstractMenuSectionComponent],
providers: [ providers: [
{ provide: Injector, useValue: {} }, { provide: Injector, useValue: {} },
{ provide: MenuService, useClass: MenuServiceStub }, { provide: MenuService, useClass: MenuServiceStub },

View File

@@ -54,6 +54,7 @@ const mockMenuID = 'mock-menuID' as MenuID;
// eslint-disable-next-line @angular-eslint/component-selector // eslint-disable-next-line @angular-eslint/component-selector
selector: '', selector: '',
template: '', template: '',
standalone: true,
}) })
@rendersSectionForMenu(mockMenuID, true) @rendersSectionForMenu(mockMenuID, true)
class TestExpandableMenuComponent { class TestExpandableMenuComponent {
@@ -63,6 +64,7 @@ class TestExpandableMenuComponent {
// eslint-disable-next-line @angular-eslint/component-selector // eslint-disable-next-line @angular-eslint/component-selector
selector: '', selector: '',
template: '', template: '',
standalone: true,
}) })
@rendersSectionForMenu(mockMenuID, false) @rendersSectionForMenu(mockMenuID, false)
class TestMenuComponent { class TestMenuComponent {
@@ -85,8 +87,6 @@ describe('MenuComponent', () => {
visible: true, visible: true,
}; };
const mockMenuID = 'mock-menuID' as MenuID;
const mockStatisticSection = { 'id': 'statistics_site', 'active': true, 'visible': true, 'index': 2, 'type': 'statistics', 'model': { 'type': 1, 'text': 'menu.section.statistics', 'link': 'statistics' } }; const mockStatisticSection = { 'id': 'statistics_site', 'active': true, 'visible': true, 'index': 2, 'type': 'statistics', 'model': { 'type': 1, 'text': 'menu.section.statistics', 'link': 'statistics' } };
let authorizationService: AuthorizationDataService; let authorizationService: AuthorizationDataService;
@@ -144,7 +144,7 @@ describe('MenuComponent', () => {
}); });
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), NoopAnimationsModule, RouterTestingModule, MenuComponent, StoreModule.forRoot(authReducer, storeModuleConfig)], imports: [TranslateModule.forRoot(), NoopAnimationsModule, RouterTestingModule, MenuComponent, StoreModule.forRoot(authReducer, storeModuleConfig), TestExpandableMenuComponent, TestMenuComponent],
providers: [ providers: [
Injector, Injector,
{ provide: ThemeService, useValue: getMockThemeService() }, { provide: ThemeService, useValue: getMockThemeService() },
@@ -152,8 +152,6 @@ describe('MenuComponent', () => {
provideMockStore({ initialState }), provideMockStore({ initialState }),
{ provide: AuthorizationDataService, useValue: authorizationService }, { provide: AuthorizationDataService, useValue: authorizationService },
{ provide: ActivatedRoute, useValue: routeStub }, { provide: ActivatedRoute, useValue: routeStub },
TestExpandableMenuComponent,
TestMenuComponent,
], ],
schemas: [NO_ERRORS_SCHEMA], schemas: [NO_ERRORS_SCHEMA],
}).overrideComponent(MenuComponent, { }).overrideComponent(MenuComponent, {
@@ -272,35 +270,4 @@ describe('MenuComponent', () => {
expect(menuService.collapseMenuPreview).toHaveBeenCalledWith(comp.menuID); expect(menuService.collapseMenuPreview).toHaveBeenCalledWith(comp.menuID);
})); }));
}); });
describe('when unauthorized statistics', () => {
beforeEach(() => {
(authorizationService as any).isAuthorized.and.returnValue(observableOf(false));
fixture.detectChanges();
});
it('should return observable of empty object', done => {
comp.getAuthorizedStatistics(mockStatisticSection).subscribe((res) => {
expect(res).toEqual({});
done();
});
});
});
describe('get authorized statistics', () => {
beforeEach(() => {
(authorizationService as any).isAuthorized.and.returnValue(observableOf(true));
fixture.detectChanges();
});
it('should return observable of statistics section menu', done => {
comp.getAuthorizedStatistics(mockStatisticSection).subscribe((res) => {
expect(res).toEqual(mockStatisticSection);
done();
});
});
});
}); });

View File

@@ -9,29 +9,26 @@ import { ActivatedRoute } from '@angular/router';
import { import {
BehaviorSubject, BehaviorSubject,
Observable, Observable,
of as observableOf,
Subscription, Subscription,
} from 'rxjs'; } from 'rxjs';
import { import {
distinctUntilChanged, distinctUntilChanged,
map, map,
mergeMap,
switchMap, switchMap,
} from 'rxjs/operators'; } from 'rxjs/operators';
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
import { GenericConstructor } from '../../core/shared/generic-constructor'; import { GenericConstructor } from '../../core/shared/generic-constructor';
import { import {
hasValue, hasValue,
isNotEmptyOperator, isNotEmptyOperator,
} from '../empty.util'; } from '../empty.util';
import { ThemeService } from '../theme-support/theme.service'; import { ThemeService } from '../theme-support/theme.service';
import { MenuService } from './menu.service';
import { MenuID } from './menu-id.model'; import { MenuID } from './menu-id.model';
import { getComponentForMenu } from './menu-section.decorator'; import { getComponentForMenu } from './menu-section.decorator';
import { MenuSection } from './menu-section.model'; import { MenuSection } from './menu-section.model';
import { AbstractMenuSectionComponent } from './menu-section/abstract-menu-section.component'; import { AbstractMenuSectionComponent } from './menu-section/abstract-menu-section.component';
import { MenuService } from './menu.service';
/** /**
* A basic implementation of a MenuComponent * A basic implementation of a MenuComponent
@@ -93,8 +90,12 @@ export class MenuComponent implements OnInit, OnDestroy {
private activatedRouteLastChild: ActivatedRoute; private activatedRouteLastChild: ActivatedRoute;
constructor(protected menuService: MenuService, protected injector: Injector, public authorizationService: AuthorizationDataService, constructor(
public route: ActivatedRoute, protected themeService: ThemeService, protected menuService: MenuService,
protected injector: Injector,
public authorizationService: AuthorizationDataService,
public route: ActivatedRoute,
protected themeService: ThemeService,
) { ) {
} }
@@ -113,12 +114,6 @@ export class MenuComponent implements OnInit, OnDestroy {
// if you return an array from a switchMap it will emit each element as a separate event. // if you return an array from a switchMap it will emit each element as a separate event.
// So this switchMap is equivalent to a subscribe with a forEach inside // So this switchMap is equivalent to a subscribe with a forEach inside
switchMap((sections: MenuSection[]) => sections), switchMap((sections: MenuSection[]) => sections),
mergeMap((section: MenuSection) => {
if (section.id.includes('statistics')) {
return this.getAuthorizedStatistics(section);
}
return observableOf(section);
}),
isNotEmptyOperator(), isNotEmptyOperator(),
switchMap((section: MenuSection) => this.getSectionComponent(section).pipe( switchMap((section: MenuSection) => this.getSectionComponent(section).pipe(
map((component: GenericConstructor<AbstractMenuSectionComponent>) => ({ section, component })), map((component: GenericConstructor<AbstractMenuSectionComponent>) => ({ section, component })),
@@ -146,32 +141,6 @@ export class MenuComponent implements OnInit, OnDestroy {
} }
} }
/**
* Get section of statistics after checking authorization
*/
getAuthorizedStatistics(section) {
return this.activatedRouteLastChild.data.pipe(
switchMap((data) => {
return this.authorizationService.isAuthorized(FeatureID.CanViewUsageStatistics, this.getObjectUrl(data)).pipe(
map((canViewUsageStatistics: boolean) => {
if (!canViewUsageStatistics) {
return {};
} else {
return section;
}
}));
}),
);
}
/**
* Get statistics route dso data
*/
getObjectUrl(data) {
const object = data.site ? data.site : data.dso?.payload;
return object?._links?.self?.href;
}
/** /**
* Collapse this menu when it's currently expanded, expand it when its currently collapsed * Collapse this menu when it's currently expanded, expand it when its currently collapsed
* @param {Event} event The user event that triggered this method * @param {Event} event The user event that triggered this method

View File

@@ -51,7 +51,7 @@ describe('BrowseMenuProvider', () => {
let provider: BrowseMenuProvider; let provider: BrowseMenuProvider;
let browseServiceStub = new BrowseServiceStub(); let browseServiceStub = BrowseServiceStub;
beforeEach(() => { beforeEach(() => {
spyOn(browseServiceStub, 'getBrowseDefinitions').and.returnValue( spyOn(browseServiceStub, 'getBrowseDefinitions').and.returnValue(

View File

@@ -1,5 +1,8 @@
import { inject } from '@angular/core';
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { of as observableOf } from 'rxjs';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { Item } from '../../../core/shared/item.model'; import { Item } from '../../../core/shared/item.model';
import { ITEM } from '../../../core/shared/item.resource-type'; import { ITEM } from '../../../core/shared/item.resource-type';
@@ -34,6 +37,18 @@ describe('StatisticsMenuProvider', () => {
}, },
]; ];
const expectedSectionsForItemInvisible: PartialMenuSection[] = [
{
visible: false,
model: {
type: MenuItemType.LINK,
text: 'menu.section.statistics',
link: `statistics/items/test-item-uuid`,
},
icon: 'chart-line',
},
];
let provider: StatisticsMenuProvider; let provider: StatisticsMenuProvider;
const item: Item = Object.assign(new Item(), { const item: Item = Object.assign(new Item(), {
@@ -57,13 +72,18 @@ describe('StatisticsMenuProvider', () => {
}], }],
}, },
}); });
let authorizationService: AuthorizationDataService;
beforeEach(() => { beforeEach(() => {
authorizationService = jasmine.createSpyObj('authorizationService', {
isAuthorized: observableOf(true),
});
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()], imports: [TranslateModule.forRoot()],
providers: [ providers: [
StatisticsMenuProvider, StatisticsMenuProvider,
{ provide: AuthorizationDataService, useValue: authorizationService },
], ],
}); });
provider = TestBed.inject(StatisticsMenuProvider); provider = TestBed.inject(StatisticsMenuProvider);
@@ -86,6 +106,13 @@ describe('StatisticsMenuProvider', () => {
done(); done();
}); });
}); });
it('should not return anything if not authorized to view statistics', (done) => {
(TestBed.inject(AuthorizationDataService) as any).isAuthorized.and.returnValue(observableOf(false));
provider.getSectionsForContext(item).subscribe((sections) => {
expect(sections).toEqual(expectedSectionsForItemInvisible);
done();
});
});
}); });
describe('getRouteContext', () => { describe('getRouteContext', () => {

View File

@@ -12,11 +12,15 @@ import {
RouterStateSnapshot, RouterStateSnapshot,
} from '@angular/router'; } from '@angular/router';
import { import {
combineLatest,
map,
Observable, Observable,
of, of,
} from 'rxjs'; } from 'rxjs';
import { getDSORoute } from '../../../app-routing-paths'; import { getDSORoute } from '../../../app-routing-paths';
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
import { FeatureID } from '../../../core/data/feature-authorization/feature-id';
import { RemoteData } from '../../../core/data/remote-data'; import { RemoteData } from '../../../core/data/remote-data';
import { DSpaceObject } from '../../../core/shared/dspace-object.model'; import { DSpaceObject } from '../../../core/shared/dspace-object.model';
import { import {
@@ -34,6 +38,11 @@ import { AbstractRouteContextMenuProvider } from './helper-providers/route-conte
*/ */
@Injectable() @Injectable()
export class StatisticsMenuProvider extends AbstractRouteContextMenuProvider<DSpaceObject> { export class StatisticsMenuProvider extends AbstractRouteContextMenuProvider<DSpaceObject> {
constructor(
protected authorizationService: AuthorizationDataService,
) {
super();
}
public getRouteContext(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<DSpaceObject> { public getRouteContext(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<DSpaceObject> {
let dsoRD: RemoteData<DSpaceObject> = route.data.dso; let dsoRD: RemoteData<DSpaceObject> = route.data.dso;
@@ -51,7 +60,10 @@ export class StatisticsMenuProvider extends AbstractRouteContextMenuProvider<DSp
} }
public getSectionsForContext(dso: DSpaceObject): Observable<PartialMenuSection[]> { public getSectionsForContext(dso: DSpaceObject): Observable<PartialMenuSection[]> {
return combineLatest([
this.authorizationService.isAuthorized(FeatureID.CanViewUsageStatistics, dso?._links.self.href),
]).pipe(
map(([authorized]) => {
let link = `statistics`; let link = `statistics`;
let dsoRoute; let dsoRoute;
@@ -62,9 +74,9 @@ export class StatisticsMenuProvider extends AbstractRouteContextMenuProvider<DSp
} }
} }
return of([ return [
{ {
visible: true, visible: authorized,
model: { model: {
type: MenuItemType.LINK, type: MenuItemType.LINK,
text: 'menu.section.statistics', text: 'menu.section.statistics',
@@ -72,7 +84,8 @@ export class StatisticsMenuProvider extends AbstractRouteContextMenuProvider<DSp
}, },
icon: 'chart-line', icon: 'chart-line',
}, },
] as PartialMenuSection[]); ];
}),
);
} }
} }