test_pagure_flask_ui_roadmap.py 24 KB

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