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 52KB

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