Moving tests over to new framework

This commit is contained in:
Matthew Ross 2018-03-21 17:11:02 -04:00
parent d8d385e2a2
commit 80cbbba967
9 changed files with 123 additions and 134 deletions

View File

@ -95,6 +95,8 @@ Developing on TaskBoard is pretty simple too.
2. Run `git checkout dev` to work on the `dev` branch 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` 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) 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 #### Unit Tests

12
package-lock.json generated
View File

@ -6122,9 +6122,9 @@
} }
}, },
"jasmine-core": { "jasmine-core": {
"version": "3.1.0", "version": "2.99.1",
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.1.0.tgz", "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.99.1.tgz",
"integrity": "sha1-pHheE11d9lAk38kiSVPfWFvSdmw=", "integrity": "sha1-5kAN8ea1bhMLYcS80JPap/boyhU=",
"dev": true "dev": true
}, },
"jasmine-spec-reporter": { "jasmine-spec-reporter": {
@ -6387,9 +6387,9 @@
"dev": true "dev": true
}, },
"karma-jasmine-html-reporter": { "karma-jasmine-html-reporter": {
"version": "1.0.0", "version": "0.2.2",
"resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.0.0.tgz", "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz",
"integrity": "sha512-SN9R/Pl9cY40yLlc7FkTcfswUr19M6ZZ25eM8X5wtZ0gvp0gneWZbe5lPYcer/Yrbz0D6QUiTSJaEzr3KBPvSg==", "integrity": "sha1-SKjl7xiAdhfuK14zwRlMNbQ5Ukw=",
"dev": true, "dev": true,
"requires": { "requires": {
"karma-jasmine": "1.1.1" "karma-jasmine": "1.1.1"

View File

@ -65,13 +65,13 @@
"bourbon": "5.0.0", "bourbon": "5.0.0",
"bourbon-neat": "1.9.0", "bourbon-neat": "1.9.0",
"codelyzer": "^4.2.1", "codelyzer": "^4.2.1",
"jasmine-core": "^3.1.0", "jasmine-core": "^2.8.0",
"jasmine-spec-reporter": "~4.2.1", "jasmine-spec-reporter": "~4.2.1",
"karma": "~2.0.0", "karma": "~2.0.0",
"karma-chrome-launcher": "~2.2.0", "karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "^1.4.2", "karma-coverage-istanbul-reporter": "^1.4.2",
"karma-jasmine": "~1.1.1", "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", "karma-phantomjs-launcher": "^1.0.4",
"protractor": "~5.3.0", "protractor": "~5.3.0",
"ts-node": "~5.0.1", "ts-node": "~5.0.1",

View File

@ -44,11 +44,8 @@ export class ApiInterceptor implements HttpInterceptor {
localStorage.setItem(this.JWT_KEY, response.data[0]); localStorage.setItem(this.JWT_KEY, response.data[0]);
} }
}, (err: any) => { }, (err: any) => {
if (!(err instanceof HttpErrorResponse)) { if ((err instanceof HttpErrorResponse) &&
return; (err.status === 401 || err.status === 400) &&
}
if ((err.status === 401 || err.status === 400) &&
(err.url + '').indexOf('login') === -1) { (err.url + '').indexOf('login') === -1) {
this.router.navigate(['']); this.router.navigate(['']);
localStorage.removeItem(this.JWT_KEY); localStorage.removeItem(this.JWT_KEY);

View File

@ -55,6 +55,8 @@ export class BoardDisplay implements OnInit {
this.userFilter = null; this.userFilter = null;
this.categoryFilter = null; this.categoryFilter = null;
this.activeBoard = new Board();
this.loading = true; this.loading = true;
stringsService.stringsChanged.subscribe(newStrings => { stringsService.stringsChanged.subscribe(newStrings => {

View File

@ -28,7 +28,7 @@ export class TopNav {
this.version = constants.VERSION; this.version = constants.VERSION;
authService.userChanged authService.userChanged
.subscribe(user => this.username = user.username); .subscribe(user => this.username = user ? user.username : '');
stringsService.stringsChanged.subscribe(newStrings => { stringsService.stringsChanged.subscribe(newStrings => {
this.strings = newStrings; this.strings = newStrings;

View File

@ -1,110 +1,106 @@
import { TestBed, inject } from '@angular/core/testing';
import { import {
HttpModule, HttpClient,
XHRBackend, HttpErrorResponse,
CookieXSRFStrategy, HTTP_INTERCEPTORS
ResponseOptions, } from '@angular/common/http';
BrowserXhr, import {
RequestOptions HttpClientTestingModule,
} from '@angular/http'; HttpTestingController
} from '@angular/common/http/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { Observable } from 'rxjs/Observable'; import { AuthService } from '../../src/app/shared/auth/auth.service';
import 'rxjs/add/operator/catch'; 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(() => { beforeEach(() => {
const backend = new XHRBackend(new BrowserXhr(), TestBed.configureTestingModule({
new ResponseOptions(), imports: [
new CookieXSRFStrategy()), HttpClientTestingModule,
requestOptions = new RequestOptions(); RouterTestingModule.withRoutes([])
],
apiHttp = new ApiHttp(backend, requestOptions, <any>routerMock); providers: [
{
provide: AuthService,
useValue: mockAuthService
},
{
provide: HTTP_INTERCEPTORS,
useClass: ApiInterceptor,
multi: true
}
]
});
}); });
it('provides API_HTTP_PROVIDERS', () => { afterEach(inject([HttpTestingController], (httpMock: HttpTestingController) => {
expect(API_HTTP_PROVIDERS).toEqual(jasmine.any(Array)); httpMock.verify();
}); }));
it('provides a factory method', () => { it('adds Content-Type header',
const actual = apiHttpFactory(null, null, null); 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', () => { req.flush({});
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']
};
} }
}; )
);
apiHttp.intercept(Observable.of(response)) it('adds Authorization header when JWT is present',
.map(response => { inject([HttpClient, HttpTestingController],
expect(localStorage.getItem('taskboard.jwt')) (http: HttpClient, httpMock: HttpTestingController) => {
.toEqual('testjwt'); localStorage.setItem('taskboard.jwt', 'fake');
});
apiHttp.intercept(Observable.throw(null, <any>response)) http.post('', {}).subscribe(response => {
.catch((err, caught) => { expect(response).toBeTruthy();
expect(localStorage.getItem('taskboard.jwt')) });
.toEqual('');
});
});
it('handles valid responses', () => { const req = httpMock.expectOne(req =>
apiHttp.handleResponse({ json: () => ({ data: [ 'jwt' ] }) }); 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', () => { it('handles errors and clears the JWT',
var error = { inject([HttpClient, HttpTestingController],
status: 401, (http: HttpClient, httpMock: HttpTestingController) => {
url: '' 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);
});
}); });

View File

@ -1,25 +1,21 @@
import { TestBed, async } from '@angular/core/testing'; import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing'; import { RouterTestingModule } from '@angular/router/testing';
import { HttpModule } from '@angular/http';
import { AppComponent } from '../../src/app/app.component'; import { AppComponent } from '../../src/app/app.component';
import { import {
Notifications,
NotificationsService, NotificationsService,
StringsService StringsService
} from '../../src/app/shared/index'; } from '../../src/app/shared/services';
import { SharedModule } from '../../src/app/shared/shared.module';
describe('AppComponent', () => { describe('AppComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
RouterTestingModule, RouterTestingModule,
HttpModule SharedModule
],
declarations: [
AppComponent,
Notifications
], ],
declarations: [ AppComponent ],
providers: [ providers: [
StringsService, StringsService,
NotificationsService NotificationsService

View File

@ -1,13 +1,10 @@
import { async, TestBed, ComponentFixture } from '@angular/core/testing' 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 { RouterTestingModule } from '@angular/router/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { FormsModule } from '@angular/forms';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { Title } from '@angular/platform-browser'; 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 { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { DragulaModule } from 'ng2-dragula/ng2-dragula'; import { DragulaModule } from 'ng2-dragula/ng2-dragula';
@ -17,12 +14,15 @@ import {
Constants, Constants,
ContextMenuService, ContextMenuService,
NotificationsService NotificationsService
} from '../../../src/app/shared/index'; } from '../../../src/app/shared/services';
import { Login } from '../../../src/app/login/login.component'; import { Login } from '../../../src/app/login/login.component';
import { Settings } from '../../../src/app/settings/index'; import { SettingsModule } from '../../../src/app/settings/settings.module';
import { Dashboard } from '../../../src/app/dashboard/index'; import { SharedModule } from '../../../src/app/shared/shared.module';
import { BoardService, BoardDisplay } from '../../../src/app/board/index'; import { DashboardModule } from '../../../src/app/dashboard/dashboard.module';
import { ROUTES } from '../../../src/app/app.routes'; 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', () => { describe('BoardDisplay', () => {
let component: BoardDisplay, let component: BoardDisplay,
@ -31,15 +31,19 @@ describe('BoardDisplay', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
RouterTestingModule.withRoutes(ROUTES), RouterTestingModule,
HttpClientTestingModule,
FormsModule, FormsModule,
DragulaModule DragulaModule,
SettingsModule,
SharedModule,
DashboardModule
], ],
declarations: [ declarations: [
BoardDisplay,
Login, Login,
Settings, BoardDisplay,
Dashboard ColumnDisplay,
TaskDisplay
], ],
providers: [ providers: [
Title, Title,
@ -49,22 +53,14 @@ describe('BoardDisplay', () => {
StringsService, StringsService,
ContextMenuService, ContextMenuService,
NotificationsService, NotificationsService,
MockBackend,
BaseRequestOptions,
{ {
provide: ActivatedRoute, provide: ActivatedRoute,
useValue: { useValue: {
url: new BehaviorSubject([{ path: 'boards/1' }]), url: new BehaviorSubject([{ path: 'boards/1' }]),
params: new BehaviorSubject({ id: 1 }) params: new BehaviorSubject({ id: 1 })
} }
},
{
provide: Http,
useFactory: (backend, options) => new Http(backend, options),
deps: [ MockBackend, BaseRequestOptions ]
} }
], ]
schemas: [ NO_ERRORS_SCHEMA ]
}).compileComponents(); }).compileComponents();
})); }));