diff --git a/project-manager/projects-server/db.json b/project-manager/projects-server/db.json index 04df9ed..51912a3 100644 --- a/project-manager/projects-server/db.json +++ b/project-manager/projects-server/db.json @@ -54,4 +54,4 @@ "change_settings": false } ] -} \ No newline at end of file +} diff --git a/project-manager/src/app/blog/blog-entry/blog-entry.component.spec.ts b/project-manager/src/app/blog/blog-entry/blog-entry.component.spec.ts index 65b55ac..42a4743 100644 --- a/project-manager/src/app/blog/blog-entry/blog-entry.component.spec.ts +++ b/project-manager/src/app/blog/blog-entry/blog-entry.component.spec.ts @@ -58,7 +58,7 @@ describe('Blog Entry Isolated Test', () => { }); -describe('BlogEntryComp -> BlogComp Relational Test', () => { +describe('BlogEntryComp -> BlogComp Dependent Test', () => { it('should remove entry from BlogComponent on "delete" button click', () => { TestBed.configureTestingModule({ diff --git a/project-manager/src/app/blog/blog.component.spec.ts b/project-manager/src/app/blog/blog.component.spec.ts index 18e1b80..fba66a7 100644 --- a/project-manager/src/app/blog/blog.component.spec.ts +++ b/project-manager/src/app/blog/blog.component.spec.ts @@ -1,4 +1,8 @@ import {BlogComponent} from './blog.component' +import {TestBed} from "@angular/core/testing"; +import {RouterTestingModule} from "@angular/router/testing"; +import {BlogEntryComponent} from "./blog-entry/blog-entry.component"; +import {BlogEntry} from "./blog-entry/blog-entry"; describe('Blog Component Isolated Test', () => { @@ -45,3 +49,39 @@ describe('Blog Component Isolated Test', () => { }).not.toBe(latestId); }); }); + + +describe('Blog Component Integration Form Test', () => { + + let fixture; + let instance; + let element; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule.withRoutes([])], + declarations: [BlogEntryComponent, BlogComponent] + }); + fixture = TestBed.createComponent(BlogComponent); + instance = fixture.componentInstance; + element = fixture.nativeElement; + }); + + it('should call create method with provided field input', () => { + const spy = spyOn(instance, 'createBlogEntry'); + + const testTitle = 'testTitle'; + const testImage = 'imageUrl'; + const testText = 'testText'; + + // beachten : value würde immer funktionieren um Werte als Variable + // übertragbar einzutragen, textContent hingegen nur bei textarea + element.querySelector('div /deep/ div > #title').value = testTitle; + element.querySelector('div /deep/ div > #image').value = testImage; + element.querySelector('div /deep/ div > #text').textContent = testText; + element.querySelector('div /deep/ div > button').click(); + + expect(spy).toHaveBeenCalledWith(testTitle, testImage, testText); + }) + +}); diff --git a/project-manager/src/app/model-driven-form/model-driven-form.component.spec.ts b/project-manager/src/app/model-driven-form/model-driven-form.component.spec.ts index dea929f..5d0c72b 100644 --- a/project-manager/src/app/model-driven-form/model-driven-form.component.spec.ts +++ b/project-manager/src/app/model-driven-form/model-driven-form.component.spec.ts @@ -61,7 +61,7 @@ describe('Model Driven Form', () => { // helper method // check for error object according to given input value - function expectErrorOnInput(formControl, inputString, expectedErrorObject?, expectedErrorAttribute?) { + let expectErrorOnInput = (formControl, inputString, expectedErrorObject?, expectedErrorAttribute?) => { formControl.setValue(inputString); if (expectedErrorObject && expectedErrorAttribute) { expect(formControl.errors[expectedErrorAttribute]).toEqual(expectedErrorObject); diff --git a/project-manager/src/app/services/login-service/login-service.ts b/project-manager/src/app/services/login-service/login-service.ts index 7cac02f..0cacfe0 100644 --- a/project-manager/src/app/services/login-service/login-service.ts +++ b/project-manager/src/app/services/login-service/login-service.ts @@ -19,8 +19,6 @@ export class LoginService { users$: Observable; - results$: User[]; - constructor(@Optional() @Inject(AUTH_ENABLED) public authEnabled = false, private http: Http, private userStore: UserStore, @Inject(SOCKET_IO) socketIO) { this.users$ = userStore.items$; @@ -28,13 +26,15 @@ export class LoginService { } getUser(name: string): User[] { - this.http.get(BASE_URL + "?name=" + name).subscribe(result => this.results$ = result.json()); - return this.results$; + let results; + this.http.get(BASE_URL + "?name=" + name).subscribe(result => results = result.json()); + return results; } login(name, password) { - if (this.getUser(name)) { - let user = this.results$[0]; + let results = this.getUser(name); + if (results) { + let user = results[0]; let passMd5 = Md5.hashStr(password); if (user && user.password === passMd5) { localStorage.setItem(CURRENT_USER, JSON.stringify(user)); diff --git a/project-manager/src/app/services/login-service/login.service.spec.ts b/project-manager/src/app/services/login-service/login.service.spec.ts new file mode 100644 index 0000000..d01cc64 --- /dev/null +++ b/project-manager/src/app/services/login-service/login.service.spec.ts @@ -0,0 +1,50 @@ +import {MockBackend} from "@angular/http/testing"; +import {inject, TestBed} from "@angular/core/testing"; +import {mockIO} from "../../mocks/mock-socket"; +import {SOCKET_IO} from "../../app.tokens"; +import {BaseRequestOptions, ConnectionBackend, Http, RequestMethod, ResponseOptions} from "@angular/http"; +import {LoginService} from "./login-service"; +import {UserStore} from "../stores/user.store"; + + +describe('Login-Service', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + LoginService, + UserStore, + {provide: SOCKET_IO, useValue: mockIO}, + BaseRequestOptions, + MockBackend, + { + provide: Http, useFactory: (mockBackend: ConnectionBackend, + defaultOptions: BaseRequestOptions) => { + return new Http(mockBackend, defaultOptions); + }, deps: [MockBackend, BaseRequestOptions] + }, + ] + }); + }); + + let loginService: LoginService; + let userStore: UserStore; + let mockBackend: MockBackend; + + beforeEach(inject([LoginService, UserStore, MockBackend], + (_loginService, _userStore, _mockBackend) => { + loginService = _loginService; + userStore = _userStore; + mockBackend = _mockBackend; + }) + ); + + it('should trigger a HTTP-GET and receive Task', (() => { + console.log(userStore.items$); + mockBackend.connections.subscribe(connection => { + const expectedUrl = 'http://localhost:3000/api/users/'; + expect(connection.request.url).toBe(expectedUrl); + expect(connection.request.method).toBe(RequestMethod.Get); + }); + })); + +}); diff --git a/project-manager/src/app/services/stores/user.store.ts b/project-manager/src/app/services/stores/user.store.ts index 1cf06c2..dea3677 100644 --- a/project-manager/src/app/services/stores/user.store.ts +++ b/project-manager/src/app/services/stores/user.store.ts @@ -1,8 +1,38 @@ import {BehaviorSubject} from 'rxjs/BehaviorSubject'; import {User} from '../../models/model-interfaces'; +import {ADD, EDIT, LOAD, REMOVE} from "./generic-store"; export class UserStore { + private users: User[] = []; + items$ = new BehaviorSubject([]); + dispatch(action) { + console.log("dispatched") + this.users = this._reduce(this.users, action); + this.items$.next(this.users); + } + + _reduce(tasks: User[], action) { + switch (action.type) { + case LOAD: + return [...action.data]; + case ADD: + return [...tasks, action.data]; + case EDIT: + return tasks.map(task => { + const editedTask = action.data; + if (task.id !== editedTask.id) { + return task; + } + return editedTask; + }); + case REMOVE: + return tasks.filter(task => task.id !== action.data.id); + default: + return tasks; + } + } + } diff --git a/project-manager/src/app/services/task-service/task.service.spec.ts b/project-manager/src/app/services/task-service/task.service.spec.ts new file mode 100644 index 0000000..ad87a16 --- /dev/null +++ b/project-manager/src/app/services/task-service/task.service.spec.ts @@ -0,0 +1,90 @@ +import {TestBed, inject, fakeAsync} from '@angular/core/testing'; +import { BaseRequestOptions, Http, ConnectionBackend, Response, ResponseOptions, RequestMethod } from '@angular/http'; +import {TaskService} from './task.service'; +import {MockBackend} from '@angular/http/testing'; +import {TaskStore} from '../stores/task.store'; +import {SOCKET_IO} from '../../app.tokens'; +import {mockIO} from '../../mocks/mock-socket'; + +describe('Task-Service', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + TaskService, + TaskStore, + {provide: SOCKET_IO, useValue: mockIO}, + BaseRequestOptions, + MockBackend, + {provide: Http, useFactory: (mockBackend: ConnectionBackend, + defaultOptions: BaseRequestOptions) => { + return new Http(mockBackend, defaultOptions); + }, deps: [MockBackend, BaseRequestOptions]}, + ] + }); + }); + + let taskService: TaskService; + let taskStore: TaskStore; + let mockBackend: MockBackend; + + beforeEach(inject([TaskService, TaskStore, MockBackend], + (_taskService, _taskStore, _mockBackend) => { + taskService = _taskService; + taskStore = _taskStore; + mockBackend = _mockBackend; + }) + ); + + const saveTask = (task, expectedUrl = null, expectedMethod = null) => { + mockBackend.connections.subscribe(connection => { + if (expectedUrl) { + expect(connection.request.url).toBe(expectedUrl); + } + if (expectedMethod) { + expect(connection.request.method).toBe(expectedMethod); + } + const response = new ResponseOptions({body: JSON.stringify(task)}); + connection.mockRespond(new Response(response)); + }); + taskService.saveTask(task).subscribe(); + }; + + it('should trigger a HTTP-POST for new Tasks', (() => { + const task = {title: 'Task 1'}; + mockBackend.connections.subscribe(connection => { + const expectedUrl = 'http://localhost:3000/api/tasks/'; + expect(connection.request.url).toBe(expectedUrl); + expect(connection.request.method).toBe(RequestMethod.Post); + const response = new ResponseOptions({body: JSON.stringify(task)}); + connection.mockRespond(new Response(response)); + }); + taskService.saveTask(task).subscribe(); + })); + + it('should trigger a HTTP-POST for new Tasks', (() => { + const task = {title: 'Task 1'}; + saveTask(task, 'http://localhost:3000/api/tasks/', RequestMethod.Post); + })); + + it('should do a HTTP-Put for existing Tasks', (() => { + const task = {id: 1, title: 'Existing Task'}; + saveTask(task, 'http://localhost:3000/api/tasks/1', RequestMethod.Put); + })); + + it('should add the Task to the store', (() => { + const spy = spyOn(taskStore, 'dispatch').and.callThrough(); + saveTask({title: 'Task 1'}); + const dispatchedAction = spy.calls.mostRecent().args[0]; + expect(dispatchedAction.type).toEqual('ADD'); + expect(dispatchedAction.data.title).toEqual('Task 1'); + })); + + it('should save the Task in store', (() => { + const spy = spyOn(taskStore, 'dispatch').and.callThrough(); + saveTask({id: 1, title: 'Task 1'}); + const dispatchedAction = spy.calls.mostRecent().args[0]; + expect(dispatchedAction.type).toEqual('EDIT'); + expect(dispatchedAction.data.title).toEqual('Task 1'); + })); + +}); \ No newline at end of file diff --git a/project-manager/src/app/tasks/edit-task/edit-task.component.spec.ts b/project-manager/src/app/tasks/edit-task/edit-task.component.spec.ts index e69de29..052e280 100644 --- a/project-manager/src/app/tasks/edit-task/edit-task.component.spec.ts +++ b/project-manager/src/app/tasks/edit-task/edit-task.component.spec.ts @@ -0,0 +1,100 @@ +import {TaskService} from '../../services/task-service/task.service'; + +import {RouterTestingModule} from '@angular/router/testing'; + +import {BehaviorSubject} from 'rxjs/BehaviorSubject'; + +import {TestBed, inject} from '@angular/core/testing'; +import {EditTaskComponent} from './edit-task.component'; +import {ShowErrorComponent} from '../../show-error/show-error.component'; +import {APPLICATION_VALIDATORS} from '../../models/app-validators'; +import {ActivatedRoute, Router} from '@angular/router'; +import {fakeAsync, tick} from '@angular/core/testing'; +import {Title} from '@angular/platform-browser'; +import {Component} from '@angular/core'; +import {ReactiveFormsModule, FormsModule} from '@angular/forms'; +import {MockTaskService} from '../../mocks/mock-task-service'; + +@Component({ + template: '' +}) +class TestComponent { +} + +describe('EditTask Component', () => { + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [FormsModule, RouterTestingModule.withRoutes([ + {path: 'new', component: EditTaskComponent}, + {path: 'edit/:id', component: EditTaskComponent} + ], + )], + declarations: [EditTaskComponent, ShowErrorComponent, APPLICATION_VALIDATORS, TestComponent], + providers: [ + Title, + {provide: TaskService, useClass: MockTaskService}, + ] + }); + }); + + let taskService: TaskService; + beforeEach(inject([TaskService], (_taskService) => { + taskService = _taskService; + })); + + it('should load the correct task in Edit-Mode', fakeAsync(() => { + const fixture = TestBed.createComponent(EditTaskComponent); + const route = TestBed.get(ActivatedRoute); + (route.params).next({id: '42'}); + + const element = fixture.nativeElement; + + const spy = spyOn(taskService, 'getTask'); + const fakeTask = {title: 'Task1', assignee: {name: 'John'}}; + spy.and.returnValue(new BehaviorSubject(fakeTask)); + + fixture.autoDetectChanges(true); + fixture.whenStable().then(() => { + tick(); + expect(spy).toHaveBeenCalledWith('42'); + const titleInput = element.querySelector('#title'); + expect(titleInput.value).toBe(fakeTask.title); + + const assigneeInput = element.querySelector('#assignee_name'); + expect(assigneeInput.value).toBe(fakeTask.assignee.name); + }); + })); + + + it('should load the correct task (with router)', fakeAsync(() => { + const fixture = TestBed.createComponent(TestComponent); + const router = TestBed.get(Router); + router.navigateByUrl('edit/42'); + + const spy = spyOn(taskService, 'getTask'); + const fakeTask = {title: 'Task1', assignee: {name: 'John'}}; + spy.and.returnValue(new BehaviorSubject(fakeTask)); + fixture.whenStable().then(() => { + tick(); + expect(spy).toHaveBeenCalledWith('42'); + const titleInput = fixture.nativeElement.querySelector('#title'); + expect(titleInput.value).toBe(fakeTask.title); + }); + })); + + it('should work without passing URL-Parameter', fakeAsync(() => { + const fixture = TestBed.createComponent(TestComponent); + const router = TestBed.get(Router); + router.navigateByUrl('new'); + const spy = spyOn(taskService, 'getTask'); + fixture.whenStable().then(() => { + tick(); + expect(spy).not.toHaveBeenCalled(); + const titleInput = fixture.nativeElement.querySelector('#title'); + expect(titleInput.value).toBe(''); + }); + })); + + +});