Redmine 4.1.7
This commit is contained in:
parent
55458d3479
commit
3ca3c37487
103 changed files with 2426 additions and 431 deletions
|
@ -28,7 +28,8 @@ class ActivitiesControllerTest < Redmine::ControllerTest
|
|||
:members,
|
||||
:groups_users,
|
||||
:enabled_modules,
|
||||
:journals, :journal_details
|
||||
:journals, :journal_details,
|
||||
:attachments, :changesets, :documents, :messages, :news, :time_entries, :wiki_content_versions
|
||||
|
||||
def test_project_index
|
||||
get :index, :params => {
|
||||
|
@ -95,6 +96,18 @@ class ActivitiesControllerTest < Redmine::ControllerTest
|
|||
assert_response 404
|
||||
end
|
||||
|
||||
def test_user_index_with_non_visible_user_id_should_respond_404
|
||||
Role.anonymous.update! :users_visibility => 'members_of_visible_projects'
|
||||
user = User.generate!
|
||||
|
||||
@request.session[:user_id] = nil
|
||||
get :index, :params => {
|
||||
:user_id => user.id
|
||||
}
|
||||
|
||||
assert_response 404
|
||||
end
|
||||
|
||||
def test_index_atom_feed
|
||||
get :index, :params => {
|
||||
:format => 'atom',
|
||||
|
@ -111,6 +124,22 @@ class ActivitiesControllerTest < Redmine::ControllerTest
|
|||
end
|
||||
end
|
||||
|
||||
def test_index_atom_feed_should_respect_feeds_limit_setting
|
||||
with_settings :feeds_limit => '20' do
|
||||
get(
|
||||
:index,
|
||||
:params => {
|
||||
:format => 'atom'
|
||||
}
|
||||
)
|
||||
end
|
||||
assert_response :success
|
||||
|
||||
assert_select 'feed' do
|
||||
assert_select 'entry', :count => 20
|
||||
end
|
||||
end
|
||||
|
||||
def test_index_atom_feed_with_explicit_selection
|
||||
get :index, :params => {
|
||||
:format => 'atom',
|
||||
|
|
|
@ -531,6 +531,23 @@ class AttachmentsControllerTest < Redmine::ControllerTest
|
|||
assert_response 403
|
||||
end
|
||||
|
||||
def test_edit_all_issue_attachment_by_user_without_edit_issue_permission_on_tracker_should_return_404
|
||||
role = Role.find(2)
|
||||
role.set_permission_trackers 'edit_issues', [2, 3]
|
||||
role.save!
|
||||
|
||||
@request.session[:user_id] = 2
|
||||
|
||||
get(
|
||||
:edit_all,
|
||||
:params => {
|
||||
:object_type => 'issues',
|
||||
:object_id => '4'
|
||||
}
|
||||
)
|
||||
assert_response 404
|
||||
end
|
||||
|
||||
def test_update_all
|
||||
@request.session[:user_id] = 2
|
||||
patch :update_all, :params => {
|
||||
|
@ -659,4 +676,25 @@ class AttachmentsControllerTest < Redmine::ControllerTest
|
|||
assert_response 302
|
||||
assert Attachment.find_by_id(3)
|
||||
end
|
||||
|
||||
def test_destroy_issue_attachment_by_user_without_edit_issue_permission_on_tracker
|
||||
role = Role.find(2)
|
||||
role.set_permission_trackers 'edit_issues', [2, 3]
|
||||
role.save!
|
||||
|
||||
@request.session[:user_id] = 2
|
||||
|
||||
set_tmp_attachments_directory
|
||||
assert_no_difference 'Attachment.count' do
|
||||
delete(
|
||||
:destroy,
|
||||
:params => {
|
||||
:id => 7
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
assert_response 403
|
||||
assert Attachment.find_by_id(7)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -124,6 +124,49 @@ class IssuesControllerTest < Redmine::ControllerTest
|
|||
assert_select 'a[href="/issues/6"]', 0
|
||||
end
|
||||
|
||||
def test_index_should_list_issues_of_closed_subprojects
|
||||
@request.session[:user_id] = 1
|
||||
project = Project.find(1)
|
||||
|
||||
with_settings :display_subprojects_issues => '1' do
|
||||
# One of subprojects is closed
|
||||
Project.find_by(:identifier => 'subproject1').close
|
||||
get(:index, :params => {:project_id => project.id})
|
||||
assert_response :success
|
||||
assert_equal 10, issues_in_list.count
|
||||
|
||||
# All subprojects are closed
|
||||
project.descendants.each(&:close)
|
||||
get(:index, :params => {:project_id => project.id})
|
||||
assert_response :success
|
||||
assert_equal 10, issues_in_list.count
|
||||
end
|
||||
end
|
||||
|
||||
def test_index_with_subproject_filter_should_not_exclude_closed_subprojects_issues
|
||||
subproject1 = Project.find(3)
|
||||
subproject2 = Project.find(4)
|
||||
subproject1.close
|
||||
|
||||
with_settings :display_subprojects_issues => '1' do
|
||||
get(
|
||||
:index,
|
||||
:params => {
|
||||
:project_id => 1,
|
||||
:set_filter => 1,
|
||||
:f => ['subproject_id'],
|
||||
:op => {'subproject_id' => '!'},
|
||||
:v => {'subproject_id' => [subproject2.id.to_s]},
|
||||
:c => ['project']
|
||||
}
|
||||
)
|
||||
end
|
||||
assert_response :success
|
||||
column_values = columns_values_in_list('project')
|
||||
assert_includes column_values, subproject1.name
|
||||
assert_equal 9, column_values.size
|
||||
end
|
||||
|
||||
def test_index_with_project_and_subprojects_should_show_private_subprojects_with_permission
|
||||
@request.session[:user_id] = 2
|
||||
Setting.display_subprojects_issues = 1
|
||||
|
@ -1664,6 +1707,22 @@ class IssuesControllerTest < Redmine::ControllerTest
|
|||
assert_select '#content a.new-issue[href="/issues/new"]', :text => 'New issue'
|
||||
end
|
||||
|
||||
def test_index_should_show_setting_link_with_edit_project_permission
|
||||
role = Role.find(1)
|
||||
role.add_permission! :edit_project
|
||||
@request.session[:user_id] = 2
|
||||
get(:index, :params => {:project_id => 1})
|
||||
assert_select '#content a.icon-settings[href="/projects/ecookbook/settings/issues"]', 1
|
||||
end
|
||||
|
||||
def test_index_should_not_show_setting_link_without_edit_project_permission
|
||||
role = Role.find(1)
|
||||
role.remove_permission! :edit_project
|
||||
@request.session[:user_id] = 2
|
||||
get(:index, :params => {:project_id => 1})
|
||||
assert_select '#content a.icon-settings[href="/projects/ecookbook/settings/issues"]', 0
|
||||
end
|
||||
|
||||
def test_index_should_not_include_new_issue_tab_when_disabled
|
||||
with_settings :new_item_menu_tab => '0' do
|
||||
@request.session[:user_id] = 2
|
||||
|
@ -1720,6 +1779,22 @@ class IssuesControllerTest < Redmine::ControllerTest
|
|||
end
|
||||
end
|
||||
|
||||
def test_index_should_respect_timespan_format
|
||||
with_settings :timespan_format => 'minutes' do
|
||||
get(
|
||||
:index,
|
||||
:params => {
|
||||
:set_filter => 1,
|
||||
:c => %w(estimated_hours total_estimated_hours spent_hours total_spent_hours)
|
||||
}
|
||||
)
|
||||
assert_select 'table.issues tr#issue-1 td.estimated_hours', :text => '200:00'
|
||||
assert_select 'table.issues tr#issue-1 td.total_estimated_hours', :text => '200:00'
|
||||
assert_select 'table.issues tr#issue-1 td.spent_hours', :text => '154:15'
|
||||
assert_select 'table.issues tr#issue-1 td.total_spent_hours', :text => '154:15'
|
||||
end
|
||||
end
|
||||
|
||||
def test_show_by_anonymous
|
||||
get :show, :params => {
|
||||
:id => 1
|
||||
|
@ -2615,6 +2690,32 @@ class IssuesControllerTest < Redmine::ControllerTest
|
|||
end
|
||||
end
|
||||
|
||||
def test_show_should_not_display_edit_attachment_icon_for_user_without_edit_issue_permission_on_tracker
|
||||
role = Role.find(2)
|
||||
role.set_permission_trackers 'edit_issues', [2, 3]
|
||||
role.save!
|
||||
|
||||
@request.session[:user_id] = 2
|
||||
|
||||
get :show, params: {id: 4}
|
||||
|
||||
assert_response :success
|
||||
assert_select 'div.attachments .icon-edit', 0
|
||||
end
|
||||
|
||||
def test_show_should_not_display_delete_attachment_icon_for_user_without_edit_issue_permission_on_tracker
|
||||
role = Role.find(2)
|
||||
role.set_permission_trackers 'edit_issues', [2, 3]
|
||||
role.save!
|
||||
|
||||
@request.session[:user_id] = 2
|
||||
|
||||
get :show, params: {id: 4}
|
||||
|
||||
assert_response :success
|
||||
assert_select 'div.attachments .icon-del', 0
|
||||
end
|
||||
|
||||
def test_get_new
|
||||
@request.session[:user_id] = 2
|
||||
get :new, :params => {
|
||||
|
@ -4816,6 +4917,41 @@ class IssuesControllerTest < Redmine::ControllerTest
|
|||
end
|
||||
end
|
||||
|
||||
def test_get_edit_should_display_visible_spent_time_custom_field
|
||||
@request.session[:user_id] = 2
|
||||
|
||||
get(
|
||||
:edit,
|
||||
:params => {
|
||||
:id => 13,
|
||||
}
|
||||
)
|
||||
|
||||
assert_response :success
|
||||
|
||||
assert_select '#issue-form select#time_entry_custom_field_values_10', 1
|
||||
end
|
||||
|
||||
def test_get_edit_should_not_display_spent_time_custom_field_not_visible
|
||||
cf = TimeEntryCustomField.find(10)
|
||||
cf.visible = false
|
||||
cf.role_ids = [1]
|
||||
cf.save!
|
||||
|
||||
@request.session[:user_id] = 2
|
||||
|
||||
get(
|
||||
:edit,
|
||||
:params => {
|
||||
:id => 13,
|
||||
}
|
||||
)
|
||||
|
||||
assert_response :success
|
||||
|
||||
assert_select '#issue-form select#time_entry_custom_field_values_10', 0
|
||||
end
|
||||
|
||||
def test_update_form_for_existing_issue
|
||||
@request.session[:user_id] = 2
|
||||
patch :edit, :params => {
|
||||
|
@ -5222,6 +5358,24 @@ class IssuesControllerTest < Redmine::ControllerTest
|
|||
assert_equal spent_hours_before + 2.5, issue.spent_hours
|
||||
end
|
||||
|
||||
def test_put_update_should_check_add_issue_notes_permission
|
||||
role = Role.find(1)
|
||||
role.remove_permission! :add_issue_notes
|
||||
@request.session[:user_id] = 2
|
||||
|
||||
assert_no_difference 'Journal.count' do
|
||||
put(
|
||||
:update,
|
||||
:params => {
|
||||
:id => 1,
|
||||
:issue => {
|
||||
:notes => 'New note'
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def test_put_update_should_preserve_parent_issue_even_if_not_visible
|
||||
parent = Issue.generate!(:project_id => 1, :is_private => true)
|
||||
issue = Issue.generate!(:parent_issue_id => parent.id)
|
||||
|
@ -5528,6 +5682,29 @@ class IssuesControllerTest < Redmine::ControllerTest
|
|||
assert_select 'input[name=?][value=?]', 'time_entry[comments]', 'this is my comment'
|
||||
end
|
||||
|
||||
def test_put_with_spent_time_when_assigned_to_of_private_issue_is_update_at_the_same_time
|
||||
@request.session[:user_id] = 3
|
||||
Role.find(2).update! :issues_visibility => 'own'
|
||||
private_issue = Issue.find(3)
|
||||
|
||||
assert_difference('TimeEntry.count', 1) do
|
||||
put(
|
||||
:update,
|
||||
params: {
|
||||
id: private_issue.id,
|
||||
issue: { assigned_to_id: nil },
|
||||
time_entry: {
|
||||
comments: "add spent time", activity_id: TimeEntryActivity.first.id, hours: 1
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
assert_select '#errorExplanation', {text: /Log time is invalid/, count: 0}
|
||||
assert_select '#errorExplanation', {text: /Issue is invalid/, count: 0}
|
||||
assert_redirected_to action: 'show', id: private_issue.id
|
||||
assert_not private_issue.reload.visible?
|
||||
end
|
||||
|
||||
def test_put_update_should_allow_fixed_version_to_be_set_to_a_subproject
|
||||
issue = Issue.find(2)
|
||||
@request.session[:user_id] = 2
|
||||
|
|
|
@ -818,4 +818,28 @@ class QueriesControllerTest < Redmine::ControllerTest
|
|||
assert_include ["Dave Lopper", "3", "active"], json
|
||||
assert_include ["Dave2 Lopper2", "5", "locked"], json
|
||||
end
|
||||
|
||||
def test_activity_filter_should_return_active_and_system_activity_ids
|
||||
TimeEntryActivity.create!(:name => 'Design', :parent_id => 9, :project_id => 1)
|
||||
TimeEntryActivity.create!(:name => 'QA', :active => false, :parent_id => 11, :project_id => 1)
|
||||
TimeEntryActivity.create!(:name => 'Inactive Activity', :active => true, :parent_id => 14, :project_id => 1)
|
||||
|
||||
@request.session[:user_id] = 2
|
||||
get(
|
||||
:filter,
|
||||
:params => {
|
||||
:project_id => 1,
|
||||
:type => 'TimeEntryQuery',
|
||||
:name => 'activity_id'
|
||||
}
|
||||
)
|
||||
assert_response :success
|
||||
assert_equal 'application/json', response.media_type
|
||||
json = ActiveSupport::JSON.decode(response.body)
|
||||
|
||||
assert_equal 3, json.count
|
||||
assert_include ["Design", "9"], json
|
||||
assert_include ["Development", "10"], json
|
||||
assert_include ["Inactive Activity", "14"], json
|
||||
end
|
||||
end
|
||||
|
|
|
@ -428,4 +428,19 @@ class SearchControllerTest < Redmine::ControllerTest
|
|||
assert_select 'dd span.highlight', :text => 'highlighted'
|
||||
end
|
||||
end
|
||||
|
||||
def test_search_should_exclude_empty_modules_params
|
||||
@request.session[:user_id] = 1
|
||||
|
||||
get :index, params: {
|
||||
q: "private",
|
||||
scope: "all",
|
||||
issues: "1",
|
||||
projects: nil
|
||||
}
|
||||
|
||||
assert_response :success
|
||||
|
||||
assert_select '#search-results dt.project', 0
|
||||
end
|
||||
end
|
||||
|
|
|
@ -226,7 +226,7 @@ class TimelogControllerTest < Redmine::ControllerTest
|
|||
assert_response :success
|
||||
|
||||
assert_select 'select[name=?]', 'time_entry[user_id]' do
|
||||
assert_select 'option[value="2"][selected=selected]'
|
||||
assert_select 'option[value="2"][selected=selected]', 1
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -237,6 +237,7 @@ class UsersControllerTest < Redmine::ControllerTest
|
|||
get :new
|
||||
assert_response :success
|
||||
assert_select 'input[name=?]', 'user[login]'
|
||||
assert_select 'label[for=?]>span.required', 'user_password', 1
|
||||
end
|
||||
|
||||
def test_create
|
||||
|
@ -427,6 +428,7 @@ class UsersControllerTest < Redmine::ControllerTest
|
|||
assert_response :success
|
||||
assert_select 'h2>a+img.gravatar'
|
||||
assert_select 'input[name=?][value=?]', 'user[login]', 'jsmith'
|
||||
assert_select 'label[for=?]>span.required', 'user_password', 0
|
||||
end
|
||||
|
||||
def test_edit_registered_user
|
||||
|
@ -708,6 +710,19 @@ class UsersControllerTest < Redmine::ControllerTest
|
|||
assert_response 404
|
||||
end
|
||||
|
||||
def test_update_with_blank_email_should_not_raise_exception
|
||||
assert_no_difference 'User.count' do
|
||||
with_settings :gravatar_enabled => '1' do
|
||||
put :update, :params => {
|
||||
:id => 2,
|
||||
:user => {:mail => ''}
|
||||
}
|
||||
end
|
||||
end
|
||||
assert_response :success
|
||||
assert_select_error /Email cannot be blank/
|
||||
end
|
||||
|
||||
def test_destroy
|
||||
assert_difference 'User.count', -1 do
|
||||
delete :destroy, :params => {:id => 2}
|
||||
|
|
|
@ -160,6 +160,12 @@ class WikiControllerTest < Redmine::ControllerTest
|
|||
assert_select 'select[name=?] option[value="2"][selected=selected]', 'wiki_page[parent_id]'
|
||||
end
|
||||
|
||||
def test_show_unexistent_version_page
|
||||
@request.session[:user_id] = 2
|
||||
get :show, :params => {:project_id => 1, :id => 'CookBook_documentation', :version => 100}
|
||||
assert_response 404
|
||||
end
|
||||
|
||||
def test_show_should_not_show_history_without_permission
|
||||
Role.anonymous.remove_permission! :view_wiki_edits
|
||||
get :show, :params => {:project_id => 1, :id => 'Page with sections', :version => 2}
|
||||
|
|
|
@ -125,7 +125,7 @@ class WorkflowsControllerTest < Redmine::ControllerTest
|
|||
assert_select 'table.workflows.transitions-always tbody tr:nth-child(2)' do
|
||||
assert_select 'td.name', :text => 'New'
|
||||
# assert that the td is enabled
|
||||
assert_select "td[title='New » New'][class=?]", 'enabled'
|
||||
assert_select "td.enabled[title='New » New']"
|
||||
# assert that the checkbox is disabled and checked
|
||||
assert_select "input[name='transitions[1][1][always]'][checked=?][disabled=?]", 'checked', 'disabled', 1
|
||||
end
|
||||
|
|
|
@ -456,6 +456,7 @@ class ApplicationHelperTest < Redmine::HelperTest
|
|||
'user:JSMITH' => link_to_user(User.find_by_id(2)),
|
||||
'user#2' => link_to_user(User.find_by_id(2)),
|
||||
'@jsmith' => link_to_user(User.find_by_id(2)),
|
||||
'@jsmith.' => "#{link_to_user(User.find_by_id(2))}.",
|
||||
'@JSMITH' => link_to_user(User.find_by_id(2)),
|
||||
'@abcd@example.com' => link_to_user(User.find_by_id(u_email_id)),
|
||||
'user:abcd@example.com' => link_to_user(User.find_by_id(u_email_id)),
|
||||
|
|
|
@ -143,12 +143,20 @@ class IssuesHelperTest < Redmine::HelperTest
|
|||
end
|
||||
|
||||
test 'show_detail should show old and new values with a project attribute' do
|
||||
User.current = User.find(2)
|
||||
detail = JournalDetail.new(:property => 'attr', :prop_key => 'project_id',
|
||||
:old_value => 1, :value => 2)
|
||||
assert_match 'eCookbook', show_detail(detail, true)
|
||||
assert_match 'OnlineStore', show_detail(detail, true)
|
||||
end
|
||||
|
||||
test 'show_detail with a project attribute should show project ID if project is not visible' do
|
||||
detail = JournalDetail.new(:property => 'attr', :prop_key => 'project_id',
|
||||
:old_value => 1, :value => 2)
|
||||
assert_match 'eCookbook', show_detail(detail, true)
|
||||
assert_match '2', show_detail(detail, true)
|
||||
end
|
||||
|
||||
test 'show_detail should show old and new values with a issue status attribute' do
|
||||
detail = JournalDetail.new(:property => 'attr', :prop_key => 'status_id',
|
||||
:old_value => 1, :value => 2)
|
||||
|
@ -352,4 +360,26 @@ class IssuesHelperTest < Redmine::HelperTest
|
|||
assert_equal '06/06/2019', issue_due_date_details(issue)
|
||||
end
|
||||
end
|
||||
|
||||
def test_issue_spent_hours_details_should_link_to_project_time_entries_depending_on_cross_project_setting
|
||||
%w(descendants).each do |setting|
|
||||
with_settings :cross_project_subtasks => setting do
|
||||
TimeEntry.generate!(:issue => Issue.generate!(:parent_issue_id => 1), :hours => 3)
|
||||
TimeEntry.generate!(:issue => Issue.generate!(:parent_issue_id => 1), :hours => 4)
|
||||
|
||||
assert_match "href=\"/projects/ecookbook/time_entries?issue_id=~1\"", CGI.unescape(issue_spent_hours_details(Issue.find(1)))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_issue_spent_hours_details_should_link_to_global_time_entries_depending_on_cross_project_setting
|
||||
%w(system tree hierarchy).each do |setting|
|
||||
with_settings :cross_project_subtasks => setting do
|
||||
TimeEntry.generate!(:issue => Issue.generate!(:parent_issue_id => 1), :hours => 3)
|
||||
TimeEntry.generate!(:issue => Issue.generate!(:parent_issue_id => 1), :hours => 4)
|
||||
|
||||
assert_match "href=\"/time_entries?issue_id=~1\"", CGI.unescape(issue_spent_hours_details(Issue.find(1)))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,4 +48,30 @@ class JournalsHelperTest < Redmine::HelperTest
|
|||
assert_kind_of Attachment, thumbnails.first
|
||||
assert_equal 'image.png', thumbnails.first.filename
|
||||
end
|
||||
|
||||
def test_journal_thumbnail_attachments_should_be_in_the_same_order_as_the_journal_details
|
||||
skip unless convert_installed?
|
||||
set_tmp_attachments_directory
|
||||
issue = Issue.generate!
|
||||
|
||||
# Thumbnails should be displayed in the same order as Journal.detail, not in attachment id order.
|
||||
attachment1 = Attachment.generate!(:file => mock_file_with_options(:original_filename => 'image1.png'), :author => User.find(1))
|
||||
attachment2 = Attachment.generate!(:file => mock_file_with_options(:original_filename => 'image2.png'), :author => User.find(1))
|
||||
journal = Journal.create!(:journalized => issue, :user_id => 1)
|
||||
JournalDetail.create!(
|
||||
:journal => journal, :property => 'attachment',
|
||||
:prop_key => attachment2.id.to_s,
|
||||
:value => 'image2.png'
|
||||
)
|
||||
JournalDetail.create!(
|
||||
:journal => journal, :property => 'attachment',
|
||||
:prop_key => attachment1.id.to_s,
|
||||
:value => 'image1.png'
|
||||
)
|
||||
journal.reload
|
||||
thumbnails = journal_thumbnail_attachments(journal)
|
||||
assert_equal 2, thumbnails.count
|
||||
assert_equal 2, journal.details.count
|
||||
assert_equal journal.details.map(&:value), thumbnails.map(&:filename)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -229,4 +229,51 @@ class Redmine::ApiTest::AttachmentsTest < Redmine::ApiTest::Base
|
|||
assert attachment.digest.present?
|
||||
assert File.exist? attachment.diskfile
|
||||
end
|
||||
|
||||
test "POST /uploads.json should be compatible with an fcgi's input" do
|
||||
set_tmp_attachments_directory
|
||||
assert_difference 'Attachment.count' do
|
||||
post(
|
||||
'/uploads.json',
|
||||
:headers => {
|
||||
"CONTENT_TYPE" => 'application/octet-stream',
|
||||
"CONTENT_LENGTH" => '12',
|
||||
"rack.input" => Rack::RewindableInput.new(StringIO.new('File content'))
|
||||
}.merge(credentials('jsmith'))
|
||||
)
|
||||
assert_response :created
|
||||
end
|
||||
json = ActiveSupport::JSON.decode(response.body)
|
||||
assert_kind_of Hash, json['upload']
|
||||
token = json['upload']['token']
|
||||
assert token.present?
|
||||
assert attachment = Attachment.find_by_token(token)
|
||||
assert_equal 12, attachment.filesize
|
||||
assert File.exist? attachment.diskfile
|
||||
end
|
||||
|
||||
test "POST /uploads.json should be compatible with a uwsgi's input" do
|
||||
set_tmp_attachments_directory
|
||||
assert_difference 'Attachment.count' do
|
||||
request_body = Rack::RewindableInput.new(StringIO.new('File content'))
|
||||
# Uwsgi_IO object does not have size method
|
||||
request_body.instance_eval('undef :size', __FILE__, __LINE__)
|
||||
post(
|
||||
'/uploads.json',
|
||||
:headers => {
|
||||
"CONTENT_TYPE" => 'application/octet-stream',
|
||||
"CONTENT_LENGTH" => '12',
|
||||
"rack.input" => request_body
|
||||
}.merge(credentials('jsmith'))
|
||||
)
|
||||
assert_response :created
|
||||
end
|
||||
json = ActiveSupport::JSON.decode(response.body)
|
||||
assert_kind_of Hash, json['upload']
|
||||
token = json['upload']['token']
|
||||
assert token.present?
|
||||
assert attachment = Attachment.find_by_token(token)
|
||||
assert_equal 12, attachment.filesize
|
||||
assert File.exist? attachment.diskfile
|
||||
end
|
||||
end
|
||||
|
|
|
@ -653,6 +653,34 @@ class Redmine::ApiTest::IssuesTest < Redmine::ApiTest::Base
|
|||
assert_response 422
|
||||
end
|
||||
|
||||
test "POST /issues.json with invalid project_id and any assigned_to_id should respond with 422" do
|
||||
post(
|
||||
'/issues.json',
|
||||
:params => {
|
||||
:issue => {
|
||||
:project_id => 999,
|
||||
:assigned_to_id => 1,
|
||||
:subject => 'API'
|
||||
}
|
||||
},
|
||||
:headers => credentials('jsmith'))
|
||||
assert_response 422
|
||||
end
|
||||
|
||||
test "POST /issues.json with invalid project_id and any fixed_version_id should respond with 422" do
|
||||
post(
|
||||
'/issues.json',
|
||||
:params => {
|
||||
:issue => {
|
||||
:project_id => 999,
|
||||
:fixed_version_id => 1,
|
||||
:subject => 'API'
|
||||
}
|
||||
},
|
||||
:headers => credentials('jsmith'))
|
||||
assert_response 422
|
||||
end
|
||||
|
||||
test "PUT /issues/:id.xml" do
|
||||
assert_difference('Journal.count') do
|
||||
put(
|
||||
|
|
|
@ -119,6 +119,17 @@ class Redmine::ApiTest::WikiPagesTest < Redmine::ApiTest::Base
|
|||
assert_equal 'jsmith', page.content.author.login
|
||||
end
|
||||
|
||||
test "GET /projects/:project_id/wiki/:title/:version.xml should not includ author if not exists" do
|
||||
WikiContentVersion.find_by_id(2).update(author_id: nil)
|
||||
|
||||
get '/projects/ecookbook/wiki/CookBook_documentation/2.xml'
|
||||
assert_response 200
|
||||
assert_equal 'application/xml', response.media_type
|
||||
assert_select 'wiki_page' do
|
||||
assert_select 'author', 0
|
||||
end
|
||||
end
|
||||
|
||||
test "PUT /projects/:project_id/wiki/:title.xml with current versino should update wiki page" do
|
||||
assert_no_difference 'WikiPage.count' do
|
||||
assert_difference 'WikiContent::Version.count' do
|
||||
|
|
|
@ -29,5 +29,7 @@ class WelcomeTest < Redmine::IntegrationTest
|
|||
assert_equal 'text/plain', @response.content_type
|
||||
# Redmine::Utils.relative_url_root does not effect on Rails 5.1.4.
|
||||
assert @response.body.match(%r{^Disallow: /projects/ecookbook/issues\r?$})
|
||||
assert @response.body.match(%r{^Disallow: /issues\?sort=\r?$})
|
||||
assert @response.body.match(%r{^Disallow: /issues\?\*set_filter=\r?$})
|
||||
end
|
||||
end
|
||||
|
|
145
test/system/inline_autocomplete_test.rb
Normal file
145
test/system/inline_autocomplete_test.rb
Normal file
|
@ -0,0 +1,145 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2020 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
require File.expand_path('../../application_system_test_case', __FILE__)
|
||||
|
||||
class InlineAutocompleteSystemTest < ApplicationSystemTestCase
|
||||
fixtures :projects, :users, :email_addresses, :roles, :members, :member_roles,
|
||||
:trackers, :projects_trackers, :enabled_modules, :issue_statuses, :issues,
|
||||
:enumerations, :custom_fields, :custom_values, :custom_fields_trackers,
|
||||
:watchers, :journals, :journal_details, :versions,
|
||||
:workflows
|
||||
|
||||
def test_inline_autocomplete_for_issues
|
||||
log_user('jsmith', 'jsmith')
|
||||
visit 'issues/new'
|
||||
|
||||
fill_in 'Description', :with => '#'
|
||||
|
||||
within('.tribute-container') do
|
||||
assert page.has_text? 'Bug #12: Closed issue on a locked version'
|
||||
assert page.has_text? 'Bug #1: Cannot print recipes'
|
||||
|
||||
first('li').click
|
||||
end
|
||||
|
||||
assert_equal '#12 ', find('#issue_description').value
|
||||
end
|
||||
|
||||
def test_inline_autocomplete_filters_autocomplete_items
|
||||
log_user('jsmith', 'jsmith')
|
||||
visit 'issues/new'
|
||||
|
||||
fill_in 'Description', :with => '#Closed'
|
||||
|
||||
within('.tribute-container') do
|
||||
assert page.has_text? 'Bug #12: Closed issue on a locked version'
|
||||
assert page.has_text? 'Bug #11: Closed issue on a closed version'
|
||||
assert_not page.has_text? 'Bug #1: Cannot print recipes'
|
||||
end
|
||||
end
|
||||
|
||||
def test_inline_autocomplete_on_issue_edit_description_should_show_autocomplete
|
||||
log_user('jsmith', 'jsmith')
|
||||
visit 'issues/1/edit'
|
||||
|
||||
within('#issue-form') do
|
||||
click_link('Edit', match: :first)
|
||||
fill_in 'Description', :with => '#'
|
||||
end
|
||||
|
||||
page.has_css?('.tribute-container li', minimum: 1)
|
||||
end
|
||||
|
||||
def test_inline_autocomplete_on_issue_edit_notes_should_show_autocomplete
|
||||
log_user('jsmith', 'jsmith')
|
||||
visit 'issues/1/edit'
|
||||
|
||||
# Prevent random fails because the element is not yet enabled
|
||||
find('#issue_notes').click
|
||||
fill_in 'issue[notes]', :with => '#'
|
||||
|
||||
page.has_css?('.tribute-container li', minimum: 1)
|
||||
end
|
||||
|
||||
def test_inline_autocomplete_on_issue_custom_field_with_full_text_formatting_should_show_autocomplete
|
||||
IssueCustomField.create!(
|
||||
:name => 'Full width field',
|
||||
:field_format => 'text', :full_width_layout => '1',
|
||||
:tracker_ids => [1], :is_for_all => true, :text_formatting => 'full'
|
||||
)
|
||||
|
||||
log_user('jsmith', 'jsmith')
|
||||
visit 'issues/new'
|
||||
|
||||
fill_in 'Full width field', :with => '#'
|
||||
|
||||
page.has_css?('.tribute-container li', minimum: 1)
|
||||
end
|
||||
|
||||
def test_inline_autocomplete_on_wiki_should_show_autocomplete
|
||||
log_user('jsmith', 'jsmith')
|
||||
visit 'projects/ecookbook/wiki/CookBook_documentation/edit'
|
||||
|
||||
# Prevent random fails because the element is not yet enabled
|
||||
find('.wiki-edit').click
|
||||
fill_in 'content[text]', :with => '#'
|
||||
|
||||
page.has_css?('.tribute-container li', minimum: 1)
|
||||
end
|
||||
|
||||
def test_inline_autocomplete_on_news_description_should_show_autocomplete
|
||||
log_user('jsmith', 'jsmith')
|
||||
visit 'projects/ecookbook/news'
|
||||
|
||||
click_link 'Add news'
|
||||
|
||||
# Prevent random fails because the element is not yet enabled
|
||||
find('.wiki-edit').click
|
||||
fill_in 'Description', :with => '#'
|
||||
|
||||
page.has_css?('.tribute-container li', minimum: 1)
|
||||
end
|
||||
|
||||
def test_inline_autocomplete_on_new_message_description_should_show_autocomplete
|
||||
log_user('jsmith', 'jsmith')
|
||||
visit 'projects/ecookbook/boards/1'
|
||||
|
||||
click_link 'New message'
|
||||
|
||||
# Prevent random fails because the element is not yet enabled
|
||||
find('.wiki-edit').click
|
||||
fill_in 'message[content]', :with => '#'
|
||||
|
||||
page.has_css?('.tribute-container li', minimum: 1)
|
||||
end
|
||||
|
||||
def test_inline_autocomplete_for_issues_should_escape_html_elements
|
||||
issue = Issue.generate!(subject: 'This issue has a <select> element', project_id: 1, tracker_id: 1)
|
||||
|
||||
log_user('jsmith', 'jsmith')
|
||||
visit 'projects/1/issues/new'
|
||||
|
||||
fill_in 'Description', :with => '#This'
|
||||
|
||||
within('.tribute-container') do
|
||||
assert page.has_text? "Bug ##{issue.id}: This issue has a <select> element"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -152,6 +152,19 @@ class AttachmentTest < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_extension_update_should_be_validated_against_denied_extensions
|
||||
with_settings :attachment_extensions_denied => "txt, png" do
|
||||
a = Attachment.new(:container => Issue.find(1),
|
||||
:file => mock_file_with_options(:original_filename => "test.jpeg"),
|
||||
:author => User.find(1))
|
||||
assert_save a
|
||||
|
||||
b = Attachment.find(a.id)
|
||||
b.filename = "test.png"
|
||||
assert !b.save
|
||||
end
|
||||
end
|
||||
|
||||
def test_valid_extension_should_be_case_insensitive
|
||||
with_settings :attachment_extensions_allowed => "txt, Png" do
|
||||
assert Attachment.valid_extension?(".pnG")
|
||||
|
@ -235,6 +248,23 @@ class AttachmentTest < ActiveSupport::TestCase
|
|||
assert_not_equal a1.diskfile, a2.diskfile
|
||||
end
|
||||
|
||||
def test_identical_attachments_created_in_same_transaction_should_not_end_up_unreadable
|
||||
attachments = []
|
||||
Project.transaction do
|
||||
3.times do
|
||||
a = Attachment.create!(
|
||||
:container => Issue.find(1), :author => User.find(1),
|
||||
:file => mock_file(:filename => 'foo', :content => 'abcde')
|
||||
)
|
||||
attachments << a
|
||||
end
|
||||
end
|
||||
attachments.each do |a|
|
||||
assert a.readable?
|
||||
end
|
||||
assert_equal 1, attachments.map(&:diskfile).uniq.size
|
||||
end
|
||||
|
||||
def test_filename_should_be_basenamed
|
||||
a = Attachment.new(:file => mock_file(:original_filename => "path/to/the/file"))
|
||||
assert_equal 'file', a.filename
|
||||
|
|
|
@ -241,6 +241,16 @@ class IssueSubtaskingTest < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_done_ratio_of_parent_with_completed_children_should_not_be_99
|
||||
with_settings :parent_issue_done_ratio => 'derived' do
|
||||
parent = Issue.generate!
|
||||
parent.generate_child!(:estimated_hours => 8.0, :done_ratio => 100)
|
||||
parent.generate_child!(:estimated_hours => 8.1, :done_ratio => 100)
|
||||
# (8.0 * 100 + 8.1 * 100) / (8.0 + 8.1) => 99.99999999999999
|
||||
assert_equal 100, parent.reload.done_ratio
|
||||
end
|
||||
end
|
||||
|
||||
def test_changing_parent_should_update_previous_parent_done_ratio
|
||||
with_settings :parent_issue_done_ratio => 'derived' do
|
||||
first_parent = Issue.generate!
|
||||
|
|
|
@ -898,6 +898,23 @@ class IssueTest < ActiveSupport::TestCase
|
|||
assert_equal Date.parse('2012-07-14'), issue.due_date
|
||||
end
|
||||
|
||||
def test_safe_attributes_notes_should_check_add_issue_notes_permission
|
||||
# With add_issue_notes permission
|
||||
user = User.find(2)
|
||||
issue = Issue.new(:project => Project.find(1))
|
||||
issue.init_journal(user)
|
||||
issue.send :safe_attributes=, {'notes' => 'note'}, user
|
||||
assert_equal 'note', issue.notes
|
||||
|
||||
# Without add_issue_notes permission
|
||||
Role.find(1).remove_permission!(:add_issue_notes)
|
||||
issue = Issue.new(:project => Project.find(1))
|
||||
user.reload
|
||||
issue.init_journal(user)
|
||||
issue.send :safe_attributes=, {'notes' => 'note'}, user
|
||||
assert_equal '', issue.notes
|
||||
end
|
||||
|
||||
def test_safe_attributes_should_accept_target_tracker_enabled_fields
|
||||
source = Tracker.find(1)
|
||||
source.core_fields = []
|
||||
|
@ -1459,6 +1476,23 @@ class IssueTest < ActiveSupport::TestCase
|
|||
assert_equal [3, nil], copy.children.map(&:assigned_to_id)
|
||||
end
|
||||
|
||||
def test_copy_should_not_add_attachments_to_journal
|
||||
set_tmp_attachments_directory
|
||||
issue = Issue.generate!
|
||||
copy = Issue.new
|
||||
copy.init_journal User.find(1)
|
||||
copy.copy_from issue
|
||||
|
||||
copy.project = issue.project
|
||||
copy.save_attachments(
|
||||
{ 'p0' => {'file' => mock_file_with_options(:original_filename => 'upload')} }
|
||||
)
|
||||
assert copy.save
|
||||
assert j = copy.journals.last
|
||||
assert_equal 1, j.details.size
|
||||
assert_equal 'relation', j.details[0].property
|
||||
end
|
||||
|
||||
def test_should_not_call_after_project_change_on_creation
|
||||
issue = Issue.new(:project_id => 1, :tracker_id => 1, :status_id => 1,
|
||||
:subject => 'Test', :author_id => 1)
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
require File.expand_path('../../../../test_helper', __FILE__)
|
||||
|
||||
class Redmine::CipheringTest < ActiveSupport::TestCase
|
||||
fixtures :auth_sources
|
||||
|
||||
def test_password_should_be_encrypted
|
||||
Redmine::Configuration.with 'database_cipher_key' => 'secret' do
|
||||
|
@ -106,4 +107,12 @@ class Redmine::CipheringTest < ActiveSupport::TestCase
|
|||
assert_equal 'bar', r.read_attribute(:password)
|
||||
end
|
||||
end
|
||||
|
||||
def test_encrypt_all_and_decrypt_all_should_skip_validation
|
||||
auth_source = auth_sources(:auth_sources_001)
|
||||
# validator checks if AuthSource#host is present
|
||||
auth_source.update_column(:host, nil)
|
||||
assert AuthSource.encrypt_all(:account_password)
|
||||
assert AuthSource.decrypt_all(:account_password)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -44,6 +44,12 @@ class Redmine::Helpers::GanttHelperTest < Redmine::HelperTest
|
|||
def gantt_start
|
||||
@gantt.date_from
|
||||
end
|
||||
private :gantt_start
|
||||
|
||||
def gantt_end
|
||||
@gantt.date_to
|
||||
end
|
||||
private :gantt_end
|
||||
|
||||
# Creates a Gantt chart for a 4 week span
|
||||
def create_gantt(project=Project.generate!, options={})
|
||||
|
@ -354,6 +360,26 @@ class Redmine::Helpers::GanttHelperTest < Redmine::HelperTest
|
|||
assert_select 'div.task_todo[style*="left:28px"]', 1
|
||||
end
|
||||
|
||||
test "#line todo line should appear if it ends on the leftmost date in the gantt" do
|
||||
create_gantt
|
||||
[gantt_start - 1, gantt_start].each do |start_date|
|
||||
@output_buffer = @gantt.line(start_date, gantt_start, 30, false, 'line', :format => :html, :zoom => 4)
|
||||
# the leftmost date (Date.today - 14 days)
|
||||
assert_select 'div.task_todo[style*="left:0px"]', 1, @output_buffer
|
||||
assert_select 'div.task_todo[style*="width:2px"]', 1, @output_buffer
|
||||
end
|
||||
end
|
||||
|
||||
test "#line todo line should appear if it starts on the rightmost date in the gantt" do
|
||||
create_gantt
|
||||
[gantt_end, gantt_end + 1].each do |end_date|
|
||||
@output_buffer = @gantt.line(gantt_end, end_date, 30, false, 'line', :format => :html, :zoom => 4)
|
||||
# the rightmost date (Date.today + 14 days)
|
||||
assert_select 'div.task_todo[style*="left:112px"]', 1, @output_buffer
|
||||
assert_select 'div.task_todo[style*="width:2px"]', 1, @output_buffer
|
||||
end
|
||||
end
|
||||
|
||||
test "#line todo line should be the total width" do
|
||||
create_gantt
|
||||
@output_buffer = @gantt.line(today - 7, today + 7, 30, false, 'line', :format => :html, :zoom => 4)
|
||||
|
@ -424,6 +450,9 @@ class Redmine::Helpers::GanttHelperTest < Redmine::HelperTest
|
|||
@output_buffer = @gantt.line(today - 7, today + 7, 30, true, 'line', :format => :html, :zoom => 4)
|
||||
assert_select "div.starting", 1
|
||||
assert_select 'div.starting[style*="left:28px"]', 1
|
||||
# starting marker on the leftmost boundary of the gantt
|
||||
@output_buffer = @gantt.line(gantt_start, today + 7, 30, true, 'line', :format => :html, :zoom => 4)
|
||||
assert_select 'div.starting[style*="left:0px"]', 1
|
||||
end
|
||||
|
||||
test "#line starting marker should not appear if the start date is before gantt start date" do
|
||||
|
@ -437,6 +466,9 @@ class Redmine::Helpers::GanttHelperTest < Redmine::HelperTest
|
|||
@output_buffer = @gantt.line(today - 7, today + 7, 30, true, 'line', :format => :html, :zoom => 4)
|
||||
assert_select "div.ending", 1
|
||||
assert_select 'div.ending[style*="left:88px"]', 1
|
||||
# ending marker on the rightmost boundary of the gantt
|
||||
@output_buffer = @gantt.line(today - 7, gantt_end, 30, true, 'line', :format => :html, :zoom => 4)
|
||||
assert_select 'div.ending[style*="left:116px"]', 1
|
||||
end
|
||||
|
||||
test "#line ending marker should not appear if the end date is before gantt start date" do
|
||||
|
|
|
@ -23,7 +23,8 @@ class Redmine::ProjectJumpBoxTest < ActiveSupport::TestCase
|
|||
fixtures :users, :projects, :user_preferences
|
||||
|
||||
def setup
|
||||
@user = User.find_by_login 'dlopper'
|
||||
@user = User.find_by_login 'jsmith'
|
||||
User.current = @user
|
||||
@ecookbook = Project.find 'ecookbook'
|
||||
@onlinestore = Project.find 'onlinestore'
|
||||
end
|
||||
|
@ -142,4 +143,16 @@ class Redmine::ProjectJumpBoxTest < ActiveSupport::TestCase
|
|||
assert_equal @onlinestore, pjb.recently_used_projects.first
|
||||
assert_equal @ecookbook, pjb.recently_used_projects.last
|
||||
end
|
||||
|
||||
def test_recents_list_should_include_only_visible_projects
|
||||
@user = User.find_by_login 'dlopper'
|
||||
User.current = @user
|
||||
|
||||
pjb = Redmine::ProjectJumpBox.new @user
|
||||
pjb.project_used @ecookbook
|
||||
pjb.project_used @onlinestore
|
||||
|
||||
assert_equal 1, pjb.recently_used_projects.size
|
||||
assert_equal @ecookbook, pjb.recently_used_projects.first
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1005,6 +1005,18 @@ class MailHandlerTest < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_reply_to_an_issue_without_permission
|
||||
set_tmp_attachments_directory
|
||||
# "add_issue_notes" permission is explicit required to allow users to add notes
|
||||
# "edit_issue" permission no longer includes the "add_issue_notes" permission
|
||||
Role.all.each {|r| r.remove_permission! :add_issue_notes}
|
||||
assert_no_difference 'Issue.count' do
|
||||
assert_no_difference 'Journal.count' do
|
||||
assert_not submit_email('ticket_reply_with_status.eml')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_reply_to_a_nonexitent_journal
|
||||
journal_id = Issue.find(2).journals.last.id
|
||||
Journal.destroy(journal_id)
|
||||
|
@ -1056,6 +1068,13 @@ class MailHandlerTest < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_reply_to_a_topic_without_permission
|
||||
Role.all.each {|r| r.remove_permission! :add_messages}
|
||||
assert_no_difference('Message.count') do
|
||||
assert_not submit_email('message_reply_by_subject.eml')
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_convert_tags_of_html_only_emails
|
||||
with_settings :text_formatting => 'textile' do
|
||||
issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'})
|
||||
|
|
|
@ -344,6 +344,13 @@ class ProjectTest < ActiveSupport::TestCase
|
|||
assert_equal parent.children.sort_by(&:name), parent.children.to_a
|
||||
end
|
||||
|
||||
def test_validate_custom_field_values_of_project
|
||||
User.current = User.find(3)
|
||||
ProjectCustomField.generate!(:name => 'CustomFieldTest', :field_format => 'int', :is_required => true, :visible => false, :role_ids => [1])
|
||||
p = Project.new(:name => 'Project test', :identifier => 'project-t')
|
||||
assert p.save!
|
||||
end
|
||||
|
||||
def test_set_parent_should_update_issue_fixed_version_associations_when_a_fixed_version_is_moved_out_of_the_hierarchy
|
||||
# Parent issue with a hierarchy project's fixed version
|
||||
parent_issue = Issue.find(1)
|
||||
|
|
|
@ -1126,6 +1126,21 @@ class QueryTest < ActiveSupport::TestCase
|
|||
assert_equal [1, 3, 7, 8], find_issues_with_query(query).map(&:id).uniq.sort
|
||||
end
|
||||
|
||||
def test_filter_on_fixed_version_status_respects_sharing
|
||||
issue = Issue.generate!(:project_id => 1, :fixed_version_id => 7)
|
||||
|
||||
filter_name = "fixed_version.status"
|
||||
|
||||
query = IssueQuery.new(:name => '_', :project => Project.find(1))
|
||||
assert_include filter_name, query.available_filters.keys
|
||||
query.filters = {filter_name => {:operator => '=', :values => ['open']}}
|
||||
assert_include issue, find_issues_with_query(query)
|
||||
|
||||
query = IssueQuery.new(:name => '_', :project => Project.find(1))
|
||||
query.filters = {filter_name => {:operator => '=', :values => ['closed']}}
|
||||
assert_not_includes find_issues_with_query(query), issue
|
||||
end
|
||||
|
||||
def test_filter_on_version_custom_field
|
||||
field = IssueCustomField.generate!(:field_format => 'version', :is_filter => true)
|
||||
issue = Issue.generate!(:project_id => 1, :tracker_id => 1, :custom_field_values => {field.id.to_s => '2'})
|
||||
|
@ -1451,6 +1466,19 @@ class QueryTest < ActiveSupport::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_available_filters_as_json_should_not_include_duplicate_assigned_to_id_values
|
||||
set_language_if_valid 'en'
|
||||
user = User.find_by_login 'dlopper'
|
||||
with_current_user User.find(1) do
|
||||
q = IssueQuery.new
|
||||
q.filters = {"assigned_to_id" => {:operator => '=', :values => user.id.to_s}}
|
||||
|
||||
filters = q.available_filters_as_json
|
||||
assert_not_include [user.name, user.id.to_s], filters['assigned_to_id']['values']
|
||||
assert_include [user.name, user.id.to_s, 'active'], filters['assigned_to_id']['values']
|
||||
end
|
||||
end
|
||||
|
||||
def test_available_filters_as_json_should_include_missing_author_id_values
|
||||
user = User.generate!
|
||||
with_current_user User.find(1) do
|
||||
|
@ -1630,6 +1658,16 @@ class QueryTest < ActiveSupport::TestCase
|
|||
assert !q.sortable_columns['cf_1']
|
||||
end
|
||||
|
||||
def test_sortable_should_return_false_for_multi_custom_field
|
||||
field = CustomField.find(1)
|
||||
field.update_attribute :multiple, true
|
||||
|
||||
q = IssueQuery.new
|
||||
|
||||
field_column = q.available_columns.detect {|c| c.name==:cf_1}
|
||||
assert !field_column.sortable?
|
||||
end
|
||||
|
||||
def test_default_sort
|
||||
q = IssueQuery.new
|
||||
assert_equal [['id', 'desc']], q.sort_criteria
|
||||
|
@ -1800,6 +1838,37 @@ class QueryTest < ActiveSupport::TestCase
|
|||
assert_include "cf_#{field.id}".to_sym, q.available_totalable_columns.map(&:name)
|
||||
end
|
||||
|
||||
def test_available_totalable_columns_should_sort_in_position_order_for_custom_field
|
||||
ProjectCustomField.delete_all
|
||||
cf_pos3 = ProjectCustomField.generate!(:position => 3, :is_for_all => true, :field_format => 'int')
|
||||
cf_pos4 = ProjectCustomField.generate!(:position => 4, :is_for_all => true, :field_format => 'float')
|
||||
cf_pos1 = ProjectCustomField.generate!(:position => 1, :is_for_all => true, :field_format => 'float')
|
||||
cf_pos2 = ProjectCustomField.generate!(:position => 2, :is_for_all => true, :field_format => 'int')
|
||||
q = ProjectQuery.new
|
||||
custom_field_columns = q.available_totalable_columns.select{|column| column.is_a?(QueryCustomFieldColumn)}
|
||||
assert_equal [cf_pos1, cf_pos2, cf_pos3, cf_pos4], custom_field_columns.collect(&:custom_field)
|
||||
|
||||
IssueCustomField.delete_all
|
||||
cf_pos3 = IssueCustomField.generate!(:position => 3, :is_for_all => true, :field_format => 'int')
|
||||
cf_pos4 = IssueCustomField.generate!(:position => 4, :is_for_all => true, :field_format => 'float')
|
||||
cf_pos1 = IssueCustomField.generate!(:position => 1, :is_for_all => true, :field_format => 'float')
|
||||
cf_pos2 = IssueCustomField.generate!(:position => 2, :is_for_all => true, :field_format => 'int')
|
||||
q = IssueQuery.new
|
||||
custom_field_columns = q.available_totalable_columns.select{|column| column.is_a?(QueryCustomFieldColumn)}
|
||||
assert_equal [cf_pos1, cf_pos2, cf_pos3, cf_pos4], custom_field_columns.collect(&:custom_field)
|
||||
|
||||
ProjectCustomField.delete_all
|
||||
IssueCustomField.delete_all
|
||||
TimeEntryCustomField.delete_all
|
||||
cf_pos3 = TimeEntryCustomField.generate!(:position => 3, :is_for_all => true, :field_format => 'int')
|
||||
cf_pos4 = TimeEntryCustomField.generate!(:position => 4, :is_for_all => true, :field_format => 'float')
|
||||
cf_pos1 = TimeEntryCustomField.generate!(:position => 1, :is_for_all => true, :field_format => 'float')
|
||||
cf_pos2 = TimeEntryCustomField.generate!(:position => 2, :is_for_all => true, :field_format => 'int')
|
||||
q = TimeEntryQuery.new
|
||||
custom_field_columns = q.available_totalable_columns.select{|column| column.is_a?(QueryCustomFieldColumn)}
|
||||
assert_equal [cf_pos1, cf_pos2, cf_pos3, cf_pos4], custom_field_columns.collect(&:custom_field)
|
||||
end
|
||||
|
||||
def test_total_for_estimated_hours
|
||||
Issue.delete_all
|
||||
Issue.generate!(:estimated_hours => 5.5)
|
||||
|
@ -2370,6 +2439,17 @@ class QueryTest < ActiveSupport::TestCase
|
|||
ActiveRecord::Base.default_timezone = :local # restore Redmine default
|
||||
end
|
||||
|
||||
def test_project_statement_with_closed_subprojects
|
||||
project = Project.find(1)
|
||||
project.descendants.each(&:close)
|
||||
|
||||
with_settings :display_subprojects_issues => '1' do
|
||||
query = IssueQuery.new(:name => '_', :project => project)
|
||||
statement = query.project_statement
|
||||
assert_equal "projects.lft >= #{project.lft} AND projects.rgt <= #{project.rgt}", statement
|
||||
end
|
||||
end
|
||||
|
||||
def test_filter_on_subprojects
|
||||
query = IssueQuery.new(:name => '_', :project => Project.find(1))
|
||||
filter_name = "subproject_id"
|
||||
|
|
75
test/unit/time_entry_custom_field_test.rb
Normal file
75
test/unit/time_entry_custom_field_test.rb
Normal file
|
@ -0,0 +1,75 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# Redmine - project management software
|
||||
# Copyright (C) 2006-2020 Jean-Philippe Lang
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
require File.expand_path('../../test_helper', __FILE__)
|
||||
|
||||
class TimeEntryCustomFieldTest < ActiveSupport::TestCase
|
||||
include Redmine::I18n
|
||||
|
||||
fixtures :roles
|
||||
|
||||
def setup
|
||||
User.current = nil
|
||||
end
|
||||
|
||||
def test_custom_field_with_visible_set_to_false_should_validate_roles
|
||||
set_language_if_valid 'en'
|
||||
field = TimeEntryCustomField.new(:name => 'Field', :field_format => 'string', :visible => false)
|
||||
assert !field.save
|
||||
assert_include "Roles cannot be blank", field.errors.full_messages
|
||||
field.role_ids = [1, 2]
|
||||
assert field.save
|
||||
end
|
||||
|
||||
def test_changing_visible_to_true_should_clear_roles
|
||||
field = TimeEntryCustomField.create!(:name => 'Field', :field_format => 'string', :visible => false, :role_ids => [1, 2])
|
||||
assert_equal 2, field.roles.count
|
||||
|
||||
field.visible = true
|
||||
field.save!
|
||||
assert_equal 0, field.roles.count
|
||||
end
|
||||
|
||||
def test_safe_attributes_should_include_only_custom_fields_visible_to_user
|
||||
cf1 = TimeEntryCustomField.create!(:name => 'Visible field',
|
||||
:field_format => 'string',
|
||||
:visible => false, :role_ids => [1])
|
||||
cf2 = TimeEntryCustomField.create!(:name => 'Non visible field',
|
||||
:field_format => 'string',
|
||||
:visible => false, :role_ids => [3])
|
||||
user = User.find(2)
|
||||
time_entry = TimeEntry.new(:issue_id => 1)
|
||||
|
||||
time_entry.send :safe_attributes=, {'custom_field_values' => {
|
||||
cf1.id.to_s => 'value1',
|
||||
cf2.id.to_s => 'value2'
|
||||
}}, user
|
||||
|
||||
assert_equal 'value1', time_entry.custom_field_value(cf1)
|
||||
assert_nil time_entry.custom_field_value(cf2)
|
||||
|
||||
time_entry.send :safe_attributes=, {'custom_fields' => [
|
||||
{'id' => cf1.id.to_s, 'value' => 'valuea'},
|
||||
{'id' => cf2.id.to_s, 'value' => 'valueb'}
|
||||
]}, user
|
||||
|
||||
assert_equal 'valuea', time_entry.custom_field_value(cf1)
|
||||
assert_nil time_entry.custom_field_value(cf2)
|
||||
end
|
||||
end
|
|
@ -29,7 +29,8 @@ class TimeEntryTest < ActiveSupport::TestCase
|
|||
:journals, :journal_details,
|
||||
:issue_categories, :enumerations,
|
||||
:groups_users,
|
||||
:enabled_modules
|
||||
:enabled_modules,
|
||||
:custom_fields, :custom_fields_projects, :custom_values
|
||||
|
||||
def setup
|
||||
User.current = nil
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue