diff --git a/.idea/workspace 2.xml b/.idea/workspace 2.xml new file mode 100644 index 000000000..cce0bf463 --- /dev/null +++ b/.idea/workspace 2.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + 1667990305514 + + + + + + + + + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..8df6ed2aa --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": [ + "/**" + ], + "program": "${workspaceFolder}/api/controllers/posts.js" + } + ] +} \ No newline at end of file diff --git a/MongoHelp.md b/MongoHelp.md new file mode 100644 index 000000000..e69de29bb diff --git a/api/app.js b/api/app.js index 07aa00b3b..a0c401358 100644 --- a/api/app.js +++ b/api/app.js @@ -1,36 +1,38 @@ -const createError = require("http-errors"); -const express = require("express"); -const path = require("path"); -const logger = require("morgan"); -const JWT = require("jsonwebtoken"); +const createError = require('http-errors'); +const express = require('express'); +const path = require('path'); +const logger = require('morgan'); +const JWT = require('jsonwebtoken'); -const postsRouter = require("./routes/posts"); -const tokensRouter = require("./routes/tokens"); -const usersRouter = require("./routes/users"); +const commentsRouter = require('./routes/comments'); +const postsRouter = require('./routes/posts'); +const tokensRouter = require('./routes/tokens'); +const usersRouter = require('./routes/users'); +const friendsRouter = require('./routes/friends'); +const likesRouter = require('./routes/likes'); const app = express(); // setup for receiving JSON -app.use(express.json()) +app.use(express.json()); -app.use(logger("dev")); +app.use(logger('dev')); app.use(express.json()); -app.use(express.static(path.join(__dirname, "public"))); +app.use(express.static(path.join(__dirname, 'public'))); // middleware function to check for valid tokens const tokenChecker = (req, res, next) => { - let token; - const authHeader = req.get("Authorization") + const authHeader = req.get('Authorization'); - if(authHeader) { - token = authHeader.slice(7) + if (authHeader) { + token = authHeader.slice(7); // Remove 'bearer' and return the actual token } JWT.verify(token, process.env.JWT_SECRET, (err, payload) => { - if(err) { - console.log(err) - res.status(401).json({message: "auth error"}); + if (err) { + console.log(err); + res.status(401).json({ message: 'auth error' }); } else { req.user_id = payload.user_id; next(); @@ -39,9 +41,12 @@ const tokenChecker = (req, res, next) => { }; // route setup -app.use("/posts", tokenChecker, postsRouter); -app.use("/tokens", tokensRouter); -app.use("/users", usersRouter); +app.use('/friends', tokenChecker, friendsRouter); +app.use('/comments', tokenChecker, commentsRouter); +app.use('/posts', tokenChecker, postsRouter); +app.use('/tokens', tokensRouter); +app.use('/users', usersRouter); +app.use('/likes', tokenChecker, likesRouter); // catch 404 and forward to error handler app.use((req, res, next) => { @@ -52,10 +57,10 @@ app.use((req, res, next) => { app.use((err, req, res) => { // set locals, only providing error in development res.locals.message = err.message; - res.locals.error = req.app.get("env") === "development" ? err : {}; + res.locals.error = req.app.get('env') === 'development' ? err : {}; // respond with details of the error - res.status(err.status || 500).json({message: 'server error'}) + res.status(err.status || 500).json({ message: 'server error' }); }); module.exports = app; diff --git a/api/controllers/comments.js b/api/controllers/comments.js new file mode 100644 index 000000000..31d3bd3c8 --- /dev/null +++ b/api/controllers/comments.js @@ -0,0 +1,23 @@ +const Post = require('../models/post'); +const TokenGenerator = require('../models/token_generator'); + +const CommentsController = { + AddComment: (req, res) => { + Post.findByIdAndUpdate( + req.body.id, + { $push: { comments: { text: req.body.message } } }, + { new: true }, + async function (err, docs) { + if (err) { + console.log(err); + } else { + console.log('Updated Post : ', docs); + } + const token = await TokenGenerator.jsonwebtoken(req.user_id); + res.status(201).json({ message: 'OK', token: token }); + } + ); + }, +}; + +module.exports = CommentsController; diff --git a/api/controllers/friends.js b/api/controllers/friends.js new file mode 100644 index 000000000..724deecc1 --- /dev/null +++ b/api/controllers/friends.js @@ -0,0 +1,43 @@ +const TokenGenerator = require('../models/token_generator'); +const User = require('../models/user'); + +const FriendsController = { + // Form friend connection + AddFriend: async (req, res) => { + let user = await User.findById(req.user_id) + let friendName = req.body.friend.split(' ') + + let potentialFriend = await User.findOne({firstName: friendName[0], lastName: friendName[1]}); + + const token = await TokenGenerator.jsonwebtoken(req.user_id); + + if (!potentialFriend) { + res.status(201).json({ message: 'Friend not found', token: token }); + } + + else if (user.friends.includes(potentialFriend._id)) { + res.status(201).json({ message: 'They are already friended', token: token }); + } + + else if (potentialFriend._id.toString() === req.user_id.toString()) { + res.status(201).json({ message: 'You cannot add yourself', token: token }); + } + + else { + user.friends.push(potentialFriend._id); + user.save(); + res.status(201).json({ message: 'OK', token: token }); + } + }, + + GetFriends: async (req, res) => { + let user = await User.findById(req.user_id).populate('friends') + let friends = await user.friends; + console.log('There are the friends', friends) + const token = await TokenGenerator.jsonwebtoken(req.user_id); + res.status(200).json({ friends: friends, token: token }); + } +} + + +module.exports = FriendsController; \ No newline at end of file diff --git a/api/controllers/likes.js b/api/controllers/likes.js new file mode 100644 index 000000000..722f26c48 --- /dev/null +++ b/api/controllers/likes.js @@ -0,0 +1,47 @@ +const Post = require('../models/post'); +const TokenGenerator = require('../models/token_generator'); + +const LikesController = { + // return all posts + // Index: (req, res) => { + // Post.find(async (err, posts) => { + // if (err) { + // throw err; + // } + // const token = await TokenGenerator.jsonwebtoken(req.user_id); + // res.status(200).json({ posts: posts, token: token }); + // }); + // }, + // create new post + // Create: (req, res) => { + // const post = new Post(req.body); + // post.poster = req.user_id; // Experimental + // post.save(async (err) => { + // if (err) { + // throw err; + // } + + // const token = await TokenGenerator.jsonwebtoken(req.user_id); + // res.status(201).json({ message: 'OK', token: token }); + // }); + // }, + + Like: (req, res) => { + Post.findByIdAndUpdate( + req.body.id, + { $push: { likes: { userObj: req.body.userId } } }, + { new: true }, + async function (err, docs) { + if (err) { + console.log(err); + } else { + console.log('Updated Post : ', docs); + } + const token = await TokenGenerator.jsonwebtoken(req.user_id); + res.status(201).json({ message: 'OK', token: token }); + } + ); + }, +}; + +module.exports = LikesController; \ No newline at end of file diff --git a/api/controllers/posts.js b/api/controllers/posts.js index dc487d2dd..749568197 100644 --- a/api/controllers/posts.js +++ b/api/controllers/posts.js @@ -1,27 +1,34 @@ -const Post = require("../models/post"); -const TokenGenerator = require("../models/token_generator"); +const Post = require('../models/post'); +const TokenGenerator = require('../models/token_generator'); const PostsController = { + // return all posts Index: (req, res) => { Post.find(async (err, posts) => { if (err) { throw err; } - const token = await TokenGenerator.jsonwebtoken(req.user_id) + const token = await TokenGenerator.jsonwebtoken(req.user_id); res.status(200).json({ posts: posts, token: token }); }); }, + // create new post Create: (req, res) => { const post = new Post(req.body); + post.poster = req.user_id; // Experimental post.save(async (err) => { if (err) { throw err; } - const token = await TokenGenerator.jsonwebtoken(req.user_id) + const token = await TokenGenerator.jsonwebtoken(req.user_id); res.status(201).json({ message: 'OK', token: token }); }); }, + + // Define a comment (linked to post_id, username or user_id and req.body.comment) + // save comment + // the fetch post reqest req.body.comment assumes that the comment is packaged as object }; module.exports = PostsController; diff --git a/api/controllers/users.js b/api/controllers/users.js index 8f195d29e..b6375c545 100644 --- a/api/controllers/users.js +++ b/api/controllers/users.js @@ -3,6 +3,7 @@ const User = require("../models/user"); const UsersController = { Create: (req, res) => { const user = new User(req.body); + console.log(req.body) user.save((err) => { if (err) { res.status(400).json({message: 'Bad request'}) diff --git a/api/models/comment.js b/api/models/comment.js new file mode 100644 index 000000000..345b057bc --- /dev/null +++ b/api/models/comment.js @@ -0,0 +1,19 @@ +// this is not in use please see posts + +const mongoose = require('mongoose'); +// Some tutorial videos require 'express', +// commented out as this isn't used in 'post' and 'user' +// const express = require("express-session") + +const CommentSchema = new mongoose.Schema({ + commentText: { type: String, required: true }, + username: { type: String }, + // Post commented out for now to keep things simple + // post: { type: mongoose.Schema.Types.ObjectId, ref: 'Post' }, + // user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }, + // post_created: { type: Date, default: Date.now } +}); + +const Comment = mongoose.model('Comment', CommentSchema); + +module.exports = Comment; diff --git a/api/models/image.js b/api/models/image.js new file mode 100644 index 000000000..45d67e2fa --- /dev/null +++ b/api/models/image.js @@ -0,0 +1,28 @@ +// const express = require('express') +// const multer = require('multer') +// //importing mongoose schema file +// const Image = require("./image"); +// const app = express() +// //setting options for multer +// const storage = multer.memoryStorage(); +// const image = multer({ storage: storage }); + + +// const mongoose = require("mongoose"); + +// const ImageSchema = new mongoose.Schema({ +// fileName: { +// type: String, +// required: true, +// }, +// file: { +// data: Buffer, +// contentType: String, +// }, +// uploadTime: { +// type: Date, +// default: Date.now, +// }, +// }); + +// module.exports = Image = mongoose.model("image", ImageSchema); diff --git a/api/models/post.js b/api/models/post.js index 6c4e213e9..a31a7e6b0 100644 --- a/api/models/post.js +++ b/api/models/post.js @@ -1,9 +1,32 @@ -const mongoose = require("mongoose"); +const { ObjectId } = require('mongodb'); +const mongoose = require('mongoose'); +const Comment = require('../models/comment'); +// const Image = require('./image') const PostSchema = new mongoose.Schema({ - message: String -}); + message: String, + poster: { + type: mongoose.SchemaTypes.ObjectId, + ref: 'User' + }, + //upload + comments: [ + { + text: String, + created: { type: Date, default: Date.now }, + }, + ], + imageUrls: [String], + likes: [ + { + userObj : mongoose.ObjectId + // user: { + // type: mongoose.Schema.Types.ObjectId, + // ref: 'User' + // } + } + ] +}, {timestamps: true}); +const Post = mongoose.model('Post', PostSchema); -const Post = mongoose.model("Post", PostSchema); - -module.exports = Post; +module.exports = Post; \ No newline at end of file diff --git a/api/models/user.js b/api/models/user.js index b85a9cdd1..e2aefa482 100644 --- a/api/models/user.js +++ b/api/models/user.js @@ -1,10 +1,21 @@ +const { ObjectId } = require("mongodb"); const mongoose = require("mongoose"); +const defaultImage = 'default_image.jpg'; + const UserSchema = new mongoose.Schema({ email: { type: String, required: true }, password: { type: String, required: true }, -}); + firstName: { type: String, required: true }, + lastName: { type: String, required: true }, + picture: { type: String, default: defaultImage }, + friends: [{ + type: mongoose.SchemaTypes.ObjectId, + ref: 'User' + }], + posts: { type: [ObjectId], ref: 'Post'} +}, { timestamps: true }); -const User = mongoose.model("User", UserSchema); +const User = mongoose.model('User', UserSchema); -module.exports = User; +module.exports = User; \ No newline at end of file diff --git a/api/routes/comments.js b/api/routes/comments.js new file mode 100644 index 000000000..70061ada9 --- /dev/null +++ b/api/routes/comments.js @@ -0,0 +1,8 @@ +const express = require('express'); +const router = express.Router(); + +const CommentsController = require('../controllers/comments'); + +router.post('/', CommentsController.AddComment); + +module.exports = router; diff --git a/api/routes/friends.js b/api/routes/friends.js new file mode 100644 index 000000000..044937d44 --- /dev/null +++ b/api/routes/friends.js @@ -0,0 +1,9 @@ +const express = require("express"); +const router = express.Router(); + +const FriendsController = require("../controllers/friends"); + +router.post("/", FriendsController.AddFriend); +router.get('/', FriendsController.GetFriends) + +module.exports = router; \ No newline at end of file diff --git a/api/routes/likes.js b/api/routes/likes.js new file mode 100644 index 000000000..7ca0ce340 --- /dev/null +++ b/api/routes/likes.js @@ -0,0 +1,8 @@ +const express = require('express'); +const router = express.Router(); + +const LikesController = require('../controllers/likes'); + +router.post('/', LikesController.Like); + +module.exports = router; diff --git a/api/routes/posts.js b/api/routes/posts.js index 3f9be8e0d..2a8574b62 100644 --- a/api/routes/posts.js +++ b/api/routes/posts.js @@ -1,9 +1,10 @@ -const express = require("express"); +const express = require('express'); const router = express.Router(); -const PostsController = require("../controllers/posts"); +const PostsController = require('../controllers/posts'); -router.get("/", PostsController.Index); -router.post("/", PostsController.Create); +// router.post('/comment', PostsController.UpdateComment); +router.get('/', PostsController.Index); +router.post('/', PostsController.Create); module.exports = router; diff --git a/api/spec/controllers/comments.spec.js b/api/spec/controllers/comments.spec.js new file mode 100644 index 000000000..53e83976f --- /dev/null +++ b/api/spec/controllers/comments.spec.js @@ -0,0 +1,163 @@ +const app = require('../../app'); +const request = require('supertest'); +require('../mongodb_helper'); +const Post = require('../../models/post'); +const User = require('../../models/user'); +const TokenGenerator = require('../../models/token_generator'); +const JWT = require('jsonwebtoken'); +let token; + +describe('/posts', () => { + beforeAll(async () => { + const user = new User({ + email: 'test@test.com', + password: '12345678', + firstName: 'John', + lastName: 'Smith', + }); + await user.save(); + token = TokenGenerator.jsonwebtoken(user.id); + }); + + beforeEach(async () => { + await Post.deleteMany({}); + }); + + afterAll(async () => { + await User.deleteMany({}); + await Post.deleteMany({}); + }); + + describe('POST, when token is present', () => { + test('responds with a 201', async () => { + let response = await request(app) + .post('/comments') + .set('Authorization', `Bearer ${token}`) + .send({ + id: '635fee24ff8189b02bbc8cf6', + message: 'hello world', + token: token, + }); + expect(response.status).toEqual(201); + }); + + test('creates a new post and adds comment', async () => { + await request(app) + .post('/posts') + .set('Authorization', `Bearer ${token}`) + .send({ message: 'hello world', token: token }); + let posts = await Post.find(); + const did = posts[0].id; + await request(app) + .post('/comments') + .set('Authorization', `Bearer ${token}`) + .send({ + id: did, + message: 'hello world', + token: token, + }); + let posts2 = await Post.find(); + expect(posts2[0].comments[0].text).toEqual('hello world'); + }); + + test('returns a new token', async () => { + let response = await request(app) + .post('/comments') + .set('Authorization', `Bearer ${token}`) + .send({ + id: '635fee24ff8189b02bbc8cf6', + message: 'hello world', + token: token, + }); + let newPayload = JWT.decode(response.body.token, process.env.JWT_SECRET); + let originalPayload = JWT.decode(token, process.env.JWT_SECRET); + expect(newPayload.iat > originalPayload.iat).toEqual(true); + }); + }); + + describe('POST, when token is missing', () => { + test('responds with a 401', async () => { + let response = await request(app).post('/comments').send({ + id: '635fee24ff8189b02bbc8cf6', + message: 'hello world', + }); + expect(response.status).toEqual(401); + }); + }); + + test('a token is not returned', async () => { + let response = await request(app).post('/comments').send({ + id: '635fee24ff8189b02bbc8cf6', + message: 'hello world', + }); + expect(response.body.token).toEqual(undefined); + }); +}); +// describe('GET, when token is present', () => { +// test('returns every post in the collection', async () => { +// let post1 = new Post({ message: 'howdy!' }); +// let post2 = new Post({ message: 'hola!' }); +// await post1.save(); +// await post2.save(); +// let response = await request(app) +// .get('/posts') +// .set('Authorization', `Bearer ${token}`) +// .send({ token: token }); +// let messages = response.body.posts.map((post) => post.message); +// expect(messages).toEqual(['howdy!', 'hola!']); +// }); + +// test('the response code is 200', async () => { +// let post1 = new Post({ message: 'howdy!' }); +// let post2 = new Post({ message: 'hola!' }); +// await post1.save(); +// await post2.save(); +// let response = await request(app) +// .get('/posts') +// .set('Authorization', `Bearer ${token}`) +// .send({ token: token }); +// expect(response.status).toEqual(200); +// }); + +// test('returns a new token', async () => { +// let post1 = new Post({ message: 'howdy!' }); +// let post2 = new Post({ message: 'hola!' }); +// await post1.save(); +// await post2.save(); +// let response = await request(app) +// .get('/posts') +// .set('Authorization', `Bearer ${token}`) +// .send({ token: token }); +// let newPayload = JWT.decode(response.body.token, process.env.JWT_SECRET); +// let originalPayload = JWT.decode(token, process.env.JWT_SECRET); +// expect(newPayload.iat > originalPayload.iat).toEqual(true); +// }); +// }); + +// describe('GET, when token is missing', () => { +// test('returns no posts', async () => { +// let post1 = new Post({ message: 'howdy!' }); +// let post2 = new Post({ message: 'hola!' }); +// await post1.save(); +// await post2.save(); +// let response = await request(app).get('/posts'); +// expect(response.body.posts).toEqual(undefined); +// }); + +// test('the response code is 401', async () => { +// let post1 = new Post({ message: 'howdy!' }); +// let post2 = new Post({ message: 'hola!' }); +// await post1.save(); +// await post2.save(); +// let response = await request(app).get('/posts'); +// expect(response.status).toEqual(401); +// }); + +// test('does not return a new token', async () => { +// let post1 = new Post({ message: 'howdy!' }); +// let post2 = new Post({ message: 'hola!' }); +// await post1.save(); +// await post2.save(); +// let response = await request(app).get('/posts'); +// expect(response.body.token).toEqual(undefined); +// }); diff --git a/api/spec/controllers/likes.spec.js b/api/spec/controllers/likes.spec.js new file mode 100644 index 000000000..674c6efc2 --- /dev/null +++ b/api/spec/controllers/likes.spec.js @@ -0,0 +1,184 @@ +const app = require('../../app'); +const request = require('supertest'); +require('../mongodb_helper'); +const Post = require('../../models/post'); +const User = require('../../models/user'); +const TokenGenerator = require('../../models/token_generator'); +const JWT = require('jsonwebtoken'); +let token; + +describe('/posts', () => { + beforeAll(async () => { + const user = new User({ + email: 'test@test.com', + password: '12345678', + firstName: 'John', + lastName: 'Smith', + }); + + await user.save(); + token = TokenGenerator.jsonwebtoken(user.id); + }); + + beforeEach(async () => { + await Post.deleteMany({}); + }); + + afterAll(async () => { + await User.deleteMany({}); + await Post.deleteMany({}); + }); + + describe('POST, when token is present', () => { + test('responds with a 201', async () => { + let response = await request(app) + .post('/likes') + .set('Authorization', `Bearer ${token}`) + .send({ message: 'hello world', token: token }); + expect(response.status).toEqual(201); + }); + + test('creates a new post and adds a like', async () => { + var user = new User({ email: 'test@test.com', password: 'testpassword', firstName: 'test', lastName: 'trial'}); + await request(app) + .post('/posts') + .set('Authorization', `Bearer ${token}`) + .send({ message: 'hello world', token: token }); + let posts = await Post.find(); + const did = posts[0].id; + await request(app) + .post('/likes') + .set('Authorization', `Bearer ${token}`) + .send({ + id: did, + userId: user.id, + token: token, + }); + let posts2 = await Post.find(); + console.log(posts2[0].likes[0].userObj) + expect(posts2[0].likes[0].userObj.toString()).toEqual(user.id); + }); + + // test('creates a new post', async () => { + // await request(app) + // .post('/posts') + // .set('Authorization', `Bearer ${token}`) + // .send({ message: 'hello world', token: token }); + // let posts = await Post.find(); + // expect(posts.length).toEqual(1); + // expect(posts[0].message).toEqual('hello world'); + // }); + + // test('creates a new post', async () => { + // await request(app) + // .post('/posts') + // .set('Authorization', `Bearer ${token}`) + // .send({ message: 'hello world', token: token }); + // let posts = await Post.find(); + // expect(posts.length).toEqual(1); + // expect(posts[0].message).toEqual('hello world'); + // }); + + // test('returns a new token', async () => { + // let response = await request(app) + // .post('/posts') + // .set('Authorization', `Bearer ${token}`) + // .send({ message: 'hello world', token: token }); + // let newPayload = JWT.decode(response.body.token, process.env.JWT_SECRET); + // let originalPayload = JWT.decode(token, process.env.JWT_SECRET); + // expect(newPayload.iat > originalPayload.iat).toEqual(true); + // }); + // }); + + // describe('POST, when token is missing', () => { + // test('responds with a 401', async () => { + // let response = await request(app) + // .post('/posts') + // .send({ message: 'hello again world' }); + // expect(response.status).toEqual(401); + // }); + + // test('a post is not created', async () => { + // await request(app).post('/posts').send({ message: 'hello again world' }); + // let posts = await Post.find(); + // expect(posts.length).toEqual(0); + // }); + + // test('a token is not returned', async () => { + // let response = await request(app) + // .post('/posts') + // .send({ message: 'hello again world' }); + // expect(response.body.token).toEqual(undefined); + // }); + // }); + + // describe('GET, when token is present', () => { + // test('returns every post in the collection', async () => { + // let post1 = new Post({ message: 'howdy!' }); + // let post2 = new Post({ message: 'hola!' }); + // await post1.save(); + // await post2.save(); + // let response = await request(app) + // .get('/posts') + // .set('Authorization', `Bearer ${token}`) + // .send({ token: token }); + // let messages = response.body.posts.map((post) => post.message); + // expect(messages).toEqual(['howdy!', 'hola!']); + // }); + + // test('the response code is 200', async () => { + // let post1 = new Post({ message: 'howdy!' }); + // let post2 = new Post({ message: 'hola!' }); + // await post1.save(); + // await post2.save(); + // let response = await request(app) + // .get('/posts') + // .set('Authorization', `Bearer ${token}`) + // .send({ token: token }); + // expect(response.status).toEqual(200); + // }); + + // test('returns a new token', async () => { + // let post1 = new Post({ message: 'howdy!' }); + // let post2 = new Post({ message: 'hola!' }); + // await post1.save(); + // await post2.save(); + // let response = await request(app) + // .get('/posts') + // .set('Authorization', `Bearer ${token}`) + // .send({ token: token }); + // let newPayload = JWT.decode(response.body.token, process.env.JWT_SECRET); + // let originalPayload = JWT.decode(token, process.env.JWT_SECRET); + // expect(newPayload.iat > originalPayload.iat).toEqual(true); + // }); + // }); + + // describe('GET, when token is missing', () => { + // test('returns no posts', async () => { + // let post1 = new Post({ message: 'howdy!' }); + // let post2 = new Post({ message: 'hola!' }); + // await post1.save(); + // await post2.save(); + // let response = await request(app).get('/posts'); + // expect(response.body.posts).toEqual(undefined); + // }); + + // test('the response code is 401', async () => { + // let post1 = new Post({ message: 'howdy!' }); + // let post2 = new Post({ message: 'hola!' }); + // await post1.save(); + // await post2.save(); + // let response = await request(app).get('/posts'); + // expect(response.status).toEqual(401); + // }); + + // test('does not return a new token', async () => { + // let post1 = new Post({ message: 'howdy!' }); + // let post2 = new Post({ message: 'hola!' }); + // await post1.save(); + // await post2.save(); + // let response = await request(app).get('/posts'); + // expect(response.body.token).toEqual(undefined); + // }); + }); +}); diff --git a/api/spec/controllers/posts.spec.js b/api/spec/controllers/posts.spec.js index 9f1e67ec3..6577c51fa 100644 --- a/api/spec/controllers/posts.spec.js +++ b/api/spec/controllers/posts.spec.js @@ -1,152 +1,163 @@ -const app = require("../../app"); -const request = require("supertest"); -require("../mongodb_helper"); +const app = require('../../app'); +const request = require('supertest'); +require('../mongodb_helper'); const Post = require('../../models/post'); const User = require('../../models/user'); const TokenGenerator = require('../../models/token_generator'); -const JWT = require("jsonwebtoken"); +const JWT = require('jsonwebtoken'); let token; -describe("/posts", () => { - beforeAll( async () => { - const user = new User({email: "test@test.com", password: "12345678"}); +describe('/posts', () => { + beforeAll(async () => { + const user = new User({ + email: 'test@test.com', + password: '12345678', + firstName: 'John', + lastName: 'Smith', + }); + await user.save(); token = TokenGenerator.jsonwebtoken(user.id); }); - beforeEach( async () => { + beforeEach(async () => { await Post.deleteMany({}); - }) + }); - afterAll( async () => { + afterAll(async () => { await User.deleteMany({}); await Post.deleteMany({}); - }) + }); - describe("POST, when token is present", () => { - test("responds with a 201", async () => { + describe('POST, when token is present', () => { + test('responds with a 201', async () => { let response = await request(app) - .post("/posts") - .set("Authorization", `Bearer ${token}`) - .send({ message: "hello world", token: token }); + .post('/posts') + .set('Authorization', `Bearer ${token}`) + .send({ message: 'hello world', token: token }); expect(response.status).toEqual(201); }); - - test("creates a new post", async () => { + + test('creates a new post', async () => { await request(app) - .post("/posts") - .set("Authorization", `Bearer ${token}`) - .send({ message: "hello world", token: token }); + .post('/posts') + .set('Authorization', `Bearer ${token}`) + .send({ message: 'hello world', token: token }); let posts = await Post.find(); expect(posts.length).toEqual(1); - expect(posts[0].message).toEqual("hello world"); + expect(posts[0].message).toEqual('hello world'); }); - - test("returns a new token", async () => { + + test('creates a new post', async () => { + await request(app) + .post('/posts') + .set('Authorization', `Bearer ${token}`) + .send({ message: 'hello world', token: token }); + let posts = await Post.find(); + expect(posts.length).toEqual(1); + expect(posts[0].message).toEqual('hello world'); + }); + + test('returns a new token', async () => { let response = await request(app) - .post("/posts") - .set("Authorization", `Bearer ${token}`) - .send({ message: "hello world", token: token }) + .post('/posts') + .set('Authorization', `Bearer ${token}`) + .send({ message: 'hello world', token: token }); let newPayload = JWT.decode(response.body.token, process.env.JWT_SECRET); let originalPayload = JWT.decode(token, process.env.JWT_SECRET); expect(newPayload.iat > originalPayload.iat).toEqual(true); - }); + }); }); - - describe("POST, when token is missing", () => { - test("responds with a 401", async () => { + + describe('POST, when token is missing', () => { + test('responds with a 401', async () => { let response = await request(app) - .post("/posts") - .send({ message: "hello again world" }); + .post('/posts') + .send({ message: 'hello again world' }); expect(response.status).toEqual(401); }); - - test("a post is not created", async () => { - await request(app) - .post("/posts") - .send({ message: "hello again world" }); + + test('a post is not created', async () => { + await request(app).post('/posts').send({ message: 'hello again world' }); let posts = await Post.find(); expect(posts.length).toEqual(0); }); - - test("a token is not returned", async () => { + + test('a token is not returned', async () => { let response = await request(app) - .post("/posts") - .send({ message: "hello again world" }); + .post('/posts') + .send({ message: 'hello again world' }); expect(response.body.token).toEqual(undefined); }); - }) + }); - describe("GET, when token is present", () => { - test("returns every post in the collection", async () => { - let post1 = new Post({message: "howdy!"}); - let post2 = new Post({message: "hola!"}); + describe('GET, when token is present', () => { + test('returns every post in the collection', async () => { + let post1 = new Post({ message: 'howdy!' }); + let post2 = new Post({ message: 'hola!' }); await post1.save(); await post2.save(); let response = await request(app) - .get("/posts") - .set("Authorization", `Bearer ${token}`) - .send({token: token}); - let messages = response.body.posts.map((post) => ( post.message )); - expect(messages).toEqual(["howdy!", "hola!"]); - }) - - test("the response code is 200", async () => { - let post1 = new Post({message: "howdy!"}); - let post2 = new Post({message: "hola!"}); + .get('/posts') + .set('Authorization', `Bearer ${token}`) + .send({ token: token }); + let messages = response.body.posts.map((post) => post.message); + expect(messages).toEqual(['howdy!', 'hola!']); + }); + + test('the response code is 200', async () => { + let post1 = new Post({ message: 'howdy!' }); + let post2 = new Post({ message: 'hola!' }); await post1.save(); await post2.save(); let response = await request(app) - .get("/posts") - .set("Authorization", `Bearer ${token}`) - .send({token: token}); + .get('/posts') + .set('Authorization', `Bearer ${token}`) + .send({ token: token }); expect(response.status).toEqual(200); - }) + }); - test("returns a new token", async () => { - let post1 = new Post({message: "howdy!"}); - let post2 = new Post({message: "hola!"}); + test('returns a new token', async () => { + let post1 = new Post({ message: 'howdy!' }); + let post2 = new Post({ message: 'hola!' }); await post1.save(); await post2.save(); let response = await request(app) - .get("/posts") - .set("Authorization", `Bearer ${token}`) - .send({token: token}); + .get('/posts') + .set('Authorization', `Bearer ${token}`) + .send({ token: token }); let newPayload = JWT.decode(response.body.token, process.env.JWT_SECRET); let originalPayload = JWT.decode(token, process.env.JWT_SECRET); expect(newPayload.iat > originalPayload.iat).toEqual(true); - }) - }) + }); + }); - describe("GET, when token is missing", () => { - test("returns no posts", async () => { - let post1 = new Post({message: "howdy!"}); - let post2 = new Post({message: "hola!"}); + describe('GET, when token is missing', () => { + test('returns no posts', async () => { + let post1 = new Post({ message: 'howdy!' }); + let post2 = new Post({ message: 'hola!' }); await post1.save(); await post2.save(); - let response = await request(app) - .get("/posts"); + let response = await request(app).get('/posts'); expect(response.body.posts).toEqual(undefined); - }) + }); - test("the response code is 401", async () => { - let post1 = new Post({message: "howdy!"}); - let post2 = new Post({message: "hola!"}); + test('the response code is 401', async () => { + let post1 = new Post({ message: 'howdy!' }); + let post2 = new Post({ message: 'hola!' }); await post1.save(); await post2.save(); - let response = await request(app) - .get("/posts"); + let response = await request(app).get('/posts'); expect(response.status).toEqual(401); - }) + }); - test("does not return a new token", async () => { - let post1 = new Post({message: "howdy!"}); - let post2 = new Post({message: "hola!"}); + test('does not return a new token', async () => { + let post1 = new Post({ message: 'howdy!' }); + let post2 = new Post({ message: 'hola!' }); await post1.save(); await post2.save(); - let response = await request(app) - .get("/posts"); + let response = await request(app).get('/posts'); expect(response.body.token).toEqual(undefined); - }) - }) + }); + }); }); diff --git a/api/spec/controllers/tokens.spec.js b/api/spec/controllers/tokens.spec.js index e5f5d9c7b..250e9214f 100644 --- a/api/spec/controllers/tokens.spec.js +++ b/api/spec/controllers/tokens.spec.js @@ -5,7 +5,7 @@ const User = require('../../models/user'); describe("/tokens", () => { beforeAll( () => { - const user = new User({ email: "test@test.com", password: "12345678" }) + const user = new User({ email: "test@test.com", password: "12345678", firstName: "John", lastName: "Smith" }) user.save() }); @@ -16,7 +16,7 @@ describe("/tokens", () => { test("a token is returned when creds are valid", async () => { let response = await request(app) .post("/tokens") - .send({email: "test@test.com", password: "12345678"}) + .send({email: "test@test.com", password: "12345678", firstName: "John", lastName: "Smith"}) expect(response.status).toEqual(201) expect(response.body.token).not.toEqual(undefined) expect(response.body.message).toEqual("OK") @@ -26,7 +26,7 @@ describe("/tokens", () => { test("a token is not returned when creds are invalid", async () => { let response = await request(app) .post("/tokens") - .send({email: "test@test.com", password: "1234"}) + .send({email: "test@test.com", password: "1234", firstName: "John", lastName: "Smith"}) expect(response.status).toEqual(401) expect(response.body.token).toEqual(undefined) expect(response.body.message).toEqual("auth error") diff --git a/api/spec/controllers/users.spec.js b/api/spec/controllers/users.spec.js index adccba0b6..48cbee8a0 100644 --- a/api/spec/controllers/users.spec.js +++ b/api/spec/controllers/users.spec.js @@ -8,21 +8,22 @@ describe("/users", () => { await User.deleteMany({}); }); - describe("POST, when email and password are provided", () => { + describe("POST, when email, password, first name and last name are provided", () => { test("the response code is 201", async () => { let response = await request(app) .post("/users") - .send({email: "poppy@email.com", password: "1234"}) + .send({email: "poppy@email.com", password: "1234", firstName: "poppy", lastName: "playtime"}) expect(response.statusCode).toBe(201) }) test("a user is created", async () => { await request(app) .post("/users") - .send({email: "scarlett@email.com", password: "1234"}) + .send({email: "scarlett@email.com", password: "1234", firstName: "scarlett", lastName: "witch"}) let users = await User.find() let newUser = users[users.length - 1] expect(newUser.email).toEqual("scarlett@email.com") + expect(newUser.lastName).toEqual("witch") }) }) @@ -30,14 +31,14 @@ describe("/users", () => { test("response code is 400", async () => { let response = await request(app) .post("/users") - .send({email: "skye@email.com"}) + .send({email: "skye@email.com", firstName: 'skye', lastName: 'broadbande'}) expect(response.statusCode).toBe(400) }); test("does not create a user", async () => { await request(app) .post("/users") - .send({email: "skye@email.com"}) + .send({email: "skye@email.com", firstName: 'skye', lastName: 'broadbande'}) let users = await User.find() expect(users.length).toEqual(0) }); @@ -47,7 +48,7 @@ describe("/users", () => { test("response code is 400", async () => { let response = await request(app) .post("/users") - .send({password: "1234"}) + .send({password: "1234", userName: "some", lastName: "one"}) expect(response.statusCode).toBe(400) }); diff --git a/api/spec/models/comment.spec.js b/api/spec/models/comment.spec.js new file mode 100644 index 000000000..f580bf612 --- /dev/null +++ b/api/spec/models/comment.spec.js @@ -0,0 +1,66 @@ +var mongoose = require('mongoose'); + +require('../mongodb_helper'); +var Comment = require('../../models/comment'); +// Comment needs Post, but this would make it an +// Integration test +// var Post = require("../../models/post"); + +describe('Comment model', () => { + beforeEach((done) => { + // Changed to posts + mongoose.connection.collections.comments.drop(() => { + done(); + }); + }); + + it('has a comment', () => { + var comment = new Comment({ commentText: 'some comment' }); + expect(comment.commentText).toEqual('some comment'); + }); + + it('has a username', () => { + var comment = new Comment({ username: 'Dave57' }); + expect(comment.username).toEqual('Dave57'); + }); + + it('can list all comments', (done) => { + Comment.find((err, comments) => { + expect(err).toBeNull(); + expect(comments).toEqual([]); + done(); + }); + }); + + it('can save a comment', (done) => { + var comment = new Comment({ commentText: 'some comment' }); + + comment.save((err) => { + expect(err).toBeNull(); + + Comment.find((err, comments) => { + expect(err).toBeNull(); + expect(comments[0].commentText).toEqual('some comment'); + done(); + }); + }); + }); + + it('can save a username and comment', (done) => { + var comment = new Comment({ + commentText: 'some comment 2', + username: 'Dave60', + }); + + comment.save((err) => { + expect(err).toBeNull(); + + Comment.find((err, comments) => { + expect(err).toBeNull(); + expect(comments[0].commentText).toEqual('some comment 2'); + expect(comments[0].username).toEqual('Dave60'); + done(); + }); + }); + }); +}); diff --git a/api/spec/models/post.spec.js b/api/spec/models/post.spec.js index 3acfd48ce..a5aacc259 100644 --- a/api/spec/models/post.spec.js +++ b/api/spec/models/post.spec.js @@ -1,21 +1,23 @@ -var mongoose = require("mongoose"); +var mongoose = require('mongoose'); +const Comment = require('../../models/comment'); +const User = require('../../models/user.js') -require("../mongodb_helper"); -var Post = require("../../models/post"); +require('../mongodb_helper'); +var Post = require('../../models/post'); -describe("Post model", () => { +describe('Post model', () => { beforeEach((done) => { mongoose.connection.collections.posts.drop(() => { done(); }); }); - it("has a message", () => { - var post = new Post({ message: "some message" }); - expect(post.message).toEqual("some message"); + it('has a message', () => { + var post = new Post({ message: 'some message' }); + expect(post.message).toEqual('some message'); }); - it("can list all posts", (done) => { + it('can list all posts', (done) => { Post.find((err, posts) => { expect(err).toBeNull(); expect(posts).toEqual([]); @@ -23,8 +25,18 @@ describe("Post model", () => { }); }); - it("can save a post", (done) => { - var post = new Post({ message: "some message" }); + it('adds a time and date to a post', () => { + var post = new Post({ + message: 'some message', + createdAt: '2014-12-23T03:15:56.257Z', + }); + var date = new Date('2014-12-23T03:15:56.257Z'); + expect(post.message).toEqual('some message'); + expect(post.createdAt).toEqual(date); + }); + + it('can save a post', (done) => { + var post = new Post({ message: 'some message' }); post.save((err) => { expect(err).toBeNull(); @@ -32,9 +44,159 @@ describe("Post model", () => { Post.find((err, posts) => { expect(err).toBeNull(); - expect(posts[0]).toMatchObject({ message: "some message" }); + expect(posts[0].message).toEqual('some message'); + done(); + }); + }); + }); + + it('can add a post comment ', (done) => { + var post = new Post({ + message: 'some message', + comments: { text: 'Some comment' }, + }); + expect(post.message).toEqual('some message'); + expect(post.comments[0].text).toEqual('Some comment'); + done(); + }); + + it('can save a post with comment', (done) => { + var post = new Post({ + message: 'some message 2', + comments: { text: 'Some comment 2' }, + }); + + post.save((err) => { + expect(err).toBeNull(); + + Post.find((err, posts) => { + expect(err).toBeNull(); + + expect(posts[0]).toMatchObject({ message: 'some message 2' }); + expect(posts[0].comments[0]).toMatchObject({ + text: 'Some comment 2', + }); + done(); + }); + }); + }); + + it('can save a post and add comment later', (done) => { + var post = new Post({ + message: 'some new message', + // comments: { text: 'Some comment 2' }, + }); + let obId = post.id; + // console.log(post.id); + // console.log(post); + post.save((err) => { + let post_id = post.id; + Post.findByIdAndUpdate( + post_id, + { $push: { comments: { text: 'New comment!' } } }, + { new: true }, + function (err, docs) { + if (err) { + console.log(err); + } else { + console.log('Updated Post : ', docs); + } + } + ); + expect(err).toBeNull(); + Post.find((err, posts) => { + expect(err).toBeNull(); + expect(posts[0]).toMatchObject({ message: 'some new message' }); + // console.log(posts[0].comments[0]); + expect(posts[0].comments[0]).toMatchObject({ + text: 'New comment!', + }); + done(); + }); + }); + }); + + xit('can save a post and add 2 comments', (done) => { + var post = new Post({ + message: 'some new message', + // comments: { text: 'Some comment 2' }, + }); + let obId = post.id; + // console.log(post.id); + // console.log(post); + post.save((err) => { + let post_id = post.id; + Post.findByIdAndUpdate( + post_id, + { $push: { comments: { text: 'New comment!' } } }, + { new: true }, + function (err, docs) { + if (err) { + console.log(err); + } else { + console.log('Updated Post : ', docs); + } + } + ); + Post.findByIdAndUpdate( + post_id, + { $push: { comments: { text: 'Hi everyone!' } } }, + { new: true }, + function (err, docs) { + if (err) { + console.log(err); + } else { + console.log('Updated Post : ', docs); + } + } + ); + expect(err).toBeNull(); + Post.find((err, posts) => { + expect(err).toBeNull(); + expect(posts[0]).toMatchObject({ message: 'some new message' }); + // console.log(posts[0].comments[0]); + expect(posts[0].comments[0].text).toEqual('New comment!'); + expect(posts[0].comments[1].text).toEqual('Hi everyone!'); done(); }); }); }); + + it('has a message', () => { + var post = new Post({ message: 'some message' }); + expect(post.message).toEqual('some message'); + }); + + it('populates the likes array with users', (done) => { + var post = new Post({ message: 'test message' }); + var user = new User({ email: 'test@test.com', password: 'testpassword', firstName: 'test', lastName: 'trial'}); + let post_id = post.id; + console.log(user); + // console.log(post.id); + post.save((err) => { + + Post.findByIdAndUpdate( + post_id, + { $push: { likes: {userObj : user.id} } }, + { new: true }, + function (err, docs) { + if (err) { + console.log(err); + } else { + console.log('Updated Post : ', docs); + } + } + ); + expect(err).toBeNull(); + Post.find((err, posts) => { + // console.log(posts[0].likes[0]) + expect(err).toBeNull(); + expect(posts[0]).toMatchObject({ message: 'test message' }); + console.log(posts[0].likes[0]); + // expect(posts[0].likes[0].id).toEqual(user._id); + expect(posts[0].likes[0].userObj.toString()).toEqual(user.id); + done(); + }); + }); +}); }); diff --git a/api/spec/models/user.spec.js b/api/spec/models/user.spec.js index ed1c93ef5..2f9c61fc7 100644 --- a/api/spec/models/user.spec.js +++ b/api/spec/models/user.spec.js @@ -1,32 +1,36 @@ -const mongoose = require("mongoose"); +const mongoose = require('mongoose'); -require("../mongodb_helper"); -const User = require("../../models/user"); +require('../mongodb_helper'); +const User = require('../../models/user'); -describe("User model", () => { +describe('User model', () => { beforeEach((done) => { mongoose.connection.collections.users.drop(() => { done(); }); }); - it("has an email address", () => { + it('has an email address', () => { const user = new User({ - email: "someone@example.com", - password: "password", + email: 'someone@example.com', + password: 'password', + firstName: 'some', + lastName: 'one', }); - expect(user.email).toEqual("someone@example.com"); + expect(user.email).toEqual('someone@example.com'); }); - it("has a password", () => { + it('has a password', () => { const user = new User({ - email: "someone@example.com", - password: "password", + email: 'someone@example.com', + password: 'password', + firstName: 'some', + lastName: 'one', }); - expect(user.password).toEqual("password"); + expect(user.password).toEqual('password'); }); - it("can list all users", (done) => { + it('can list all users', (done) => { User.find((err, users) => { expect(err).toBeNull(); expect(users).toEqual([]); @@ -34,10 +38,12 @@ describe("User model", () => { }); }); - it("can save a user", (done) => { + it('can save a user', (done) => { const user = new User({ - email: "someone@example.com", - password: "password", + email: 'someone@example.com', + password: 'password', + firstName: 'some', + lastName: 'one', }); user.save((err) => { @@ -47,11 +53,44 @@ describe("User model", () => { expect(err).toBeNull(); expect(users[0]).toMatchObject({ - email: "someone@example.com", - password: "password", + email: 'someone@example.com', + password: 'password', + firstName: 'some', + lastName: 'one', }); done(); }); }); }); + + // TODO: Finish this test. Testing the friendslist. + it('can add friends', (done) => { + // Create 3 users + const user1 = new User({ + email: 'some1@example.com', + password: 'password', + firstName: 'Spongebob', + lastName: 'Squarepants', + }); + const user2 = new User({ + email: 'some2@example.com', + password: 'password', + firstName: 'Sandy', + lastName: 'Cheeks', + }); + const user3 = new User({ + email: 'some3@example.com', + password: 'password', + firstName: 'Eugene', + lastName: 'Krabs', + }); + + // Save all 3 users on the database. + user1.save(); + user2.save(); + user3.save(); + let spongebob = User.findOne({ name: 'spongebob' }); + console.log(spongebob); + done(); + }); }); diff --git a/cypress.config.js b/cypress.config.js new file mode 100644 index 000000000..97f47c412 --- /dev/null +++ b/cypress.config.js @@ -0,0 +1,9 @@ +const { defineConfig } = require("cypress"); + +module.exports = defineConfig({ + e2e: { + setupNodeEvents(on, config) { + // implement node event listeners here + }, + }, +}); diff --git a/cypress/e2e/1-getting-started/todo.cy.js b/cypress/e2e/1-getting-started/todo.cy.js new file mode 100644 index 000000000..4768ff923 --- /dev/null +++ b/cypress/e2e/1-getting-started/todo.cy.js @@ -0,0 +1,143 @@ +/// + +// Welcome to Cypress! +// +// This spec file contains a variety of sample tests +// for a todo list app that are designed to demonstrate +// the power of writing tests in Cypress. +// +// To learn more about how Cypress works and +// what makes it such an awesome testing tool, +// please read our getting started guide: +// https://on.cypress.io/introduction-to-cypress + +describe('example to-do app', () => { + beforeEach(() => { + // Cypress starts out with a blank slate for each test + // so we must tell it to visit our website with the `cy.visit()` command. + // Since we want to visit the same URL at the start of all our tests, + // we include it in our beforeEach function so that it runs before each test + cy.visit('https://example.cypress.io/todo') + }) + + it('displays two todo items by default', () => { + // We use the `cy.get()` command to get all elements that match the selector. + // Then, we use `should` to assert that there are two matched items, + // which are the two default items. + cy.get('.todo-list li').should('have.length', 2) + + // We can go even further and check that the default todos each contain + // the correct text. We use the `first` and `last` functions + // to get just the first and last matched elements individually, + // and then perform an assertion with `should`. + cy.get('.todo-list li').first().should('have.text', 'Pay electric bill') + cy.get('.todo-list li').last().should('have.text', 'Walk the dog') + }) + + it('can add new todo items', () => { + // We'll store our item text in a variable so we can reuse it + const newItem = 'Feed the cat' + + // Let's get the input element and use the `type` command to + // input our new list item. After typing the content of our item, + // we need to type the enter key as well in order to submit the input. + // This input has a data-test attribute so we'll use that to select the + // element in accordance with best practices: + // https://on.cypress.io/selecting-elements + cy.get('[data-test=new-todo]').type(`${newItem}{enter}`) + + // Now that we've typed our new item, let's check that it actually was added to the list. + // Since it's the newest item, it should exist as the last element in the list. + // In addition, with the two default items, we should have a total of 3 elements in the list. + // Since assertions yield the element that was asserted on, + // we can chain both of these assertions together into a single statement. + cy.get('.todo-list li') + .should('have.length', 3) + .last() + .should('have.text', newItem) + }) + + it('can check off an item as completed', () => { + // In addition to using the `get` command to get an element by selector, + // we can also use the `contains` command to get an element by its contents. + // However, this will yield the