forked from DSpace/dspace-angular
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
119915: Extracted the authority/controlled vocabulary logic out of Ds…
…oEditMetadataValueComponent Also: - Converted the code to the standalone structure - Made DsoEditMetadataValueFieldLoaderComponent extend AbstractComponentLoaderComponent
- Loading branch information
1 parent
acf80db
commit 8db0766
Showing
20 changed files
with
902 additions
and
840 deletions.
There are no files selected for viewing
39 changes: 39 additions & 0 deletions
39
...-field/dso-edit-metadata-authority-field/dso-edit-metadata-authority-field.component.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<ds-dynamic-scrollable-dropdown *ngIf="mdValue.editing && (isScrollableVocabulary$ | async)" | ||
[bindId]="mdField" | ||
[group]="group" | ||
[model]="getModel()" | ||
(change)="onChangeAuthorityField($event)"> | ||
</ds-dynamic-scrollable-dropdown> | ||
<ds-dynamic-onebox | ||
*ngIf="mdValue.editing && ((isHierarchicalVocabulary$ | async) || (isSuggesterVocabulary$ | async))" | ||
[group]="group" | ||
[model]="getModel()" | ||
(change)="onChangeAuthorityField($event)"> | ||
</ds-dynamic-onebox> | ||
<div class="mt-2" *ngIf=" mdValue.editing && (isAuthorityControlled$ | async) && (isSuggesterVocabulary$ | async)"> | ||
<div class="btn-group w-75"> | ||
<i dsAuthorityConfidenceState | ||
class="fas fa-fw p-0 mr-1 mt-auto mb-auto" | ||
aria-hidden="true" | ||
[authorityValue]="mdValue.newValue.confidence" | ||
[iconMode]="true" | ||
></i> | ||
<input class="form-control form-outline" data-test="authority-input" [(ngModel)]="mdValue.newValue.authority" | ||
[disabled]="!editingAuthority" | ||
[attr.aria-label]="(dsoType + '.edit.metadata.edit.authority.key') | translate" | ||
(change)="onChangeAuthorityKey()"/> | ||
<button class="btn btn-outline-secondary btn-sm ng-star-inserted" id="metadata-confirm-btn" | ||
*ngIf="!editingAuthority" | ||
[title]="dsoType + '.edit.metadata.edit.buttons.open-authority-edition' | translate" | ||
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.open-authority-edition' | translate }}" | ||
(click)="onChangeEditingAuthorityStatus(true)"> | ||
<i class="fas fa-lock fa-fw"></i> | ||
</button> | ||
<button class="btn btn-outline-success btn-sm ng-star-inserted" id="metadata-confirm-btn" *ngIf="editingAuthority" | ||
[title]="dsoType + '.edit.metadata.edit.buttons.close-authority-edition' | translate" | ||
ngbTooltip="{{ dsoType + '.edit.metadata.edit.buttons.close-authority-edition' | translate }}" | ||
(click)="onChangeEditingAuthorityStatus(false)"> | ||
<i class="fas fa-lock-open fa-fw"></i> | ||
</button> | ||
</div> | ||
</div> |
Empty file.
353 changes: 353 additions & 0 deletions
353
...eld/dso-edit-metadata-authority-field/dso-edit-metadata-authority-field.component.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,353 @@ | ||
import { | ||
ComponentFixture, | ||
TestBed, | ||
waitForAsync, | ||
} from '@angular/core/testing'; | ||
import { By } from '@angular/platform-browser'; | ||
import { TranslateModule } from '@ngx-translate/core'; | ||
|
||
import { ItemDataService } from '../../../../core/data/item-data.service'; | ||
import { MetadataField } from '../../../../core/metadata/metadata-field.model'; | ||
import { MetadataSchema } from '../../../../core/metadata/metadata-schema.model'; | ||
import { RegistryService } from '../../../../core/registry/registry.service'; | ||
import { Collection } from '../../../../core/shared/collection.model'; | ||
import { ConfidenceType } from '../../../../core/shared/confidence-type'; | ||
import { DSpaceObject } from '../../../../core/shared/dspace-object.model'; | ||
import { Item } from '../../../../core/shared/item.model'; | ||
import { MetadataValue } from '../../../../core/shared/metadata.models'; | ||
import { Vocabulary } from '../../../../core/submission/vocabularies/models/vocabulary.model'; | ||
import { VocabularyService } from '../../../../core/submission/vocabularies/vocabulary.service'; | ||
import { DynamicOneboxModel } from '../../../../shared/form/builder/ds-dynamic-form-ui/models/onebox/dynamic-onebox.model'; | ||
import { DynamicScrollableDropdownModel } from '../../../../shared/form/builder/ds-dynamic-form-ui/models/scrollable-dropdown/dynamic-scrollable-dropdown.model'; | ||
import { NotificationsService } from '../../../../shared/notifications/notifications.service'; | ||
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils'; | ||
import { createPaginatedList } from '../../../../shared/testing/utils.test'; | ||
import { VocabularyServiceStub } from '../../../../shared/testing/vocabulary-service.stub'; | ||
import { DsoEditMetadataValue } from '../../dso-edit-metadata-form'; | ||
import { DsoEditMetadataAuthorityFieldComponent } from './dso-edit-metadata-authority-field.component'; | ||
|
||
describe('DsoEditMetadataAuthorityFieldComponent', () => { | ||
let component: DsoEditMetadataAuthorityFieldComponent; | ||
let fixture: ComponentFixture<DsoEditMetadataAuthorityFieldComponent>; | ||
|
||
let vocabularyService: any; | ||
let itemService: ItemDataService; | ||
let registryService: RegistryService; | ||
let notificationsService: NotificationsService; | ||
|
||
let dso: DSpaceObject; | ||
|
||
const collection = Object.assign(new Collection(), { | ||
uuid: 'fake-uuid', | ||
}); | ||
|
||
const item = Object.assign(new Item(), { | ||
_links: { | ||
self: { href: 'fake-item-url/item' }, | ||
}, | ||
id: 'item', | ||
uuid: 'item', | ||
owningCollection: createSuccessfulRemoteDataObject$(collection), | ||
}); | ||
|
||
const mockVocabularyScrollable: Vocabulary = { | ||
id: 'scrollable', | ||
name: 'scrollable', | ||
scrollable: true, | ||
hierarchical: false, | ||
preloadLevel: 0, | ||
type: 'vocabulary', | ||
_links: { | ||
self: { | ||
href: 'self', | ||
}, | ||
entries: { | ||
href: 'entries', | ||
}, | ||
}, | ||
}; | ||
|
||
const mockVocabularyHierarchical: Vocabulary = { | ||
id: 'hierarchical', | ||
name: 'hierarchical', | ||
scrollable: false, | ||
hierarchical: true, | ||
preloadLevel: 2, | ||
type: 'vocabulary', | ||
_links: { | ||
self: { | ||
href: 'self', | ||
}, | ||
entries: { | ||
href: 'entries', | ||
}, | ||
}, | ||
}; | ||
|
||
const mockVocabularySuggester: Vocabulary = { | ||
id: 'suggester', | ||
name: 'suggester', | ||
scrollable: false, | ||
hierarchical: false, | ||
preloadLevel: 0, | ||
type: 'vocabulary', | ||
_links: { | ||
self: { | ||
href: 'self', | ||
}, | ||
entries: { | ||
href: 'entries', | ||
}, | ||
}, | ||
}; | ||
|
||
let editMetadataValue: DsoEditMetadataValue; | ||
let metadataValue: MetadataValue; | ||
let metadataSchema: MetadataSchema; | ||
let metadataFields: MetadataField[]; | ||
|
||
beforeEach(async () => { | ||
itemService = jasmine.createSpyObj('itemService', { | ||
findByHref: createSuccessfulRemoteDataObject$(item), | ||
}); | ||
vocabularyService = new VocabularyServiceStub(); | ||
registryService = jasmine.createSpyObj('registryService', { | ||
queryMetadataFields: createSuccessfulRemoteDataObject$(createPaginatedList(metadataFields)), | ||
}); | ||
notificationsService = jasmine.createSpyObj('notificationsService', ['error', 'success']); | ||
|
||
metadataValue = Object.assign(new MetadataValue(), { | ||
value: 'Regular Name', | ||
language: 'en', | ||
place: 0, | ||
authority: undefined, | ||
}); | ||
editMetadataValue = new DsoEditMetadataValue(metadataValue); | ||
metadataSchema = Object.assign(new MetadataSchema(), { | ||
id: 0, | ||
prefix: 'metadata', | ||
namespace: 'https://example.com/', | ||
}); | ||
metadataFields = [ | ||
Object.assign(new MetadataField(), { | ||
id: 0, | ||
element: 'regular', | ||
qualifier: null, | ||
schema: createSuccessfulRemoteDataObject$(metadataSchema), | ||
}), | ||
]; | ||
dso = Object.assign(new DSpaceObject(), { | ||
_links: { | ||
self: { href: 'fake-dso-url/dso' }, | ||
}, | ||
}); | ||
|
||
await TestBed.configureTestingModule({ | ||
imports: [ | ||
DsoEditMetadataAuthorityFieldComponent, | ||
TranslateModule.forRoot(), | ||
], | ||
providers: [ | ||
{ provide: VocabularyService, useValue: vocabularyService }, | ||
{ provide: ItemDataService, useValue: itemService }, | ||
{ provide: RegistryService, useValue: registryService }, | ||
{ provide: NotificationsService, useValue: notificationsService }, | ||
], | ||
}).compileComponents(); | ||
|
||
fixture = TestBed.createComponent(DsoEditMetadataAuthorityFieldComponent); | ||
component = fixture.componentInstance; | ||
component.mdValue = editMetadataValue; | ||
component.dso = dso; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
describe('when the metadata field uses a scrollable vocabulary and is editing', () => { | ||
beforeEach(waitForAsync(() => { | ||
spyOn(vocabularyService, 'getVocabularyByMetadataAndCollection').and.returnValue(createSuccessfulRemoteDataObject$(mockVocabularyScrollable)); | ||
metadataValue = Object.assign(new MetadataValue(), { | ||
value: 'Authority Controlled value', | ||
language: 'en', | ||
place: 0, | ||
authority: null, | ||
}); | ||
editMetadataValue = new DsoEditMetadataValue(metadataValue); | ||
editMetadataValue.editing = true; | ||
component.mdValue = editMetadataValue; | ||
component.mdField = 'metadata.scrollable'; | ||
component.ngOnInit(); | ||
fixture.detectChanges(); | ||
})); | ||
|
||
it('should render the DsDynamicScrollableDropdownComponent', () => { | ||
expect(vocabularyService.getVocabularyByMetadataAndCollection).toHaveBeenCalled(); | ||
expect(fixture.debugElement.query(By.css('ds-dynamic-scrollable-dropdown'))).toBeTruthy(); | ||
}); | ||
|
||
it('getModel should return a DynamicScrollableDropdownModel', () => { | ||
const model = component.getModel(); | ||
|
||
expect(model instanceof DynamicScrollableDropdownModel).toBe(true); | ||
expect(model.vocabularyOptions.name).toBe(mockVocabularyScrollable.name); | ||
}); | ||
}); | ||
|
||
describe('when the metadata field uses a hierarchical vocabulary and is editing', () => { | ||
beforeEach(waitForAsync(() => { | ||
spyOn(vocabularyService, 'getVocabularyByMetadataAndCollection').and.returnValue(createSuccessfulRemoteDataObject$(mockVocabularyHierarchical)); | ||
metadataValue = Object.assign(new MetadataValue(), { | ||
value: 'Authority Controlled value', | ||
language: 'en', | ||
place: 0, | ||
authority: null, | ||
}); | ||
editMetadataValue = new DsoEditMetadataValue(metadataValue); | ||
editMetadataValue.editing = true; | ||
component.mdValue = editMetadataValue; | ||
component.mdField = 'metadata.hierarchical'; | ||
component.ngOnInit(); | ||
fixture.detectChanges(); | ||
})); | ||
|
||
it('should render the DsDynamicOneboxComponent', () => { | ||
expect(vocabularyService.getVocabularyByMetadataAndCollection).toHaveBeenCalled(); | ||
expect(fixture.debugElement.query(By.css('ds-dynamic-onebox'))).toBeTruthy(); | ||
}); | ||
|
||
it('getModel should return a DynamicOneboxModel', () => { | ||
const model = component.getModel(); | ||
|
||
expect(model instanceof DynamicOneboxModel).toBe(true); | ||
expect(model.vocabularyOptions.name).toBe(mockVocabularyHierarchical.name); | ||
}); | ||
}); | ||
|
||
describe('when the metadata field uses a suggester vocabulary and is editing', () => { | ||
beforeEach(waitForAsync(() => { | ||
spyOn(vocabularyService, 'getVocabularyByMetadataAndCollection').and.returnValue(createSuccessfulRemoteDataObject$(mockVocabularySuggester)); | ||
spyOn(component.confirm, 'emit'); | ||
metadataValue = Object.assign(new MetadataValue(), { | ||
value: 'Authority Controlled value', | ||
language: 'en', | ||
place: 0, | ||
authority: 'authority-key', | ||
confidence: ConfidenceType.CF_UNCERTAIN, | ||
}); | ||
editMetadataValue = new DsoEditMetadataValue(metadataValue); | ||
editMetadataValue.editing = true; | ||
component.mdValue = editMetadataValue; | ||
component.mdField = 'metadata.suggester'; | ||
component.ngOnInit(); | ||
fixture.detectChanges(); | ||
})); | ||
|
||
it('should render the DsDynamicOneboxComponent', () => { | ||
expect(vocabularyService.getVocabularyByMetadataAndCollection).toHaveBeenCalled(); | ||
expect(fixture.debugElement.query(By.css('ds-dynamic-onebox'))).toBeTruthy(); | ||
}); | ||
|
||
it('getModel should return a DynamicOneboxModel', () => { | ||
const model = component.getModel(); | ||
|
||
expect(model instanceof DynamicOneboxModel).toBe(true); | ||
expect(model.vocabularyOptions.name).toBe(mockVocabularySuggester.name); | ||
}); | ||
|
||
describe('authority key edition', () => { | ||
|
||
it('should update confidence to CF_NOVALUE when authority is cleared', () => { | ||
component.mdValue.newValue.authority = ''; | ||
|
||
component.onChangeAuthorityKey(); | ||
|
||
expect(component.mdValue.newValue.confidence).toBe(ConfidenceType.CF_NOVALUE); | ||
expect(component.confirm.emit).toHaveBeenCalledWith(false); | ||
}); | ||
|
||
it('should update confidence to CF_ACCEPTED when authority key is edited', () => { | ||
component.mdValue.newValue.authority = 'newAuthority'; | ||
component.mdValue.originalValue.authority = 'oldAuthority'; | ||
|
||
component.onChangeAuthorityKey(); | ||
|
||
expect(component.mdValue.newValue.confidence).toBe(ConfidenceType.CF_ACCEPTED); | ||
expect(component.confirm.emit).toHaveBeenCalledWith(false); | ||
}); | ||
|
||
it('should not update confidence when authority key remains the same', () => { | ||
component.mdValue.newValue.authority = 'sameAuthority'; | ||
component.mdValue.originalValue.authority = 'sameAuthority'; | ||
|
||
component.onChangeAuthorityKey(); | ||
|
||
expect(component.mdValue.newValue.confidence).toBe(ConfidenceType.CF_UNCERTAIN); | ||
expect(component.confirm.emit).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should call onChangeEditingAuthorityStatus with true when clicking the lock button', () => { | ||
spyOn(component, 'onChangeEditingAuthorityStatus'); | ||
const lockButton = fixture.nativeElement.querySelector('#metadata-confirm-btn'); | ||
|
||
lockButton.click(); | ||
|
||
expect(component.onChangeEditingAuthorityStatus).toHaveBeenCalledWith(true); | ||
}); | ||
|
||
it('should disable the input when editingAuthority is false', (done) => { | ||
component.editingAuthority = false; | ||
|
||
fixture.detectChanges(); | ||
|
||
fixture.detectChanges(); | ||
fixture.whenStable().then(() => { | ||
const inputElement = fixture.nativeElement.querySelector('input[data-test="authority-input"]'); | ||
expect(inputElement.disabled).toBeTruthy(); | ||
done(); | ||
}); | ||
}); | ||
|
||
it('should enable the input when editingAuthority is true', (done) => { | ||
component.editingAuthority = true; | ||
|
||
fixture.detectChanges(); | ||
fixture.whenStable().then(() => { | ||
const inputElement = fixture.nativeElement.querySelector('input[data-test="authority-input"]'); | ||
expect(inputElement.disabled).toBeFalsy(); | ||
done(); | ||
}); | ||
|
||
|
||
}); | ||
|
||
it('should update mdValue.newValue properties when authority is present', () => { | ||
const event = { | ||
value: 'Some value', | ||
authority: 'Some authority', | ||
}; | ||
|
||
component.onChangeAuthorityField(event); | ||
|
||
expect(component.mdValue.newValue.value).toBe(event.value); | ||
expect(component.mdValue.newValue.authority).toBe(event.authority); | ||
expect(component.mdValue.newValue.confidence).toBe(ConfidenceType.CF_ACCEPTED); | ||
expect(component.confirm.emit).toHaveBeenCalledWith(false); | ||
}); | ||
|
||
it('should update mdValue.newValue properties when authority is not present', () => { | ||
const event = { | ||
value: 'Some value', | ||
authority: null, | ||
}; | ||
|
||
component.onChangeAuthorityField(event); | ||
|
||
expect(component.mdValue.newValue.value).toBe(event.value); | ||
expect(component.mdValue.newValue.authority).toBeNull(); | ||
expect(component.mdValue.newValue.confidence).toBe(ConfidenceType.CF_UNSET); | ||
expect(component.confirm.emit).toHaveBeenCalledWith(false); | ||
}); | ||
|
||
}); | ||
|
||
}); | ||
}); |
Oops, something went wrong.