##// END OF EJS Templates
Merged r14242 (#18580)....
Jean-Philippe Lang -
r13870:b51dfd414bc6
parent child
Show More
@@ -1,99 +1,99
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 ContextMenusController < ApplicationController
18 class ContextMenusController < ApplicationController
19 helper :watchers
19 helper :watchers
20 helper :issues
20 helper :issues
21
21
22 before_filter :find_issues, :only => :issues
22 before_filter :find_issues, :only => :issues
23
23
24 def issues
24 def issues
25 if (@issues.size == 1)
25 if (@issues.size == 1)
26 @issue = @issues.first
26 @issue = @issues.first
27 end
27 end
28 @issue_ids = @issues.map(&:id).sort
28 @issue_ids = @issues.map(&:id).sort
29
29
30 @allowed_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
30 @allowed_statuses = @issues.map(&:new_statuses_allowed_to).reduce(:&)
31
31
32 @can = {:edit => User.current.allowed_to?(:edit_issues, @projects),
32 @can = {:edit => User.current.allowed_to?(:edit_issues, @projects),
33 :log_time => (@project && User.current.allowed_to?(:log_time, @project)),
33 :log_time => (@project && User.current.allowed_to?(:log_time, @project)),
34 :update => (User.current.allowed_to?(:edit_issues, @projects) || (User.current.allowed_to?(:change_status, @projects) && !@allowed_statuses.blank?)),
34 :update => (User.current.allowed_to?(:edit_issues, @projects) || (User.current.allowed_to?(:change_status, @projects) && !@allowed_statuses.blank?)),
35 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
35 :move => (@project && User.current.allowed_to?(:move_issues, @project)),
36 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
36 :copy => (@issue && @project.trackers.include?(@issue.tracker) && User.current.allowed_to?(:add_issues, @project)),
37 :delete => User.current.allowed_to?(:delete_issues, @projects)
37 :delete => User.current.allowed_to?(:delete_issues, @projects)
38 }
38 }
39 if @project
39 if @project
40 if @issue
40 if @issue
41 @assignables = @issue.assignable_users
41 @assignables = @issue.assignable_users
42 else
42 else
43 @assignables = @project.assignable_users
43 @assignables = @project.assignable_users
44 end
44 end
45 @trackers = @project.trackers
45 @trackers = @project.trackers
46 else
46 else
47 #when multiple projects, we only keep the intersection of each set
47 #when multiple projects, we only keep the intersection of each set
48 @assignables = @projects.map(&:assignable_users).reduce(:&)
48 @assignables = @projects.map(&:assignable_users).reduce(:&)
49 @trackers = @projects.map(&:trackers).reduce(:&)
49 @trackers = @projects.map(&:trackers).reduce(:&)
50 end
50 end
51 @versions = @projects.map {|p| p.shared_versions.open}.reduce(:&)
51 @versions = @projects.map {|p| p.shared_versions.open}.reduce(:&)
52
52
53 @priorities = IssuePriority.active.reverse
53 @priorities = IssuePriority.active.reverse
54 @back = back_url
54 @back = back_url
55
55
56 @options_by_custom_field = {}
56 @options_by_custom_field = {}
57 if @can[:edit]
57 if @can[:edit]
58 custom_fields = @issues.map(&:editable_custom_fields).reduce(:&).reject(&:multiple?)
58 custom_fields = @issues.map(&:editable_custom_fields).reduce(:&).reject(&:multiple?)
59 custom_fields.each do |field|
59 custom_fields.each do |field|
60 values = field.possible_values_options(@projects)
60 values = field.possible_values_options(@projects)
61 if values.present?
61 if values.present?
62 @options_by_custom_field[field] = values
62 @options_by_custom_field[field] = values
63 end
63 end
64 end
64 end
65 end
65 end
66
66
67 @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
67 @safe_attributes = @issues.map(&:safe_attribute_names).reduce(:&)
68 render :layout => false
68 render :layout => false
69 end
69 end
70
70
71 def time_entries
71 def time_entries
72 @time_entries = TimeEntry.where(:id => params[:ids]).preload(:project).to_a
72 @time_entries = TimeEntry.where(:id => params[:ids]).preload(:project).to_a
73 (render_404; return) unless @time_entries.present?
73 (render_404; return) unless @time_entries.present?
74 if (@time_entries.size == 1)
74 if (@time_entries.size == 1)
75 @time_entry = @time_entries.first
75 @time_entry = @time_entries.first
76 end
76 end
77
77
78 @projects = @time_entries.collect(&:project).compact.uniq
78 @projects = @time_entries.collect(&:project).compact.uniq
79 @project = @projects.first if @projects.size == 1
79 @project = @projects.first if @projects.size == 1
80 @activities = TimeEntryActivity.shared.active
80 @activities = TimeEntryActivity.shared.active
81 @can = {:edit => User.current.allowed_to?(:edit_time_entries, @projects),
81
82 :delete => User.current.allowed_to?(:edit_time_entries, @projects)
82 edit_allowed = @time_entries.all? {|t| t.editable_by?(User.current)}
83 }
83 @can = {:edit => edit_allowed, :delete => edit_allowed}
84 @back = back_url
84 @back = back_url
85
85
86 @options_by_custom_field = {}
86 @options_by_custom_field = {}
87 if @can[:edit]
87 if @can[:edit]
88 custom_fields = @time_entries.map(&:editable_custom_fields).reduce(:&).reject(&:multiple?)
88 custom_fields = @time_entries.map(&:editable_custom_fields).reduce(:&).reject(&:multiple?)
89 custom_fields.each do |field|
89 custom_fields.each do |field|
90 values = field.possible_values_options(@projects)
90 values = field.possible_values_options(@projects)
91 if values.present?
91 if values.present?
92 @options_by_custom_field[field] = values
92 @options_by_custom_field[field] = values
93 end
93 end
94 end
94 end
95 end
95 end
96
96
97 render :layout => false
97 render :layout => false
98 end
98 end
99 end
99 end
@@ -1,280 +1,281
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 TimelogController < ApplicationController
18 class TimelogController < ApplicationController
19 menu_item :issues
19 menu_item :issues
20
20
21 before_filter :find_time_entry, :only => [:show, :edit, :update]
21 before_filter :find_time_entry, :only => [:show, :edit, :update]
22 before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
22 before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
23 before_filter :authorize, :only => [:show, :edit, :update, :bulk_edit, :bulk_update, :destroy]
23 before_filter :authorize, :only => [:show, :edit, :update, :bulk_edit, :bulk_update, :destroy]
24
24
25 before_filter :find_optional_project, :only => [:new, :create, :index, :report]
25 before_filter :find_optional_project, :only => [:new, :create, :index, :report]
26 before_filter :authorize_global, :only => [:new, :create, :index, :report]
26 before_filter :authorize_global, :only => [:new, :create, :index, :report]
27
27
28 accept_rss_auth :index
28 accept_rss_auth :index
29 accept_api_auth :index, :show, :create, :update, :destroy
29 accept_api_auth :index, :show, :create, :update, :destroy
30
30
31 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
31 rescue_from Query::StatementInvalid, :with => :query_statement_invalid
32
32
33 helper :sort
33 helper :sort
34 include SortHelper
34 include SortHelper
35 helper :issues
35 helper :issues
36 include TimelogHelper
36 include TimelogHelper
37 helper :custom_fields
37 helper :custom_fields
38 include CustomFieldsHelper
38 include CustomFieldsHelper
39 helper :queries
39 helper :queries
40 include QueriesHelper
40 include QueriesHelper
41
41
42 def index
42 def index
43 @query = TimeEntryQuery.build_from_params(params, :project => @project, :name => '_')
43 @query = TimeEntryQuery.build_from_params(params, :project => @project, :name => '_')
44
44
45 sort_init(@query.sort_criteria.empty? ? [['spent_on', 'desc']] : @query.sort_criteria)
45 sort_init(@query.sort_criteria.empty? ? [['spent_on', 'desc']] : @query.sort_criteria)
46 sort_update(@query.sortable_columns)
46 sort_update(@query.sortable_columns)
47 scope = time_entry_scope(:order => sort_clause).
47 scope = time_entry_scope(:order => sort_clause).
48 includes(:project, :user, :issue).
48 includes(:project, :user, :issue).
49 preload(:issue => [:project, :tracker, :status, :assigned_to, :priority])
49 preload(:issue => [:project, :tracker, :status, :assigned_to, :priority])
50
50
51 respond_to do |format|
51 respond_to do |format|
52 format.html {
52 format.html {
53 @entry_count = scope.count
53 @entry_count = scope.count
54 @entry_pages = Paginator.new @entry_count, per_page_option, params['page']
54 @entry_pages = Paginator.new @entry_count, per_page_option, params['page']
55 @entries = scope.offset(@entry_pages.offset).limit(@entry_pages.per_page).all
55 @entries = scope.offset(@entry_pages.offset).limit(@entry_pages.per_page).all
56 @total_hours = scope.sum(:hours).to_f
56 @total_hours = scope.sum(:hours).to_f
57
57
58 render :layout => !request.xhr?
58 render :layout => !request.xhr?
59 }
59 }
60 format.api {
60 format.api {
61 @entry_count = scope.count
61 @entry_count = scope.count
62 @offset, @limit = api_offset_and_limit
62 @offset, @limit = api_offset_and_limit
63 @entries = scope.offset(@offset).limit(@limit).preload(:custom_values => :custom_field).all
63 @entries = scope.offset(@offset).limit(@limit).preload(:custom_values => :custom_field).all
64 }
64 }
65 format.atom {
65 format.atom {
66 entries = scope.limit(Setting.feeds_limit.to_i).reorder("#{TimeEntry.table_name}.created_on DESC").all
66 entries = scope.limit(Setting.feeds_limit.to_i).reorder("#{TimeEntry.table_name}.created_on DESC").all
67 render_feed(entries, :title => l(:label_spent_time))
67 render_feed(entries, :title => l(:label_spent_time))
68 }
68 }
69 format.csv {
69 format.csv {
70 # Export all entries
70 # Export all entries
71 @entries = scope.all
71 @entries = scope.all
72 send_data(query_to_csv(@entries, @query, params), :type => 'text/csv; header=present', :filename => 'timelog.csv')
72 send_data(query_to_csv(@entries, @query, params), :type => 'text/csv; header=present', :filename => 'timelog.csv')
73 }
73 }
74 end
74 end
75 end
75 end
76
76
77 def report
77 def report
78 @query = TimeEntryQuery.build_from_params(params, :project => @project, :name => '_')
78 @query = TimeEntryQuery.build_from_params(params, :project => @project, :name => '_')
79 scope = time_entry_scope
79 scope = time_entry_scope
80
80
81 @report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], scope)
81 @report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], scope)
82
82
83 respond_to do |format|
83 respond_to do |format|
84 format.html { render :layout => !request.xhr? }
84 format.html { render :layout => !request.xhr? }
85 format.csv { send_data(report_to_csv(@report), :type => 'text/csv; header=present', :filename => 'timelog.csv') }
85 format.csv { send_data(report_to_csv(@report), :type => 'text/csv; header=present', :filename => 'timelog.csv') }
86 end
86 end
87 end
87 end
88
88
89 def show
89 def show
90 respond_to do |format|
90 respond_to do |format|
91 # TODO: Implement html response
91 # TODO: Implement html response
92 format.html { render :nothing => true, :status => 406 }
92 format.html { render :nothing => true, :status => 406 }
93 format.api
93 format.api
94 end
94 end
95 end
95 end
96
96
97 def new
97 def new
98 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
98 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
99 @time_entry.safe_attributes = params[:time_entry]
99 @time_entry.safe_attributes = params[:time_entry]
100 end
100 end
101
101
102 def create
102 def create
103 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
103 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
104 @time_entry.safe_attributes = params[:time_entry]
104 @time_entry.safe_attributes = params[:time_entry]
105 if @time_entry.project && !User.current.allowed_to?(:log_time, @time_entry.project)
105 if @time_entry.project && !User.current.allowed_to?(:log_time, @time_entry.project)
106 render_403
106 render_403
107 return
107 return
108 end
108 end
109
109
110 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
110 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
111
111
112 if @time_entry.save
112 if @time_entry.save
113 respond_to do |format|
113 respond_to do |format|
114 format.html {
114 format.html {
115 flash[:notice] = l(:notice_successful_create)
115 flash[:notice] = l(:notice_successful_create)
116 if params[:continue]
116 if params[:continue]
117 options = {
117 options = {
118 :time_entry => {
118 :time_entry => {
119 :project_id => params[:time_entry][:project_id],
119 :project_id => params[:time_entry][:project_id],
120 :issue_id => @time_entry.issue_id,
120 :issue_id => @time_entry.issue_id,
121 :activity_id => @time_entry.activity_id
121 :activity_id => @time_entry.activity_id
122 },
122 },
123 :back_url => params[:back_url]
123 :back_url => params[:back_url]
124 }
124 }
125 if params[:project_id] && @time_entry.project
125 if params[:project_id] && @time_entry.project
126 redirect_to new_project_time_entry_path(@time_entry.project, options)
126 redirect_to new_project_time_entry_path(@time_entry.project, options)
127 elsif params[:issue_id] && @time_entry.issue
127 elsif params[:issue_id] && @time_entry.issue
128 redirect_to new_issue_time_entry_path(@time_entry.issue, options)
128 redirect_to new_issue_time_entry_path(@time_entry.issue, options)
129 else
129 else
130 redirect_to new_time_entry_path(options)
130 redirect_to new_time_entry_path(options)
131 end
131 end
132 else
132 else
133 redirect_back_or_default project_time_entries_path(@time_entry.project)
133 redirect_back_or_default project_time_entries_path(@time_entry.project)
134 end
134 end
135 }
135 }
136 format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
136 format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
137 end
137 end
138 else
138 else
139 respond_to do |format|
139 respond_to do |format|
140 format.html { render :action => 'new' }
140 format.html { render :action => 'new' }
141 format.api { render_validation_errors(@time_entry) }
141 format.api { render_validation_errors(@time_entry) }
142 end
142 end
143 end
143 end
144 end
144 end
145
145
146 def edit
146 def edit
147 @time_entry.safe_attributes = params[:time_entry]
147 @time_entry.safe_attributes = params[:time_entry]
148 end
148 end
149
149
150 def update
150 def update
151 @time_entry.safe_attributes = params[:time_entry]
151 @time_entry.safe_attributes = params[:time_entry]
152
152
153 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
153 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
154
154
155 if @time_entry.save
155 if @time_entry.save
156 respond_to do |format|
156 respond_to do |format|
157 format.html {
157 format.html {
158 flash[:notice] = l(:notice_successful_update)
158 flash[:notice] = l(:notice_successful_update)
159 redirect_back_or_default project_time_entries_path(@time_entry.project)
159 redirect_back_or_default project_time_entries_path(@time_entry.project)
160 }
160 }
161 format.api { render_api_ok }
161 format.api { render_api_ok }
162 end
162 end
163 else
163 else
164 respond_to do |format|
164 respond_to do |format|
165 format.html { render :action => 'edit' }
165 format.html { render :action => 'edit' }
166 format.api { render_validation_errors(@time_entry) }
166 format.api { render_validation_errors(@time_entry) }
167 end
167 end
168 end
168 end
169 end
169 end
170
170
171 def bulk_edit
171 def bulk_edit
172 @available_activities = TimeEntryActivity.shared.active
172 @available_activities = TimeEntryActivity.shared.active
173 @custom_fields = TimeEntry.first.available_custom_fields
173 @custom_fields = TimeEntry.first.available_custom_fields
174 end
174 end
175
175
176 def bulk_update
176 def bulk_update
177 attributes = parse_params_for_bulk_time_entry_attributes(params)
177 attributes = parse_params_for_bulk_time_entry_attributes(params)
178
178
179 unsaved_time_entry_ids = []
179 unsaved_time_entry_ids = []
180 @time_entries.each do |time_entry|
180 @time_entries.each do |time_entry|
181 time_entry.reload
181 time_entry.reload
182 time_entry.safe_attributes = attributes
182 time_entry.safe_attributes = attributes
183 call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry })
183 call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry })
184 unless time_entry.save
184 unless time_entry.save
185 logger.info "time entry could not be updated: #{time_entry.errors.full_messages}" if logger && logger.info
185 logger.info "time entry could not be updated: #{time_entry.errors.full_messages}" if logger && logger.info
186 # Keep unsaved time_entry ids to display them in flash error
186 # Keep unsaved time_entry ids to display them in flash error
187 unsaved_time_entry_ids << time_entry.id
187 unsaved_time_entry_ids << time_entry.id
188 end
188 end
189 end
189 end
190 set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids)
190 set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids)
191 redirect_back_or_default project_time_entries_path(@projects.first)
191 redirect_back_or_default project_time_entries_path(@projects.first)
192 end
192 end
193
193
194 def destroy
194 def destroy
195 destroyed = TimeEntry.transaction do
195 destroyed = TimeEntry.transaction do
196 @time_entries.each do |t|
196 @time_entries.each do |t|
197 unless t.destroy && t.destroyed?
197 unless t.destroy && t.destroyed?
198 raise ActiveRecord::Rollback
198 raise ActiveRecord::Rollback
199 end
199 end
200 end
200 end
201 end
201 end
202
202
203 respond_to do |format|
203 respond_to do |format|
204 format.html {
204 format.html {
205 if destroyed
205 if destroyed
206 flash[:notice] = l(:notice_successful_delete)
206 flash[:notice] = l(:notice_successful_delete)
207 else
207 else
208 flash[:error] = l(:notice_unable_delete_time_entry)
208 flash[:error] = l(:notice_unable_delete_time_entry)
209 end
209 end
210 redirect_back_or_default project_time_entries_path(@projects.first)
210 redirect_back_or_default project_time_entries_path(@projects.first)
211 }
211 }
212 format.api {
212 format.api {
213 if destroyed
213 if destroyed
214 render_api_ok
214 render_api_ok
215 else
215 else
216 render_validation_errors(@time_entries)
216 render_validation_errors(@time_entries)
217 end
217 end
218 }
218 }
219 end
219 end
220 end
220 end
221
221
222 private
222 private
223 def find_time_entry
223 def find_time_entry
224 @time_entry = TimeEntry.find(params[:id])
224 @time_entry = TimeEntry.find(params[:id])
225 unless @time_entry.editable_by?(User.current)
225 unless @time_entry.editable_by?(User.current)
226 render_403
226 render_403
227 return false
227 return false
228 end
228 end
229 @project = @time_entry.project
229 @project = @time_entry.project
230 rescue ActiveRecord::RecordNotFound
230 rescue ActiveRecord::RecordNotFound
231 render_404
231 render_404
232 end
232 end
233
233
234 def find_time_entries
234 def find_time_entries
235 @time_entries = TimeEntry.where(:id => params[:id] || params[:ids]).all
235 @time_entries = TimeEntry.where(:id => params[:id] || params[:ids]).all
236 raise ActiveRecord::RecordNotFound if @time_entries.empty?
236 raise ActiveRecord::RecordNotFound if @time_entries.empty?
237 raise Unauthorized unless @time_entries.all? {|t| t.editable_by?(User.current)}
237 @projects = @time_entries.collect(&:project).compact.uniq
238 @projects = @time_entries.collect(&:project).compact.uniq
238 @project = @projects.first if @projects.size == 1
239 @project = @projects.first if @projects.size == 1
239 rescue ActiveRecord::RecordNotFound
240 rescue ActiveRecord::RecordNotFound
240 render_404
241 render_404
241 end
242 end
242
243
243 def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids)
244 def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids)
244 if unsaved_time_entry_ids.empty?
245 if unsaved_time_entry_ids.empty?
245 flash[:notice] = l(:notice_successful_update) unless time_entries.empty?
246 flash[:notice] = l(:notice_successful_update) unless time_entries.empty?
246 else
247 else
247 flash[:error] = l(:notice_failed_to_save_time_entries,
248 flash[:error] = l(:notice_failed_to_save_time_entries,
248 :count => unsaved_time_entry_ids.size,
249 :count => unsaved_time_entry_ids.size,
249 :total => time_entries.size,
250 :total => time_entries.size,
250 :ids => '#' + unsaved_time_entry_ids.join(', #'))
251 :ids => '#' + unsaved_time_entry_ids.join(', #'))
251 end
252 end
252 end
253 end
253
254
254 def find_optional_project
255 def find_optional_project
255 if params[:issue_id].present?
256 if params[:issue_id].present?
256 @issue = Issue.find(params[:issue_id])
257 @issue = Issue.find(params[:issue_id])
257 @project = @issue.project
258 @project = @issue.project
258 elsif params[:project_id].present?
259 elsif params[:project_id].present?
259 @project = Project.find(params[:project_id])
260 @project = Project.find(params[:project_id])
260 end
261 end
261 rescue ActiveRecord::RecordNotFound
262 rescue ActiveRecord::RecordNotFound
262 render_404
263 render_404
263 end
264 end
264
265
265 # Returns the TimeEntry scope for index and report actions
266 # Returns the TimeEntry scope for index and report actions
266 def time_entry_scope(options={})
267 def time_entry_scope(options={})
267 scope = @query.results_scope(options)
268 scope = @query.results_scope(options)
268 if @issue
269 if @issue
269 scope = scope.on_issue(@issue)
270 scope = scope.on_issue(@issue)
270 end
271 end
271 scope
272 scope
272 end
273 end
273
274
274 def parse_params_for_bulk_time_entry_attributes(params)
275 def parse_params_for_bulk_time_entry_attributes(params)
275 attributes = (params[:time_entry] || {}).reject {|k,v| v.blank?}
276 attributes = (params[:time_entry] || {}).reject {|k,v| v.blank?}
276 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
277 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
277 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
278 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
278 attributes
279 attributes
279 end
280 end
280 end
281 end
@@ -1,279 +1,291
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 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class ContextMenusControllerTest < ActionController::TestCase
20 class ContextMenusControllerTest < ActionController::TestCase
21 fixtures :projects,
21 fixtures :projects,
22 :trackers,
22 :trackers,
23 :projects_trackers,
23 :projects_trackers,
24 :roles,
24 :roles,
25 :member_roles,
25 :member_roles,
26 :members,
26 :members,
27 :enabled_modules,
27 :enabled_modules,
28 :workflows,
28 :workflows,
29 :journals, :journal_details,
29 :journals, :journal_details,
30 :versions,
30 :versions,
31 :issues, :issue_statuses, :issue_categories,
31 :issues, :issue_statuses, :issue_categories,
32 :users,
32 :users,
33 :enumerations,
33 :enumerations,
34 :time_entries
34 :time_entries
35
35
36 def test_context_menu_one_issue
36 def test_context_menu_one_issue
37 @request.session[:user_id] = 2
37 @request.session[:user_id] = 2
38 get :issues, :ids => [1]
38 get :issues, :ids => [1]
39 assert_response :success
39 assert_response :success
40 assert_template 'context_menus/issues'
40 assert_template 'context_menus/issues'
41
41
42 assert_select 'a.icon-edit[href=?]', '/issues/1/edit', :text => 'Edit'
42 assert_select 'a.icon-edit[href=?]', '/issues/1/edit', :text => 'Edit'
43 assert_select 'a.icon-copy[href=?]', '/projects/ecookbook/issues/1/copy', :text => 'Copy'
43 assert_select 'a.icon-copy[href=?]', '/projects/ecookbook/issues/1/copy', :text => 'Copy'
44 assert_select 'a.icon-del[href=?]', '/issues?ids%5B%5D=1', :text => 'Delete'
44 assert_select 'a.icon-del[href=?]', '/issues?ids%5B%5D=1', :text => 'Delete'
45
45
46 # Statuses
46 # Statuses
47 assert_select 'a[href=?]', '/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bstatus_id%5D=5', :text => 'Closed'
47 assert_select 'a[href=?]', '/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bstatus_id%5D=5', :text => 'Closed'
48 assert_select 'a[href=?]', '/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bpriority_id%5D=8', :text => 'Immediate'
48 assert_select 'a[href=?]', '/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bpriority_id%5D=8', :text => 'Immediate'
49 # No inactive priorities
49 # No inactive priorities
50 assert_select 'a', :text => /Inactive Priority/, :count => 0
50 assert_select 'a', :text => /Inactive Priority/, :count => 0
51 # Versions
51 # Versions
52 assert_select 'a[href=?]', '/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bfixed_version_id%5D=3', :text => '2.0'
52 assert_select 'a[href=?]', '/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bfixed_version_id%5D=3', :text => '2.0'
53 assert_select 'a[href=?]', '/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bfixed_version_id%5D=4', :text => 'eCookbook Subproject 1 - 2.0'
53 assert_select 'a[href=?]', '/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bfixed_version_id%5D=4', :text => 'eCookbook Subproject 1 - 2.0'
54 # Assignees
54 # Assignees
55 assert_select 'a[href=?]', '/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bassigned_to_id%5D=3', :text => 'Dave Lopper'
55 assert_select 'a[href=?]', '/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bassigned_to_id%5D=3', :text => 'Dave Lopper'
56 end
56 end
57
57
58 def test_context_menu_one_issue_by_anonymous
58 def test_context_menu_one_issue_by_anonymous
59 get :issues, :ids => [1]
59 get :issues, :ids => [1]
60 assert_response :success
60 assert_response :success
61 assert_template 'context_menus/issues'
61 assert_template 'context_menus/issues'
62 assert_tag :tag => 'a', :content => 'Delete',
62 assert_tag :tag => 'a', :content => 'Delete',
63 :attributes => { :href => '#',
63 :attributes => { :href => '#',
64 :class => 'icon-del disabled' }
64 :class => 'icon-del disabled' }
65 end
65 end
66
66
67 def test_context_menu_multiple_issues_of_same_project
67 def test_context_menu_multiple_issues_of_same_project
68 @request.session[:user_id] = 2
68 @request.session[:user_id] = 2
69 get :issues, :ids => [1, 2]
69 get :issues, :ids => [1, 2]
70 assert_response :success
70 assert_response :success
71 assert_template 'context_menus/issues'
71 assert_template 'context_menus/issues'
72 assert_not_nil assigns(:issues)
72 assert_not_nil assigns(:issues)
73 assert_equal [1, 2], assigns(:issues).map(&:id).sort
73 assert_equal [1, 2], assigns(:issues).map(&:id).sort
74
74
75 ids = assigns(:issues).map(&:id).sort.map {|i| "ids%5B%5D=#{i}"}.join('&amp;')
75 ids = assigns(:issues).map(&:id).sort.map {|i| "ids%5B%5D=#{i}"}.join('&amp;')
76
76
77 assert_select 'a.icon-edit[href=?]', "/issues/bulk_edit?#{ids}", :text => 'Edit'
77 assert_select 'a.icon-edit[href=?]', "/issues/bulk_edit?#{ids}", :text => 'Edit'
78 assert_select 'a.icon-copy[href=?]', "/issues/bulk_edit?copy=1&amp;#{ids}", :text => 'Copy'
78 assert_select 'a.icon-copy[href=?]', "/issues/bulk_edit?copy=1&amp;#{ids}", :text => 'Copy'
79 assert_select 'a.icon-del[href=?]', "/issues?#{ids}", :text => 'Delete'
79 assert_select 'a.icon-del[href=?]', "/issues?#{ids}", :text => 'Delete'
80
80
81 assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&amp;issue%5Bstatus_id%5D=5", :text => 'Closed'
81 assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&amp;issue%5Bstatus_id%5D=5", :text => 'Closed'
82 assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&amp;issue%5Bpriority_id%5D=8", :text => 'Immediate'
82 assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&amp;issue%5Bpriority_id%5D=8", :text => 'Immediate'
83 assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&amp;issue%5Bassigned_to_id%5D=3", :text => 'Dave Lopper'
83 assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&amp;issue%5Bassigned_to_id%5D=3", :text => 'Dave Lopper'
84 end
84 end
85
85
86 def test_context_menu_multiple_issues_of_different_projects
86 def test_context_menu_multiple_issues_of_different_projects
87 @request.session[:user_id] = 2
87 @request.session[:user_id] = 2
88 get :issues, :ids => [1, 2, 6]
88 get :issues, :ids => [1, 2, 6]
89 assert_response :success
89 assert_response :success
90 assert_template 'context_menus/issues'
90 assert_template 'context_menus/issues'
91 assert_not_nil assigns(:issues)
91 assert_not_nil assigns(:issues)
92 assert_equal [1, 2, 6], assigns(:issues).map(&:id).sort
92 assert_equal [1, 2, 6], assigns(:issues).map(&:id).sort
93
93
94 ids = assigns(:issues).map(&:id).sort.map {|i| "ids%5B%5D=#{i}"}.join('&amp;')
94 ids = assigns(:issues).map(&:id).sort.map {|i| "ids%5B%5D=#{i}"}.join('&amp;')
95
95
96 assert_select 'a.icon-edit[href=?]', "/issues/bulk_edit?#{ids}", :text => 'Edit'
96 assert_select 'a.icon-edit[href=?]', "/issues/bulk_edit?#{ids}", :text => 'Edit'
97 assert_select 'a.icon-del[href=?]', "/issues?#{ids}", :text => 'Delete'
97 assert_select 'a.icon-del[href=?]', "/issues?#{ids}", :text => 'Delete'
98
98
99 assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&amp;issue%5Bstatus_id%5D=5", :text => 'Closed'
99 assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&amp;issue%5Bstatus_id%5D=5", :text => 'Closed'
100 assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&amp;issue%5Bpriority_id%5D=8", :text => 'Immediate'
100 assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&amp;issue%5Bpriority_id%5D=8", :text => 'Immediate'
101 assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&amp;issue%5Bassigned_to_id%5D=2", :text => 'John Smith'
101 assert_select 'a[href=?]', "/issues/bulk_update?#{ids}&amp;issue%5Bassigned_to_id%5D=2", :text => 'John Smith'
102 end
102 end
103
103
104 def test_context_menu_should_include_list_custom_fields
104 def test_context_menu_should_include_list_custom_fields
105 field = IssueCustomField.create!(:name => 'List', :field_format => 'list',
105 field = IssueCustomField.create!(:name => 'List', :field_format => 'list',
106 :possible_values => ['Foo', 'Bar'], :is_for_all => true, :tracker_ids => [1, 2, 3])
106 :possible_values => ['Foo', 'Bar'], :is_for_all => true, :tracker_ids => [1, 2, 3])
107 @request.session[:user_id] = 2
107 @request.session[:user_id] = 2
108 get :issues, :ids => [1]
108 get :issues, :ids => [1]
109
109
110 assert_select "li.cf_#{field.id}" do
110 assert_select "li.cf_#{field.id}" do
111 assert_select 'a[href=#]', :text => 'List'
111 assert_select 'a[href=#]', :text => 'List'
112 assert_select 'ul' do
112 assert_select 'ul' do
113 assert_select 'a', 3
113 assert_select 'a', 3
114 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=Foo", :text => 'Foo'
114 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=Foo", :text => 'Foo'
115 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
115 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
116 end
116 end
117 end
117 end
118 end
118 end
119
119
120 def test_context_menu_should_not_include_null_value_for_required_custom_fields
120 def test_context_menu_should_not_include_null_value_for_required_custom_fields
121 field = IssueCustomField.create!(:name => 'List', :is_required => true, :field_format => 'list',
121 field = IssueCustomField.create!(:name => 'List', :is_required => true, :field_format => 'list',
122 :possible_values => ['Foo', 'Bar'], :is_for_all => true, :tracker_ids => [1, 2, 3])
122 :possible_values => ['Foo', 'Bar'], :is_for_all => true, :tracker_ids => [1, 2, 3])
123 @request.session[:user_id] = 2
123 @request.session[:user_id] = 2
124 get :issues, :ids => [1, 2]
124 get :issues, :ids => [1, 2]
125
125
126 assert_select "li.cf_#{field.id}" do
126 assert_select "li.cf_#{field.id}" do
127 assert_select 'a[href=#]', :text => 'List'
127 assert_select 'a[href=#]', :text => 'List'
128 assert_select 'ul' do
128 assert_select 'ul' do
129 assert_select 'a', 2
129 assert_select 'a', 2
130 assert_select 'a', :text => 'none', :count => 0
130 assert_select 'a', :text => 'none', :count => 0
131 end
131 end
132 end
132 end
133 end
133 end
134
134
135 def test_context_menu_on_single_issue_should_select_current_custom_field_value
135 def test_context_menu_on_single_issue_should_select_current_custom_field_value
136 field = IssueCustomField.create!(:name => 'List', :field_format => 'list',
136 field = IssueCustomField.create!(:name => 'List', :field_format => 'list',
137 :possible_values => ['Foo', 'Bar'], :is_for_all => true, :tracker_ids => [1, 2, 3])
137 :possible_values => ['Foo', 'Bar'], :is_for_all => true, :tracker_ids => [1, 2, 3])
138 issue = Issue.find(1)
138 issue = Issue.find(1)
139 issue.custom_field_values = {field.id => 'Bar'}
139 issue.custom_field_values = {field.id => 'Bar'}
140 issue.save!
140 issue.save!
141 @request.session[:user_id] = 2
141 @request.session[:user_id] = 2
142 get :issues, :ids => [1]
142 get :issues, :ids => [1]
143
143
144 assert_select "li.cf_#{field.id}" do
144 assert_select "li.cf_#{field.id}" do
145 assert_select 'a[href=#]', :text => 'List'
145 assert_select 'a[href=#]', :text => 'List'
146 assert_select 'ul' do
146 assert_select 'ul' do
147 assert_select 'a', 3
147 assert_select 'a', 3
148 assert_select 'a.icon-checked', :text => 'Bar'
148 assert_select 'a.icon-checked', :text => 'Bar'
149 end
149 end
150 end
150 end
151 end
151 end
152
152
153 def test_context_menu_should_include_bool_custom_fields
153 def test_context_menu_should_include_bool_custom_fields
154 field = IssueCustomField.create!(:name => 'Bool', :field_format => 'bool',
154 field = IssueCustomField.create!(:name => 'Bool', :field_format => 'bool',
155 :is_for_all => true, :tracker_ids => [1, 2, 3])
155 :is_for_all => true, :tracker_ids => [1, 2, 3])
156 @request.session[:user_id] = 2
156 @request.session[:user_id] = 2
157 get :issues, :ids => [1]
157 get :issues, :ids => [1]
158
158
159 assert_select "li.cf_#{field.id}" do
159 assert_select "li.cf_#{field.id}" do
160 assert_select 'a[href=#]', :text => 'Bool'
160 assert_select 'a[href=#]', :text => 'Bool'
161 assert_select 'ul' do
161 assert_select 'ul' do
162 assert_select 'a', 3
162 assert_select 'a', 3
163 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=0", :text => 'No'
163 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=0", :text => 'No'
164 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=1", :text => 'Yes'
164 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=1", :text => 'Yes'
165 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
165 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
166 end
166 end
167 end
167 end
168 end
168 end
169
169
170 def test_context_menu_should_include_user_custom_fields
170 def test_context_menu_should_include_user_custom_fields
171 field = IssueCustomField.create!(:name => 'User', :field_format => 'user',
171 field = IssueCustomField.create!(:name => 'User', :field_format => 'user',
172 :is_for_all => true, :tracker_ids => [1, 2, 3])
172 :is_for_all => true, :tracker_ids => [1, 2, 3])
173 @request.session[:user_id] = 2
173 @request.session[:user_id] = 2
174 get :issues, :ids => [1]
174 get :issues, :ids => [1]
175
175
176 assert_select "li.cf_#{field.id}" do
176 assert_select "li.cf_#{field.id}" do
177 assert_select 'a[href=#]', :text => 'User'
177 assert_select 'a[href=#]', :text => 'User'
178 assert_select 'ul' do
178 assert_select 'ul' do
179 assert_select 'a', Project.find(1).members.count + 1
179 assert_select 'a', Project.find(1).members.count + 1
180 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=2", :text => 'John Smith'
180 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=2", :text => 'John Smith'
181 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
181 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
182 end
182 end
183 end
183 end
184 end
184 end
185
185
186 def test_context_menu_should_include_version_custom_fields
186 def test_context_menu_should_include_version_custom_fields
187 field = IssueCustomField.create!(:name => 'Version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1, 2, 3])
187 field = IssueCustomField.create!(:name => 'Version', :field_format => 'version', :is_for_all => true, :tracker_ids => [1, 2, 3])
188 @request.session[:user_id] = 2
188 @request.session[:user_id] = 2
189 get :issues, :ids => [1]
189 get :issues, :ids => [1]
190
190
191 assert_select "li.cf_#{field.id}" do
191 assert_select "li.cf_#{field.id}" do
192 assert_select 'a[href=#]', :text => 'Version'
192 assert_select 'a[href=#]', :text => 'Version'
193 assert_select 'ul' do
193 assert_select 'ul' do
194 assert_select 'a', Project.find(1).shared_versions.count + 1
194 assert_select 'a', Project.find(1).shared_versions.count + 1
195 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=3", :text => '2.0'
195 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=3", :text => '2.0'
196 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
196 assert_select 'a[href=?]', "/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
197 end
197 end
198 end
198 end
199 end
199 end
200
200
201 def test_context_menu_should_show_enabled_custom_fields_for_the_role_only
201 def test_context_menu_should_show_enabled_custom_fields_for_the_role_only
202 enabled_cf = IssueCustomField.generate!(:field_format => 'bool', :is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [1,2])
202 enabled_cf = IssueCustomField.generate!(:field_format => 'bool', :is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [1,2])
203 disabled_cf = IssueCustomField.generate!(:field_format => 'bool', :is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [2])
203 disabled_cf = IssueCustomField.generate!(:field_format => 'bool', :is_for_all => true, :tracker_ids => [1], :visible => false, :role_ids => [2])
204 issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
204 issue = Issue.generate!(:project_id => 1, :tracker_id => 1)
205
205
206 @request.session[:user_id] = 2
206 @request.session[:user_id] = 2
207 get :issues, :ids => [issue.id]
207 get :issues, :ids => [issue.id]
208
208
209 assert_select "li.cf_#{enabled_cf.id}"
209 assert_select "li.cf_#{enabled_cf.id}"
210 assert_select "li.cf_#{disabled_cf.id}", 0
210 assert_select "li.cf_#{disabled_cf.id}", 0
211 end
211 end
212
212
213 def test_context_menu_by_assignable_user_should_include_assigned_to_me_link
213 def test_context_menu_by_assignable_user_should_include_assigned_to_me_link
214 @request.session[:user_id] = 2
214 @request.session[:user_id] = 2
215 get :issues, :ids => [1]
215 get :issues, :ids => [1]
216 assert_response :success
216 assert_response :success
217 assert_template 'context_menus/issues'
217 assert_template 'context_menus/issues'
218
218
219 assert_select 'a[href=?]', '/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bassigned_to_id%5D=2', :text => / me /
219 assert_select 'a[href=?]', '/issues/bulk_update?ids%5B%5D=1&amp;issue%5Bassigned_to_id%5D=2', :text => / me /
220 end
220 end
221
221
222 def test_context_menu_should_propose_shared_versions_for_issues_from_different_projects
222 def test_context_menu_should_propose_shared_versions_for_issues_from_different_projects
223 @request.session[:user_id] = 2
223 @request.session[:user_id] = 2
224 version = Version.create!(:name => 'Shared', :sharing => 'system', :project_id => 1)
224 version = Version.create!(:name => 'Shared', :sharing => 'system', :project_id => 1)
225
225
226 get :issues, :ids => [1, 4]
226 get :issues, :ids => [1, 4]
227 assert_response :success
227 assert_response :success
228 assert_template 'context_menus/issues'
228 assert_template 'context_menus/issues'
229
229
230 assert_include version, assigns(:versions)
230 assert_include version, assigns(:versions)
231 assert_select 'a', :text => 'eCookbook - Shared'
231 assert_select 'a', :text => 'eCookbook - Shared'
232 end
232 end
233
233
234 def test_context_menu_with_issue_that_is_not_visible_should_fail
234 def test_context_menu_with_issue_that_is_not_visible_should_fail
235 get :issues, :ids => [1, 4] # issue 4 is not visible
235 get :issues, :ids => [1, 4] # issue 4 is not visible
236 assert_response 302
236 assert_response 302
237 end
237 end
238
238
239 def test_should_respond_with_404_without_ids
239 def test_should_respond_with_404_without_ids
240 get :issues
240 get :issues
241 assert_response 404
241 assert_response 404
242 end
242 end
243
243
244 def test_time_entries_context_menu
244 def test_time_entries_context_menu
245 @request.session[:user_id] = 2
245 @request.session[:user_id] = 2
246 get :time_entries, :ids => [1, 2]
246 get :time_entries, :ids => [1, 2]
247 assert_response :success
247 assert_response :success
248 assert_template 'context_menus/time_entries'
248 assert_template 'context_menus/time_entries'
249
249
250 assert_select 'a:not(.disabled)', :text => 'Edit'
250 assert_select 'a:not(.disabled)', :text => 'Edit'
251 end
251 end
252
252
253 def test_time_entries_context_menu_should_include_custom_fields
253 def test_time_entries_context_menu_should_include_custom_fields
254 field = TimeEntryCustomField.generate!(:name => "Field", :field_format => "list", :possible_values => ["foo", "bar"])
254 field = TimeEntryCustomField.generate!(:name => "Field", :field_format => "list", :possible_values => ["foo", "bar"])
255
255
256 @request.session[:user_id] = 2
256 @request.session[:user_id] = 2
257 get :time_entries, :ids => [1, 2]
257 get :time_entries, :ids => [1, 2]
258 assert_response :success
258 assert_response :success
259 assert_select "li.cf_#{field.id}" do
259 assert_select "li.cf_#{field.id}" do
260 assert_select 'a[href=#]', :text => "Field"
260 assert_select 'a[href=#]', :text => "Field"
261 assert_select 'ul' do
261 assert_select 'ul' do
262 assert_select 'a', 3
262 assert_select 'a', 3
263 assert_select 'a[href=?]', "/time_entries/bulk_update?ids%5B%5D=1&amp;ids%5B%5D=2&amp;time_entry%5Bcustom_field_values%5D%5B#{field.id}%5D=foo", :text => 'foo'
263 assert_select 'a[href=?]', "/time_entries/bulk_update?ids%5B%5D=1&amp;ids%5B%5D=2&amp;time_entry%5Bcustom_field_values%5D%5B#{field.id}%5D=foo", :text => 'foo'
264 assert_select 'a[href=?]', "/time_entries/bulk_update?ids%5B%5D=1&amp;ids%5B%5D=2&amp;time_entry%5Bcustom_field_values%5D%5B#{field.id}%5D=bar", :text => 'bar'
264 assert_select 'a[href=?]', "/time_entries/bulk_update?ids%5B%5D=1&amp;ids%5B%5D=2&amp;time_entry%5Bcustom_field_values%5D%5B#{field.id}%5D=bar", :text => 'bar'
265 assert_select 'a[href=?]', "/time_entries/bulk_update?ids%5B%5D=1&amp;ids%5B%5D=2&amp;time_entry%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
265 assert_select 'a[href=?]', "/time_entries/bulk_update?ids%5B%5D=1&amp;ids%5B%5D=2&amp;time_entry%5Bcustom_field_values%5D%5B#{field.id}%5D=__none__", :text => 'none'
266 end
266 end
267 end
267 end
268 end
268 end
269
269
270 def test_time_entries_context_menu_with_edit_own_time_entries_permission
271 @request.session[:user_id] = 2
272 Role.find_by_name('Manager').remove_permission! :edit_time_entries
273 Role.find_by_name('Manager').add_permission! :edit_own_time_entries
274 ids = (0..1).map {TimeEntry.generate!(:user => User.find(2)).id}
275
276 get :time_entries, :ids => ids
277 assert_response :success
278 assert_template 'context_menus/time_entries'
279 assert_select 'a:not(.disabled)', :text => 'Edit'
280 end
281
270 def test_time_entries_context_menu_without_edit_permission
282 def test_time_entries_context_menu_without_edit_permission
271 @request.session[:user_id] = 2
283 @request.session[:user_id] = 2
272 Role.find_by_name('Manager').remove_permission! :edit_time_entries
284 Role.find_by_name('Manager').remove_permission! :edit_time_entries
273
285
274 get :time_entries, :ids => [1, 2]
286 get :time_entries, :ids => [1, 2]
275 assert_response :success
287 assert_response :success
276 assert_template 'context_menus/time_entries'
288 assert_template 'context_menus/time_entries'
277 assert_select 'a.disabled', :text => 'Edit'
289 assert_select 'a.disabled', :text => 'Edit'
278 end
290 end
279 end
291 end
@@ -1,727 +1,756
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_no_tag 'option', :content => 'Inactive Activity'
95 assert_no_tag 'option', :content => 'Inactive Activity'
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 # Default activity selected
103 # Default activity selected
104 assert_tag :tag => 'form', :attributes => { :action => '/projects/ecookbook/time_entries/2' }
104 assert_tag :tag => 'form', :attributes => { :action => '/projects/ecookbook/time_entries/2' }
105 end
105 end
106
106
107 def test_get_edit_with_an_existing_time_entry_with_inactive_activity
107 def test_get_edit_with_an_existing_time_entry_with_inactive_activity
108 te = TimeEntry.find(1)
108 te = TimeEntry.find(1)
109 te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
109 te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
110 te.save!
110 te.save!
111
111
112 @request.session[:user_id] = 1
112 @request.session[:user_id] = 1
113 get :edit, :project_id => 1, :id => 1
113 get :edit, :project_id => 1, :id => 1
114 assert_response :success
114 assert_response :success
115 assert_template 'edit'
115 assert_template 'edit'
116 # Blank option since nothing is pre-selected
116 # Blank option since nothing is pre-selected
117 assert_tag :tag => 'option', :content => '--- Please select ---'
117 assert_tag :tag => 'option', :content => '--- Please select ---'
118 end
118 end
119
119
120 def test_post_create
120 def test_post_create
121 @request.session[:user_id] = 3
121 @request.session[:user_id] = 3
122 assert_difference 'TimeEntry.count' do
122 assert_difference 'TimeEntry.count' do
123 post :create, :project_id => 1,
123 post :create, :project_id => 1,
124 :time_entry => {:comments => 'Some work on TimelogControllerTest',
124 :time_entry => {:comments => 'Some work on TimelogControllerTest',
125 # Not the default activity
125 # Not the default activity
126 :activity_id => '11',
126 :activity_id => '11',
127 :spent_on => '2008-03-14',
127 :spent_on => '2008-03-14',
128 :issue_id => '1',
128 :issue_id => '1',
129 :hours => '7.3'}
129 :hours => '7.3'}
130 assert_redirected_to '/projects/ecookbook/time_entries'
130 assert_redirected_to '/projects/ecookbook/time_entries'
131 end
131 end
132
132
133 t = TimeEntry.order('id DESC').first
133 t = TimeEntry.order('id DESC').first
134 assert_not_nil t
134 assert_not_nil t
135 assert_equal 'Some work on TimelogControllerTest', t.comments
135 assert_equal 'Some work on TimelogControllerTest', t.comments
136 assert_equal 1, t.project_id
136 assert_equal 1, t.project_id
137 assert_equal 1, t.issue_id
137 assert_equal 1, t.issue_id
138 assert_equal 11, t.activity_id
138 assert_equal 11, t.activity_id
139 assert_equal 7.3, t.hours
139 assert_equal 7.3, t.hours
140 assert_equal 3, t.user_id
140 assert_equal 3, t.user_id
141 end
141 end
142
142
143 def test_post_create_with_blank_issue
143 def test_post_create_with_blank_issue
144 @request.session[:user_id] = 3
144 @request.session[:user_id] = 3
145 assert_difference 'TimeEntry.count' do
145 assert_difference 'TimeEntry.count' do
146 post :create, :project_id => 1,
146 post :create, :project_id => 1,
147 :time_entry => {:comments => 'Some work on TimelogControllerTest',
147 :time_entry => {:comments => 'Some work on TimelogControllerTest',
148 # Not the default activity
148 # Not the default activity
149 :activity_id => '11',
149 :activity_id => '11',
150 :issue_id => '',
150 :issue_id => '',
151 :spent_on => '2008-03-14',
151 :spent_on => '2008-03-14',
152 :hours => '7.3'}
152 :hours => '7.3'}
153 assert_redirected_to '/projects/ecookbook/time_entries'
153 assert_redirected_to '/projects/ecookbook/time_entries'
154 end
154 end
155
155
156 t = TimeEntry.order('id DESC').first
156 t = TimeEntry.order('id DESC').first
157 assert_not_nil t
157 assert_not_nil t
158 assert_equal 'Some work on TimelogControllerTest', t.comments
158 assert_equal 'Some work on TimelogControllerTest', t.comments
159 assert_equal 1, t.project_id
159 assert_equal 1, t.project_id
160 assert_nil t.issue_id
160 assert_nil t.issue_id
161 assert_equal 11, t.activity_id
161 assert_equal 11, t.activity_id
162 assert_equal 7.3, t.hours
162 assert_equal 7.3, t.hours
163 assert_equal 3, t.user_id
163 assert_equal 3, t.user_id
164 end
164 end
165
165
166 def test_create_and_continue_at_project_level
166 def test_create_and_continue_at_project_level
167 @request.session[:user_id] = 2
167 @request.session[:user_id] = 2
168 assert_difference 'TimeEntry.count' do
168 assert_difference 'TimeEntry.count' do
169 post :create, :time_entry => {:project_id => '1',
169 post :create, :time_entry => {:project_id => '1',
170 :activity_id => '11',
170 :activity_id => '11',
171 :issue_id => '',
171 :issue_id => '',
172 :spent_on => '2008-03-14',
172 :spent_on => '2008-03-14',
173 :hours => '7.3'},
173 :hours => '7.3'},
174 :continue => '1'
174 :continue => '1'
175 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D=1'
175 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D=1'
176 end
176 end
177 end
177 end
178
178
179 def test_create_and_continue_at_issue_level
179 def test_create_and_continue_at_issue_level
180 @request.session[:user_id] = 2
180 @request.session[:user_id] = 2
181 assert_difference 'TimeEntry.count' do
181 assert_difference 'TimeEntry.count' do
182 post :create, :time_entry => {:project_id => '',
182 post :create, :time_entry => {:project_id => '',
183 :activity_id => '11',
183 :activity_id => '11',
184 :issue_id => '1',
184 :issue_id => '1',
185 :spent_on => '2008-03-14',
185 :spent_on => '2008-03-14',
186 :hours => '7.3'},
186 :hours => '7.3'},
187 :continue => '1'
187 :continue => '1'
188 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=1&time_entry%5Bproject_id%5D='
188 assert_redirected_to '/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=1&time_entry%5Bproject_id%5D='
189 end
189 end
190 end
190 end
191
191
192 def test_create_and_continue_with_project_id
192 def test_create_and_continue_with_project_id
193 @request.session[:user_id] = 2
193 @request.session[:user_id] = 2
194 assert_difference 'TimeEntry.count' do
194 assert_difference 'TimeEntry.count' do
195 post :create, :project_id => 1,
195 post :create, :project_id => 1,
196 :time_entry => {:activity_id => '11',
196 :time_entry => {:activity_id => '11',
197 :issue_id => '',
197 :issue_id => '',
198 :spent_on => '2008-03-14',
198 :spent_on => '2008-03-14',
199 :hours => '7.3'},
199 :hours => '7.3'},
200 :continue => '1'
200 :continue => '1'
201 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 assert_redirected_to '/projects/ecookbook/time_entries/new?time_entry%5Bactivity_id%5D=11&time_entry%5Bissue_id%5D=&time_entry%5Bproject_id%5D='
202 end
202 end
203 end
203 end
204
204
205 def test_create_and_continue_with_issue_id
205 def test_create_and_continue_with_issue_id
206 @request.session[:user_id] = 2
206 @request.session[:user_id] = 2
207 assert_difference 'TimeEntry.count' do
207 assert_difference 'TimeEntry.count' do
208 post :create, :issue_id => 1,
208 post :create, :issue_id => 1,
209 :time_entry => {:activity_id => '11',
209 :time_entry => {:activity_id => '11',
210 :issue_id => '1',
210 :issue_id => '1',
211 :spent_on => '2008-03-14',
211 :spent_on => '2008-03-14',
212 :hours => '7.3'},
212 :hours => '7.3'},
213 :continue => '1'
213 :continue => '1'
214 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 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='
215 end
215 end
216 end
216 end
217
217
218 def test_create_without_log_time_permission_should_be_denied
218 def test_create_without_log_time_permission_should_be_denied
219 @request.session[:user_id] = 2
219 @request.session[:user_id] = 2
220 Role.find_by_name('Manager').remove_permission! :log_time
220 Role.find_by_name('Manager').remove_permission! :log_time
221 post :create, :project_id => 1,
221 post :create, :project_id => 1,
222 :time_entry => {:activity_id => '11',
222 :time_entry => {:activity_id => '11',
223 :issue_id => '',
223 :issue_id => '',
224 :spent_on => '2008-03-14',
224 :spent_on => '2008-03-14',
225 :hours => '7.3'}
225 :hours => '7.3'}
226
226
227 assert_response 403
227 assert_response 403
228 end
228 end
229
229
230 def test_create_without_project_and_issue_should_fail
230 def test_create_without_project_and_issue_should_fail
231 @request.session[:user_id] = 2
231 @request.session[:user_id] = 2
232 post :create, :time_entry => {:issue_id => ''}
232 post :create, :time_entry => {:issue_id => ''}
233
233
234 assert_response :success
234 assert_response :success
235 assert_template 'new'
235 assert_template 'new'
236 end
236 end
237
237
238 def test_create_with_failure
238 def test_create_with_failure
239 @request.session[:user_id] = 2
239 @request.session[:user_id] = 2
240 post :create, :project_id => 1,
240 post :create, :project_id => 1,
241 :time_entry => {:activity_id => '',
241 :time_entry => {:activity_id => '',
242 :issue_id => '',
242 :issue_id => '',
243 :spent_on => '2008-03-14',
243 :spent_on => '2008-03-14',
244 :hours => '7.3'}
244 :hours => '7.3'}
245
245
246 assert_response :success
246 assert_response :success
247 assert_template 'new'
247 assert_template 'new'
248 end
248 end
249
249
250 def test_create_without_project
250 def test_create_without_project
251 @request.session[:user_id] = 2
251 @request.session[:user_id] = 2
252 assert_difference 'TimeEntry.count' do
252 assert_difference 'TimeEntry.count' do
253 post :create, :time_entry => {:project_id => '1',
253 post :create, :time_entry => {:project_id => '1',
254 :activity_id => '11',
254 :activity_id => '11',
255 :issue_id => '',
255 :issue_id => '',
256 :spent_on => '2008-03-14',
256 :spent_on => '2008-03-14',
257 :hours => '7.3'}
257 :hours => '7.3'}
258 end
258 end
259
259
260 assert_redirected_to '/projects/ecookbook/time_entries'
260 assert_redirected_to '/projects/ecookbook/time_entries'
261 time_entry = TimeEntry.order('id DESC').first
261 time_entry = TimeEntry.order('id DESC').first
262 assert_equal 1, time_entry.project_id
262 assert_equal 1, time_entry.project_id
263 end
263 end
264
264
265 def test_create_without_project_should_fail_with_issue_not_inside_project
265 def test_create_without_project_should_fail_with_issue_not_inside_project
266 @request.session[:user_id] = 2
266 @request.session[:user_id] = 2
267 assert_no_difference 'TimeEntry.count' do
267 assert_no_difference 'TimeEntry.count' do
268 post :create, :time_entry => {:project_id => '1',
268 post :create, :time_entry => {:project_id => '1',
269 :activity_id => '11',
269 :activity_id => '11',
270 :issue_id => '5',
270 :issue_id => '5',
271 :spent_on => '2008-03-14',
271 :spent_on => '2008-03-14',
272 :hours => '7.3'}
272 :hours => '7.3'}
273 end
273 end
274
274
275 assert_response :success
275 assert_response :success
276 assert assigns(:time_entry).errors[:issue_id].present?
276 assert assigns(:time_entry).errors[:issue_id].present?
277 end
277 end
278
278
279 def test_create_without_project_should_deny_without_permission
279 def test_create_without_project_should_deny_without_permission
280 @request.session[:user_id] = 2
280 @request.session[:user_id] = 2
281 Project.find(3).disable_module!(:time_tracking)
281 Project.find(3).disable_module!(:time_tracking)
282
282
283 assert_no_difference 'TimeEntry.count' do
283 assert_no_difference 'TimeEntry.count' do
284 post :create, :time_entry => {:project_id => '3',
284 post :create, :time_entry => {:project_id => '3',
285 :activity_id => '11',
285 :activity_id => '11',
286 :issue_id => '',
286 :issue_id => '',
287 :spent_on => '2008-03-14',
287 :spent_on => '2008-03-14',
288 :hours => '7.3'}
288 :hours => '7.3'}
289 end
289 end
290
290
291 assert_response 403
291 assert_response 403
292 end
292 end
293
293
294 def test_create_without_project_with_failure
294 def test_create_without_project_with_failure
295 @request.session[:user_id] = 2
295 @request.session[:user_id] = 2
296 assert_no_difference 'TimeEntry.count' do
296 assert_no_difference 'TimeEntry.count' do
297 post :create, :time_entry => {:project_id => '1',
297 post :create, :time_entry => {:project_id => '1',
298 :activity_id => '11',
298 :activity_id => '11',
299 :issue_id => '',
299 :issue_id => '',
300 :spent_on => '2008-03-14',
300 :spent_on => '2008-03-14',
301 :hours => ''}
301 :hours => ''}
302 end
302 end
303
303
304 assert_response :success
304 assert_response :success
305 assert_tag 'select', :attributes => {:name => 'time_entry[project_id]'},
305 assert_tag 'select', :attributes => {:name => 'time_entry[project_id]'},
306 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}}
306 :child => {:tag => 'option', :attributes => {:value => '1', :selected => 'selected'}}
307 end
307 end
308
308
309 def test_update
309 def test_update
310 entry = TimeEntry.find(1)
310 entry = TimeEntry.find(1)
311 assert_equal 1, entry.issue_id
311 assert_equal 1, entry.issue_id
312 assert_equal 2, entry.user_id
312 assert_equal 2, entry.user_id
313
313
314 @request.session[:user_id] = 1
314 @request.session[:user_id] = 1
315 put :update, :id => 1,
315 put :update, :id => 1,
316 :time_entry => {:issue_id => '2',
316 :time_entry => {:issue_id => '2',
317 :hours => '8'}
317 :hours => '8'}
318 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
318 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
319 entry.reload
319 entry.reload
320
320
321 assert_equal 8, entry.hours
321 assert_equal 8, entry.hours
322 assert_equal 2, entry.issue_id
322 assert_equal 2, entry.issue_id
323 assert_equal 2, entry.user_id
323 assert_equal 2, entry.user_id
324 end
324 end
325
325
326 def test_update_should_allow_to_change_issue_to_another_project
326 def test_update_should_allow_to_change_issue_to_another_project
327 entry = TimeEntry.generate!(:issue_id => 1)
327 entry = TimeEntry.generate!(:issue_id => 1)
328
328
329 @request.session[:user_id] = 1
329 @request.session[:user_id] = 1
330 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
330 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
331 assert_response 302
331 assert_response 302
332 entry.reload
332 entry.reload
333
333
334 assert_equal 5, entry.issue_id
334 assert_equal 5, entry.issue_id
335 assert_equal 3, entry.project_id
335 assert_equal 3, entry.project_id
336 end
336 end
337
337
338 def test_update_should_not_allow_to_change_issue_to_an_invalid_project
338 def test_update_should_not_allow_to_change_issue_to_an_invalid_project
339 entry = TimeEntry.generate!(:issue_id => 1)
339 entry = TimeEntry.generate!(:issue_id => 1)
340 Project.find(3).disable_module!(:time_tracking)
340 Project.find(3).disable_module!(:time_tracking)
341
341
342 @request.session[:user_id] = 1
342 @request.session[:user_id] = 1
343 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
343 put :update, :id => entry.id, :time_entry => {:issue_id => '5'}
344 assert_response 200
344 assert_response 200
345 assert_include "Issue is invalid", assigns(:time_entry).errors.full_messages
345 assert_include "Issue is invalid", assigns(:time_entry).errors.full_messages
346 end
346 end
347
347
348 def test_get_bulk_edit
348 def test_get_bulk_edit
349 @request.session[:user_id] = 2
349 @request.session[:user_id] = 2
350 get :bulk_edit, :ids => [1, 2]
350 get :bulk_edit, :ids => [1, 2]
351 assert_response :success
351 assert_response :success
352 assert_template 'bulk_edit'
352 assert_template 'bulk_edit'
353
353
354 assert_select 'ul#bulk-selection' do
354 assert_select 'ul#bulk-selection' do
355 assert_select 'li', 2
355 assert_select 'li', 2
356 assert_select 'li a', :text => '03/23/2007 - eCookbook: 4.25 hours'
356 assert_select 'li a', :text => '03/23/2007 - eCookbook: 4.25 hours'
357 end
357 end
358
358
359 assert_select 'form#bulk_edit_form[action=?]', '/time_entries/bulk_update' do
359 assert_select 'form#bulk_edit_form[action=?]', '/time_entries/bulk_update' do
360 # System wide custom field
360 # System wide custom field
361 assert_select 'select[name=?]', 'time_entry[custom_field_values][10]'
361 assert_select 'select[name=?]', 'time_entry[custom_field_values][10]'
362
362
363 # Activities
363 # Activities
364 assert_select 'select[name=?]', 'time_entry[activity_id]' do
364 assert_select 'select[name=?]', 'time_entry[activity_id]' do
365 assert_select 'option[value=]', :text => '(No change)'
365 assert_select 'option[value=]', :text => '(No change)'
366 assert_select 'option[value=9]', :text => 'Design'
366 assert_select 'option[value=9]', :text => 'Design'
367 end
367 end
368 end
368 end
369 end
369 end
370
370
371 def test_get_bulk_edit_on_different_projects
371 def test_get_bulk_edit_on_different_projects
372 @request.session[:user_id] = 2
372 @request.session[:user_id] = 2
373 get :bulk_edit, :ids => [1, 2, 6]
373 get :bulk_edit, :ids => [1, 2, 6]
374 assert_response :success
374 assert_response :success
375 assert_template 'bulk_edit'
375 assert_template 'bulk_edit'
376 end
376 end
377
377
378 def test_bulk_edit_with_edit_own_time_entries_permission
379 @request.session[:user_id] = 2
380 Role.find_by_name('Manager').remove_permission! :edit_time_entries
381 Role.find_by_name('Manager').add_permission! :edit_own_time_entries
382 ids = (0..1).map {TimeEntry.generate!(:user => User.find(2)).id}
383
384 get :bulk_edit, :ids => ids
385 assert_response :success
386 end
387
378 def test_bulk_update
388 def test_bulk_update
379 @request.session[:user_id] = 2
389 @request.session[:user_id] = 2
380 # update time entry activity
390 # update time entry activity
381 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
391 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
382
392
383 assert_response 302
393 assert_response 302
384 # check that the issues were updated
394 # check that the issues were updated
385 assert_equal [9, 9], TimeEntry.where(:id => [1, 2]).collect {|i| i.activity_id}
395 assert_equal [9, 9], TimeEntry.where(:id => [1, 2]).collect {|i| i.activity_id}
386 end
396 end
387
397
388 def test_bulk_update_with_failure
398 def test_bulk_update_with_failure
389 @request.session[:user_id] = 2
399 @request.session[:user_id] = 2
390 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
400 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
391
401
392 assert_response 302
402 assert_response 302
393 assert_match /Failed to save 2 time entrie/, flash[:error]
403 assert_match /Failed to save 2 time entrie/, flash[:error]
394 end
404 end
395
405
396 def test_bulk_update_on_different_projects
406 def test_bulk_update_on_different_projects
397 @request.session[:user_id] = 2
407 @request.session[:user_id] = 2
398 # makes user a manager on the other project
408 # makes user a manager on the other project
399 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
409 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
400
410
401 # update time entry activity
411 # update time entry activity
402 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
412 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
403
413
404 assert_response 302
414 assert_response 302
405 # check that the issues were updated
415 # check that the issues were updated
406 assert_equal [9, 9, 9], TimeEntry.where(:id => [1, 2, 4]).collect {|i| i.activity_id}
416 assert_equal [9, 9, 9], TimeEntry.where(:id => [1, 2, 4]).collect {|i| i.activity_id}
407 end
417 end
408
418
409 def test_bulk_update_on_different_projects_without_rights
419 def test_bulk_update_on_different_projects_without_rights
410 @request.session[:user_id] = 3
420 @request.session[:user_id] = 3
411 user = User.find(3)
421 user = User.find(3)
412 action = { :controller => "timelog", :action => "bulk_update" }
422 action = { :controller => "timelog", :action => "bulk_update" }
413 assert user.allowed_to?(action, TimeEntry.find(1).project)
423 assert user.allowed_to?(action, TimeEntry.find(1).project)
414 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
424 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
415 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
425 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
416 assert_response 403
426 assert_response 403
417 end
427 end
418
428
429 def test_bulk_update_with_edit_own_time_entries_permission
430 @request.session[:user_id] = 2
431 Role.find_by_name('Manager').remove_permission! :edit_time_entries
432 Role.find_by_name('Manager').add_permission! :edit_own_time_entries
433 ids = (0..1).map {TimeEntry.generate!(:user => User.find(2)).id}
434
435 post :bulk_update, :ids => ids, :time_entry => { :activity_id => 9 }
436 assert_response 302
437 end
438
439 def test_bulk_update_with_edit_own_time_entries_permissions_should_be_denied_for_time_entries_of_other_user
440 @request.session[:user_id] = 2
441 Role.find_by_name('Manager').remove_permission! :edit_time_entries
442 Role.find_by_name('Manager').add_permission! :edit_own_time_entries
443
444 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9 }
445 assert_response 403
446 end
447
419 def test_bulk_update_custom_field
448 def test_bulk_update_custom_field
420 @request.session[:user_id] = 2
449 @request.session[:user_id] = 2
421 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
450 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
422
451
423 assert_response 302
452 assert_response 302
424 assert_equal ["0", "0"], TimeEntry.where(:id => [1, 2]).collect {|i| i.custom_value_for(10).value}
453 assert_equal ["0", "0"], TimeEntry.where(:id => [1, 2]).collect {|i| i.custom_value_for(10).value}
425 end
454 end
426
455
427 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
456 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
428 @request.session[:user_id] = 2
457 @request.session[:user_id] = 2
429 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
458 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
430
459
431 assert_response :redirect
460 assert_response :redirect
432 assert_redirected_to '/time_entries'
461 assert_redirected_to '/time_entries'
433 end
462 end
434
463
435 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
464 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
436 @request.session[:user_id] = 2
465 @request.session[:user_id] = 2
437 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
466 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
438
467
439 assert_response :redirect
468 assert_response :redirect
440 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
469 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
441 end
470 end
442
471
443 def test_post_bulk_update_without_edit_permission_should_be_denied
472 def test_post_bulk_update_without_edit_permission_should_be_denied
444 @request.session[:user_id] = 2
473 @request.session[:user_id] = 2
445 Role.find_by_name('Manager').remove_permission! :edit_time_entries
474 Role.find_by_name('Manager').remove_permission! :edit_time_entries
446 post :bulk_update, :ids => [1,2]
475 post :bulk_update, :ids => [1,2]
447
476
448 assert_response 403
477 assert_response 403
449 end
478 end
450
479
451 def test_destroy
480 def test_destroy
452 @request.session[:user_id] = 2
481 @request.session[:user_id] = 2
453 delete :destroy, :id => 1
482 delete :destroy, :id => 1
454 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
483 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
455 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
484 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
456 assert_nil TimeEntry.find_by_id(1)
485 assert_nil TimeEntry.find_by_id(1)
457 end
486 end
458
487
459 def test_destroy_should_fail
488 def test_destroy_should_fail
460 # simulate that this fails (e.g. due to a plugin), see #5700
489 # simulate that this fails (e.g. due to a plugin), see #5700
461 TimeEntry.any_instance.expects(:destroy).returns(false)
490 TimeEntry.any_instance.expects(:destroy).returns(false)
462
491
463 @request.session[:user_id] = 2
492 @request.session[:user_id] = 2
464 delete :destroy, :id => 1
493 delete :destroy, :id => 1
465 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
494 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
466 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
495 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
467 assert_not_nil TimeEntry.find_by_id(1)
496 assert_not_nil TimeEntry.find_by_id(1)
468 end
497 end
469
498
470 def test_index_all_projects
499 def test_index_all_projects
471 get :index
500 get :index
472 assert_response :success
501 assert_response :success
473 assert_template 'index'
502 assert_template 'index'
474 assert_not_nil assigns(:total_hours)
503 assert_not_nil assigns(:total_hours)
475 assert_equal "162.90", "%.2f" % assigns(:total_hours)
504 assert_equal "162.90", "%.2f" % assigns(:total_hours)
476 assert_tag :form,
505 assert_tag :form,
477 :attributes => {:action => "/time_entries", :id => 'query_form'}
506 :attributes => {:action => "/time_entries", :id => 'query_form'}
478 end
507 end
479
508
480 def test_index_all_projects_should_show_log_time_link
509 def test_index_all_projects_should_show_log_time_link
481 @request.session[:user_id] = 2
510 @request.session[:user_id] = 2
482 get :index
511 get :index
483 assert_response :success
512 assert_response :success
484 assert_template 'index'
513 assert_template 'index'
485 assert_tag 'a', :attributes => {:href => '/time_entries/new'}, :content => /Log time/
514 assert_tag 'a', :attributes => {:href => '/time_entries/new'}, :content => /Log time/
486 end
515 end
487
516
488 def test_index_my_spent_time
517 def test_index_my_spent_time
489 @request.session[:user_id] = 2
518 @request.session[:user_id] = 2
490 get :index, :user_id => 'me'
519 get :index, :user_id => 'me'
491 assert_response :success
520 assert_response :success
492 assert_template 'index'
521 assert_template 'index'
493 assert assigns(:entries).all? {|entry| entry.user_id == 2}
522 assert assigns(:entries).all? {|entry| entry.user_id == 2}
494 end
523 end
495
524
496 def test_index_at_project_level
525 def test_index_at_project_level
497 get :index, :project_id => 'ecookbook'
526 get :index, :project_id => 'ecookbook'
498 assert_response :success
527 assert_response :success
499 assert_template 'index'
528 assert_template 'index'
500 assert_not_nil assigns(:entries)
529 assert_not_nil assigns(:entries)
501 assert_equal 4, assigns(:entries).size
530 assert_equal 4, assigns(:entries).size
502 # project and subproject
531 # project and subproject
503 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
532 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
504 assert_not_nil assigns(:total_hours)
533 assert_not_nil assigns(:total_hours)
505 assert_equal "162.90", "%.2f" % assigns(:total_hours)
534 assert_equal "162.90", "%.2f" % assigns(:total_hours)
506 assert_tag :form,
535 assert_tag :form,
507 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
536 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
508 end
537 end
509
538
510 def test_index_with_display_subprojects_issues_to_false_should_not_include_subproject_entries
539 def test_index_with_display_subprojects_issues_to_false_should_not_include_subproject_entries
511 entry = TimeEntry.generate!(:project => Project.find(3))
540 entry = TimeEntry.generate!(:project => Project.find(3))
512
541
513 with_settings :display_subprojects_issues => '0' do
542 with_settings :display_subprojects_issues => '0' do
514 get :index, :project_id => 'ecookbook'
543 get :index, :project_id => 'ecookbook'
515 assert_response :success
544 assert_response :success
516 assert_template 'index'
545 assert_template 'index'
517 assert_not_include entry, assigns(:entries)
546 assert_not_include entry, assigns(:entries)
518 end
547 end
519 end
548 end
520
549
521 def test_index_with_display_subprojects_issues_to_false_and_subproject_filter_should_include_subproject_entries
550 def test_index_with_display_subprojects_issues_to_false_and_subproject_filter_should_include_subproject_entries
522 entry = TimeEntry.generate!(:project => Project.find(3))
551 entry = TimeEntry.generate!(:project => Project.find(3))
523
552
524 with_settings :display_subprojects_issues => '0' do
553 with_settings :display_subprojects_issues => '0' do
525 get :index, :project_id => 'ecookbook', :subproject_id => 3
554 get :index, :project_id => 'ecookbook', :subproject_id => 3
526 assert_response :success
555 assert_response :success
527 assert_template 'index'
556 assert_template 'index'
528 assert_include entry, assigns(:entries)
557 assert_include entry, assigns(:entries)
529 end
558 end
530 end
559 end
531
560
532 def test_index_at_project_level_with_date_range
561 def test_index_at_project_level_with_date_range
533 get :index, :project_id => 'ecookbook',
562 get :index, :project_id => 'ecookbook',
534 :f => ['spent_on'],
563 :f => ['spent_on'],
535 :op => {'spent_on' => '><'},
564 :op => {'spent_on' => '><'},
536 :v => {'spent_on' => ['2007-03-20', '2007-04-30']}
565 :v => {'spent_on' => ['2007-03-20', '2007-04-30']}
537 assert_response :success
566 assert_response :success
538 assert_template 'index'
567 assert_template 'index'
539 assert_not_nil assigns(:entries)
568 assert_not_nil assigns(:entries)
540 assert_equal 3, assigns(:entries).size
569 assert_equal 3, assigns(:entries).size
541 assert_not_nil assigns(:total_hours)
570 assert_not_nil assigns(:total_hours)
542 assert_equal "12.90", "%.2f" % assigns(:total_hours)
571 assert_equal "12.90", "%.2f" % assigns(:total_hours)
543 assert_tag :form,
572 assert_tag :form,
544 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
573 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
545 end
574 end
546
575
547 def test_index_at_project_level_with_date_range_using_from_and_to_params
576 def test_index_at_project_level_with_date_range_using_from_and_to_params
548 get :index, :project_id => 'ecookbook', :from => '2007-03-20', :to => '2007-04-30'
577 get :index, :project_id => 'ecookbook', :from => '2007-03-20', :to => '2007-04-30'
549 assert_response :success
578 assert_response :success
550 assert_template 'index'
579 assert_template 'index'
551 assert_not_nil assigns(:entries)
580 assert_not_nil assigns(:entries)
552 assert_equal 3, assigns(:entries).size
581 assert_equal 3, assigns(:entries).size
553 assert_not_nil assigns(:total_hours)
582 assert_not_nil assigns(:total_hours)
554 assert_equal "12.90", "%.2f" % assigns(:total_hours)
583 assert_equal "12.90", "%.2f" % assigns(:total_hours)
555 assert_tag :form,
584 assert_tag :form,
556 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
585 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
557 end
586 end
558
587
559 def test_index_at_project_level_with_period
588 def test_index_at_project_level_with_period
560 get :index, :project_id => 'ecookbook',
589 get :index, :project_id => 'ecookbook',
561 :f => ['spent_on'],
590 :f => ['spent_on'],
562 :op => {'spent_on' => '>t-'},
591 :op => {'spent_on' => '>t-'},
563 :v => {'spent_on' => ['7']}
592 :v => {'spent_on' => ['7']}
564 assert_response :success
593 assert_response :success
565 assert_template 'index'
594 assert_template 'index'
566 assert_not_nil assigns(:entries)
595 assert_not_nil assigns(:entries)
567 assert_not_nil assigns(:total_hours)
596 assert_not_nil assigns(:total_hours)
568 assert_tag :form,
597 assert_tag :form,
569 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
598 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
570 end
599 end
571
600
572 def test_index_at_issue_level
601 def test_index_at_issue_level
573 get :index, :issue_id => 1
602 get :index, :issue_id => 1
574 assert_response :success
603 assert_response :success
575 assert_template 'index'
604 assert_template 'index'
576 assert_not_nil assigns(:entries)
605 assert_not_nil assigns(:entries)
577 assert_equal 2, assigns(:entries).size
606 assert_equal 2, assigns(:entries).size
578 assert_not_nil assigns(:total_hours)
607 assert_not_nil assigns(:total_hours)
579 assert_equal 154.25, assigns(:total_hours)
608 assert_equal 154.25, assigns(:total_hours)
580 # display all time
609 # display all time
581 assert_nil assigns(:from)
610 assert_nil assigns(:from)
582 assert_nil assigns(:to)
611 assert_nil assigns(:to)
583 assert_tag :form,
612 assert_tag :form,
584 :attributes => {:action => "/issues/1/time_entries", :id => 'query_form'}
613 :attributes => {:action => "/issues/1/time_entries", :id => 'query_form'}
585 end
614 end
586
615
587 def test_index_should_sort_by_spent_on_and_created_on
616 def test_index_should_sort_by_spent_on_and_created_on
588 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)
617 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)
589 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)
618 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)
590 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)
619 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)
591
620
592 get :index, :project_id => 1,
621 get :index, :project_id => 1,
593 :f => ['spent_on'],
622 :f => ['spent_on'],
594 :op => {'spent_on' => '><'},
623 :op => {'spent_on' => '><'},
595 :v => {'spent_on' => ['2012-06-15', '2012-06-16']}
624 :v => {'spent_on' => ['2012-06-15', '2012-06-16']}
596 assert_response :success
625 assert_response :success
597 assert_equal [t2, t1, t3], assigns(:entries)
626 assert_equal [t2, t1, t3], assigns(:entries)
598
627
599 get :index, :project_id => 1,
628 get :index, :project_id => 1,
600 :f => ['spent_on'],
629 :f => ['spent_on'],
601 :op => {'spent_on' => '><'},
630 :op => {'spent_on' => '><'},
602 :v => {'spent_on' => ['2012-06-15', '2012-06-16']},
631 :v => {'spent_on' => ['2012-06-15', '2012-06-16']},
603 :sort => 'spent_on'
632 :sort => 'spent_on'
604 assert_response :success
633 assert_response :success
605 assert_equal [t3, t1, t2], assigns(:entries)
634 assert_equal [t3, t1, t2], assigns(:entries)
606 end
635 end
607
636
608 def test_index_with_filter_on_issue_custom_field
637 def test_index_with_filter_on_issue_custom_field
609 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
638 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
610 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
639 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
611
640
612 get :index, :f => ['issue.cf_2'], :op => {'issue.cf_2' => '='}, :v => {'issue.cf_2' => ['filter_on_issue_custom_field']}
641 get :index, :f => ['issue.cf_2'], :op => {'issue.cf_2' => '='}, :v => {'issue.cf_2' => ['filter_on_issue_custom_field']}
613 assert_response :success
642 assert_response :success
614 assert_equal [entry], assigns(:entries)
643 assert_equal [entry], assigns(:entries)
615 end
644 end
616
645
617 def test_index_with_issue_custom_field_column
646 def test_index_with_issue_custom_field_column
618 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
647 issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {2 => 'filter_on_issue_custom_field'})
619 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
648 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
620
649
621 get :index, :c => %w(project spent_on issue comments hours issue.cf_2)
650 get :index, :c => %w(project spent_on issue comments hours issue.cf_2)
622 assert_response :success
651 assert_response :success
623 assert_include :'issue.cf_2', assigns(:query).column_names
652 assert_include :'issue.cf_2', assigns(:query).column_names
624 assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field'
653 assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field'
625 end
654 end
626
655
627 def test_index_with_time_entry_custom_field_column
656 def test_index_with_time_entry_custom_field_column
628 field = TimeEntryCustomField.generate!(:field_format => 'string')
657 field = TimeEntryCustomField.generate!(:field_format => 'string')
629 entry = TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value'})
658 entry = TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value'})
630 field_name = "cf_#{field.id}"
659 field_name = "cf_#{field.id}"
631
660
632 get :index, :c => ["hours", field_name]
661 get :index, :c => ["hours", field_name]
633 assert_response :success
662 assert_response :success
634 assert_include field_name.to_sym, assigns(:query).column_names
663 assert_include field_name.to_sym, assigns(:query).column_names
635 assert_select "td.#{field_name}", :text => 'CF Value'
664 assert_select "td.#{field_name}", :text => 'CF Value'
636 end
665 end
637
666
638 def test_index_with_time_entry_custom_field_sorting
667 def test_index_with_time_entry_custom_field_sorting
639 field = TimeEntryCustomField.generate!(:field_format => 'string', :name => 'String Field')
668 field = TimeEntryCustomField.generate!(:field_format => 'string', :name => 'String Field')
640 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 1'})
669 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 1'})
641 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 3'})
670 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 3'})
642 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 2'})
671 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 2'})
643 field_name = "cf_#{field.id}"
672 field_name = "cf_#{field.id}"
644
673
645 get :index, :c => ["hours", field_name], :sort => field_name
674 get :index, :c => ["hours", field_name], :sort => field_name
646 assert_response :success
675 assert_response :success
647 assert_include field_name.to_sym, assigns(:query).column_names
676 assert_include field_name.to_sym, assigns(:query).column_names
648 assert_select "th a.sort", :text => 'String Field'
677 assert_select "th a.sort", :text => 'String Field'
649
678
650 # Make sure that values are properly sorted
679 # Make sure that values are properly sorted
651 values = assigns(:entries).map {|e| e.custom_field_value(field)}.compact
680 values = assigns(:entries).map {|e| e.custom_field_value(field)}.compact
652 assert_equal 3, values.size
681 assert_equal 3, values.size
653 assert_equal values.sort, values
682 assert_equal values.sort, values
654 end
683 end
655
684
656 def test_index_atom_feed
685 def test_index_atom_feed
657 get :index, :project_id => 1, :format => 'atom'
686 get :index, :project_id => 1, :format => 'atom'
658 assert_response :success
687 assert_response :success
659 assert_equal 'application/atom+xml', @response.content_type
688 assert_equal 'application/atom+xml', @response.content_type
660 assert_not_nil assigns(:items)
689 assert_not_nil assigns(:items)
661 assert assigns(:items).first.is_a?(TimeEntry)
690 assert assigns(:items).first.is_a?(TimeEntry)
662 end
691 end
663
692
664 def test_index_at_project_level_should_include_csv_export_dialog
693 def test_index_at_project_level_should_include_csv_export_dialog
665 get :index, :project_id => 'ecookbook',
694 get :index, :project_id => 'ecookbook',
666 :f => ['spent_on'],
695 :f => ['spent_on'],
667 :op => {'spent_on' => '>='},
696 :op => {'spent_on' => '>='},
668 :v => {'spent_on' => ['2007-04-01']},
697 :v => {'spent_on' => ['2007-04-01']},
669 :c => ['spent_on', 'user']
698 :c => ['spent_on', 'user']
670 assert_response :success
699 assert_response :success
671
700
672 assert_select '#csv-export-options' do
701 assert_select '#csv-export-options' do
673 assert_select 'form[action=?][method=get]', '/projects/ecookbook/time_entries.csv' do
702 assert_select 'form[action=?][method=get]', '/projects/ecookbook/time_entries.csv' do
674 # filter
703 # filter
675 assert_select 'input[name=?][value=?]', 'f[]', 'spent_on'
704 assert_select 'input[name=?][value=?]', 'f[]', 'spent_on'
676 assert_select 'input[name=?][value=?]', 'op[spent_on]', '&gt;='
705 assert_select 'input[name=?][value=?]', 'op[spent_on]', '&gt;='
677 assert_select 'input[name=?][value=?]', 'v[spent_on][]', '2007-04-01'
706 assert_select 'input[name=?][value=?]', 'v[spent_on][]', '2007-04-01'
678 # columns
707 # columns
679 assert_select 'input[name=?][value=?]', 'c[]', 'spent_on'
708 assert_select 'input[name=?][value=?]', 'c[]', 'spent_on'
680 assert_select 'input[name=?][value=?]', 'c[]', 'user'
709 assert_select 'input[name=?][value=?]', 'c[]', 'user'
681 assert_select 'input[name=?]', 'c[]', 2
710 assert_select 'input[name=?]', 'c[]', 2
682 end
711 end
683 end
712 end
684 end
713 end
685
714
686 def test_index_cross_project_should_include_csv_export_dialog
715 def test_index_cross_project_should_include_csv_export_dialog
687 get :index
716 get :index
688 assert_response :success
717 assert_response :success
689
718
690 assert_select '#csv-export-options' do
719 assert_select '#csv-export-options' do
691 assert_select 'form[action=?][method=get]', '/time_entries.csv'
720 assert_select 'form[action=?][method=get]', '/time_entries.csv'
692 end
721 end
693 end
722 end
694
723
695 def test_index_at_issue_level_should_include_csv_export_dialog
724 def test_index_at_issue_level_should_include_csv_export_dialog
696 get :index, :issue_id => 3
725 get :index, :issue_id => 3
697 assert_response :success
726 assert_response :success
698
727
699 assert_select '#csv-export-options' do
728 assert_select '#csv-export-options' do
700 assert_select 'form[action=?][method=get]', '/issues/3/time_entries.csv'
729 assert_select 'form[action=?][method=get]', '/issues/3/time_entries.csv'
701 end
730 end
702 end
731 end
703
732
704 def test_index_csv_all_projects
733 def test_index_csv_all_projects
705 Setting.date_format = '%m/%d/%Y'
734 Setting.date_format = '%m/%d/%Y'
706 get :index, :format => 'csv'
735 get :index, :format => 'csv'
707 assert_response :success
736 assert_response :success
708 assert_equal 'text/csv; header=present', response.content_type
737 assert_equal 'text/csv; header=present', response.content_type
709 end
738 end
710
739
711 def test_index_csv
740 def test_index_csv
712 Setting.date_format = '%m/%d/%Y'
741 Setting.date_format = '%m/%d/%Y'
713 get :index, :project_id => 1, :format => 'csv'
742 get :index, :project_id => 1, :format => 'csv'
714 assert_response :success
743 assert_response :success
715 assert_equal 'text/csv; header=present', response.content_type
744 assert_equal 'text/csv; header=present', response.content_type
716 end
745 end
717
746
718 def test_index_csv_should_fill_issue_column_with_tracker_id_and_subject
747 def test_index_csv_should_fill_issue_column_with_tracker_id_and_subject
719 issue = Issue.find(1)
748 issue = Issue.find(1)
720 entry = TimeEntry.generate!(:issue => issue, :comments => "Issue column content test")
749 entry = TimeEntry.generate!(:issue => issue, :comments => "Issue column content test")
721
750
722 get :index, :format => 'csv'
751 get :index, :format => 'csv'
723 line = response.body.split("\n").detect {|l| l.include?(entry.comments)}
752 line = response.body.split("\n").detect {|l| l.include?(entry.comments)}
724 assert_not_nil line
753 assert_not_nil line
725 assert_include "#{issue.tracker} #1: #{issue.subject}", line
754 assert_include "#{issue.tracker} #1: #{issue.subject}", line
726 end
755 end
727 end
756 end
General Comments 0
You need to be logged in to leave comments. Login now