test_pagure_flask_ui_roadmap.py 24 KB

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