From 44bf17853b55ad0e216a84d7ec9a4043b32d4f88 Mon Sep 17 00:00:00 2001 From: Debora Serra Date: Tue, 26 Nov 2024 16:02:09 -0800 Subject: [PATCH] test: add e2e tests to tour --- backend/src/test/e2e/tour.test.js | 496 ++++++++++++++++++++++++++ backend/src/test/mocks/banner.mock.js | 33 +- backend/src/test/mocks/tour.mock.js | 12 +- 3 files changed, 506 insertions(+), 35 deletions(-) create mode 100644 backend/src/test/e2e/tour.test.js diff --git a/backend/src/test/e2e/tour.test.js b/backend/src/test/e2e/tour.test.js new file mode 100644 index 00000000..032b59b1 --- /dev/null +++ b/backend/src/test/e2e/tour.test.js @@ -0,0 +1,496 @@ +import { expect } from "chai"; +import { after, afterEach, before, beforeEach, describe } from "mocha"; +import waitOn from "wait-on"; +import app from "../../../index.js"; +import db from "../../models/index.js"; +import mocks from "../mocks/tour.mock.js"; +import { UserBuilder, validList } from "../mocks/user.mock.js"; +import chai from "./index.js"; + +const user = UserBuilder.user; +const dbReadyOptions = { + resources: ["tcp:localhost:5432"], + delay: 1000, + timeout: 30000, + interval: 1000, +}; + +const tour = mocks.TourBuilder.tour; +const tourList = mocks.toursList; + +const createTour = async (token, tour) => { + const { id, createdBy, ...rest } = tour; + const res = await chai.request + .execute(app) + .post("/api/tour/add_tour") + .set("Authorization", `Bearer ${token}`) + .send(rest); + return res.body; +}; + +describe("E2e tests helperLink", () => { + describe("POST /api/tour/add_tour", () => { + before(async () => { + db.sequelize.connectionManager.initPools(); + }); + after(async () => { + const conn = await db.sequelize.connectionManager.getConnection(); + db.sequelize.connectionManager.releaseConnection(conn); + }); + let token; + + beforeEach(async () => { + try { + await waitOn(dbReadyOptions); + } catch (err) { + console.error("Database not ready in time:", err); + throw err; + } + const login = await chai.request + .execute(app) + .post("/api/auth/register") + .send(user().build()); + token = login.body.token; + }); + afterEach(async () => { + await db.sequelize.sync({ force: true, match: /_test$/ }); + }); + it("should return 401 if no token is provided", async () => { + const res = await chai.request + .execute(app) + .post("/api/tour/add_tour") + .send(tour().build()); + expect(res).to.have.status(401); + const body = res.body; + expect(body).to.be.deep.equal({ error: "No token provided" }); + }); + it("should return 400 if title is not provided", async () => { + const res = await chai.request + .execute(app) + .post("/api/tour/add_tour") + .set("Authorization", `Bearer ${token}`) + .send(tour().missingTitle().build()); + expect(res).to.have.status(400); + const body = res.body; + expect(body).to.be.deep.equal({ + errors: [ + { + msg: "title, pageTargeting, theme, and triggeringFrequency are required", + }, + ], + }); + }); + it("should return 400 if pageTargeting is not provided", async () => { + const res = await chai.request + .execute(app) + .post("/api/tour/add_tour") + .set("Authorization", `Bearer ${token}`) + .send(tour().missingPageTargeting().build()); + expect(res).to.have.status(400); + const body = res.body; + expect(body).to.be.deep.equal({ + errors: [ + { + msg: "title, pageTargeting, theme, and triggeringFrequency are required", + }, + ], + }); + }); + it("should return 400 if theme is not provided", async () => { + const res = await chai.request + .execute(app) + .post("/api/tour/add_tour") + .set("Authorization", `Bearer ${token}`) + .send(tour().missingTheme().build()); + expect(res).to.have.status(400); + const body = res.body; + expect(body).to.be.deep.equal({ + errors: [ + { + msg: "title, pageTargeting, theme, and triggeringFrequency are required", + }, + ], + }); + }); + it("should return 400 if triggeringFrequency is not provided", async () => { + const res = await chai.request + .execute(app) + .post("/api/tour/add_tour") + .set("Authorization", `Bearer ${token}`) + .send(tour().missingTriggeringFrequency().build()); + expect(res).to.have.status(400); + const body = res.body; + expect(body).to.be.deep.equal({ + errors: [ + { + msg: "title, pageTargeting, theme, and triggeringFrequency are required", + }, + ], + }); + }); + it("should return 400 if pageTargeting is invalid", async () => { + const res = await chai.request + .execute(app) + .post("/api/tour/add_tour") + .set("Authorization", `Bearer ${token}`) + .send(tour().invalidPageTargeting().build()); + expect(res).to.have.status(400); + const body = res.body; + expect(body).to.be.deep.equal({ + errors: [ + { + msg: "Invalid value for pageTargeting, theme, or triggeringFrequency", + }, + ], + }); + }); + it("should return 400 if theme is invalid", async () => { + const res = await chai.request + .execute(app) + .post("/api/tour/add_tour") + .set("Authorization", `Bearer ${token}`) + .send(tour().invalidTheme().build()); + expect(res).to.have.status(400); + const body = res.body; + expect(body).to.be.deep.equal({ + errors: [ + { + msg: "Invalid value for pageTargeting, theme, or triggeringFrequency", + }, + ], + }); + }); + it("should return 400 if triggeringFrequency is invalid", async () => { + const res = await chai.request + .execute(app) + .post("/api/tour/add_tour") + .set("Authorization", `Bearer ${token}`) + .send(tour().invalidTriggeringFrequency().build()); + expect(res).to.have.status(400); + const body = res.body; + expect(body).to.be.deep.equal({ + errors: [ + { + msg: "Invalid value for pageTargeting, theme, or triggeringFrequency", + }, + ], + }); + }); + it("should return 201 if all required fields are provided", async () => { + const res = await chai.request + .execute(app) + .post("/api/tour/add_tour") + .set("Authorization", `Bearer ${token}`) + .send(tour().withoutId().build()); + expect(res).to.have.status(201); + const body = res.body; + expect(body).to.be.deep.equal(tour().build()); + }); + }); + describe("DELETE /api/tour/delete_tour/:id", () => { + before(async () => { + db.sequelize.connectionManager.initPools(); + }); + after(async () => { + const conn = await db.sequelize.connectionManager.getConnection(); + db.sequelize.connectionManager.releaseConnection(conn); + }); + let token; + + beforeEach(async () => { + try { + await waitOn(dbReadyOptions); + } catch (err) { + console.error("Database not ready in time:", err); + throw err; + } + const login = await chai.request + .execute(app) + .post("/api/auth/register") + .send(user().build()); + token = login.body.token; + }); + afterEach(async () => { + await db.sequelize.sync({ force: true, match: /_test$/ }); + }); + it("should return 401 if no token is provided", async () => { + const res = await chai.request + .execute(app) + .delete("/api/tour/delete_tour/1") + .send(); + expect(res).to.have.status(401); + const body = res.body; + expect(body).to.be.deep.equal({ error: "No token provided" }); + }); + it("should return 404 if tour is not found", async () => { + const res = await chai.request + .execute(app) + .delete("/api/tour/delete_tour/1") + .set("Authorization", `Bearer ${token}`) + .send(); + expect(res).to.have.status(404); + const body = res.body; + expect(body).to.be.deep.equal({ msg: "Tour not found" }); + }); + it("should return 200 if tour is found", async () => { + await createTour(token, tour().withoutId().build()); + const res = await chai.request + .execute(app) + .delete("/api/tour/delete_tour/1") + .set("Authorization", `Bearer ${token}`); + expect(res).to.have.status(200); + const body = res.body; + expect(body).to.be.deep.equal({ msg: "Tour deleted successfully" }); + }); + }); + describe("PUT /api/tour/edit_tour/:id", () => { + before(async () => { + db.sequelize.connectionManager.initPools(); + }); + after(async () => { + const conn = await db.sequelize.connectionManager.getConnection(); + db.sequelize.connectionManager.releaseConnection(conn); + }); + let token; + + beforeEach(async () => { + try { + await waitOn(dbReadyOptions); + } catch (err) { + console.error("Database not ready in time:", err); + throw err; + } + const login = await chai.request + .execute(app) + .post("/api/auth/register") + .send(user().build()); + token = login.body.token; + await createTour(token, tour().withoutId().build()); + }); + afterEach(async () => { + await db.sequelize.sync({ force: true, match: /_test$/ }); + }); + it("should return 401 if no token is provided", async () => { + const res = await chai.request + .execute(app) + .put("/api/tour/edit_tour/1") + .send(tour().build()); + expect(res).to.have.status(401); + const body = res.body; + expect(body).to.be.deep.equal({ error: "No token provided" }); + }); + it("should return 200 if all required fields are provided", async () => { + const res = await chai.request + .execute(app) + .put("/api/tour/edit_tour/1") + .set("Authorization", `Bearer ${token}`) + .send({ title: "New title" }); + expect(res).to.have.status(200); + const body = res.body; + expect(body).not.to.be.deep.equal(tour().build()); + expect(body).to.have.property("title", "New title"); + }); + it("should return 404 if tour is not found", async () => { + const res = await chai.request + .execute(app) + .put("/api/tour/edit_tour/2") + .set("Authorization", `Bearer ${token}`) + .send({ title: "New title" }); + expect(res).to.have.status(404); + const body = res.body; + expect(body).to.be.deep.equal({ msg: "Tour not found" }); + }); + }); + describe("GET /api/tour/all_tours", () => { + before(async () => { + db.sequelize.connectionManager.initPools(); + }); + after(async () => { + const conn = await db.sequelize.connectionManager.getConnection(); + db.sequelize.connectionManager.releaseConnection(conn); + }); + let token; + + beforeEach(async () => { + try { + await waitOn(dbReadyOptions); + } catch (err) { + console.error("Database not ready in time:", err); + throw err; + } + const login = await chai.request + .execute(app) + .post("/api/auth/register") + .send(user().build()); + token = login.body.token; + await chai.request + .execute(app) + .post("/api/team/invite") + .set("Authorization", `Bearer ${token}`) + .send({ invitedEmail: validList[1].email, role: "member" }); + const login2 = await chai.request + .execute(app) + .post("/api/auth/register") + .send({ ...validList[1], role: 2 }); + const token2 = login2.body.token; + await Promise.all( + tourList.map(async (tour) => { + return await createTour(tour.createdBy === 1 ? token : token2, tour); + }) + ); + }); + afterEach(async () => { + await db.sequelize.sync({ force: true, match: /_test$/ }); + }); + it("should return 401 if no token is provided", async () => { + const res = await chai.request + .execute(app) + .get("/api/tour/all_tours") + .send(); + expect(res).to.have.status(401); + const body = res.body; + expect(body).to.be.deep.equal({ error: "No token provided" }); + }); + it("should return 200 if tours are found", async () => { + await createTour(token, tour().withoutId().build()); + const res = await chai.request + .execute(app) + .get("/api/tour/all_tours") + .set("Authorization", `Bearer ${token}`); + expect(res).to.have.status(200); + const body = res.body; + body.forEach((info, i) => { + const { id, creator, createdBy: c, ...rest } = info; + const listTourItem = tourList.find((tour) => tour.title === rest.title); + const { id: tourId, createdBy, ...expected } = listTourItem; + expect(rest).to.be.deep.equal(expected); + expect(creator.id).to.be.equal(c); + }); + expect(body).to.have.lengthOf(11); + }); + }); + describe("GET /api/tour/tours", () => { + before(async () => { + db.sequelize.connectionManager.initPools(); + }); + after(async () => { + const conn = await db.sequelize.connectionManager.getConnection(); + db.sequelize.connectionManager.releaseConnection(conn); + }); + let token; + + beforeEach(async () => { + try { + await waitOn(dbReadyOptions); + } catch (err) { + console.error("Database not ready in time:", err); + throw err; + } + const login = await chai.request + .execute(app) + .post("/api/auth/register") + .send(user().build()); + token = login.body.token; + await chai.request + .execute(app) + .post("/api/team/invite") + .set("Authorization", `Bearer ${token}`) + .send({ invitedEmail: validList[1].email, role: "member" }); + const login2 = await chai.request + .execute(app) + .post("/api/auth/register") + .send({ ...validList[1], role: 2 }); + const token2 = login2.body.token; + await Promise.all( + tourList.map(async (tour) => { + await createTour(tour.createdBy === 1 ? token : token2, tour); + }) + ); + }); + afterEach(async () => { + await db.sequelize.sync({ force: true, match: /_test$/ }); + }); + it("should return 401 if no token is provided", async () => { + const res = await chai.request.execute(app).get("/api/tour/tours").send(); + expect(res).to.have.status(401); + const body = res.body; + expect(body).to.be.deep.equal({ error: "No token provided" }); + }); + it("should return 200 if tours are found", async () => { + await createTour(token, tour().withoutId().build()); + const res = await chai.request + .execute(app) + .get("/api/tour/tours") + .set("Authorization", `Bearer ${token}`); + expect(res).to.have.status(200); + const body = res.body; + body.forEach((info, i) => { + const { id, creator, createdBy: c, ...rest } = info; + const listTourItem = tourList.find((tour) => tour.title === rest.title); + if (c !== 1) { + expect(rest).not.to.include(listTourItem); + } else { + const { id: tourId, createdBy, ...expected } = listTourItem; + expect(rest).to.be.deep.equal(expected); + } + }); + expect(body).to.have.lengthOf(6); + }); + }); + describe("GET /api/tour/get_tour/:id", () => { + before(async () => { + db.sequelize.connectionManager.initPools(); + }); + after(async () => { + const conn = await db.sequelize.connectionManager.getConnection(); + db.sequelize.connectionManager.releaseConnection(conn); + }); + let token; + + beforeEach(async () => { + try { + await waitOn(dbReadyOptions); + } catch (err) { + console.error("Database not ready in time:", err); + throw err; + } + const login = await chai.request + .execute(app) + .post("/api/auth/register") + .send(user().build()); + token = login.body.token; + }); + afterEach(async () => { + await db.sequelize.sync({ force: true, match: /_test$/ }); + }); + it("should return 401 if no token is provided", async () => { + const res = await chai.request + .execute(app) + .get("/api/tour/get_tour/1") + .send(); + expect(res).to.have.status(401); + const body = res.body; + expect(body).to.be.deep.equal({ error: "No token provided" }); + }); + it("should return 404 if tour is not found", async () => { + const res = await chai.request + .execute(app) + .get("/api/tour/get_tour/1") + .set("Authorization", `Bearer ${token}`) + .send(); + expect(res).to.have.status(404); + const body = res.body; + expect(body).to.be.deep.equal({ msg: "Tour not found" }); + }); + it("should return 200 if tour is found", async () => { + await createTour(token, tour().withoutId().build()); + const res = await chai.request + .execute(app) + .get("/api/tour/get_tour/1") + .set("Authorization", `Bearer ${token}`); + expect(res).to.have.status(200); + const { creator, ...rest } = res.body; + expect(rest).to.be.deep.equal(tour().build()); + }); + }); +}); diff --git a/backend/src/test/mocks/banner.mock.js b/backend/src/test/mocks/banner.mock.js index 7ead9329..1a88364c 100644 --- a/backend/src/test/mocks/banner.mock.js +++ b/backend/src/test/mocks/banner.mock.js @@ -24,26 +24,6 @@ class BannerBuilder { this.banner.position = ""; return this; } - missingUrl() { - this.banner.url = ""; - return this; - } - missingFontColor() { - this.banner.fontColor = ""; - return this; - } - missingBackgroundColor() { - this.banner.backgroundColor = ""; - return this; - } - missingBannerText() { - this.banner.bannerText = ""; - return this; - } - missingCreatedBy() { - this.banner.createdBy = null; - return this; - } invalidCloseButtonAction() { this.banner.closeButtonAction = "close"; @@ -53,10 +33,7 @@ class BannerBuilder { this.banner.position = "side"; return this; } - invalidUrl() { - this.banner.url = "url"; - return this; - } + invalidFontColor() { this.banner.fontColor = "blue"; return this; @@ -65,14 +42,6 @@ class BannerBuilder { this.banner.backgroundColor = "black"; return this; } - invalidBannerText() { - this.banner.bannerText = 12; - return this; - } - invalidCreatedBy() { - this.banner.createdBy = undefined; - return this; - } build() { return this.banner; diff --git a/backend/src/test/mocks/tour.mock.js b/backend/src/test/mocks/tour.mock.js index 70388b3c..6d17e895 100644 --- a/backend/src/test/mocks/tour.mock.js +++ b/backend/src/test/mocks/tour.mock.js @@ -11,7 +11,7 @@ class TourBuilder { constructor(id) { this.tour = { id: id, - title: "title", + title: `title ${id}`, description: "description", statusActive: true, pageTargeting: this.pageTargeting[0], @@ -85,8 +85,14 @@ class TourBuilder { } } -const toursList = new Array(5) +const toursList = new Array(10) .fill(null) - .map((_, i) => TourBuilder.tour(i + 1).build()); + .map((_, i) => TourBuilder.tour(i + 1).build()) + .map((tour, i) => { + if (i % 2 === 0) { + return { ...tour, createdBy: 2 }; + } + return tour; + }); module.exports = { TourBuilder, toursList };