Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
}
},
"options": {
"codeCoverage": true,
"main": "src/test.ts",
"karmaConfig": "./karma.conf.js",
"polyfills": "src/polyfills.ts",
Expand Down
16 changes: 12 additions & 4 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

module.exports = function (config) {
config.set({
browsers: ['Chrome', 'ChromeHeadless', 'ChromeHeadlessCI'],
customLaunchers: {
ChromeHeadlessCI: {
base: 'ChromeHeadless',
Expand All @@ -23,11 +22,20 @@ module.exports = function (config) {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
fixWebpackSourcePaths: true
dir: require('path').join(__dirname, 'coverage'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true,
thresholds: {
global: {
statements: 95,
branches: 85,
functions: 90,
lines: 95
}
}
},

reporters: ['progress', 'kjhtml'],
reporters: ['progress', 'kjhtml', 'coverage-istanbul'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
Expand Down
124 changes: 124 additions & 0 deletions src/app/error.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { TestBed } from '@angular/core/testing';
import { HttpErrorResponse, HttpHeaders } from '@angular/common/http';
import { HttpErrorHandler } from './error.service';

describe('HttpErrorHandler', () => {
let service: HttpErrorHandler;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [HttpErrorHandler]
});
service = TestBed.inject(HttpErrorHandler);
});

it('should be created', () => {
expect(service).toBeTruthy();
});

it('should create a curried handleError function via createHandleError', () => {
const handler = service.createHandleError('TestService');
expect(handler).toBeTruthy();
expect(typeof handler).toBe('function');
});

it('should handle client-side ErrorEvent', () => {
const handler = service.createHandleError('TestService');
const errorHandler = handler('testOp', 'fallback');
const errorEvent = new ErrorEvent('Network error', { message: 'client error' });
const httpError = new HttpErrorResponse({ error: errorEvent, status: 0 });

errorHandler(httpError).subscribe({
error: (msg: string) => {
expect(msg).toBe('client error');
}
});
});

it('should handle server-side error', () => {
const handler = service.createHandleError('TestService');
const errorHandler = handler('testOp', 'fallback');
const httpError = new HttpErrorResponse({ error: 'Not Found', status: 404 });

errorHandler(httpError).subscribe({
error: (msg: string) => {
expect(msg).toContain('server returned code 404');
}
});
});

it('should extract errorMessage from errors header', () => {
const handler = service.createHandleError('TestService');
const errorHandler = handler('testOp', 'fallback');
const headers = new HttpHeaders().set('errors', JSON.stringify([{ errorMessage: 'Field is required' }]));
const httpError = new HttpErrorResponse({ error: 'Bad Request', status: 400, headers });

errorHandler(httpError).subscribe({
error: (msg: string) => {
expect(msg).toBe('Field is required');
}
});
});

it('should use server error message when errors header has no errorMessage', () => {
const handler = service.createHandleError('TestService');
const errorHandler = handler('testOp', 'fallback');
const headers = new HttpHeaders().set('errors', JSON.stringify([{ field: 'name' }]));
const httpError = new HttpErrorResponse({ error: 'Bad Request', status: 400, headers });

errorHandler(httpError).subscribe({
error: (msg: string) => {
expect(msg).toContain('server returned code 400');
}
});
});

it('should use server error message when errors header is empty array', () => {
const handler = service.createHandleError('TestService');
const errorHandler = handler('testOp', 'fallback');
const headers = new HttpHeaders().set('errors', JSON.stringify([]));
const httpError = new HttpErrorResponse({ error: 'Bad Request', status: 400, headers });

errorHandler(httpError).subscribe({
error: (msg: string) => {
expect(msg).toContain('server returned code 400');
}
});
});

it('should use server error message when errors header is not an array', () => {
const handler = service.createHandleError('TestService');
const errorHandler = handler('testOp', 'fallback');
const headers = new HttpHeaders().set('errors', JSON.stringify({ errorMessage: 'not array' }));
const httpError = new HttpErrorResponse({ error: 'Bad Request', status: 400, headers });

errorHandler(httpError).subscribe({
error: (msg: string) => {
expect(msg).toContain('server returned code 400');
}
});
});

it('should use default parameters when none provided', () => {
const errorFn = service.handleError();
const httpError = new HttpErrorResponse({ error: 'Server Error', status: 500 });

errorFn(httpError).subscribe({
error: (msg: string) => {
expect(msg).toContain('server returned code 500');
}
});
});

it('should use default serviceName when createHandleError called without args', () => {
const handler = service.createHandleError();
const errorHandler = handler();
const httpError = new HttpErrorResponse({ error: 'Server Error', status: 500 });

errorHandler(httpError).subscribe({
error: (msg: string) => {
expect(msg).toContain('server returned code 500');
}
});
});
});
25 changes: 24 additions & 1 deletion src/app/owners/owner-add/owner-add.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { OwnerService } from '../owner.service';
import { RouterTestingModule } from '@angular/router/testing';
import { RouterStub } from '../../testing/router-stubs';
import { Owner } from '../owner';
import { Observable, of } from 'rxjs';
import { Observable, of, throwError } from 'rxjs';
import { By } from '@angular/platform-browser';
import { OwnersRoutingModule } from '../owners-routing.module';
import { OwnerListComponent } from '../owner-list/owner-list.component';
Expand Down Expand Up @@ -105,4 +105,27 @@ describe('OwnerAddComponent', () => {
expect(component.onSubmit).toHaveBeenCalled();
}));

it('should submit owner and navigate to owners list', () => {
const ownerServiceLocal = fixture.debugElement.injector.get(OwnerService);
const testOwner: Owner = { id: null, firstName: 'James', lastName: 'Franklin', address: '110 W. Liberty St.', city: 'Madison', telephone: '6085551023', pets: [] };
const returnOwner: Owner = { ...testOwner, id: 1 };
spyOn(ownerServiceLocal, 'addOwner').and.returnValue(of(returnOwner));
component.onSubmit(testOwner);
expect(component.owner).toEqual(returnOwner);
expect(router.navigate).toHaveBeenCalledWith(['/owners']);
});

it('should set errorMessage on submit error', () => {
const ownerServiceLocal = fixture.debugElement.injector.get(OwnerService);
const testOwner: Owner = { id: null, firstName: 'James', lastName: 'Franklin', address: '110 W. Liberty St.', city: 'Madison', telephone: '6085551023', pets: [] };
spyOn(ownerServiceLocal, 'addOwner').and.returnValue(throwError('submit error'));
component.onSubmit(testOwner);
expect(component.errorMessage).toBe('submit error');
});

it('should navigate to owners list on gotoOwnersList', () => {
component.gotoOwnersList();
expect(router.navigate).toHaveBeenCalledWith(['/owners']);
});

});
31 changes: 30 additions & 1 deletion src/app/owners/owner-detail/owner-detail.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,15 @@ import { OwnerService } from '../owner.service';
import { ActivatedRoute, Router } from '@angular/router';
import { ActivatedRouteStub, RouterStub } from '../../testing/router-stubs';
import { Owner } from '../owner';
import { Observable, of } from 'rxjs';
import { Observable, of, throwError } from 'rxjs';

class OwnerServiceStub {
getOwnerById(): Observable<Owner> {
return of({ id: 1, firstName: 'James', lastName: 'Franklin' } as Owner);
}
deleteOwner(ownerId: string): Observable<number> {
return of(204);
}
}

describe('OwnerDetailComponent', () => {
Expand Down Expand Up @@ -131,4 +134,30 @@ describe('OwnerDetailComponent', () => {
expect(router.navigate).toHaveBeenCalledWith(['/owners']);
});

it('should set errorMessage on getOwnerById error', () => {
const ownerServiceLocal = fixture.debugElement.injector.get(OwnerService);
spyOn(ownerServiceLocal, 'getOwnerById').and.returnValue(throwError('load error'));
component.ngOnInit();
expect(component.errorMessage).toBe('load error');
});

it('should navigate to owners list on gotoOwnersList', () => {
spyOn(router, 'navigate');
component.gotoOwnersList();
expect(router.navigate).toHaveBeenCalledWith(['/owners']);
});

it('should navigate to edit owner on editOwner', () => {
spyOn(router, 'navigate');
component.owner = owner;
component.editOwner();
expect(router.navigate).toHaveBeenCalledWith(['/owners', 10, 'edit']);
});

it('should navigate to add pet on addPet', () => {
spyOn(router, 'navigate');
component.addPet(owner);
expect(router.navigate).toHaveBeenCalledWith(['/owners', 10, 'pets', 'add']);
});

});
35 changes: 34 additions & 1 deletion src/app/owners/owner-edit/owner-edit.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,17 @@ import { OwnerService } from '../owner.service';
import { ActivatedRoute, Router } from '@angular/router';
import { ActivatedRouteStub, RouterStub } from '../../testing/router-stubs';
import { Owner } from '../owner';
import { Observable, of } from 'rxjs';
import { Observable, of, throwError } from 'rxjs';
import { By } from '@angular/platform-browser';
import { OwnerListComponent } from '../owner-list/owner-list.component';

class OwnserServiceStub {
getOwnerById(): Observable<Owner> {
return of({ id: 1, firstName: 'James' } as Owner);
}
updateOwner(ownerId: string, owner: Owner): Observable<Owner> {
return of(owner);
}
}

describe('OwnerEditComponent', () => {
Expand Down Expand Up @@ -97,4 +100,34 @@ describe('OwnerEditComponent', () => {
expect(component.onSubmit).toHaveBeenCalled();
}));

it('should set errorMessage on getOwnerById error', () => {
const ownerServiceLocal = fixture.debugElement.injector.get(OwnerService);
spyOn(ownerServiceLocal, 'getOwnerById').and.returnValue(throwError('load error'));
component.ngOnInit();
expect(component.errorMessage).toBe('load error');
});

it('should submit owner and navigate to owner detail', () => {
const ownerServiceLocal = fixture.debugElement.injector.get(OwnerService);
const testOwner: Owner = { id: 1, firstName: 'James', lastName: 'Franklin', address: '110 W. Liberty St.', city: 'Madison', telephone: '6085551023', pets: [] };
spyOn(ownerServiceLocal, 'updateOwner').and.returnValue(of(testOwner));
component.onSubmit(testOwner);
expect(router.navigate).toHaveBeenCalledWith(['/owners', 1]);
});

it('should set errorMessage on submit error', () => {
const ownerServiceLocal = fixture.debugElement.injector.get(OwnerService);
const testOwner: Owner = { id: 1, firstName: 'James', lastName: 'Franklin', address: '110 W. Liberty St.', city: 'Madison', telephone: '6085551023', pets: [] };
spyOn(ownerServiceLocal, 'updateOwner').and.returnValue(throwError('update error'));
component.onSubmit(testOwner);
expect(component.errorMessage).toBe('update error');
});

it('should navigate to owner detail on gotoOwnerDetail', () => {
const testOwner: Owner = { id: 5, firstName: 'Test', lastName: 'Owner', address: '', city: '', telephone: '', pets: [] };
component.gotoOwnerDetail(testOwner);
expect(component.errorMessage).toBeNull();
expect(router.navigate).toHaveBeenCalledWith(['/owners', 5]);
});

});
41 changes: 39 additions & 2 deletions src/app/owners/owner-list/owner-list.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ import {DebugElement, NO_ERRORS_SCHEMA} from '@angular/core';

import {OwnerListComponent} from './owner-list.component';
import {FormsModule} from '@angular/forms';
import {ActivatedRoute} from '@angular/router';
import {ActivatedRoute, Router} from '@angular/router';
import {OwnerService} from '../owner.service';
import {Owner} from '../owner';
import {Observable, of} from 'rxjs';
import {Observable, of, throwError} from 'rxjs';
import {RouterTestingModule} from '@angular/router/testing';
import {CommonModule} from '@angular/common';
import {PartsModule} from '../../parts/parts.module';
Expand All @@ -48,6 +48,9 @@ class OwnerServiceStub {
getOwners(): Observable<Owner[]> {
return of();
}
searchOwners(lastName: string): Observable<Owner[]> {
return of();
}
}

describe('OwnerListComponent', () => {
Expand Down Expand Up @@ -137,4 +140,38 @@ describe('OwnerListComponent', () => {
});
}));

it('should navigate to owner detail on onSelect', () => {
const router = fixture.debugElement.injector.get(Router);
spyOn(router, 'navigate');
const owner: Owner = testOwners[0];
component.onSelect(owner);
expect(router.navigate).toHaveBeenCalledWith(['/owners', owner.id]);
});

it('should navigate to add owner on addOwner', () => {
const router = fixture.debugElement.injector.get(Router);
spyOn(router, 'navigate');
component.addOwner();
expect(router.navigate).toHaveBeenCalledWith(['/owners/add']);
});

it('should search by last name with empty string', () => {
spy.and.returnValue(of(testOwners));
component.searchByLastName('');
expect(component.owners).toEqual(testOwners);
});

it('should search by last name with non-empty string', () => {
const searchSpy = spyOn(ownerService as any, 'searchOwners').and.returnValue(of(testOwners));
component.searchByLastName('Franklin');
expect(searchSpy).toHaveBeenCalledWith('Franklin');
expect(component.owners).toEqual(testOwners);
});

it('should set owners to null on searchOwners error', () => {
spyOn(ownerService as any, 'searchOwners').and.returnValue(throwError('search error'));
component.searchByLastName('InvalidName');
expect(component.owners).toBeNull();
});

});
9 changes: 9 additions & 0 deletions src/app/owners/owner.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,15 @@ describe('OwnerService', () => {
expect(req.request.body).toEqual(null);
});

it('should search owners without lastName query param when undefined', () => {
ownerService.searchOwners(undefined).subscribe(owners => {
expect(owners).toEqual(expectedOwners);
});
const req = httpTestingController.expectOne(ownerService.entityUrl);
expect(req.request.method).toEqual('GET');
req.flush(expectedOwners);
});

it('search for delete Owner', () => {

const errorResponse = new HttpErrorResponse({
Expand Down
Loading