test_pagure_flask_ui_roadmap.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2016-2018 - Copyright Red Hat Inc
  4. Authors:
  5. Pierre-Yves Chibon <pingou@pingoured.fr>
  6. """
  7. from __future__ import unicode_literals, absolute_import
  8. import datetime
  9. import json
  10. import unittest
  11. import shutil
  12. import sys
  13. import tempfile
  14. import os
  15. import pygit2
  16. from mock import patch
  17. sys.path.insert(0, os.path.join(os.path.dirname(
  18. os.path.abspath(__file__)), '..'))
  19. import pagure.lib.query
  20. import tests
  21. from pagure.lib.repo import PagureRepo
  22. class PagureFlaskRoadmaptests(tests.Modeltests):
  23. """ Tests for the pagure's roadmap """
  24. @patch('pagure.lib.git.update_git')
  25. @patch('pagure.lib.notify.send_email')
  26. def test_ticket_with_no_roadmap(self, p_send_email, p_ugt):
  27. """ Test creating a ticket without roadmap. """
  28. p_send_email.return_value = True
  29. p_ugt.return_value = True
  30. tests.create_projects(self.session)
  31. tests.create_projects_git(os.path.join(self.path, 'repos'), bare=True)
  32. user = tests.FakeUser()
  33. user.username = 'pingou'
  34. with tests.user_set(self.app.application, user):
  35. # Get the CSRF token
  36. output = self.app.get('/test/new_issue')
  37. self.assertEqual(output.status_code, 200)
  38. output_text = output.get_data(as_text=True)
  39. self.assertTrue(
  40. '<h4 class="font-weight-bold mb-4">New Issue</h4>\n'
  41. in output_text)
  42. csrf_token = output_text.split(
  43. 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
  44. data = {
  45. 'title': 'Test issue',
  46. 'issue_content': 'We really should improve on this issue',
  47. 'status': 'Open',
  48. 'csrf_token': csrf_token,
  49. }
  50. # Create the issue
  51. output = self.app.post(
  52. '/test/new_issue', data=data, follow_redirects=True)
  53. self.assertEqual(output.status_code, 200)
  54. output_text = output.get_data(as_text=True)
  55. self.assertIn(
  56. '<title>Issue #1: Test issue - test - Pagure</title>',
  57. output_text)
  58. self.assertIn(
  59. '<a class="btn btn-outline-secondary btn-sm border-0" '
  60. 'href="/test/issue/1/edit" title="Edit this issue">',
  61. output_text)
  62. @patch('pagure.lib.git.update_git')
  63. @patch('pagure.lib.notify.send_email')
  64. def test_ticket_with_roadmap(self, p_send_email, p_ugt):
  65. """ Test creating a ticket with roadmap. """
  66. p_send_email.return_value = True
  67. p_ugt.return_value = True
  68. tests.create_projects(self.session)
  69. tests.create_projects_git(os.path.join(self.path, 'repos'), bare=True)
  70. # Set some milestone
  71. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  72. repo.milestone = {'v1.0': '', 'v2.0': 'Tomorrow!'}
  73. self.session.add(repo)
  74. self.session.commit()
  75. user = tests.FakeUser()
  76. user.username = 'pingou'
  77. with tests.user_set(self.app.application, user):
  78. # Get the CSRF token
  79. output = self.app.get('/test/new_issue')
  80. self.assertEqual(output.status_code, 200)
  81. output_text = output.get_data(as_text=True)
  82. self.assertTrue(
  83. '<h4 class="font-weight-bold mb-4">New Issue</h4>\n'
  84. in output_text)
  85. csrf_token = output_text.split(
  86. 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
  87. data = {
  88. 'title': 'Test issue',
  89. 'issue_content': 'We really should improve on this issue',
  90. 'status': 'Open',
  91. 'csrf_token': csrf_token,
  92. }
  93. # Create the issue
  94. output = self.app.post(
  95. '/test/new_issue', data=data, follow_redirects=True)
  96. self.assertEqual(output.status_code, 200)
  97. output_text = output.get_data(as_text=True)
  98. self.assertIn(
  99. '<title>Issue #1: Test issue - test - Pagure</title>',
  100. output_text)
  101. self.assertIn(
  102. '<a class="btn btn-outline-secondary btn-sm border-0" '
  103. 'href="/test/issue/1/edit" title="Edit this issue">',
  104. output_text)
  105. # Mark the ticket for the roadmap
  106. data = {
  107. 'tag': 'roadmap',
  108. 'csrf_token': csrf_token,
  109. }
  110. output = self.app.post(
  111. '/test/issue/1/update', data=data, follow_redirects=True)
  112. self.assertEqual(output.status_code, 200)
  113. output_text = output.get_data(as_text=True)
  114. self.assertIn(
  115. '<title>Issue #1: Test issue - test - Pagure</title>',
  116. output_text)
  117. self.assertIn(
  118. '<a class="btn btn-outline-secondary btn-sm border-0" '
  119. 'href="/test/issue/1/edit" title="Edit this issue">',
  120. output_text)
  121. def test_update_milestones(self):
  122. """ Test updating milestones of a repo. """
  123. tests.create_projects(self.session)
  124. tests.create_projects_git(os.path.join(self.path, 'repos'), bare=True)
  125. # Set some milestones
  126. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  127. self.assertEqual(repo.milestones, {})
  128. user = tests.FakeUser()
  129. user.username = 'pingou'
  130. with tests.user_set(self.app.application, user):
  131. # Get the CSRF token
  132. output = self.app.get('/test/settings')
  133. self.assertEqual(output.status_code, 200)
  134. output_text = output.get_data(as_text=True)
  135. self.assertIn(
  136. '<title>Settings - test - Pagure</title>', output_text)
  137. self.assertIn(
  138. '<h5 class="pl-2 font-weight-bold text-muted">'
  139. 'Project Settings</h5>\n', output_text)
  140. csrf_token = output_text.split(
  141. 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
  142. data = {
  143. 'milestones': 1,
  144. 'milestone_1_name': '1',
  145. 'milestone_1_date': 'Tomorrow',
  146. }
  147. output = self.app.post(
  148. '/test/update/milestones', data=data, follow_redirects=True)
  149. self.assertEqual(output.status_code, 200)
  150. output_text = output.get_data(as_text=True)
  151. # Check the redirect
  152. self.assertIn(
  153. '<title>Settings - test - Pagure</title>', output_text)
  154. self.assertIn(
  155. '<h5 class="pl-2 font-weight-bold text-muted">'
  156. 'Project Settings</h5>\n', output_text)
  157. # Check the result of the action -- None, no CSRF
  158. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  159. self.assertEqual(repo.milestones, {})
  160. data = {
  161. 'milestones': 1,
  162. 'milestone_1_name': '1',
  163. 'milestone_date': '',
  164. 'csrf_token': csrf_token,
  165. }
  166. output = self.app.post(
  167. '/test/update/milestones', data=data, follow_redirects=True)
  168. self.assertEqual(output.status_code, 200)
  169. output_text = output.get_data(as_text=True)
  170. # Check the redirect
  171. self.assertIn(
  172. '<title>Settings - test - Pagure</title>', output_text)
  173. self.assertIn(
  174. '<h5 class="pl-2 font-weight-bold text-muted">'
  175. 'Project Settings</h5>\n', output_text)
  176. self.assertIn('Milestones updated', output_text)
  177. # Check the result of the action -- Milestones recorded
  178. self.session.commit()
  179. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  180. self.assertEqual(repo.milestones, {'1': {'active': False, 'date': None}})
  181. data = {
  182. 'milestones': [1, 2],
  183. 'milestone_1_name': 'v1.0',
  184. 'milestone_2_name': 'v2.0',
  185. 'milestone_1_date': 'Tomorrow',
  186. 'milestone_2_date': '',
  187. 'csrf_token': csrf_token,
  188. }
  189. output = self.app.post(
  190. '/test/update/milestones', data=data, follow_redirects=True)
  191. self.assertEqual(output.status_code, 200)
  192. output_text = output.get_data(as_text=True)
  193. # Check the redirect
  194. self.assertIn(
  195. '<title>Settings - test - Pagure</title>', output_text)
  196. self.assertIn(
  197. '<h5 class="pl-2 font-weight-bold text-muted">'
  198. 'Project Settings</h5>\n', output_text)
  199. self.assertIn('Milestones updated', output_text)
  200. # Check the result of the action -- Milestones recorded
  201. self.session.commit()
  202. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  203. self.assertEqual(
  204. repo.milestones,
  205. {
  206. 'v1.0': {'active': False, 'date': 'Tomorrow'},
  207. 'v2.0': {'active': False, 'date': None}
  208. }
  209. )
  210. # Check error - less milestones than dates
  211. data = {
  212. 'milestones': [1, 2],
  213. 'milestone_1_name': 'v1.0',
  214. 'milestone_2_name': 'v2.0',
  215. 'milestone_1_date': 'Tomorrow',
  216. 'milestone_2_date': 'Next week',
  217. 'milestone_3_date': 'Next Year',
  218. 'csrf_token': csrf_token,
  219. }
  220. output = self.app.post(
  221. '/test/update/milestones', data=data, follow_redirects=True)
  222. self.assertEqual(output.status_code, 200)
  223. output_text = output.get_data(as_text=True)
  224. # Check the redirect
  225. self.assertIn(
  226. '<title>Settings - test - Pagure</title>', output_text)
  227. self.assertIn(
  228. '<h5 class="pl-2 font-weight-bold text-muted">'
  229. 'Project Settings</h5>\n', output_text)
  230. # Check the result of the action -- Milestones un-changed
  231. self.session.commit()
  232. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  233. self.assertEqual(
  234. repo.milestones,
  235. {
  236. 'v1.0': {'active': False, 'date': 'Tomorrow'},
  237. 'v2.0': {'active': False, 'date': 'Next week'}
  238. }
  239. )
  240. # Check error - Twice the same milestone
  241. data = {
  242. 'milestones': [1, 2, 3],
  243. 'milestone_1_name': 'v1.0',
  244. 'milestone_2_name': 'v2.0',
  245. 'milestone_3_name': 'v2.0',
  246. 'milestone_1_date': 'Tomorrow',
  247. 'milestone_2_date': 'Next week',
  248. 'milestone_3_date': 'Next Year',
  249. 'csrf_token': csrf_token,
  250. }
  251. output = self.app.post(
  252. '/test/update/milestones', data=data, follow_redirects=True)
  253. self.assertEqual(output.status_code, 200)
  254. output_text = output.get_data(as_text=True)
  255. # Check the redirect
  256. self.assertIn(
  257. '<title>Settings - test - Pagure</title>', output_text)
  258. self.assertIn(
  259. '<h5 class="pl-2 font-weight-bold text-muted">'
  260. 'Project Settings</h5>\n', output_text)
  261. self.assertIn(
  262. 'Milestone v2.0 is present multiple times',
  263. 'Milestone v2.0 is present multiple times',
  264. output_text)
  265. # Check the result of the action -- Milestones un-changed
  266. self.session.commit()
  267. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  268. self.assertEqual(
  269. repo.milestones,
  270. {
  271. 'v1.0': {'active': False, 'date': 'Tomorrow'},
  272. 'v2.0': {'active': False, 'date': 'Next week'}
  273. }
  274. )
  275. # Check error - Twice the same date
  276. data = {
  277. 'milestones': [1, 2, 3],
  278. 'milestone_1_name': 'v1.0',
  279. 'milestone_2_name': 'v2.0',
  280. 'milestone_3_name': 'v3.0',
  281. 'milestone_1_date': 'Tomorrow',
  282. 'milestone_2_date': 'Next week',
  283. 'milestone_3_date': 'Next week',
  284. 'csrf_token': csrf_token,
  285. }
  286. output = self.app.post(
  287. '/test/update/milestones', data=data, follow_redirects=True)
  288. self.assertEqual(output.status_code, 200)
  289. output_text = output.get_data(as_text=True)
  290. # Check the redirect
  291. self.assertIn(
  292. '<title>Settings - test - Pagure</title>', output_text)
  293. self.assertIn(
  294. '<h5 class="pl-2 font-weight-bold text-muted">'
  295. 'Project Settings</h5>\n', output_text)
  296. self.assertIn(
  297. 'Milestones updated',
  298. output_text)
  299. # Check the result of the action -- Milestones updated
  300. self.session.commit()
  301. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  302. self.assertEqual(
  303. repo.milestones,
  304. {
  305. 'v1.0': {'active': False, 'date': 'Tomorrow'},
  306. 'v2.0': {'active': False, 'date': 'Next week'},
  307. 'v3.0': {'active': False, 'date': 'Next week'},
  308. }
  309. )
  310. # Check for an invalid project
  311. output = self.app.post(
  312. '/foo/update/milestones', data=data)
  313. self.assertEqual(output.status_code, 404)
  314. # Check the behavior if the project disabled the issue tracker
  315. settings = repo.settings
  316. settings['issue_tracker'] = False
  317. repo.settings = settings
  318. self.session.add(repo)
  319. self.session.commit()
  320. output = self.app.post(
  321. '/test/update/milestones', data=data)
  322. self.assertEqual(output.status_code, 404)
  323. # Check for a non-admin user
  324. settings = repo.settings
  325. settings['issue_tracker'] = True
  326. repo.settings = settings
  327. self.session.add(repo)
  328. self.session.commit()
  329. user.username = 'ralph'
  330. with tests.user_set(self.app.application, user):
  331. output = self.app.post(
  332. '/test/update/milestones', data=data)
  333. self.assertEqual(output.status_code, 403)
  334. @patch('pagure.lib.git.update_git')
  335. @patch('pagure.lib.notify.send_email')
  336. def test_milestones_without_dates(self, p_send_email, p_ugt):
  337. """ Test creating two milestones with no dates. """
  338. tests.create_projects(self.session)
  339. tests.create_projects_git(os.path.join(self.path, 'repos'), bare=True)
  340. user = tests.FakeUser()
  341. user.username = 'pingou'
  342. with tests.user_set(self.app.application, user):
  343. # Get the CSRF token
  344. output = self.app.get('/test/settings')
  345. output_text = output.get_data(as_text=True)
  346. csrf_token = output_text.split(
  347. 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
  348. data = {
  349. 'milestones': [1, 2],
  350. 'milestone_1_name': 'v1.0',
  351. 'milestone_2_name': 'v2.0',
  352. 'milestone_1_date': '',
  353. 'milestone_2_date': '',
  354. 'csrf_token': csrf_token,
  355. }
  356. output = self.app.post(
  357. '/test/update/milestones', data=data, follow_redirects=True)
  358. self.assertEqual(output.status_code, 200)
  359. output_text = output.get_data(as_text=True)
  360. # Check the redirect
  361. self.assertIn(
  362. '<title>Settings - test - Pagure</title>', output_text)
  363. self.assertIn(
  364. '<h5 class="pl-2 font-weight-bold text-muted">'
  365. 'Project Settings</h5>\n', output_text)
  366. self.assertIn('Milestones updated', output_text)
  367. # Check the result of the action -- Milestones recorded
  368. self.session.commit()
  369. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  370. self.assertEqual(
  371. repo.milestones,
  372. {
  373. 'v1.0': {'active': False, 'date': None},
  374. 'v2.0': {'active': False, 'date': None}
  375. }
  376. )
  377. @patch('pagure.lib.git.update_git')
  378. @patch('pagure.lib.notify.send_email')
  379. def test_roadmap_ui(self, p_send_email, p_ugt):
  380. """ Test viewing the roadmap of a repo. """
  381. p_send_email.return_value = True
  382. p_ugt.return_value = True
  383. self.test_update_milestones()
  384. user = tests.FakeUser()
  385. user.username = 'pingou'
  386. with tests.user_set(self.app.application, user):
  387. # Get the CSRF token
  388. output = self.app.get('/test/new_issue')
  389. self.assertEqual(output.status_code, 200)
  390. output_text = output.get_data(as_text=True)
  391. self.assertTrue(
  392. '<h4 class="font-weight-bold mb-4">New Issue</h4>\n'
  393. in output_text)
  394. csrf_token = output_text.split(
  395. 'name="csrf_token" type="hidden" value="')[1].split('">')[0]
  396. # Create an unplanned milestone
  397. data = {
  398. 'milestones': [1, 2, 3],
  399. 'milestone_1_name': 'v1.0',
  400. 'milestone_2_name': 'v2.0',
  401. 'milestone_3_name': 'unplanned',
  402. 'milestone_1_date': 'Tomorrow',
  403. 'milestone_2_date': '',
  404. 'milestone_3_date': '',
  405. 'milestone_1_active': True,
  406. 'milestone_2_active': True,
  407. 'milestone_3_active': True,
  408. 'csrf_token': csrf_token,
  409. }
  410. output = self.app.post(
  411. '/test/update/milestones', data=data, follow_redirects=True)
  412. self.assertEqual(output.status_code, 200)
  413. output_text = output.get_data(as_text=True)
  414. # Check the redirect
  415. self.assertIn(
  416. '<title>Settings - test - Pagure</title>', output_text)
  417. self.assertIn(
  418. '<h5 class="pl-2 font-weight-bold text-muted">'
  419. 'Project Settings</h5>\n', output_text)
  420. self.assertIn('Milestones updated', output_text)
  421. # Check the result of the action -- Milestones recorded
  422. self.session.commit()
  423. repo = pagure.lib.query._get_project(self.session, 'test')
  424. self.assertEqual(
  425. repo.milestones,
  426. {
  427. 'unplanned': {'active': True, 'date': None},
  428. 'v1.0': {'active': True, 'date': 'Tomorrow'},
  429. 'v2.0': {'active': True, 'date': None}
  430. }
  431. )
  432. # Create the issues
  433. for cnt in range(6):
  434. cnt += 1
  435. data = {
  436. 'title': 'Test issue %s' % cnt,
  437. 'issue_content': 'We really should improve on this '
  438. 'issue %s' % cnt,
  439. 'csrf_token': csrf_token,
  440. }
  441. output = self.app.post(
  442. '/test/new_issue', data=data, follow_redirects=True)
  443. self.assertEqual(output.status_code, 200)
  444. output_text = output.get_data(as_text=True)
  445. self.assertIn(
  446. '<title>Issue #{0}: Test issue {0} - test - '
  447. 'Pagure</title>'.format(cnt),
  448. output_text)
  449. self.assertIn(
  450. '<a class="btn btn-outline-secondary btn-sm border-0" '
  451. 'href="/test/issue/%s/edit" title="Edit this issue">' % cnt,
  452. output_text)
  453. # Mark the ticket for the roadmap
  454. mstone = 'v%s.0' % cnt
  455. if cnt >= 3:
  456. if (cnt % 3) == 0:
  457. mstone = 'unplanned'
  458. else:
  459. mstone = 'v%s.0' % (cnt % 3)
  460. data = {
  461. 'milestone': mstone,
  462. 'csrf_token': csrf_token,
  463. }
  464. output = self.app.post(
  465. '/test/issue/%s/update' % cnt, data=data,
  466. follow_redirects=True)
  467. self.assertEqual(output.status_code, 200)
  468. output_text = output.get_data(as_text=True)
  469. self.assertIn(
  470. '<title>Issue #{0}: Test issue {0} - test - '
  471. 'Pagure</title>'.format(cnt),
  472. output_text)
  473. self.assertIn(
  474. '<a class="btn btn-outline-secondary btn-sm border-0" '
  475. 'href="/test/issue/%s/edit" title="Edit this issue">' % cnt,
  476. output_text)
  477. self.assertIn(
  478. 'Issue set to the milestone: %s' % mstone,
  479. output_text)
  480. self.assertIn(
  481. '<div class="ml-2" id="milestone_plain">', output_text)
  482. self.assertIn(
  483. '<a href="/test/roadmap/%s/">' % mstone, output_text)
  484. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  485. # Mark ticket #1 as Fixed
  486. for iid in [1, 4]:
  487. ticket = pagure.lib.query.search_issues(
  488. self.session,
  489. repo,
  490. issueid=iid
  491. )
  492. ticket.status = 'Closed'
  493. ticket.close_status = 'Fixed'
  494. self.session.add(ticket)
  495. self.session.commit()
  496. # test the roadmap view
  497. output = self.app.get('/test/roadmap')
  498. self.assertEqual(output.status_code, 200)
  499. output_text = output.get_data(as_text=True)
  500. self.assertIn(
  501. '<span class="fa fa-fw fa-map-signs"></span>\n'
  502. ' <span class="font'
  503. '-weight-bold">v1.0</span>',
  504. output_text)
  505. self.assertIn(
  506. '<span class="fa fa-fw fa-map-signs"></span>\n'
  507. ' <span class="font'
  508. '-weight-bold">unplanned</span>',
  509. output_text)
  510. self.assertIn(
  511. 'title="100% Completed | 2 Closed Issues | 0 Open Issues"\n',
  512. output_text)
  513. self.assertIn(
  514. 'title="0% Completed | 0 Closed Issues | 2 Open Issues"\n',
  515. output_text)
  516. self.assertIn(
  517. 'title="0% Completed | 0 Closed Issues | 2 Open Issues"\n',
  518. output_text)
  519. # test the roadmap view for a specific milestone
  520. output = self.app.get('/test/roadmap/v2.0/')
  521. self.assertEqual(output.status_code, 200)
  522. output_text = output.get_data(as_text=True)
  523. self.assertIn(
  524. '<span class="fa fa-fw fa-exclamation-circle"></span> 2 Open\n',
  525. output_text)
  526. self.assertIn(
  527. '<span class="fa fa-fw fa-exclamation-circle"></span> 0 Closed\n',
  528. output_text)
  529. self.assertIn(
  530. '<a class="notblue" href="/test/issue/2">', output_text)
  531. self.assertEquals(
  532. output_text.count('<a class="notblue" href="/test/issue/2">'),
  533. 1)
  534. # test the roadmap view for errors
  535. output = self.app.get('/foo/roadmap')
  536. self.assertEqual(output.status_code, 404)
  537. repo = pagure.lib.query.get_authorized_project(self.session, 'test')
  538. settings = repo.settings
  539. settings['issue_tracker'] = False
  540. repo.settings = settings
  541. self.session.add(repo)
  542. self.session.commit()
  543. output = self.app.get('/test/roadmap', data=data)
  544. self.assertEqual(output.status_code, 404)
  545. if __name__ == '__main__':
  546. unittest.main(verbosity=2)