[CST-5307] Move profile-claim-item-modal.component to profile page module and add unit tests

This commit is contained in:
Giuseppe Digilio
2022-05-11 11:59:38 +02:00
parent 853dcecfb8
commit 1e9d393edf
7 changed files with 336 additions and 124 deletions

View File

@@ -26,7 +26,7 @@
</div>
<div class="modal-footer">
<div class="mr-5">
<input type="checkbox" [checked]="false" (change)="toggleCheckbox()"/>
<input type="checkbox" [checked]="checked" (change)="toggleCheckbox()"/>
{{ 'dso-selector.claim.item.not-mine-label' | translate }}
</div>
<button type="submit" class="btn btn-primary ml-5 mr-2" (click)="createFromScratch()" [disabled]="!checked">

View File

@@ -0,0 +1,223 @@
import { ActivatedRoute, Router } from '@angular/router';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
import { of } from 'rxjs';
import { TranslateModule } from '@ngx-translate/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ProfileClaimItemModalComponent } from './profile-claim-item-modal.component';
import { ProfileClaimService } from '../profile-claim/profile-claim.service';
import { Item } from '../../core/shared/item.model';
import { ItemSearchResult } from '../../shared/object-collection/shared/item-search-result.model';
import { SearchObjects } from '../../shared/search/models/search-objects.model';
import { createSuccessfulRemoteDataObject } from '../../shared/remote-data.utils';
import { getItemPageRoute } from '../../item-page/item-page-routing-paths';
import { RouterStub } from '../../shared/testing/router.stub';
describe('ProfileClaimItemModalComponent', () => {
let component: ProfileClaimItemModalComponent;
let fixture: ComponentFixture<ProfileClaimItemModalComponent>;
const item1: Item = Object.assign(new Item(), {
uuid: 'e1c51c69-896d-42dc-8221-1d5f2ad5516e',
metadata: {
'person.email': [
{
value: 'fake@email.com'
}
],
'person.familyName': [
{
value: 'Doe'
}
],
'person.givenName': [
{
value: 'John'
}
]
},
_links: {
self: {
href: 'item-href'
}
}
});
const item2: Item = Object.assign(new Item(), {
uuid: 'c8279647-1acc-41ae-b036-951d5f65649b',
metadata: {
'person.email': [
{
value: 'fake2@email.com'
}
],
'dc.title': [
{
value: 'John, Doe'
}
]
},
_links: {
self: {
href: 'item-href'
}
}
});
const item3: Item = Object.assign(new Item(), {
uuid: 'c8279647-1acc-41ae-b036-951d5f65649b',
metadata: {
'person.email': [
{
value: 'fake3@email.com'
}
],
'dc.title': [
{
value: 'John, Doe'
}
]
},
_links: {
self: {
href: 'item-href'
}
}
});
const searchResult1 = Object.assign(new ItemSearchResult(), { indexableObject: item1 });
const searchResult2 = Object.assign(new ItemSearchResult(), { indexableObject: item2 });
const searchResult3 = Object.assign(new ItemSearchResult(), { indexableObject: item3 });
const searchResult = Object.assign(new SearchObjects(), {
page: [searchResult1, searchResult2, searchResult3]
});
const emptySearchResult = Object.assign(new SearchObjects(), {
page: []
});
const searchResultRD = createSuccessfulRemoteDataObject(searchResult);
const emptySearchResultRD = createSuccessfulRemoteDataObject(emptySearchResult);
const profileClaimService = jasmine.createSpyObj('profileClaimService', {
searchForSuggestions: jasmine.createSpy('searchForSuggestions')
});
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()],
declarations: [ProfileClaimItemModalComponent],
providers: [
{ provide: NgbActiveModal, useValue: {} },
{ provide: ActivatedRoute, useValue: {} },
{ provide: Router, useValue: new RouterStub() },
{ provide: ProfileClaimService, useValue: profileClaimService }
],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProfileClaimItemModalComponent);
component = fixture.componentInstance;
});
describe('when there are suggestions', () => {
beforeEach(() => {
profileClaimService.searchForSuggestions.and.returnValue(of(searchResultRD));
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should init the list of suggestions', () => {
const entries = fixture.debugElement.queryAll(By.css('.list-group-item'));
expect(component.listEntries$.value).toEqual(searchResultRD);
expect(entries.length).toBe(3);
});
it('should close modal and call navigate method', () => {
spyOn(component, 'close');
spyOn(component, 'navigate');
component.selectItem(item1);
expect(component.close).toHaveBeenCalled();
expect(component.navigate).toHaveBeenCalledWith(item1);
});
it('should call router navigate method', () => {
const route = [getItemPageRoute(item1)];
component.navigate(item1);
expect((component as any).router.navigate).toHaveBeenCalledWith(route);
});
it('should toggle checkbox', () => {
component.toggleCheckbox();
expect((component as any).checked).toBe(true);
});
it('should emit create event', () => {
spyOn(component, 'close');
spyOn(component.create, 'emit');
component.createFromScratch();
expect(component.create.emit).toHaveBeenCalled();
expect(component.close).toHaveBeenCalled();
});
});
describe('when there are not suggestions', () => {
beforeEach(() => {
profileClaimService.searchForSuggestions.and.returnValue(of(emptySearchResultRD));
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should init the list of suggestions', () => {
const entries = fixture.debugElement.queryAll(By.css('.list-group-item'));
expect(component.listEntries$.value).toEqual(emptySearchResultRD);
expect(entries.length).toBe(0);
});
it('should close modal and call navigate method', () => {
spyOn(component, 'close');
spyOn(component, 'navigate');
component.selectItem(item1);
expect(component.close).toHaveBeenCalled();
expect(component.navigate).toHaveBeenCalledWith(item1);
});
it('should call router navigate method', () => {
const route = [getItemPageRoute(item1)];
component.navigate(item1);
expect((component as any).router.navigate).toHaveBeenCalledWith(route);
});
it('should toggle checkbox', () => {
component.toggleCheckbox();
expect((component as any).checked).toBe(true);
});
it('should emit create event', () => {
spyOn(component, 'close');
spyOn(component.create, 'emit');
component.createFromScratch();
expect(component.create.emit).toHaveBeenCalled();
expect(component.close).toHaveBeenCalled();
});
});
});

View File

@@ -0,0 +1,105 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { BehaviorSubject } from 'rxjs';
import { RemoteData } from '../../core/data/remote-data';
import { Item } from '../../core/shared/item.model';
import {
DSOSelectorModalWrapperComponent
} from '../../shared/dso-selector/modal-wrappers/dso-selector-modal-wrapper.component';
import { getItemPageRoute } from '../../item-page/item-page-routing-paths';
import { EPerson } from '../../core/eperson/models/eperson.model';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { ViewMode } from '../../core/shared/view-mode.model';
import { ProfileClaimService } from '../profile-claim/profile-claim.service';
import { CollectionElementLinkType } from '../../shared/object-collection/collection-element-link.type';
import { SearchObjects } from '../../shared/search/models/search-objects.model';
/**
* Component representing a modal that show a list of suggested profile item to claim
*/
@Component({
selector: 'ds-profile-claim-item-modal',
templateUrl: './profile-claim-item-modal.component.html'
})
export class ProfileClaimItemModalComponent extends DSOSelectorModalWrapperComponent implements OnInit {
/**
* The current page's DSO
*/
@Input() dso: DSpaceObject;
/**
* List of suggested profiles, if any
*/
listEntries$: BehaviorSubject<RemoteData<SearchObjects<DSpaceObject>>> = new BehaviorSubject(null);
/**
* The view mode of the listed objects
*/
viewMode = ViewMode.ListElement;
/**
* The available link types
*/
linkTypes = CollectionElementLinkType;
/**
* A boolean representing form checkbox status
*/
checked = false;
/**
* An event fired when user click on submit button
*/
@Output() create: EventEmitter<any> = new EventEmitter<any>();
constructor(protected activeModal: NgbActiveModal, protected route: ActivatedRoute, private router: Router,
private profileClaimService: ProfileClaimService) {
super(activeModal, route);
}
/**
* Retrieve suggested profiles, if any
*/
ngOnInit(): void {
this.profileClaimService.searchForSuggestions(this.dso as EPerson).subscribe(
(result: RemoteData<SearchObjects<DSpaceObject>>) => this.listEntries$.next(result)
);
}
/**
* Close modal and Navigate to given DSO
*
* @param dso
*/
selectItem(dso: DSpaceObject): void {
this.close();
this.navigate(dso);
}
/**
* Navigate to given DSO
*
* @param dso
*/
navigate(dso: DSpaceObject) {
this.router.navigate([getItemPageRoute(dso as Item)]);
}
/**
* Change the status of form's checkbox
*/
toggleCheckbox() {
this.checked = !this.checked;
}
/**
* Emit an event when profile should be created from scratch
*/
createFromScratch() {
this.create.emit();
this.close();
}
}

View File

@@ -5,10 +5,13 @@ import { ProfilePageRoutingModule } from './profile-page-routing.module';
import { ProfilePageComponent } from './profile-page.component';
import { ProfilePageMetadataFormComponent } from './profile-page-metadata-form/profile-page-metadata-form.component';
import { ProfilePageSecurityFormComponent } from './profile-page-security-form/profile-page-security-form.component';
import { ProfilePageResearcherFormComponent } from './profile-page-researcher-form/profile-page-researcher-form.component';
import {
ProfilePageResearcherFormComponent
} from './profile-page-researcher-form/profile-page-researcher-form.component';
import { ThemedProfilePageComponent } from './themed-profile-page.component';
import { FormModule } from '../shared/form/form.module';
import { UiSwitchModule } from 'ngx-ui-switch';
import { ProfileClaimItemModalComponent } from './profile-claim-item-modal/profile-claim-item-modal.component';
@NgModule({
@@ -29,6 +32,7 @@ import { UiSwitchModule } from 'ngx-ui-switch';
declarations: [
ProfilePageComponent,
ThemedProfilePageComponent,
ProfileClaimItemModalComponent,
ProfilePageMetadataFormComponent,
ProfilePageSecurityFormComponent,
ProfilePageResearcherFormComponent

View File

@@ -1,45 +0,0 @@
import { ActivatedRoute, Router } from '@angular/router';
/* tslint:disable:no-unused-variable */
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ClaimItemSelectorComponent } from './claim-item-selector.component';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ProfileClaimService } from '../../../../profile-page/profile-claim/profile-claim.service';
import { of } from 'rxjs';
describe('ClaimItemSelectorComponent', () => {
let component: ClaimItemSelectorComponent;
let fixture: ComponentFixture<ClaimItemSelectorComponent>;
const profileClaimService = jasmine.createSpyObj('profileClaimService', {
search: of({ payload: {page: []}})
});
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot()],
declarations: [ ClaimItemSelectorComponent ],
providers: [
{ provide: NgbActiveModal, useValue: {} },
{ provide: ActivatedRoute, useValue: {} },
{ provide: Router, useValue: {} },
{ provide: ProfileClaimService, useValue: profileClaimService }
],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ClaimItemSelectorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -1,69 +0,0 @@
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { BehaviorSubject } from 'rxjs';
import { PaginatedList } from '../../../../core/data/paginated-list.model';
import { RemoteData } from '../../../../core/data/remote-data';
import { Item } from '../../../../core/shared/item.model';
import { SearchResult } from '../../../search/models/search-result.model';
import { DSOSelectorModalWrapperComponent } from '../dso-selector-modal-wrapper.component';
import { getItemPageRoute } from '../../../../item-page/item-page-routing-paths';
import { EPerson } from '../../../../core/eperson/models/eperson.model';
import { DSpaceObject } from '../../../../core/shared/dspace-object.model';
import { ViewMode } from '../../../../core/shared/view-mode.model';
import { ProfileClaimService } from '../../../../profile-page/profile-claim/profile-claim.service';
import { CollectionElementLinkType } from '../../../object-collection/collection-element-link.type';
/**
* Component
*/
@Component({
selector: 'ds-claim-item-selector',
templateUrl: './claim-item-selector.component.html'
})
export class ClaimItemSelectorComponent extends DSOSelectorModalWrapperComponent implements OnInit {
@Input() dso: DSpaceObject;
listEntries$: BehaviorSubject<RemoteData<PaginatedList<SearchResult<DSpaceObject>>>> = new BehaviorSubject(null);
viewMode = ViewMode.ListElement;
// enum to be exposed
linkTypes = CollectionElementLinkType;
checked = false;
@Output() create: EventEmitter<any> = new EventEmitter<any>();
constructor(protected activeModal: NgbActiveModal, protected route: ActivatedRoute, private router: Router,
private profileClaimService: ProfileClaimService) {
super(activeModal, route);
}
ngOnInit(): void {
this.profileClaimService.search(this.dso as EPerson).subscribe(
(result) => this.listEntries$.next(result)
);
}
// triggered when an item is selected
selectItem(dso: DSpaceObject): void {
this.close();
this.navigate(dso);
}
navigate(dso: DSpaceObject) {
this.router.navigate([getItemPageRoute(dso as Item)]);
}
toggleCheckbox() {
this.checked = !this.checked;
}
createFromScratch() {
this.create.emit();
this.close();
}
}

View File

@@ -285,9 +285,6 @@ import { LogInOidcComponent } from './log-in/methods/oidc/log-in-oidc.component'
import {
ThemedItemListPreviewComponent
} from './object-list/my-dspace-result-list-element/item-list-preview/themed-item-list-preview.component';
import {
ClaimItemSelectorComponent
} from './dso-selector/modal-wrappers/claim-item-selector/claim-item-selector.component';
import { ExternalLinkMenuItemComponent } from './menu/menu-item/external-link-menu-item.component';
import { PersonPageClaimButtonComponent } from './dso-page/person-page-claim-button/person-page-claim-button.component';
@@ -458,9 +455,7 @@ const COMPONENTS = [
CollectionSidebarSearchListElementComponent,
CommunitySidebarSearchListElementComponent,
SearchNavbarComponent,
ScopeSelectorModalComponent,
ClaimItemSelectorComponent
ScopeSelectorModalComponent
];
const ENTRY_COMPONENTS = [
@@ -517,7 +512,6 @@ const ENTRY_COMPONENTS = [
OnClickMenuItemComponent,
TextMenuItemComponent,
ScopeSelectorModalComponent,
ClaimItemSelectorComponent,
ExternalLinkMenuItemComponent
];