##// END OF EJS Templates
Merged r13562 (#18269)....
Jean-Philippe Lang -
r13248:2d612f32b7c0
parent child
Show More
@@ -1,202 +1,206
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2014 Jean-Philippe Lang
4 # Copyright (C) 2006-2014 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 module QueriesHelper
20 module QueriesHelper
21 include ApplicationHelper
21 include ApplicationHelper
22
22
23 def filters_options_for_select(query)
23 def filters_options_for_select(query)
24 options_for_select(filters_options(query))
24 options_for_select(filters_options(query))
25 end
25 end
26
26
27 def filters_options(query)
27 def filters_options(query)
28 options = [[]]
28 options = [[]]
29 options += query.available_filters.map do |field, field_options|
29 options += query.available_filters.map do |field, field_options|
30 [field_options[:name], field]
30 [field_options[:name], field]
31 end
31 end
32 end
32 end
33
33
34 def query_filters_hidden_tags(query)
34 def query_filters_hidden_tags(query)
35 tags = ''.html_safe
35 tags = ''.html_safe
36 query.filters.each do |field, options|
36 query.filters.each do |field, options|
37 tags << hidden_field_tag("f[]", field, :id => nil)
37 tags << hidden_field_tag("f[]", field, :id => nil)
38 tags << hidden_field_tag("op[#{field}]", options[:operator], :id => nil)
38 tags << hidden_field_tag("op[#{field}]", options[:operator], :id => nil)
39 options[:values].each do |value|
39 options[:values].each do |value|
40 tags << hidden_field_tag("v[#{field}][]", value, :id => nil)
40 tags << hidden_field_tag("v[#{field}][]", value, :id => nil)
41 end
41 end
42 end
42 end
43 tags
43 tags
44 end
44 end
45
45
46 def query_columns_hidden_tags(query)
46 def query_columns_hidden_tags(query)
47 tags = ''.html_safe
47 tags = ''.html_safe
48 query.columns.each do |column|
48 query.columns.each do |column|
49 tags << hidden_field_tag("c[]", column.name, :id => nil)
49 tags << hidden_field_tag("c[]", column.name, :id => nil)
50 end
50 end
51 tags
51 tags
52 end
52 end
53
53
54 def query_hidden_tags(query)
54 def query_hidden_tags(query)
55 query_filters_hidden_tags(query) + query_columns_hidden_tags(query)
55 query_filters_hidden_tags(query) + query_columns_hidden_tags(query)
56 end
56 end
57
57
58 def available_block_columns_tags(query)
58 def available_block_columns_tags(query)
59 tags = ''.html_safe
59 tags = ''.html_safe
60 query.available_block_columns.each do |column|
60 query.available_block_columns.each do |column|
61 tags << content_tag('label', check_box_tag('c[]', column.name.to_s, query.has_column?(column), :id => nil) + " #{column.caption}", :class => 'inline')
61 tags << content_tag('label', check_box_tag('c[]', column.name.to_s, query.has_column?(column), :id => nil) + " #{column.caption}", :class => 'inline')
62 end
62 end
63 tags
63 tags
64 end
64 end
65
65
66 def query_available_inline_columns_options(query)
66 def query_available_inline_columns_options(query)
67 (query.available_inline_columns - query.columns).reject(&:frozen?).collect {|column| [column.caption, column.name]}
67 (query.available_inline_columns - query.columns).reject(&:frozen?).collect {|column| [column.caption, column.name]}
68 end
68 end
69
69
70 def query_selected_inline_columns_options(query)
70 def query_selected_inline_columns_options(query)
71 (query.inline_columns & query.available_inline_columns).reject(&:frozen?).collect {|column| [column.caption, column.name]}
71 (query.inline_columns & query.available_inline_columns).reject(&:frozen?).collect {|column| [column.caption, column.name]}
72 end
72 end
73
73
74 def render_query_columns_selection(query, options={})
74 def render_query_columns_selection(query, options={})
75 tag_name = (options[:name] || 'c') + '[]'
75 tag_name = (options[:name] || 'c') + '[]'
76 render :partial => 'queries/columns', :locals => {:query => query, :tag_name => tag_name}
76 render :partial => 'queries/columns', :locals => {:query => query, :tag_name => tag_name}
77 end
77 end
78
78
79 def column_header(column)
79 def column_header(column)
80 column.sortable ? sort_header_tag(column.name.to_s, :caption => column.caption,
80 column.sortable ? sort_header_tag(column.name.to_s, :caption => column.caption,
81 :default_order => column.default_order) :
81 :default_order => column.default_order) :
82 content_tag('th', h(column.caption))
82 content_tag('th', h(column.caption))
83 end
83 end
84
84
85 def column_content(column, issue)
85 def column_content(column, issue)
86 value = column.value_object(issue)
86 value = column.value_object(issue)
87 if value.is_a?(Array)
87 if value.is_a?(Array)
88 value.collect {|v| column_value(column, issue, v)}.compact.join(', ').html_safe
88 value.collect {|v| column_value(column, issue, v)}.compact.join(', ').html_safe
89 else
89 else
90 column_value(column, issue, value)
90 column_value(column, issue, value)
91 end
91 end
92 end
92 end
93
93
94 def column_value(column, issue, value)
94 def column_value(column, issue, value)
95 case column.name
95 case column.name
96 when :id
96 when :id
97 link_to value, issue_path(issue)
97 link_to value, issue_path(issue)
98 when :subject
98 when :subject
99 link_to value, issue_path(issue)
99 link_to value, issue_path(issue)
100 when :parent
100 when :parent
101 value ? (value.visible? ? link_to_issue(value, :subject => false) : "##{value.id}") : ''
101 value ? (value.visible? ? link_to_issue(value, :subject => false) : "##{value.id}") : ''
102 when :description
102 when :description
103 issue.description? ? content_tag('div', textilizable(issue, :description), :class => "wiki") : ''
103 issue.description? ? content_tag('div', textilizable(issue, :description), :class => "wiki") : ''
104 when :done_ratio
104 when :done_ratio
105 progress_bar(value, :width => '80px')
105 progress_bar(value, :width => '80px')
106 when :relations
106 when :relations
107 other = value.other_issue(issue)
107 other = value.other_issue(issue)
108 content_tag('span',
108 content_tag('span',
109 (l(value.label_for(issue)) + " " + link_to_issue(other, :subject => false, :tracker => false)).html_safe,
109 (l(value.label_for(issue)) + " " + link_to_issue(other, :subject => false, :tracker => false)).html_safe,
110 :class => value.css_classes_for(issue))
110 :class => value.css_classes_for(issue))
111 else
111 else
112 format_object(value)
112 format_object(value)
113 end
113 end
114 end
114 end
115
115
116 def csv_content(column, issue)
116 def csv_content(column, issue)
117 value = column.value_object(issue)
117 value = column.value_object(issue)
118 if value.is_a?(Array)
118 if value.is_a?(Array)
119 value.collect {|v| csv_value(column, issue, v)}.compact.join(', ')
119 value.collect {|v| csv_value(column, issue, v)}.compact.join(', ')
120 else
120 else
121 csv_value(column, issue, value)
121 csv_value(column, issue, value)
122 end
122 end
123 end
123 end
124
124
125 def csv_value(column, issue, value)
125 def csv_value(column, object, value)
126 format_object(value, false) do |value|
126 format_object(value, false) do |value|
127 case value.class.name
127 case value.class.name
128 when 'Float'
128 when 'Float'
129 sprintf("%.2f", value).gsub('.', l(:general_csv_decimal_separator))
129 sprintf("%.2f", value).gsub('.', l(:general_csv_decimal_separator))
130 when 'IssueRelation'
130 when 'IssueRelation'
131 other = value.other_issue(issue)
131 other = value.other_issue(object)
132 l(value.label_for(issue)) + " ##{other.id}"
132 l(value.label_for(object)) + " ##{other.id}"
133 when 'Issue'
133 when 'Issue'
134 value.id
134 if object.is_a?(TimeEntry)
135 "#{value.tracker} ##{value.id}: #{value.subject}"
136 else
137 value.id
138 end
135 else
139 else
136 value
140 value
137 end
141 end
138 end
142 end
139 end
143 end
140
144
141 def query_to_csv(items, query, options={})
145 def query_to_csv(items, query, options={})
142 encoding = l(:general_csv_encoding)
146 encoding = l(:general_csv_encoding)
143 columns = (options[:columns] == 'all' ? query.available_inline_columns : query.inline_columns)
147 columns = (options[:columns] == 'all' ? query.available_inline_columns : query.inline_columns)
144 query.available_block_columns.each do |column|
148 query.available_block_columns.each do |column|
145 if options[column.name].present?
149 if options[column.name].present?
146 columns << column
150 columns << column
147 end
151 end
148 end
152 end
149
153
150 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
154 export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv|
151 # csv header fields
155 # csv header fields
152 csv << columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) }
156 csv << columns.collect {|c| Redmine::CodesetUtil.from_utf8(c.caption.to_s, encoding) }
153 # csv lines
157 # csv lines
154 items.each do |item|
158 items.each do |item|
155 csv << columns.collect {|c| Redmine::CodesetUtil.from_utf8(csv_content(c, item), encoding) }
159 csv << columns.collect {|c| Redmine::CodesetUtil.from_utf8(csv_content(c, item), encoding) }
156 end
160 end
157 end
161 end
158 export
162 export
159 end
163 end
160
164
161 # Retrieve query from session or build a new query
165 # Retrieve query from session or build a new query
162 def retrieve_query
166 def retrieve_query
163 if !params[:query_id].blank?
167 if !params[:query_id].blank?
164 cond = "project_id IS NULL"
168 cond = "project_id IS NULL"
165 cond << " OR project_id = #{@project.id}" if @project
169 cond << " OR project_id = #{@project.id}" if @project
166 @query = IssueQuery.where(cond).find(params[:query_id])
170 @query = IssueQuery.where(cond).find(params[:query_id])
167 raise ::Unauthorized unless @query.visible?
171 raise ::Unauthorized unless @query.visible?
168 @query.project = @project
172 @query.project = @project
169 session[:query] = {:id => @query.id, :project_id => @query.project_id}
173 session[:query] = {:id => @query.id, :project_id => @query.project_id}
170 sort_clear
174 sort_clear
171 elsif api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
175 elsif api_request? || params[:set_filter] || session[:query].nil? || session[:query][:project_id] != (@project ? @project.id : nil)
172 # Give it a name, required to be valid
176 # Give it a name, required to be valid
173 @query = IssueQuery.new(:name => "_")
177 @query = IssueQuery.new(:name => "_")
174 @query.project = @project
178 @query.project = @project
175 @query.build_from_params(params)
179 @query.build_from_params(params)
176 session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
180 session[:query] = {:project_id => @query.project_id, :filters => @query.filters, :group_by => @query.group_by, :column_names => @query.column_names}
177 else
181 else
178 # retrieve from session
182 # retrieve from session
179 @query = nil
183 @query = nil
180 @query = IssueQuery.find_by_id(session[:query][:id]) if session[:query][:id]
184 @query = IssueQuery.find_by_id(session[:query][:id]) if session[:query][:id]
181 @query ||= IssueQuery.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
185 @query ||= IssueQuery.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
182 @query.project = @project
186 @query.project = @project
183 end
187 end
184 end
188 end
185
189
186 def retrieve_query_from_session
190 def retrieve_query_from_session
187 if session[:query]
191 if session[:query]
188 if session[:query][:id]
192 if session[:query][:id]
189 @query = IssueQuery.find_by_id(session[:query][:id])
193 @query = IssueQuery.find_by_id(session[:query][:id])
190 return unless @query
194 return unless @query
191 else
195 else
192 @query = IssueQuery.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
196 @query = IssueQuery.new(:name => "_", :filters => session[:query][:filters], :group_by => session[:query][:group_by], :column_names => session[:query][:column_names])
193 end
197 end
194 if session[:query].has_key?(:project_id)
198 if session[:query].has_key?(:project_id)
195 @query.project_id = session[:query][:project_id]
199 @query.project_id = session[:query][:project_id]
196 else
200 else
197 @query.project = @project
201 @query.project = @project
198 end
202 end
199 @query
203 @query
200 end
204 end
201 end
205 end
202 end
206 end
@@ -1,717 +1,727
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # Redmine - project management software
2 # Redmine - project management software
3 # Copyright (C) 2006-2014 Jean-Philippe Lang
3 # Copyright (C) 2006-2014 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_update
378 def test_bulk_update
379 @request.session[:user_id] = 2
379 @request.session[:user_id] = 2
380 # update time entry activity
380 # update time entry activity
381 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
381 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
382
382
383 assert_response 302
383 assert_response 302
384 # check that the issues were updated
384 # check that the issues were updated
385 assert_equal [9, 9], TimeEntry.where(:id => [1, 2]).collect {|i| i.activity_id}
385 assert_equal [9, 9], TimeEntry.where(:id => [1, 2]).collect {|i| i.activity_id}
386 end
386 end
387
387
388 def test_bulk_update_with_failure
388 def test_bulk_update_with_failure
389 @request.session[:user_id] = 2
389 @request.session[:user_id] = 2
390 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
390 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
391
391
392 assert_response 302
392 assert_response 302
393 assert_match /Failed to save 2 time entrie/, flash[:error]
393 assert_match /Failed to save 2 time entrie/, flash[:error]
394 end
394 end
395
395
396 def test_bulk_update_on_different_projects
396 def test_bulk_update_on_different_projects
397 @request.session[:user_id] = 2
397 @request.session[:user_id] = 2
398 # makes user a manager on the other project
398 # makes user a manager on the other project
399 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
399 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
400
400
401 # update time entry activity
401 # update time entry activity
402 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
402 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
403
403
404 assert_response 302
404 assert_response 302
405 # check that the issues were updated
405 # check that the issues were updated
406 assert_equal [9, 9, 9], TimeEntry.where(:id => [1, 2, 4]).collect {|i| i.activity_id}
406 assert_equal [9, 9, 9], TimeEntry.where(:id => [1, 2, 4]).collect {|i| i.activity_id}
407 end
407 end
408
408
409 def test_bulk_update_on_different_projects_without_rights
409 def test_bulk_update_on_different_projects_without_rights
410 @request.session[:user_id] = 3
410 @request.session[:user_id] = 3
411 user = User.find(3)
411 user = User.find(3)
412 action = { :controller => "timelog", :action => "bulk_update" }
412 action = { :controller => "timelog", :action => "bulk_update" }
413 assert user.allowed_to?(action, TimeEntry.find(1).project)
413 assert user.allowed_to?(action, TimeEntry.find(1).project)
414 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
414 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
415 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
415 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
416 assert_response 403
416 assert_response 403
417 end
417 end
418
418
419 def test_bulk_update_custom_field
419 def test_bulk_update_custom_field
420 @request.session[:user_id] = 2
420 @request.session[:user_id] = 2
421 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
421 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
422
422
423 assert_response 302
423 assert_response 302
424 assert_equal ["0", "0"], TimeEntry.where(:id => [1, 2]).collect {|i| i.custom_value_for(10).value}
424 assert_equal ["0", "0"], TimeEntry.where(:id => [1, 2]).collect {|i| i.custom_value_for(10).value}
425 end
425 end
426
426
427 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
427 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
428 @request.session[:user_id] = 2
428 @request.session[:user_id] = 2
429 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
429 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
430
430
431 assert_response :redirect
431 assert_response :redirect
432 assert_redirected_to '/time_entries'
432 assert_redirected_to '/time_entries'
433 end
433 end
434
434
435 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
435 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
436 @request.session[:user_id] = 2
436 @request.session[:user_id] = 2
437 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
437 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
438
438
439 assert_response :redirect
439 assert_response :redirect
440 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
440 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
441 end
441 end
442
442
443 def test_post_bulk_update_without_edit_permission_should_be_denied
443 def test_post_bulk_update_without_edit_permission_should_be_denied
444 @request.session[:user_id] = 2
444 @request.session[:user_id] = 2
445 Role.find_by_name('Manager').remove_permission! :edit_time_entries
445 Role.find_by_name('Manager').remove_permission! :edit_time_entries
446 post :bulk_update, :ids => [1,2]
446 post :bulk_update, :ids => [1,2]
447
447
448 assert_response 403
448 assert_response 403
449 end
449 end
450
450
451 def test_destroy
451 def test_destroy
452 @request.session[:user_id] = 2
452 @request.session[:user_id] = 2
453 delete :destroy, :id => 1
453 delete :destroy, :id => 1
454 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
454 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
455 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
455 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
456 assert_nil TimeEntry.find_by_id(1)
456 assert_nil TimeEntry.find_by_id(1)
457 end
457 end
458
458
459 def test_destroy_should_fail
459 def test_destroy_should_fail
460 # simulate that this fails (e.g. due to a plugin), see #5700
460 # simulate that this fails (e.g. due to a plugin), see #5700
461 TimeEntry.any_instance.expects(:destroy).returns(false)
461 TimeEntry.any_instance.expects(:destroy).returns(false)
462
462
463 @request.session[:user_id] = 2
463 @request.session[:user_id] = 2
464 delete :destroy, :id => 1
464 delete :destroy, :id => 1
465 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
465 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
466 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
466 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
467 assert_not_nil TimeEntry.find_by_id(1)
467 assert_not_nil TimeEntry.find_by_id(1)
468 end
468 end
469
469
470 def test_index_all_projects
470 def test_index_all_projects
471 get :index
471 get :index
472 assert_response :success
472 assert_response :success
473 assert_template 'index'
473 assert_template 'index'
474 assert_not_nil assigns(:total_hours)
474 assert_not_nil assigns(:total_hours)
475 assert_equal "162.90", "%.2f" % assigns(:total_hours)
475 assert_equal "162.90", "%.2f" % assigns(:total_hours)
476 assert_tag :form,
476 assert_tag :form,
477 :attributes => {:action => "/time_entries", :id => 'query_form'}
477 :attributes => {:action => "/time_entries", :id => 'query_form'}
478 end
478 end
479
479
480 def test_index_all_projects_should_show_log_time_link
480 def test_index_all_projects_should_show_log_time_link
481 @request.session[:user_id] = 2
481 @request.session[:user_id] = 2
482 get :index
482 get :index
483 assert_response :success
483 assert_response :success
484 assert_template 'index'
484 assert_template 'index'
485 assert_tag 'a', :attributes => {:href => '/time_entries/new'}, :content => /Log time/
485 assert_tag 'a', :attributes => {:href => '/time_entries/new'}, :content => /Log time/
486 end
486 end
487
487
488 def test_index_my_spent_time
488 def test_index_my_spent_time
489 @request.session[:user_id] = 2
489 @request.session[:user_id] = 2
490 get :index, :user_id => 'me'
490 get :index, :user_id => 'me'
491 assert_response :success
491 assert_response :success
492 assert_template 'index'
492 assert_template 'index'
493 assert assigns(:entries).all? {|entry| entry.user_id == 2}
493 assert assigns(:entries).all? {|entry| entry.user_id == 2}
494 end
494 end
495
495
496 def test_index_at_project_level
496 def test_index_at_project_level
497 get :index, :project_id => 'ecookbook'
497 get :index, :project_id => 'ecookbook'
498 assert_response :success
498 assert_response :success
499 assert_template 'index'
499 assert_template 'index'
500 assert_not_nil assigns(:entries)
500 assert_not_nil assigns(:entries)
501 assert_equal 4, assigns(:entries).size
501 assert_equal 4, assigns(:entries).size
502 # project and subproject
502 # project and subproject
503 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
503 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
504 assert_not_nil assigns(:total_hours)
504 assert_not_nil assigns(:total_hours)
505 assert_equal "162.90", "%.2f" % assigns(:total_hours)
505 assert_equal "162.90", "%.2f" % assigns(:total_hours)
506 assert_tag :form,
506 assert_tag :form,
507 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
507 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
508 end
508 end
509
509
510 def test_index_with_display_subprojects_issues_to_false_should_not_include_subproject_entries
510 def test_index_with_display_subprojects_issues_to_false_should_not_include_subproject_entries
511 entry = TimeEntry.generate!(:project => Project.find(3))
511 entry = TimeEntry.generate!(:project => Project.find(3))
512
512
513 with_settings :display_subprojects_issues => '0' do
513 with_settings :display_subprojects_issues => '0' do
514 get :index, :project_id => 'ecookbook'
514 get :index, :project_id => 'ecookbook'
515 assert_response :success
515 assert_response :success
516 assert_template 'index'
516 assert_template 'index'
517 assert_not_include entry, assigns(:entries)
517 assert_not_include entry, assigns(:entries)
518 end
518 end
519 end
519 end
520
520
521 def test_index_with_display_subprojects_issues_to_false_and_subproject_filter_should_include_subproject_entries
521 def test_index_with_display_subprojects_issues_to_false_and_subproject_filter_should_include_subproject_entries
522 entry = TimeEntry.generate!(:project => Project.find(3))
522 entry = TimeEntry.generate!(:project => Project.find(3))
523
523
524 with_settings :display_subprojects_issues => '0' do
524 with_settings :display_subprojects_issues => '0' do
525 get :index, :project_id => 'ecookbook', :subproject_id => 3
525 get :index, :project_id => 'ecookbook', :subproject_id => 3
526 assert_response :success
526 assert_response :success
527 assert_template 'index'
527 assert_template 'index'
528 assert_include entry, assigns(:entries)
528 assert_include entry, assigns(:entries)
529 end
529 end
530 end
530 end
531
531
532 def test_index_at_project_level_with_date_range
532 def test_index_at_project_level_with_date_range
533 get :index, :project_id => 'ecookbook',
533 get :index, :project_id => 'ecookbook',
534 :f => ['spent_on'],
534 :f => ['spent_on'],
535 :op => {'spent_on' => '><'},
535 :op => {'spent_on' => '><'},
536 :v => {'spent_on' => ['2007-03-20', '2007-04-30']}
536 :v => {'spent_on' => ['2007-03-20', '2007-04-30']}
537 assert_response :success
537 assert_response :success
538 assert_template 'index'
538 assert_template 'index'
539 assert_not_nil assigns(:entries)
539 assert_not_nil assigns(:entries)
540 assert_equal 3, assigns(:entries).size
540 assert_equal 3, assigns(:entries).size
541 assert_not_nil assigns(:total_hours)
541 assert_not_nil assigns(:total_hours)
542 assert_equal "12.90", "%.2f" % assigns(:total_hours)
542 assert_equal "12.90", "%.2f" % assigns(:total_hours)
543 assert_tag :form,
543 assert_tag :form,
544 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
544 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
545 end
545 end
546
546
547 def test_index_at_project_level_with_date_range_using_from_and_to_params
547 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'
548 get :index, :project_id => 'ecookbook', :from => '2007-03-20', :to => '2007-04-30'
549 assert_response :success
549 assert_response :success
550 assert_template 'index'
550 assert_template 'index'
551 assert_not_nil assigns(:entries)
551 assert_not_nil assigns(:entries)
552 assert_equal 3, assigns(:entries).size
552 assert_equal 3, assigns(:entries).size
553 assert_not_nil assigns(:total_hours)
553 assert_not_nil assigns(:total_hours)
554 assert_equal "12.90", "%.2f" % assigns(:total_hours)
554 assert_equal "12.90", "%.2f" % assigns(:total_hours)
555 assert_tag :form,
555 assert_tag :form,
556 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
556 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
557 end
557 end
558
558
559 def test_index_at_project_level_with_period
559 def test_index_at_project_level_with_period
560 get :index, :project_id => 'ecookbook',
560 get :index, :project_id => 'ecookbook',
561 :f => ['spent_on'],
561 :f => ['spent_on'],
562 :op => {'spent_on' => '>t-'},
562 :op => {'spent_on' => '>t-'},
563 :v => {'spent_on' => ['7']}
563 :v => {'spent_on' => ['7']}
564 assert_response :success
564 assert_response :success
565 assert_template 'index'
565 assert_template 'index'
566 assert_not_nil assigns(:entries)
566 assert_not_nil assigns(:entries)
567 assert_not_nil assigns(:total_hours)
567 assert_not_nil assigns(:total_hours)
568 assert_tag :form,
568 assert_tag :form,
569 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
569 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
570 end
570 end
571
571
572 def test_index_at_issue_level
572 def test_index_at_issue_level
573 get :index, :issue_id => 1
573 get :index, :issue_id => 1
574 assert_response :success
574 assert_response :success
575 assert_template 'index'
575 assert_template 'index'
576 assert_not_nil assigns(:entries)
576 assert_not_nil assigns(:entries)
577 assert_equal 2, assigns(:entries).size
577 assert_equal 2, assigns(:entries).size
578 assert_not_nil assigns(:total_hours)
578 assert_not_nil assigns(:total_hours)
579 assert_equal 154.25, assigns(:total_hours)
579 assert_equal 154.25, assigns(:total_hours)
580 # display all time
580 # display all time
581 assert_nil assigns(:from)
581 assert_nil assigns(:from)
582 assert_nil assigns(:to)
582 assert_nil assigns(:to)
583 assert_tag :form,
583 assert_tag :form,
584 :attributes => {:action => "/issues/1/time_entries", :id => 'query_form'}
584 :attributes => {:action => "/issues/1/time_entries", :id => 'query_form'}
585 end
585 end
586
586
587 def test_index_should_sort_by_spent_on_and_created_on
587 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)
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)
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)
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)
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)
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)
591
591
592 get :index, :project_id => 1,
592 get :index, :project_id => 1,
593 :f => ['spent_on'],
593 :f => ['spent_on'],
594 :op => {'spent_on' => '><'},
594 :op => {'spent_on' => '><'},
595 :v => {'spent_on' => ['2012-06-15', '2012-06-16']}
595 :v => {'spent_on' => ['2012-06-15', '2012-06-16']}
596 assert_response :success
596 assert_response :success
597 assert_equal [t2, t1, t3], assigns(:entries)
597 assert_equal [t2, t1, t3], assigns(:entries)
598
598
599 get :index, :project_id => 1,
599 get :index, :project_id => 1,
600 :f => ['spent_on'],
600 :f => ['spent_on'],
601 :op => {'spent_on' => '><'},
601 :op => {'spent_on' => '><'},
602 :v => {'spent_on' => ['2012-06-15', '2012-06-16']},
602 :v => {'spent_on' => ['2012-06-15', '2012-06-16']},
603 :sort => 'spent_on'
603 :sort => 'spent_on'
604 assert_response :success
604 assert_response :success
605 assert_equal [t3, t1, t2], assigns(:entries)
605 assert_equal [t3, t1, t2], assigns(:entries)
606 end
606 end
607
607
608 def test_index_with_filter_on_issue_custom_field
608 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'})
609 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)
610 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
611
611
612 get :index, :f => ['issue.cf_2'], :op => {'issue.cf_2' => '='}, :v => {'issue.cf_2' => ['filter_on_issue_custom_field']}
612 get :index, :f => ['issue.cf_2'], :op => {'issue.cf_2' => '='}, :v => {'issue.cf_2' => ['filter_on_issue_custom_field']}
613 assert_response :success
613 assert_response :success
614 assert_equal [entry], assigns(:entries)
614 assert_equal [entry], assigns(:entries)
615 end
615 end
616
616
617 def test_index_with_issue_custom_field_column
617 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'})
618 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)
619 entry = TimeEntry.generate!(:issue => issue, :hours => 2.5)
620
620
621 get :index, :c => %w(project spent_on issue comments hours issue.cf_2)
621 get :index, :c => %w(project spent_on issue comments hours issue.cf_2)
622 assert_response :success
622 assert_response :success
623 assert_include :'issue.cf_2', assigns(:query).column_names
623 assert_include :'issue.cf_2', assigns(:query).column_names
624 assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field'
624 assert_select 'td.issue_cf_2', :text => 'filter_on_issue_custom_field'
625 end
625 end
626
626
627 def test_index_with_time_entry_custom_field_column
627 def test_index_with_time_entry_custom_field_column
628 field = TimeEntryCustomField.generate!(:field_format => 'string')
628 field = TimeEntryCustomField.generate!(:field_format => 'string')
629 entry = TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value'})
629 entry = TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value'})
630 field_name = "cf_#{field.id}"
630 field_name = "cf_#{field.id}"
631
631
632 get :index, :c => ["hours", field_name]
632 get :index, :c => ["hours", field_name]
633 assert_response :success
633 assert_response :success
634 assert_include field_name.to_sym, assigns(:query).column_names
634 assert_include field_name.to_sym, assigns(:query).column_names
635 assert_select "td.#{field_name}", :text => 'CF Value'
635 assert_select "td.#{field_name}", :text => 'CF Value'
636 end
636 end
637
637
638 def test_index_with_time_entry_custom_field_sorting
638 def test_index_with_time_entry_custom_field_sorting
639 field = TimeEntryCustomField.generate!(:field_format => 'string', :name => 'String Field')
639 field = TimeEntryCustomField.generate!(:field_format => 'string', :name => 'String Field')
640 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 1'})
640 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'})
641 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'})
642 TimeEntry.generate!(:hours => 2.5, :custom_field_values => {field.id => 'CF Value 2'})
643 field_name = "cf_#{field.id}"
643 field_name = "cf_#{field.id}"
644
644
645 get :index, :c => ["hours", field_name], :sort => field_name
645 get :index, :c => ["hours", field_name], :sort => field_name
646 assert_response :success
646 assert_response :success
647 assert_include field_name.to_sym, assigns(:query).column_names
647 assert_include field_name.to_sym, assigns(:query).column_names
648 assert_select "th a.sort", :text => 'String Field'
648 assert_select "th a.sort", :text => 'String Field'
649
649
650 # Make sure that values are properly sorted
650 # Make sure that values are properly sorted
651 values = assigns(:entries).map {|e| e.custom_field_value(field)}.compact
651 values = assigns(:entries).map {|e| e.custom_field_value(field)}.compact
652 assert_equal 3, values.size
652 assert_equal 3, values.size
653 assert_equal values.sort, values
653 assert_equal values.sort, values
654 end
654 end
655
655
656 def test_index_atom_feed
656 def test_index_atom_feed
657 get :index, :project_id => 1, :format => 'atom'
657 get :index, :project_id => 1, :format => 'atom'
658 assert_response :success
658 assert_response :success
659 assert_equal 'application/atom+xml', @response.content_type
659 assert_equal 'application/atom+xml', @response.content_type
660 assert_not_nil assigns(:items)
660 assert_not_nil assigns(:items)
661 assert assigns(:items).first.is_a?(TimeEntry)
661 assert assigns(:items).first.is_a?(TimeEntry)
662 end
662 end
663
663
664 def test_index_at_project_level_should_include_csv_export_dialog
664 def test_index_at_project_level_should_include_csv_export_dialog
665 get :index, :project_id => 'ecookbook',
665 get :index, :project_id => 'ecookbook',
666 :f => ['spent_on'],
666 :f => ['spent_on'],
667 :op => {'spent_on' => '>='},
667 :op => {'spent_on' => '>='},
668 :v => {'spent_on' => ['2007-04-01']},
668 :v => {'spent_on' => ['2007-04-01']},
669 :c => ['spent_on', 'user']
669 :c => ['spent_on', 'user']
670 assert_response :success
670 assert_response :success
671
671
672 assert_select '#csv-export-options' do
672 assert_select '#csv-export-options' do
673 assert_select 'form[action=?][method=get]', '/projects/ecookbook/time_entries.csv' do
673 assert_select 'form[action=?][method=get]', '/projects/ecookbook/time_entries.csv' do
674 # filter
674 # filter
675 assert_select 'input[name=?][value=?]', 'f[]', 'spent_on'
675 assert_select 'input[name=?][value=?]', 'f[]', 'spent_on'
676 assert_select 'input[name=?][value=?]', 'op[spent_on]', '&gt;='
676 assert_select 'input[name=?][value=?]', 'op[spent_on]', '&gt;='
677 assert_select 'input[name=?][value=?]', 'v[spent_on][]', '2007-04-01'
677 assert_select 'input[name=?][value=?]', 'v[spent_on][]', '2007-04-01'
678 # columns
678 # columns
679 assert_select 'input[name=?][value=?]', 'c[]', 'spent_on'
679 assert_select 'input[name=?][value=?]', 'c[]', 'spent_on'
680 assert_select 'input[name=?][value=?]', 'c[]', 'user'
680 assert_select 'input[name=?][value=?]', 'c[]', 'user'
681 assert_select 'input[name=?]', 'c[]', 2
681 assert_select 'input[name=?]', 'c[]', 2
682 end
682 end
683 end
683 end
684 end
684 end
685
685
686 def test_index_cross_project_should_include_csv_export_dialog
686 def test_index_cross_project_should_include_csv_export_dialog
687 get :index
687 get :index
688 assert_response :success
688 assert_response :success
689
689
690 assert_select '#csv-export-options' do
690 assert_select '#csv-export-options' do
691 assert_select 'form[action=?][method=get]', '/time_entries.csv'
691 assert_select 'form[action=?][method=get]', '/time_entries.csv'
692 end
692 end
693 end
693 end
694
694
695 def test_index_at_issue_level_should_include_csv_export_dialog
695 def test_index_at_issue_level_should_include_csv_export_dialog
696 get :index, :issue_id => 3
696 get :index, :issue_id => 3
697 assert_response :success
697 assert_response :success
698
698
699 assert_select '#csv-export-options' do
699 assert_select '#csv-export-options' do
700 assert_select 'form[action=?][method=get]', '/issues/3/time_entries.csv'
700 assert_select 'form[action=?][method=get]', '/issues/3/time_entries.csv'
701 end
701 end
702 end
702 end
703
703
704 def test_index_csv_all_projects
704 def test_index_csv_all_projects
705 Setting.date_format = '%m/%d/%Y'
705 Setting.date_format = '%m/%d/%Y'
706 get :index, :format => 'csv'
706 get :index, :format => 'csv'
707 assert_response :success
707 assert_response :success
708 assert_equal 'text/csv; header=present', response.content_type
708 assert_equal 'text/csv; header=present', response.content_type
709 end
709 end
710
710
711 def test_index_csv
711 def test_index_csv
712 Setting.date_format = '%m/%d/%Y'
712 Setting.date_format = '%m/%d/%Y'
713 get :index, :project_id => 1, :format => 'csv'
713 get :index, :project_id => 1, :format => 'csv'
714 assert_response :success
714 assert_response :success
715 assert_equal 'text/csv; header=present', response.content_type
715 assert_equal 'text/csv; header=present', response.content_type
716 end
716 end
717
718 def test_index_csv_should_fill_issue_column_with_tracker_id_and_subject
719 issue = Issue.find(1)
720 entry = TimeEntry.generate!(:issue => issue, :comments => "Issue column content test")
721
722 get :index, :format => 'csv'
723 line = response.body.split("\n").detect {|l| l.include?(entry.comments)}
724 assert_not_nil line
725 assert_include "#{issue.tracker} #1: #{issue.subject}", line
726 end
717 end
727 end
General Comments 0
You need to be logged in to leave comments. Login now