bento_search 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/README.md +6 -5
  2. data/app/assets/javascripts/bento_search/ajax_load.js +42 -16
  3. data/app/assets/stylesheets/bento_search/suggested_styles.css +9 -0
  4. data/app/controllers/bento_search/search_controller.rb +15 -6
  5. data/app/helpers/bento_search_helper.rb +24 -8
  6. data/app/item_decorators/bento_search/no_links.rb +13 -0
  7. data/app/models/bento_search/openurl_creator.rb +18 -8
  8. data/app/models/bento_search/registrar.rb +2 -6
  9. data/app/models/bento_search/result_item.rb +43 -3
  10. data/app/models/bento_search/results.rb +4 -0
  11. data/app/models/bento_search/search_engine.rb +25 -23
  12. data/app/search_engines/bento_search/ebsco_host_engine.rb +42 -17
  13. data/app/search_engines/bento_search/google_books_engine.rb +2 -0
  14. data/app/search_engines/bento_search/google_site_search_engine.rb +177 -0
  15. data/app/search_engines/bento_search/mock_engine.rb +5 -0
  16. data/app/search_engines/bento_search/primo_engine.rb +23 -2
  17. data/app/search_engines/bento_search/scopus_engine.rb +4 -1
  18. data/app/search_engines/bento_search/summon_engine.rb +4 -14
  19. data/app/search_engines/bento_search/worldcat_sru_dc_engine.rb +293 -0
  20. data/app/views/bento_search/_std_item.html.erb +4 -5
  21. data/app/views/bento_search/_wrap_with_count.html.erb +20 -0
  22. data/app/views/bento_search/search/search.html.erb +15 -1
  23. data/config/locales/en.yml +6 -4
  24. data/lib/bento_search/util.rb +13 -0
  25. data/lib/bento_search/version.rb +1 -1
  26. data/test/dummy/log/development.log +1 -0
  27. data/test/dummy/log/test.log +24357 -0
  28. data/test/functional/bento_search/search_controller_test.rb +39 -0
  29. data/test/helper/bento_search_helper_test.rb +47 -5
  30. data/test/unit/ebsco_host_engine_test.rb +15 -0
  31. data/test/unit/google_books_engine_test.rb +1 -0
  32. data/test/unit/google_site_search_test.rb +122 -0
  33. data/test/unit/item_decorators_test.rb +12 -1
  34. data/test/unit/openurl_creator_test.rb +19 -3
  35. data/test/unit/primo_engine_test.rb +5 -3
  36. data/test/unit/result_item_test.rb +36 -1
  37. data/test/unit/search_engine_test.rb +27 -4
  38. data/test/unit/worldcat_sru_dc_engine_test.rb +120 -0
  39. data/test/vcr_cassettes/google_site/basic_smoke_test.yml +254 -0
  40. data/test/vcr_cassettes/google_site/empty_result_set.yml +53 -0
  41. data/test/vcr_cassettes/google_site/pagination_object_is_correct_for_actual_page_when_you_ask_for_too_far.yml +260 -0
  42. data/test/vcr_cassettes/google_site/with_highlighting.yml +265 -0
  43. data/test/vcr_cassettes/google_site/without_highlighting.yml +267 -0
  44. data/test/vcr_cassettes/primo/proper_tags_for_snippets.yml +517 -502
  45. data/test/vcr_cassettes/primo/search_smoke_test.yml +1 -1
  46. data/test/vcr_cassettes/worldcat_sru_dc/smoke_test.yml +628 -0
  47. metadata +40 -4
@@ -13,6 +13,17 @@ module BentoSearch
13
13
  # no allow_routable_results
14
14
  end
15
15
 
16
+ BentoSearch.register_engine("with_layout_config") do |config|
17
+ config.engine = "MockEngine"
18
+
19
+ config.allow_routable_results = true
20
+
21
+ config.for_display do |display|
22
+ display.ajax do |ajax|
23
+ ajax.wrapper_template = "bento_search/wrap_with_count"
24
+ end
25
+ end
26
+ end
16
27
  end
17
28
 
18
29
  def teardown
@@ -28,6 +39,17 @@ module BentoSearch
28
39
  assert_template "bento_search/search"
29
40
  end
30
41
 
42
+ test "custom layout config" do
43
+ get :search, {:engine_id => "with_layout_config", :query => "my search"}
44
+
45
+ assert_response :success
46
+
47
+ assert_not_nil assigns(:partial_wrapper)
48
+
49
+ assert_template "bento_search/_wrap_with_count"
50
+ assert_template "bento_search/search"
51
+ end
52
+
31
53
  test "non-routable engine" do
32
54
  get :search, {:engine_id => "not_routable", :query => "my search"}
33
55
 
@@ -40,6 +62,22 @@ module BentoSearch
40
62
  assert_response 404
41
63
  end
42
64
 
65
+
66
+ test "respects public_settable_search_args" do
67
+ get :search, {:engine_id => "mock",
68
+ 'query' => "query", 'sort' => "sort", 'per_page' => "15",
69
+ 'page' => "6", 'search_field' => "title", 'not_allowed' => "not allowed"}
70
+
71
+
72
+ search_args = assigns[:engine].last_args
73
+
74
+ [:query, :sort, :per_page, :page, :search_field].each do |allowed_key|
75
+ assert search_args.has_key?(allowed_key)
76
+ end
77
+ assert ! search_args.has_key?(:not_allowed)
78
+ assert ! search_args.has_key?("not_allowed")
79
+ end
80
+
43
81
  test "custom before filter" do
44
82
  # Okay, we're going to do a weird thing with a custom controller subclass
45
83
  # we can add a custom before filter like a local app might.
@@ -74,6 +112,7 @@ module BentoSearch
74
112
 
75
113
  end
76
114
 
115
+
77
116
 
78
117
 
79
118
 
@@ -1,5 +1,8 @@
1
1
  require 'test_helper'
2
2
 
3
+ # this seems to work? Rails view testing is a mess.
4
+ require 'sprockets/helpers/rails_helper'
5
+
3
6
  class BentoSearchHelperTest < ActionView::TestCase
4
7
  include BentoSearchHelper
5
8
 
@@ -8,13 +11,11 @@ class BentoSearchHelperTest < ActionView::TestCase
8
11
  def teardown
9
12
  BentoSearch.reset_engine_registrations!
10
13
  end
11
-
12
-
13
-
14
14
 
15
15
  def setup
16
16
  # Make routing work
17
- @routes = Rails.application.routes
17
+ @routes = Rails.application.routes
18
+
18
19
  end
19
20
 
20
21
  def test_with_results_arg
@@ -89,6 +90,8 @@ class BentoSearchHelperTest < ActionView::TestCase
89
90
 
90
91
  div = results.find(:attributes => {:class => "bento_search_ajax_wait"})
91
92
  assert div, "produces div.bento_search_ajax_wait"
93
+ assert_equal "ajax_auto", div["data-bento-search-load"], "has data-bento-search-load attribute"
94
+
92
95
 
93
96
  assert_present div.attributes["data-bento-ajax-url"]
94
97
  url = URI.parse(div.attributes["data-bento-ajax-url"])
@@ -97,12 +100,35 @@ class BentoSearchHelperTest < ActionView::TestCase
97
100
  query = CGI.parse(url.query.gsub("&amp;", "&")) # gsub weirdness of HTML::Tag
98
101
  assert_equal ["QUERY"], query["query"]
99
102
  assert_empty query["load"]
103
+
104
+ # hidden loading msg
105
+ loading_msg = div.find(:attributes => {:class => "bento_search_ajax_loading"})
106
+ assert_present loading_msg, "bento_search_ajax_loading present"
107
+ assert_match /display\:none/, loading_msg["style"], "loading has CSS style hidden"
100
108
 
101
109
  assert div.find(:tag => "noscript"), "has <noscript> tag"
102
110
 
103
- assert (img = div.find(:tag => "img")), "Has spinner gif"
111
+ assert (img = loading_msg.find(:tag => "img")), "Has spinner gif"
104
112
  assert_equal I18n.translate("bento_search.ajax_loading"), img.attributes["alt"]
105
113
  end
114
+
115
+ def test_ajax_triggered_load
116
+ BentoSearch.register_engine("test_engine") do |conf|
117
+ conf.engine = "MockEngine"
118
+ end
119
+
120
+ results = bento_search("test_engine", :query => "QUERY", :load => :ajax_triggered)
121
+ results = HTML::Document.new(results)
122
+
123
+ div = results.find(:attributes => {:class => "bento_search_ajax_wait"})
124
+ assert div, "produces div.bento_search_ajax_wait"
125
+ assert_equal "ajax_triggered", div["data-bento-search-load"], "has data-bento-search-load attribute"
126
+
127
+ # hidden loading msg
128
+ loading_msg = div.find(:attributes => {:class => "bento_search_ajax_loading"})
129
+ assert_present loading_msg, "bento_search_ajax_loading present"
130
+ assert_match /display\:none/, loading_msg["style"], "loading has CSS style hidden"
131
+ end
106
132
 
107
133
 
108
134
  def test_sort_hash_for
@@ -121,6 +147,22 @@ class BentoSearchHelperTest < ActionView::TestCase
121
147
  assert_equal key, hash[ I18n.translate(key, :scope => "bento_search.sort_keys") ]
122
148
  end
123
149
  end
150
+
151
+ def test_sort_hash_for_no_i18n
152
+ # If there's no 18n key available, use reasonable humanized approximation
153
+
154
+ engine = MockEngine.new(:sort_definitions => {"no_key_test" => {}})
155
+
156
+ hash = bento_sort_hash_for(engine)
157
+
158
+ assert_present hash
159
+
160
+ key = hash.key("no_key_test")
161
+
162
+ assert_no_match /translation missing/, key
163
+
164
+ assert_equal "No Key Test", key
165
+ end
124
166
 
125
167
 
126
168
  end
@@ -80,6 +80,18 @@ class EbscoHostEngineTest < ActiveSupport::TestCase
80
80
  assert_equal ["cancer "], query_params["query"]
81
81
  end
82
82
 
83
+ def test_removes_question_marks
84
+ # who knows why, ebsco doesn't like question marks even inside
85
+ # quoted phrases, some special char to ebsco.
86
+ url = @engine.query_url(:query => "cancer?", :sort => "date_desc")
87
+ query_params = CGI.parse( URI.parse(url).query )
88
+ assert_equal ["cancer "], query_params["query"]
89
+
90
+ url = @engine.query_url(:query => '"cancer?"', :sort => "date_desc")
91
+ query_params = CGI.parse( URI.parse(url).query )
92
+ assert_equal ['"cancer "'], query_params["query"]
93
+ end
94
+
83
95
  test_with_cassette("live search smoke test", :ebscohost) do
84
96
 
85
97
  results = @engine.search(:query => "cancer")
@@ -95,6 +107,9 @@ class EbscoHostEngineTest < ActiveSupport::TestCase
95
107
 
96
108
  assert_present first.format
97
109
  assert_present first.format_str
110
+
111
+ assert_present first.language_code
112
+ assert_present first.language_str
98
113
  end
99
114
 
100
115
  test_with_cassette("get_info", :ebscohost) do
@@ -35,6 +35,7 @@ class GoogleBooksEngineTest < ActiveSupport::TestCase
35
35
  assert_not_empty first.abstract
36
36
  assert first.abstract.html_safe?
37
37
 
38
+ assert_present first.language_code
38
39
 
39
40
  assert_not_empty first.authors
40
41
  assert_not_empty first.authors.first.display
@@ -0,0 +1,122 @@
1
+ require 'test_helper'
2
+
3
+ require 'uri'
4
+ require 'cgi'
5
+
6
+ # To run these tests without VCR cassettes, need
7
+ # ENV GOOGLE_SITE_SEARCH_KEY and GOOGLE_SITE_SEARCH_CX
8
+ class GoogleSiteSearchTest < ActiveSupport::TestCase
9
+ extend TestWithCassette
10
+
11
+ @@api_key = ENV["GOOGLE_SITE_SEARCH_KEY"] || "DUMMY_API_KEY"
12
+ @@cx = ENV["GOOGLE_SITE_SEARCH_CX"] || "DUMMY_CX"
13
+
14
+ VCR.configure do |c|
15
+ c.filter_sensitive_data("DUMMY_API_KEY", :google_site) { @@api_key }
16
+ c.filter_sensitive_data("DUMMY_CX", :google_site) { @@cx }
17
+ end
18
+
19
+ setup do
20
+ @config = {:api_key => @@api_key, :cx => @@cx}
21
+ @engine = BentoSearch::GoogleSiteSearchEngine.new(@config)
22
+ end
23
+
24
+ test("basic query construction") do
25
+ url = @engine.send(:construct_query, {:query => "hours policies"})
26
+
27
+ query_params = CGI.parse( URI.parse(url).query )
28
+
29
+ assert_equal ["hours policies"], query_params["q"]
30
+ end
31
+
32
+ test("pagination construction") do
33
+ url = @engine.send(:construct_query, {:query => "books", :per_page => 5, :start => 10})
34
+
35
+ query_params = CGI.parse( URI.parse(url).query )
36
+
37
+ assert_equal ["5"], query_params["num"]
38
+ assert_equal ["11"], query_params["start"]
39
+ end
40
+
41
+ test("silently refuses to paginate too far") do
42
+ # google won't let you paginate past ~10 pages, (101 - num). We silently
43
+ # refuse
44
+
45
+ url = @engine.send(:construct_query, {:query => "books", :start => 110})
46
+
47
+ query_params = CGI.parse( URI.parse(url).query )
48
+
49
+ assert_equal ["91"], query_params["start"]
50
+ end
51
+
52
+ test_with_cassette("pagination object is correct for actual page when you ask for too far", :google_site) do
53
+ results = @engine.search("books", :start => 1000)
54
+
55
+ pagination = results.pagination
56
+
57
+ assert_equal 10, pagination.current_page
58
+ assert_equal 91, pagination.start_record
59
+ end
60
+
61
+ test("sort query construction") do
62
+ url = @engine.send(:construct_query, {:query => "books", :sort => "date_desc"})
63
+
64
+ query_params = CGI.parse( URI.parse(url).query )
65
+
66
+ assert_equal ["date"], query_params["sort"]
67
+ end
68
+
69
+ test_with_cassette("basic smoke test", :google_site) do
70
+ results = @engine.search("books")
71
+
72
+ assert_present results
73
+ assert_present results.total_items
74
+ assert_kind_of Fixnum, results.total_items
75
+
76
+ first = results.first
77
+
78
+ assert_present first.title
79
+ assert_present first.link
80
+ assert_present first.abstract
81
+ assert_present first.journal_title # used as source_title for display url
82
+
83
+ # no openurls for google, we decided
84
+ assert_nil first.to_openurl
85
+ end
86
+
87
+ test_with_cassette("with highlighting", :google_site) do
88
+ engine = BentoSearch::GoogleSiteSearchEngine.new(@config.merge(:highlighting => true))
89
+
90
+ results = engine.search("books")
91
+
92
+ first = results.first
93
+
94
+ assert first.title.html_safe?
95
+ assert first.abstract.html_safe?
96
+ assert first.journal_title.html_safe?
97
+
98
+ assert first.published_in.html_safe?
99
+ end
100
+
101
+ test_with_cassette("without highlighting", :google_site) do
102
+ engine = BentoSearch::GoogleSiteSearchEngine.new(@config.merge(:highlighting => false))
103
+
104
+ results = engine.search("books")
105
+
106
+ first = results.first
107
+
108
+ assert ! first.title.html_safe?
109
+ assert ! first.abstract.html_safe?
110
+ assert ! first.journal_title.html_safe?
111
+ end
112
+
113
+ test_with_cassette("empty result set", :google_site) do
114
+ results = nil
115
+ assert_nothing_raised { results = @engine.search('"adfa lakjdr xavier aldkfj 92323kjadf"') }
116
+
117
+ assert ! results.failed?
118
+
119
+ assert results.empty?
120
+ end
121
+
122
+ end
@@ -1,6 +1,6 @@
1
1
  require 'test_helper'
2
2
 
3
- class LinkDecoratorsTest < ActiveSupport::TestCase
3
+ class ItemDecoratorsTest < ActiveSupport::TestCase
4
4
  MockEngine = BentoSearch::MockEngine
5
5
 
6
6
  # simple decorator that replaces main link
@@ -35,6 +35,17 @@ class LinkDecoratorsTest < ActiveSupport::TestCase
35
35
  end
36
36
  end
37
37
 
38
+ test "decorator as string" do
39
+ @engine = MockEngine.new(:item_decorators => ["::ItemDecoratorsTest::Decorator"])
40
+
41
+ results = @engine.search(:query => "Query")
42
+
43
+ results.each do |result|
44
+ assert_kind_of Decorator, result
45
+ end
46
+
47
+ end
48
+
38
49
  # Is it a good idea to have a decorator that mutates on 'extend'?
39
50
  # I'm not sure, I think probably not, but it is possible.
40
51
  # Here we'll use it to move an original link to other links
@@ -1,3 +1,5 @@
1
+ require 'test_helper'
2
+
1
3
  class OpenurlCreatorTest < ActiveSupport::TestCase
2
4
 
3
5
  def test_create_article
@@ -80,7 +82,7 @@ class OpenurlCreatorTest < ActiveSupport::TestCase
80
82
 
81
83
  end
82
84
 
83
- def create_hardcoded_kev
85
+ def test_create_hardcoded_kev
84
86
  item = BentoSearch::ResultItem.new(
85
87
  :format => "Book",
86
88
  :title => "Something",
@@ -89,13 +91,13 @@ class OpenurlCreatorTest < ActiveSupport::TestCase
89
91
 
90
92
  r = BentoSearch::OpenurlCreator.new(item).to_openurl.referent
91
93
 
92
- assert_equal "article", r.format
94
+ assert_equal "journal", r.format
93
95
  assert_equal "Foo Bar", r.metadata["title"]
94
96
  assert_equal "Smith", r.metadata["au"]
95
97
 
96
98
  end
97
99
 
98
- def result_item_to_openurl
100
+ def test_result_item_to_openurl
99
101
  item = BentoSearch::ResultItem.new(
100
102
  :format => "Book",
101
103
  :title => "Something",
@@ -107,5 +109,19 @@ class OpenurlCreatorTest < ActiveSupport::TestCase
107
109
  assert_kind_of OpenURL::ContextObject, openurl
108
110
  end
109
111
 
112
+ def test_strip_tags
113
+ item = BentoSearch::ResultItem.new(
114
+ :title => "<b>My Title</b>".html_safe,
115
+ :year => 2012,
116
+ :isbn => "XXXX",
117
+ )
118
+
119
+ openurl = item.to_openurl
120
+
121
+ assert_equal "My Title", openurl.referent.metadata["title"]
122
+ end
123
+
124
+
125
+
110
126
 
111
127
  end
@@ -2,12 +2,14 @@ require 'test_helper'
2
2
 
3
3
  class PrimoEngineTest < ActiveSupport::TestCase
4
4
  extend TestWithCassette
5
-
6
- @@host_port = (ENV['PRIMO_HOST_PORT'] || 'EXAMPLE.ORG:80' )
5
+
6
+ # dummy host needs to be in all lowercase, and not explicitly
7
+ # say '80' to work right with VCR
8
+ @@host_port = (ENV['PRIMO_HOST_PORT'] || 'example.org' )
7
9
  @@institution = (ENV['PRIMO_INSTITUTION'] || 'DUMMY_INSTITUTION')
8
10
 
9
11
  VCR.configure do |c|
10
- c.filter_sensitive_data("EXAMPLE.ORG:80", :primo) { @@host_port }
12
+ c.filter_sensitive_data("example.org", :primo) { @@host_port }
11
13
  c.filter_sensitive_data("DUMMY_INSTITUTION", :primo) { @@institution }
12
14
  end
13
15
 
@@ -27,10 +27,45 @@ class ResultItemTest < ActiveSupport::TestCase
27
27
  assert_equal "new", dup.title
28
28
  assert_equal "original", r.title
29
29
 
30
- assert_not_same dup, r
30
+ assert_not_same dup, r
31
+ end
32
+
33
+ def test_openurl_disabled
34
+ r = ResultItem.new(:title => "original")
35
+
36
+ assert_present r.to_openurl
37
+
38
+ r.openurl_disabled = true
39
+
40
+ assert_nil r.to_openurl
41
+ end
42
+
43
+ def test_language
44
+ r = ResultItem.new(:title => "something", :language_code => "en")
45
+ assert_equal "en", r.language_code
46
+ assert_equal "English", r.language_str
47
+
48
+ r = ResultItem.new(:title => "something", :language_code => "eng")
49
+ assert_equal "eng", r.language_code
50
+ assert_equal "English", r.language_str
31
51
 
52
+ # language_str override
53
+ r = ResultItem.new(:title => "something", :language_code => "en", :language_str => "Weird English")
54
+ assert_equal "en", r.language_code
55
+ assert_equal "Weird English", r.language_str
56
+
57
+ # language_str only
58
+ r = ResultItem.new(:title => "something", :language_str => "English")
59
+ assert_nil r.language_code
60
+ assert_equal "English", r.language_str
32
61
 
33
62
  end
34
63
 
64
+ def test_bad_language_code
65
+ r = ResultItem.new(:title => "something", :language_code => "not_valid")
66
+
67
+ assert_equal "not_valid", r.language_code
68
+ assert_nil r.language_str
69
+ end
35
70
 
36
71
  end