[CST-9636] WIP Create unit tests

This commit is contained in:
Giuseppe Digilio
2023-05-10 20:18:20 +02:00
parent e31fc562c5
commit 64c0fff370
15 changed files with 382 additions and 53 deletions

View File

@@ -17,6 +17,7 @@ import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
import { BulkAccessBrowseComponent } from './bulk-access/browse/bulk-access-browse.component';
import { BulkAccessSettingsComponent } from './bulk-access/settings/bulk-access-settings.component';
import { SearchModule } from '../shared/search/search.module';
import { AccessControlFormModule } from '../shared/access-control-form-container/access-control-form.module';
/**
* Condition for displaying error messages on email form field
@@ -27,15 +28,16 @@ export const ValidateEmailErrorStateMatcher: DynamicErrorMessagesMatcher =
};
@NgModule({
imports: [
CommonModule,
SharedModule,
RouterModule,
AccessControlRoutingModule,
FormModule,
NgbAccordionModule,
SearchModule,
],
imports: [
CommonModule,
SharedModule,
RouterModule,
AccessControlRoutingModule,
FormModule,
NgbAccordionModule,
SearchModule,
AccessControlFormModule,
],
exports: [
MembersListComponent,
],

View File

@@ -51,6 +51,3 @@
</ng-template>
</ngb-panel>
</ngb-accordion>

View File

@@ -1,25 +1,82 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { of } from 'rxjs';
import { NgbAccordionModule, NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
import { BulkAccessBrowseComponent } from './bulk-access-browse.component';
import { SelectableListService } from '../../../shared/object-list/selectable-list/selectable-list.service';
import { SelectableObject } from '../../../shared/object-list/selectable-list/selectable-list.service.spec';
import { PageInfo } from '../../../core/shared/page-info.model';
import { buildPaginatedList } from '../../../core/data/paginated-list.model';
import { createSuccessfulRemoteDataObject } from '../../../shared/remote-data.utils';
describe('BrowseComponent', () => {
describe('BulkAccessBrowseComponent', () => {
let component: BulkAccessBrowseComponent;
let fixture: ComponentFixture<BulkAccessBrowseComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ BulkAccessBrowseComponent ]
})
.compileComponents();
});
const listID1 = 'id1';
const value1 = 'Selected object';
const value2 = 'Another selected object';
const selected1 = new SelectableObject(value1);
const selected2 = new SelectableObject(value2);
const testSelection = { id: listID1, selection: [selected1, selected2] } ;
const selectableListService = jasmine.createSpyObj('SelectableListService', ['getSelectableList', 'deselectAll']);
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NgbAccordionModule,
NgbNavModule,
TranslateModule.forRoot()
],
declarations: [BulkAccessBrowseComponent],
providers: [ { provide: SelectableListService, useValue: selectableListService }, ],
schemas: [
NO_ERRORS_SCHEMA
]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(BulkAccessBrowseComponent);
component = fixture.componentInstance;
(component as any).selectableListService.getSelectableList.and.returnValue(of(testSelection));
fixture.detectChanges();
});
it('should create', () => {
afterEach(() => {
fixture.destroy();
component = null;
});
it('should create the component', () => {
expect(component).toBeTruthy();
});
it('should have an initial active nav id of "search"', () => {
expect(component.activateId).toEqual('search');
});
it('should have an initial pagination options object with default values', () => {
expect(component.paginationOptions$.getValue().id).toEqual('bas');
expect(component.paginationOptions$.getValue().pageSize).toEqual(5);
expect(component.paginationOptions$.getValue().currentPage).toEqual(1);
});
it('should have an initial remote data with a paginated list as value', () => {
const list = buildPaginatedList(new PageInfo({
"elementsPerPage": 5,
"totalElements": 2,
"totalPages": 1,
"currentPage": 1
}), [selected1, selected2]) ;
const rd = createSuccessfulRemoteDataObject(list);
expect(component.objectsSelected$.value).toEqual(rd);
});
});

View File

@@ -1,7 +1,7 @@
import { Component, Input, OnDestroy, OnInit } from '@angular/core';
import { BehaviorSubject, Subscription } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators';
import { distinctUntilChanged, map, tap } from 'rxjs/operators';
import { SEARCH_CONFIG_SERVICE } from '../../../my-dspace-page/my-dspace-page.component';
import { SearchConfigurationService } from '../../../core/shared/search/search-configuration.service';
@@ -67,7 +67,8 @@ export class BulkAccessBrowseComponent implements OnInit, OnDestroy {
this.subs.push(
this.selectableListService.getSelectableList(this.listId).pipe(
distinctUntilChanged(),
map((list: SelectableListState) => this.generatePaginatedListBySelectedElements(list))
map((list: SelectableListState) => this.generatePaginatedListBySelectedElements(list)),
tap(console.log)
).subscribe(this.objectsSelected$)
)
}
@@ -76,14 +77,12 @@ export class BulkAccessBrowseComponent implements OnInit, OnDestroy {
this.paginationOptions$.next(Object.assign(new PaginationComponentOptions(), this.paginationOptions$.value, {
currentPage: this.paginationOptions$.value.currentPage + 1
}));
console.log(this.paginationOptions$.value);
}
pagePrev() {
this.paginationOptions$.next(Object.assign(new PaginationComponentOptions(), this.paginationOptions$.value, {
currentPage: this.paginationOptions$.value.currentPage - 1
}));
console.log(this.paginationOptions$.value);
}
private calculatePageCount(pageSize, totalCount = 0) {

View File

@@ -1,8 +1,18 @@
<div class="container">
<ds-bulk-access-browse [listId]="listId"></ds-bulk-access-browse>
<div class="clearfix mb-3"></div>
<ds-bulk-access-settings [listId]="listId"></ds-bulk-access-settings>
<ds-bulk-access-settings #dsBulkSettings ></ds-bulk-access-settings>
<hr>
<div class="d-flex justify-content-end">
<button class="btn btn-outline-primary mr-3" (click)="reset()">
{{ 'access-control-reset' | translate }}
</button>
<button class="btn btn-primary" [disabled]="!canExport()" (click)="submit()">
{{ 'access-control-execute' | translate }}
</button>
</div>
</div>

View File

@@ -1,25 +1,115 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { TranslateModule } from '@ngx-translate/core';
import { of } from 'rxjs';
import { BulkAccessComponent } from './bulk-access.component';
import { BulkAccessControlService } from '../../shared/access-control-form-container/bulk-access-control.service';
import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service';
import { SelectableListState } from '../../shared/object-list/selectable-list/selectable-list.reducer';
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
import { Process } from '../../process-page/processes/process.model';
describe('BulkAccessComponent', () => {
fdescribe('BulkAccessComponent', () => {
let component: BulkAccessComponent;
let fixture: ComponentFixture<BulkAccessComponent>;
let bulkAccessControlService: any;
let selectableListService: any;
const selectableListServiceMock = jasmine.createSpyObj('SelectableListService', ['getSelectableList', 'deselectAll']);
const bulkAccessControlServiceMock = jasmine.createSpyObj('bulkAccessControlService', ['createPayloadFile', 'executeScript']);
const mockFormState = {
"bitstream": [],
"item": [
{
"name": "embargo",
"startDate": {
"year": 2026,
"month": 5,
"day": 31
},
"endDate": null
}
],
"state": {
"item": {
"toggleStatus": true,
"accessMode": "replace"
},
"bitstream": {
"toggleStatus": false,
"accessMode": "",
"changesLimit": "",
"selectedBitstreams": []
}
}
};
const mockSettings: any = jasmine.createSpyObj('AccessControlFormContainerComponent', {
getValue: jasmine.createSpy('getValue'),
reset: jasmine.createSpy('reset')
});
const selection: any[] = [{ indexableObject: { uuid: '1234' } }, { indexableObject: { uuid: '5678' } }];
const selectableListState: SelectableListState = { id: 'test', selection };
const expectedIdList = ['1234', '5678'];
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ BulkAccessComponent ]
imports: [ TranslateModule.forRoot() ],
declarations: [ BulkAccessComponent ],
providers: [
{ provide: BulkAccessControlService, useValue: bulkAccessControlServiceMock },
{ provide: SelectableListService, useValue: selectableListServiceMock }
],
schemas: [NO_ERRORS_SCHEMA]
})
.compileComponents();
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(BulkAccessComponent);
component = fixture.componentInstance;
bulkAccessControlService = TestBed.inject(BulkAccessControlService);
selectableListService = TestBed.inject(SelectableListService);
(component as any).selectableListService.getSelectableList.and.returnValue(of(selectableListState));
fixture.detectChanges();
component.settings = mockSettings;
});
afterEach(() => {
fixture.destroy();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should generate the id list by selected elements', () => {
expect(component.objectsSelected$.value).toEqual(expectedIdList);
});
it('should disable the execute button when there are no objects selected', () => {
expect(component.canExport()).toBe(false);
});
it('should enable the execute button when there are objects selected', () => {
component.objectsSelected$.next(['1234']);
expect(component.canExport()).toBe(true);
});
it('should call the settings reset method when reset is called', () => {
spyOn(component.settings, 'reset');
component.reset();
expect(component.settings.reset).toHaveBeenCalled();
});
it('should call the bulkAccessControlService executeScript method when submit is called', () => {
(component.settings as any).getValue.and.returnValue(mockFormState)
bulkAccessControlService.executeScript.and.returnValue(createSuccessfulRemoteDataObject$(new Process()));
component.objectsSelected$.next(['1234']);
component.submit();
expect(bulkAccessControlService.executeScript).toHaveBeenCalled();
});
});

View File

@@ -1,8 +1,31 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnInit, ViewChild } from '@angular/core';
import { BulkAccessSettingsComponent } from './settings/bulk-access-settings.component';
import { distinctUntilChanged, map, take, tap } from 'rxjs/operators';
import { BulkAccessControlService } from '../../shared/access-control-form-container/bulk-access-control.service';
import { SelectableListState } from '../../shared/object-list/selectable-list/selectable-list.reducer';
import { BehaviorSubject, Subscription } from 'rxjs';
import { SelectableListService } from '../../shared/object-list/selectable-list/selectable-list.service';
@Component({
selector: 'ds-bulk-access',
templateUrl: './bulk-access.component.html',
// templateUrl: './bulk-access.component.html',
template: `<div class="container">
<ds-bulk-access-browse [listId]="listId"></ds-bulk-access-browse>
<div class="clearfix mb-3"></div>
<ds-bulk-access-settings #dsBulkSettings ></ds-bulk-access-settings>
<hr>
<div class="d-flex justify-content-end">
<button class="btn btn-outline-primary mr-3" (click)="reset()">
{{ 'access-control-reset' | translate }}
</button>
<button class="btn btn-primary" [disabled]="!canExport()" (click)="submit()">
{{ 'access-control-execute' | translate }}
</button>
</div>
</div>`,
styleUrls: ['./bulk-access.component.scss']
})
export class BulkAccessComponent implements OnInit {
@@ -13,9 +36,78 @@ export class BulkAccessComponent implements OnInit {
*/
listId: string = 'bulk-access-list';
constructor() { }
/**
* The list of the objects already selected
*/
objectsSelected$: BehaviorSubject<string[]> = new BehaviorSubject<string[]>([]);
/**
* Array to track all subscriptions and unsubscribe them onDestroy
*/
private subs: Subscription[] = [];
/**
* The SectionsDirective reference
*/
@ViewChild('dsBulkSettings') settings: BulkAccessSettingsComponent;
constructor(
private bulkAccessControlService: BulkAccessControlService,
private selectableListService: SelectableListService
) { }
ngOnInit(): void {
this.subs.push(
this.selectableListService.getSelectableList(this.listId).pipe(
distinctUntilChanged(),
tap(console.log),
map((list: SelectableListState) => this.generateIdListBySelectedElements(list)),
tap(console.log)
).subscribe(this.objectsSelected$)
)
}
canExport(): boolean {
return this.objectsSelected$.value?.length > 0;
}
/**
* Reset the form to its initial state
* This will also reset the state of the child components (bitstream and item access)
*/
reset(): void {
this.settings.reset()
}
/**
* Submit the form
* This will create a payload file and execute the script
*/
submit(): void {
const settings = this.settings.getValue();
const bitstreamAccess = settings.bitstream;
const itemAccess = settings.item;
const { file } = this.bulkAccessControlService.createPayloadFile({
bitstreamAccess,
itemAccess,
state: settings.state
});
this.bulkAccessControlService.executeScript(
this.objectsSelected$.value || [],
file
).pipe(take(1)).subscribe((res) => {
console.log('success', res);
});
}
/**
* Generate The RemoteData object containing the list of the selected elements
* @param list
* @private
*/
private generateIdListBySelectedElements(list: SelectableListState): string[] {
return list?.selection?.map((entry: any) => entry.indexableObject.uuid);
}
}

View File

@@ -15,7 +15,7 @@
</div>
</ng-template>
<ng-template ngbPanelContent>
<p>bulk-access-settings works!</p>
<ds-access-control-form-container #dsAccessControlForm [showSubmit]="false"></ds-access-control-form-container>
</ng-template>
</ngb-panel>
</ngb-accordion>

View File

@@ -1,25 +1,80 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap';
import { TranslateModule } from '@ngx-translate/core';
import { BulkAccessSettingsComponent } from './bulk-access-settings.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
describe('BulkAccessSettingsComponent', () => {
let component: BulkAccessSettingsComponent;
let fixture: ComponentFixture<BulkAccessSettingsComponent>;
const mockFormState = {
"bitstream": [],
"item": [
{
"name": "embargo",
"startDate": {
"year": 2026,
"month": 5,
"day": 31
},
"endDate": null
}
],
"state": {
"item": {
"toggleStatus": true,
"accessMode": "replace"
},
"bitstream": {
"toggleStatus": false,
"accessMode": "",
"changesLimit": "",
"selectedBitstreams": []
}
}
};
const mockControl: any = jasmine.createSpyObj('AccessControlFormContainerComponent', {
getFormValue: jasmine.createSpy('getFormValue'),
reset: jasmine.createSpy('reset')
});
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ BulkAccessSettingsComponent ]
})
.compileComponents();
imports: [NgbAccordionModule, TranslateModule.forRoot()],
declarations: [BulkAccessSettingsComponent],
schemas: [NO_ERRORS_SCHEMA]
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(BulkAccessSettingsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
component.controlForm = mockControl;
});
it('should create', () => {
it('should create the component', () => {
expect(component).toBeTruthy();
});
it('should have a method to get the form value', () => {
expect(component.getValue).toBeDefined();
});
it('should have a method to reset the form', () => {
expect(component.reset).toBeDefined();
});
it('should return the correct form value', () => {
const expectedValue = mockFormState;
(component.controlForm as any).getFormValue.and.returnValue(mockFormState)
const actualValue = component.getValue();
expect(actualValue).toEqual(expectedValue);
});
it('should call reset on the control form', () => {
component.reset();
expect(component.controlForm.reset).toHaveBeenCalled();
});
});

View File

@@ -1,20 +1,34 @@
import { Component, Input, OnInit } from '@angular/core';
import { Component, ViewChild } from '@angular/core';
import {
AccessControlFormContainerComponent
} from '../../../shared/access-control-form-container/access-control-form-container.component';
@Component({
selector: 'ds-bulk-access-settings',
templateUrl: './bulk-access-settings.component.html',
styleUrls: ['./bulk-access-settings.component.scss']
templateUrl: 'bulk-access-settings.component.html',
styleUrls: ['./bulk-access-settings.component.scss'],
exportAs: 'dsBulkSettings'
})
export class BulkAccessSettingsComponent implements OnInit {
export class BulkAccessSettingsComponent {
/**
* The selection list id
* The SectionsDirective reference
*/
@Input() listId!: string;
@ViewChild('dsAccessControlForm') controlForm: AccessControlFormContainerComponent<any>;
constructor() { }
/**
* Will be used from a parent component to read the value of the form
*/
getValue() {
return this.controlForm.getFormValue();
}
ngOnInit(): void {
/**
* Reset the form to its initial state
* This will also reset the state of the child components (bitstream and item access)
*/
reset() {
this.controlForm.reset()
}
}

View File

@@ -11,7 +11,7 @@ import { ControlMaxEndDatePipe } from './control-max-end-date.pipe';
import { DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
fdescribe('AccessControlArrayFormComponent', () => {
describe('AccessControlArrayFormComponent', () => {
let component: AccessControlArrayFormComponent;
let fixture: ComponentFixture<AccessControlArrayFormComponent>;

View File

@@ -65,6 +65,11 @@ export class AccessControlFormContainerComponent<T extends DSpaceObject> impleme
* Will be used from a parent component to read the value of the form
*/
getFormValue() {
console.log({
bitstream: this.bitstreamAccessCmp.getValue(),
item: this.itemAccessCmp.getValue(),
state: this.state
});
return {
bitstream: this.bitstreamAccessCmp.getValue(),
item: this.itemAccessCmp.getValue(),

View File

@@ -1,12 +1,14 @@
import { TestBed } from '@angular/core/testing';
import { BulkAccessControlService } from './bulk-access-control.service';
import { BulkAccessControlService, BulkAccessPayload } from './bulk-access-control.service';
import { ScriptDataService } from '../../core/data/processes/script-data.service';
import { ProcessParameter } from '../../process-page/processes/process-parameter.model';
fdescribe('BulkAccessControlService', () => {
describe('BulkAccessControlService', () => {
let service: BulkAccessControlService;
let scriptServiceSpy: jasmine.SpyObj<ScriptDataService>;
beforeEach(() => {
const spy = jasmine.createSpyObj('ScriptDataService', ['invoke']);
TestBed.configureTestingModule({
@@ -25,7 +27,7 @@ fdescribe('BulkAccessControlService', () => {
describe('createPayloadFile', () => {
it('should create a file and return the URL and file object', () => {
const payload = { data: 'test' };
const payload: BulkAccessPayload = { state: null, bitstreamAccess: null, itemAccess: null };
const result = service.createPayloadFile(payload);
expect(result.url).toBeTruthy();

View File

@@ -4,11 +4,17 @@ import { ScriptDataService } from '../../core/data/processes/script-data.service
import { ProcessParameter } from '../../process-page/processes/process-parameter.model';
import { AccessControlFormState } from './access-control-form-container.component';
export interface BulkAccessPayload {
state: AccessControlFormState;
bitstreamAccess: any;
itemAccess: any;
}
@Injectable({ providedIn: 'root' })
export class BulkAccessControlService {
constructor(private scriptService: ScriptDataService) {}
createPayloadFile(payload: { state: AccessControlFormState, bitstreamAccess, itemAccess }) {
createPayloadFile(payload: BulkAccessPayload) {
const content = convertToBulkAccessControlFileModel(payload);
const blob = new Blob([JSON.stringify(content, null, 2)], {

View File

@@ -11,7 +11,7 @@ import {
} from './selectable-list.actions';
import { AppState } from '../../../app.reducer';
class SelectableObject extends ListableObject {
export class SelectableObject extends ListableObject {
constructor(private value: string) {
super();
}