diff --git a/client/src/app/posts/post-details/post-details.component.html b/client/src/app/posts/post-details/post-details.component.html index 82d2c974c54aecb3fb5fa36438205f2b5edb0f36..2a00591fc4bfa0413c496c35a26914885f27de45 100644 --- a/client/src/app/posts/post-details/post-details.component.html +++ b/client/src/app/posts/post-details/post-details.component.html @@ -5,3 +5,5 @@ <br> <p>Publiseringstidspunkt: {{post.getTimestamp}}</p> <p>Eier: {{post.getOwner}}</p> + +<app-button text="Slett annonse" (click)="deletePost()"></app-button> diff --git a/client/src/app/posts/post-details/post-details.component.spec.ts b/client/src/app/posts/post-details/post-details.component.spec.ts index 90f92b94c0e46f064508801e1d57403a5fb1c26d..42603d5568ce4350023a4129d448992301039b81 100644 --- a/client/src/app/posts/post-details/post-details.component.spec.ts +++ b/client/src/app/posts/post-details/post-details.component.spec.ts @@ -1,14 +1,49 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { Post } from 'src/app/models/post.model'; +import { PostService } from '../post.service'; import { PostDetailsComponent } from './post-details.component'; describe('PostDetailsComponent', () => { let component: PostDetailsComponent; let fixture: ComponentFixture<PostDetailsComponent>; + let mockPostService; beforeEach(async () => { + // PostService mock setup + mockPostService = jasmine.createSpyObj(['getPost', 'deletePost']); + mockPostService.getPost.and.returnValue( + new Promise<Post>( + (resolve) => { + resolve(new Post({ + id: 5, + title: "Test", + description: "TestDescription", + timestamp: 23947298, + owner: "user", + imageUrl: null, + price: 49, + categoryid: 2 + })); + }) + ); + mockPostService.deletePost.and.returnValue( + new Promise<any>( + (resolve) => { + resolve({data: []}) + }) + ); + await TestBed.configureTestingModule({ - declarations: [ PostDetailsComponent ] + declarations: [ PostDetailsComponent ], + imports: [ HttpClientTestingModule, RouterTestingModule ], + providers: [ + { provide: ActivatedRoute, useValue: { snapshot: {params: {id: 5}}}}, + { provide: PostService, useValue: mockPostService } + ] }) .compileComponents(); }); @@ -22,4 +57,29 @@ describe('PostDetailsComponent', () => { it('should create', () => { expect(component).toBeTruthy(); }); + + it('should get post with id from url parameter', async () => { + // Waits for ngOnInit and checks that we get post + fixture.whenStable().then(() => { + expect(mockPostService.getPost).toHaveBeenCalledWith(5); + expect(component.post).toEqual(new Post({ + id: 5, + title: "Test", + description: "TestDescription", + timestamp: 23947298, + owner: "user", + imageUrl: null, + price: 49, + categoryid: 2 + })); + }); + }); + + it('should delete post with id', async () => { + // Waits for ngOnInit and checks that we can delete post + fixture.whenStable().then(() => { + component.deletePost(); + expect(mockPostService.deletePost).toHaveBeenCalledWith(5); + }); + }); }); diff --git a/client/src/app/posts/post-details/post-details.component.ts b/client/src/app/posts/post-details/post-details.component.ts index 72dd9a89f30421c3ba8c25a6a8feeeb687ee87e6..e8517a7ffb776e9017b938d9e49510a383744039 100644 --- a/client/src/app/posts/post-details/post-details.component.ts +++ b/client/src/app/posts/post-details/post-details.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { Post } from 'src/app/models/post.model'; import { PostService } from '../post.service'; -import { ActivatedRoute } from '@angular/router' +import { ActivatedRoute, Router } from '@angular/router' @Component({ selector: 'app-post-details', @@ -12,19 +12,27 @@ export class PostDetailsComponent implements OnInit { post: Post = new Post(); - constructor(private postService: PostService, private activatedRoute: ActivatedRoute) { } + constructor(private postService: PostService, private activatedRoute: ActivatedRoute, private router: Router) { } ngOnInit(): void { // Gets id parameter from URL - this.activatedRoute.params.subscribe(params => { - const id = params["id"]; + const id = this.activatedRoute.snapshot.params["id"]; - // Gets Post with id from database - this.postService.getPost(id).then(post => { - this.post = post; - }).catch(error => { - console.log(error); - }); + // Gets Post with id from database + this.postService.getPost(id).then(post => { + this.post = post; + }).catch(error => { + console.log(error); + }); + } + + deletePost() { + // Deletes post in database and navigates to post list + this.postService.deletePost(this.post.getId).then(data => { + console.log("Successfully deleted post: " + this.post.getId); + this.router.navigateByUrl("/annonse"); + }).catch(error => { + console.log(error); }); } } 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 31c4d5b8657d2bc3ea4af3df65d67e7624b33d83..355179e798b86258795160602ef64ddb4409b167 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,5 +1,5 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; import { FormsModule } from '@angular/forms'; import { Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; diff --git a/client/src/app/posts/post-list/post-list.component.spec.ts b/client/src/app/posts/post-list/post-list.component.spec.ts index 2d39c24944619ae92c499e826d476de93f005a4c..464c09fe2d5d9261f2ae08d144b4a0c5063c314c 100644 --- a/client/src/app/posts/post-list/post-list.component.spec.ts +++ b/client/src/app/posts/post-list/post-list.component.spec.ts @@ -1,3 +1,4 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PostListComponent } from './post-list.component'; @@ -8,7 +9,8 @@ describe('PostListComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ PostListComponent ] + declarations: [ PostListComponent ], + imports: [ HttpClientTestingModule ] }) .compileComponents(); }); diff --git a/client/src/app/posts/post-thumbnail/post-thumbnail.component.spec.ts b/client/src/app/posts/post-thumbnail/post-thumbnail.component.spec.ts index ea7282db0c1f44a777d0bbf58a7ca927de9d6fd7..04d5c006c2140f6b05b4069b18dfac01c71f285d 100644 --- a/client/src/app/posts/post-thumbnail/post-thumbnail.component.spec.ts +++ b/client/src/app/posts/post-thumbnail/post-thumbnail.component.spec.ts @@ -1,4 +1,5 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; import { PostThumbnailComponent } from './post-thumbnail.component'; @@ -8,7 +9,8 @@ describe('PostThumbnailComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ PostThumbnailComponent ] + declarations: [ PostThumbnailComponent ], + imports: [ RouterTestingModule ] }) .compileComponents(); }); diff --git a/client/src/app/posts/post.service.spec.ts b/client/src/app/posts/post.service.spec.ts index 0439fbe95c5eafe960d20c4e678c5672ab71c278..144bdc0c1ad6beb7de01dc2fec5e7b34db187d06 100644 --- a/client/src/app/posts/post.service.spec.ts +++ b/client/src/app/posts/post.service.spec.ts @@ -176,5 +176,37 @@ describe('PostService', () => { req.error(new ErrorEvent("400")); }); }); + + describe('deletePost', () => { + it('should delete post', () => { + + // Deletes post with id = 2 + service.deletePost(2) + .then(data => {}) + .catch(error => { + fail(); + }); + + // Mocks and checks HTTP request + const req = httpMock.expectOne("api/post/2"); + expect(req.request.method).toBe("DELETE"); + req.flush({ + data: [] + }); + }); + + it('should reject on http error', () => { + + // Deletes post with id = 5, should catch HTTP error + service.deletePost(5).then(data => { + fail(); + }).catch(error => {}); + + // Mocks and checks HTTP request + const req = httpMock.expectOne("api/post/5"); + expect(req.request.method).toBe("DELETE"); + req.error(new ErrorEvent("400")); + }); + }); }); diff --git a/client/src/app/posts/post.service.ts b/client/src/app/posts/post.service.ts index e28949586aaf3f30b7c0a8441aae4cc882aa2374..0ef4e10dac2d271fcc99a5dc3e260126d88e0b84 100644 --- a/client/src/app/posts/post.service.ts +++ b/client/src/app/posts/post.service.ts @@ -138,4 +138,29 @@ export class PostService { private get_all_categories() { return this.http.get(this.categoryUrl); } + + /** + * Delete post in database by id. + */ + deletePost(id: number): Promise<any> { + return new Promise<any>( + (resolve, reject) => { + this.delete_post(id).subscribe((data: any) => { + try { + resolve(data); + } catch (err: any) { + reject(err); + } + }, + (err: any) => { + console.log(err.message); + reject(err); + }); + } + ); + } + + private delete_post(id: number) { + return this.http.delete(this.postUrl + id); + } } diff --git a/server/src/controllers/postController/index.ts b/server/src/controllers/postController/index.ts index ed5a95f2e7c0d5568a7b6d1854194d7fe8210f9c..f9c4eb3deb692a472687cbe328b91851fcff84ed 100644 --- a/server/src/controllers/postController/index.ts +++ b/server/src/controllers/postController/index.ts @@ -16,7 +16,7 @@ router.route("/").post(async (request: Request, response: Response) => { description, timestamp, owner, - category, + categoryid, imageUrl, } = request.body; try { @@ -25,12 +25,12 @@ router.route("/").post(async (request: Request, response: Response) => { description: description, timestamp: timestamp, owner: owner, - category: category, + categoryid: categoryid, imageUrl: imageUrl, }; if (Object.values(post).filter((p) => p == undefined).length > 0) return response.status(500).send("Error"); - const input = `INSERT INTO post(title, description, timestamp, owner, category, imageUrl) VALUES (?,?,?,?,?,?)`; + const input = `INSERT INTO post(title, description, timestamp, owner, categoryid, imageUrl) VALUES (?,?,?,?,?,?)`; return response.status(200).json(await query(input, Object.values(post))); } catch (error) { return response.status(400).send("Bad Request"); @@ -85,7 +85,7 @@ router.route("/:id").delete(async (request: Request, response: Response) => { try { response .status(200) - .json(await query("SELECT * FROM post WHERE id=?;", [postId])); + .json(await query("DELETE FROM post WHERE id=?;", [postId])); } catch (error) { response.status(400).send("Bad Request"); }