##// END OF EJS Templates
Merged r14162 (#19464)....
Jean-Philippe Lang -
r13824:94910b1e0924
parent child
Show More
@@ -1,126 +1,131
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class TimeEntry < ActiveRecord::Base
18 class TimeEntry < ActiveRecord::Base
19 include Redmine::SafeAttributes
19 include Redmine::SafeAttributes
20 # could have used polymorphic association
20 # could have used polymorphic association
21 # project association here allows easy loading of time entries at project level with one database trip
21 # project association here allows easy loading of time entries at project level with one database trip
22 belongs_to :project
22 belongs_to :project
23 belongs_to :issue
23 belongs_to :issue
24 belongs_to :user
24 belongs_to :user
25 belongs_to :activity, :class_name => 'TimeEntryActivity'
25 belongs_to :activity, :class_name => 'TimeEntryActivity'
26
26
27 attr_protected :user_id, :tyear, :tmonth, :tweek
27 attr_protected :user_id, :tyear, :tmonth, :tweek
28
28
29 acts_as_customizable
29 acts_as_customizable
30 acts_as_event :title => Proc.new {|o| "#{l_hours(o.hours)} (#{(o.issue || o.project).event_title})"},
30 acts_as_event :title => Proc.new {|o| "#{l_hours(o.hours)} (#{(o.issue || o.project).event_title})"},
31 :url => Proc.new {|o| {:controller => 'timelog', :action => 'index', :project_id => o.project, :issue_id => o.issue}},
31 :url => Proc.new {|o| {:controller => 'timelog', :action => 'index', :project_id => o.project, :issue_id => o.issue}},
32 :author => :user,
32 :author => :user,
33 :group => :issue,
33 :group => :issue,
34 :description => :comments
34 :description => :comments
35
35
36 acts_as_activity_provider :timestamp => "#{table_name}.created_on",
36 acts_as_activity_provider :timestamp => "#{table_name}.created_on",
37 :author_key => :user_id,
37 :author_key => :user_id,
38 :scope => joins(:project).preload(:project)
38 :scope => joins(:project).preload(:project)
39
39
40 validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on
40 validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on
41 validates_numericality_of :hours, :allow_nil => true, :message => :invalid
41 validates_numericality_of :hours, :allow_nil => true, :message => :invalid
42 validates_length_of :comments, :maximum => 255, :allow_nil => true
42 validates_length_of :comments, :maximum => 255, :allow_nil => true
43 validates :spent_on, :date => true
43 validates :spent_on, :date => true
44 before_validation :set_project_if_nil
44 before_validation :set_project_if_nil
45 validate :validate_time_entry
45 validate :validate_time_entry
46
46
47 scope :visible, lambda {|*args|
47 scope :visible, lambda {|*args|
48 joins(:project).
48 joins(:project).
49 where(Project.allowed_to_condition(args.shift || User.current, :view_time_entries, *args))
49 where(Project.allowed_to_condition(args.shift || User.current, :view_time_entries, *args))
50 }
50 }
51 scope :on_issue, lambda {|issue|
51 scope :on_issue, lambda {|issue|
52 joins(:issue).
52 joins(:issue).
53 where("#{Issue.table_name}.root_id = #{issue.root_id} AND #{Issue.table_name}.lft >= #{issue.lft} AND #{Issue.table_name}.rgt <= #{issue.rgt}")
53 where("#{Issue.table_name}.root_id = #{issue.root_id} AND #{Issue.table_name}.lft >= #{issue.lft} AND #{Issue.table_name}.rgt <= #{issue.rgt}")
54 }
54 }
55
55
56 safe_attributes 'hours', 'comments', 'project_id', 'issue_id', 'activity_id', 'spent_on', 'custom_field_values', 'custom_fields'
56 safe_attributes 'hours', 'comments', 'project_id', 'issue_id', 'activity_id', 'spent_on', 'custom_field_values', 'custom_fields'
57
57
58 def initialize(attributes=nil, *args)
58 def initialize(attributes=nil, *args)
59 super
59 super
60 if new_record? && self.activity.nil?
60 if new_record? && self.activity.nil?
61 if default_activity = TimeEntryActivity.default
61 if default_activity = TimeEntryActivity.default
62 self.activity_id = default_activity.id
62 self.activity_id = default_activity.id
63 end
63 end
64 self.hours = nil if hours == 0
64 self.hours = nil if hours == 0
65 end
65 end
66 end
66 end
67
67
68 def safe_attributes=(attrs, user=User.current)
68 def safe_attributes=(attrs, user=User.current)
69 if attrs
69 if attrs
70 attrs = super(attrs)
70 attrs = super(attrs)
71 if issue_id_changed? && attrs[:project_id].blank? && issue && issue.project_id != project_id
71 if issue_id_changed? && issue
72 if user.allowed_to?(:log_time, issue.project)
72 if user.allowed_to?(:log_time, issue.project)
73 if attrs[:project_id].blank? && issue.project_id != project_id
73 self.project_id = issue.project_id
74 self.project_id = issue.project_id
74 end
75 end
76 @invalid_issue_id = nil
77 else
78 @invalid_issue_id = issue_id
79 end
75 end
80 end
76 end
81 end
77 attrs
82 attrs
78 end
83 end
79
84
80 def set_project_if_nil
85 def set_project_if_nil
81 self.project = issue.project if issue && project.nil?
86 self.project = issue.project if issue && project.nil?
82 end
87 end
83
88
84 def validate_time_entry
89 def validate_time_entry
85 errors.add :hours, :invalid if hours && (hours < 0 || hours >= 1000)
90 errors.add :hours, :invalid if hours && (hours < 0 || hours >= 1000)
86 errors.add :project_id, :invalid if project.nil?
91 errors.add :project_id, :invalid if project.nil?
87 errors.add :issue_id, :invalid if (issue_id && !issue) || (issue && project!=issue.project)
92 errors.add :issue_id, :invalid if (issue_id && !issue) || (issue && project!=issue.project) || @invalid_issue_id
88 end
93 end
89
94
90 def hours=(h)
95 def hours=(h)
91 write_attribute :hours, (h.is_a?(String) ? (h.to_hours || h) : h)
96 write_attribute :hours, (h.is_a?(String) ? (h.to_hours || h) : h)
92 end
97 end
93
98
94 def hours
99 def hours
95 h = read_attribute(:hours)
100 h = read_attribute(:hours)
96 if h.is_a?(Float)
101 if h.is_a?(Float)
97 h.round(2)
102 h.round(2)
98 else
103 else
99 h
104 h
100 end
105 end
101 end
106 end
102
107
103 # tyear, tmonth, tweek assigned where setting spent_on attributes
108 # tyear, tmonth, tweek assigned where setting spent_on attributes
104 # these attributes make time aggregations easier
109 # these attributes make time aggregations easier
105 def spent_on=(date)
110 def spent_on=(date)
106 super
111 super
107 self.tyear = spent_on ? spent_on.year : nil
112 self.tyear = spent_on ? spent_on.year : nil
108 self.tmonth = spent_on ? spent_on.month : nil
113 self.tmonth = spent_on ? spent_on.month : nil
109 self.tweek = spent_on ? Date.civil(spent_on.year, spent_on.month, spent_on.day).cweek : nil
114 self.tweek = spent_on ? Date.civil(spent_on.year, spent_on.month, spent_on.day).cweek : nil
110 end
115 end
111
116
112 # Returns true if the time entry can be edited by usr, otherwise false
117 # Returns true if the time entry can be edited by usr, otherwise false
113 def editable_by?(usr)
118 def editable_by?(usr)
114 (usr == user && usr.allowed_to?(:edit_own_time_entries, project)) || usr.allowed_to?(:edit_time_entries, project)
119 (usr == user && usr.allowed_to?(:edit_own_time_entries, project)) || usr.allowed_to?(:edit_time_entries, project)
115 end
120 end
116
121
117 # Returns the custom_field_values that can be edited by the given user
122 # Returns the custom_field_values that can be edited by the given user
118 def editable_custom_field_values(user=nil)
123 def editable_custom_field_values(user=nil)
119 visible_custom_field_values
124 visible_custom_field_values
120 end
125 end
121
126
122 # Returns the custom fields that can be edited by the given user
127 # Returns the custom fields that can be edited by the given user
123 def editable_custom_fields(user=nil)
128 def editable_custom_fields(user=nil)
124 editable_custom_field_values(user).map(&:custom_field).uniq
129 editable_custom_field_values(user).map(&:custom_field).uniq
125 end
130 end
126 end
131 end
@@ -1,723 +1,773
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # Redmine - project management software
2 # Redmine - project management software
3 # Copyright (C) 2006-2015 Jean-Philippe Lang
3 # Copyright (C) 2006-2015 Jean-Philippe Lang
4 #
4 #
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
8 # of the License, or (at your option) any later version.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
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
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.
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
18
19 require File.expand_path('../../test_helper', __FILE__)
19 require File.expand_path('../../test_helper', __FILE__)
20
20
21 class TimelogControllerTest < ActionController::TestCase
21 class TimelogControllerTest < ActionController::TestCase
22 fixtures :projects, :enabled_modules, :roles, :members,
22 fixtures :projects, :enabled_modules, :roles, :members,
23 :member_roles, :issues, :time_entries, :users,
23 :member_roles, :issues, :time_entries, :users,
24 :trackers, :enumerations, :issue_statuses,
24 :trackers, :enumerations, :issue_statuses,
25 :custom_fields, :custom_values,
25 :custom_fields, :custom_values,
26 :projects_trackers, :custom_fields_trackers,
26 :projects_trackers, :custom_fields_trackers,
27 :custom_fields_projects
27 :custom_fields_projects
28
28
29 include Redmine::I18n
29 include Redmine::I18n
30
30
31 def test_new
31 def test_new
32 @request.session[:user_id] = 3
32 @request.session[:user_id] = 3
33 get :new
33 get :new
34 assert_response :success
34 assert_response :success
35 assert_template 'new'
35 assert_template 'new'
36 assert_select 'input[name=?][type=hidden]', 'project_id', 0
36 assert_select 'input[name=?][type=hidden]', 'project_id', 0
37 assert_select 'input[name=?][type=hidden]', 'issue_id', 0
37 assert_select 'input[name=?][type=hidden]', 'issue_id', 0
38 assert_select 'select[name=?]', 'time_entry[project_id]' do
38 assert_select 'select[name=?]', 'time_entry[project_id]' do
39 # blank option for project
39 # blank option for project
40 assert_select 'option[value=""]'
40 assert_select 'option[value=""]'
41 end
41 end
42 end
42 end
43
43
44 def test_new_with_project_id
44 def test_new_with_project_id
45 @request.session[:user_id] = 3
45 @request.session[:user_id] = 3
46 get :new, :project_id => 1
46 get :new, :project_id => 1
47 assert_response :success
47 assert_response :success
48 assert_template 'new'
48 assert_template 'new'
49 assert_select 'input[name=?][type=hidden]', 'project_id'
49 assert_select 'input[name=?][type=hidden]', 'project_id'
50 assert_select 'input[name=?][type=hidden]', 'issue_id', 0
50 assert_select 'input[name=?][type=hidden]', 'issue_id', 0
51 assert_select 'select[name=?]', 'time_entry[project_id]', 0
51 assert_select 'select[name=?]', 'time_entry[project_id]', 0
52 end
52 end
53
53
54 def test_new_with_issue_id
54 def test_new_with_issue_id
55 @request.session[:user_id] = 3
55 @request.session[:user_id] = 3
56 get :new, :issue_id => 2
56 get :new, :issue_id => 2
57 assert_response :success
57 assert_response :success
58 assert_template 'new'
58 assert_template 'new'
59 assert_select 'input[name=?][type=hidden]', 'project_id', 0
59 assert_select 'input[name=?][type=hidden]', 'project_id', 0
60 assert_select 'input[name=?][type=hidden]', 'issue_id'
60 assert_select 'input[name=?][type=hidden]', 'issue_id'
61 assert_select 'select[name=?]', 'time_entry[project_id]', 0
61 assert_select 'select[name=?]', 'time_entry[project_id]', 0
62 end
62 end
63
63
64 def test_new_without_project_should_prefill_the_form
64 def test_new_without_project_should_prefill_the_form
65 @request.session[:user_id] = 3
65 @request.session[:user_id] = 3
66 get :new, :time_entry => {:project_id => '1'}
66 get :new, :time_entry => {:project_id => '1'}
67 assert_response :success
67 assert_response :success
68 assert_template 'new'
68 assert_template 'new'
69 assert_select 'select[name=?]', 'time_entry[project_id]' do
69 assert_select 'select[name=?]', 'time_entry[project_id]' do
70 assert_select 'option[value="1"][selected=selected]'
70 assert_select 'option[value="1"][selected=selected]'
71 end
71 end
72 end
72 end
73
73
74 def test_new_without_project_should_deny_without_permission
74 def test_new_without_project_should_deny_without_permission
75 Role.all.each {|role| role.remove_permission! :log_time}
75 Role.all.each {|role| role.remove_permission! :log_time}
76 @request.session[:user_id] = 3
76 @request.session[:user_id] = 3
77
77
78 get :new
78 get :new
79 assert_response 403
79 assert_response 403
80 end
80 end
81
81
82 def test_new_should_select_default_activity
82 def test_new_should_select_default_activity
83 @request.session[:user_id] = 3
83 @request.session[:user_id] = 3
84 get :new, :project_id => 1
84 get :new, :project_id => 1
85 assert_response :success
85 assert_response :success
86 assert_select 'select[name=?]', 'time_entry[activity_id]' do
86 assert_select 'select[name=?]', 'time_entry[activity_id]' do
87 assert_select 'option[selected=selected]', :text => 'Development'
87 assert_select 'option[selected=selected]', :text => 'Development'
88 end
88 end
89 end
89 end
90
90
91 def test_new_should_only_show_active_time_entry_activities
91 def test_new_should_only_show_active_time_entry_activities
92 @request.session[:user_id] = 3
92 @request.session[:user_id] = 3
93 get :new, :project_id => 1
93 get :new, :project_id => 1
94 assert_response :success
94 assert_response :success
95 assert_select 'option', :text => 'Inactive Activity', :count => 0
95 assert_select 'option', :text => 'Inactive Activity', :count => 0
96 end
96 end
97
97
98 def test_get_edit_existing_time
98 def test_get_edit_existing_time
99 @request.session[:user_id] = 2
99 @request.session[:user_id] = 2
100 get :edit, :id => 2, :project_id => nil
100 get :edit, :id => 2, :project_id => nil
101 assert_response :success
101 assert_response :success
102 assert_template 'edit'
102 assert_template 'edit'
103 assert_select 'form[action=?]', '/time_entries/2'
103 assert_select 'form[action=?]', '/time_entries/2'
104 end
104 end
105
105
106 def test_get_edit_with_an_existing_time_entry_with_inactive_activity
106 def test_get_edit_with_an_existing_time_entry_with_inactive_activity
107 te = TimeEntry.find(1)
107 te = TimeEntry.find(1)
108 te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
108 te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
109 te.save!
109 te.save!
110
110
111 @request.session[:user_id] = 1
111 @request.session[:user_id] = 1
112 get :edit, :project_id => 1, :id => 1
112 get :edit, :project_id => 1, :id => 1
113 assert_response :success
113 assert_response :success
114 assert_template 'edit'
114 assert_template 'edit'
115 # Blank option since nothing is pre-selected
115 # Blank option since nothing is pre-selected
116 assert_select 'option', :text => '--- Please select ---'
116 assert_select 'option', :text => '--- Please select ---'
117 end
117 end
118
118
119 def test_post_create
119 def test_post_create
120 @request.session[:user_id] = 3
120 @request.session[:user_id] = 3
121 assert_difference 'TimeEntry.count' do
121 assert_difference 'TimeEntry.count' do
122 post :create, :project_id => 1,
122 post :create, :project_id => 1,
123 :time_entry => {:comments => 'Some work on TimelogControllerTest',
123 :time_entry => {:comments => 'Some work on TimelogControllerTest',
124 # Not the default activity
124 # Not the default activity
125 :activity_id => '11',
125 :activity_id => '11',
126 :spent_on => '2008-03-14',
126 :spent_on => '2008-03-14',
127 :issue_id => '1',
127 :issue_id => '1',
128 :hours => '7.3'}
128 :hours => '7.3'}
129 assert_redirected_to '/projects/ecookbook/time_entries'
129 assert_redirected_to '/projects/ecookbook/time_entries'
130 end
130 end
131
131
132 t = TimeEntry.order('id DESC').first
132 t = TimeEntry.order('id DESC').first
133 assert_not_nil t
133 assert_not_nil t
134 assert_equal 'Some work on TimelogControllerTest', t.comments
134 assert_equal 'Some work on TimelogControllerTest', t.comments
135 assert_equal 1, t.project_id
135 assert_equal 1, t.project_id
136 assert_equal 1, t.issue_id
136 assert_equal 1, t.issue_id
137 assert_equal 11, t.activity_id
137 assert_equal 11, t.activity_id
138 assert_equal 7.3, t.hours
138 assert_equal 7.3, t.hours
139 assert_equal 3, t.user_id
139 assert_equal 3, t.user_id
140 end
140 end
141
141
142 def test_post_create_with_blank_issue
142 def test_post_create_with_blank_issue
143 @request.session[:user_id] = 3
143 @request.session[:user_id] = 3
144 assert_difference 'TimeEntry.count' do
144 assert_difference 'TimeEntry.count' do
145 post :create, :project_id => 1,
145 post :create, :project_id => 1,
146 :time_entry => {:comments => 'Some work on TimelogControllerTest',
146 :time_entry => {:comments => 'Some work on TimelogControllerTest',
147 # Not the default activity
147 # Not the default activity
148 :activity_id => '11',
148 :activity_id => '11',
149 :issue_id => '',
149 :issue_id => '',
150 :spent_on => '2008-03-14',
150 :spent_on => '2008-03-14',
151 :hours => '7.3'}
151 :hours => '7.3'}
152 assert_redirected_to '/projects/ecookbook/time_entries'
152 assert_redirected_to '/projects/ecookbook/time_entries'
153 end
153 end
154
154
155 t = TimeEntry.order('id DESC').first
155 t = TimeEntry.order('id DESC').first
156 assert_not_nil t
156 assert_not_nil t
157 assert_equal 'Some work on TimelogControllerTest', t.comments
157 assert_equal 'Some work on TimelogControllerTest', t.comments
158 assert_equal 1, t.project_id
158 assert_equal 1, t.project_id
159 assert_nil t.issue_id
159 assert_nil t.issue_id
160 assert_equal 11, t.activity_id
160 assert_equal 11, t.activity_id
161 assert_equal 7.3, t.hours
161 assert_equal 7.3, t.hours
162 assert_equal 3, t.user_id
162 assert_equal 3, t.user_id
163 end
163 end
164
164
165 def test_create_on_project_with_time_tracking_disabled_should_fail
166 Project.find(1).disable_module! :time_tracking
167
168 @request.session[:user_id] = 2
169 assert_no_difference 'TimeEntry.count' do
170 post :create, :time_entry => {
171 :project_id => '1', :issue_id => '',
172 :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
173 }
174 end
175 end
176
177 def test_create_on_project_without_permission_should_fail
178 Role.find(1).remove_permission! :log_time
179
180 @request.session[:user_id] = 2
181 assert_no_difference 'TimeEntry.count' do
182 post :create, :time_entry => {
183 :project_id => '1', :issue_id => '',
184 :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
185 }
186 end
187 end
188
189 def test_create_on_issue_in_project_with_time_tracking_disabled_should_fail
190 Project.find(1).disable_module! :time_tracking
191
192 @request.session[:user_id] = 2
193 assert_no_difference 'TimeEntry.count' do
194 post :create, :time_entry => {
195 :project_id => '', :issue_id => '1',
196 :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
197 }
198 assert_select_error /Issue is invalid/
199 end
200 end
201
202 def test_create_on_issue_in_project_without_permission_should_fail
203 Role.find(1).remove_permission! :log_time
204
205 @request.session[:user_id] = 2
206 assert_no_difference 'TimeEntry.count' do
207 post :create, :time_entry => {
208 :project_id => '', :issue_id => '1',
209 :activity_id => '11', :spent_on => '2008-03-14', :hours => '7.3'
210 }
211 assert_select_error /Issue is invalid/
212 end
213 end
214
165 def test_create_and_continue_at_project_level
215 def test_create_and_continue_at_project_level
166 @request.session[:user_id] = 2
216 @request.session[:user_id] = 2
167 assert_difference 'TimeEntry.count' do
217 assert_difference 'TimeEntry.count' do
168 post :create, :time_entry => {:project_id => '1',
218 post :create, :time_entry => {:project_id => '1',
169 :activity_id => '11',
219 :activity_id => '11',
170 :issue_id => '',
220 :issue_id => '',
171 :spent_on => '2008-03-14',
221 :spent_on => '2008-03-14',
172 :hours => '7.3'},
222 :hours => '7.3'},
173 :continue => '1'
223 :continue => '1'
174 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D=1'
224 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D=1'
175 end
225 end
176 end
226 end
177
227
178 def test_create_and_continue_at_issue_level
228 def test_create_and_continue_at_issue_level
179 @request.session[:user_id] = 2
229 @request.session[:user_id] = 2
180 assert_difference 'TimeEntry.count' do
230 assert_difference 'TimeEntry.count' do
181 post :create, :time_entry => {:project_id => '',
231 post :create, :time_entry => {:project_id => '',
182 :activity_id => '11',
232 :activity_id => '11',
183 :issue_id => '1',
233 :issue_id => '1',
184 :spent_on => '2008-03-14',
234 :spent_on => '2008-03-14',
185 :hours => '7.3'},
235 :hours => '7.3'},
186 :continue => '1'
236 :continue => '1'
187 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=1&time_entry%5Bproject_id%5D='
237 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=1&time_entry%5Bproject_id%5D='
188 end
238 end
189 end
239 end
190
240
191 def test_create_and_continue_with_project_id
241 def test_create_and_continue_with_project_id
192 @request.session[:user_id] = 2
242 @request.session[:user_id] = 2
193 assert_difference 'TimeEntry.count' do
243 assert_difference 'TimeEntry.count' do
194 post :create, :project_id => 1,
244 post :create, :project_id => 1,
195 :time_entry => {:activity_id => '11',
245 :time_entry => {:activity_id => '11',
196 :issue_id => '',
246 :issue_id => '',
197 :spent_on => '2008-03-14',
247 :spent_on => '2008-03-14',
198 :hours => '7.3'},
248 :hours => '7.3'},
199 :continue => '1'
249 :continue => '1'
200 assert_redirected_to '/projects/ecookbook/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D='
250 assert_redirected_to '/projects/ecookbook/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D='
201 end
251 end
202 end
252 end
203
253
204 def test_create_and_continue_with_issue_id
254 def test_create_and_continue_with_issue_id
205 @request.session[:user_id] = 2
255 @request.session[:user_id] = 2
206 assert_difference 'TimeEntry.count' do
256 assert_difference 'TimeEntry.count' do
207 post :create, :issue_id => 1,
257 post :create, :issue_id => 1,
208 :time_entry => {:activity_id => '11',
258 :time_entry => {:activity_id => '11',
209 :issue_id => '1',
259 :issue_id => '1',
210 :spent_on => '2008-03-14',
260 :spent_on => '2008-03-14',
211 :hours => '7.3'},
261 :hours => '7.3'},
212 :continue => '1'
262 :continue => '1'
213 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='
263 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='
214 end
264 end
215 end
265 end
216
266
217 def test_create_without_log_time_permission_should_be_denied
267 def test_create_without_log_time_permission_should_be_denied
218 @request.session[:user_id] = 2
268 @request.session[:user_id] = 2
219 Role.find_by_name('Manager').remove_permission! :log_time
269 Role.find_by_name('Manager').remove_permission! :log_time
220 post :create, :project_id => 1,
270 post :create, :project_id => 1,
221 :time_entry => {:activity_id => '11',
271 :time_entry => {:activity_id => '11',
222 :issue_id => '',
272 :issue_id => '',
223 :spent_on => '2008-03-14',
273 :spent_on => '2008-03-14',
224 :hours => '7.3'}
274 :hours => '7.3'}
225
275
226 assert_response 403
276 assert_response 403
227 end
277 end
228
278
229 def test_create_without_project_and_issue_should_fail
279 def test_create_without_project_and_issue_should_fail
230 @request.session[:user_id] = 2
280 @request.session[:user_id] = 2
231 post :create, :time_entry => {:issue_id => ''}
281 post :create, :time_entry => {:issue_id => ''}
232
282
233 assert_response :success
283 assert_response :success
234 assert_template 'new'
284 assert_template 'new'
235 end
285 end
236
286
237 def test_create_with_failure
287 def test_create_with_failure
238 @request.session[:user_id] = 2
288 @request.session[:user_id] = 2
239 post :create, :project_id => 1,
289 post :create, :project_id => 1,
240 :time_entry => {:activity_id => '',
290 :time_entry => {:activity_id => '',
241 :issue_id => '',
291 :issue_id => '',
242 :spent_on => '2008-03-14',
292 :spent_on => '2008-03-14',
243 :hours => '7.3'}
293 :hours => '7.3'}
244
294
245 assert_response :success
295 assert_response :success
246 assert_template 'new'
296 assert_template 'new'
247 end
297 end
248
298
249 def test_create_without_project
299 def test_create_without_project
250 @request.session[:user_id] = 2
300 @request.session[:user_id] = 2
251 assert_difference 'TimeEntry.count' do
301 assert_difference 'TimeEntry.count' do
252 post :create, :time_entry => {:project_id => '1',
302 post :create, :time_entry => {:project_id => '1',
253 :activity_id => '11',
303 :activity_id => '11',
254 :issue_id => '',
304 :issue_id => '',
255 :spent_on => '2008-03-14',
305 :spent_on => '2008-03-14',
256 :hours => '7.3'}
306 :hours => '7.3'}
257 end
307 end
258
308
259 assert_redirected_to '/projects/ecookbook/time_entries'
309 assert_redirected_to '/projects/ecookbook/time_entries'
260 time_entry = TimeEntry.order('id DESC').first
310 time_entry = TimeEntry.order('id DESC').first
261 assert_equal 1, time_entry.project_id
311 assert_equal 1, time_entry.project_id
262 end
312 end
263
313
264 def test_create_without_project_should_fail_with_issue_not_inside_project
314 def test_create_without_project_should_fail_with_issue_not_inside_project
265 @request.session[:user_id] = 2
315 @request.session[:user_id] = 2
266 assert_no_difference 'TimeEntry.count' do
316 assert_no_difference 'TimeEntry.count' do
267 post :create, :time_entry => {:project_id => '1',
317 post :create, :time_entry => {:project_id => '1',
268 :activity_id => '11',
318 :activity_id => '11',
269 :issue_id => '5',
319 :issue_id => '5',
270 :spent_on => '2008-03-14',
320 :spent_on => '2008-03-14',
271 :hours => '7.3'}
321 :hours => '7.3'}
272 end
322 end
273
323
274 assert_response :success
324 assert_response :success
275 assert assigns(:time_entry).errors[:issue_id].present?
325 assert assigns(:time_entry).errors[:issue_id].present?
276 end
326 end
277
327
278 def test_create_without_project_should_deny_without_permission
328 def test_create_without_project_should_deny_without_permission
279 @request.session[:user_id] = 2
329 @request.session[:user_id] = 2
280 Project.find(3).disable_module!(:time_tracking)
330 Project.find(3).disable_module!(:time_tracking)
281
331
282 assert_no_difference 'TimeEntry.count' do
332 assert_no_difference 'TimeEntry.count' do
283 post :create, :time_entry => {:project_id => '3',
333 post :create, :time_entry => {:project_id => '3',
284 :activity_id => '11',
334 :activity_id => '11',
285 :issue_id => '',
335 :issue_id => '',
286 :spent_on => '2008-03-14',
336 :spent_on => '2008-03-14',
287 :hours => '7.3'}
337 :hours => '7.3'}
288 end
338 end
289
339
290 assert_response 403
340 assert_response 403
291 end
341 end
292
342
293 def test_create_without_project_with_failure
343 def test_create_without_project_with_failure
294 @request.session[:user_id] = 2
344 @request.session[:user_id] = 2
295 assert_no_difference 'TimeEntry.count' do
345 assert_no_difference 'TimeEntry.count' do
296 post :create, :time_entry => {:project_id => '1',
346 post :create, :time_entry => {:project_id => '1',
297 :activity_id => '11',
347 :activity_id => '11',
298 :issue_id => '',
348 :issue_id => '',
299 :spent_on => '2008-03-14',
349 :spent_on => '2008-03-14',
300 :hours => ''}
350 :hours => ''}
301 end
351 end
302
352
303 assert_response :success
353 assert_response :success
304 assert_select 'select[name=?]', 'time_entry[project_id]' do
354 assert_select 'select[name=?]', 'time_entry[project_id]' do
305 assert_select 'option[value="1"][selected=selected]'
355 assert_select 'option[value="1"][selected=selected]'
306 end
356 end
307 end
357 end
308
358
309 def test_update
359 def test_update
310 entry = TimeEntry.find(1)
360 entry = TimeEntry.find(1)
311 assert_equal 1, entry.issue_id
361 assert_equal 1, entry.issue_id
312 assert_equal 2, entry.user_id
362 assert_equal 2, entry.user_id
313
363
314 @request.session[:user_id] = 1
364 @request.session[:user_id] = 1
315 put :update, :id => 1,
365 put :update, :id => 1,
316 :time_entry => {:issue_id => '2',
366 :time_entry => {:issue_id => '2',
317 :hours => '8'}
367 :hours => '8'}
318 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
368 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
319 entry.reload
369 entry.reload
320
370
321 assert_equal 8, entry.hours
371 assert_equal 8, entry.hours
322 assert_equal 2, entry.issue_id
372 assert_equal 2, entry.issue_id
323 assert_equal 2, entry.user_id
373 assert_equal 2, entry.user_id
324 end
374 end
325
375
326 def test_update_should_allow_to_change_issue_to_another_project
376 def test_update_should_allow_to_change_issue_to_another_project
327 entry = TimeEntry.generate!(:issue_id => 1)
377 entry = TimeEntry.generate!(:issue_id => 1)
328
378
329 @request.session[:user_id] = 1
379 @request.session[:user_id] = 1
330 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
380 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
331 assert_response 302
381 assert_response 302
332 entry.reload
382 entry.reload
333
383
334 assert_equal 5, entry.issue_id
384 assert_equal 5, entry.issue_id
335 assert_equal 3, entry.project_id
385 assert_equal 3, entry.project_id
336 end
386 end
337
387
338 def test_update_should_not_allow_to_change_issue_to_an_invalid_project
388 def test_update_should_not_allow_to_change_issue_to_an_invalid_project
339 entry = TimeEntry.generate!(:issue_id => 1)
389 entry = TimeEntry.generate!(:issue_id => 1)
340 Project.find(3).disable_module!(:time_tracking)
390 Project.find(3).disable_module!(:time_tracking)
341
391
342 @request.session[:user_id] = 1
392 @request.session[:user_id] = 1
343 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
393 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
344 assert_response 200
394 assert_response 200
345 assert_include "Issue is invalid", assigns(:time_entry).errors.full_messages
395 assert_include "Issue is invalid", assigns(:time_entry).errors.full_messages
346 end
396 end
347
397
348 def test_get_bulk_edit
398 def test_get_bulk_edit
349 @request.session[:user_id] = 2
399 @request.session[:user_id] = 2
350 get :bulk_edit, :ids => [1, 2]
400 get :bulk_edit, :ids => [1, 2]
351 assert_response :success
401 assert_response :success
352 assert_template 'bulk_edit'
402 assert_template 'bulk_edit'
353
403
354 assert_select 'ul#bulk-selection' do
404 assert_select 'ul#bulk-selection' do
355 assert_select 'li', 2
405 assert_select 'li', 2
356 assert_select 'li a', :text => '03/23/2007 - eCookbook: 4.25 hours'
406 assert_select 'li a', :text => '03/23/2007 - eCookbook: 4.25 hours'
357 end
407 end
358
408
359 assert_select 'form#bulk_edit_form[action=?]', '/time_entries/bulk_update' do
409 assert_select 'form#bulk_edit_form[action=?]', '/time_entries/bulk_update' do
360 # System wide custom field
410 # System wide custom field
361 assert_select 'select[name=?]', 'time_entry[custom_field_values][10]'
411 assert_select 'select[name=?]', 'time_entry[custom_field_values][10]'
362
412
363 # Activities
413 # Activities
364 assert_select 'select[name=?]', 'time_entry[activity_id]' do
414 assert_select 'select[name=?]', 'time_entry[activity_id]' do
365 assert_select 'option[value=""]', :text => '(No change)'
415 assert_select 'option[value=""]', :text => '(No change)'
366 assert_select 'option[value="9"]', :text => 'Design'
416 assert_select 'option[value="9"]', :text => 'Design'
367 end
417 end
368 end
418 end
369 end
419 end
370
420
371 def test_get_bulk_edit_on_different_projects
421 def test_get_bulk_edit_on_different_projects
372 @request.session[:user_id] = 2
422 @request.session[:user_id] = 2
373 get :bulk_edit, :ids => [1, 2, 6]
423 get :bulk_edit, :ids => [1, 2, 6]
374 assert_response :success
424 assert_response :success
375 assert_template 'bulk_edit'
425 assert_template 'bulk_edit'
376 end
426 end
377
427
378 def test_bulk_update
428 def test_bulk_update
379 @request.session[:user_id] = 2
429 @request.session[:user_id] = 2
380 # update time entry activity
430 # update time entry activity
381 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
431 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
382
432
383 assert_response 302
433 assert_response 302
384 # check that the issues were updated
434 # check that the issues were updated
385 assert_equal [9, 9], TimeEntry.where(:id => [1, 2]).collect {|i| i.activity_id}
435 assert_equal [9, 9], TimeEntry.where(:id => [1, 2]).collect {|i| i.activity_id}
386 end
436 end
387
437
388 def test_bulk_update_with_failure
438 def test_bulk_update_with_failure
389 @request.session[:user_id] = 2
439 @request.session[:user_id] = 2
390 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
440 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
391
441
392 assert_response 302
442 assert_response 302
393 assert_match /Failed to save 2 time entrie/, flash[:error]
443 assert_match /Failed to save 2 time entrie/, flash[:error]
394 end
444 end
395
445
396 def test_bulk_update_on_different_projects
446 def test_bulk_update_on_different_projects
397 @request.session[:user_id] = 2
447 @request.session[:user_id] = 2
398 # makes user a manager on the other project
448 # makes user a manager on the other project
399 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
449 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
400
450
401 # update time entry activity
451 # update time entry activity
402 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
452 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
403
453
404 assert_response 302
454 assert_response 302
405 # check that the issues were updated
455 # check that the issues were updated
406 assert_equal [9, 9, 9], TimeEntry.where(:id => [1, 2, 4]).collect {|i| i.activity_id}
456 assert_equal [9, 9, 9], TimeEntry.where(:id => [1, 2, 4]).collect {|i| i.activity_id}
407 end
457 end
408
458
409 def test_bulk_update_on_different_projects_without_rights
459 def test_bulk_update_on_different_projects_without_rights
410 @request.session[:user_id] = 3
460 @request.session[:user_id] = 3
411 user = User.find(3)
461 user = User.find(3)
412 action = { :controller => "timelog", :action => "bulk_update" }
462 action = { :controller => "timelog", :action => "bulk_update" }
413 assert user.allowed_to?(action, TimeEntry.find(1).project)
463 assert user.allowed_to?(action, TimeEntry.find(1).project)
414 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
464 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
415 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
465 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
416 assert_response 403
466 assert_response 403
417 end
467 end
418
468
419 def test_bulk_update_custom_field
469 def test_bulk_update_custom_field
420 @request.session[:user_id] = 2
470 @request.session[:user_id] = 2
421 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
471 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
422
472
423 assert_response 302
473 assert_response 302
424 assert_equal ["0", "0"], TimeEntry.where(:id => [1, 2]).collect {|i| i.custom_value_for(10).value}
474 assert_equal ["0", "0"], TimeEntry.where(:id => [1, 2]).collect {|i| i.custom_value_for(10).value}
425 end
475 end
426
476
427 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
477 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
428 @request.session[:user_id] = 2
478 @request.session[:user_id] = 2
429 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
479 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
430
480
431 assert_response :redirect
481 assert_response :redirect
432 assert_redirected_to '/time_entries'
482 assert_redirected_to '/time_entries'
433 end
483 end
434
484
435 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
485 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
436 @request.session[:user_id] = 2
486 @request.session[:user_id] = 2
437 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
487 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
438
488
439 assert_response :redirect
489 assert_response :redirect
440 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
490 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
441 end
491 end
442
492
443 def test_post_bulk_update_without_edit_permission_should_be_denied
493 def test_post_bulk_update_without_edit_permission_should_be_denied
444 @request.session[:user_id] = 2
494 @request.session[:user_id] = 2
445 Role.find_by_name('Manager').remove_permission! :edit_time_entries
495 Role.find_by_name('Manager').remove_permission! :edit_time_entries
446 post :bulk_update, :ids => [1,2]
496 post :bulk_update, :ids => [1,2]
447
497
448 assert_response 403
498 assert_response 403
449 end
499 end
450
500
451 def test_destroy
501 def test_destroy
452 @request.session[:user_id] = 2
502 @request.session[:user_id] = 2
453 delete :destroy, :id => 1
503 delete :destroy, :id => 1
454 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
504 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
455 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
505 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
456 assert_nil TimeEntry.find_by_id(1)
506 assert_nil TimeEntry.find_by_id(1)
457 end
507 end
458
508
459 def test_destroy_should_fail
509 def test_destroy_should_fail
460 # simulate that this fails (e.g. due to a plugin), see #5700
510 # simulate that this fails (e.g. due to a plugin), see #5700
461 TimeEntry.any_instance.expects(:destroy).returns(false)
511 TimeEntry.any_instance.expects(:destroy).returns(false)
462
512
463 @request.session[:user_id] = 2
513 @request.session[:user_id] = 2
464 delete :destroy, :id => 1
514 delete :destroy, :id => 1
465 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
515 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
466 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
516 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
467 assert_not_nil TimeEntry.find_by_id(1)
517 assert_not_nil TimeEntry.find_by_id(1)
468 end
518 end
469
519
470 def test_index_all_projects
520 def test_index_all_projects
471 get :index
521 get :index
472 assert_response :success
522 assert_response :success
473 assert_template 'index'
523 assert_template 'index'
474 assert_not_nil assigns(:total_hours)
524 assert_not_nil assigns(:total_hours)
475 assert_equal "162.90", "%.2f" % assigns(:total_hours)
525 assert_equal "162.90", "%.2f" % assigns(:total_hours)
476 assert_select 'form#query_form[action=?]', '/time_entries'
526 assert_select 'form#query_form[action=?]', '/time_entries'
477 end
527 end
478
528
479 def test_index_all_projects_should_show_log_time_link
529 def test_index_all_projects_should_show_log_time_link
480 @request.session[:user_id] = 2
530 @request.session[:user_id] = 2
481 get :index
531 get :index
482 assert_response :success
532 assert_response :success
483 assert_template 'index'
533 assert_template 'index'
484 assert_select 'a[href=?]', '/time_entries/new', :text => /Log time/
534 assert_select 'a[href=?]', '/time_entries/new', :text => /Log time/
485 end
535 end
486
536
487 def test_index_my_spent_time
537 def test_index_my_spent_time
488 @request.session[:user_id] = 2
538 @request.session[:user_id] = 2
489 get :index, :user_id => 'me'
539 get :index, :user_id => 'me'
490 assert_response :success
540 assert_response :success
491 assert_template 'index'
541 assert_template 'index'
492 assert assigns(:entries).all? {|entry| entry.user_id == 2}
542 assert assigns(:entries).all? {|entry| entry.user_id == 2}
493 end
543 end
494
544
495 def test_index_at_project_level
545 def test_index_at_project_level
496 get :index, :project_id => 'ecookbook'
546 get :index, :project_id => 'ecookbook'
497 assert_response :success
547 assert_response :success
498 assert_template 'index'
548 assert_template 'index'
499 assert_not_nil assigns(:entries)
549 assert_not_nil assigns(:entries)
500 assert_equal 4, assigns(:entries).size
550 assert_equal 4, assigns(:entries).size
501 # project and subproject
551 # project and subproject
502 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
552 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
503 assert_not_nil assigns(:total_hours)
553 assert_not_nil assigns(:total_hours)
504 assert_equal "162.90", "%.2f" % assigns(:total_hours)
554 assert_equal "162.90", "%.2f" % assigns(:total_hours)
505 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
555 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
506 end
556 end
507
557
508 def test_index_with_display_subprojects_issues_to_false_should_not_include_subproject_entries
558 def test_index_with_display_subprojects_issues_to_false_should_not_include_subproject_entries
509 entry = TimeEntry.generate!(:project => Project.find(3))
559 entry = TimeEntry.generate!(:project => Project.find(3))
510
560
511 with_settings :display_subprojects_issues => '0' do
561 with_settings :display_subprojects_issues => '0' do
512 get :index, :project_id => 'ecookbook'
562 get :index, :project_id => 'ecookbook'
513 assert_response :success
563 assert_response :success
514 assert_template 'index'
564 assert_template 'index'
515 assert_not_include entry, assigns(:entries)
565 assert_not_include entry, assigns(:entries)
516 end
566 end
517 end
567 end
518
568
519 def test_index_with_display_subprojects_issues_to_false_and_subproject_filter_should_include_subproject_entries
569 def test_index_with_display_subprojects_issues_to_false_and_subproject_filter_should_include_subproject_entries
520 entry = TimeEntry.generate!(:project => Project.find(3))
570 entry = TimeEntry.generate!(:project => Project.find(3))
521
571
522 with_settings :display_subprojects_issues => '0' do
572 with_settings :display_subprojects_issues => '0' do
523 get :index, :project_id => 'ecookbook', :subproject_id => 3
573 get :index, :project_id => 'ecookbook', :subproject_id => 3
524 assert_response :success
574 assert_response :success
525 assert_template 'index'
575 assert_template 'index'
526 assert_include entry, assigns(:entries)
576 assert_include entry, assigns(:entries)
527 end
577 end
528 end
578 end
529
579
530 def test_index_at_project_level_with_date_range
580 def test_index_at_project_level_with_date_range
531 get :index, :project_id => 'ecookbook',
581 get :index, :project_id => 'ecookbook',
532 :f => ['spent_on'],
582 :f => ['spent_on'],
533 :op => {'spent_on' => '><'},
583 :op => {'spent_on' => '><'},
534 :v => {'spent_on' => ['2007-03-20', '2007-04-30']}
584 :v => {'spent_on' => ['2007-03-20', '2007-04-30']}
535 assert_response :success
585 assert_response :success
536 assert_template 'index'
586 assert_template 'index'
537 assert_not_nil assigns(:entries)
587 assert_not_nil assigns(:entries)
538 assert_equal 3, assigns(:entries).size
588 assert_equal 3, assigns(:entries).size
539 assert_not_nil assigns(:total_hours)
589 assert_not_nil assigns(:total_hours)
540 assert_equal "12.90", "%.2f" % assigns(:total_hours)
590 assert_equal "12.90", "%.2f" % assigns(:total_hours)
541 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
591 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
542 end
592 end
543
593
544 def test_index_at_project_level_with_date_range_using_from_and_to_params
594 def test_index_at_project_level_with_date_range_using_from_and_to_params
545 get :index, :project_id => 'ecookbook', :from => '2007-03-20', :to => '2007-04-30'
595 get :index, :project_id => 'ecookbook', :from => '2007-03-20', :to => '2007-04-30'
546 assert_response :success
596 assert_response :success
547 assert_template 'index'
597 assert_template 'index'
548 assert_not_nil assigns(:entries)
598 assert_not_nil assigns(:entries)
549 assert_equal 3, assigns(:entries).size
599 assert_equal 3, assigns(:entries).size
550 assert_not_nil assigns(:total_hours)
600 assert_not_nil assigns(:total_hours)
551 assert_equal "12.90", "%.2f" % assigns(:total_hours)
601 assert_equal "12.90", "%.2f" % assigns(:total_hours)
552 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
602 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
553 end
603 end
554
604
555 def test_index_at_project_level_with_period
605 def test_index_at_project_level_with_period
556 get :index, :project_id => 'ecookbook',
606 get :index, :project_id => 'ecookbook',
557 :f => ['spent_on'],
607 :f => ['spent_on'],
558 :op => {'spent_on' => '>t-'},
608 :op => {'spent_on' => '>t-'},
559 :v => {'spent_on' => ['7']}
609 :v => {'spent_on' => ['7']}
560 assert_response :success
610 assert_response :success
561 assert_template 'index'
611 assert_template 'index'
562 assert_not_nil assigns(:entries)
612 assert_not_nil assigns(:entries)
563 assert_not_nil assigns(:total_hours)
613 assert_not_nil assigns(:total_hours)
564 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
614 assert_select 'form#query_form[action=?]', '/projects/ecookbook/time_entries'
565 end
615 end
566
616
567 def test_index_at_issue_level
617 def test_index_at_issue_level
568 get :index, :issue_id => 1
618 get :index, :issue_id => 1
569 assert_response :success
619 assert_response :success
570 assert_template 'index'
620 assert_template 'index'
571 assert_not_nil assigns(:entries)
621 assert_not_nil assigns(:entries)
572 assert_equal 2, assigns(:entries).size
622 assert_equal 2, assigns(:entries).size
573 assert_not_nil assigns(:total_hours)
623 assert_not_nil assigns(:total_hours)
574 assert_equal 154.25, assigns(:total_hours)
624 assert_equal 154.25, assigns(:total_hours)
575 # display all time
625 # display all time
576 assert_nil assigns(:from)
626 assert_nil assigns(:from)
577 assert_nil assigns(:to)
627 assert_nil assigns(:to)
578 assert_select 'form#query_form[action=?]', '/issues/1/time_entries'
628 assert_select 'form#query_form[action=?]', '/issues/1/time_entries'
579 end
629 end
580
630
581 def test_index_should_sort_by_spent_on_and_created_on
631 def test_index_should_sort_by_spent_on_and_created_on
582 t1 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:00:00', :activity_id => 10)
632 t1 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:00:00', :activity_id => 10)
583 t2 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:05:00', :activity_id => 10)
633 t2 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:05:00', :activity_id => 10)
584 t3 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-15', :created_on => '2012-06-16 20:10:00', :activity_id => 10)
634 t3 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-15', :created_on => '2012-06-16 20:10:00', :activity_id => 10)
585
635
586 get :index, :project_id => 1,
636 get :index, :project_id => 1,
587 :f => ['spent_on'],
637 :f => ['spent_on'],
588 :op => {'spent_on' => '><'},
638 :op => {'spent_on' => '><'},
589 :v => {'spent_on' => ['2012-06-15', '2012-06-16']}
639 :v => {'spent_on' => ['2012-06-15', '2012-06-16']}
590 assert_response :success
640 assert_response :success
591 assert_equal [t2, t1, t3], assigns(:entries)
641 assert_equal [t2, t1, t3], assigns(:entries)
592
642
593 get :index, :project_id => 1,
643 get :index, :project_id => 1,
594 :f => ['spent_on'],
644 :f => ['spent_on'],
595 :op => {'spent_on' => '><'},
645 :op => {'spent_on' => '><'},
596 :v => {'spent_on' => ['2012-06-15', '2012-06-16']},
646 :v => {'spent_on' => ['2012-06-15', '2012-06-16']},
597 :sort => 'spent_on'
647 :sort => 'spent_on'
598 assert_response :success
648 assert_response :success
599 assert_equal [t3, t1, t2], assigns(:entries)
649 assert_equal [t3, t1, t2], assigns(:entries)
600 end
650 end
601
651
602 def test_index_with_filter_on_issue_custom_field
652 def test_index_with_filter_on_issue_custom_field
603 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
653 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
604 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
654 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
605
655
606 get :index, :f => ['issue.cf_2'], :op => {'issue.cf_2' => '='}, :v => {'issue.cf_2' => ['filter_on_issue_custom_field']}
656 get :index, :f => ['issue.cf_2'], :op => {'issue.cf_2' => '='}, :v => {'issue.cf_2' => ['filter_on_issue_custom_field']}
607 assert_response :success
657 assert_response :success
608 assert_equal [entry], assigns(:entries)
658 assert_equal [entry], assigns(:entries)
609 end
659 end
610
660
611 def test_index_with_issue_custom_field_column
661 def test_index_with_issue_custom_field_column
612 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
662 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
613 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
663 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
614
664
615 get :index, :c => %w(project spent_on issue comments hours issue.cf_2)
665 get :index, :c => %w(project spent_on issue comments hours issue.cf_2)
616 assert_response :success
666 assert_response :success
617 assert_include :'issue.cf_2', assigns(:query).column_names
667 assert_include :'issue.cf_2', assigns(:query).column_names
618 assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field'
668 assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field'
619 end
669 end
620
670
621 def test_index_with_time_entry_custom_field_column
671 def test_index_with_time_entry_custom_field_column
622 field = TimeEntryCustomField.generate!(:field_format => 'string')
672 field = TimeEntryCustomField.generate!(:field_format => 'string')
623 entry = TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value'})
673 entry = TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value'})
624 field_name = "cf_#{field.id}"
674 field_name = "cf_#{field.id}"
625
675
626 get :index, :c => ["hours", field_name]
676 get :index, :c => ["hours", field_name]
627 assert_response :success
677 assert_response :success
628 assert_include field_name.to_sym, assigns(:query).column_names
678 assert_include field_name.to_sym, assigns(:query).column_names
629 assert_select "td.#{field_name}", :text => 'CF Value'
679 assert_select "td.#{field_name}", :text => 'CF Value'
630 end
680 end
631
681
632 def test_index_with_time_entry_custom_field_sorting
682 def test_index_with_time_entry_custom_field_sorting
633 field = TimeEntryCustomField.generate!(:field_format => 'string', :name => 'String Field')
683 field = TimeEntryCustomField.generate!(:field_format => 'string', :name => 'String Field')
634 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 1'})
684 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 1'})
635 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 3'})
685 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 3'})
636 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 2'})
686 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 2'})
637 field_name = "cf_#{field.id}"
687 field_name = "cf_#{field.id}"
638
688
639 get :index, :c => ["hours", field_name], :sort => field_name
689 get :index, :c => ["hours", field_name], :sort => field_name
640 assert_response :success
690 assert_response :success
641 assert_include field_name.to_sym, assigns(:query).column_names
691 assert_include field_name.to_sym, assigns(:query).column_names
642 assert_select "th a.sort", :text => 'String Field'
692 assert_select "th a.sort", :text => 'String Field'
643
693
644 # Make sure that values are properly sorted
694 # Make sure that values are properly sorted
645 values = assigns(:entries).map {|e| e.custom_field_value(field)}.compact
695 values = assigns(:entries).map {|e| e.custom_field_value(field)}.compact
646 assert_equal 3, values.size
696 assert_equal 3, values.size
647 assert_equal values.sort, values
697 assert_equal values.sort, values
648 end
698 end
649
699
650 def test_index_atom_feed
700 def test_index_atom_feed
651 get :index, :project_id => 1, :format => 'atom'
701 get :index, :project_id => 1, :format => 'atom'
652 assert_response :success
702 assert_response :success
653 assert_equal 'application/atom+xml', @response.content_type
703 assert_equal 'application/atom+xml', @response.content_type
654 assert_not_nil assigns(:items)
704 assert_not_nil assigns(:items)
655 assert assigns(:items).first.is_a?(TimeEntry)
705 assert assigns(:items).first.is_a?(TimeEntry)
656 end
706 end
657
707
658 def test_index_at_project_level_should_include_csv_export_dialog
708 def test_index_at_project_level_should_include_csv_export_dialog
659 get :index, :project_id => 'ecookbook',
709 get :index, :project_id => 'ecookbook',
660 :f => ['spent_on'],
710 :f => ['spent_on'],
661 :op => {'spent_on' => '>='},
711 :op => {'spent_on' => '>='},
662 :v => {'spent_on' => ['2007-04-01']},
712 :v => {'spent_on' => ['2007-04-01']},
663 :c => ['spent_on', 'user']
713 :c => ['spent_on', 'user']
664 assert_response :success
714 assert_response :success
665
715
666 assert_select '#csv-export-options' do
716 assert_select '#csv-export-options' do
667 assert_select 'form[action=?][method=get]', '/projects/ecookbook/time_entries.csv' do
717 assert_select 'form[action=?][method=get]', '/projects/ecookbook/time_entries.csv' do
668 # filter
718 # filter
669 assert_select 'input[name=?][value=?]', 'f[]', 'spent_on'
719 assert_select 'input[name=?][value=?]', 'f[]', 'spent_on'
670 assert_select 'input[name=?][value=?]', 'op[spent_on]', '>='
720 assert_select 'input[name=?][value=?]', 'op[spent_on]', '>='
671 assert_select 'input[name=?][value=?]', 'v[spent_on][]', '2007-04-01'
721 assert_select 'input[name=?][value=?]', 'v[spent_on][]', '2007-04-01'
672 # columns
722 # columns
673 assert_select 'input[name=?][value=?]', 'c[]', 'spent_on'
723 assert_select 'input[name=?][value=?]', 'c[]', 'spent_on'
674 assert_select 'input[name=?][value=?]', 'c[]', 'user'
724 assert_select 'input[name=?][value=?]', 'c[]', 'user'
675 assert_select 'input[name=?]', 'c[]', 2
725 assert_select 'input[name=?]', 'c[]', 2
676 end
726 end
677 end
727 end
678 end
728 end
679
729
680 def test_index_cross_project_should_include_csv_export_dialog
730 def test_index_cross_project_should_include_csv_export_dialog
681 get :index
731 get :index
682 assert_response :success
732 assert_response :success
683
733
684 assert_select '#csv-export-options' do
734 assert_select '#csv-export-options' do
685 assert_select 'form[action=?][method=get]', '/time_entries.csv'
735 assert_select 'form[action=?][method=get]', '/time_entries.csv'
686 end
736 end
687 end
737 end
688
738
689 def test_index_at_issue_level_should_include_csv_export_dialog
739 def test_index_at_issue_level_should_include_csv_export_dialog
690 get :index, :issue_id => 3
740 get :index, :issue_id => 3
691 assert_response :success
741 assert_response :success
692
742
693 assert_select '#csv-export-options' do
743 assert_select '#csv-export-options' do
694 assert_select 'form[action=?][method=get]', '/issues/3/time_entries.csv'
744 assert_select 'form[action=?][method=get]', '/issues/3/time_entries.csv'
695 end
745 end
696 end
746 end
697
747
698 def test_index_csv_all_projects
748 def test_index_csv_all_projects
699 with_settings :date_format => '%m/%d/%Y' do
749 with_settings :date_format => '%m/%d/%Y' do
700 get :index, :format => 'csv'
750 get :index, :format => 'csv'
701 assert_response :success
751 assert_response :success
702 assert_equal 'text/csv; header=present', response.content_type
752 assert_equal 'text/csv; header=present', response.content_type
703 end
753 end
704 end
754 end
705
755
706 def test_index_csv
756 def test_index_csv
707 with_settings :date_format => '%m/%d/%Y' do
757 with_settings :date_format => '%m/%d/%Y' do
708 get :index, :project_id => 1, :format => 'csv'
758 get :index, :project_id => 1, :format => 'csv'
709 assert_response :success
759 assert_response :success
710 assert_equal 'text/csv; header=present', response.content_type
760 assert_equal 'text/csv; header=present', response.content_type
711 end
761 end
712 end
762 end
713
763
714 def test_index_csv_should_fill_issue_column_with_tracker_id_and_subject
764 def test_index_csv_should_fill_issue_column_with_tracker_id_and_subject
715 issue = Issue.find(1)
765 issue = Issue.find(1)
716 entry = TimeEntry.generate!(:issue => issue, :comments => "Issue column content test")
766 entry = TimeEntry.generate!(:issue => issue, :comments => "Issue column content test")
717
767
718 get :index, :format => 'csv'
768 get :index, :format => 'csv'
719 line = response.body.split("\n").detect {|l| l.include?(entry.comments)}
769 line = response.body.split("\n").detect {|l| l.include?(entry.comments)}
720 assert_not_nil line
770 assert_not_nil line
721 assert_include "#{issue.tracker} #1: #{issue.subject}", line
771 assert_include "#{issue.tracker} #1: #{issue.subject}", line
722 end
772 end
723 end
773 end
General Comments 0
You need to be logged in to leave comments. Login now