Skip to content

Commit

Permalink
[Issue comixed#233] Add the ReadingListEdit component and tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
mcpierce committed May 23, 2020
1 parent 3e32fb5 commit d5881be
Show file tree
Hide file tree
Showing 6 changed files with 320 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<p-dialog [visible]='editingReadingList'
modal='true'
[header]='"reading-list-edit.title"|translate'>
<form [formGroup]='readingListForm'>
<div class='ui-g'>
<div class='ui-g-12 cx-input-label'>
<label for='reading-list-name'>
{{'reading-list-edit.label.reading-list-name'|translate}}
</label>
</div>
<div class='ui-g-12 cx-input-field'>
<input id='reading-list-name'
class='form-control'
formControlName='name'
pInputText>
</div>
<div class='ui-g-12 cx-input-label'>
<label for='reading-list-summary'
class='cx-input-label'>
{{'reading-list-edit.label.reading-list-summary'|translate}}
</label>
</div>
<div class='ui-g-12 cx-input-field'>
<input id='reading-list-summary'
class='form-control cx-input-field'
formControlName='summary'
pInputText>
</div>
<div class='ui-g-12'>
<button pButton
type='button'
class='cx-action-button ui-button-info'
[label]='"button.save"|translate'
icon='fa fa-fw fa-save'
[disabled]='!readingListForm.valid'
(click)='saveReadingList()'></button>
<button pButton
type='button'
class='cx-action-button ui-button-danger'
[label]='"button.cancel"|translate'
icon='fa fa-fw fa-window-close'
(click)='cancelEditing()'></button>
</div>
</div>
</form>
</p-dialog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
/*
* ComiXed - A digital comic book library management application.
* Copyright (C) 2020, The ComiXed Project
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses>
*/

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { ReadingListEditComponent } from './reading-list-edit.component';
import { TranslateModule } from '@ngx-translate/core';
import { DialogModule } from 'primeng/dialog';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ButtonModule } from 'primeng/button';
import { Store, StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { LoggerModule } from '@angular-ru/logger';
import { Confirmation, ConfirmationService, MessageService } from 'primeng/api';
import { AppState, ReadingListAdaptor } from 'app/library';
import { ComicAdaptor } from 'app/comics/adaptors/comic.adaptor';
import { READING_LIST_1 } from 'app/comics/models/reading-list.fixtures';
import { ReadingList } from 'app/comics/models/reading-list';
import {
ReadingListCreate,
ReadingListEdit
} from 'app/library/actions/reading-list.actions';
import {
READING_LIST_FEATURE_KEY,
reducer
} from 'app/library/reducers/reading-list.reducer';
import { ReadingListEffects } from 'app/library/effects/reading-list.effects';

describe('ReadingListEditComponent', () => {
const READING_LIST = READING_LIST_1;
const READING_LIST_ID = READING_LIST.id;
const READING_LIST_NAME = READING_LIST.name;
const READING_LIST_SUMMARY = READING_LIST.summary;
const NEW_READING_LIST = {
id: null,
name: '',
summary: '',
comics: [],
lastUpdated: null
} as ReadingList;

let component: ReadingListEditComponent;
let fixture: ComponentFixture<ReadingListEditComponent>;
let store: Store<AppState>;
let readingListAdaptor: ReadingListAdaptor;
let confirmationService: ConfirmationService;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
HttpClientTestingModule,
FormsModule,
ReactiveFormsModule,
LoggerModule.forRoot(),
TranslateModule.forRoot(),
StoreModule.forRoot({}),
StoreModule.forFeature(READING_LIST_FEATURE_KEY, reducer),
EffectsModule.forRoot([]),
EffectsModule.forFeature([ReadingListEffects]),
DialogModule,
ButtonModule
],
declarations: [ReadingListEditComponent],
providers: [
MessageService,
ConfirmationService,
ComicAdaptor,
ReadingListAdaptor
]
}).compileComponents();

fixture = TestBed.createComponent(ReadingListEditComponent);
component = fixture.componentInstance;
store = TestBed.get(Store);
readingListAdaptor = TestBed.get(ReadingListAdaptor);
confirmationService = TestBed.get(ConfirmationService);
fixture.detectChanges();
}));

it('should create', () => {
expect(component).toBeTruthy();
});

describe('when creating a reading list', () => {
beforeEach(() => {
store.dispatch(new ReadingListCreate());
});

it('loads the name', () => {
expect(component.readingListForm.controls['name'].value).toEqual(
NEW_READING_LIST.name
);
});

it('loads the summary', () => {
expect(component.readingListForm.controls['summary'].value).toEqual(
NEW_READING_LIST.summary
);
});
});

describe('when editing a reading list', () => {
beforeEach(() => {
store.dispatch(new ReadingListEdit({ readingList: READING_LIST }));
});

it('loads the name', () => {
expect(component.readingListForm.controls['name'].value).toEqual(
READING_LIST.name
);
});

it('loads the summary', () => {
expect(component.readingListForm.controls['summary'].value).toEqual(
READING_LIST.summary
);
});
});

describe('saving a reading list', () => {
beforeEach(() => {
spyOn(readingListAdaptor, 'save');
store.dispatch(new ReadingListEdit({ readingList: READING_LIST }));
spyOn(confirmationService, 'confirm').and.callFake(
(confirm: Confirmation) => confirm.accept()
);
component.saveReadingList();
});

it('should call the reading list adaptor save method', () => {
expect(readingListAdaptor.save).toHaveBeenCalledWith(
READING_LIST_ID,
READING_LIST_NAME,
READING_LIST_SUMMARY
);
});
});

describe('canceling the edit', () => {
beforeEach(() => {
spyOn(readingListAdaptor, 'cancelEdit');
component.cancelEditing();
});

it('calls the reading list adaptor cancel edit method', () => {
expect(readingListAdaptor.cancelEdit).toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* ComiXed - A digital comic book library management application.
* Copyright (C) 2020, The ComiXed Project
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses>
*/

import { Component, OnDestroy, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Subscription } from 'rxjs';
import { ReadingList } from 'app/comics/models/reading-list';
import { LoggerService } from '@angular-ru/logger';
import { ReadingListAdaptor } from 'app/library';
import { filter } from 'rxjs/operators';
import { ConfirmationService } from 'primeng/api';
import { TranslateService } from '@ngx-translate/core';

@Component({
selector: 'app-reading-list-edit',
templateUrl: './reading-list-edit.component.html',
styleUrls: ['./reading-list-edit.component.scss']
})
export class ReadingListEditComponent implements OnInit, OnDestroy {
readingListForm: FormGroup;

editReadingListSubscription: Subscription;
editingReadingList = false;
readingListSubscription: Subscription;
readingList: ReadingList = null;
id = null;

constructor(
private formBuilder: FormBuilder,
private logger: LoggerService,
private readingListAdaptor: ReadingListAdaptor,
private confirmationService: ConfirmationService,
private translateService: TranslateService
) {
this.readingListForm = this.formBuilder.group({
name: ['', Validators.required],
summary: ['']
});
this.editReadingListSubscription = this.readingListAdaptor.editing$
.pipe(filter(readingList => !!readingList))
.subscribe(editing => (this.editingReadingList = editing));
this.readingListSubscription = this.readingListAdaptor.current$
.pipe(filter(readingList => !!readingList))
.subscribe(readingList => this.loadReadingList(readingList));
}

ngOnInit() {}

ngOnDestroy() {
this.editReadingListSubscription.unsubscribe();
this.readingListSubscription.unsubscribe();
}

private loadReadingList(readingList: ReadingList): void {
this.id = readingList.id;
this.readingListForm.controls['name'].setValue(readingList.name);
this.readingListForm.controls['summary'].setValue(readingList.summary);
this.readingListForm.markAsPristine();
}

saveReadingList() {
const name = this.readingListForm.controls['name'].value;
const summary = this.readingListForm.controls['summary'].value;
this.confirmationService.confirm({
header: this.translateService.instant('reading-list-edit.save.header'),
message: this.translateService.instant('reading-list-edit.save.message', {
name: name
}),
accept: () => this.readingListAdaptor.save(this.id, name, summary)
});
}

cancelEditing() {
this.readingListAdaptor.cancelEdit();
}
}
9 changes: 7 additions & 2 deletions comixed-frontend/src/app/library/library.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ import { ContextMenuModule } from 'primeng/contextmenu';
import {
ButtonModule,
DialogModule,
InputTextareaModule,
InputTextModule,
ProgressSpinnerModule,
ScrollPanelModule,
SidebarModule,
Expand Down Expand Up @@ -79,6 +81,7 @@ import { LibraryAdminPageComponent } from 'app/library/pages/library-admin-page/
import { FileSaverModule } from 'ngx-filesaver';
import { LibraryNavigationTreeComponent } from './components/library-navigation-tree/library-navigation-tree.component';
import { DuplicateComicsPageComponent } from './pages/duplicate-comics-page/duplicate-comics-page.component';
import { ReadingListEditComponent } from './components/reading-list-edit/reading-list-edit.component';

@NgModule({
imports: [
Expand Down Expand Up @@ -125,7 +128,8 @@ import { DuplicateComicsPageComponent } from './pages/duplicate-comics-page/dupl
ButtonModule,
FileSaverModule,
TreeModule,
ScrollPanelModule
ScrollPanelModule,
InputTextModule
],
exports: [CommonModule, ComicsModule, ComicListComponent],
declarations: [
Expand All @@ -146,7 +150,8 @@ import { DuplicateComicsPageComponent } from './pages/duplicate-comics-page/dupl
ConsolidateLibraryComponent,
LibraryAdminPageComponent,
LibraryNavigationTreeComponent,
DuplicateComicsPageComponent
DuplicateComicsPageComponent,
ReadingListEditComponent
],
providers: [
LibraryService,
Expand Down
11 changes: 11 additions & 0 deletions comixed-frontend/src/assets/i18n/library-en.json
Original file line number Diff line number Diff line change
Expand Up @@ -458,5 +458,16 @@
},
"duplicate-comics-page": {
"title": "Duplicate Comics ({count})"
},
"reading-list-edit": {
"title": "Reading List Details",
"label": {
"reading-list-name": "Name",
"reading-list-summary": "Summary"
},
"save": {
"header": "Save Reading List",
"message": "Save changes to \"{name}\"?"
}
}
}

0 comments on commit d5881be

Please sign in to comment.