workarea-classic_search_autocomplete 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +20 -0
  3. data/.eslintrc +35 -0
  4. data/.eslintrc.json +35 -0
  5. data/.github/workflows/ci.yml +60 -0
  6. data/.gitignore +19 -0
  7. data/.rubocop.yml +2 -0
  8. data/.stylelintrc +8 -0
  9. data/.stylelintrc.json +8 -0
  10. data/CHANGELOG.md +19 -0
  11. data/Gemfile +17 -0
  12. data/LICENSE +52 -0
  13. data/README.md +45 -0
  14. data/Rakefile +60 -0
  15. data/app/assets/javascripts/jquery_ui/storefront/categorized_autocomplete.js +53 -0
  16. data/app/assets/javascripts/workarea/storefront/modules/search_fields.js +69 -0
  17. data/app/assets/javascripts/workarea/storefront/templates/ui_menu_heading.jst.ejs +1 -0
  18. data/app/assets/javascripts/workarea/storefront/templates/ui_menu_item.jst.ejs +8 -0
  19. data/app/assets/stylesheets/jquery_ui/storefront/_ui_autocomplete.scss +17 -0
  20. data/app/assets/stylesheets/jquery_ui/storefront/_ui_menu.scss +40 -0
  21. data/app/controllers/.keep +0 -0
  22. data/app/controllers/workarea/storefront/searches_controller.decorator +14 -0
  23. data/app/queries/workarea/search/search_suggestions.rb +45 -0
  24. data/app/view_models/workarea/storefront/search_suggestion_view_model.rb +85 -0
  25. data/app/views/workarea/storefront/searches/index.json.jbuilder +1 -0
  26. data/bin/rails +25 -0
  27. data/config/initializers/appends.rb +26 -0
  28. data/config/initializers/workarea.rb +2 -0
  29. data/config/routes.rb +5 -0
  30. data/lib/workarea/classic_search_autocomplete.rb +11 -0
  31. data/lib/workarea/classic_search_autocomplete/engine.rb +10 -0
  32. data/lib/workarea/classic_search_autocomplete/version.rb +5 -0
  33. data/package.json +9 -0
  34. data/script/admin_ci +9 -0
  35. data/script/ci +11 -0
  36. data/script/core_ci +9 -0
  37. data/script/plugins_ci +9 -0
  38. data/script/storefront_ci +9 -0
  39. data/test/dummy/.ruby-version +1 -0
  40. data/test/dummy/Rakefile +6 -0
  41. data/test/dummy/app/assets/config/manifest.js +3 -0
  42. data/test/dummy/app/assets/images/.keep +0 -0
  43. data/test/dummy/app/assets/javascripts/application.js +14 -0
  44. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  45. data/test/dummy/app/controllers/application_controller.rb +2 -0
  46. data/test/dummy/app/controllers/concerns/.keep +0 -0
  47. data/test/dummy/app/helpers/application_helper.rb +2 -0
  48. data/test/dummy/app/jobs/application_job.rb +2 -0
  49. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  50. data/test/dummy/app/models/concerns/.keep +0 -0
  51. data/test/dummy/app/views/layouts/application.html.erb +15 -0
  52. data/test/dummy/app/views/layouts/mailer.html.erb +13 -0
  53. data/test/dummy/app/views/layouts/mailer.text.erb +1 -0
  54. data/test/dummy/bin/bundle +3 -0
  55. data/test/dummy/bin/rails +4 -0
  56. data/test/dummy/bin/rake +4 -0
  57. data/test/dummy/bin/setup +25 -0
  58. data/test/dummy/bin/update +25 -0
  59. data/test/dummy/config.ru +5 -0
  60. data/test/dummy/config/application.rb +34 -0
  61. data/test/dummy/config/boot.rb +5 -0
  62. data/test/dummy/config/environment.rb +5 -0
  63. data/test/dummy/config/environments/development.rb +52 -0
  64. data/test/dummy/config/environments/production.rb +83 -0
  65. data/test/dummy/config/environments/test.rb +45 -0
  66. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  67. data/test/dummy/config/initializers/assets.rb +12 -0
  68. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  69. data/test/dummy/config/initializers/content_security_policy.rb +25 -0
  70. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  71. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  72. data/test/dummy/config/initializers/inflections.rb +16 -0
  73. data/test/dummy/config/initializers/mime_types.rb +4 -0
  74. data/test/dummy/config/initializers/workarea.rb +5 -0
  75. data/test/dummy/config/initializers/wrap_parameters.rb +9 -0
  76. data/test/dummy/config/locales/en.yml +33 -0
  77. data/test/dummy/config/puma.rb +34 -0
  78. data/test/dummy/config/routes.rb +5 -0
  79. data/test/dummy/config/spring.rb +6 -0
  80. data/test/dummy/db/seeds.rb +2 -0
  81. data/test/dummy/lib/assets/.keep +0 -0
  82. data/test/dummy/log/.keep +0 -0
  83. data/test/integration/workarea/storefront/search_autocomplete_integration_test.rb +43 -0
  84. data/test/queries/workarea/search/search_suggestions_test.rb +77 -0
  85. data/test/system/workarea/storefront/search_autocomplete_analytics_system_test.rb +46 -0
  86. data/test/teaspoon_env.rb +6 -0
  87. data/test/test_helper.rb +10 -0
  88. data/test/view_models/workarea/storefront/search_suggestion_view_model_test.rb +42 -0
  89. data/workarea-classic_search_autocomplete.gemspec +25 -0
  90. data/yarn.lock +3265 -0
  91. metadata +156 -0
@@ -0,0 +1,12 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Version of your assets, change this if you want to expire all your assets.
4
+ Rails.application.config.assets.version = '1.0'
5
+
6
+ # Add additional assets to the asset load path.
7
+ # Rails.application.config.assets.paths << Emoji.images_path
8
+
9
+ # Precompile additional assets.
10
+ # application.js, application.css, and all non-JS/CSS in the app/assets
11
+ # folder are already added.
12
+ # Rails.application.config.assets.precompile += %w( admin.js admin.css )
@@ -0,0 +1,7 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4
+ # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5
+
6
+ # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7
+ # Rails.backtrace_cleaner.remove_silencers!
@@ -0,0 +1,25 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Define an application-wide content security policy
4
+ # For further information see the following documentation
5
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
6
+
7
+ # Rails.application.config.content_security_policy do |policy|
8
+ # policy.default_src :self, :https
9
+ # policy.font_src :self, :https, :data
10
+ # policy.img_src :self, :https, :data
11
+ # policy.object_src :none
12
+ # policy.script_src :self, :https
13
+ # policy.style_src :self, :https
14
+
15
+ # # Specify URI for violation reports
16
+ # # policy.report_uri "/csp-violation-report-endpoint"
17
+ # end
18
+
19
+ # If you are using UJS then enable automatic nonce generation
20
+ # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
21
+
22
+ # Report CSP violations to a specified URI
23
+ # For further information see the following documentation:
24
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
25
+ # Rails.application.config.content_security_policy_report_only = true
@@ -0,0 +1,5 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Specify a serializer for the signed and encrypted cookie jars.
4
+ # Valid options are :json, :marshal, and :hybrid.
5
+ Rails.application.config.action_dispatch.cookies_serializer = :json
@@ -0,0 +1,4 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Configure sensitive parameters which will be filtered from the log file.
4
+ Rails.application.config.filter_parameters += [:password]
@@ -0,0 +1,16 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Add new inflection rules using the following format. Inflections
4
+ # are locale specific, and you may define rules for as many different
5
+ # locales as you wish. All of these examples are active by default:
6
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
7
+ # inflect.plural /^(ox)$/i, '\1en'
8
+ # inflect.singular /^(ox)en/i, '\1'
9
+ # inflect.irregular 'person', 'people'
10
+ # inflect.uncountable %w( fish sheep )
11
+ # end
12
+
13
+ # These inflection rules are supported but not enabled by default:
14
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
15
+ # inflect.acronym 'RESTful'
16
+ # end
@@ -0,0 +1,4 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Add new mime types for use in respond_to blocks:
4
+ # Mime::Type.register "text/richtext", :rtf
@@ -0,0 +1,5 @@
1
+ Workarea.configure do |config|
2
+ # Basic site info
3
+ config.site_name = 'Workarea SACLASSIC'
4
+ config.host = 'www.example.com'
5
+ end
@@ -0,0 +1,9 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # This file contains settings for ActionController::ParamsWrapper which
4
+ # is enabled by default.
5
+
6
+ # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7
+ ActiveSupport.on_load(:action_controller) do
8
+ wrap_parameters format: [:json]
9
+ end
@@ -0,0 +1,33 @@
1
+ # Files in the config/locales directory are used for internationalization
2
+ # and are automatically loaded by Rails. If you want to use locales other
3
+ # than English, add the necessary files in this directory.
4
+ #
5
+ # To use the locales, use `I18n.t`:
6
+ #
7
+ # I18n.t 'hello'
8
+ #
9
+ # In views, this is aliased to just `t`:
10
+ #
11
+ # <%= t('hello') %>
12
+ #
13
+ # To use a different locale, set it with `I18n.locale`:
14
+ #
15
+ # I18n.locale = :es
16
+ #
17
+ # This would use the information in config/locales/es.yml.
18
+ #
19
+ # The following keys must be escaped otherwise they will not be retrieved by
20
+ # the default I18n backend:
21
+ #
22
+ # true, false, on, off, yes, no
23
+ #
24
+ # Instead, surround them with single quotes.
25
+ #
26
+ # en:
27
+ # 'true': 'foo'
28
+ #
29
+ # To learn more, please read the Rails Internationalization guide
30
+ # available at http://guides.rubyonrails.org/i18n.html.
31
+
32
+ en:
33
+ hello: "Hello world"
@@ -0,0 +1,34 @@
1
+ # Puma can serve each request in a thread from an internal thread pool.
2
+ # The `threads` method setting takes two numbers: a minimum and maximum.
3
+ # Any libraries that use thread pools should be configured to match
4
+ # the maximum value specified for Puma. Default is set to 5 threads for minimum
5
+ # and maximum; this matches the default thread size of Active Record.
6
+ #
7
+ threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
8
+ threads threads_count, threads_count
9
+
10
+ # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
11
+ #
12
+ port ENV.fetch("PORT") { 3000 }
13
+
14
+ # Specifies the `environment` that Puma will run in.
15
+ #
16
+ environment ENV.fetch("RAILS_ENV") { "development" }
17
+
18
+ # Specifies the number of `workers` to boot in clustered mode.
19
+ # Workers are forked webserver processes. If using threads and workers together
20
+ # the concurrency of the application would be max `threads` * `workers`.
21
+ # Workers do not work on JRuby or Windows (both of which do not support
22
+ # processes).
23
+ #
24
+ # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
25
+
26
+ # Use the `preload_app!` method when specifying a `workers` number.
27
+ # This directive tells Puma to first boot the application and load code
28
+ # before forking the application. This takes advantage of Copy On Write
29
+ # process behavior so workers use less memory.
30
+ #
31
+ # preload_app!
32
+
33
+ # Allow puma to be restarted by `rails restart` command.
34
+ plugin :tmp_restart
@@ -0,0 +1,5 @@
1
+ Rails.application.routes.draw do
2
+ mount Workarea::Core::Engine => '/'
3
+ mount Workarea::Admin::Engine => '/admin', as: 'admin'
4
+ mount Workarea::Storefront::Engine => '/', as: 'storefront'
5
+ end
@@ -0,0 +1,6 @@
1
+ %w[
2
+ .ruby-version
3
+ .rbenv-vars
4
+ tmp/restart.txt
5
+ tmp/caching-dev.txt
6
+ ].each { |path| Spring.watch(path) }
@@ -0,0 +1,2 @@
1
+ require 'workarea/seeds'
2
+ Workarea::Seeds.run
File without changes
File without changes
@@ -0,0 +1,43 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ module Storefront
5
+ class SearchAutocompleteIntegrationTest < Workarea::IntegrationTest
6
+ def test_renders_search_suggestions_in_json
7
+ create_product(name: 'Foo')
8
+ create_category(name: 'Foo Category')
9
+ create_page(name: 'Foo Page')
10
+
11
+ Metrics::SearchByDay.save_search('foo', 3)
12
+ travel_to 1.week.from_now
13
+ GenerateInsights.generate_all!
14
+ BulkIndexSearches.perform
15
+
16
+ get storefront.searches_path(q: 'foo', format: 'json')
17
+ results = JSON.parse(response.body)['results']
18
+ assert_equal(4, results.length)
19
+
20
+ search = results.detect { |r| r['type'] == 'Searches' }
21
+ assert(search.present?)
22
+ assert_equal('foo', search['value'])
23
+ assert_equal(storefront.search_path(q: 'foo'), search['url'])
24
+
25
+ product = results.detect { |r| r['type'] == 'Products' }
26
+ assert(product.present?)
27
+ assert_equal('Foo', product['value'])
28
+ assert_match(/product_images/, product['image'])
29
+ assert_equal(storefront.product_path('foo'), product['url'])
30
+
31
+ category = results.detect { |r| r['type'] == 'Categories' }
32
+ assert(category.present?)
33
+ assert_equal('Foo Category', category['value'])
34
+ assert_equal(storefront.category_path('foo-category'), category['url'])
35
+
36
+ page = results.detect { |r| r['type'] == 'Pages' }
37
+ assert(page.present?)
38
+ assert_equal('Foo Page', page['value'])
39
+ assert_equal(storefront.page_path('foo-page'), page['url'])
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,77 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ module Search
5
+ class SearchSuggestionsTest < IntegrationTest
6
+ def test_results
7
+ Metrics::SearchByDay.save_search('test', 1)
8
+ 2.times { Metrics::SearchByDay.save_search('test product', 2) }
9
+ travel_to 1.week.from_now
10
+ GenerateInsights.generate_all!
11
+ BulkIndexSearches.perform
12
+
13
+ create_product(name: 'test product 1')
14
+ create_product(name: 'test product 2', active: false)
15
+ BulkIndexProducts.perform
16
+
17
+ results = SearchSuggestions.new(q: 'tes').results
18
+
19
+ assert_equal(3, results.length)
20
+ assert_results_include(results, 'test product')
21
+ assert_results_include(results, 'test')
22
+ assert_results_include(results, 'test product 1')
23
+ end
24
+
25
+ def test_results_in_release
26
+ Metrics::SearchByDay.save_search('test', 1)
27
+ 2.times { Metrics::SearchByDay.save_search('test product', 2) }
28
+ travel_to 1.week.from_now
29
+ GenerateInsights.generate_all!
30
+ BulkIndexSearches.perform
31
+
32
+ product_one = create_product(name: 'test product 1')
33
+ product_two = create_product(name: 'test product 2')
34
+ BulkIndexProducts.perform
35
+
36
+ results = SearchSuggestions.new(q: 'tes').results
37
+ assert_equal(4, results.length)
38
+ assert_results_include(results, 'test product')
39
+ assert_results_include(results, 'test')
40
+ assert_results_include(results, 'test product 1')
41
+ assert_results_include(results, 'test product 2')
42
+
43
+ create_release.as_current do
44
+ product_two.update!(active: false)
45
+
46
+ results = SearchSuggestions.new(q: 'tes').results
47
+ assert_equal(3, results.length)
48
+ assert_results_include(results, 'test product')
49
+ assert_results_include(results, 'test')
50
+ assert_results_include(results, 'test product 1')
51
+ end
52
+
53
+ results = SearchSuggestions.new(q: 'tes').results
54
+ assert_equal(4, results.length)
55
+ assert_results_include(results, 'test product')
56
+ assert_results_include(results, 'test')
57
+ assert_results_include(results, 'test product 1')
58
+ assert_results_include(results, 'test product 2')
59
+
60
+ create_release.as_current do
61
+ product_two.update!(name: 'test 4', active: true)
62
+
63
+ results = SearchSuggestions.new(q: 'tes').results
64
+ assert_equal(4, results.length)
65
+ assert_results_include(results, 'test product')
66
+ assert_results_include(results, 'test')
67
+ assert_results_include(results, 'test product 1')
68
+ assert_results_include(results, 'test 4')
69
+ end
70
+ end
71
+
72
+ def assert_results_include(results, name)
73
+ refute_nil(results.detect { |r| r['_source']['content']['name'] == name })
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,46 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ module Storefront
5
+ class SearchAutocompleteAnalyticsSystemTest < Workarea::SystemTest
6
+ include Storefront::SystemTest
7
+
8
+ def test_announcing_product_click_event_search_type_ahead
9
+ products = [
10
+ create_product(
11
+ id: 'PROD1',
12
+ name: 'Test Product 1',
13
+ variants: [{ sku: 'SKU1', regular: 10.to_m }],
14
+ filters: { 'Size' => 'Medium', 'Color' => 'Blue' }
15
+ ),
16
+ create_product(
17
+ id: 'PROD2',
18
+ name: 'Test Product 2',
19
+ variants: [{ sku: 'SKU2', regular: 12.to_m }],
20
+ filters: {
21
+ 'Size' => ['Medium', 'Small'],
22
+ 'Color' => ['Blue', 'Green']
23
+ }
24
+ )
25
+ ]
26
+
27
+ products.each { |p| Search::Storefront::Product.new(p).save }
28
+ visit storefront.root_path
29
+ disable_analytics_dom_events
30
+
31
+ page.evaluate_script('$("#storefront_search").categorizedAutocomplete("search", "test");')
32
+
33
+ find('li', text: 'Test Product 1').click
34
+
35
+ events = find_analytics_events(for_event: 'productClick')
36
+ assert_equal(1, events.count)
37
+ payload = events.first['arguments'].first
38
+
39
+ assert_equal('PROD1', payload['id'])
40
+ assert_equal('Test Product 1', payload['name'])
41
+ assert_equal(false, payload['sale'])
42
+ assert_equal(10, payload['price'])
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,6 @@
1
+ require 'workarea/testing/teaspoon'
2
+
3
+ Teaspoon.configure do |config|
4
+ config.root = Workarea::ClassicSearchAutocomplete::Engine.root
5
+ Workarea::Teaspoon.apply(config)
6
+ end
@@ -0,0 +1,10 @@
1
+ # Configure Rails Environment
2
+ ENV['RAILS_ENV'] = 'test'
3
+
4
+ require File.expand_path("../../test/dummy/config/environment.rb", __FILE__)
5
+ require 'rails/test_help'
6
+ require 'workarea/test_help'
7
+
8
+ # Filter out Minitest backtrace while allowing backtrace from other libraries
9
+ # to be shown.
10
+ Minitest.backtrace_filter = Minitest::BacktraceFilter.new
@@ -0,0 +1,42 @@
1
+ require 'test_helper'
2
+
3
+ module Workarea
4
+ module Storefront
5
+ class SearchSuggestionViewModelTest < TestCase
6
+ setup :set_asset_host_config
7
+ teardown :restore_asset_host_config
8
+
9
+ def set_asset_host_config
10
+ @current_asset_host = Rails.application.config.action_controller.asset_host
11
+ Rails.application.config.action_controller.asset_host = 'http://cdn.client.com'
12
+ end
13
+
14
+ def restore_asset_host_config
15
+ Rails.application.config.action_controller.asset_host = @current_asset_host
16
+ end
17
+
18
+ def test_image_handles_blank
19
+ search_suggestion = { '_source' => { 'cache' => { 'image' => '' } } }
20
+ view_model = SearchSuggestionViewModel.new(search_suggestion)
21
+ assert(view_model.image.nil?)
22
+ end
23
+
24
+ def test_image_handles_different_urls_from_index
25
+ possible_index_urls = %w(
26
+ /image.jpg
27
+ https://staging.client.com/image.jpg
28
+ http://cdn.client.com/image.jpg
29
+ )
30
+
31
+ possible_index_urls.each do |url_from_index|
32
+ search_suggestion = {
33
+ '_source' => { 'cache' => { 'image' => url_from_index } }
34
+ }
35
+
36
+ view_model = SearchSuggestionViewModel.new(search_suggestion)
37
+ assert_equal('http://cdn.client.com/image.jpg', view_model.image)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+ $:.push File.expand_path("lib", __dir__)
2
+
3
+ # Maintain your gem's version:
4
+ require "workarea/classic_search_autocomplete/version"
5
+
6
+ # Describe your gem and declare its dependencies:
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "workarea-classic_search_autocomplete"
9
+ spec.version = Workarea::ClassicSearchAutocomplete::VERSION
10
+ spec.authors = ["Curt Howard"]
11
+ spec.email = ["choward@workarea.com"]
12
+ spec.homepage = 'https://github.com/workarea-commerce/workarea-classic_search_autocomplete'
13
+ spec.summary = "Basic Autocomplete results displayed while searching"
14
+ spec.description = <<~DESCRIPTION
15
+ This plugin displays live results as the user begins to type a search into
16
+ the Storefront search field in a small UI displayed below the field. It
17
+ leverages a customized version of the jQuery UI Autocomplete functionality.
18
+ For a better overall UX and UI for your customers, consider the
19
+ workarea-search_autocomplete plugin instead.
20
+ DESCRIPTION
21
+
22
+ spec.files = `git ls-files`.split("\n")
23
+
24
+ spec.add_dependency 'workarea', '~> 3.x', '>= 3.5.x'
25
+ end