Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Item edit page metadatafields suggestion & validation #860

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -123,7 +123,7 @@ export class AbstractItemUpdateComponent extends AbstractTrackableComponent impl
/**
* Check if the current page is entirely valid
*/
protected isValid() {
public isValid() {
return this.objectUpdatesService.isValidPage(this.url);
}

Expand Down
Expand Up @@ -5,14 +5,15 @@
</div>
<div *ngIf="(editable | async)" class="field-container">
<ds-filter-input-suggestions [suggestions]="(metadataFieldSuggestions | async)"
[(ngModel)]="metadata.key"
[(ngModel)]="metadata.key"
[url]="this.url"
[metadata]="this.metadata"
(submitSuggestion)="update(suggestionControl)"
(clickSuggestion)="update(suggestionControl)"
(typeSuggestion)="update(suggestionControl)"
(dsClickOutside)="checkValidity(suggestionControl)"
(findSuggestions)="findMetadataFieldSuggestions($event)"
#suggestionControl="ngModel"
[dsInListValidator]="metadataFields"
[valid]="(valid | async) !== false"
dsAutoFocus autoFocusSelector=".suggestion_input"
[ngModelOptions]="{standalone: true}"
Expand Down Expand Up @@ -46,12 +47,12 @@
</td>
<td class="text-center">
<div class="btn-group edit-field">
<button [disabled]="!(canSetEditable() | async)" *ngIf="!(editable | async)"
<button [disabled]="!(canSetEditable() | async)" *ngIf="!(editable | async)"
(click)="setEditable(true)" class="btn btn-outline-primary btn-sm"
title="{{'item.edit.metadata.edit.buttons.edit' | translate}}">
<i class="fas fa-edit fa-fw"></i>
</button>
<button [disabled]="!(canSetUneditable() | async)" *ngIf="(editable | async)"
<button [disabled]="!(canSetUneditable() | async) || (valid | async) === false" *ngIf="(editable | async)"
(click)="setEditable(false)" class="btn btn-outline-success btn-sm"
title="{{'item.edit.metadata.edit.buttons.unedit' | translate}}">
<i class="fas fa-check fa-fw"></i>
Expand Down
@@ -1,11 +1,12 @@
import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { TranslateModule } from '@ngx-translate/core';
import { getTestScheduler } from 'jasmine-marbles';
import { of as observableOf } from 'rxjs';
import { TestScheduler } from 'rxjs/testing';
import { MetadataFieldDataService } from '../../../../core/data/metadata-field-data.service';
import { FieldChangeType } from '../../../../core/data/object-updates/object-updates.actions';
import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service';
import { PaginatedList } from '../../../../core/data/paginated-list';
Expand All @@ -14,9 +15,14 @@ import { MetadataSchema } from '../../../../core/metadata/metadata-schema.model'
import { RegistryService } from '../../../../core/registry/registry.service';
import { MetadatumViewModel } from '../../../../core/shared/metadata.models';
import { InputSuggestion } from '../../../../shared/input-suggestions/input-suggestions.model';
import { SharedModule } from '../../../../shared/shared.module';
import { createSuccessfulRemoteDataObject$ } from '../../../../shared/remote-data.utils';
import {
createSuccessfulRemoteDataObject$
} from '../../../../shared/remote-data.utils';
import { followLink } from '../../../../shared/utils/follow-link-config.model';
import { EditInPlaceFieldComponent } from './edit-in-place-field.component';
import { FilterInputSuggestionsComponent } from '../../../../shared/input-suggestions/filter-suggestions/filter-input-suggestions.component';
import { MockComponent, MockDirective } from 'ng-mocks';
import { DebounceDirective } from '../../../../shared/utils/debounce.directive';

let comp: EditInPlaceFieldComponent;
let fixture: ComponentFixture<EditInPlaceFieldComponent>;
Expand All @@ -25,17 +31,21 @@ let el: HTMLElement;
let metadataFieldService;
let objectUpdatesService;
let paginatedMetadataFields;
const mdSchema = Object.assign(new MetadataSchema(), { prefix: 'dc' })
const mdSchema = Object.assign(new MetadataSchema(), { prefix: 'dc' });
const mdSchemaRD$ = createSuccessfulRemoteDataObject$(mdSchema);
const mdField1 = Object.assign(new MetadataField(), {
schema: mdSchema,
schema: mdSchemaRD$,
element: 'contributor',
qualifier: 'author'
});
const mdField2 = Object.assign(new MetadataField(), { schema: mdSchema, element: 'title' });
const mdField2 = Object.assign(new MetadataField(), {
schema: mdSchemaRD$,
element: 'title'
});
const mdField3 = Object.assign(new MetadataField(), {
schema: mdSchema,
schema: mdSchemaRD$,
element: 'description',
qualifier: 'abstract'
qualifier: 'abstract',
});

const metadatum = Object.assign(new MetadatumViewModel(), {
Expand Down Expand Up @@ -74,11 +84,16 @@ describe('EditInPlaceFieldComponent', () => {
);

TestBed.configureTestingModule({
imports: [FormsModule, SharedModule, TranslateModule.forRoot()],
declarations: [EditInPlaceFieldComponent],
imports: [FormsModule, TranslateModule.forRoot()],
declarations: [
EditInPlaceFieldComponent,
MockDirective(DebounceDirective),
MockComponent(FilterInputSuggestionsComponent)
],
providers: [
{ provide: RegistryService, useValue: metadataFieldService },
{ provide: ObjectUpdatesService, useValue: objectUpdatesService },
{ provide: MetadataFieldDataService, useValue: {} }
], schemas: [
CUSTOM_ELEMENTS_SCHEMA
]
Expand All @@ -94,13 +109,12 @@ describe('EditInPlaceFieldComponent', () => {
comp.url = url;
comp.fieldUpdate = fieldUpdate;
comp.metadata = metadatum;

fixture.detectChanges();
});

describe('update', () => {
beforeEach(() => {
comp.update();
fixture.detectChanges();
});

it('it should call saveChangeFieldUpdate on the objectUpdatesService with the correct url and metadata', () => {
Expand All @@ -112,6 +126,7 @@ describe('EditInPlaceFieldComponent', () => {
const editable = false;
beforeEach(() => {
comp.setEditable(editable);
fixture.detectChanges();
});

it('it should call setEditableFieldUpdate on the objectUpdatesService with the correct url and uuid and false', () => {
Expand All @@ -121,7 +136,7 @@ describe('EditInPlaceFieldComponent', () => {

describe('editable is true', () => {
beforeEach(() => {
comp.editable = observableOf(true);
objectUpdatesService.isEditable.and.returnValue(observableOf(true));
fixture.detectChanges();
});
it('the div should contain input fields or textareas', () => {
Expand All @@ -133,7 +148,7 @@ describe('EditInPlaceFieldComponent', () => {

describe('editable is false', () => {
beforeEach(() => {
comp.editable = observableOf(false);
objectUpdatesService.isEditable.and.returnValue(observableOf(false));
fixture.detectChanges();
});
it('the div should contain no input fields or textareas', () => {
Expand All @@ -145,7 +160,7 @@ describe('EditInPlaceFieldComponent', () => {

describe('isValid is true', () => {
beforeEach(() => {
comp.valid = observableOf(true);
objectUpdatesService.isValid.and.returnValue(observableOf(true));
fixture.detectChanges();
});
it('the div should not contain an error message', () => {
Expand All @@ -157,10 +172,10 @@ describe('EditInPlaceFieldComponent', () => {

describe('isValid is false', () => {
beforeEach(() => {
comp.valid = observableOf(false);
objectUpdatesService.isValid.and.returnValue(observableOf(false));
fixture.detectChanges();
});
it('the div should contain no input fields or textareas', () => {
it('there should be an error message', () => {
const errorMessages = de.queryAll(By.css('small.text-danger'));
expect(errorMessages.length).toBeGreaterThan(0);

Expand All @@ -170,6 +185,7 @@ describe('EditInPlaceFieldComponent', () => {
describe('remove', () => {
beforeEach(() => {
comp.remove();
fixture.detectChanges();
});

it('it should call saveRemoveFieldUpdate on the objectUpdatesService with the correct url and metadata', () => {
Expand All @@ -180,6 +196,7 @@ describe('EditInPlaceFieldComponent', () => {
describe('removeChangesFromField', () => {
beforeEach(() => {
comp.removeChangesFromField();
fixture.detectChanges();
});

it('it should call removeChangesFromField on the objectUpdatesService with the correct url and uuid', () => {
Expand All @@ -192,19 +209,19 @@ describe('EditInPlaceFieldComponent', () => {

const metadataFieldSuggestions: InputSuggestion[] =
[
{ displayValue: mdField1.toString().split('.').join('.&#8203;'), value: mdField1.toString() },
{ displayValue: mdField2.toString().split('.').join('.&#8203;'), value: mdField2.toString() },
{ displayValue: mdField3.toString().split('.').join('.&#8203;'), value: mdField3.toString() }
{ displayValue: ('dc.' + mdField1.toString()).split('.').join('.&#8203;'), value: ('dc.' + mdField1.toString()) },
{ displayValue: ('dc.' + mdField2.toString()).split('.').join('.&#8203;'), value: ('dc.' + mdField2.toString()) },
{ displayValue: ('dc.' + mdField3.toString()).split('.').join('.&#8203;'), value: ('dc.' + mdField3.toString()) }
];

beforeEach(() => {
beforeEach(fakeAsync(() => {
comp.findMetadataFieldSuggestions(query);

});
tick();
fixture.detectChanges();
}));

it('it should call queryMetadataFields on the metadataFieldService with the correct query', () => {

expect(metadataFieldService.queryMetadataFields).toHaveBeenCalledWith(query);
expect(metadataFieldService.queryMetadataFields).toHaveBeenCalledWith(query, null, followLink('schema'));
});

it('it should set metadataFieldSuggestions to the right value', () => {
Expand All @@ -216,7 +233,8 @@ describe('EditInPlaceFieldComponent', () => {
describe('canSetEditable', () => {
describe('when editable is currently true', () => {
beforeEach(() => {
comp.editable = observableOf(true);
objectUpdatesService.isEditable.and.returnValue(observableOf(true));
fixture.detectChanges();
});

it('canSetEditable should return an observable emitting false', () => {
Expand All @@ -227,12 +245,14 @@ describe('EditInPlaceFieldComponent', () => {

describe('when editable is currently false', () => {
beforeEach(() => {
comp.editable = observableOf(false);
objectUpdatesService.isEditable.and.returnValue(observableOf(false));
fixture.detectChanges();
});

describe('when the fieldUpdate\'s changeType is currently not REMOVE', () => {
beforeEach(() => {
comp.fieldUpdate.changeType = FieldChangeType.ADD;
fixture.detectChanges();
});
it('canSetEditable should return an observable emitting true', () => {
const expected = '(a|)';
Expand All @@ -243,6 +263,7 @@ describe('EditInPlaceFieldComponent', () => {
describe('when the fieldUpdate\'s changeType is currently REMOVE', () => {
beforeEach(() => {
comp.fieldUpdate.changeType = FieldChangeType.REMOVE;
fixture.detectChanges();
});
it('canSetEditable should return an observable emitting false', () => {
const expected = '(a|)';
Expand All @@ -255,7 +276,8 @@ describe('EditInPlaceFieldComponent', () => {
describe('canSetUneditable', () => {
describe('when editable is currently true', () => {
beforeEach(() => {
comp.editable = observableOf(true);
objectUpdatesService.isEditable.and.returnValue(observableOf(true));
fixture.detectChanges();
});

it('canSetUneditable should return an observable emitting true', () => {
Expand All @@ -266,7 +288,8 @@ describe('EditInPlaceFieldComponent', () => {

describe('when editable is currently false', () => {
beforeEach(() => {
comp.editable = observableOf(false);
objectUpdatesService.isEditable.and.returnValue(observableOf(false));
fixture.detectChanges();
});

it('canSetUneditable should return an observable emitting false', () => {
Expand All @@ -278,7 +301,7 @@ describe('EditInPlaceFieldComponent', () => {

describe('when canSetEditable emits true', () => {
beforeEach(() => {
comp.editable = observableOf(false);
objectUpdatesService.isEditable.and.returnValue(observableOf(false));
spyOn(comp, 'canSetEditable').and.returnValue(observableOf(true));
fixture.detectChanges();
});
Expand All @@ -290,7 +313,7 @@ describe('EditInPlaceFieldComponent', () => {

describe('when canSetEditable emits false', () => {
beforeEach(() => {
comp.editable = observableOf(false);
objectUpdatesService.isEditable.and.returnValue(observableOf(false));
spyOn(comp, 'canSetEditable').and.returnValue(observableOf(false));
fixture.detectChanges();
});
Expand All @@ -302,7 +325,7 @@ describe('EditInPlaceFieldComponent', () => {

describe('when canSetUneditable emits true', () => {
beforeEach(() => {
comp.editable = observableOf(true);
objectUpdatesService.isEditable.and.returnValue(observableOf(true));
spyOn(comp, 'canSetUneditable').and.returnValue(observableOf(true));
fixture.detectChanges();
});
Expand All @@ -314,7 +337,7 @@ describe('EditInPlaceFieldComponent', () => {

describe('when canSetUneditable emits false', () => {
beforeEach(() => {
comp.editable = observableOf(true);
objectUpdatesService.isEditable.and.returnValue(observableOf(true));
spyOn(comp, 'canSetUneditable').and.returnValue(observableOf(false));
fixture.detectChanges();
});
Expand Down Expand Up @@ -372,6 +395,7 @@ describe('EditInPlaceFieldComponent', () => {
describe('when the fieldUpdate\'s changeType is currently not REMOVE or ADD', () => {
beforeEach(() => {
comp.fieldUpdate.changeType = FieldChangeType.UPDATE;
fixture.detectChanges();
});
it('canRemove should return an observable emitting true', () => {
const expected = '(a|)';
Expand All @@ -382,6 +406,7 @@ describe('EditInPlaceFieldComponent', () => {
describe('when the fieldUpdate\'s changeType is currently ADD', () => {
beforeEach(() => {
comp.fieldUpdate.changeType = FieldChangeType.ADD;
fixture.detectChanges();
});
it('canRemove should return an observable emitting false', () => {
const expected = '(a|)';
Expand All @@ -394,7 +419,7 @@ describe('EditInPlaceFieldComponent', () => {

describe('when editable is currently true', () => {
beforeEach(() => {
comp.editable = observableOf(true);
objectUpdatesService.isEditable.and.returnValue(observableOf(true));
comp.fieldUpdate.changeType = undefined;
fixture.detectChanges();
});
Expand All @@ -408,6 +433,7 @@ describe('EditInPlaceFieldComponent', () => {
describe('when the fieldUpdate\'s changeType is currently ADD, UPDATE or REMOVE', () => {
beforeEach(() => {
comp.fieldUpdate.changeType = FieldChangeType.ADD;
fixture.detectChanges();
});

it('canUndo should return an observable emitting true', () => {
Expand All @@ -419,6 +445,7 @@ describe('EditInPlaceFieldComponent', () => {
describe('when the fieldUpdate\'s changeType is currently undefined', () => {
beforeEach(() => {
comp.fieldUpdate.changeType = undefined;
fixture.detectChanges();
});

it('canUndo should return an observable emitting false', () => {
Expand Down