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

feat(list-editor): add deletion functionality (DSP-1334) #378

Merged
merged 10 commits into from Feb 16, 2021
30 changes: 29 additions & 1 deletion src/app/main/dialog/dialog.component.html
Expand Up @@ -155,9 +155,37 @@
<app-edit-list-item [iri]="data.id" [projectIri]="data.project" (closeDialog)="dialogRef.close($event)"></app-edit-list-item>
</div>

<!-- Delete list node-->
<div *ngSwitchCase="'deleteListNode'">
<app-dialog-header [title]="data.title" [subtitle]="'appLabels.form.lists.title.deleteListNode' | translate"></app-dialog-header>
Do you want to delete this node?
<mat-dialog-actions>
<button mat-button mat-dialog-close class="cancel-button center" [mat-dialog-close]="false">
Cancel
</button>
<span class="fill-remaining-space"></span>
<button mat-button mat-raised-button [color]="'warn'" class="confirm-button center"
[mat-dialog-close]="true">
Delete
</button>
</mat-dialog-actions>
</div>

<!-- Delete list node error-->
<div *ngSwitchCase="'deleteListNodeError'">
Unable to delete this list node as it is in use.
<mat-dialog-actions>
<span class="fill-remaining-space"></span>
<button mat-button mat-raised-button [color]="'warn'" class="confirm-button center"
[mat-dialog-close]="true">
Okay
</button>
</mat-dialog-actions>
</div>

<!-- Delete list -->
<div *ngSwitchCase="'deleteList'">
<app-dialog-header [title]="data.title" [subtitle]="'appLabels.form.lists.title.delete' | translate"></app-dialog-header>
<app-dialog-header [title]="data.title" [subtitle]="'appLabels.form.lists.title.deleteList' | translate"></app-dialog-header>
Do you want to delete this list?
<mat-dialog-actions>
<button mat-button mat-dialog-close class="cancel-button center" [mat-dialog-close]="false">
Expand Down
Expand Up @@ -29,6 +29,12 @@
(click)="$event.stopPropagation(); openDialog('editListNode', labels[0].value, iri)">
<mat-icon>edit</mat-icon>
</button>
<button mat-button
class="delete"
title="delete"
(click)="$event.stopPropagation(); openDialog('deleteListNode', labels[0].value, iri)">
<mat-icon>delete</mat-icon>
</button>
</div>
</div>
</div>
176 changes: 144 additions & 32 deletions src/app/project/list/list-item-form/list-item-form.component.spec.ts
@@ -1,69 +1,181 @@
import { HttpClientModule } from '@angular/common/http';
import { OverlayContainer } from '@angular/cdk/overlay';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { Component, OnInit, ViewChild } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatButtonHarness } from '@angular/material/button/testing';
import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatDialogHarness } from '@angular/material/dialog/testing';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterTestingModule } from '@angular/router/testing';
import { KnoraApiConnection } from '@dasch-swiss/dsp-js';
import { ApiResponseData, DeleteListNodeResponse, ListsEndpointAdmin, StringLiteral } from '@dasch-swiss/dsp-js';
import {
AppInitService,
DspActionModule,
DspApiConfigToken,
DspApiConnectionToken,
DspCoreModule
DspApiConnectionToken
} from '@dasch-swiss/dsp-ui';
import { TranslateModule } from '@ngx-translate/core';
import { of } from 'rxjs';
import { AjaxResponse } from 'rxjs/ajax';
import { DialogHeaderComponent } from 'src/app/main/dialog/dialog-header/dialog-header.component';
import { DialogComponent } from 'src/app/main/dialog/dialog.component';
import { ErrorComponent } from 'src/app/main/error/error.component';
import { TestConfig } from 'test.config';
import { ListItemFormComponent } from './list-item-form.component';
import { ListItemFormComponent, ListNodeOperation } from './list-item-form.component';

/**
* Test host component to simulate parent component.
*/
@Component({
template: `
<app-list-item-form
#listItemForm
[iri]="iri"
[language]="language"
(refreshParent)="updateView($event)"
[projectIri]="projectIri"
[projectcode]="projectCode"
[labels]="labels">
</app-list-item-form>`
})
class TestHostComponent implements OnInit {

@ViewChild('listItemForm') listItemForm: ListItemFormComponent;

iri = 'http://rdfh.ch/lists/0001/notUsedList01';

language = 'en';

projectIri = 'http://rdfh.ch/projects/0001';

projectCode = '0001';

labels: StringLiteral[];

constructor() {}

ngOnInit() {
this.labels = [
{
value: 'node 1',
language: 'en'
}
];
}

}

describe('ListItemFormComponent', () => {
let component: ListItemFormComponent;
let fixture: ComponentFixture<ListItemFormComponent>;
let testHostComponent: TestHostComponent;
let testHostFixture: ComponentFixture<TestHostComponent>;
let rootLoader: HarnessLoader;
let overlayContainer: OverlayContainer;

beforeEach(async(() => {

const listsEndpointSpyObj = {
admin: {
listsEndpoint: jasmine.createSpyObj('listsEndpoint', ['deleteListNode'])
}
};

TestBed.configureTestingModule({
declarations: [
ListItemFormComponent,
TestHostComponent,
DialogComponent,
ErrorComponent
DialogHeaderComponent
],
imports: [
BrowserAnimationsModule,
DspActionModule,
DspCoreModule,
HttpClientModule,
MatIconModule,
MatInputModule,
ReactiveFormsModule,
RouterTestingModule,
MatDialogModule,
MatButtonModule,
TranslateModule.forRoot()
],
providers: [
AppInitService,
{
provide: DspApiConfigToken,
useValue: TestConfig.ApiConfig
provide: DspApiConnectionToken,
useValue: listsEndpointSpyObj
},
{
provide: DspApiConnectionToken,
useValue: new KnoraApiConnection(TestConfig.ApiConfig)
}
provide: MAT_DIALOG_DATA,
useValue: {}
},
{
provide: MatDialogRef,
useValue: {}
},
]

})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(ListItemFormComponent);
component = fixture.componentInstance;
fixture.detectChanges();
testHostFixture = TestBed.createComponent(TestHostComponent);
testHostComponent = testHostFixture.componentInstance;
testHostFixture.detectChanges();

expect(testHostComponent).toBeTruthy();

testHostComponent.listItemForm.showActionBubble = true;
testHostFixture.detectChanges();

overlayContainer = TestBed.inject(OverlayContainer);
rootLoader = TestbedHarnessEnvironment.documentRootLoader(testHostFixture);
});

it('should create', () => {
expect(component).toBeTruthy();
afterEach(async () => {
const dialogs = await rootLoader.getAllHarnesses(MatDialogHarness);
await Promise.all(dialogs.map(async d => await d.close()));

// angular won't call this for us so we need to do it ourselves to avoid leaks.
overlayContainer.ngOnDestroy();
});

it('should show a dialog box when the delete button is clicked', async () => {

const deleteListNodeResponse: DeleteListNodeResponse = new DeleteListNodeResponse();
deleteListNodeResponse.node.children = [];
deleteListNodeResponse.node.id = 'http://rdfh.ch/lists/0001/notUsedList';
deleteListNodeResponse.node.isRootNode = true;
deleteListNodeResponse.node.name = 'notUsedList';
deleteListNodeResponse.node.projectIri = 'http://rdfh.ch/projects/0001';

const listSpy = TestBed.inject(DspApiConnectionToken);

(listSpy.admin.listsEndpoint as jasmine.SpyObj<ListsEndpointAdmin>).deleteListNode.and.callFake(
() => {

const response = deleteListNodeResponse;

return of(ApiResponseData.fromAjaxResponse({response} as AjaxResponse));
}
);

spyOn(testHostComponent.listItemForm.refreshParent, 'emit');

const deleteButton = await rootLoader.getHarness(MatButtonHarness.with({selector: '.delete'}));
await deleteButton.click();

const dialogHarnesses = await rootLoader.getAllHarnesses(MatDialogHarness);

expect(dialogHarnesses.length).toEqual(1);

const confirmButton = await rootLoader.getHarness(MatButtonHarness.with({selector: '.confirm-button'}));

await confirmButton.click();

const listNodeOperation: ListNodeOperation = new ListNodeOperation();
listNodeOperation.listNode = deleteListNodeResponse.node;
listNodeOperation.operation = 'delete';

testHostFixture.whenStable().then(() => {
expect(listSpy.admin.listsEndpoint.deleteListNode).toHaveBeenCalledWith(testHostComponent.iri);
expect(listSpy.admin.listsEndpoint.deleteListNode).toHaveBeenCalledTimes(1);

expect(testHostComponent.listItemForm.refreshParent.emit).toHaveBeenCalledWith(listNodeOperation);
expect(testHostComponent.listItemForm.refreshParent.emit).toHaveBeenCalledTimes(1);
});

});
});