Skip to content

Commit acd0b9c

Browse files
[DSC-1708] unit test & comments
1 parent 9aae4c1 commit acd0b9c

9 files changed

Lines changed: 279 additions & 40 deletions

src/app/shared/metadata-link-view/metadata-link-view-avatar-popover/metadata-link-view-avatar-popover.component.spec.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,36 @@
11
/* tslint:disable:no-unused-variable */
22
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3-
import { By } from '@angular/platform-browser';
4-
import { DebugElement } from '@angular/core';
53

64
import { MetadataLinkViewAvatarPopoverComponent } from './metadata-link-view-avatar-popover.component';
5+
import { of as observableOf } from 'rxjs';
6+
import { AuthService } from '../../../core/auth/auth.service';
7+
import { AuthorizationDataService } from '../../../core/data/feature-authorization/authorization-data.service';
8+
import { FileService } from '../../../core/shared/file.service';
79

810
describe('MetadataLinkViewAvatarPopoverComponent', () => {
911
let component: MetadataLinkViewAvatarPopoverComponent;
1012
let fixture: ComponentFixture<MetadataLinkViewAvatarPopoverComponent>;
13+
let authService;
14+
let authorizationService;
15+
let fileService;
1116

1217
beforeEach(async(() => {
18+
authService = jasmine.createSpyObj('AuthService', {
19+
isAuthenticated: observableOf(true),
20+
});
21+
authorizationService = jasmine.createSpyObj('AuthorizationService', {
22+
isAuthorized: observableOf(true),
23+
});
24+
fileService = jasmine.createSpyObj('FileService', {
25+
retrieveFileDownloadLink: null
26+
});
1327
TestBed.configureTestingModule({
14-
declarations: [ MetadataLinkViewAvatarPopoverComponent ]
28+
declarations: [ MetadataLinkViewAvatarPopoverComponent ],
29+
providers: [
30+
{ provide: AuthService, useValue: authService },
31+
{ provide: AuthorizationDataService, useValue: authorizationService },
32+
{ provide: FileService, useValue: fileService }
33+
]
1534
})
1635
.compileComponents();
1736
}));

src/app/shared/metadata-link-view/metadata-link-view-avatar-popover/metadata-link-view-avatar-popover.component.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,8 @@ import { ThumbnailComponent } from 'src/app/thumbnail/thumbnail.component';
88
})
99
export class MetadataLinkViewAvatarPopoverComponent extends ThumbnailComponent {
1010

11+
/**
12+
* The fallback image to use when the thumbnail is not available
13+
*/
1114
fallbackImage = 'assets/images/person-placeholder.svg';
1215
}
Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
1-
<div [class]="field.styleValue">
2-
<span class="d-flex align-items-center txt-value">
1+
<span class="d-flex align-items-center txt-value">
2+
<a *ngIf="(orcidUrl$ | async); else noOrcidUrl" href="{{(orcidUrl$ | async)}}/{{metadataValue.value}}" target="_blank">
3+
{{ metadataValue.value }}
4+
</a>
35

4-
<a *ngIf="(orcidUrl$ | async); else noOrcidUrl" href="{{(orcidUrl$ | async)}}/{{metadataValue.value}}" target="_blank">
5-
{{ metadataValue.value }}
6-
</a>
6+
<ng-template #noOrcidUrl>{{ metadataValue.value }}</ng-template>
77

8-
<ng-template #noOrcidUrl>{{ metadataValue.value }}</ng-template>
8+
<img *ngIf="hasOrcidBadge()"
9+
placement="top"
10+
ngbTooltip="{{ 'orcid.badge.tooltip' | translate }}"
11+
class="orcid-icon"
12+
alt="orcid-logo"
13+
src="assets/images/orcid.logo.icon.svg"/>
914

10-
<img *ngIf="hasOrcidBadge()"
11-
placement="top"
12-
ngbTooltip="{{ 'orcid.badge.tooltip' | translate }}"
13-
class="orcid-icon"
14-
alt="orcid-logo"
15-
src="assets/images/orcid.logo.icon.svg"/>
16-
17-
</span>
18-
</div>
15+
</span>

src/app/shared/metadata-link-view/metadata-link-view-orcid/metadata-link-view-orcid.component.spec.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,70 @@
1+
import { ConfigurationDataService } from './../../../core/data/configuration-data.service';
2+
import { Item } from 'src/app/core/shared/item.model';
3+
import { TranslateLoaderMock } from './../../testing/translate-loader.mock';
4+
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
15
import { ComponentFixture, TestBed } from '@angular/core/testing';
26

37
import { MetadataLinkViewOrcidComponent } from './metadata-link-view-orcid.component';
8+
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
9+
import { MetadataValue } from 'src/app/core/shared/metadata.models';
10+
import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
411

512
describe('MetadataLinkViewOrcidComponent', () => {
613
let component: MetadataLinkViewOrcidComponent;
714
let fixture: ComponentFixture<MetadataLinkViewOrcidComponent>;
815

16+
const configurationDataService = jasmine.createSpyObj('configurationDataService', {
17+
findByPropertyName: createSuccessfulRemoteDataObject$({ values: ['https://sandbox.orcid.org'] })
18+
});
19+
20+
21+
const metadataValue = Object.assign(new MetadataValue(), {
22+
'value': '0000-0001-8918-3592',
23+
'language': 'en_US',
24+
'authority': null,
25+
'confidence': -1,
26+
'place': 0
27+
});
28+
29+
const testItem = Object.assign(new Item(),
30+
{
31+
type: 'item',
32+
metadata: {
33+
'person.identifier.orcid': [metadataValue],
34+
'dspace.orcid.authenticated': [
35+
{
36+
language: null,
37+
value: 'authenticated'
38+
}
39+
]
40+
},
41+
uuid: 'test-item-uuid',
42+
}
43+
);
44+
945
beforeEach(async () => {
1046
await TestBed.configureTestingModule({
11-
declarations: [ MetadataLinkViewOrcidComponent ]
47+
declarations: [ MetadataLinkViewOrcidComponent ],
48+
imports: [TranslateModule.forRoot({
49+
loader: {
50+
provide: TranslateLoader,
51+
useClass: TranslateLoaderMock
52+
}
53+
}), BrowserAnimationsModule],
54+
providers: [
55+
{ provide: 'fieldProvider', useValue: null },
56+
{ provide: 'itemProvider', useValue: testItem },
57+
{ provide: 'metadataValueProvider', useValue: metadataValue },
58+
{ provide: 'renderingSubTypeProvider', useValue: '' },
59+
{ provide: 'tabNameProvider', useValue: '' },
60+
{ provide: ConfigurationDataService, useValue: configurationDataService}
61+
],
1262
})
1363
.compileComponents();
1464

1565
fixture = TestBed.createComponent(MetadataLinkViewOrcidComponent);
1666
component = fixture.componentInstance;
67+
component.itemValue = testItem;
1768
fixture.detectChanges();
1869
});
1970

src/app/shared/metadata-link-view/metadata-link-view-popover/metadata-link-view-popover.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
<a
5555
class="font-weight-bold"
5656
[routerLink]="[getItemPageRoute()]"
57-
data-test="viewItemButton"
57+
data-test="more-info-link"
5858
>
5959
{{ "metadata-link-view.popover.label.more-info" | translate }}
6060
</a>
Lines changed: 107 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,131 @@
1-
/* tslint:disable:no-unused-variable */
1+
import { MetadataValueFilter } from 'src/app/core/shared/metadata.models';
2+
import { Item } from 'src/app/core/shared/item.model';
23
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
3-
import { By } from '@angular/platform-browser';
4-
import { DebugElement } from '@angular/core';
54

65
import { MetadataLinkViewPopoverComponent } from './metadata-link-view-popover.component';
6+
import { environment } from 'src/environments/environment.test';
7+
import { TranslateModule } from '@ngx-translate/core';
8+
import { NO_ERRORS_SCHEMA } from '@angular/core';
9+
import { By } from '@angular/platform-browser';
10+
import { createSuccessfulRemoteDataObject$ } from '../../remote-data.utils';
11+
import { Bitstream } from 'src/app/core/shared/bitstream.model';
712

813
describe('MetadataLinkViewPopoverComponent', () => {
914
let component: MetadataLinkViewPopoverComponent;
1015
let fixture: ComponentFixture<MetadataLinkViewPopoverComponent>;
1116

17+
18+
const itemMock = Object.assign(new Item(), {
19+
uuid: '1234-1234-1234-1234',
20+
21+
firstMetadataValue(keyOrKeys: string | string[], valueFilter?: MetadataValueFilter): string {
22+
return itemMock.metadata[keyOrKeys as string][0].value;
23+
},
24+
25+
metadata: {
26+
'dc.title': [
27+
{
28+
value: 'file name',
29+
language: null
30+
}
31+
],
32+
'dc.identifier.uri': [
33+
{
34+
value: 'http://example.com',
35+
language: null
36+
}
37+
],
38+
'dc.description.abstract': [
39+
{
40+
value: 'Long text description',
41+
language: null
42+
}
43+
],
44+
'organization.identifier.ror': [
45+
{
46+
value: 'https://ror.org/1234',
47+
language: null
48+
}
49+
],
50+
'person.identifier.orcid': [
51+
{
52+
value: 'https://orcid.org/0000-0000-0000-0000',
53+
language: null
54+
}
55+
],
56+
'dspace.entity.type': [
57+
{
58+
value: 'Person',
59+
language: null
60+
}
61+
]
62+
},
63+
thumbnail: createSuccessfulRemoteDataObject$(new Bitstream())
64+
});
65+
1266
beforeEach(async(() => {
1367
TestBed.configureTestingModule({
14-
declarations: [ MetadataLinkViewPopoverComponent ]
68+
declarations: [ MetadataLinkViewPopoverComponent ],
69+
imports: [TranslateModule.forRoot()],
70+
schemas: [NO_ERRORS_SCHEMA]
1571
})
1672
.compileComponents();
1773
}));
1874

1975
beforeEach(() => {
2076
fixture = TestBed.createComponent(MetadataLinkViewPopoverComponent);
2177
component = fixture.componentInstance;
78+
component.item = itemMock;
79+
itemMock.firstMetadataValue = jasmine.createSpy()
80+
.withArgs('dspace.entity.type').and.returnValue('Person')
81+
.withArgs('dc.title').and.returnValue('Test Title')
82+
.withArgs('dc.identifier.uri').and.returnValue('http://example.com')
83+
.withArgs('dc.description.abstract').and.returnValue('Long text description')
84+
.withArgs('organization.identifier.ror').and.returnValue('https://ror.org/1234')
85+
.withArgs('person.identifier.orcid').and.returnValue('https://orcid.org/0000-0000-0000-0000');
86+
2287
fixture.detectChanges();
2388
});
2489

2590
it('should create', () => {
2691
expect(component).toBeTruthy();
2792
});
93+
94+
it('should display the item title', () => {
95+
const titleElement = fixture.debugElement.query(By.css('.font-weight-bold.h4'));
96+
expect(titleElement.nativeElement.textContent).toContain('Test Title');
97+
});
98+
99+
it('should display a link for each metadata field that is a valid link', () => {
100+
component.entityMetdataFields = ['dc.identifier.uri'];
101+
fixture.detectChanges();
102+
const linkElement = fixture.debugElement.query(By.css('a[href="http://example.com"]'));
103+
expect(linkElement).toBeTruthy();
104+
});
105+
106+
it('should retrieve the identifier subtype configuration based on the given metadata value', () => {
107+
const metadataValue = 'organization.identifier.ror';
108+
const expectedSubtypeConfig = environment.identifierSubtypes.find((config) => config.name === 'ror');
109+
expect(component.getSourceSubTypeIdentifier(metadataValue)).toEqual(expectedSubtypeConfig);
110+
});
111+
112+
113+
it('should check if a given metadata value is a valid link', () => {
114+
const validLink = 'http://example.com';
115+
const invalidLink = 'not a link';
116+
expect(component.isLink(validLink)).toBeTrue();
117+
expect(component.isLink(invalidLink)).toBeFalse();
118+
});
119+
120+
it('should display the "more info" link with the correct router link', () => {
121+
spyOn(component, 'getItemPageRoute').and.returnValue('/item/' + itemMock.uuid);
122+
fixture.detectChanges();
123+
const moreInfoLinkElement = fixture.debugElement.query(By.css('a[data-test="more-info-link"]'));
124+
expect(moreInfoLinkElement.nativeElement.routerLink).toContain('/item/' + itemMock.uuid);
125+
});
126+
127+
it('should display the avatar popover when item has a thumbnail', () => {
128+
const avatarPopoverElement = fixture.debugElement.query(By.css('ds-metadata-link-view-avatar-popover'));
129+
expect(avatarPopoverElement).toBeTruthy();
130+
});
28131
});

src/app/shared/metadata-link-view/metadata-link-view-popover/metadata-link-view-popover.component.ts

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { Item } from './../../../core/shared/item.model';
44
import { Component, Input, OnInit } from '@angular/core';
55
import { environment } from 'src/environments/environment';
66
import { hasNoValue, hasValue } from '../../empty.util';
7-
import { MetadataView } from '../metadata-view.model';
87

98
import { AuthorithyIcon } from 'src/config/submission-config.interface';
109
import { getItemPageRoute } from 'src/app/item-page/item-page-routing-paths';
@@ -16,25 +15,47 @@ import { getItemPageRoute } from 'src/app/item-page/item-page-routing-paths';
1615
})
1716
export class MetadataLinkViewPopoverComponent implements OnInit {
1817

18+
/**
19+
* The item to display the metadata for
20+
*/
1921
@Input() item: Item;
2022

21-
@Input() metadataView: MetadataView;
22-
23+
/**
24+
* The metadata link view popover data configuration.
25+
* This configuration is used to determine which metadata fields to display for the given entity type
26+
*/
2327
metadataLinkViewPopoverData: MetadataLinkViewPopoverDataConfig = environment.metadataLinkViewPopoverData;
2428

29+
/**
30+
* The metadata fields to display for the given entity type
31+
*/
2532
entityMetdataFields: string[] = [];
2633

34+
/**
35+
* The metadata fields including long text metadata values.
36+
* These metadata values should be truncated to a certain length.
37+
*/
2738
longTextMetadataList = ['dc.description.abstract', 'dc.description'];
2839

40+
/**
41+
* The source icons configuration
42+
*/
2943
sourceIcons: AuthorithyIcon[] = environment.submission.icons.authority.sourceIcons;
3044

3145
/**
3246
* The identifier subtype configurations
3347
*/
3448
identifierSubtypeConfig: IdentifierSubtypesConfig[] = environment.identifierSubtypes;
3549

50+
/**
51+
* Whether the entity type is not found in the metadataLinkViewPopoverData configuration
52+
*/
3653
isOtherEntityType = false;
3754

55+
/**
56+
* If `metadataLinkViewPopoverData` is provided, it retrieves the metadata fields based on the entity type.
57+
* If no metadata fields are found for the entity type, it falls back to the fallback metadata list.
58+
*/
3859
ngOnInit() {
3960
if (this.metadataLinkViewPopoverData) {
4061
const metadataFields = this.metadataLinkViewPopoverData.entityDataConfig.find((config) => config.entityType === this.item.entityType);
@@ -43,20 +64,27 @@ export class MetadataLinkViewPopoverComponent implements OnInit {
4364
}
4465
}
4566

67+
/**
68+
* Checks if the given metadata value is a valid link.
69+
*/
4670
isLink(metadataValue: string): boolean {
4771
const urlRegex = /^(http|https):\/\/[^ "]+$/;
4872
return urlRegex.test(metadataValue);
4973
}
5074

51-
getSourceIconPath(metadataValue: string): string {
52-
const icon = this.sourceIcons.find((i) => i.source.toLowerCase() === metadataValue.toLowerCase());
53-
return hasValue(icon) ? icon.path : '';
54-
}
55-
75+
/**
76+
* Returns the page route for the item.
77+
* @returns The page route for the item.
78+
*/
5679
getItemPageRoute(): string {
5780
return getItemPageRoute(this.item);
5881
}
5982

83+
/**
84+
* Retrieves the identifier subtype configuration based on the given metadata value.
85+
* @param metadataValue - The metadata value used to determine the identifier subtype.
86+
* @returns The identifier subtype configuration object.
87+
*/
6088
getSourceSubTypeIdentifier(metadataValue: string): IdentifierSubtypesConfig {
6189
const metadataValueSplited = metadataValue.split('.');
6290
const subtype = metadataValueSplited[metadataValueSplited.length - 1];

0 commit comments

Comments
 (0)