From 80cbbba96722ac02ad1f434a4f11a1819481b267 Mon Sep 17 00:00:00 2001 From: Matthew Ross Date: Wed, 21 Mar 2018 17:11:02 -0400 Subject: [PATCH] Moving tests over to new framework --- README.md | 2 + package-lock.json | 12 +- package.json | 4 +- src/app/app.api-http.ts | 7 +- src/app/board/board.component.ts | 2 + src/app/shared/top-nav/top-nav.component.ts | 2 +- test/app/app.api-http.spec.ts | 172 ++++++++++---------- test/app/app.component.spec.ts | 12 +- test/app/board/board.component.spec.ts | 44 +++-- 9 files changed, 123 insertions(+), 134 deletions(-) diff --git a/README.md b/README.md index 68eae03..e3451e9 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,8 @@ Developing on TaskBoard is pretty simple too. 2. Run `git checkout dev` to work on the `dev` branch 3. If you don't have it already, install the Angular CLI globally with `npm i -g @angular/cli` 4. Run `npm i` to install dependencies (this also installs the API dependencies) + 5. Run `npm run watch` for the build to automatically run after any change. + a. You will need to change permissions on `dist/api/` manually after the first build. #### Unit Tests diff --git a/package-lock.json b/package-lock.json index ea3ec27..0e836de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6122,9 +6122,9 @@ } }, "jasmine-core": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.1.0.tgz", - "integrity": "sha1-pHheE11d9lAk38kiSVPfWFvSdmw=", + "version": "2.99.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz", + "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=", "dev": true }, "jasmine-spec-reporter": { @@ -6387,9 +6387,9 @@ "dev": true }, "karma-jasmine-html-reporter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.0.0.tgz", - "integrity": "sha512-SN9R/Pl9cY40yLlc7FkTcfswUr19M6ZZ25eM8X5wtZ0gvp0gneWZbe5lPYcer/Yrbz0D6QUiTSJaEzr3KBPvSg==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz", + "integrity": "sha1-SKjl7xiAdhfuK14zwRlMNbQ5Ukw=", "dev": true, "requires": { "karma-jasmine": "1.1.1" diff --git a/package.json b/package.json index b7b45d7..0c3c43f 100644 --- a/package.json +++ b/package.json @@ -65,13 +65,13 @@ "bourbon": "5.0.0", "bourbon-neat": "1.9.0", "codelyzer": "^4.2.1", - "jasmine-core": "^3.1.0", + "jasmine-core": "^2.8.0", "jasmine-spec-reporter": "~4.2.1", "karma": "~2.0.0", "karma-chrome-launcher": "~2.2.0", "karma-coverage-istanbul-reporter": "^1.4.2", "karma-jasmine": "~1.1.1", - "karma-jasmine-html-reporter": "^1.0.0", + "karma-jasmine-html-reporter": "^0.2.2", "karma-phantomjs-launcher": "^1.0.4", "protractor": "~5.3.0", "ts-node": "~5.0.1", diff --git a/src/app/app.api-http.ts b/src/app/app.api-http.ts index baf674f..1b33250 100644 --- a/src/app/app.api-http.ts +++ b/src/app/app.api-http.ts @@ -44,11 +44,8 @@ export class ApiInterceptor implements HttpInterceptor { localStorage.setItem(this.JWT_KEY, response.data[0]); } }, (err: any) => { - if (!(err instanceof HttpErrorResponse)) { - return; - } - - if ((err.status === 401 || err.status === 400) && + if ((err instanceof HttpErrorResponse) && + (err.status === 401 || err.status === 400) && (err.url + '').indexOf('login') === -1) { this.router.navigate(['']); localStorage.removeItem(this.JWT_KEY); diff --git a/src/app/board/board.component.ts b/src/app/board/board.component.ts index b3fe5c1..56cf898 100644 --- a/src/app/board/board.component.ts +++ b/src/app/board/board.component.ts @@ -55,6 +55,8 @@ export class BoardDisplay implements OnInit { this.userFilter = null; this.categoryFilter = null; + this.activeBoard = new Board(); + this.loading = true; stringsService.stringsChanged.subscribe(newStrings => { diff --git a/src/app/shared/top-nav/top-nav.component.ts b/src/app/shared/top-nav/top-nav.component.ts index 9d2d5c9..f85361f 100644 --- a/src/app/shared/top-nav/top-nav.component.ts +++ b/src/app/shared/top-nav/top-nav.component.ts @@ -28,7 +28,7 @@ export class TopNav { this.version = constants.VERSION; authService.userChanged - .subscribe(user => this.username = user.username); + .subscribe(user => this.username = user ? user.username : ''); stringsService.stringsChanged.subscribe(newStrings => { this.strings = newStrings; diff --git a/test/app/app.api-http.spec.ts b/test/app/app.api-http.spec.ts index 5314e82..ec28d35 100644 --- a/test/app/app.api-http.spec.ts +++ b/test/app/app.api-http.spec.ts @@ -1,110 +1,106 @@ +import { TestBed, inject } from '@angular/core/testing'; import { - HttpModule, - XHRBackend, - CookieXSRFStrategy, - ResponseOptions, - BrowserXhr, - RequestOptions -} from '@angular/http'; + HttpClient, + HttpErrorResponse, + HTTP_INTERCEPTORS +} from '@angular/common/http'; +import { + HttpClientTestingModule, + HttpTestingController +} from '@angular/common/http/testing'; +import { RouterTestingModule } from '@angular/router/testing'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/catch'; +import { AuthService } from '../../src/app/shared/auth/auth.service'; +import { ApiInterceptor } from '../../src/app/app.api-http'; -import { ApiHttp, API_HTTP_PROVIDERS, apiHttpFactory } from '../../src/app/app.api-http'; +describe('ApiInterceptor', () => { + const mockAuthService = { -describe('ApiHttp', () => { - let apiHttp; - - const routerMock = { - path: 'test', - url: 'test', - navigate(arr) { - this.path = arr[0]; - } }; beforeEach(() => { - const backend = new XHRBackend(new BrowserXhr(), - new ResponseOptions(), - new CookieXSRFStrategy()), - requestOptions = new RequestOptions(); - - apiHttp = new ApiHttp(backend, requestOptions, routerMock); + TestBed.configureTestingModule({ + imports: [ + HttpClientTestingModule, + RouterTestingModule.withRoutes([]) + ], + providers: [ + { + provide: AuthService, + useValue: mockAuthService + }, + { + provide: HTTP_INTERCEPTORS, + useClass: ApiInterceptor, + multi: true + } + ] + }); }); - it('provides API_HTTP_PROVIDERS', () => { - expect(API_HTTP_PROVIDERS).toEqual(jasmine.any(Array)); - }); + afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => { + httpMock.verify(); + })); - it('provides a factory method', () => { - const actual = apiHttpFactory(null, null, null); + it('adds Content-Type header', + inject([HttpClient, HttpTestingController], + (http: HttpClient, httpMock: HttpTestingController) => { + http.get('').subscribe(response => { + expect(response).toBeTruthy(); + }); - expect(actual).toEqual(jasmine.any(ApiHttp)); - }); + const req = httpMock.expectOne(req => + req.headers.has('Content-Type') && req.headers.get('Content-Type') === 'application/json' + ); + expect(req.request.method).toEqual('GET'); - it('should create', () => { - expect(apiHttp).toBeTruthy(); - }); - - it('has a request method', () => { - expect(apiHttp.request('')).toEqual(jasmine.any(Observable)); - }); - - it('handles the HTTP methods', () => { - expect(apiHttp.get('')).toEqual(jasmine.any(Observable)); - expect(apiHttp.post('')).toEqual(jasmine.any(Observable)); - expect(apiHttp.put('')).toEqual(jasmine.any(Observable)); - expect(apiHttp.delete('')).toEqual(jasmine.any(Observable)); - }); - - it('injects headers', () => { - localStorage.setItem('taskboard.jwt', 'testjwt'); - - const headers = apiHttp.getRequestOptionArgs().headers; - - expect(headers._headers.get('content-type')[0]) - .toEqual('application/json'); - expect(headers._headers.get('authorization')[0]) - .toEqual('testjwt'); - }); - - it('intercepts observable responses', () => { - const response = { - json() { - return { - data: ['testjwt'] - }; + req.flush({}); } - }; + ) + ); - apiHttp.intercept(Observable.of(response)) - .map(response => { - expect(localStorage.getItem('taskboard.jwt')) - .toEqual('testjwt'); - }); + it('adds Authorization header when JWT is present', + inject([HttpClient, HttpTestingController], + (http: HttpClient, httpMock: HttpTestingController) => { + localStorage.setItem('taskboard.jwt', 'fake'); - apiHttp.intercept(Observable.throw(null, response)) - .catch((err, caught) => { - expect(localStorage.getItem('taskboard.jwt')) - .toEqual(''); - }); - }); + http.post('', {}).subscribe(response => { + expect(response).toBeTruthy(); + }); - it('handles valid responses', () => { - apiHttp.handleResponse({ json: () => ({ data: [ 'jwt' ] }) }); + const req = httpMock.expectOne(req => + req.headers.has('Authorization') && req.headers.get('Authorization') === 'fake' + ); + expect(req.request.method).toEqual('POST'); - expect(localStorage.getItem('taskboard.jwt')).toEqual('jwt'); - }); + req.flush({ data: ['newToken'] }); + expect(localStorage.getItem('taskboard.jwt')).toEqual('newToken'); + } + ) + ) - it('handles error responses', () => { - var error = { - status: 401, - url: '' - }; + it('handles errors and clears the JWT', + inject([HttpClient, HttpTestingController], + (http: HttpClient, httpMock: HttpTestingController) => { + localStorage.setItem('taskboard.jwt', 'fake'); - apiHttp.handleError(error, null); + http.get('').subscribe(response => { + expect(response).toBeTruthy(); + }, error => { + expect(error).toBeTruthy(); + }); + + const req = httpMock.expectOne(req => + req.headers.has('Content-Type') && req.headers.get('Content-Type') === 'application/json' + ); + expect(req.request.method).toEqual('GET'); + + const error = new HttpErrorResponse({ status: 401 }); + req.flush({}, error); + expect(localStorage.getItem('Authorization')).toEqual(null); + } + ) + ) - expect(localStorage.getItem('taskboard.jwt')).toEqual(null); - }); }); diff --git a/test/app/app.component.spec.ts b/test/app/app.component.spec.ts index a2e05b4..5c1e0ff 100644 --- a/test/app/app.component.spec.ts +++ b/test/app/app.component.spec.ts @@ -1,25 +1,21 @@ import { TestBed, async } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; -import { HttpModule } from '@angular/http'; import { AppComponent } from '../../src/app/app.component'; import { - Notifications, NotificationsService, StringsService -} from '../../src/app/shared/index'; +} from '../../src/app/shared/services'; +import { SharedModule } from '../../src/app/shared/shared.module'; describe('AppComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ RouterTestingModule, - HttpModule - ], - declarations: [ - AppComponent, - Notifications + SharedModule ], + declarations: [ AppComponent ], providers: [ StringsService, NotificationsService diff --git a/test/app/board/board.component.spec.ts b/test/app/board/board.component.spec.ts index 0f0e247..9574a2f 100644 --- a/test/app/board/board.component.spec.ts +++ b/test/app/board/board.component.spec.ts @@ -1,13 +1,10 @@ import { async, TestBed, ComponentFixture } from '@angular/core/testing' -import { NO_ERRORS_SCHEMA } from '@angular/core'; -import { FormsModule } from '@angular/forms'; import { RouterTestingModule } from '@angular/router/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { FormsModule } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; import { Title } from '@angular/platform-browser'; -import { BaseRequestOptions, Http } from '@angular/http'; -import { MockBackend, MockConnection } from '@angular/http/testing'; - import { BehaviorSubject } from 'rxjs/BehaviorSubject'; import { DragulaModule } from 'ng2-dragula/ng2-dragula'; @@ -17,12 +14,15 @@ import { Constants, ContextMenuService, NotificationsService -} from '../../../src/app/shared/index'; +} from '../../../src/app/shared/services'; import { Login } from '../../../src/app/login/login.component'; -import { Settings } from '../../../src/app/settings/index'; -import { Dashboard } from '../../../src/app/dashboard/index'; -import { BoardService, BoardDisplay } from '../../../src/app/board/index'; -import { ROUTES } from '../../../src/app/app.routes'; +import { SettingsModule } from '../../../src/app/settings/settings.module'; +import { SharedModule } from '../../../src/app/shared/shared.module'; +import { DashboardModule } from '../../../src/app/dashboard/dashboard.module'; +import { BoardDisplay } from '../../../src/app/board/board.component'; +import { BoardService } from '../../../src/app/board/board.service'; +import { ColumnDisplay } from '../../../src/app/board/column/column.component'; +import { TaskDisplay } from '../../../src/app/board/task/task.component'; describe('BoardDisplay', () => { let component: BoardDisplay, @@ -31,15 +31,19 @@ describe('BoardDisplay', () => { beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ - RouterTestingModule.withRoutes(ROUTES), + RouterTestingModule, + HttpClientTestingModule, FormsModule, - DragulaModule + DragulaModule, + SettingsModule, + SharedModule, + DashboardModule ], declarations: [ - BoardDisplay, Login, - Settings, - Dashboard + BoardDisplay, + ColumnDisplay, + TaskDisplay ], providers: [ Title, @@ -49,22 +53,14 @@ describe('BoardDisplay', () => { StringsService, ContextMenuService, NotificationsService, - MockBackend, - BaseRequestOptions, { provide: ActivatedRoute, useValue: { url: new BehaviorSubject([{ path: 'boards/1' }]), params: new BehaviorSubject({ id: 1 }) } - }, - { - provide: Http, - useFactory: (backend, options) => new Http(backend, options), - deps: [ MockBackend, BaseRequestOptions ] } - ], - schemas: [ NO_ERRORS_SCHEMA ] + ] }).compileComponents(); }));