##// END OF EJS Templates
Merged r6201 from trunk....
Jean-Philippe Lang -
r6082:2a799e83675a
parent child
Show More
@@ -1,114 +1,114
1 # redMine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class SearchController < ApplicationController
18 class SearchController < ApplicationController
19 before_filter :find_optional_project
19 before_filter :find_optional_project
20
20
21 helper :messages
21 helper :messages
22 include MessagesHelper
22 include MessagesHelper
23
23
24 def index
24 def index
25 @question = params[:q] || ""
25 @question = params[:q] || ""
26 @question.strip!
26 @question.strip!
27 @all_words = params[:all_words] || (params[:submit] ? false : true)
27 @all_words = params[:all_words] ? params[:all_words].present? : true
28 @titles_only = !params[:titles_only].nil?
28 @titles_only = params[:titles_only] ? params[:titles_only].present? : false
29
29
30 projects_to_search =
30 projects_to_search =
31 case params[:scope]
31 case params[:scope]
32 when 'all'
32 when 'all'
33 nil
33 nil
34 when 'my_projects'
34 when 'my_projects'
35 User.current.memberships.collect(&:project)
35 User.current.memberships.collect(&:project)
36 when 'subprojects'
36 when 'subprojects'
37 @project ? (@project.self_and_descendants.active) : nil
37 @project ? (@project.self_and_descendants.active) : nil
38 else
38 else
39 @project
39 @project
40 end
40 end
41
41
42 offset = nil
42 offset = nil
43 begin; offset = params[:offset].to_time if params[:offset]; rescue; end
43 begin; offset = params[:offset].to_time if params[:offset]; rescue; end
44
44
45 # quick jump to an issue
45 # quick jump to an issue
46 if @question.match(/^#?(\d+)$/) && Issue.visible.find_by_id($1.to_i)
46 if @question.match(/^#?(\d+)$/) && Issue.visible.find_by_id($1.to_i)
47 redirect_to :controller => "issues", :action => "show", :id => $1
47 redirect_to :controller => "issues", :action => "show", :id => $1
48 return
48 return
49 end
49 end
50
50
51 @object_types = Redmine::Search.available_search_types.dup
51 @object_types = Redmine::Search.available_search_types.dup
52 if projects_to_search.is_a? Project
52 if projects_to_search.is_a? Project
53 # don't search projects
53 # don't search projects
54 @object_types.delete('projects')
54 @object_types.delete('projects')
55 # only show what the user is allowed to view
55 # only show what the user is allowed to view
56 @object_types = @object_types.select {|o| User.current.allowed_to?("view_#{o}".to_sym, projects_to_search)}
56 @object_types = @object_types.select {|o| User.current.allowed_to?("view_#{o}".to_sym, projects_to_search)}
57 end
57 end
58
58
59 @scope = @object_types.select {|t| params[t]}
59 @scope = @object_types.select {|t| params[t]}
60 @scope = @object_types if @scope.empty?
60 @scope = @object_types if @scope.empty?
61
61
62 # extract tokens from the question
62 # extract tokens from the question
63 # eg. hello "bye bye" => ["hello", "bye bye"]
63 # eg. hello "bye bye" => ["hello", "bye bye"]
64 @tokens = @question.scan(%r{((\s|^)"[\s\w]+"(\s|$)|\S+)}).collect {|m| m.first.gsub(%r{(^\s*"\s*|\s*"\s*$)}, '')}
64 @tokens = @question.scan(%r{((\s|^)"[\s\w]+"(\s|$)|\S+)}).collect {|m| m.first.gsub(%r{(^\s*"\s*|\s*"\s*$)}, '')}
65 # tokens must be at least 2 characters long
65 # tokens must be at least 2 characters long
66 @tokens = @tokens.uniq.select {|w| w.length > 1 }
66 @tokens = @tokens.uniq.select {|w| w.length > 1 }
67
67
68 if !@tokens.empty?
68 if !@tokens.empty?
69 # no more than 5 tokens to search for
69 # no more than 5 tokens to search for
70 @tokens.slice! 5..-1 if @tokens.size > 5
70 @tokens.slice! 5..-1 if @tokens.size > 5
71
71
72 @results = []
72 @results = []
73 @results_by_type = Hash.new {|h,k| h[k] = 0}
73 @results_by_type = Hash.new {|h,k| h[k] = 0}
74
74
75 limit = 10
75 limit = 10
76 @scope.each do |s|
76 @scope.each do |s|
77 r, c = s.singularize.camelcase.constantize.search(@tokens, projects_to_search,
77 r, c = s.singularize.camelcase.constantize.search(@tokens, projects_to_search,
78 :all_words => @all_words,
78 :all_words => @all_words,
79 :titles_only => @titles_only,
79 :titles_only => @titles_only,
80 :limit => (limit+1),
80 :limit => (limit+1),
81 :offset => offset,
81 :offset => offset,
82 :before => params[:previous].nil?)
82 :before => params[:previous].nil?)
83 @results += r
83 @results += r
84 @results_by_type[s] += c
84 @results_by_type[s] += c
85 end
85 end
86 @results = @results.sort {|a,b| b.event_datetime <=> a.event_datetime}
86 @results = @results.sort {|a,b| b.event_datetime <=> a.event_datetime}
87 if params[:previous].nil?
87 if params[:previous].nil?
88 @pagination_previous_date = @results[0].event_datetime if offset && @results[0]
88 @pagination_previous_date = @results[0].event_datetime if offset && @results[0]
89 if @results.size > limit
89 if @results.size > limit
90 @pagination_next_date = @results[limit-1].event_datetime
90 @pagination_next_date = @results[limit-1].event_datetime
91 @results = @results[0, limit]
91 @results = @results[0, limit]
92 end
92 end
93 else
93 else
94 @pagination_next_date = @results[-1].event_datetime if offset && @results[-1]
94 @pagination_next_date = @results[-1].event_datetime if offset && @results[-1]
95 if @results.size > limit
95 if @results.size > limit
96 @pagination_previous_date = @results[-(limit)].event_datetime
96 @pagination_previous_date = @results[-(limit)].event_datetime
97 @results = @results[-(limit), limit]
97 @results = @results[-(limit), limit]
98 end
98 end
99 end
99 end
100 else
100 else
101 @question = ""
101 @question = ""
102 end
102 end
103 render :layout => false if request.xhr?
103 render :layout => false if request.xhr?
104 end
104 end
105
105
106 private
106 private
107 def find_optional_project
107 def find_optional_project
108 return true unless params[:id]
108 return true unless params[:id]
109 @project = Project.find(params[:id])
109 @project = Project.find(params[:id])
110 check_project_privacy
110 check_project_privacy
111 rescue ActiveRecord::RecordNotFound
111 rescue ActiveRecord::RecordNotFound
112 render_404
112 render_404
113 end
113 end
114 end
114 end
@@ -1,64 +1,64
1 # redMine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 module SearchHelper
18 module SearchHelper
19 def highlight_tokens(text, tokens)
19 def highlight_tokens(text, tokens)
20 return text unless text && tokens && !tokens.empty?
20 return text unless text && tokens && !tokens.empty?
21 re_tokens = tokens.collect {|t| Regexp.escape(t)}
21 re_tokens = tokens.collect {|t| Regexp.escape(t)}
22 regexp = Regexp.new "(#{re_tokens.join('|')})", Regexp::IGNORECASE
22 regexp = Regexp.new "(#{re_tokens.join('|')})", Regexp::IGNORECASE
23 result = ''
23 result = ''
24 text.split(regexp).each_with_index do |words, i|
24 text.split(regexp).each_with_index do |words, i|
25 if result.length > 1200
25 if result.length > 1200
26 # maximum length of the preview reached
26 # maximum length of the preview reached
27 result << '...'
27 result << '...'
28 break
28 break
29 end
29 end
30 words = words.mb_chars
30 words = words.mb_chars
31 if i.even?
31 if i.even?
32 result << h(words.length > 100 ? "#{words.slice(0..44)} ... #{words.slice(-45..-1)}" : words)
32 result << h(words.length > 100 ? "#{words.slice(0..44)} ... #{words.slice(-45..-1)}" : words)
33 else
33 else
34 t = (tokens.index(words.downcase) || 0) % 4
34 t = (tokens.index(words.downcase) || 0) % 4
35 result << content_tag('span', h(words), :class => "highlight token-#{t}")
35 result << content_tag('span', h(words), :class => "highlight token-#{t}")
36 end
36 end
37 end
37 end
38 result
38 result
39 end
39 end
40
40
41 def type_label(t)
41 def type_label(t)
42 l("label_#{t.singularize}_plural", :default => t.to_s.humanize)
42 l("label_#{t.singularize}_plural", :default => t.to_s.humanize)
43 end
43 end
44
44
45 def project_select_tag
45 def project_select_tag
46 options = [[l(:label_project_all), 'all']]
46 options = [[l(:label_project_all), 'all']]
47 options << [l(:label_my_projects), 'my_projects'] unless User.current.memberships.empty?
47 options << [l(:label_my_projects), 'my_projects'] unless User.current.memberships.empty?
48 options << [l(:label_and_its_subprojects, @project.name), 'subprojects'] unless @project.nil? || @project.descendants.active.empty?
48 options << [l(:label_and_its_subprojects, @project.name), 'subprojects'] unless @project.nil? || @project.descendants.active.empty?
49 options << [@project.name, ''] unless @project.nil?
49 options << [@project.name, ''] unless @project.nil?
50 select_tag('scope', options_for_select(options, params[:scope].to_s)) if options.size > 1
50 select_tag('scope', options_for_select(options, params[:scope].to_s)) if options.size > 1
51 end
51 end
52
52
53 def render_results_by_type(results_by_type)
53 def render_results_by_type(results_by_type)
54 links = []
54 links = []
55 # Sorts types by results count
55 # Sorts types by results count
56 results_by_type.keys.sort {|a, b| results_by_type[b] <=> results_by_type[a]}.each do |t|
56 results_by_type.keys.sort {|a, b| results_by_type[b] <=> results_by_type[a]}.each do |t|
57 c = results_by_type[t]
57 c = results_by_type[t]
58 next if c == 0
58 next if c == 0
59 text = "#{type_label(t)} (#{c})"
59 text = "#{type_label(t)} (#{c})"
60 links << link_to(text, :q => params[:q], :titles_only => params[:title_only], :all_words => params[:all_words], :scope => params[:scope], t => 1)
60 links << link_to(text, :q => params[:q], :titles_only => params[:titles_only], :all_words => params[:all_words], :scope => params[:scope], t => 1)
61 end
61 end
62 ('<ul>' + links.map {|link| content_tag('li', link)}.join(' ') + '</ul>') unless links.empty?
62 ('<ul>' + links.map {|link| content_tag('li', link)}.join(' ') + '</ul>') unless links.empty?
63 end
63 end
64 end
64 end
@@ -1,47 +1,49
1 <h2><%= l(:label_search) %></h2>
1 <h2><%= l(:label_search) %></h2>
2
2
3 <div class="box">
3 <div class="box">
4 <% form_tag({}, :method => :get) do %>
4 <% form_tag({}, :method => :get) do %>
5 <p><%= text_field_tag 'q', @question, :size => 60, :id => 'search-input' %>
5 <p><%= text_field_tag 'q', @question, :size => 60, :id => 'search-input' %>
6 <%= javascript_tag "Field.focus('search-input')" %>
6 <%= javascript_tag "Field.focus('search-input')" %>
7 <%= project_select_tag %>
7 <%= project_select_tag %>
8 <%= hidden_field_tag 'all_words', '', :id => nil %>
8 <label><%= check_box_tag 'all_words', 1, @all_words %> <%= l(:label_all_words) %></label>
9 <label><%= check_box_tag 'all_words', 1, @all_words %> <%= l(:label_all_words) %></label>
10 <%= hidden_field_tag 'titles_only', '', :id => nil %>
9 <label><%= check_box_tag 'titles_only', 1, @titles_only %> <%= l(:label_search_titles_only) %></label>
11 <label><%= check_box_tag 'titles_only', 1, @titles_only %> <%= l(:label_search_titles_only) %></label>
10 </p>
12 </p>
11 <p>
13 <p>
12 <% @object_types.each do |t| %>
14 <% @object_types.each do |t| %>
13 <label><%= check_box_tag t, 1, @scope.include?(t) %> <%= type_label(t) %></label>
15 <label><%= check_box_tag t, 1, @scope.include?(t) %> <%= type_label(t) %></label>
14 <% end %>
16 <% end %>
15 </p>
17 </p>
16
18
17 <p><%= submit_tag l(:button_submit), :name => 'submit' %></p>
19 <p><%= submit_tag l(:button_submit), :name => 'submit' %></p>
18 <% end %>
20 <% end %>
19 </div>
21 </div>
20
22
21 <% if @results %>
23 <% if @results %>
22 <div id="search-results-counts">
24 <div id="search-results-counts">
23 <%= render_results_by_type(@results_by_type) unless @scope.size == 1 %>
25 <%= render_results_by_type(@results_by_type) unless @scope.size == 1 %>
24 </div>
26 </div>
25
27
26 <h3><%= l(:label_result_plural) %> (<%= @results_by_type.values.sum %>)</h3>
28 <h3><%= l(:label_result_plural) %> (<%= @results_by_type.values.sum %>)</h3>
27 <dl id="search-results">
29 <dl id="search-results">
28 <% @results.each do |e| %>
30 <% @results.each do |e| %>
29 <dt class="<%= e.event_type %>"><%= content_tag('span', h(e.project), :class => 'project') unless @project == e.project %> <%= link_to highlight_tokens(truncate(e.event_title, :length => 255), @tokens), e.event_url %></dt>
31 <dt class="<%= e.event_type %>"><%= content_tag('span', h(e.project), :class => 'project') unless @project == e.project %> <%= link_to highlight_tokens(truncate(e.event_title, :length => 255), @tokens), e.event_url %></dt>
30 <dd><span class="description"><%= highlight_tokens(e.event_description, @tokens) %></span>
32 <dd><span class="description"><%= highlight_tokens(e.event_description, @tokens) %></span>
31 <span class="author"><%= format_time(e.event_datetime) %></span></dd>
33 <span class="author"><%= format_time(e.event_datetime) %></span></dd>
32 <% end %>
34 <% end %>
33 </dl>
35 </dl>
34 <% end %>
36 <% end %>
35
37
36 <p><center>
38 <p><center>
37 <% if @pagination_previous_date %>
39 <% if @pagination_previous_date %>
38 <%= link_to_content_update('&#171; ' + l(:label_previous),
40 <%= link_to_content_update('&#171; ' + l(:label_previous),
39 params.merge(:previous => 1, :offset => @pagination_previous_date.strftime("%Y%m%d%H%M%S"))) %>&nbsp;
41 params.merge(:previous => 1, :offset => @pagination_previous_date.strftime("%Y%m%d%H%M%S"))) %>&nbsp;
40 <% end %>
42 <% end %>
41 <% if @pagination_next_date %>
43 <% if @pagination_next_date %>
42 <%= link_to_content_update(l(:label_next) + ' &#187;',
44 <%= link_to_content_update(l(:label_next) + ' &#187;',
43 params.merge(:previous => nil, :offset => @pagination_next_date.strftime("%Y%m%d%H%M%S"))) %>
45 params.merge(:previous => nil, :offset => @pagination_next_date.strftime("%Y%m%d%H%M%S"))) %>
44 <% end %>
46 <% end %>
45 </center></p>
47 </center></p>
46
48
47 <% html_title(l(:label_search)) -%>
49 <% html_title(l(:label_search)) -%>
@@ -1,147 +1,162
1 require File.expand_path('../../test_helper', __FILE__)
1 require File.expand_path('../../test_helper', __FILE__)
2 require 'search_controller'
2 require 'search_controller'
3
3
4 # Re-raise errors caught by the controller.
4 # Re-raise errors caught by the controller.
5 class SearchController; def rescue_action(e) raise e end; end
5 class SearchController; def rescue_action(e) raise e end; end
6
6
7 class SearchControllerTest < ActionController::TestCase
7 class SearchControllerTest < ActionController::TestCase
8 fixtures :projects, :enabled_modules, :roles, :users, :members, :member_roles,
8 fixtures :projects, :enabled_modules, :roles, :users, :members, :member_roles,
9 :issues, :trackers, :issue_statuses,
9 :issues, :trackers, :issue_statuses,
10 :custom_fields, :custom_values,
10 :custom_fields, :custom_values,
11 :repositories, :changesets
11 :repositories, :changesets
12
12
13 def setup
13 def setup
14 @controller = SearchController.new
14 @controller = SearchController.new
15 @request = ActionController::TestRequest.new
15 @request = ActionController::TestRequest.new
16 @response = ActionController::TestResponse.new
16 @response = ActionController::TestResponse.new
17 User.current = nil
17 User.current = nil
18 end
18 end
19
19
20 def test_search_for_projects
20 def test_search_for_projects
21 get :index
21 get :index
22 assert_response :success
22 assert_response :success
23 assert_template 'index'
23 assert_template 'index'
24
24
25 get :index, :q => "cook"
25 get :index, :q => "cook"
26 assert_response :success
26 assert_response :success
27 assert_template 'index'
27 assert_template 'index'
28 assert assigns(:results).include?(Project.find(1))
28 assert assigns(:results).include?(Project.find(1))
29 end
29 end
30
30
31 def test_search_all_projects
31 def test_search_all_projects
32 get :index, :q => 'recipe subproject commit', :submit => 'Search'
32 get :index, :q => 'recipe subproject commit', :all_words => ''
33 assert_response :success
33 assert_response :success
34 assert_template 'index'
34 assert_template 'index'
35
35
36 assert assigns(:results).include?(Issue.find(2))
36 assert assigns(:results).include?(Issue.find(2))
37 assert assigns(:results).include?(Issue.find(5))
37 assert assigns(:results).include?(Issue.find(5))
38 assert assigns(:results).include?(Changeset.find(101))
38 assert assigns(:results).include?(Changeset.find(101))
39 assert_tag :dt, :attributes => { :class => /issue/ },
39 assert_tag :dt, :attributes => { :class => /issue/ },
40 :child => { :tag => 'a', :content => /Add ingredients categories/ },
40 :child => { :tag => 'a', :content => /Add ingredients categories/ },
41 :sibling => { :tag => 'dd', :content => /should be classified by categories/ }
41 :sibling => { :tag => 'dd', :content => /should be classified by categories/ }
42
42
43 assert assigns(:results_by_type).is_a?(Hash)
43 assert assigns(:results_by_type).is_a?(Hash)
44 assert_equal 5, assigns(:results_by_type)['changesets']
44 assert_equal 5, assigns(:results_by_type)['changesets']
45 assert_tag :a, :content => 'Changesets (5)'
45 assert_tag :a, :content => 'Changesets (5)'
46 end
46 end
47
47
48 def test_search_issues
48 def test_search_issues
49 get :index, :q => 'issue', :issues => 1
49 get :index, :q => 'issue', :issues => 1
50 assert_response :success
50 assert_response :success
51 assert_template 'index'
51 assert_template 'index'
52
52
53 assert_equal true, assigns(:all_words)
54 assert_equal false, assigns(:titles_only)
53 assert assigns(:results).include?(Issue.find(8))
55 assert assigns(:results).include?(Issue.find(8))
54 assert assigns(:results).include?(Issue.find(5))
56 assert assigns(:results).include?(Issue.find(5))
55 assert_tag :dt, :attributes => { :class => /issue closed/ },
57 assert_tag :dt, :attributes => { :class => /issue closed/ },
56 :child => { :tag => 'a', :content => /Closed/ }
58 :child => { :tag => 'a', :content => /Closed/ }
57 end
59 end
58
60
59 def test_search_project_and_subprojects
61 def test_search_project_and_subprojects
60 get :index, :id => 1, :q => 'recipe subproject', :scope => 'subprojects', :submit => 'Search'
62 get :index, :id => 1, :q => 'recipe subproject', :scope => 'subprojects', :all_words => ''
61 assert_response :success
63 assert_response :success
62 assert_template 'index'
64 assert_template 'index'
63 assert assigns(:results).include?(Issue.find(1))
65 assert assigns(:results).include?(Issue.find(1))
64 assert assigns(:results).include?(Issue.find(5))
66 assert assigns(:results).include?(Issue.find(5))
65 end
67 end
66
68
67 def test_search_without_searchable_custom_fields
69 def test_search_without_searchable_custom_fields
68 CustomField.update_all "searchable = #{ActiveRecord::Base.connection.quoted_false}"
70 CustomField.update_all "searchable = #{ActiveRecord::Base.connection.quoted_false}"
69
71
70 get :index, :id => 1
72 get :index, :id => 1
71 assert_response :success
73 assert_response :success
72 assert_template 'index'
74 assert_template 'index'
73 assert_not_nil assigns(:project)
75 assert_not_nil assigns(:project)
74
76
75 get :index, :id => 1, :q => "can"
77 get :index, :id => 1, :q => "can"
76 assert_response :success
78 assert_response :success
77 assert_template 'index'
79 assert_template 'index'
78 end
80 end
79
81
80 def test_search_with_searchable_custom_fields
82 def test_search_with_searchable_custom_fields
81 get :index, :id => 1, :q => "stringforcustomfield"
83 get :index, :id => 1, :q => "stringforcustomfield"
82 assert_response :success
84 assert_response :success
83 results = assigns(:results)
85 results = assigns(:results)
84 assert_not_nil results
86 assert_not_nil results
85 assert_equal 1, results.size
87 assert_equal 1, results.size
86 assert results.include?(Issue.find(7))
88 assert results.include?(Issue.find(7))
87 end
89 end
88
90
89 def test_search_all_words
91 def test_search_all_words
90 # 'all words' is on by default
92 # 'all words' is on by default
91 get :index, :id => 1, :q => 'recipe updating saving'
93 get :index, :id => 1, :q => 'recipe updating saving', :all_words => '1'
94 assert_equal true, assigns(:all_words)
92 results = assigns(:results)
95 results = assigns(:results)
93 assert_not_nil results
96 assert_not_nil results
94 assert_equal 1, results.size
97 assert_equal 1, results.size
95 assert results.include?(Issue.find(3))
98 assert results.include?(Issue.find(3))
96 end
99 end
97
100
98 def test_search_one_of_the_words
101 def test_search_one_of_the_words
99 get :index, :id => 1, :q => 'recipe updating saving', :submit => 'Search'
102 get :index, :id => 1, :q => 'recipe updating saving', :all_words => ''
103 assert_equal false, assigns(:all_words)
100 results = assigns(:results)
104 results = assigns(:results)
101 assert_not_nil results
105 assert_not_nil results
102 assert_equal 3, results.size
106 assert_equal 3, results.size
103 assert results.include?(Issue.find(3))
107 assert results.include?(Issue.find(3))
104 end
108 end
105
109
106 def test_search_titles_only_without_result
110 def test_search_titles_only_without_result
107 get :index, :id => 1, :q => 'recipe updating saving', :all_words => '1', :titles_only => '1', :submit => 'Search'
111 get :index, :id => 1, :q => 'recipe updating saving', :titles_only => '1'
108 results = assigns(:results)
112 results = assigns(:results)
109 assert_not_nil results
113 assert_not_nil results
110 assert_equal 0, results.size
114 assert_equal 0, results.size
111 end
115 end
112
116
113 def test_search_titles_only
117 def test_search_titles_only
114 get :index, :id => 1, :q => 'recipe', :titles_only => '1', :submit => 'Search'
118 get :index, :id => 1, :q => 'recipe', :titles_only => '1'
119 assert_equal true, assigns(:titles_only)
115 results = assigns(:results)
120 results = assigns(:results)
116 assert_not_nil results
121 assert_not_nil results
117 assert_equal 2, results.size
122 assert_equal 2, results.size
118 end
123 end
119
124
125 def test_search_content
126 Issue.update_all("description = 'This is a searchkeywordinthecontent'", "id=1")
127
128 get :index, :id => 1, :q => 'searchkeywordinthecontent', :titles_only => ''
129 assert_equal false, assigns(:titles_only)
130 results = assigns(:results)
131 assert_not_nil results
132 assert_equal 1, results.size
133 end
134
120 def test_search_with_invalid_project_id
135 def test_search_with_invalid_project_id
121 get :index, :id => 195, :q => 'recipe'
136 get :index, :id => 195, :q => 'recipe'
122 assert_response 404
137 assert_response 404
123 assert_nil assigns(:results)
138 assert_nil assigns(:results)
124 end
139 end
125
140
126 def test_quick_jump_to_issue
141 def test_quick_jump_to_issue
127 # issue of a public project
142 # issue of a public project
128 get :index, :q => "3"
143 get :index, :q => "3"
129 assert_redirected_to '/issues/3'
144 assert_redirected_to '/issues/3'
130
145
131 # issue of a private project
146 # issue of a private project
132 get :index, :q => "4"
147 get :index, :q => "4"
133 assert_response :success
148 assert_response :success
134 assert_template 'index'
149 assert_template 'index'
135 end
150 end
136
151
137 def test_large_integer
152 def test_large_integer
138 get :index, :q => '4615713488'
153 get :index, :q => '4615713488'
139 assert_response :success
154 assert_response :success
140 assert_template 'index'
155 assert_template 'index'
141 end
156 end
142
157
143 def test_tokens_with_quotes
158 def test_tokens_with_quotes
144 get :index, :id => 1, :q => '"good bye" hello "bye bye"'
159 get :index, :id => 1, :q => '"good bye" hello "bye bye"'
145 assert_equal ["good bye", "hello", "bye bye"], assigns(:tokens)
160 assert_equal ["good bye", "hello", "bye bye"], assigns(:tokens)
146 end
161 end
147 end
162 end
General Comments 0
You need to be logged in to leave comments. Login now