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.

time_entry_activity_test.rb 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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 TimeEntryActivityTest < ActiveSupport::TestCase
  20. fixtures :enumerations, :time_entries,
  21. :custom_fields, :custom_values,
  22. :issues, :projects, :users,
  23. :members, :roles, :member_roles,
  24. :trackers, :issue_statuses,
  25. :projects_trackers,
  26. :issue_categories,
  27. :groups_users,
  28. :enabled_modules
  29. include Redmine::I18n
  30. def setup
  31. User.current = nil
  32. end
  33. def test_should_be_an_enumeration
  34. assert TimeEntryActivity <= Enumeration
  35. end
  36. def test_objects_count
  37. assert_equal 3, TimeEntryActivity.find_by_name("Design").objects_count
  38. assert_equal 2, TimeEntryActivity.find_by_name("Development").objects_count
  39. end
  40. def test_option_name
  41. assert_equal :enumeration_activities, TimeEntryActivity.new.option_name
  42. end
  43. def test_create_with_custom_field
  44. field = TimeEntryActivityCustomField.find_by_name('Billable')
  45. e = TimeEntryActivity.new(:name => 'Custom Data')
  46. e.custom_field_values = {field.id => "1"}
  47. assert e.save
  48. e.reload
  49. assert_equal "1", e.custom_value_for(field).value
  50. end
  51. def test_create_without_required_custom_field_should_fail
  52. set_language_if_valid 'en'
  53. field = TimeEntryActivityCustomField.find_by_name('Billable')
  54. field.update_attribute(:is_required, true)
  55. e = TimeEntryActivity.new(:name => 'Custom Data')
  56. assert !e.save
  57. assert_equal ["Billable cannot be blank"], e.errors.full_messages
  58. end
  59. def test_create_with_required_custom_field_should_succeed
  60. field = TimeEntryActivityCustomField.find_by_name('Billable')
  61. field.update_attribute(:is_required, true)
  62. e = TimeEntryActivity.new(:name => 'Custom Data')
  63. e.custom_field_values = {field.id => "1"}
  64. assert e.save
  65. end
  66. def test_update_with_required_custom_field_change
  67. set_language_if_valid 'en'
  68. field = TimeEntryActivityCustomField.find_by_name('Billable')
  69. field.update_attribute(:is_required, true)
  70. e = TimeEntryActivity.find(10)
  71. assert e.available_custom_fields.include?(field)
  72. # No change to custom field, record can be saved
  73. assert e.save
  74. # Blanking custom field, save should fail
  75. e.custom_field_values = {field.id => ""}
  76. assert !e.save
  77. assert_equal ["Billable cannot be blank"], e.errors.full_messages
  78. # Update custom field to valid value, save should succeed
  79. e.custom_field_values = {field.id => "0"}
  80. assert e.save
  81. e.reload
  82. assert_equal "0", e.custom_value_for(field).value
  83. end
  84. def test_system_activity_with_child_in_use_should_be_in_use
  85. project = Project.generate!
  86. system_activity = TimeEntryActivity.create!(:name => 'Activity')
  87. project_activity =
  88. TimeEntryActivity.create!(:name => 'Activity', :project => project,
  89. :parent_id => system_activity.id)
  90. TimeEntry.generate!(:project => project, :activity => project_activity)
  91. assert project_activity.in_use?
  92. assert system_activity.in_use?
  93. end
  94. def test_destroying_a_system_activity_should_reassign_children_activities
  95. project = Project.generate!
  96. entries = []
  97. system_activity = TimeEntryActivity.create!(:name => 'Activity')
  98. entries << TimeEntry.generate!(:project => project, :activity => system_activity)
  99. project_activity =
  100. TimeEntryActivity.create!(:name => 'Activity', :project => project,
  101. :parent_id => system_activity.id)
  102. entries << TimeEntry.generate!(:project => project.reload, :activity => project_activity)
  103. assert_difference 'TimeEntryActivity.count', -2 do
  104. assert_nothing_raised do
  105. assert system_activity.destroy(TimeEntryActivity.find_by_name('Development'))
  106. end
  107. end
  108. assert entries.all? {|entry| entry.reload.activity.name == 'Development'}
  109. end
  110. def test_project_activity_without_parent_should_not_disable_system_activities
  111. project = Project.find(1)
  112. activity = TimeEntryActivity.create!(:name => 'Csutom', :project => project)
  113. assert_include activity, project.activities
  114. assert_include TimeEntryActivity.find(9), project.activities
  115. end
  116. def test_project_activity_should_have_the_same_position_as_parent_activity
  117. project = Project.find(1)
  118. parent_activity = TimeEntryActivity.find_by(position: 3, parent_id: nil)
  119. project.update_or_create_time_entry_activities(
  120. {
  121. parent_activity.id.to_s => {
  122. 'parent_id' => parent_activity.id.to_s,
  123. 'active' => '0',
  124. 'custom_field_values' => {'7' => '1'}
  125. }
  126. }
  127. )
  128. project_activity = TimeEntryActivity.find_by(position: 3, parent_id: parent_activity.id, project_id: 1)
  129. assert_equal parent_activity.position, project_activity.position
  130. # Changing the position of the parent activity also changes the position of the activity in each project.
  131. other_parent_activity = TimeEntryActivity.find_by(position: 4, parent_id: nil)
  132. project.update_or_create_time_entry_activities(
  133. {
  134. other_parent_activity.id.to_s => {
  135. 'parent_id' => other_parent_activity.id.to_s,
  136. 'active' => '0',
  137. 'custom_field_values' => {'7' => '1'}
  138. }
  139. }
  140. )
  141. other_project_activity = TimeEntryActivity.find_by(position: 4, parent_id: other_parent_activity.id, project_id: 1)
  142. parent_activity.update(position: 4)
  143. assert_equal 4, parent_activity.reload.position
  144. assert_equal parent_activity.position, project_activity.reload.position
  145. assert_equal 3, other_parent_activity.reload.position
  146. assert_equal other_parent_activity.position, other_project_activity.reload.position
  147. end
  148. def test_project_activity_should_have_the_same_name_as_parent_activity
  149. parent_activity = TimeEntryActivity.find_by(name: 'Design', parent_id: nil)
  150. project = Project.find(1)
  151. project.update_or_create_time_entry_activities(
  152. {
  153. parent_activity.id.to_s => {
  154. 'parent_id' => parent_activity.id.to_s,
  155. 'active' => '0',
  156. 'custom_field_values' => {'7' => '1'}
  157. }
  158. }
  159. )
  160. project_activity = TimeEntryActivity.find_by(name: 'Design', parent_id: parent_activity.id, project_id: project.id)
  161. assert_equal parent_activity.name, project_activity.name
  162. parent_activity.update(name: 'Design1')
  163. assert_equal parent_activity.reload.name, project_activity.reload.name
  164. # When changing the name of parent_activity,
  165. # if the name of parent_activity before the change and the name of project_activity do not match, the name of project_activity is not changed.
  166. project_activity.update(name: 'Design2')
  167. parent_activity.update(name: 'Design3')
  168. assert_equal 'Design2', project_activity.reload.name
  169. assert_equal 'Design3', parent_activity.reload.name
  170. end
  171. def test_project_activity_should_not_be_created_if_no_custom_value_is_changed
  172. system_activity = TimeEntryActivity.find(9) # Design
  173. assert_equal true, system_activity.active
  174. custom_field_value = system_activity.custom_field_values.detect{|cfv| cfv.custom_field.id == 7}
  175. assert_nil custom_field_value.value
  176. project = Project.find(1)
  177. assert_equal 0, project.time_entry_activities.count
  178. assert_no_difference 'TimeEntryActivity.count' do
  179. project.update_or_create_time_entry_activities(
  180. {
  181. '9' => {
  182. 'parent_id' => '9',
  183. 'active' => '1',
  184. 'custom_field_values' => {'7' => ''}
  185. }
  186. }
  187. )
  188. end
  189. end
  190. def test_default_should_return_default_activity_if_default_activity_is_included_in_the_project_activities
  191. project = Project.find(1)
  192. assert_equal TimeEntryActivity.default(project).id, 10
  193. end
  194. def test_default_should_return_project_specific_default_activity_if_default_activity_is_not_included_in_the_project_activities
  195. project = Project.find(1)
  196. project_specific_default_activity = TimeEntryActivity.create!(name: 'Development', parent_id: 10, project_id: project.id, is_default: false)
  197. assert_not_equal TimeEntryActivity.default(project).id, 10
  198. assert_equal TimeEntryActivity.default(project).id, project_specific_default_activity.id
  199. end
  200. def test_default_activity_id_without_user_and_project_should_return_global_default_activity
  201. assert_equal 10, TimeEntryActivity.default_activity_id
  202. end
  203. def test_default_activity_id_with_user_and_project_should_return_role_default_activity
  204. # set a default activity for Manager role
  205. manager = Role.find(1)
  206. manager.default_time_entry_activity_id = 9
  207. manager.save
  208. assert_equal 9, TimeEntryActivity.default_activity_id(User.find(2), Project.find(1))
  209. end
  210. def test_default_activity_id_with_user_and_project_should_consider_role_position
  211. project = Project.find(1)
  212. user = User.find(2)
  213. # set a default activity for Manager role
  214. manager = Role.find(1)
  215. manager.default_time_entry_activity_id = 9
  216. manager.save!
  217. # set a default activity for Developer role
  218. # and set the role position first
  219. developer = Role.find(2)
  220. developer.default_time_entry_activity_id = 11
  221. developer.position = 1
  222. developer.save!
  223. member = Member.find_or_initialize_by(:project_id => project.id, :user_id => user.id)
  224. member.role_ids = [1, 2]
  225. member.save!
  226. assert_equal 11, TimeEntryActivity.default_activity_id(user, project)
  227. end
  228. def test_default_activity_id_should_include_only_available_activities
  229. # set a default activity for Manager role
  230. manager = Role.find(1)
  231. manager.default_time_entry_activity_id = 9
  232. manager.save!
  233. project = Project.find(1)
  234. # disable role default activity
  235. disable_activity = TimeEntryActivity.new({:name => "QA", :project => project, :parent => TimeEntryActivity.find(9), :active => false})
  236. disable_activity.save!
  237. assert_equal 10, TimeEntryActivity.default_activity_id(User.find(2), project)
  238. end
  239. def test_default_activity_id_should_selected_from_highest_priority_of_multiple_default_activity_candidates
  240. project = Project.find(1)
  241. manager = Role.find(1)
  242. manager.default_time_entry_activity_id = 9
  243. manager.save
  244. # Returns the role_default_activity with the highest priority
  245. assert_equal 9, TimeEntryActivity.default_activity_id(User.find(2), project)
  246. # Returns the child activity of role_default_activity if there is an activity that has the id of role_default_activity as parent_id
  247. design_project_activity = TimeEntryActivity.create!(name: 'Design', parent_id: 9, project_id: project.id, is_default: false)
  248. development_project_activity = TimeEntryActivity.create!(name: 'Development', parent_id: 10, project_id: project.id, is_default: true)
  249. qa_project_activity = TimeEntryActivity.create!(name: 'QA', parent_id: 11, project_id: project.id, is_default: false)
  250. assert_equal design_project_activity.id, TimeEntryActivity.default_activity_id(User.find(2), project)
  251. # Returns default project activity if role_default_activity is not present
  252. manager.default_time_entry_activity_id = nil
  253. manager.save
  254. assert_equal development_project_activity.id, TimeEntryActivity.default_activity_id(User.find(2), project)
  255. # Returns global default activity if role_default_activity and project activities are not present
  256. [design_project_activity, development_project_activity, qa_project_activity].each {|activity| activity.destroy}
  257. TimeEntryActivity.find(11).update(is_default: true)
  258. assert_equal 11, TimeEntryActivity.default_activity_id(User.find(2), project)
  259. # If there is only one activity available, it returns that activity.
  260. [TimeEntryActivity.find(10), TimeEntryActivity.find(11)].each {|a| a.update(active: false)}
  261. assert_equal 9, TimeEntryActivity.default_activity_id(User.find(2), project)
  262. end
  263. end