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.

timelog_controller_test.rb 58KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836
  1. # frozen_string_literal: true
  2. # Redmine - project management software
  3. # Copyright (C) 2006- 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_relative '../test_helper'
  19. class TimelogControllerTest < Redmine::ControllerTest
  20. fixtures :projects, :enabled_modules, :roles, :members,
  21. :member_roles, :issues, :time_entries, :users, :email_addresses,
  22. :trackers, :enumerations, :issue_statuses,
  23. :custom_fields, :custom_values,
  24. :projects_trackers, :custom_fields_trackers,
  25. :custom_fields_projects, :issue_categories, :versions
  26. include Redmine::I18n
  27. def setup
  28. super
  29. Setting.default_language = 'en'
  30. end
  31. def test_new
  32. @request.session[:user_id] = 3
  33. get :new
  34. assert_response :success
  35. assert_select 'input[name=?][type=hidden]', 'project_id', 0
  36. assert_select 'input[name=?][type=hidden]', 'issue_id', 0
  37. assert_select 'span[id=?]', 'time_entry_issue'
  38. assert_select 'select[name=?]', 'time_entry[project_id]' do
  39. # blank option for project
  40. assert_select 'option[value=""]'
  41. end
  42. assert_select 'label[for=?]', 'time_entry_user_id', 0
  43. assert_select 'select[name=?]', 'time_entry[user_id]', 0
  44. end
  45. def test_new_with_project_id
  46. @request.session[:user_id] = 3
  47. get :new, :params => {:project_id => 1}
  48. assert_response :success
  49. assert_select 'input[name=?][type=hidden]', 'project_id'
  50. assert_select 'input[name=?][type=hidden]', 'issue_id', 0
  51. assert_select 'select[name=?]', 'time_entry[project_id]', 0
  52. end
  53. def test_new_with_issue_id
  54. @request.session[:user_id] = 3
  55. get :new, :params => {:issue_id => 2}
  56. assert_response :success
  57. assert_select 'input[name=?][type=hidden]', 'project_id', 0
  58. assert_select 'input[name=?][type=hidden]', 'issue_id'
  59. assert_select 'a[href=?]', '/issues/2', :text => /Feature request #2/
  60. assert_select 'select[name=?]', 'time_entry[project_id]', 0
  61. end
  62. def test_new_without_project_should_prefill_the_form
  63. @request.session[:user_id] = 3
  64. get :new, :params => {:time_entry => {:project_id => '1'}}
  65. assert_response :success
  66. assert_select 'select[name=?]', 'time_entry[project_id]' do
  67. assert_select 'option[value="1"][selected=selected]'
  68. end
  69. end
  70. def test_new_without_project_should_deny_without_permission
  71. Role.all.each {|role| role.remove_permission! :log_time}
  72. @request.session[:user_id] = 3
  73. get :new
  74. assert_response 403
  75. end
  76. def test_new_should_select_default_role_activity
  77. developer = Role.find(2)
  78. developer.default_time_entry_activity_id = 9
  79. developer.save!
  80. @request.session[:user_id] = 3
  81. get :new, :params => {:project_id => 1}
  82. assert_response :success
  83. assert_select 'select[name=?]', 'time_entry[activity_id]' do
  84. assert_select 'option[selected=selected]', :text => 'Design'
  85. end
  86. end
  87. def test_new_should_select_default_global_activity_for_user_roles_without_default_activities
  88. @request.session[:user_id] = 3
  89. get :new, :params => {:project_id => 1}
  90. assert_response :success
  91. assert_select 'select[name=?]', 'time_entry[activity_id]' do
  92. assert_select 'option[selected=selected]', :text => 'Development'
  93. end
  94. end
  95. def test_new_should_only_show_active_time_entry_activities
  96. @request.session[:user_id] = 3
  97. get :new, :params => {:project_id => 1}
  98. assert_response :success
  99. assert_select 'option', :text => 'Inactive Activity', :count => 0
  100. end
  101. def test_new_should_show_user_select_if_user_has_permission
  102. Role.find_by_name('Manager').add_permission! :log_time_for_other_users
  103. @request.session[:user_id] = 2
  104. get :new, :params => {:project_id => 1}
  105. assert_response :success
  106. assert_select 'select[name=?]', 'time_entry[user_id]' do
  107. assert_select 'option', 3
  108. assert_select 'option[value=?]', '2', 2
  109. assert_select 'option[value=?]', '3', 1
  110. # locked members should not be available
  111. assert_select 'option[value=?]', '4', 0
  112. end
  113. end
  114. def test_new_user_select_should_include_current_user_if_is_logged
  115. @request.session[:user_id] = 1
  116. get :new, :params => {:project_id => 1}
  117. assert_response :success
  118. assert_select 'select[name=?]', 'time_entry[user_id]' do
  119. assert_select 'option[value=?]', '1', :text => '<< me >>'
  120. assert_select 'option[value=?]', '1', :text => 'Redmine Admin'
  121. end
  122. end
  123. def test_new_should_not_show_user_select_if_user_does_not_have_permission
  124. @request.session[:user_id] = 2
  125. get :new, :params => {:project_id => 1}
  126. assert_response :success
  127. assert_select 'select[name=?]', 'time_entry[user_id]', 0
  128. end
  129. def test_post_new_as_js_should_update_activity_options
  130. @request.session[:user_id] = 3
  131. post :new, :params => {:time_entry => {:project_id => 1}, :format => 'js'}
  132. assert_response :success
  133. assert_include '#time_entry_activity_id', response.body
  134. end
  135. def test_get_edit_existing_time
  136. @request.session[:user_id] = 2
  137. get :edit, :params => {:id => 2, :project_id => nil}
  138. assert_response :success
  139. assert_select 'form[action=?]', '/time_entries/2'
  140. # Time entry user should be shown as text
  141. # for user without permission to log time for other users
  142. assert_select 'label[for=?]', 'time_entry_user_id', 1
  143. assert_select 'a.user.active', :text => 'Redmine Admin'
  144. end
  145. def test_get_edit_with_an_existing_time_entry_with_inactive_activity
  146. te = TimeEntry.find(1)
  147. te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
  148. te.save!(:validate => false)
  149. @request.session[:user_id] = 1
  150. get :edit, :params => {:project_id => 1, :id => 1}
  151. assert_response :success
  152. # Blank option since nothing is pre-selected
  153. assert_select 'option', :text => '--- Please select ---'
  154. end
  155. def test_get_edit_should_show_projects_select
  156. @request.session[:user_id] = 2
  157. get :edit, :params => {:id => 2, :project_id => nil}
  158. assert_response :success
  159. assert_select 'select[name=?]', 'time_entry[project_id]'
  160. end
  161. def test_get_edit_should_validate_back_url
  162. @request.session[:user_id] = 2
  163. get :edit, :params => {:id => 2, :project_id => nil, :back_url => '/valid'}
  164. assert_response :success
  165. assert_select 'a[href=?]', '/valid', {:text => 'Cancel'}
  166. get :edit, :params => {:id => 2, :project_id => nil, :back_url => 'invalid'}
  167. assert_response :success
  168. assert_select 'a[href=?]', 'invalid', {:text => 'Cancel', :count => 0}
  169. assert_select 'a[href=?]', '/projects/ecookbook/time_entries', {:text => 'Cancel'}
  170. end
  171. def test_get_edit_with_an_existing_time_entry_with_locked_user
  172. user = User.find(3)
  173. entry = TimeEntry.generate!(:user_id => user.id, :comments => "Time entry on a future locked user")
  174. entry.save!
  175. user.status = User::STATUS_LOCKED
  176. user.save!
  177. Role.find_by_name('Manager').add_permission! :log_time_for_other_users
  178. @request.session[:user_id] = 2
  179. get :edit, :params => {
  180. :id => entry.id
  181. }
  182. assert_response :success
  183. assert_select 'select[name=?]', 'time_entry[user_id]' do
  184. # User with id 3 should be selected even if it's locked
  185. assert_select 'option[value="3"][selected=selected]'
  186. end
  187. end
  188. def test_get_edit_for_other_user
  189. Role.find_by_name('Manager').add_permission! :log_time_for_other_users
  190. @request.session[:user_id] = 2
  191. get :edit, :params => {
  192. :id => 1
  193. }
  194. assert_response :success
  195. assert_select 'select[name=?]', 'time_entry[user_id]' do
  196. assert_select 'option[value="2"][selected=selected]', 1
  197. end
  198. end
  199. def test_post_create
  200. @request.session[:user_id] = 3
  201. assert_difference 'TimeEntry.count' do
  202. post(
  203. :create,
  204. :params => {
  205. :project_id => 1,
  206. :time_entry => {
  207. :comments => 'Some work on TimelogControllerTest',
  208. # Not the default activity
  209. :activity_id => '11',
  210. :spent_on => '2008-03-14',
  211. :issue_id => '1',
  212. :hours => '7.3'
  213. }
  214. }
  215. )
  216. assert_redirected_to '/projects/ecookbook/time_entries'
  217. end
  218. t = TimeEntry.order('id DESC').first
  219. assert_not_nil t
  220. assert_equal 'Some work on TimelogControllerTest', t.comments
  221. assert_equal 1, t.project_id
  222. assert_equal 1, t.issue_id
  223. assert_equal 11, t.activity_id
  224. assert_equal 7.3, t.hours
  225. assert_equal 3, t.user_id
  226. end
  227. def test_post_create_with_blank_issue
  228. @request.session[:user_id] = 3
  229. assert_difference 'TimeEntry.count' do
  230. post :create, :params => {
  231. :project_id => 1,
  232. :time_entry => {
  233. :comments => 'Some work on TimelogControllerTest',
  234. # Not the default activity
  235. :activity_id => '11',
  236. :issue_id => '',
  237. :spent_on => '2008-03-14',
  238. :hours => '7.3'
  239. }
  240. }
  241. assert_redirected_to '/projects/ecookbook/time_entries'
  242. end
  243. t = TimeEntry.order('id DESC').first
  244. assert_not_nil t
  245. assert_equal 'Some work on TimelogControllerTest', t.comments
  246. assert_equal 1, t.project_id
  247. assert_nil t.issue_id
  248. assert_equal 11, t.activity_id
  249. assert_equal 7.3, t.hours
  250. assert_equal 3, t.user_id
  251. end
  252. def test_create_on_project_with_time_tracking_disabled_should_fail
  253. Project.find(1).disable_module! :time_tracking
  254. @request.session[:user_id] = 2
  255. assert_no_difference 'TimeEntry.count' do
  256. post :create, :params => {
  257. :time_entry => {
  258. :project_id => '1', :issue_id => '',
  259. :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
  260. }
  261. }
  262. end
  263. end
  264. def test_create_on_project_without_permission_should_fail
  265. Role.find(1).remove_permission! :log_time
  266. @request.session[:user_id] = 2
  267. assert_no_difference 'TimeEntry.count' do
  268. post :create, :params => {
  269. :time_entry => {
  270. :project_id => '1', :issue_id => '',
  271. :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
  272. }
  273. }
  274. end
  275. end
  276. def test_create_on_issue_in_project_with_time_tracking_disabled_should_fail
  277. Project.find(1).disable_module! :time_tracking
  278. @request.session[:user_id] = 2
  279. assert_no_difference 'TimeEntry.count' do
  280. post :create, :params => {
  281. :time_entry => {
  282. :project_id => '', :issue_id => '1',
  283. :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
  284. }
  285. }
  286. assert_select_error /Issue is invalid/
  287. end
  288. end
  289. def test_create_on_issue_in_project_without_permission_should_fail
  290. Role.find(1).remove_permission! :log_time
  291. @request.session[:user_id] = 2
  292. assert_no_difference 'TimeEntry.count' do
  293. post :create, :params => {
  294. :time_entry => {
  295. :project_id => '', :issue_id => '1',
  296. :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
  297. }
  298. }
  299. assert_select_error /Issue is invalid/
  300. end
  301. end
  302. def test_create_on_issue_that_is_not_visible_should_not_disclose_subject
  303. issue = Issue.generate!(:subject => "issue_that_is_not_visible", :is_private => true)
  304. assert !issue.visible?(User.find(3))
  305. @request.session[:user_id] = 3
  306. assert_no_difference 'TimeEntry.count' do
  307. post :create, :params => {
  308. :time_entry => {
  309. :project_id => '', :issue_id => issue.id.to_s,
  310. :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
  311. }
  312. }
  313. end
  314. assert_select_error /Issue is invalid/
  315. assert_select "input[name=?][value=?]", "time_entry[issue_id]", issue.id.to_s
  316. assert_select "#time_entry_issue a", 0
  317. assert !response.body.include?('issue_that_is_not_visible')
  318. end
  319. def test_create_for_other_user
  320. Role.find_by_name('Manager').add_permission! :log_time_for_other_users
  321. @request.session[:user_id] = 2
  322. post(
  323. :create,
  324. :params => {
  325. :project_id => 1,
  326. :time_entry => {
  327. :comments => 'Some work on TimelogControllerTest',
  328. # Not the default activity
  329. :activity_id => '11',
  330. :spent_on => '2008-03-14',
  331. :issue_id => '1',
  332. :hours => '7.3',
  333. :user_id => '3'
  334. }
  335. }
  336. )
  337. assert_redirected_to '/projects/ecookbook/time_entries'
  338. t = TimeEntry.last
  339. assert_equal 3, t.user_id
  340. assert_equal 2, t.author_id
  341. end
  342. def test_create_for_other_user_should_fail_without_permission
  343. Role.find_by_name('Manager').remove_permission! :log_time_for_other_users
  344. @request.session[:user_id] = 2
  345. post(
  346. :create,
  347. :params => {
  348. :project_id => 1,
  349. :time_entry => {
  350. :comments => 'Some work on TimelogControllerTest',
  351. # Not the default activity
  352. :activity_id => '11',
  353. :spent_on => '2008-03-14',
  354. :issue_id => '1',
  355. :hours => '7.3',
  356. :user_id => '3'
  357. }
  358. }
  359. )
  360. assert_response :success
  361. assert_select_error /User is invalid/
  362. end
  363. def test_create_and_continue_at_project_level
  364. @request.session[:user_id] = 2
  365. assert_difference 'TimeEntry.count' do
  366. post :create, :params => {
  367. :time_entry => {
  368. :project_id => '1',
  369. :activity_id => '11',
  370. :issue_id => '',
  371. :spent_on => '2008-03-14',
  372. :hours => '7.3'
  373. },
  374. :continue => '1'
  375. }
  376. assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D=1&time_entry%5Bspent_on%5D=2008-03-14'
  377. end
  378. end
  379. def test_create_and_continue_at_issue_level
  380. @request.session[:user_id] = 2
  381. assert_difference 'TimeEntry.count' do
  382. post :create, :params => {
  383. :time_entry => {
  384. :project_id => '',
  385. :activity_id => '11',
  386. :issue_id => '1',
  387. :spent_on => '2008-03-14',
  388. :hours => '7.3'
  389. },
  390. :continue => '1'
  391. }
  392. assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=1&time_entry%5Bproject_id%5D=&time_entry%5Bspent_on%5D=2008-03-14'
  393. end
  394. end
  395. def test_create_and_continue_with_project_id
  396. @request.session[:user_id] = 2
  397. assert_difference 'TimeEntry.count' do
  398. post :create, :params => {
  399. :project_id => 1,
  400. :time_entry => {
  401. :activity_id => '11',
  402. :issue_id => '',
  403. :spent_on => '2008-03-14',
  404. :hours => '7.3'
  405. },
  406. :continue => '1'
  407. }
  408. assert_redirected_to '/projects/ecookbook/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D=1&time_entry%5Bspent_on%5D=2008-03-14'
  409. end
  410. end
  411. def test_create_and_continue_with_issue_id
  412. @request.session[:user_id] = 2
  413. assert_difference 'TimeEntry.count' do
  414. post :create, :params => {
  415. :issue_id => 1,
  416. :time_entry => {
  417. :activity_id => '11',
  418. :issue_id => '1',
  419. :spent_on => '2008-03-14',
  420. :hours => '7.3'
  421. },
  422. :continue => '1'
  423. }
  424. assert_redirected_to '/issues/1/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=1&time_entry%5Bproject_id%5D=&time_entry%5Bspent_on%5D=2008-03-14'
  425. end
  426. end
  427. def test_create_without_log_time_permission_should_be_denied
  428. @request.session[:user_id] = 2
  429. Role.find_by_name('Manager').remove_permission! :log_time
  430. post :create, :params => {
  431. :project_id => 1,
  432. :time_entry => {
  433. :activity_id => '11',
  434. :issue_id => '',
  435. :spent_on => '2008-03-14',
  436. :hours => '7.3'
  437. }
  438. }
  439. assert_response 403
  440. end
  441. def test_create_without_project_and_issue_should_fail
  442. @request.session[:user_id] = 2
  443. post :create, :params => {:time_entry => {:issue_id => ''}}
  444. assert_response :success
  445. assert_select_error /Project cannot be blank/
  446. end
  447. def test_create_with_failure
  448. @request.session[:user_id] = 2
  449. post :create, :params => {
  450. :project_id => 1,
  451. :time_entry => {
  452. :activity_id => '',
  453. :issue_id => '',
  454. :spent_on => '2008-03-14',
  455. :hours => '7.3'
  456. }
  457. }
  458. assert_response :success
  459. end
  460. def test_create_without_project
  461. @request.session[:user_id] = 2
  462. assert_difference 'TimeEntry.count' do
  463. post :create, :params => {
  464. :time_entry => {
  465. :project_id => '1',
  466. :activity_id => '11',
  467. :issue_id => '',
  468. :spent_on => '2008-03-14',
  469. :hours => '7.3'
  470. }
  471. }
  472. end
  473. assert_redirected_to '/projects/ecookbook/time_entries'
  474. time_entry = TimeEntry.order('id DESC').first
  475. assert_equal 1, time_entry.project_id
  476. end
  477. def test_create_without_project_should_fail_with_issue_not_inside_project
  478. @request.session[:user_id] = 2
  479. assert_no_difference 'TimeEntry.count' do
  480. post :create, :params => {
  481. :time_entry => {
  482. :project_id => '1',
  483. :activity_id => '11',
  484. :issue_id => '5',
  485. :spent_on => '2008-03-14',
  486. :hours => '7.3'
  487. }
  488. }
  489. end
  490. assert_response :success
  491. assert_select_error /Issue is invalid/
  492. end
  493. def test_create_without_project_should_deny_without_permission
  494. @request.session[:user_id] = 2
  495. Project.find(3).disable_module!(:time_tracking)
  496. assert_no_difference 'TimeEntry.count' do
  497. post :create, :params => {
  498. :time_entry => {
  499. :project_id => '3',
  500. :activity_id => '11',
  501. :issue_id => '',
  502. :spent_on => '2008-03-14',
  503. :hours => '7.3'
  504. }
  505. }
  506. end
  507. assert_response 403
  508. end
  509. def test_create_without_project_with_failure
  510. @request.session[:user_id] = 2
  511. assert_no_difference 'TimeEntry.count' do
  512. post :create, :params => {
  513. :time_entry => {
  514. :project_id => '1',
  515. :activity_id => '11',
  516. :issue_id => '',
  517. :spent_on => '2008-03-14',
  518. :hours => ''
  519. }
  520. }
  521. end
  522. assert_response :success
  523. assert_select 'select[name=?]', 'time_entry[project_id]' do
  524. assert_select 'option[value="1"][selected=selected]'
  525. end
  526. end
  527. def test_update
  528. entry = TimeEntry.find(1)
  529. assert_equal 1, entry.issue_id
  530. assert_equal 2, entry.user_id
  531. @request.session[:user_id] = 1
  532. put :update, :params => {
  533. :id => 1,
  534. :time_entry => {
  535. :issue_id => '2',
  536. :hours => '8'
  537. }
  538. }
  539. assert_redirected_to :action => 'index', :project_id => 'ecookbook'
  540. entry.reload
  541. assert_equal 8, entry.hours
  542. assert_equal 2, entry.issue_id
  543. assert_equal 2, entry.user_id
  544. end
  545. def test_update_should_allow_to_change_issue_to_another_project
  546. entry = TimeEntry.generate!(:issue_id => 1)
  547. @request.session[:user_id] = 1
  548. put :update, :params => {
  549. :id => entry.id,
  550. :time_entry => {
  551. :issue_id => '5'
  552. }
  553. }
  554. assert_response 302
  555. entry.reload
  556. assert_equal 5, entry.issue_id
  557. assert_equal 3, entry.project_id
  558. end
  559. def test_update_should_not_allow_to_change_issue_to_an_invalid_project
  560. entry = TimeEntry.generate!(:issue_id => 1)
  561. Project.find(3).disable_module!(:time_tracking)
  562. @request.session[:user_id] = 1
  563. put :update, :params => {
  564. :id => entry.id,
  565. :time_entry => {
  566. :issue_id => '5'
  567. }
  568. }
  569. assert_response :success
  570. assert_select_error /Issue is invalid/
  571. end
  572. def test_update_should_allow_to_change_project
  573. entry = TimeEntry.generate!(:project_id => 1)
  574. @request.session[:user_id] = 1
  575. put :update, :params => {
  576. :id => entry.id,
  577. :time_entry => {
  578. :project_id => '2'
  579. }
  580. }
  581. assert_response 302
  582. entry.reload
  583. assert_equal 2, entry.project_id
  584. end
  585. def test_update_should_fail_with_issue_from_another_project
  586. entry = TimeEntry.generate!(:project_id => 1, :issue_id => 1)
  587. @request.session[:user_id] = 1
  588. put :update, :params => {
  589. :id => entry.id,
  590. :time_entry => {
  591. :project_id => '2'
  592. }
  593. }
  594. assert_response :success
  595. assert_select_error /Issue is invalid/
  596. end
  597. def test_update_should_fail_when_changing_user_without_permission
  598. Role.find_by_name('Manager').remove_permission! :log_time_for_other_users
  599. @request.session[:user_id] = 2
  600. put :update, :params => {
  601. :id => 3,
  602. :time_entry => {
  603. :user_id => '3'
  604. }
  605. }
  606. assert_response :success
  607. assert_select_error /User is invalid/
  608. end
  609. def test_update_should_allow_updating_existing_entry_logged_on_a_locked_user
  610. entry = TimeEntry.generate!(:user_id => 2, :hours => 4, :comments => "Time entry on a future locked user")
  611. Role.find_by_name('Manager').add_permission! :log_time_for_other_users
  612. @request.session[:user_id] = 2
  613. put :update, :params => {
  614. :id => entry.id,
  615. :time_entry => {
  616. :hours => '6'
  617. }
  618. }
  619. assert_response :redirect
  620. entry.reload
  621. # Ensure user didn't change
  622. assert_equal 2, entry.user_id
  623. assert_equal 6.0, entry.hours
  624. end
  625. def test_get_bulk_edit
  626. @request.session[:user_id] = 2
  627. get :bulk_edit, :params => {:ids => [1, 2]}
  628. assert_response :success
  629. assert_select 'ul#bulk-selection' do
  630. assert_select 'li', 2
  631. assert_select 'li a', :text => '03/23/2007 - eCookbook: 4.25 hours (John Smith)'
  632. end
  633. assert_select 'form#bulk_edit_form[action=?]', '/time_entries/bulk_update' do
  634. assert_select 'select[name=?]', 'time_entry[project_id]'
  635. # Clear issue checkbox
  636. assert_select 'input[name=?][value=?]', 'time_entry[issue_id]', 'none'
  637. # System wide custom field
  638. assert_select 'select[name=?]', 'time_entry[custom_field_values][10]'
  639. # Activities
  640. assert_select 'select[name=?]', 'time_entry[activity_id]' do
  641. assert_select 'option[value=""]', :text => '(No change)'
  642. assert_select 'option[value="9"]', :text => 'Design'
  643. end
  644. end
  645. end
  646. def test_get_bulk_edit_on_different_projects
  647. @request.session[:user_id] = 2
  648. get :bulk_edit, :params => {:ids => [1, 2, 6]}
  649. assert_response :success
  650. end
  651. def test_get_bulk_edit_on_different_projects_should_propose_only_common_activites
  652. project = Project.find(3)
  653. TimeEntryActivity.create!(:name => 'QA', :project => project, :parent => TimeEntryActivity.find_by_name('QA'), :active => false)
  654. @request.session[:user_id] = 1
  655. get :bulk_edit, :params => {:ids => [1, 2, 4]}
  656. assert_response :success
  657. assert_select 'select[id=?]', 'time_entry_activity_id' do
  658. assert_select 'option', 3
  659. assert_select 'option[value=?]', '11', 0
  660. end
  661. end
  662. def test_get_bulk_edit_on_same_project_should_propose_project_activities
  663. project = Project.find(1)
  664. override_activity = TimeEntryActivity.create!({:name => "QA override", :parent => TimeEntryActivity.find_by_name("QA"), :project => project})
  665. @request.session[:user_id] = 1
  666. get :bulk_edit, :params => {:ids => [1, 2]}
  667. assert_response :success
  668. assert_select 'select[id=?]', 'time_entry_activity_id' do
  669. assert_select 'option', 4
  670. assert_select 'option[value=?]', override_activity.id.to_s, :text => 'QA override'
  671. end
  672. end
  673. def test_bulk_edit_with_edit_own_time_entries_permission
  674. @request.session[:user_id] = 2
  675. Role.find_by_name('Manager').remove_permission! :edit_time_entries
  676. Role.find_by_name('Manager').add_permission! :edit_own_time_entries
  677. ids = (0..1).map {TimeEntry.generate!(:user => User.find(2)).id}
  678. get :bulk_edit, :params => {:ids => ids}
  679. assert_response :success
  680. end
  681. def test_bulk_update
  682. @request.session[:user_id] = 2
  683. # update time entry activity
  684. post :bulk_update, :params => {:ids => [1, 2], :time_entry => {:activity_id => 9}}
  685. assert_response 302
  686. # check that the issues were updated
  687. assert_equal [9, 9], TimeEntry.where(:id => [1, 2]).collect {|i| i.activity_id}
  688. end
  689. def test_bulk_update_with_failure
  690. @request.session[:user_id] = 2
  691. post :bulk_update, :params => {:ids => [1, 2], :time_entry => {:hours => 'A'}}
  692. assert_response :success
  693. assert_select_error /Failed to save 2 time entrie/
  694. end
  695. def test_bulk_update_on_different_projects
  696. @request.session[:user_id] = 2
  697. # makes user a manager on the other project
  698. Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
  699. # update time entry activity
  700. post :bulk_update, :params => {:ids => [1, 2, 4], :time_entry => {:activity_id => 9}}
  701. assert_response 302
  702. # check that the issues were updated
  703. assert_equal [9, 9, 9], TimeEntry.where(:id => [1, 2, 4]).collect {|i| i.activity_id}
  704. end
  705. def test_bulk_update_on_different_projects_without_rights
  706. @request.session[:user_id] = 3
  707. user = User.find(3)
  708. action = {:controller => "timelog", :action => "bulk_update"}
  709. assert user.allowed_to?(action, TimeEntry.find(1).project)
  710. assert ! user.allowed_to?(action, TimeEntry.find(5).project)
  711. post :bulk_update, :params => {:ids => [1, 5], :time_entry => {:activity_id => 9}}
  712. assert_response 403
  713. end
  714. def test_bulk_update_with_edit_own_time_entries_permission
  715. @request.session[:user_id] = 2
  716. Role.find_by_name('Manager').remove_permission! :edit_time_entries
  717. Role.find_by_name('Manager').add_permission! :edit_own_time_entries
  718. ids = (0..1).map {TimeEntry.generate!(:user => User.find(2)).id}
  719. post :bulk_update, :params => {:ids => ids, :time_entry => {:activity_id => 9}}
  720. assert_response 302
  721. end
  722. def test_bulk_update_with_edit_own_time_entries_permissions_should_be_denied_for_time_entries_of_other_user
  723. @request.session[:user_id] = 2
  724. Role.find_by_name('Manager').remove_permission! :edit_time_entries
  725. Role.find_by_name('Manager').add_permission! :edit_own_time_entries
  726. post :bulk_update, :params => {:ids => [1, 2], :time_entry => {:activity_id => 9}}
  727. assert_response 403
  728. end
  729. def test_bulk_update_custom_field
  730. @request.session[:user_id] = 2
  731. post(
  732. :bulk_update,
  733. :params => {
  734. :ids => [1, 2],
  735. :time_entry => {:custom_field_values => {'10' => '0'}}
  736. }
  737. )
  738. assert_response 302
  739. assert_equal ["0", "0"], TimeEntry.where(:id => [1, 2]).collect {|i| i.custom_value_for(10).value}
  740. end
  741. def test_bulk_update_clear_custom_field
  742. field = TimeEntryCustomField.generate!(:field_format => 'string')
  743. @request.session[:user_id] = 2
  744. post(
  745. :bulk_update,
  746. :params => {
  747. :ids => [1, 2],
  748. :time_entry => {:custom_field_values => {field.id.to_s => '__none__'}}
  749. }
  750. )
  751. assert_response 302
  752. assert_equal ["", ""], TimeEntry.where(:id => [1, 2]).collect {|i| i.custom_value_for(field).value}
  753. end
  754. def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
  755. @request.session[:user_id] = 2
  756. post :bulk_update, :params => {:ids => [1, 2], :back_url => '/time_entries'}
  757. assert_response :redirect
  758. assert_redirected_to '/time_entries'
  759. end
  760. def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
  761. @request.session[:user_id] = 2
  762. post :bulk_update, :params => {:ids => [1, 2], :back_url => 'http://google.com'}
  763. assert_response :redirect
  764. assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
  765. end
  766. def test_post_bulk_update_without_edit_permission_should_be_denied
  767. @request.session[:user_id] = 2
  768. Role.find_by_name('Manager').remove_permission! :edit_time_entries
  769. post :bulk_update, :params => {:ids => [1, 2]}
  770. assert_response 403
  771. end
  772. def test_destroy
  773. @request.session[:user_id] = 2
  774. delete :destroy, :params => {:id => 1}
  775. assert_redirected_to :action => 'index', :project_id => 'ecookbook'
  776. assert_equal I18n.t(:notice_successful_delete), flash[:notice]
  777. assert_nil TimeEntry.find_by_id(1)
  778. end
  779. def test_destroy_should_fail
  780. # simulate that this fails (e.g. due to a plugin), see #5700
  781. TimeEntry.any_instance.expects(:destroy).returns(false)
  782. @request.session[:user_id] = 2
  783. delete :destroy, :params => {:id => 1}
  784. assert_redirected_to :action => 'index', :project_id => 'ecookbook'
  785. assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
  786. assert_not_nil TimeEntry.find_by_id(1)
  787. end
  788. def test_destroy_should_redirect_to_referer
  789. referer = 'http://test.host/time_entries?utf8=✓&set_filter=1&&f%5B%5D=user_id&op%5Buser_id%5D=%3D&v%5Buser_id%5D%5B%5D=me'
  790. @request.env["HTTP_REFERER"] = referer
  791. @request.session[:user_id] = 2
  792. delete :destroy, :params => {:id => 1}
  793. assert_redirected_to referer
  794. end
  795. def test_index_all_projects
  796. get :index
  797. assert_response :success
  798. assert_select '.total-for-hours', :text => 'Hours: 162:54'
  799. assert_select 'form#query_form[action=?]', '/time_entries'
  800. assert_equal ['Project', 'Date', 'User', 'Activity', 'Issue', 'Comment', 'Hours'], columns_in_list
  801. assert_select '.query-totals>span', 1
  802. end
  803. def test_index_with_default_query_setting
  804. with_settings :time_entry_list_defaults => {'column_names' => %w(spent_on issue user hours), 'totalable_names' => []} do
  805. get :index
  806. assert_response :success
  807. end
  808. assert_select 'table.time-entries thead' do
  809. assert_select 'th.project'
  810. assert_select 'th.spent_on'
  811. assert_select 'th.issue'
  812. assert_select 'th.user'
  813. assert_select 'th.hours'
  814. end
  815. assert_select 'table.time-entries tbody' do
  816. assert_select 'td.project'
  817. assert_select 'td.spent_on'
  818. assert_select 'td.issue'
  819. assert_select 'td.user'
  820. assert_select 'td.hours'
  821. end
  822. assert_equal ['Project', 'Date', 'Issue', 'User', 'Hours'], columns_in_list
  823. end
  824. def test_index_with_default_query_setting_using_custom_field
  825. field = TimeEntryCustomField.create!(:name => 'Foo', :field_format => 'int')
  826. with_settings(
  827. :time_entry_list_defaults => {
  828. 'column_names' => ["spent_on", "user", "hours", "cf_#{field.id}"],
  829. 'totalable_names' => ["hours", "cf_#{field.id}"]
  830. }
  831. ) do
  832. get :index
  833. assert_response :success
  834. end
  835. assert_equal ['Project', 'Date', 'User', 'Hours', 'Foo'], columns_in_list
  836. assert_select '.total-for-hours'
  837. assert_select ".total-for-cf-#{field.id}"
  838. assert_select '.query-totals>span', 2
  839. end
  840. def test_index_all_projects_should_show_log_time_link
  841. @request.session[:user_id] = 2
  842. get :index
  843. assert_response :success
  844. assert_select 'a[href=?]', '/time_entries/new', :text => /Log time/
  845. end
  846. def test_index_my_spent_time
  847. @request.session[:user_id] = 2
  848. get :index, :params => {:user_id => 'me', :c => ['user']}
  849. assert_response :success
  850. users = css_select('table.time-entries tbody td.user').map(&:text).uniq
  851. assert_equal ["John Smith"], users
  852. end
  853. def test_index_at_project_level
  854. @request.session[:user_id] = 2
  855. get :index, :params => {:project_id => 'ecookbook', :c => ['project']}
  856. assert_response :success
  857. assert_select 'tr.time-entry', 4
  858. # project and subproject
  859. projects = css_select('table.time-entries tbody td.project').map(&:text).uniq.sort
  860. assert_equal ["eCookbook", "eCookbook Subproject 1"], projects
  861. assert_select '.total-for-hours', :text => 'Hours: 162:54'
  862. assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
  863. # 'Log time' shoudl link to log time on the filtered issue
  864. assert_select 'a[href=?]', "/projects/ecookbook/time_entries/new"
  865. end
  866. def test_index_with_display_subprojects_issues_to_false_should_not_include_subproject_entries
  867. entry = TimeEntry.generate!(:project => Project.find(3))
  868. with_settings :display_subprojects_issues => '0' do
  869. get :index, :params => {:project_id => 'ecookbook', :c => ['project']}
  870. assert_response :success
  871. projects = css_select('table.time-entries tbody td.project').map(&:text).uniq.sort
  872. assert_equal ["eCookbook"], projects
  873. end
  874. end
  875. def test_index_with_display_subprojects_issues_to_false_and_subproject_filter_should_include_subproject_entries
  876. entry = TimeEntry.generate!(:project => Project.find(3))
  877. with_settings :display_subprojects_issues => '0' do
  878. get :index, :params => {:project_id => 'ecookbook', :c => ['project'], :subproject_id => 3}
  879. assert_response :success
  880. projects = css_select('table.time-entries tbody td.project').map(&:text).uniq.sort
  881. assert_equal ["eCookbook", "eCookbook Subproject 1"], projects
  882. end
  883. end
  884. def test_index_at_project_level_with_issue_id_short_filter
  885. issue = Issue.generate!(:project_id => 1)
  886. TimeEntry.generate!(:issue => issue, :hours => 4)
  887. TimeEntry.generate!(:issue => issue, :hours => 3)
  888. @request.session[:user_id] = 2
  889. get :index, :params => {:project_id => 'ecookbook', :issue_id => issue.id.to_s, :set_filter => 1}
  890. assert_select '.total-for-hours', :text => 'Hours: 7:00'
  891. # 'Log time' shoudl link to log time on the filtered issue
  892. assert_select 'a[href=?]', "/issues/#{issue.id}/time_entries/new"
  893. end
  894. def test_index_at_project_level_with_issue_fixed_version_id_short_filter
  895. version = Version.generate!(:project_id => 1)
  896. issue = Issue.generate!(:project_id => 1, :fixed_version => version)
  897. TimeEntry.generate!(:issue => issue, :hours => 2)
  898. TimeEntry.generate!(:issue => issue, :hours => 3)
  899. @request.session[:user_id] = 2
  900. get :index, :params => {:project_id => 'ecookbook', :"issue.fixed_version_id" => version.id.to_s, :set_filter => 1}
  901. assert_select '.total-for-hours', :text => 'Hours: 5:00'
  902. end
  903. def test_index_at_project_level_with_multiple_issue_fixed_version_ids
  904. version = Version.generate!(:project_id => 1)
  905. version2 = Version.generate!(:project_id => 1)
  906. issue = Issue.generate!(:project_id => 1, :fixed_version => version)
  907. issue2 = Issue.generate!(:project_id => 1, :fixed_version => version2)
  908. TimeEntry.generate!(:issue => issue, :hours => 2)
  909. TimeEntry.generate!(:issue => issue2, :hours => 3)
  910. @request.session[:user_id] = 2
  911. get(
  912. :index,
  913. :params => {
  914. :project_id => 'ecookbook',
  915. :f => ['issue.fixed_version_id'],
  916. :op => {'issue.fixed_version_id' => '='},
  917. :v => {'issue.fixed_version_id' => [version.id.to_s, version2.id.to_s]}
  918. }
  919. )
  920. assert_response :success
  921. assert_select 'tr.time-entry', 2
  922. assert_select '.total-for-hours', :text => 'Hours: 5:00'
  923. end
  924. def test_index_at_project_level_with_date_range
  925. get(
  926. :index,
  927. :params => {
  928. :project_id => 'ecookbook',
  929. :f => ['spent_on'],
  930. :op => {'spent_on' => '><'},
  931. :v => {'spent_on' => ['2007-03-20', '2007-04-30']}
  932. }
  933. )
  934. assert_response :success
  935. assert_select 'tr.time-entry', 3
  936. assert_select '.total-for-hours', :text => 'Hours: 12:54'
  937. assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
  938. end
  939. def test_index_at_project_level_with_date_range_using_from_and_to_params
  940. get(
  941. :index,
  942. :params => {
  943. :project_id => 'ecookbook',
  944. :from => '2007-03-20',
  945. :to => '2007-04-30'
  946. }
  947. )
  948. assert_response :success
  949. assert_select 'tr.time-entry', 3
  950. assert_select '.total-for-hours', :text => 'Hours: 12:54'
  951. assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
  952. end
  953. def test_index_at_project_level_with_period
  954. get :index, :params => {
  955. :project_id => 'ecookbook',
  956. :f => ['spent_on'],
  957. :op => {'spent_on' => '>t-'},
  958. :v => {'spent_on' => ['7']}
  959. }
  960. assert_response :success
  961. assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
  962. end
  963. def test_index_should_sort_by_spent_on_and_created_on
  964. t1 =
  965. TimeEntry.create!(
  966. :author => User.find(1), :user => User.find(1),
  967. :project => Project.find(1),
  968. :hours => 1,
  969. :spent_on => '2012-06-16', :created_on => '2012-06-16 20:00:00',
  970. :activity_id => 10
  971. )
  972. t2 =
  973. TimeEntry.create!(
  974. :author => User.find(1), :user => User.find(1),
  975. :project => Project.find(1),
  976. :hours => 1,
  977. :spent_on => '2012-06-16', :created_on => '2012-06-16 20:05:00',
  978. :activity_id => 10
  979. )
  980. t3 =
  981. TimeEntry.create!(
  982. :author => User.find(1), :user => User.find(1),
  983. :project => Project.find(1),
  984. :hours => 1, :spent_on => '2012-06-15', :created_on => '2012-06-16 20:10:00',
  985. :activity_id => 10
  986. )
  987. get :index, :params => {
  988. :project_id => 1,
  989. :f => ['spent_on'],
  990. :op => {'spent_on' => '><'},
  991. :v => {'spent_on' => ['2012-06-15', '2012-06-16']}
  992. }
  993. assert_response :success
  994. assert_equal(
  995. [t2, t1, t3].map {|t| t.id.to_s},
  996. css_select('input[name="ids[]"]').map {|e| e.attr(:value)}
  997. )
  998. get(
  999. :index,
  1000. :params => {
  1001. :project_id => 1,
  1002. :f => ['spent_on'],
  1003. :op => {'spent_on' => '><'},
  1004. :v => {'spent_on' => ['2012-06-15', '2012-06-16']},
  1005. :sort => 'spent_on'
  1006. }
  1007. )
  1008. assert_response :success
  1009. assert_equal(
  1010. [t3, t1, t2].map {|t| t.id.to_s},
  1011. css_select('input[name="ids[]"]').map {|e| e.attr(:value)}
  1012. )
  1013. end
  1014. def test_index_should_sort_by_tweek_and_spent_on
  1015. t1 = TimeEntry.generate!(:spent_on => '2012-06-10') # tyear:2012, tweek:23
  1016. t2 = TimeEntry.generate!(:spent_on => '2012-06-11') # tyear:2012, tweek:24
  1017. t3 = TimeEntry.generate!(:spent_on => '2012-06-12') # tyear:2012, tweek:24
  1018. t4 = TimeEntry.generate!(:spent_on => '2013-06-12') # tyear:2013, tweek:24
  1019. params = {
  1020. :project_id => 1,
  1021. :f => ['spent_on'],
  1022. :op => {'spent_on' => '><'},
  1023. :v => {'spent_on' => ['2012-06-10', '2013-06-12']}
  1024. }
  1025. [
  1026. [{:sort => 'tweek,spent_on'}, [t1, t2, t3, t4]],
  1027. [{:sort => 'tweek,spent_on:desc'}, [t1, t3, t2, t4]],
  1028. [{:sort => 'tweek:desc,spent_on'}, [t4, t2, t3, t1]],
  1029. [{:sort => 'tweek:desc,spent_on:desc'}, [t4, t3, t2, t1]],
  1030. ].each do |sort_criteria, expected|
  1031. get :index, :params => params.dup.merge(sort_criteria)
  1032. assert_response :success
  1033. expected_ids = expected.map {|t| t.id.to_s}
  1034. actual_ids = css_select('input[name="ids[]"]').map {|e| e.attr(:value)}
  1035. assert_equal expected_ids, actual_ids
  1036. end
  1037. end
  1038. def test_index_with_activity_filter
  1039. activity = TimeEntryActivity.create!(:name => 'Activity')
  1040. entry = TimeEntry.generate!(:issue_id => 1, :hours => 4.5, :activity => activity)
  1041. get(
  1042. :index,
  1043. :params => {
  1044. :f => ['activity_id'],
  1045. :op => {'activity_id' => '='},
  1046. :v => {'activity_id' => [activity.id.to_s]}
  1047. }
  1048. )
  1049. assert_response :success
  1050. assert_select "tr#time-entry-#{entry.id}"
  1051. assert_select "table.time-entries tbody tr", 1
  1052. end
  1053. def test_index_with_issue_status_filter
  1054. Issue.where(:status_id => 4).update_all(:status_id => 2)
  1055. issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :status_id => 4)
  1056. entry = TimeEntry.generate!(:issue => issue, :hours => 4.5)
  1057. get(
  1058. :index,
  1059. :params => {
  1060. :f => ['issue.status_id'],
  1061. :op => {'issue.status_id' => '='},
  1062. :v => {'issue.status_id' => ['4']}
  1063. }
  1064. )
  1065. assert_response :success
  1066. assert_equal [entry.id.to_s], css_select('input[name="ids[]"]').map {|e| e.attr(:value)}
  1067. end
  1068. def text_index_with_issue_subject_filter
  1069. get(
  1070. :index,
  1071. :params => {
  1072. :f => ['issue.subject'],
  1073. :op => {'issue.subject' => '~'},
  1074. :v => {'issue.subject' => ['"updating a recipe"']}
  1075. }
  1076. )
  1077. assert_response :success
  1078. assert_equal [3], css_select('input[name="ids[]"]').map {|e| e.attr(:value)}
  1079. end
  1080. def test_index_with_project_status_filter
  1081. project = Project.find(3)
  1082. project.close
  1083. project.save
  1084. get(
  1085. :index,
  1086. :params => {
  1087. :set_filter => 1,
  1088. :f => ['project.status'],
  1089. :op => {'project.status' => '='},
  1090. :v => {'project.status' => ['1']}
  1091. }
  1092. )
  1093. assert_response :success
  1094. time_entries = css_select('input[name="ids[]"]').map {|e| e.attr(:value)}
  1095. assert_include '1', time_entries
  1096. assert_not_include '4', time_entries
  1097. end
  1098. def test_index_with_issue_status_column
  1099. issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :status_id => 4)
  1100. entry = TimeEntry.generate!(:issue => issue)
  1101. get :index, :params => {
  1102. :c => %w(project spent_on issue comments hours issue.status)
  1103. }
  1104. assert_response :success
  1105. assert_select 'th.issue-status'
  1106. assert_select 'td.issue-status', :text => issue.status.name
  1107. end
  1108. def test_index_with_issue_status_sort
  1109. TimeEntry.delete_all
  1110. TimeEntry.generate!(:issue => Issue.generate!(:project_id => 1, :tracker_id => 1, :status_id => 1))
  1111. TimeEntry.generate!(:issue => Issue.generate!(:project_id => 1, :tracker_id => 1, :status_id => 5))
  1112. TimeEntry.generate!(:issue => Issue.generate!(:project_id => 1, :tracker_id => 1, :status_id => 3))
  1113. TimeEntry.generate!(:project_id => 1)
  1114. get :index, :params => {
  1115. :c => ["hours", 'issue.status'],
  1116. :sort => 'issue.status'
  1117. }
  1118. assert_response :success
  1119. # Make sure that values are properly sorted
  1120. values = css_select("td.issue-status").map(&:text).reject(&:blank?)
  1121. assert_equal IssueStatus.where(:id => [1, 5, 3]).sorted.pluck(:name), values
  1122. end
  1123. def test_index_with_issue_tracker_filter
  1124. Issue.where(:tracker_id => 2).update_all(:tracker_id => 1)
  1125. issue = Issue.generate!(:project_id => 1, :tracker_id => 2)
  1126. entry = TimeEntry.generate!(:issue => issue, :hours => 4.5)
  1127. get :index, :params => {
  1128. :f => ['issue.tracker_id'],
  1129. :op => {'issue.tracker_id' => '='},
  1130. :v => {'issue.tracker_id' => ['2']}
  1131. }
  1132. assert_response :success
  1133. assert_equal [entry.id.to_s], css_select('input[name="ids[]"]').map {|e| e.attr(:value)}
  1134. end
  1135. def test_index_with_issue_tracker_column
  1136. issue = Issue.generate!(:project_id => 1, :tracker_id => 2)
  1137. entry = TimeEntry.generate!(:issue => issue)
  1138. get :index, :params => {
  1139. :c => %w(project spent_on issue comments hours issue.tracker)
  1140. }
  1141. assert_response :success
  1142. assert_select 'td.issue-tracker', :text => issue.tracker.name
  1143. end
  1144. def test_index_with_issue_tracker_sort
  1145. TimeEntry.delete_all
  1146. TimeEntry.generate!(:issue => Issue.generate!(:tracker_id => 1))
  1147. TimeEntry.generate!(:issue => Issue.generate!(:tracker_id => 3))
  1148. TimeEntry.generate!(:issue => Issue.generate!(:tracker_id => 2))
  1149. TimeEntry.generate!(:project_id => 1)
  1150. get :index, :params => {
  1151. :c => ["hours", 'issue.tracker'],
  1152. :sort => 'issue.tracker'
  1153. }
  1154. assert_response :success
  1155. # Make sure that values are properly sorted
  1156. values = css_select("td.issue-tracker").map(&:text).reject(&:blank?)
  1157. assert_equal Tracker.where(:id => [1, 2, 3]).sorted.pluck(:name), values
  1158. end
  1159. def test_index_with_issue_category_filter
  1160. get :index, :params => {
  1161. :project_id => 'ecookbook',
  1162. :f => ['issue.category_id'],
  1163. :op => {'issue.category_id' => '='},
  1164. :v => {'issue.category_id' => ['1']}
  1165. }
  1166. assert_response :success
  1167. assert_equal ['1', '2'], css_select('input[name="ids[]"]').map {|e| e.attr(:value)}
  1168. end
  1169. def test_index_with_issue_category_column
  1170. get :index, :params => {
  1171. :project_id => 'ecookbook',
  1172. :c => %w(project spent_on issue comments hours issue.category)
  1173. }
  1174. assert_response :success
  1175. assert_select 'td.issue-category', :text => 'Printing'
  1176. end
  1177. def test_index_with_issue_parent_filter
  1178. issue1 = Issue.generate!(project_id: 'ecookbook', parent_id: 2)
  1179. entry1 = TimeEntry.generate!(issue: issue1, hours: 2.5)
  1180. issue2 = Issue.generate!(project_id: 'ecookbook', parent_id: 5)
  1181. entry2 = TimeEntry.generate!(issue: issue2, hours: 5.0)
  1182. get :index, params: {
  1183. project_id: 'ecookbook',
  1184. f: ['issue.parent_id'],
  1185. op: {'issue.parent_id' => '='},
  1186. v: {'issue.parent_id' => ['2,5']}
  1187. }
  1188. assert_response :success
  1189. assert_equal [entry1.id, entry2.id].sort, css_select('input[name="ids[]"]').map {|e| e.attr(:value).to_i}.sort
  1190. end
  1191. def test_index_with_issue_parent_column
  1192. issue = Issue.generate!(project_id: 'ecookbook', parent_id: 2)
  1193. entry = TimeEntry.generate!(issue: issue, hours: 2.5)
  1194. get :index, params: {
  1195. project_id: 'ecookbook',
  1196. c: %w(project spent_on issue comments hours issue.parent)
  1197. }
  1198. assert_response :success
  1199. assert_select 'td.issue-parent', text: "#{issue.parent.tracker} ##{issue.parent.id}"
  1200. end
  1201. def test_index_with_issue_parent_sort
  1202. issue1 = Issue.generate!(project_id: 'ecookbook', parent_id: 2)
  1203. entry1 = TimeEntry.generate!(issue: issue1, hours: 2.5)
  1204. issue2 = Issue.generate!(project_id: 'ecookbook', parent_id: 5)
  1205. entry2 = TimeEntry.generate!(issue: issue2, hours: 5.0)
  1206. get :index, :params => {
  1207. :c => ["hours", 'issue.parent'],
  1208. :sort => 'issue.parent'
  1209. }
  1210. assert_response :success
  1211. # Make sure that values are properly sorted
  1212. values = css_select("td.issue-parent").map(&:text).reject(&:blank?)
  1213. assert_equal ["#{issue1.parent.tracker} ##{issue1.parent.id}", "#{issue2.parent.tracker} ##{issue2.parent.id}"].sort, values.sort
  1214. end
  1215. def test_index_with_issue_fixed_version_column
  1216. issue = Issue.find(1)
  1217. issue.fixed_version = Version.find(3)
  1218. issue.save!
  1219. get :index, :params => {
  1220. :project_id => 'ecookbook',
  1221. :c => %w(project spent_on issue comments hours issue.fixed_version)
  1222. }
  1223. assert_response :success
  1224. assert_select 'td.issue-fixed_version', :text => '2.0'
  1225. end
  1226. def test_index_with_author_filter
  1227. get :index, :params => {
  1228. :project_id => 'ecookbook',
  1229. :f => ['author_id'],
  1230. :op => {'author_id' => '='},
  1231. :v => {'author_id' => ['2']}
  1232. }
  1233. assert_response :success
  1234. assert_equal ['1'], css_select('input[name="ids[]"]').map {|e| e.attr(:value)}
  1235. end
  1236. def test_index_with_author_column
  1237. get :index, :params => {
  1238. :project_id => 'ecookbook',
  1239. :c => %w(project spent_on issue comments hours author)
  1240. }
  1241. assert_response :success
  1242. assert_select 'td.author', :text => 'Redmine Admin'
  1243. end
  1244. def test_index_with_issue_category_sort
  1245. issue = Issue.find(3)
  1246. issue.category_id = 2
  1247. issue.save!
  1248. get :index, :params => {
  1249. :c => ["hours", 'issue.category'],
  1250. :sort => 'issue.category'
  1251. }
  1252. assert_response :success
  1253. # Make sure that values are properly sorted
  1254. values = css_select("td.issue-category").map(&:text).reject(&:blank?)
  1255. assert_equal ['Printing', 'Printing', 'Recipes'], values
  1256. end
  1257. def test_index_with_issue_fixed_version_sort
  1258. issue = Issue.find(1)
  1259. issue.fixed_version = Version.find(3)
  1260. issue.save!
  1261. TimeEntry.generate!(:issue => Issue.find(12))
  1262. get :index, :params => {
  1263. :project_id => 'ecookbook',
  1264. :c => ["hours", 'issue.fixed_version'],
  1265. :sort => 'issue.fixed_version'
  1266. }
  1267. assert_response :success
  1268. # Make sure that values are properly sorted
  1269. values = css_select("td.issue-fixed_version").map(&:text).reject(&:blank?)
  1270. assert_equal ['1.0', '2.0', '2.0'], values
  1271. end
  1272. def test_index_with_filter_on_issue_custom_field
  1273. issue =
  1274. Issue.generate!(
  1275. :project_id => 1, :tracker_id => 1,
  1276. :custom_field_values => {2 => 'filter_on_issue_custom_field'}
  1277. )
  1278. entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
  1279. get :index, :params => {
  1280. :f => ['issue.cf_2'],
  1281. :op => {'issue.cf_2' => '='},
  1282. :v => {'issue.cf_2' => ['filter_on_issue_custom_field']}
  1283. }
  1284. assert_response :success
  1285. assert_equal(
  1286. [entry.id.to_s],
  1287. css_select('input[name="ids[]"]').map {|e| e.attr(:value)}
  1288. )
  1289. end
  1290. def test_index_with_issue_custom_field_column
  1291. issue =
  1292. Issue.generate!(
  1293. :project_id => 1, :tracker_id => 1,
  1294. :custom_field_values => {2 => 'filter_on_issue_custom_field'}
  1295. )
  1296. entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
  1297. get :index, :params => {
  1298. :c => %w(project spent_on issue comments hours issue.cf_2)
  1299. }
  1300. assert_response :success
  1301. assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field'
  1302. end
  1303. def test_index_should_not_disclose_issue_data
  1304. category = IssueCategory.find 2
  1305. issue =
  1306. Issue.generate!(
  1307. :project_id => 1, :tracker_id => 1,
  1308. :custom_field_values => {2 => 'filter_on_issue_custom_field'}
  1309. )
  1310. entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
  1311. session[:user_id] = 3
  1312. issue.update_columns is_private: true, category_id: category.id
  1313. assert_not issue.visible?(User.find(3))
  1314. # since the issue is not visible, its custom fields and associated ojects should not be visible either
  1315. get :index, :params => {
  1316. :c => %w(issue issue.cf_2 issue.category)
  1317. }
  1318. assert_response :success
  1319. assert_select 'td.issue', :text => /#{issue.subject}/, :count => 0
  1320. assert_select 'td.issue-category', :text => /#{category.name}/, :count => 0
  1321. assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field', :count => 0
  1322. end
  1323. def test_index_should_not_filter_by_invisible_issue_data
  1324. issue =
  1325. Issue.generate!(
  1326. :project_id => 1, :tracker_id => 1,
  1327. :custom_field_values => {2 => 'filter_on_issue_custom_field'}
  1328. )
  1329. entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
  1330. session[:user_id] = 3
  1331. issue.update_columns is_private: true
  1332. assert_not issue.visible?(User.find(3))
  1333. # since the issue is not visible, its custom fields and associated ojects should not be filterable
  1334. get :index, :params => {
  1335. :f => ['issue.tracker_id'],
  1336. :op => {'issue.tracker_id' => '='},
  1337. :v => {'issue.tracker_id' => ["1"]},
  1338. }
  1339. assert_response :success
  1340. assert_not_include entry.id.to_s, css_select('input[name="ids[]"]').map {|e| e.attr(:value)}
  1341. get :index, :params => {
  1342. :f => ['issue.cf_2'],
  1343. :op => {'issue.cf_2' => '='},
  1344. :v => {'issue.cf_2' => ['filter_on_issue_custom_field']},
  1345. }
  1346. assert_response :success
  1347. assert_equal [], css_select('input[name="ids[]"]').map {|e| e.attr(:value)}
  1348. end
  1349. def test_indext_should_not_sort_by_invisible_issue_data
  1350. category1 = IssueCategory.find 1
  1351. category2 = IssueCategory.find 2
  1352. issue1 =
  1353. Issue.generate!(
  1354. :project_id => 1, :tracker_id => 1,
  1355. :custom_field_values => {2 => 'filter_on_issue_custom_field'}
  1356. )
  1357. issue2 =
  1358. Issue.generate!(
  1359. :project_id => 1, :tracker_id => 1,
  1360. :custom_field_values => {2 => 'xxx_this_will_be_last'}
  1361. )
  1362. entry1 = TimeEntry.generate!(:issue => issue1, :hours => 2.5, :spent_on => '2022-06-13')
  1363. entry2 = TimeEntry.generate!(:issue => issue2, :hours => 2.5, :spent_on => '2022-06-12')
  1364. session[:user_id] = 3
  1365. issue1.update_columns is_private: true, category_id: category1.id
  1366. issue2.update_columns is_private: true, category_id: category2.id
  1367. assert_not issue1.visible?(User.find(3))
  1368. assert_not issue2.visible?(User.find(3))
  1369. # since the issues are not visible, their custom fields and associated ojects should not be sortable
  1370. # issue.cf_2:desc would be entry2, entry1
  1371. # spent_on:desc is entry1, entry2
  1372. get :index, :params => {
  1373. :sort => "issue.cf_2:desc,spent_on:desc",
  1374. :f => ['spent_on'],
  1375. :op => {'spent_on' => '><'},
  1376. :v => {'spent_on' => ['2022-06-12', '2022-06-13']},
  1377. }
  1378. assert_response :success
  1379. assert_equal [entry1.id.to_s, entry2.id.to_s], css_select('input[name="ids[]"]').map {|e| e.attr(:value)}
  1380. # issue.category:desc would be entry2, entry1
  1381. # spent_on:desc is entry1, entry2
  1382. get :index, :params => {
  1383. :sort => "issue.category:desc,spent_on:desc",
  1384. :f => ['spent_on'],
  1385. :op => {'spent_on' => '><'},
  1386. :v => {'spent_on' => ['2022-06-12', '2022-06-13']},
  1387. }
  1388. assert_response :success
  1389. assert_equal [entry1.id.to_s, entry2.id.to_s], css_select('input[name="ids[]"]').map {|e| e.attr(:value)}
  1390. end
  1391. def test_index_with_time_entry_custom_field_column
  1392. field = TimeEntryCustomField.generate!(:field_format => 'string')
  1393. entry = TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value'})
  1394. field_name = "cf_#{field.id}"
  1395. get :index, :params => {
  1396. :c => ["hours", field_name]
  1397. }
  1398. assert_response :success
  1399. assert_select "td.#{field_name}", :text => 'CF Value'
  1400. end
  1401. def test_index_with_time_entry_custom_field_sorting
  1402. field = TimeEntryCustomField.generate!(:field_format => 'string', :name => 'String Field')
  1403. TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 1'})
  1404. TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 3'})
  1405. TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 2'})
  1406. field_name = "cf_#{field.id}"
  1407. get :index, :params => {
  1408. :c => ["hours", field_name],
  1409. :sort => field_name
  1410. }
  1411. assert_response :success
  1412. assert_select "th.cf_#{field.id} a.sort", :text => 'String Field'
  1413. # Make sure that values are properly sorted
  1414. values = css_select("td.#{field_name}").map(&:text).reject(&:blank?)
  1415. assert_equal values.sort, values
  1416. assert_equal 3, values.size
  1417. end
  1418. def test_index_with_invalid_date_filter_should_not_validate
  1419. @request.session[:user_id] = 2
  1420. get(
  1421. :index,
  1422. :params => {
  1423. :set_filter => '1',
  1424. :f => ['spent_on'],
  1425. :op => {'spent_on' => '='},
  1426. :v => {'spent_on' => ['2016-09-010']}
  1427. }
  1428. )
  1429. assert_select_error 'Date is invalid'
  1430. assert_select 'table.time-entries', 0
  1431. end
  1432. def test_index_with_query
  1433. query = TimeEntryQuery.new(:project_id => 1, :name => 'Time Entry Query', :visibility => 2)
  1434. query.save!
  1435. @request.session[:user_id] = 2
  1436. get :index, :params => {:project_id => 'ecookbook', :query_id => query.id}
  1437. assert_response :success
  1438. assert_select 'h2', :text => query.name
  1439. assert_select '#sidebar a.selected', :text => query.name
  1440. end
  1441. def test_index_atom_feed
  1442. get :index, :params => {:project_id => 1, :format => 'atom'}
  1443. assert_response :success
  1444. assert_equal 'application/atom+xml', @response.media_type
  1445. assert_select 'entry > title', :text => /7:39 hours/
  1446. end
  1447. def test_index_at_project_level_should_include_csv_export_dialog
  1448. get :index, :params => {
  1449. :project_id => 'ecookbook',
  1450. :f => ['spent_on'],
  1451. :op => {'spent_on' => '>='},
  1452. :v => {'spent_on' => ['2007-04-01']},
  1453. :c => ['spent_on', 'user']
  1454. }
  1455. assert_response :success
  1456. assert_select '#csv-export-options' do
  1457. assert_select 'form[action=?][method=get]', '/projects/ecookbook/time_entries.csv' do
  1458. # filter
  1459. assert_select 'input[name=?][value=?]', 'f[]', 'spent_on'
  1460. assert_select 'input[name=?][value=?]', 'op[spent_on]', '>='
  1461. assert_select 'input[name=?][value=?]', 'v[spent_on][]', '2007-04-01'
  1462. # columns
  1463. assert_select 'input[name=?][type=hidden][value=?]', 'c[]', 'spent_on'
  1464. assert_select 'input[name=?][type=hidden][value=?]', 'c[]', 'user'
  1465. assert_select 'input[name=?][type=hidden]', 'c[]', 2
  1466. assert_select 'input[name=?][value=?]', 'c[]', 'all_inline'
  1467. end
  1468. end
  1469. end
  1470. def test_index_cross_project_should_include_csv_export_dialog
  1471. get :index
  1472. assert_response :success
  1473. assert_select '#csv-export-options' do
  1474. assert_select 'form[action=?][method=get]', '/time_entries.csv'
  1475. end
  1476. end
  1477. def test_index_csv_all_projects
  1478. with_settings :date_format => '%m/%d/%Y' do
  1479. get :index, :params => {:format => 'csv'}
  1480. assert_response :success
  1481. assert_equal 'text/csv; header=present', response.media_type
  1482. end
  1483. end
  1484. def test_index_csv
  1485. with_settings :date_format => '%m/%d/%Y' do
  1486. get :index, :params => {:project_id => 1, :format => 'csv'}
  1487. assert_response :success
  1488. assert_equal 'text/csv; header=present', response.media_type
  1489. end
  1490. end
  1491. def test_index_csv_filename_query_name_param
  1492. get :index, :params => {:format => 'csv'}
  1493. assert_response :success
  1494. assert_match /timelog.csv/, @response.headers['Content-Disposition']
  1495. end
  1496. def test_index_csv_filename_with_query_name_param
  1497. get :index, :params => {:query_name => 'My Query Name', :format => 'csv'}
  1498. assert_response :success
  1499. assert_match /my_query_name\.csv/, @response.headers['Content-Disposition']
  1500. end
  1501. def test_index_csv_should_fill_issue_column_with_tracker_id_and_subject
  1502. issue = Issue.find(1)
  1503. entry = TimeEntry.generate!(:issue => issue, :comments => "Issue column content test")
  1504. get :index, :params => {:format => 'csv'}
  1505. line = response.body.split("\n").detect {|l| l.include?(entry.comments)}
  1506. assert_not_nil line
  1507. assert_include "#{issue.tracker} #1: #{issue.subject}", line
  1508. end
  1509. def test_index_csv_should_fill_issue_column_with_issue_id_if_issue_that_is_not_visible
  1510. @request.session[:user_id] = 3
  1511. issue = Issue.generate!(:author_id => 1, :is_private => true)
  1512. entry = TimeEntry.generate!(:issue => issue, :comments => "Issue column content test")
  1513. get :index, :params => {:format => 'csv'}
  1514. assert_not issue.visible?
  1515. line = response.body.split("\n").detect {|l| l.include?(entry.comments)}
  1516. assert_not_nil line
  1517. assert_not_include "#{issue.tracker} ##{issue.id}: #{issue.subject}", line
  1518. assert_include "##{issue.id}", line
  1519. end
  1520. def test_index_grouped_by_created_on
  1521. skip unless TimeEntryQuery.new.groupable_columns.detect {|c| c.name == :created_on}
  1522. get(
  1523. :index,
  1524. :params => {
  1525. :set_filter => 1,
  1526. :group_by => 'created_on'
  1527. }
  1528. )
  1529. assert_response :success
  1530. assert_select 'tr.group span.name', :text => '03/23/2007' do
  1531. assert_select '+ span.count', :text => '2'
  1532. end
  1533. end
  1534. def test_index_grouped_by_issue
  1535. get(
  1536. :index,
  1537. :params => {
  1538. :set_filter => 1,
  1539. :group_by => 'issue'
  1540. }
  1541. )
  1542. assert_response :success
  1543. assert_select 'tr.group span.name', :text => 'Bug #1: Cannot print recipes' do
  1544. assert_select '+ span.count', :text => '2'
  1545. end
  1546. end
  1547. def test_index_with_inline_issue_long_text_custom_field_column
  1548. field = IssueCustomField.create!(:name => 'Long text', :field_format => 'text', :full_width_layout => '1',
  1549. :tracker_ids => [1], :is_for_all => true)
  1550. issue = Issue.find(1)
  1551. issue.custom_field_values = {field.id => 'This is a long text'}
  1552. issue.save!
  1553. get(
  1554. :index,
  1555. :params => {
  1556. :set_filter => 1,
  1557. :c => ['subject', 'description', "issue.cf_#{field.id}"]
  1558. }
  1559. )
  1560. assert_response :success
  1561. assert_select "td.issue_cf_#{field.id}", :text => 'This is a long text'
  1562. end
  1563. end