You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

context_menus_controller_test.rb 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. # frozen_string_literal: true
  2. # Redmine - project management software
  3. # Copyright (C) 2006-2021 Jean-Philippe Lang
  4. #
  5. # This program is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License
  7. # as published by the Free Software Foundation; either version 2
  8. # of the License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. require File.expand_path('../../test_helper', __FILE__)
  19. class ContextMenusControllerTest < Redmine::ControllerTest
  20. fixtures :projects,
  21. :trackers,
  22. :projects_trackers,
  23. :roles,
  24. :member_roles,
  25. :members,
  26. :enabled_modules,
  27. :workflows,
  28. :journals, :journal_details,
  29. :versions,
  30. :issues, :issue_statuses, :issue_categories,
  31. :users,
  32. :enumerations,
  33. :time_entries,
  34. :custom_fields, :custom_fields_trackers, :custom_fields_projects
  35. def test_context_menu_one_issue_should_link_to_issue_path
  36. @request.session[:user_id] = 2
  37. get(
  38. :issues,
  39. :params => {
  40. :ids => [1]
  41. }
  42. )
  43. assert_response :success
  44. assert_select 'a.icon-edit[href=?]', '/issues/1/edit', :text => 'Edit'
  45. assert_select 'a.icon-copy-link[data-clipboard-text=?]', 'http://test.host/issues/1', :text => 'Copy link'
  46. assert_select 'a.icon-copy[href=?]', '/projects/ecookbook/issues/1/copy', :text => 'Copy'
  47. assert_select 'a.icon-del[href=?]', '/issues?ids%5B%5D=1', :text => 'Delete'
  48. # Statuses
  49. assert_select 'a[href=?][data-method="patch"]', '/issues/1?ids%5B%5D=1&issue%5Bstatus_id%5D=5', :text => 'Closed'
  50. assert_select 'a[href=?][data-method="patch"]', '/issues/1?ids%5B%5D=1&issue%5Bpriority_id%5D=8', :text => 'Immediate'
  51. # No inactive priorities
  52. assert_select 'a', :text => /Inactive Priority/, :count => 0
  53. # Versions
  54. assert_select 'a[href=?][data-method="patch"]', '/issues/1?ids%5B%5D=1&issue%5Bfixed_version_id%5D=3', :text => '2.0'
  55. assert_select 'a[href=?][data-method="patch"]', '/issues/1?ids%5B%5D=1&issue%5Bfixed_version_id%5D=4', :text => 'eCookbook Subproject 1 - 2.0'
  56. # Assignees
  57. assert_select 'a[href=?][data-method="patch"]', '/issues/1?ids%5B%5D=1&issue%5Bassigned_to_id%5D=3', :text => 'Dave Lopper'
  58. end
  59. def test_context_menu_multiple_issues_should_link_to_bulk_update_issues_path
  60. @request.session[:user_id] = 2
  61. get :issues, :params => {
  62. :ids => [1, 2]
  63. }
  64. assert_response :success
  65. assert_select 'a.icon-edit[href=?]', '/issues/bulk_edit?ids%5B%5D=1&ids%5B%5D=2', :text => 'Bulk edit'
  66. assert_select 'a.icon-copy[href=?]', '/issues/bulk_edit?copy=1&ids%5B%5D=1&ids%5B%5D=2', :text => 'Copy'
  67. assert_select 'a.icon-del[href=?]', '/issues?ids%5B%5D=1&ids%5B%5D=2', :text => 'Delete'
  68. # Statuses
  69. assert_select 'a[href=?][data-method="patch"]', '/issues/bulk_update?ids%5B%5D=1&ids%5B%5D=2&issue%5Bstatus_id%5D=5', :text => 'Closed'
  70. assert_select 'a[href=?][data-method="patch"]', '/issues/bulk_update?ids%5B%5D=1&ids%5B%5D=2&issue%5Bpriority_id%5D=8', :text => 'Immediate'
  71. # No inactive priorities
  72. assert_select 'a', :text => /Inactive Priority/, :count => 0
  73. # Versions
  74. assert_select 'a[href=?][data-method="patch"]', '/issues/bulk_update?ids%5B%5D=1&ids%5B%5D=2&issue%5Bfixed_version_id%5D=3', :text => '2.0'
  75. assert_select 'a[href=?][data-method="patch"]', '/issues/bulk_update?ids%5B%5D=1&ids%5B%5D=2&issue%5Bfixed_version_id%5D=4', :text => 'eCookbook Subproject 1 - 2.0'
  76. # Assignees
  77. assert_select 'a[href=?][data-method="patch"]', '/issues/bulk_update?ids%5B%5D=1&ids%5B%5D=2&issue%5Bassigned_to_id%5D=3', :text => 'Dave Lopper'
  78. end
  79. def test_context_menu_one_issue_by_anonymous
  80. with_settings :default_language => 'en' do
  81. get(
  82. :issues,
  83. :params => {
  84. :ids => [1]
  85. }
  86. )
  87. assert_response :success
  88. assert_select 'a.icon-del.disabled[href="#"]', :text => 'Delete'
  89. end
  90. end
  91. def test_context_menu_multiple_issues_of_same_project
  92. @request.session[:user_id] = 2
  93. get(
  94. :issues,
  95. :params => {
  96. :ids => [1, 2]
  97. }
  98. )
  99. assert_response :success
  100. ids = [1, 2].map {|i| "ids%5B%5D=#{i}"}.join('&')
  101. assert_select 'a.icon-edit[href=?]', "/issues/bulk_edit?#{ids}", :text => 'Bulk edit'
  102. # issue_id: '1,2', set_filter: 1, status_id: '*'
  103. assert_select 'a.icon-copy-link[data-clipboard-text=?]', "http://test.host/projects/ecookbook/issues?issue_id=1%2C2&set_filter=1&status_id=%2A", :text => 'Copy link'
  104. assert_select 'a.icon-copy[href=?]', "/issues/bulk_edit?copy=1&#{ids}", :text => 'Copy'
  105. assert_select 'a.icon-del[href=?]', "/issues?#{ids}", :text => 'Delete'
  106. assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&issue%5Bstatus_id%5D=5", :text => 'Closed'
  107. assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&issue%5Bpriority_id%5D=8", :text => 'Immediate'
  108. assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&issue%5Bassigned_to_id%5D=3", :text => 'Dave Lopper'
  109. end
  110. def test_context_menu_multiple_issues_of_different_projects
  111. @request.session[:user_id] = 2
  112. get(
  113. :issues,
  114. :params => {
  115. :ids => [1, 2, 6]
  116. }
  117. )
  118. assert_response :success
  119. ids = [1, 2, 6].map {|i| "ids%5B%5D=#{i}"}.join('&')
  120. assert_select 'a.icon-edit[href=?]', "/issues/bulk_edit?#{ids}", :text => 'Bulk edit'
  121. # issue_id: '1,2,6', set_filter: 1, status_id: '*'
  122. assert_select 'a.icon-copy-link[data-clipboard-text=?]', "http://test.host/issues?issue_id=1%2C2%2C6&set_filter=1&status_id=%2A", :text => 'Copy link'
  123. assert_select 'a.icon-del[href=?]', "/issues?#{ids}", :text => 'Delete'
  124. assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&issue%5Bstatus_id%5D=5", :text => 'Closed'
  125. assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&issue%5Bpriority_id%5D=8", :text => 'Immediate'
  126. assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&issue%5Bassigned_to_id%5D=2", :text => 'John Smith'
  127. end
  128. def test_context_menu_should_include_list_custom_fields
  129. field = IssueCustomField.create!(:name => 'List', :field_format => 'list',
  130. :possible_values => ['Foo', 'Bar'], :is_for_all => true, :tracker_ids => [1, 2, 3])
  131. @request.session[:user_id] = 2
  132. get(
  133. :issues,
  134. :params => {
  135. :ids => [1]
  136. }
  137. )
  138. assert_select "li.cf_#{field.id}" do
  139. assert_select 'a[href="#"]', :text => 'List'
  140. assert_select 'ul' do
  141. assert_select 'a', 3
  142. assert_select 'a[href=?]', "/issues/1?ids%5B%5D=1&issue%5Bcustom_field_values%5D%5B#{field.id}%5D=Foo", :text => 'Foo'
  143. assert_select 'a[href=?]', "/issues/1?ids%5B%5D=1&issue%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
  144. end
  145. end
  146. end
  147. def test_context_menu_multiple_issues_should_include_list_custom_fields
  148. field = IssueCustomField.create!(:name => 'List', :field_format => 'list',
  149. :possible_values => ['Foo', 'Bar'], :is_for_all => true, :tracker_ids => [1, 2, 3])
  150. @request.session[:user_id] = 2
  151. get(
  152. :issues,
  153. :params => {
  154. :ids => [1, 2]
  155. }
  156. )
  157. assert_select "li.cf_#{field.id}" do
  158. assert_select 'a[href="#"]', :text => 'List'
  159. assert_select 'ul' do
  160. assert_select 'a', 3
  161. assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&ids%5B%5D=2&issue%5Bcustom_field_values%5D%5B#{field.id}%5D=Foo", :text => 'Foo'
  162. assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&ids%5B%5D=2&issue%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
  163. end
  164. end
  165. end
  166. def test_context_menu_should_not_include_null_value_for_required_custom_fields
  167. field = IssueCustomField.create!(:name => 'List', :is_required => true, :field_format => 'list',
  168. :possible_values => ['Foo', 'Bar'], :is_for_all => true, :tracker_ids => [1, 2, 3])
  169. @request.session[:user_id] = 2
  170. get(
  171. :issues,
  172. :params => {
  173. :ids => [1, 2]
  174. }
  175. )
  176. assert_select "li.cf_#{field.id}" do
  177. assert_select 'a[href="#"]', :text => 'List'
  178. assert_select 'ul' do
  179. assert_select 'a', 2
  180. assert_select 'a', :text => 'none', :count => 0
  181. end
  182. end
  183. end
  184. def test_context_menu_on_single_issue_should_select_current_custom_field_value
  185. field = IssueCustomField.create!(:name => 'List', :field_format => 'list',
  186. :possible_values => ['Foo', 'Bar'], :is_for_all => true, :tracker_ids => [1, 2, 3])
  187. issue = Issue.find(1)
  188. issue.custom_field_values = {field.id => 'Bar'}
  189. issue.save!
  190. @request.session[:user_id] = 2
  191. get(
  192. :issues,
  193. :params => {
  194. :ids => [1]
  195. }
  196. )
  197. assert_select "li.cf_#{field.id}" do
  198. assert_select 'a[href="#"]', :text => 'List'
  199. assert_select 'ul' do
  200. assert_select 'a', 3
  201. assert_select 'a.icon.icon-checked', :text => 'Bar'
  202. end
  203. end
  204. end
  205. def test_context_menu_should_include_bool_custom_fields
  206. field = IssueCustomField.create!(:name => 'Bool', :field_format => 'bool',
  207. :is_for_all => true, :tracker_ids => [1, 2, 3])
  208. @request.session[:user_id] = 2
  209. get(
  210. :issues,
  211. :params => {
  212. :ids => [1]
  213. }
  214. )
  215. assert_select "li.cf_#{field.id}" do
  216. assert_select 'a[href="#"]', :text => 'Bool'
  217. assert_select 'ul' do
  218. assert_select 'a', 3
  219. assert_select 'a[href=?]', "/issues/1?ids%5B%5D=1&issue%5Bcustom_field_values%5D%5B#{field.id}%5D=0", :text => 'No'
  220. assert_select 'a[href=?]', "/issues/1?ids%5B%5D=1&issue%5Bcustom_field_values%5D%5B#{field.id}%5D=1", :text => 'Yes'
  221. assert_select 'a[href=?]', "/issues/1?ids%5B%5D=1&issue%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
  222. end
  223. end
  224. end
  225. def test_context_menu_should_include_user_custom_fields
  226. field = IssueCustomField.create!(:name => 'User', :field_format => 'user',
  227. :is_for_all => true, :tracker_ids => [1, 2, 3])
  228. @request.session[:user_id] = 2
  229. get(
  230. :issues,
  231. :params => {
  232. :ids => [1]
  233. }
  234. )
  235. assert_select "li.cf_#{field.id}" do
  236. assert_select 'a[href="#"]', :text => 'User'
  237. assert_select 'ul' do
  238. assert_select 'a', Project.find(1).members.count + 2 # users + 'none' + 'me'
  239. assert_select 'a[href=?]', "/issues/1?ids%5B%5D=1&issue%5Bcustom_field_values%5D%5B#{field.id}%5D=2", :text => 'John Smith'
  240. assert_select 'a[href=?]', "/issues/1?ids%5B%5D=1&issue%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
  241. end
  242. end
  243. end
  244. def test_context_menu_should_include_version_custom_fields
  245. field = IssueCustomField.create!(:name => 'Version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1, 2, 3])
  246. @request.session[:user_id] = 2
  247. get(
  248. :issues,
  249. :params => {
  250. :ids => [1]
  251. }
  252. )
  253. assert_select "li.cf_#{field.id}" do
  254. assert_select 'a[href="#"]', :text => 'Version'
  255. assert_select 'ul' do
  256. assert_select 'a', Project.find(1).shared_versions.count + 1
  257. assert_select 'a[href=?]', "/issues/1?ids%5B%5D=1&issue%5Bcustom_field_values%5D%5B#{field.id}%5D=3", :text => '2.0'
  258. assert_select 'a[href=?]', "/issues/1?ids%5B%5D=1&issue%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
  259. end
  260. end
  261. end
  262. def test_context_menu_should_show_enabled_custom_fields_for_the_role_only
  263. enabled_cf =
  264. IssueCustomField.generate!(
  265. :field_format => 'bool', :is_for_all => true,
  266. :tracker_ids => [1], :visible => false, :role_ids => [1, 2]
  267. )
  268. disabled_cf =
  269. IssueCustomField.generate!(
  270. :field_format => 'bool', :is_for_all => true,
  271. :tracker_ids => [1], :visible => false, :role_ids => [2]
  272. )
  273. issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
  274. @request.session[:user_id] = 2
  275. get(
  276. :issues,
  277. :params => {
  278. :ids => [issue.id]
  279. }
  280. )
  281. assert_select "li.cf_#{enabled_cf.id}"
  282. assert_select "li.cf_#{disabled_cf.id}", 0
  283. end
  284. def test_context_menu_by_assignable_user_should_include_assigned_to_me_link
  285. @request.session[:user_id] = 2
  286. get(
  287. :issues,
  288. :params => {
  289. :ids => [1, 2]
  290. }
  291. )
  292. assert_response :success
  293. assert_select 'a[href=?]', '/issues/bulk_update?ids%5B%5D=1&ids%5B%5D=2&issue%5Bassigned_to_id%5D=2', :text => / me /
  294. end
  295. def test_context_menu_should_propose_shared_versions_for_issues_from_different_projects
  296. @request.session[:user_id] = 2
  297. version = Version.create!(:name => 'Shared', :sharing => 'system', :project_id => 1)
  298. get(
  299. :issues,
  300. :params => {
  301. :ids => [1, 4]
  302. }
  303. )
  304. assert_response :success
  305. assert_select 'a', :text => 'eCookbook - Shared'
  306. end
  307. def test_context_menu_should_include_add_subtask_link
  308. @request.session[:user_id] = 2
  309. get(
  310. :issues,
  311. :params => {
  312. :ids => [1]
  313. }
  314. )
  315. assert_response :success
  316. assert_select 'a.icon-add[href=?]', '/projects/ecookbook/issues/new?issue%5Bparent_issue_id%5D=1&issue%5Btracker_id%5D=1', :text => 'Add subtask'
  317. end
  318. def test_context_menu_with_closed_issue_should_not_include_add_subtask_link
  319. @request.session[:user_id] = 2
  320. get(
  321. :issues,
  322. :params => {
  323. :ids => [8]
  324. }
  325. )
  326. assert_response :success
  327. assert_select 'a.icon-add', :text => 'Add subtask', :count => 0
  328. end
  329. def test_context_menu_multiple_issues_should_not_include_add_subtask_link
  330. @request.session[:user_id] = 2
  331. get(
  332. :issues,
  333. :params => {
  334. :ids => [1, 2]
  335. }
  336. )
  337. assert_response :success
  338. assert_select 'a.icon-add', :text => 'Add subtask', :count => 0
  339. end
  340. def test_context_menu_with_issue_that_is_not_visible_should_fail
  341. get(
  342. :issues,
  343. :params => {
  344. :ids => [1, 4] # issue 4 is not visible
  345. }
  346. )
  347. assert_response 302
  348. end
  349. def test_should_respond_with_404_without_ids
  350. get :issues
  351. assert_response 404
  352. end
  353. def test_time_entries_context_menu
  354. @request.session[:user_id] = 2
  355. get(
  356. :time_entries,
  357. :params => {
  358. :ids => [1, 2]
  359. }
  360. )
  361. assert_response :success
  362. assert_select 'a:not(.disabled)', :text => 'Bulk edit'
  363. end
  364. def test_context_menu_for_one_time_entry
  365. @request.session[:user_id] = 2
  366. get(
  367. :time_entries,
  368. :params => {
  369. :ids => [1]
  370. }
  371. )
  372. assert_response :success
  373. assert_select 'a:not(.disabled)', :text => 'Edit'
  374. end
  375. def test_time_entries_context_menu_should_include_custom_fields
  376. field = TimeEntryCustomField.generate!(:name => "Field", :field_format => "list", :possible_values => ["foo", "bar"])
  377. @request.session[:user_id] = 2
  378. get(
  379. :time_entries,
  380. :params => {
  381. :ids => [1, 2]
  382. }
  383. )
  384. assert_response :success
  385. assert_select "li.cf_#{field.id}" do
  386. assert_select 'a[href="#"]', :text => "Field"
  387. assert_select 'ul' do
  388. assert_select 'a', 3
  389. assert_select 'a[href=?]', "/time_entries/bulk_update?ids%5B%5D=1&ids%5B%5D=2&time_entry%5Bcustom_field_values%5D%5B#{field.id}%5D=foo", :text => 'foo'
  390. assert_select 'a[href=?]', "/time_entries/bulk_update?ids%5B%5D=1&ids%5B%5D=2&time_entry%5Bcustom_field_values%5D%5B#{field.id}%5D=bar", :text => 'bar'
  391. assert_select 'a[href=?]', "/time_entries/bulk_update?ids%5B%5D=1&ids%5B%5D=2&time_entry%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
  392. end
  393. end
  394. end
  395. def test_time_entries_context_menu_with_edit_own_time_entries_permission
  396. @request.session[:user_id] = 2
  397. Role.find_by_name('Manager').remove_permission! :edit_time_entries
  398. Role.find_by_name('Manager').add_permission! :edit_own_time_entries
  399. ids = (0..1).map {TimeEntry.generate!(:user => User.find(2)).id}
  400. get(
  401. :time_entries,
  402. :params => {
  403. :ids => ids
  404. }
  405. )
  406. assert_response :success
  407. assert_select 'a:not(.disabled)', :text => 'Bulk edit'
  408. end
  409. def test_time_entries_context_menu_without_edit_permission
  410. @request.session[:user_id] = 2
  411. Role.find_by_name('Manager').remove_permission! :edit_time_entries
  412. get(
  413. :time_entries,
  414. :params => {
  415. :ids => [1, 2]
  416. }
  417. )
  418. assert_response :success
  419. assert_select 'a.disabled', :text => 'Bulk edit'
  420. end
  421. end