Skip to content

Commit

Permalink
added API test with JEST
Browse files Browse the repository at this point in the history
  • Loading branch information
Pacific NDAHIRO authored and Pacific NDAHIRO committed Mar 1, 2024
1 parent 6d20f60 commit 56ddb9b
Show file tree
Hide file tree
Showing 13 changed files with 5,078 additions and 791 deletions.
Binary file added .DS_Store
Binary file not shown.
8 changes: 8 additions & 0 deletions jest.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"preset": "ts-jest",
"testEnvironment": "node",
"testPathIgnorePatterns": ["/node_modules/", "/dist/"],
"transform": {
"^.+\\.(ts|tsx)?$": "ts-jest"
}
}
5,472 changes: 4,692 additions & 780 deletions package-lock.json

Large diffs are not rendered by default.

16 changes: 12 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,26 @@
"version": "1.0.0",
"description": "Backend for my-brand application",
"main": "src/index.ts",

"scripts": {
"build": "tsc",
"start": "npm run build && node build/index.js",
"start": "node src/index.ts",
"dev": "nodemon -- npx ts-node --esm src/index.ts",
"test": "echo \"Error: no test specified\" && exit 1"
"test": "jest"
},
"author": "Ndahiro Pacific",
"or": "Ndahiro Pacific",
"license": "ISC",
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jest": "^29.5.12",
"@types/jsonwebtoken": "^9.0.5",
"@types/node": "^20.11.20",
"jest": "^29.7.0",
"ts-jest": "^29.1.2",
"typescript": "^5.3.3"
},
"dependencies": {
"@types/swagger-ui-express": "^4.1.6",
"bcrypt": "^5.1.1",
"body-parser": "^1.20.2",
"cloudinary": "^2.0.1",
Expand All @@ -28,7 +31,12 @@
"express": "^4.18.2",
"joi": "^17.12.2",
"jsonwebtoken": "^9.0.2",
"lowdb": "^7.0.1",
"mongoose": "^8.2.0",
"supertest": "^6.3.4",
"swagger-autogen": "^2.23.7",
"swagger-jsdoc": "^6.2.8",
"swagger-ui-express": "^5.0.0",
"ts-node": "^10.9.2"
},
"nodemonConfig": {
Expand Down
Binary file added src/.DS_Store
Binary file not shown.
14 changes: 10 additions & 4 deletions src/controllers/blogController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ import { UserInterface } from "../middlewares/isAuthenticated";
import { createSlug } from "../utils/strings";
import { uploadToCloudinary, uploadOptions } from "../utils/uploads";


// CONFIGURE DOTENV
dotenv.config();

// LOAD ENV VARIABLES
const { JWT_SECRET } = process.env;

class blogController {
static getBlogById(req: Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>) {
throw new Error("Method not implemented.");
}
static getAllBlogs(req: Request<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>, res: Response<any, Record<string, any>>) {
throw new Error("Method not implemented.");
}
// CREATE BLOG
static async createBlog(req: UserInterface, res: Response) {
try {
Expand Down Expand Up @@ -58,9 +65,9 @@ class blogController {
return res.status(500).json({ message: error.message });
}
}

// LIST BLOGS
static async listBlogs(req: Request, res: Response) {

static async listBlogs(req: Request, res: Response): Promise<Response<any, Record<string, any>>> {
try {
const blogs = await Blog.find().sort({ createAt: -1 }).populate({
path: "userId",
Expand Down Expand Up @@ -311,5 +318,4 @@ class blogController {
}

}

export default blogController;
44 changes: 42 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,21 @@ import routes from './routes';
// CONFIGURE DOTENV
dotenv.config();

// LOAD ENV VARIABLES
// LOAD ENV VARIABLES
const { PORT = 3000, MONGODB_URI } = process.env;

// CREATE EXPRESS APP
const app: Application = express();
// //STORING THE DATA IN A FILE
// const low = require("lowdb");
// const FileSync = require("lowdb/adapters/FileSync");

// const adapter = new FileSync("db.json");
// const db = low(adapter);

// db.defaults({ blogs: [] }).write();



// MIDDLEWARES
app.use(cors());
Expand All @@ -20,6 +30,7 @@ app.use(express.json({ limit: '50mb' }));
// ROUTES
app.use('/api', routes);


// START SERVER
const server = app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
Expand All @@ -35,4 +46,33 @@ Promise.all([server, mongoose.connect(MONGODB_URI)])
});

// EXPORT APP
export default app;
export default app;




const swaggerUI = require("swagger-ui-express");
const swaggerJsDoc = require("swagger-jsdoc");


const options = {
definition: {
openapi: "3.0.0",
info: {
title: "Library API",
version: "1.0.0",
description: "A simple Express Library API",
},
servers: [
{
url: "http://localhost:3000",
},
],
},
apis: ["./routes/*.ts"],
};

const specs = swaggerJsDoc(options);
app.use("/api-docs", swaggerUI.serve, swaggerUI.setup(specs));

// app.db = db;
2 changes: 1 addition & 1 deletion src/middlewares/isAuthenticated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export interface UserInterface extends Request {
}

export const isAuthenticated = (req: UserInterface, res: Response, next) => {
const token = req.headers.authorization?.split(" ")[1];
const token = req.headers.authorization?.split(" ")[1];

if (!token) {
return res.status(401).json({ message: "Unauthorized" });
Expand Down
107 changes: 107 additions & 0 deletions src/tests/blog.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import request from "supertest";
import app from "../index";
import { token } from "./setup";

var articleId: string;

const article = {
title: "Hello World",
content: "This is a test article",
};

const updatedArticle = {
title: "World Hello",
content: "This is an updated test article",
};

describe("POST /api/articles", () => {
it("should create a new article", async () => {
const res = await request(app)
.post("/api/articles")
.set("isAuthenticated", token)
.send(article);

articleId = res.body.article._id;

expect(res.status).toEqual(201);
expect(res.body.status).toEqual("success");
expect(res.body.article).toBeInstanceOf(Object);
expect(res.body.article.title).toEqual(article.title);
expect(res.body.article.content).toEqual(article.content);
});
});

describe("GET /api/articles", () => {
it("should list all articles", async () => {
const res = await request(app).get("/api/articles");

expect(res.status).toEqual(200);
expect(res.body.status).toEqual("success");
expect(res.body.articles).toBeInstanceOf(Array);
expect(res.body.articles.length).toBeGreaterThan(0);
});
});

describe("GET /api/articles/:id", () => {
it("should return a single article", async () => {
const res = await request(app).get(`/api/articles/${articleId}`);

expect(res.status).toEqual(200);
expect(res.body.status).toEqual("success");
expect(res.body.article).toBeInstanceOf(Object);
expect(res.body.article._id).toEqual(articleId);
});
});

describe("GET /api/articles/:id/like", () => {
it("should like an article", async () => {
const res = await request(app)
.get(`/api/articles/${articleId}/like`)
.set("isAuthenticated", token);

expect(res.status).toEqual(200);
expect(res.body.status).toEqual("success");
expect(res.body.article).toBeInstanceOf(Object);
expect(res.body.article._id).toEqual(articleId);
expect(res.body.article.likes).toBeGreaterThan(0);
});
});

describe("GET /api/articles/:id/unlike", () => {
it("should unlike an article", async () => {
const res = await request(app)
.get(`/api/articles/${articleId}/unlike`)
.set("isAuthenticated", token);

expect(res.status).toEqual(200);
expect(res.body.status).toEqual("success");
expect(res.body.article).toBeInstanceOf(Object);
expect(res.body.article._id).toEqual(articleId);
expect(res.body.article.likes).toEqual(0);
});
});

describe("PUT /api/articles/:id", () => {
it("should update an article", async () => {
const res = await request(app)
.put(`/api/articles/${articleId}`)
.set("isAuthenticated", token)
.send(updatedArticle);

expect(res.status).toEqual(200);
expect(res.body.status).toEqual("success");
expect(res.body.article).toBeInstanceOf(Object);
expect(res.body.article.title).toEqual(updatedArticle.title);
expect(res.body.article.content).toEqual(updatedArticle.content);
});
});

describe("DELETE /api/articles/:id", () => {
it("should delete an article", async () => {
const res = await request(app)
.delete(`/api/articles/${articleId}`)
.set("isAuthenticated", token);

expect(res.status).toEqual(204);
});
});
92 changes: 92 additions & 0 deletions src/tests/comment.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import request from "supertest";
import app from "../index";
import { token } from "./setup";

let articleId: string;
let commentId: string;

const article = {
title: "Hello World",
content: "This is a test article",
};

const comment = {
content: "This is a test comment",
};

const updatedComment = {
content: "This is an updated test comment",
};

describe("POST /api/articles/:id/comments", () => {
it("should create a new comment for an article", async () => {
const articleResponse = await request(app)
.post("/api/articles")
.set("isAuthenticated", token)
.send(article);

articleId = articleResponse.body.article._id;

const commentResponse = await request(app)
.post(`/api/articles/${articleId}/comments`)
.set("isAuthenticated", token)
.send(comment);

commentId = commentResponse.body.comment._id;

expect(commentResponse.status).toEqual(201);
expect(commentResponse.body.status).toEqual("success");
expect(commentResponse.body.comment).toBeInstanceOf(Object);
expect(commentResponse.body.comment.content).toEqual(comment.content);
});
});

describe("GET /api/articles/:id/comments", () => {
it("should get all comments for an article", async () => {
const response = await request(app)
.get(`/api/articles/${articleId}/comments`)
.set("isAuthenticated", token);

expect(response.status).toEqual(200);
expect(response.body.status).toEqual("success");
expect(response.body.comments).toBeInstanceOf(Array);
expect(response.body.comments.length).toBeGreaterThan(0);
});
});

describe("GET /api/articles/:id/comments/:id", () => {
it("should get a single comment for an article", async () => {
const response = await request(app)
.get(`/api/articles/${articleId}/comments/${commentId}`)
.set("isAuthenticated", token);

expect(response.status).toEqual(200);
expect(response.body.status).toEqual("success");
expect(response.body.comment).toBeInstanceOf(Object);
expect(response.body.comment.content).toEqual(comment.content);
});
});

describe("PUT /api/articles/:id/comments/:id", () => {
it("should update a single comment for an article", async () => {
const response = await request(app)
.put(`/api/articles/${articleId}/comments/${commentId}`)
.set("isAuthenticated", token)
.send(updatedComment);

expect(response.status).toEqual(200);
expect(response.body.status).toEqual("success");
expect(response.body.comment).toBeInstanceOf(Object);
expect(response.body.comment.content).toEqual(updatedComment.content);
});
});

describe("DELETE /api/articles/:id/comments/:id", () => {
it("should delete a single comment for an article", async () => {
const response = await request(app)
.delete(`/api/articles/${articleId}/comments/${commentId}`)
.set("isAuthenticated", token);

expect(response.status).toEqual(204);
});
});
25 changes: 25 additions & 0 deletions src/tests/setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import mongoose from "mongoose";
import request from "supertest";
import app from "../index";

var token: string;

const user = {
name: "Test User",
email: Math.random() + "@test.com",
password: "password",
};

beforeEach(async () => {
jest.spyOn(console, "log").mockImplementation(() => {});
await request(app).post("/api/user/register").send(user);
const res = await request(app).post("/api/user/login").send(user);
token = res.body.token;
});

afterAll(async () => {
await mongoose.connection.db?.dropDatabase();
await mongoose.disconnect();
});

export { token, user };
Loading

0 comments on commit 56ddb9b

Please sign in to comment.