diff --git a/src/app/+item-page/bitstreams/upload/upload-bitstream.component.spec.ts b/src/app/+item-page/bitstreams/upload/upload-bitstream.component.spec.ts index 59868a69c7..c7aa267dc5 100644 --- a/src/app/+item-page/bitstreams/upload/upload-bitstream.component.spec.ts +++ b/src/app/+item-page/bitstreams/upload/upload-bitstream.component.spec.ts @@ -12,16 +12,27 @@ import { AuthService } from '../../../core/auth/auth.service'; import { AuthServiceStub } from '../../../shared/testing/auth-service-stub'; import { Item } from '../../../core/shared/item.model'; import { of as observableOf } from 'rxjs'; -import { createSuccessfulRemoteDataObject } from '../../../shared/testing/utils'; +import { + createPaginatedList, + createSuccessfulRemoteDataObject, + createSuccessfulRemoteDataObject$ +} from '../../../shared/testing/utils'; import { RouterStub } from '../../../shared/testing/router-stub'; import { NotificationsServiceStub } from '../../../shared/testing/notifications-service-stub'; import { VarDirective } from '../../../shared/utils/var.directive'; import { Bitstream } from '../../../core/shared/bitstream.model'; +import { BundleDataService } from '../../../core/data/bundle-data.service'; +import { Bundle } from '../../../core/shared/bundle.model'; describe('UploadBistreamComponent', () => { let comp: UploadBitstreamComponent; let fixture: ComponentFixture; + const bundle = Object.assign(new Bundle(), { + id: 'bundle', + uuid: 'bundle', + self: 'bundle-selflink' + }); const itemName = 'fake-name'; const mockItem = Object.assign(new Item(), { id: 'fake-id', @@ -33,7 +44,8 @@ describe('UploadBistreamComponent', () => { value: itemName } ] - } + }, + bundles: createSuccessfulRemoteDataObject$(createPaginatedList([bundle])) }); let routeStub; const routerStub = new RouterStub(); @@ -41,6 +53,9 @@ describe('UploadBistreamComponent', () => { const mockItemDataService = jasmine.createSpyObj('mockItemDataService', { getBitstreamsEndpoint: observableOf(restEndpoint) }); + const bundleService = jasmine.createSpyObj('bundleService', { + getBitstreamsEndpoint: observableOf(restEndpoint) + }); const authToken = 'fake-auth-token'; const authServiceStub = Object.assign(new AuthServiceStub(), { buildAuthHeader: () => authToken @@ -90,7 +105,10 @@ describe('UploadBistreamComponent', () => { data: observableOf({ item: createSuccessfulRemoteDataObject(mockItem) }), - queryParams: observableOf(queryParams) + queryParams: observableOf(queryParams), + snapshot: { + queryParams: queryParams + } }; TestBed.configureTestingModule({ @@ -102,6 +120,7 @@ describe('UploadBistreamComponent', () => { { provide: ItemDataService, useValue: mockItemDataService }, { provide: NotificationsService, useValue: notificationsServiceStub }, { provide: AuthService, useValue: authServiceStub }, + { provide: BundleDataService, useValue: bundleService } ], schemas: [ NO_ERRORS_SCHEMA ] diff --git a/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.spec.ts b/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.spec.ts index 8039f5d7cd..d104959bdc 100644 --- a/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-bitstreams/item-bitstreams.component.spec.ts @@ -24,6 +24,8 @@ import { RequestService } from '../../../core/data/request.service'; import { SearchConfigurationService } from '../../../+search-page/search-service/search-configuration.service'; import { ObjectValuesPipe } from '../../../shared/utils/object-values-pipe'; import { VarDirective } from '../../../shared/utils/var.directive'; +import { BundleDataService } from '../../../core/data/bundle-data.service'; +import { Bundle } from '../../../core/shared/bundle.model'; let comp: ItemBitstreamsComponent; let fixture: ComponentFixture; @@ -45,6 +47,19 @@ const fieldUpdate2 = { field: bitstream2, changeType: FieldChangeType.REMOVE }; +const bundle = Object.assign(new Bundle(), { + id: 'bundle1', + uuid: 'bundle1', + self: 'bundle1-selflink', + bitstreams: createMockRDPaginatedObs([bitstream1, bitstream2]) +}); +const moveOperations = [ + { + op: 'move', + from: '/0', + path: '/1' + } +]; const date = new Date(); const url = 'thisUrl'; let item: Item; @@ -57,6 +72,7 @@ let bitstreamService: BitstreamDataService; let objectCache: ObjectCacheService; let requestService: RequestService; let searchConfig: SearchConfigurationService; +let bundleService: BundleDataService; describe('ItemBitstreamsComponent', () => { beforeEach(async(() => { @@ -72,13 +88,15 @@ describe('ItemBitstreamsComponent', () => { }), saveAddFieldUpdate: {}, discardFieldUpdates: {}, + discardAllFieldUpdates: {}, reinstateFieldUpdates: observableOf(true), initialize: {}, getUpdatedFields: observableOf([bitstream1, bitstream2]), getLastModified: observableOf(date), hasUpdates: observableOf(true), isReinstatable: observableOf(false), - isValidPage: observableOf(true) + isValidPage: observableOf(true), + getMoveOperations: observableOf(moveOperations) } ); router = Object.assign(new RouterStub(), { @@ -105,12 +123,13 @@ describe('ItemBitstreamsComponent', () => { item = Object.assign(new Item(), { uuid: 'item', id: 'item', - bitstreams: createMockRDPaginatedObs([bitstream1, bitstream2]), + bundles: createMockRDPaginatedObs([bundle]), lastModified: date }); itemService = Object.assign( { getBitstreams: () => createMockRDPaginatedObs([bitstream1, bitstream2]), - findById: () => createMockRDObs(item) + findById: () => createMockRDObs(item), + getBundles: () => createMockRDPaginatedObs([bundle]) }); route = Object.assign({ parent: { @@ -118,6 +137,9 @@ describe('ItemBitstreamsComponent', () => { }, url: url }); + bundleService = jasmine.createSpyObj('bundleService', { + patch: {} + }); TestBed.configureTestingModule({ imports: [TranslateModule.forRoot()], @@ -133,6 +155,7 @@ describe('ItemBitstreamsComponent', () => { { provide: ObjectCacheService, useValue: objectCache }, { provide: RequestService, useValue: requestService }, { provide: SearchConfigurationService, useValue: searchConfig }, + { provide: BundleDataService, useValue: bundleService }, ChangeDetectorRef ], schemas: [ NO_ERRORS_SCHEMA @@ -159,6 +182,24 @@ describe('ItemBitstreamsComponent', () => { it('should not call deleteAndReturnResponse on the bitstreamService for the unmarked field', () => { expect(bitstreamService.deleteAndReturnResponse).not.toHaveBeenCalledWith(bitstream1); }); + + it('should send out a patch for the move operations', () => { + expect(bundleService.patch).toHaveBeenCalled(); + }); + }); + + describe('discard', () => { + it('should discard ALL field updates', () => { + comp.discard(); + expect(objectUpdatesService.discardAllFieldUpdates).toHaveBeenCalled(); + }); + }); + + describe('reinstate', () => { + it('should reinstate field updates on the bundle', () => { + comp.reinstate(); + expect(objectUpdatesService.reinstateFieldUpdates).toHaveBeenCalledWith(bundle.self); + }); }); }); diff --git a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.spec.ts b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.spec.ts new file mode 100644 index 0000000000..62f8b0abe3 --- /dev/null +++ b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream-bundle/item-edit-bitstream-bundle.component.spec.ts @@ -0,0 +1,143 @@ +import { ItemEditBitstreamBundleComponent } from './item-edit-bitstream-bundle.component'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { of as observableOf } from 'rxjs/internal/observable/of'; +import { TranslateModule } from '@ngx-translate/core'; +import { VarDirective } from '../../../../shared/utils/var.directive'; +import { ObjectUpdatesService } from '../../../../core/data/object-updates/object-updates.service'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { Bitstream } from '../../../../core/shared/bitstream.model'; +import { createMockRDObs } from '../item-bitstreams.component.spec'; +import { BitstreamFormat } from '../../../../core/shared/bitstream-format.model'; +import { BundleDataService } from '../../../../core/data/bundle-data.service'; +import { createPaginatedList, createSuccessfulRemoteDataObject$ } from '../../../../shared/testing/utils'; +import { ObjectValuesPipe } from '../../../../shared/utils/object-values-pipe'; +import { Item } from '../../../../core/shared/item.model'; +import { Bundle } from '../../../../core/shared/bundle.model'; + +describe('ItemEditBitstreamBundleComponent', () => { + let comp: ItemEditBitstreamBundleComponent; + let fixture: ComponentFixture; + + let objectUpdatesService: ObjectUpdatesService; + let bundleService: BundleDataService; + + const item = Object.assign(new Item(), { + id: 'item-1', + uuid: 'item-1' + }); + const bundle = Object.assign(new Bundle(), { + id: 'bundle-1', + uuid: 'bundle-1', + self: 'bundle-1-selflink' + }); + const date = new Date(); + const format = Object.assign(new BitstreamFormat(), { + shortDescription: 'PDF' + }); + const bitstream1 = Object.assign(new Bitstream(), { + uuid: 'bitstreamUUID1', + name: 'Fake Bitstream 1', + bundleName: 'ORIGINAL', + description: 'Description', + format: createMockRDObs(format) + }); + const fieldUpdate1 = { + field: bitstream1, + changeType: undefined + }; + const bitstream2 = Object.assign(new Bitstream(), { + uuid: 'bitstreamUUID2', + name: 'Fake Bitstream 2', + bundleName: 'ORIGINAL', + description: 'Description', + format: createMockRDObs(format) + }); + const fieldUpdate2 = { + field: bitstream2, + changeType: undefined + }; + const batchSize = 10; + + beforeEach(async(() => { + objectUpdatesService = jasmine.createSpyObj('objectUpdatesService', + { + getFieldUpdates: observableOf({ + [bitstream1.uuid]: fieldUpdate1, + [bitstream2.uuid]: fieldUpdate2, + }), + getFieldUpdatesExclusive: observableOf({ + [bitstream1.uuid]: fieldUpdate1, + [bitstream2.uuid]: fieldUpdate2, + }), + getFieldUpdatesByCustomOrder: observableOf({ + [bitstream1.uuid]: fieldUpdate1, + [bitstream2.uuid]: fieldUpdate2, + }), + saveMoveFieldUpdate: {}, + saveRemoveFieldUpdate: {}, + removeSingleFieldUpdate: {}, + saveAddFieldUpdate: {}, + discardFieldUpdates: {}, + reinstateFieldUpdates: observableOf(true), + initialize: {}, + getUpdatedFields: observableOf([bitstream1, bitstream2]), + getLastModified: observableOf(date), + hasUpdates: observableOf(true), + isReinstatable: observableOf(false), + isValidPage: observableOf(true) + } + ); + + bundleService = jasmine.createSpyObj('bundleService', { + getBitstreams: createSuccessfulRemoteDataObject$(createPaginatedList([bitstream1, bitstream2])) + }); + + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [ItemEditBitstreamBundleComponent, VarDirective, ObjectValuesPipe], + providers: [ + { provide: ObjectUpdatesService, useValue: objectUpdatesService }, + { provide: BundleDataService, useValue: bundleService } + ], schemas: [ + NO_ERRORS_SCHEMA + ] + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ItemEditBitstreamBundleComponent); + comp = fixture.componentInstance; + comp.item = item; + comp.bundle = bundle; + comp.batchSize = batchSize; + fixture.detectChanges(); + }); + + describe('A drag-and-drop event', () => { + it('should send a move update to the objectUpdatesService', () => { + const event = { + previousIndex: 0, + currentIndex: 1 + }; + comp.drop(event as any); + expect(objectUpdatesService.saveMoveFieldUpdate).toHaveBeenCalledWith(bundle.self, event.previousIndex, event.currentIndex); + }); + }); + + describe('loadMore', () => { + it('should increase the current size by ' + batchSize, () => { + const initialSize = comp.currentSize$.value; + comp.loadMore(); + const newSize = comp.currentSize$.value; + expect(initialSize + batchSize).toEqual(newSize); + }); + }); + + describe('loadAll', () => { + it('should increase the current size by a lot', () => { + comp.loadAll(); + const newSize = comp.currentSize$.value; + expect(newSize).toBeGreaterThanOrEqual(999); + }); + }); +}); diff --git a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.spec.ts b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.spec.ts index dd85931664..175c78428e 100644 --- a/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.spec.ts +++ b/src/app/+item-page/edit-item-page/item-bitstreams/item-edit-bitstream/item-edit-bitstream.component.spec.ts @@ -5,10 +5,9 @@ import { of as observableOf } from 'rxjs/internal/observable/of'; import { Bitstream } from '../../../../core/shared/bitstream.model'; import { TranslateModule } from '@ngx-translate/core'; import { VarDirective } from '../../../../shared/utils/var.directive'; -import { DebugElement, NO_ERRORS_SCHEMA } from '@angular/core'; +import { NO_ERRORS_SCHEMA } from '@angular/core'; import { createMockRDObs } from '../item-bitstreams.component.spec'; import { BitstreamFormat } from '../../../../core/shared/bitstream-format.model'; -import { By } from '@angular/platform-browser'; let comp: ItemEditBitstreamComponent; let fixture: ComponentFixture; @@ -33,8 +32,6 @@ const url = 'thisUrl'; let objectUpdatesService: ObjectUpdatesService; describe('ItemEditBitstreamComponent', () => { - let tdElements: DebugElement[]; - beforeEach(async(() => { objectUpdatesService = jasmine.createSpyObj('objectUpdatesService', { @@ -44,6 +41,8 @@ describe('ItemEditBitstreamComponent', () => { getFieldUpdatesExclusive: observableOf({ [bitstream.uuid]: fieldUpdate, }), + saveRemoveFieldUpdate: {}, + removeSingleFieldUpdate: {}, saveAddFieldUpdate: {}, discardFieldUpdates: {}, reinstateFieldUpdates: observableOf(true), @@ -71,26 +70,40 @@ describe('ItemEditBitstreamComponent', () => { fixture = TestBed.createComponent(ItemEditBitstreamComponent); comp = fixture.componentInstance; comp.fieldUpdate = fieldUpdate; - comp.url = url; + comp.bundleUrl = url; comp.ngOnChanges(undefined); fixture.detectChanges(); - - tdElements = fixture.debugElement.queryAll(By.css('td')); }); - it('should display the bitstream\'s name in the first table cell', () => { - expect(tdElements[0].nativeElement.textContent.trim()).toEqual(bitstream.name); + describe('when remove is called', () => { + beforeEach(() => { + comp.remove(); + }); + + it('should call saveRemoveFieldUpdate on objectUpdatesService', () => { + expect(objectUpdatesService.saveRemoveFieldUpdate).toHaveBeenCalledWith(url, bitstream); + }); }); - it('should display the bitstream\'s bundle in the second table cell', () => { - expect(tdElements[1].nativeElement.textContent.trim()).toEqual(bitstream.bundleName); + describe('when undo is called', () => { + beforeEach(() => { + comp.undo(); + }); + + it('should call removeSingleFieldUpdate on objectUpdatesService', () => { + expect(objectUpdatesService.removeSingleFieldUpdate).toHaveBeenCalledWith(url, bitstream.uuid); + }); }); - it('should display the bitstream\'s description in the third table cell', () => { - expect(tdElements[2].nativeElement.textContent.trim()).toEqual(bitstream.description); + describe('when canRemove is called', () => { + it('should return true', () => { + expect(comp.canRemove()).toEqual(true) + }); }); - it('should display the bitstream\'s format in the fourth table cell', () => { - expect(tdElements[3].nativeElement.textContent.trim()).toEqual(format.shortDescription); + describe('when canUndo is called', () => { + it('should return false', () => { + expect(comp.canUndo()).toEqual(false) + }); }); }); diff --git a/src/app/core/data/object-updates/object-updates.reducer.spec.ts b/src/app/core/data/object-updates/object-updates.reducer.spec.ts index d9aacc8d34..1fe3b97070 100644 --- a/src/app/core/data/object-updates/object-updates.reducer.spec.ts +++ b/src/app/core/data/object-updates/object-updates.reducer.spec.ts @@ -3,8 +3,8 @@ import { AddFieldUpdateAction, DiscardObjectUpdatesAction, FieldChangeType, - InitializeFieldsAction, - ReinstateObjectUpdatesAction, + InitializeFieldsAction, MoveFieldUpdateAction, + ReinstateObjectUpdatesAction, RemoveAllObjectUpdatesAction, RemoveFieldUpdateAction, RemoveObjectUpdatesAction, SetEditableFieldUpdateAction, SetValidFieldUpdateAction } from './object-updates.actions'; @@ -81,8 +81,8 @@ describe('objectUpdatesReducer', () => { }, lastModified: modDate, customOrder: { - initialOrder: [], - newOrder: [], + initialOrder: [identifiable1.uuid, identifiable2.uuid, identifiable3.uuid], + newOrder: [identifiable1.uuid, identifiable2.uuid, identifiable3.uuid], changed: false } } @@ -107,7 +107,12 @@ describe('objectUpdatesReducer', () => { isValid: true }, }, - lastModified: modDate + lastModified: modDate, + customOrder: { + initialOrder: [identifiable1.uuid, identifiable2.uuid, identifiable3.uuid], + newOrder: [identifiable1.uuid, identifiable2.uuid, identifiable3.uuid], + changed: false + } }, [url + OBJECT_UPDATES_TRASH_PATH]: { fieldStates: { @@ -138,7 +143,12 @@ describe('objectUpdatesReducer', () => { changeType: FieldChangeType.ADD } }, - lastModified: modDate + lastModified: modDate, + customOrder: { + initialOrder: [identifiable1.uuid, identifiable2.uuid, identifiable3.uuid], + newOrder: [identifiable1.uuid, identifiable2.uuid, identifiable3.uuid], + changed: false + } } }; @@ -275,10 +285,27 @@ describe('objectUpdatesReducer', () => { expect(newState[url + OBJECT_UPDATES_TRASH_PATH]).toBeUndefined(); }); + it('should remove all updates from the state when the REMOVE_ALL action is dispatched', () => { + const action = new RemoveAllObjectUpdatesAction(); + + const newState = objectUpdatesReducer(discardedTestState, action as any); + expect(newState[url].fieldUpdates).toBeUndefined(); + expect(newState[url + OBJECT_UPDATES_TRASH_PATH]).toBeUndefined(); + }); + it('should remove a given field\'s update from the state when the REMOVE_FIELD action is dispatched, based on the payload', () => { const action = new RemoveFieldUpdateAction(url, uuid); const newState = objectUpdatesReducer(testState, action); expect(newState[url].fieldUpdates[uuid]).toBeUndefined(); }); + + it('should move the custom order from the state when the MOVE action is dispatched', () => { + const action = new MoveFieldUpdateAction(url, 0, 1); + + const newState = objectUpdatesReducer(testState, action); + expect(newState[url].customOrder.newOrder[0]).toEqual(testState[url].customOrder.newOrder[1]); + expect(newState[url].customOrder.newOrder[1]).toEqual(testState[url].customOrder.newOrder[0]); + expect(newState[url].customOrder.changed).toEqual(true); + }); });