poll.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. process.env.NODE_ENV = 'test'
  2. let chai = require('chai')
  3. let server = require('../server')
  4. let should = chai.should()
  5. let { sequelize, PollQuestion, PollAnswer, PollVote, User } = require('../models')
  6. const Errors = require('../lib/errors.js')
  7. chai.use(require('chai-http'))
  8. chai.use(require('chai-things'))
  9. describe('Poll', () => {
  10. let admin = chai.request.agent(server)
  11. let user1 = chai.request.agent(server)
  12. let user2 = chai.request.agent(server)
  13. let user3 = chai.request.agent(server)
  14. //Wait for app to start before commencing
  15. before((done) => {
  16. if(server.locals.appStarted) done()
  17. server.on('appStarted', () => {
  18. done()
  19. })
  20. })
  21. describe('Poll', () => {
  22. before(async () => {
  23. try {
  24. let accounts = []
  25. accounts.push(
  26. admin
  27. .post('/api/v1/user')
  28. .set('content-type', 'application/json')
  29. .send({
  30. username: 'adminaccount',
  31. password: 'password',
  32. admin: true
  33. })
  34. )
  35. accounts.push(
  36. user1
  37. .post('/api/v1/user')
  38. .set('content-type', 'application/json')
  39. .send({
  40. username: 'useraccount1',
  41. password: 'password'
  42. })
  43. )
  44. accounts.push(
  45. user2
  46. .post('/api/v1/user')
  47. .set('content-type', 'application/json')
  48. .send({
  49. username: 'useraccount2',
  50. password: 'password'
  51. })
  52. )
  53. accounts.push(
  54. user3
  55. .post('/api/v1/user')
  56. .set('content-type', 'application/json')
  57. .send({
  58. username: 'useraccount3',
  59. password: 'password'
  60. })
  61. )
  62. await Promise.all(accounts)
  63. await admin
  64. .post('/api/v1/category')
  65. .set('content-type', 'application/json')
  66. .send({ name: 'category' })
  67. let threads = []
  68. for(let i = 0; i < 4; i++) {
  69. threads.push(
  70. user1
  71. .post('/api/v1/thread')
  72. .set('content-type', 'application/json')
  73. .send({ category: 'CATEGORY', name: 'thread' + i })
  74. )
  75. }
  76. await Promise.all(threads)
  77. return true
  78. } catch (e) {
  79. return e
  80. }
  81. })
  82. describe('POST /poll', () => {
  83. it('should create a PollQuestion and PollAnswers', async () => {
  84. let res = await user1
  85. .post('/api/v1/poll')
  86. .set('content-type', 'application/json')
  87. .send({
  88. question: 'Question here',
  89. answers: ['answer 1', 'answer 2', 'answer 3'],
  90. threadId: 1
  91. })
  92. res.should.be.json
  93. res.should.have.status(200)
  94. res.body.should.have.property('question', 'Question here')
  95. res.body.should.have.property('id', 1)
  96. let poll = await PollQuestion.findById(1, {
  97. include: [User, PollAnswer]
  98. })
  99. poll.should.have.property('question', 'Question here')
  100. poll.PollAnswers.should.have.property('length', 3)
  101. poll.PollAnswers.should.contain.something.with.property('answer', 'answer 1')
  102. poll.PollAnswers.should.contain.something.with.property('answer', 'answer 2')
  103. poll.PollAnswers.should.contain.something.with.property('answer', 'answer 3')
  104. })
  105. it('should return an error if answers is missing', done => {
  106. user1
  107. .post('/api/v1/poll')
  108. .set('content-type', 'application/json')
  109. .send({
  110. question: 'Question here',
  111. threadId: 2
  112. })
  113. .end((err, res) => {
  114. res.should.have.status(400)
  115. res.body.errors.should.contain.something.with.property(
  116. 'message',
  117. 'You must provide at least 2 answers'
  118. )
  119. done()
  120. })
  121. })
  122. it('should return an error if answers is less than two', done => {
  123. user1
  124. .post('/api/v1/poll')
  125. .set('content-type', 'application/json')
  126. .send({
  127. question: 'Question here',
  128. answers: [],
  129. threadId: 2
  130. })
  131. .end((err, res) => {
  132. res.should.have.status(400)
  133. res.body.errors.should.contain.something.with.property(
  134. 'message',
  135. 'You must provide at least 2 answers'
  136. )
  137. done()
  138. })
  139. })
  140. it('should return an error if answers contains duplicates', done => {
  141. user1
  142. .post('/api/v1/poll')
  143. .set('content-type', 'application/json')
  144. .send({
  145. question: 'Question here',
  146. answers: ['answer', 'answer 1', 'answer'],
  147. threadId: 2
  148. })
  149. .end((err, res) => {
  150. res.should.have.status(400)
  151. res.body.errors.should.contain.something.with.property(
  152. 'message',
  153. 'Answers cannot contain any duplicates'
  154. )
  155. done()
  156. })
  157. })
  158. it('should return an error if thread invalid', done => {
  159. user1
  160. .post('/api/v1/poll')
  161. .set('content-type', 'application/json')
  162. .send({
  163. question: 'Question here',
  164. answers: ['answer', 'answer 1'],
  165. threadId: 404
  166. })
  167. .end((err, res) => {
  168. res.should.have.status(400)
  169. res.body.errors.should.contain.something.with.property(
  170. 'message',
  171. 'invalid thread id'
  172. )
  173. done()
  174. })
  175. })
  176. it('should return an error if thread already has poll', done => {
  177. user1
  178. .post('/api/v1/poll')
  179. .set('content-type', 'application/json')
  180. .send({
  181. question: 'Question here',
  182. answers: ['answer', 'answer 1'],
  183. threadId: 1
  184. })
  185. .end((err, res) => {
  186. res.should.have.status(400)
  187. res.body.errors.should.contain.something.with.property(
  188. 'message',
  189. 'invalid thread id'
  190. )
  191. done()
  192. })
  193. })
  194. it('should return an error if thread user not same as poll user', done => {
  195. admin
  196. .post('/api/v1/poll')
  197. .set('content-type', 'application/json')
  198. .send({
  199. question: 'Question here',
  200. answers: ['answer', 'answer 1'],
  201. threadId: 2
  202. })
  203. .end((err, res) => {
  204. res.should.have.status(401)
  205. res.body.errors.should.contain.something.that.deep.equals(
  206. Errors.requestNotAuthorized
  207. )
  208. done()
  209. })
  210. })
  211. it('should return an error if question not provided', done => {
  212. user1
  213. .post('/api/v1/poll')
  214. .set('content-type', 'application/json')
  215. .send({
  216. answers: ['answer 1', 'answer 2'],
  217. threadId: 2
  218. })
  219. .end((err, res) => {
  220. res.should.have.status(400)
  221. res.body.errors.should.contain.something.with.property(
  222. 'message',
  223. 'question cannot be null'
  224. )
  225. done()
  226. })
  227. })
  228. it('should return an error if not logged in', done => {
  229. chai.request(server)
  230. .post('/api/v1/poll')
  231. .set('content-type', 'application/json')
  232. .send({
  233. question: 'Question here',
  234. answers: ['answer', 'answer 2'],
  235. threadId: 2
  236. })
  237. .end((err, res) => {
  238. res.should.have.status(401)
  239. res.body.errors.should.contain.something.which.deep.equals(Errors.requestNotAuthorized)
  240. done()
  241. })
  242. })
  243. })
  244. describe('POST /poll/:id', () => {
  245. let id, id2
  246. before(async () => {
  247. try {
  248. let res = await user1
  249. .post('/api/v1/poll')
  250. .set('content-type', 'application/json')
  251. .send({
  252. question: 'Poll question',
  253. answers: ['poll answer 1', 'poll answer 2', 'poll answer 3'],
  254. threadId: 2
  255. })
  256. id = res.body.id
  257. let res2 = await user1
  258. .post('/api/v1/poll')
  259. .set('content-type', 'application/json')
  260. .send({
  261. question: 'Poll question',
  262. answers: ['poll answer 1', 'poll answer 2', 'poll answer 3'],
  263. threadId: 3
  264. })
  265. id2 = res2.body.id
  266. return true
  267. } catch (e) {
  268. return e
  269. }
  270. })
  271. it('should add a vote to the poll', async () => {
  272. let res = await user1
  273. .post('/api/v1/poll/' + id)
  274. .set('content-type', 'application/json')
  275. .send({ answer: 'poll answer 1' })
  276. res.should.be.json
  277. res.should.have.status(200)
  278. res.body.should.have.property('question', 'Poll question')
  279. res.body.should.have.deep.property('PollAnswers')
  280. let answer = await PollAnswer.findOne({
  281. where: {
  282. answer: 'poll answer 1'
  283. }
  284. })
  285. let vote = await PollVote.findById(
  286. res.body.PollAnswers[0].PollVotes[0].id
  287. )
  288. vote.should.not.be.null
  289. vote.should.have.property('PollQuestionId', id)
  290. vote.should.have.property('PollAnswerId', answer.id)
  291. })
  292. it('should return an error if voting twice', done => {
  293. user1
  294. .post('/api/v1/poll/' + id)
  295. .set('content-type', 'application/json')
  296. .send({ answer: 'poll answer 2' })
  297. .end((err, res) => {
  298. res.should.have.status(400)
  299. res.body.errors.should.contain.something.that.has.property(
  300. 'message',
  301. 'you cannot vote twice'
  302. )
  303. done()
  304. })
  305. })
  306. it('should return an error if invalid id', done => {
  307. user1
  308. .post('/api/v1/poll/404')
  309. .set('content-type', 'application/json')
  310. .send({ answer: 'poll answer 1' })
  311. .end((err, res) => {
  312. res.should.have.status(400)
  313. res.body.errors.should.contain.something.that.has.property(
  314. 'message',
  315. 'invalid poll id'
  316. )
  317. done()
  318. })
  319. })
  320. it('should return an error if missing answer', done => {
  321. user1
  322. .post('/api/v1/poll/' + id2)
  323. .set('content-type', 'application/json')
  324. .send()
  325. .end((err, res) => {
  326. res.should.have.status(400)
  327. res.body.errors.should.contain.something.that.has.property(
  328. 'message',
  329. 'invalid answer'
  330. )
  331. done()
  332. })
  333. })
  334. it('should return an error if answer is invalid', done => {
  335. user1
  336. .post('/api/v1/poll/' + id2)
  337. .set('content-type', 'application/json')
  338. .send({ answer: 'not an option' })
  339. .end((err, res) => {
  340. res.should.have.status(400)
  341. res.body.errors.should.contain.something.that.has.property(
  342. 'message',
  343. 'invalid answer'
  344. )
  345. done()
  346. })
  347. })
  348. it('should return an error if not logged in', done => {
  349. chai.request(server)
  350. .post('/api/v1/poll/' + id)
  351. .set('content-type', 'application/json')
  352. .send({ answer: 'poll answer 1' })
  353. .end((err, res) => {
  354. res.should.have.status(401)
  355. res.body.errors.should.contain.something.which.deep.equals(Errors.requestNotAuthorized)
  356. })
  357. done()
  358. })
  359. })
  360. describe('GET /poll/:id', () => {
  361. let pollId
  362. before(async () => {
  363. try {
  364. let pollRes = await user1
  365. .post('/api/v1/poll')
  366. .set('content-type', 'application/json')
  367. .send({
  368. question: 'Do you like polls?',
  369. answers: ['yes', 'no', 'meh'],
  370. threadId: 4
  371. })
  372. pollId = pollRes.body.id
  373. await user1
  374. .post('/api/v1/poll/' + pollId)
  375. .set('content-type', 'application/json')
  376. .send({ answer: 'yes' })
  377. await user2
  378. .post('/api/v1/poll/' + pollId)
  379. .set('content-type', 'application/json')
  380. .send({ answer: 'yes' })
  381. await user3
  382. .post('/api/v1/poll/' + pollId)
  383. .set('content-type', 'application/json')
  384. .send({ answer: 'no' })
  385. return true
  386. } catch (e) {
  387. console.log(e)
  388. return e
  389. }
  390. })
  391. it('should get the poll question and accompanying answers and votes', async () => {
  392. let res = await user1.get('/api/v1/poll/' + pollId)
  393. res.should.be.json
  394. res.should.have.status(200)
  395. res.body.should.have.property('question', 'Do you like polls?')
  396. res.body.PollAnswers.should.have.property('length', 3)
  397. res.body.should.have.property('totalVotes', 3)
  398. res.body.should.have.property('hasVoted', true)
  399. res.body.should.have.deep.property('PollAnswers.0.answer', 'yes')
  400. res.body.should.have.deep.property('PollAnswers.0.PollVotes.length', 2)
  401. res.body.should.have.deep.property('PollAnswers.0.percent', 66.7)
  402. res.body.should.have.deep.property('PollAnswers.1.answer', 'no')
  403. res.body.should.have.deep.property('PollAnswers.1.PollVotes.length', 1)
  404. res.body.should.have.deep.property('PollAnswers.1.percent', 33.3)
  405. res.body.should.have.deep.property('PollAnswers.2.answer', 'meh')
  406. res.body.should.have.deep.property('PollAnswers.2.PollVotes.length', 0)
  407. res.body.should.have.deep.property('PollAnswers.2.percent', 0)
  408. })
  409. it('should set hasVoted to false if user not voted', async () => {
  410. let res = await admin.get('/api/v1/poll/' + pollId)
  411. res.should.be.json
  412. res.should.have.status(200)
  413. res.body.should.have.property('hasVoted', false)
  414. })
  415. it('should set hasVoted to false if user not logged in', async () => {
  416. let res = await chai.request(server).get('/api/v1/poll/' + pollId)
  417. res.should.be.json
  418. res.should.have.status(200)
  419. res.body.should.have.property('hasVoted', false)
  420. })
  421. it('should return an error if invalid id', done => {
  422. chai.request(server)
  423. .get('/api/v1/poll/' + pollId)
  424. .end((err, res) => {
  425. res.should.have.status(400)
  426. res.body.errors.should.contain.something.with.property(
  427. 'message',
  428. 'invalid poll id'
  429. )
  430. })
  431. done()
  432. })
  433. })
  434. })
  435. after(() => {
  436. sequelize.sync({ force: true })
  437. })
  438. })