diff --git a/client/src/app/posts/post-form/post-form.component.html b/client/src/app/posts/post-form/post-form.component.html index 1593251702284fd0606b506975fc90cc32971379..bbe7faa0d67fc82b46d62ecab24386b9c0a1e992 100644 --- a/client/src/app/posts/post-form/post-form.component.html +++ b/client/src/app/posts/post-form/post-form.component.html @@ -1,20 +1,22 @@ <h3>Lag annonse</h3> -<label for="title">Title</label> -<input id="title" [(ngModel)]="title"> +<label for="title">Tittel</label> +<input id="title" [(ngModel)]="title" (blur)="checkForm()"> -<label for="description">Description</label> - -<input id="description" [(ngModel)]="description"> +<label for="description">Beskrivelse</label> +<input id="description" [(ngModel)]="description" (blur)="checkForm()"> <label for="price">Pris</label> -<input id="price" [(ngModel)]="price"> +<input type="number" id="price" [(ngModel)]="price" (blur)="checkForm()"> <label for="category">Kategori</label> -<select id="category" [(ngModel)]="categoryid"> +<select id="category" [(ngModel)]="categoryid" (change)="checkForm()" > + <option value="0" selected>Velg kategori</option> <option value="1">Antikviteter og Kunst</option> <option value="2">Dyr og Utstyr</option> <option value="3">Elektronikk og Hvitevarer</option> </select> +<p>{{statusMessage}}</p> + <button (click)="publishPost()">Publiser</button> \ No newline at end of file diff --git a/client/src/app/posts/post-form/post-form.component.spec.ts b/client/src/app/posts/post-form/post-form.component.spec.ts index e648ca926cdbca23c95b78f296a8133436097447..7e82535b45a1a2c9cfa883b910a956bd3dc4ff89 100644 --- a/client/src/app/posts/post-form/post-form.component.spec.ts +++ b/client/src/app/posts/post-form/post-form.component.spec.ts @@ -1,16 +1,20 @@ -import { HttpClientModule } from '@angular/common/http'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { FormsModule } from '@angular/forms'; +import { Router } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; import { PostFormComponent } from './post-form.component'; describe('PostFormComponent', () => { let component: PostFormComponent; let fixture: ComponentFixture<PostFormComponent>; + let router: Router; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ PostFormComponent ], - imports: [ HttpClientModule ] + imports: [ HttpClientTestingModule, RouterTestingModule, FormsModule ] }) .compileComponents(); }); @@ -19,23 +23,52 @@ describe('PostFormComponent', () => { fixture = TestBed.createComponent(PostFormComponent); component = fixture.componentInstance; fixture.detectChanges(); + + router = TestBed.inject(Router); }); it('should create', () => { expect(component).toBeTruthy(); }); - it('should serialize Post', () => { - expect(component.serializedPost == "").toBeFalse(); + it('should validate form', () => { + expect(component.checkForm()).toBeFalse(); + expect(component.statusMessage).toBe("Tittelen kan ikke være tom"); + + component.title = "Title"; + expect(component.checkForm()).toBeFalse(); + expect(component.statusMessage).toBe("Beskrivelsen kan ikke være tom"); + + component.description = "Description"; + component.price = -100; + expect(component.checkForm()).toBeFalse(); + expect(component.statusMessage).toBe("Prisen kan ikke være negativ"); - try{ - JSON.stringify(component.serializedPost); - }catch{ - fail("Could not serialize"); - } - }) + component.price = null; + expect(component.checkForm()).toBeFalse(); + expect(component.statusMessage).toBe("Annonsen må ha en pris"); - it('should deserialize Post', () => { - expect(component.deserializedPost.getOwner).toEqual("Admin"); - }) + component.price = 50; + expect(component.checkForm()).toBeFalse(); + expect(component.statusMessage).toBe("Annonsen må ha en kategori"); + + component.categoryid = 2; + expect(component.checkForm()).toBeTrue(); + expect(component.statusMessage).toBe(""); + }); + + it('should stop publishing invalid post', fakeAsync(() => { + component.publishPost(); + expect(component.statusMessage).toBe("Tittelen kan ikke være tom"); + })); + + it('should route after publishing post', () => { + component.title = "Title"; + component.description = "Description"; + component.price = 50; + component.categoryid = 2; + component.publishPost(); + + expect(router.url).toBe('/'); + }); }); diff --git a/client/src/app/posts/post-form/post-form.component.ts b/client/src/app/posts/post-form/post-form.component.ts index ff2ec99079354f33a6070bfaef373820364ce66c..5864463a491b3e0ac1ee152b7db60a0017e1b836 100644 --- a/client/src/app/posts/post-form/post-form.component.ts +++ b/client/src/app/posts/post-form/post-form.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; import { Post } from 'src/app/models/post.model'; import { PostService } from '../post.service'; @@ -12,30 +13,72 @@ export class PostFormComponent implements OnInit { title: string = ""; description: string = ""; price: number = 0; - categoryid: number; + categoryid: number = 0; - constructor(private postService: PostService) { } + statusMessage: string = ""; - ngOnInit(): void { + constructor(private postService: PostService, private router: Router) { } + ngOnInit() { } + /** + * Validates form. + */ + checkForm(): boolean { + if (this.title == "") { + this.setStatusMessage("Tittelen kan ikke være tom"); + return false; + } + if (this.description == "") { + this.setStatusMessage("Beskrivelsen kan ikke være tom"); + return false; + } + if (this.price < 0) { + this.setStatusMessage("Prisen kan ikke være negativ"); + return false; + } + if (this.price == null) { + this.setStatusMessage("Annonsen må ha en pris"); + return false; + } + if (this.categoryid < 1) { + this.setStatusMessage("Annonsen må ha en kategori"); + return false; + } + + this.setStatusMessage(""); + return true; + } + + /** + * Publishes post if it is valid. + */ publishPost() { - let newPost = new Post({ - title: this.title, - description: this.description, - timestamp: new Date(), - owner: "admin", - imageUrl: "", - price: this.price, - categoryid: this.categoryid - }); - console.log(newPost); - this.postService.addPost(newPost).then(status => { - // Flytte til annonsevisning - console.log("Post was added: " + status); - }).catch(error => { - console.log("Error adding post: " + error); - }); + if (this.checkForm()) { + let newPost = new Post({ + title: this.title, + description: this.description, + timestamp: new Date(), + owner: "admin", + imageUrl: "", + price: this.price, + categoryid: this.categoryid + }); + + this.postService.addPost(newPost).then(status => { + console.log("Post was added: " + status); + this.router.navigateByUrl("/"); + }).catch(error => { + console.log("Error adding post: " + error); + }); + } + } + + /** + * Sets a status message. + */ + setStatusMessage(message: string){ + this.statusMessage = message; } } \ No newline at end of file diff --git a/client/src/app/posts/post.service.spec.ts b/client/src/app/posts/post.service.spec.ts index 56f231326ed3d7fb9ef217163c8354630049346c..24c073bf45c74c0c777218e4220d8f29d1f7179b 100644 --- a/client/src/app/posts/post.service.spec.ts +++ b/client/src/app/posts/post.service.spec.ts @@ -1,20 +1,128 @@ -import { HttpClientModule } from '@angular/common/http'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { TestBed } from '@angular/core/testing'; +import { Post } from '../models/post.model'; import { PostService } from './post.service'; describe('PostService', () => { let service: PostService; + let httpMock: HttpTestingController; beforeEach(() => { TestBed.configureTestingModule({ - imports: [ HttpClientModule ] + imports: [ HttpClientTestingModule ] }); service = TestBed.inject(PostService); + httpMock = TestBed.inject(HttpTestingController); }); it('should be created', () => { expect(service).toBeTruthy(); }); + + describe('getPost', () => { + it('should get a post', () => { + service.getPost(1).then(post => { + expect(post.getId).toBe(1); + expect(post.getTitle).toBe("Test"); + }).catch(error => { + fail(); + }); + + const req = httpMock.expectOne("api/post/1"); + expect(req.request.method).toBe("GET"); + req.flush({ + data: [{ + id: 1, + title: "Test", + description: "TestDescription", + timestamp: 23947298, + owner: "user", + imageUrl: null, + price: 49, + categoryid: 2 + }] + }); + }); + + it('should reject on invalid post', () => { + service.getPost(2).then(post => { + fail(); + }).catch(error => {}); + + const req = httpMock.expectOne("api/post/2"); + expect(req.request.method).toBe("GET"); + req.flush({ + data: [{ + id: 0, + title: "Test", + description: "TestDescription", + timestamp: 23947298 + }] + }); + }); + + it('should reject on http error', () => { + service.getPost(2).then(post => { + fail(); + }).catch(error => {}); + + const req = httpMock.expectOne("api/post/2"); + expect(req.request.method).toBe("GET"); + req.error(new ErrorEvent("400")); + }); + }); + + describe('addPost', () => { + it('should add a post', () => { + let post = new Post({ + id: 1, + title: "Test", + description: "TestDescription", + timestamp: 23947298, + owner: "user", + imageUrl: null, + price: 49, + categoryid: 2 + }); + + service.addPost(post) + .then(post => {}) + .catch(error => { + fail(); + }); + + const req = httpMock.expectOne("api/post/"); + expect(req.request.method).toBe("POST"); + expect(req.request.body).toEqual(post.serialize()); + req.flush({ + data: [{ + status: "success" + }] + }); + }); + + it('should reject on http error', () => { + let post = new Post({ + id: 1, + title: "Test", + description: "TestDescription", + timestamp: 23947298, + owner: "user", + imageUrl: null, + price: 49, + categoryid: 2 + }); + + service.addPost(post).then(post => { + fail(); + }).catch(error => {}); + + const req = httpMock.expectOne("api/post/"); + expect(req.request.method).toBe("POST"); + expect(req.request.body).toEqual(post.serialize()); + req.error(new ErrorEvent("400")); + }); + }); }); diff --git a/client/src/app/posts/post.service.ts b/client/src/app/posts/post.service.ts index f083213c9fe881ea9e60a671dbacd725a6a0ba64..e476800c7e5c82b55cb3c2c5f4ad54c4bfe784c3 100644 --- a/client/src/app/posts/post.service.ts +++ b/client/src/app/posts/post.service.ts @@ -11,6 +11,9 @@ export class PostService { constructor(private http: HttpClient) { } + /** + * Get post from database by id. + */ getPost(id: number): Promise<Post> { return new Promise<Post>( (resolve, reject) => { @@ -20,6 +23,7 @@ export class PostService { post.deserialize(data.data[0]); if (post.getId == 0) { reject("Could not find Post with id: " + id); + return; } resolve(post); } catch (err: any) { @@ -38,12 +42,14 @@ export class PostService { return this.http.get(this.postUrl + id); } + /** + * Adds post to database. + */ addPost(post: Post): Promise<string> { return new Promise<string>( (resolve, reject) => { this.add_post(post).subscribe((data: any) => { try { - console.log(data); resolve(data.status); } catch (err: any) { reject(err);