spree_analytics_trackers 1.0.1 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.bundle/config +2 -0
- data/.rubocop.yml +2 -0
- data/.travis.yml +30 -15
- data/Appraisals +3 -9
- data/LICENSE +1 -1
- data/README.md +7 -3
- data/app/assets/javascripts/spree/frontend/add_to_cart_analytics.js +50 -0
- data/app/assets/javascripts/spree/frontend/remove_from_cart_analytics.js +34 -0
- data/app/controllers/spree/admin/trackers_controller.rb +8 -0
- data/app/controllers/spree_analytics_trackers/checkout_controller_decorator.rb +11 -0
- data/app/controllers/spree_analytics_trackers/store_controller_decorator.rb +13 -0
- data/app/controllers/spree_analytics_trackers/user_confirmations_controller_decorator.rb +11 -0
- data/app/controllers/spree_analytics_trackers/user_passwords_controller_decorator.rb +11 -0
- data/app/controllers/spree_analytics_trackers/user_registrations_controller_decorator.rb +11 -0
- data/app/controllers/spree_analytics_trackers/user_sessions_controller_decorator.rb +11 -0
- data/app/helpers/spree/trackers_helper.rb +79 -16
- data/app/models/spree/tracker.rb +11 -5
- data/app/models/spree_analytics_trackers/user_decorator.rb +9 -0
- data/app/overrides/add_analytics_to_admin_sidebar.rb +2 -3
- data/app/overrides/add_cart_viewed_to_orders_edit.rb +1 -1
- data/app/overrides/add_checkout_step_viewed_to_checkout_edit.rb +11 -3
- data/app/overrides/add_google_analytics_page_view_to_spree_application.rb +10 -0
- data/app/overrides/add_google_analytics_to_spree_application.rb +4 -4
- data/app/overrides/add_google_purchase_to_orders_show.rb +1 -1
- data/app/overrides/add_product_list_filtered_to_taxons_show.rb +1 -1
- data/app/overrides/add_product_list_viewed_to_products_list.rb +1 -1
- data/app/overrides/add_product_viewed_to_products_show.rb +2 -2
- data/app/overrides/add_products_searched_to_products_list.rb +2 -2
- data/app/overrides/add_segment_initializer_to_layout.rb +3 -3
- data/app/overrides/add_segment_page_viewed_to_layout.rb +10 -0
- data/app/presenters/spree_analytics_trackers/base_presenter.rb +14 -0
- data/app/presenters/spree_analytics_trackers/segment/order_presenter.rb +32 -0
- data/app/presenters/spree_analytics_trackers/segment/product_presenter.rb +40 -0
- data/app/views/spree/admin/shared/_analytics_sidebar_menu.html.erb +3 -5
- data/app/views/spree/admin/trackers/_form.html.erb +13 -7
- data/app/views/spree/admin/trackers/index.html.erb +6 -8
- data/app/views/spree/shared/trackers/google_analytics/_checkout_step_viewed.js.erb +19 -0
- data/app/views/spree/shared/trackers/google_analytics/_initializer.js.erb +25 -0
- data/app/views/spree/shared/trackers/google_analytics/_page_viewed.js.erb +13 -0
- data/app/views/spree/shared/trackers/google_analytics/_purchase.js.erb +22 -0
- data/app/views/spree/shared/trackers/segment/_cart_viewed.js.erb +24 -19
- data/app/views/spree/shared/trackers/segment/_checkout_step_viewed.js.erb +20 -11
- data/app/views/spree/shared/trackers/segment/_initializer.js.erb +2 -3
- data/app/views/spree/shared/trackers/segment/_order_complete.js.erb +8 -45
- data/app/views/spree/shared/trackers/segment/_page_viewed.js.erb +7 -0
- data/app/views/spree/shared/trackers/segment/_product_list_filtered.js.erb +19 -28
- data/app/views/spree/shared/trackers/segment/_product_list_viewed.js.erb +17 -20
- data/app/views/spree/shared/trackers/segment/_product_viewed.js.erb +12 -6
- data/app/views/spree/shared/trackers/segment/_products_searched.js.erb +7 -9
- data/config/locales/en.yml +0 -2
- data/config/locales/zh-TW.yml +19 -0
- data/db/migrate/20200721163729_add_store_id_to_spree_trackers.rb +14 -0
- data/gemfiles/{spree_3_5.gemfile → spree_4_1.gemfile} +2 -2
- data/gemfiles/spree_master.gemfile +0 -1
- data/lib/generators/spree_analytics_trackers/install/install_generator.rb +8 -0
- data/lib/spree_analytics_trackers.rb +1 -0
- data/lib/spree_analytics_trackers/factories.rb +1 -0
- data/lib/spree_analytics_trackers/version.rb +3 -9
- data/spec/features/admin/configuration/analytics_tracker_spec.rb +18 -7
- data/spec/features/checkout_spec.rb +15 -0
- data/spec/features/products_spec.rb +315 -0
- data/spec/features/user_login_spec.rb +7 -0
- data/spec/features/user_password_recovery_spec.rb +7 -0
- data/spec/features/user_registration_spec.rb +7 -0
- data/spec/fixtures/thinking-cat.jpg +0 -0
- data/spec/models/spree/tracker_spec.rb +15 -0
- data/spec/presenters/spree_analytics_trackers/segment/order_presenter_spec.rb +34 -0
- data/spec/spec_helper.rb +7 -82
- data/spec/support/custom_products.rb +26 -0
- data/spree_analytics_trackers.gemspec +4 -17
- metadata +56 -212
- data/app/controllers/spree/store_controller_decorator.rb +0 -5
- data/app/models/spree/line_item_decorator.rb +0 -6
- data/app/models/spree/order_decorator.rb +0 -7
- data/app/models/spree/product_decorator.rb +0 -11
- data/app/models/spree/user_decorator.rb +0 -5
- data/app/overrides/add_google_checkout_to_checkout_edit.rb +0 -9
- data/app/overrides/add_product_added_to_orders_edit.rb +0 -10
- data/app/views/spree/shared/_google_add_items.html.erb +0 -13
- data/app/views/spree/shared/_google_analytics.html.erb +0 -18
- data/app/views/spree/shared/_google_checkout.html.erb +0 -14
- data/app/views/spree/shared/_google_purchase.html.erb +0 -19
- data/app/views/spree/shared/trackers/segment/_product_added.js.erb +0 -9
- data/gemfiles/spree_3_7.gemfile +0 -9
|
@@ -1,12 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
<% if segment_enabled? && @order.present? && !@order.completed? %>
|
|
2
|
+
<script>
|
|
3
|
+
window.addEventListener('turbolinks:load', function() {
|
|
4
|
+
if (typeof analytics !== 'undefined') {
|
|
5
|
+
analytics.track('Checkout Step Viewed',
|
|
6
|
+
{
|
|
7
|
+
checkout_id: '<%= @order.number %>',
|
|
8
|
+
step: '<%= (@order.checkout_steps.index(@order.state) + 1) %>'
|
|
9
|
+
}
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
analytics.page('Checkout Step Viewed',
|
|
13
|
+
{
|
|
14
|
+
checkout_id: '<%= @order.number %>',
|
|
15
|
+
step: '<%= (@order.checkout_steps.index(@order.state) + 1) %>'
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
}
|
|
6
19
|
});
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
step: '<%= (@order.checkout_steps.index(@order.state) + 1) %>'
|
|
10
|
-
});
|
|
11
|
-
}
|
|
12
|
-
</script>
|
|
20
|
+
</script>
|
|
21
|
+
<% end %>
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
<% if
|
|
1
|
+
<% if segment_enabled? %>
|
|
2
2
|
<script>
|
|
3
3
|
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","debug","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var e=analytics.methods[t];analytics[e]=analytics.factory(e)}analytics.load=function(t,e){var n=document.createElement("script");n.type="text/javascript";n.async=!0;n.src="https://cdn.segment.com/analytics.js/v1/"+t+"/analytics.min.js";var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(n,a);analytics._loadOptions=e};analytics.SNIPPET_VERSION="4.1.0";
|
|
4
|
-
|
|
5
|
-
analytics.page()
|
|
4
|
+
analytics.load('<%=segment_tracker.analytics_id%>'.replace(/^\s+|\s+$/g, ''));
|
|
6
5
|
}}();
|
|
7
6
|
</script>
|
|
8
7
|
<% end %>
|
|
@@ -1,48 +1,11 @@
|
|
|
1
|
-
<% if order_just_completed?(@order) %>
|
|
1
|
+
<% if segment_enabled? && @order.present? && order_just_completed?(@order) %>
|
|
2
|
+
<% order_json = SpreeAnalyticsTrackers::Segment::OrderPresenter.new(@order).to_json %>
|
|
2
3
|
<script>
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
discount: <%= @order.promo_total %>,
|
|
10
|
-
coupon: '<%= @order.promo_code %>',
|
|
11
|
-
currency: '<%= @order.currency %>',
|
|
12
|
-
products: [
|
|
13
|
-
<% @order.line_items.each do |line_item| %>
|
|
14
|
-
{
|
|
15
|
-
product_id: '<%= line_item.product.id %>',
|
|
16
|
-
sku: '<%= line_item.variant.sku %>',
|
|
17
|
-
name: '<%= line_item.product.name %>',
|
|
18
|
-
price: <%= line_item.price %>,
|
|
19
|
-
quantity: <%= line_item.quantity %>
|
|
20
|
-
},
|
|
21
|
-
<% end %>
|
|
22
|
-
]
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
analytics.page('Order Completed', {
|
|
26
|
-
order_id: '<%= @order.number %>',
|
|
27
|
-
total: <%= @order.total %>,
|
|
28
|
-
shipping: <%= @order.shipment_total %>,
|
|
29
|
-
tax: <%= @order.additional_tax_total %>,
|
|
30
|
-
discount: <%= @order.promo_total %>,
|
|
31
|
-
coupon: '<%= @order.promo_code %>',
|
|
32
|
-
currency: '<%= @order.currency %>',
|
|
33
|
-
products: [
|
|
34
|
-
<% @order.line_items.each do |line_item| %>
|
|
35
|
-
{
|
|
36
|
-
product_id: '<%= line_item.product.id %>',
|
|
37
|
-
sku: '<%= line_item.variant.sku %>',
|
|
38
|
-
name: '<%= line_item.product.name %>',
|
|
39
|
-
price: <%= line_item.price %>,
|
|
40
|
-
quantity: <%= line_item.quantity %>
|
|
41
|
-
},
|
|
42
|
-
<% end %>
|
|
43
|
-
]
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
}
|
|
4
|
+
window.addEventListener('turbolinks:load', function() {
|
|
5
|
+
if (typeof analytics !== 'undefined') {
|
|
6
|
+
analytics.page('Order Completed', <%= order_json.html_safe %>);
|
|
7
|
+
analytics.track('Order Completed', <%= order_json.html_safe %>);
|
|
8
|
+
}
|
|
9
|
+
});
|
|
47
10
|
</script>
|
|
48
11
|
<% end %>
|
|
@@ -1,32 +1,23 @@
|
|
|
1
|
-
<% if
|
|
1
|
+
<% if segment_enabled? && any_filtering_params? %>
|
|
2
2
|
<script>
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
3
|
+
window.addEventListener('turbolinks:load', function() {
|
|
4
|
+
if (typeof analytics !== 'undefined') {
|
|
5
|
+
var segmentProductListFilteredJson = {
|
|
6
|
+
filters: [
|
|
7
|
+
<% filtering_params_with_values.each do |type, value| %>
|
|
8
|
+
<%= { type: type, value: value } .to_json.html_safe %>,
|
|
9
|
+
<% end %>
|
|
10
|
+
],
|
|
11
|
+
products: [
|
|
12
|
+
<% @products.each_with_index do |product, index| %>
|
|
13
|
+
<%= product_for_segment(product, position: index + 1) %>,
|
|
14
|
+
<% end %>
|
|
15
|
+
]
|
|
16
|
+
}
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<% end %>
|
|
22
|
-
],
|
|
23
|
-
products: [
|
|
24
|
-
<% @products.each_with_index do |product, index| %>
|
|
25
|
-
<%= product_for_segment(product, position: index+1, image: product.images.first) %>,
|
|
26
|
-
<% end %>
|
|
27
|
-
]
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
}
|
|
18
|
+
analytics.track('Product List Filtered', segmentProductListFilteredJson);
|
|
19
|
+
analytics.page('Product List Filtered', segmentProductListFilteredJson);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
31
22
|
</script>
|
|
32
23
|
<% end %>
|
|
@@ -1,22 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
<% if segment_enabled? && defined?(products) && products.present? %>
|
|
2
|
+
<script>
|
|
3
|
+
window.addEventListener('turbolinks:load', function() {
|
|
4
|
+
if (typeof analytics !== 'undefined') {
|
|
5
|
+
var segmentProductListViewedJson = {
|
|
6
|
+
category: '<%= @taxon&.name %>',
|
|
7
|
+
products: [
|
|
8
|
+
<% products.each_with_index do |product, index| %>
|
|
9
|
+
<%= product_for_segment(product, position: index+1, image: default_image_for_product_or_variant(product)) %>,
|
|
10
|
+
<% end %>
|
|
11
|
+
]
|
|
12
|
+
}
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
<% products.each_with_index do |product, index| %>
|
|
16
|
-
<%= product_for_segment(product, position: index+1, image: product.images.first) %>,
|
|
17
|
-
<% end %>
|
|
18
|
-
]
|
|
14
|
+
analytics.track('Product List Viewed', segmentProductListViewedJson);
|
|
15
|
+
analytics.page('Product List Viewed', segmentProductListViewedJson);
|
|
16
|
+
}
|
|
19
17
|
});
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
</script>
|
|
18
|
+
</script>
|
|
19
|
+
<% end %>
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
<% if segment_enabled? && @product.present? %>
|
|
2
|
+
<script>
|
|
3
|
+
window.addEventListener('turbolinks:load', function() {
|
|
4
|
+
if (typeof analytics !== 'undefined') {
|
|
5
|
+
var segmentProductViewed = <%= product_for_segment(@product, image: default_image_for_product_or_variant(@product)) %>;
|
|
6
|
+
|
|
7
|
+
analytics.page('Product Viewed', segmentProductViewed);
|
|
8
|
+
analytics.track('Product Viewed', segmentProductViewed);
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
</script>
|
|
12
|
+
<% end %>
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
<% if params.key?(:keywords) %>
|
|
1
|
+
<% if segment_enabled? && params.key?(:keywords) && params[:keywords].present? %>
|
|
2
2
|
<script>
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
query: '<%= params[:keywords] %>'
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
});
|
|
10
|
-
}
|
|
3
|
+
window.addEventListener('turbolinks:load', function() {
|
|
4
|
+
if (typeof analytics !== 'undefined') {
|
|
5
|
+
analytics.track('Products Searched', { query: '<%= params[:keywords] %>' });
|
|
6
|
+
analytics.page('Products Searched', { query: '<%= params[:keywords] %>' });
|
|
7
|
+
}
|
|
8
|
+
});
|
|
11
9
|
</script>
|
|
12
10
|
<% end %>
|
data/config/locales/en.yml
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
zh-TW:
|
|
2
|
+
activerecord:
|
|
3
|
+
attributes:
|
|
4
|
+
spree/tracker:
|
|
5
|
+
one: 追蹤器
|
|
6
|
+
other: 追蹤器
|
|
7
|
+
spree:
|
|
8
|
+
analytics_engine: 分析引擎
|
|
9
|
+
analytics_trackers: 分析追蹤器
|
|
10
|
+
new_tracker: 新增追蹤器
|
|
11
|
+
google_analytics: Google 分析
|
|
12
|
+
google_analytics_id: Google 追蹤器代碼
|
|
13
|
+
analytics_desc_header_1: Spree 分析
|
|
14
|
+
analytics_desc_header_2: 新增即時分析到 Spree 的後台
|
|
15
|
+
analytics_desc_list_1: 獲得即時銷售數據
|
|
16
|
+
analytics_desc_list_2: 免費安裝
|
|
17
|
+
analytics_desc_list_3: 不需要寫任何代碼安裝
|
|
18
|
+
analytics_desc_list_4: 完全免費!
|
|
19
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class AddStoreIdToSpreeTrackers < ActiveRecord::Migration[6.0]
|
|
2
|
+
def change
|
|
3
|
+
add_column :spree_trackers, :store_id, :integer
|
|
4
|
+
add_index :spree_trackers, :store_id
|
|
5
|
+
|
|
6
|
+
Spree::Tracker.reset_column_information
|
|
7
|
+
|
|
8
|
+
default_store = Spree::Store.default
|
|
9
|
+
Spree::Tracker.all.each do |tracker|
|
|
10
|
+
tracker.store = default_store
|
|
11
|
+
tracker.save
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -3,6 +3,14 @@ module SpreeAnalyticsTrackers
|
|
|
3
3
|
class InstallGenerator < Rails::Generators::Base
|
|
4
4
|
class_option :auto_run_migrations, type: :boolean, default: false
|
|
5
5
|
|
|
6
|
+
def add_javascripts
|
|
7
|
+
javascripts_path = 'vendor/assets/javascripts/spree/frontend/all.js'
|
|
8
|
+
return unless File.file?(javascripts_path)
|
|
9
|
+
|
|
10
|
+
append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/add_to_cart_analytics\n"
|
|
11
|
+
append_file 'vendor/assets/javascripts/spree/frontend/all.js', "//= require spree/frontend/remove_from_cart_analytics\n"
|
|
12
|
+
end
|
|
13
|
+
|
|
6
14
|
def add_migrations
|
|
7
15
|
run 'bundle exec rake railties:install:migrations FROM=spree_analytics_trackers'
|
|
8
16
|
end
|
|
@@ -1,17 +1,11 @@
|
|
|
1
1
|
module SpreeAnalyticsTrackers
|
|
2
|
+
VERSION = '2.1.1'.freeze
|
|
3
|
+
|
|
2
4
|
module_function
|
|
3
5
|
|
|
4
6
|
# Returns the version of the currently loaded SpreeAnalyticsTrackers as a
|
|
5
7
|
# <tt>Gem::Version</tt>.
|
|
6
8
|
def version
|
|
7
|
-
Gem::Version.new VERSION
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
module VERSION
|
|
11
|
-
MAJOR = 1
|
|
12
|
-
MINOR = 0
|
|
13
|
-
TINY = 1
|
|
14
|
-
|
|
15
|
-
STRING = [MAJOR, MINOR, TINY].compact.join('.')
|
|
9
|
+
Gem::Version.new VERSION
|
|
16
10
|
end
|
|
17
11
|
end
|
|
@@ -3,10 +3,13 @@ require 'spec_helper'
|
|
|
3
3
|
describe 'Analytics Tracker', type: :feature do
|
|
4
4
|
stub_authorization!
|
|
5
5
|
|
|
6
|
+
let!(:default_store) { Spree::Store.default }
|
|
7
|
+
let!(:another_store) { create(:store, name: 'Eurozone') }
|
|
8
|
+
|
|
6
9
|
context 'index' do
|
|
7
10
|
before(:each) do
|
|
8
|
-
create(:tracker)
|
|
9
|
-
create(:tracker, analytics_id: 'A200')
|
|
11
|
+
create(:tracker, store: default_store)
|
|
12
|
+
create(:tracker, analytics_id: 'A200', store: another_store)
|
|
10
13
|
visit spree.admin_trackers_path
|
|
11
14
|
end
|
|
12
15
|
|
|
@@ -18,13 +21,15 @@ describe 'Analytics Tracker', type: :feature do
|
|
|
18
21
|
within_row(1) do
|
|
19
22
|
expect(column_text(1)).to eq('A100')
|
|
20
23
|
expect(column_text(2)).to eq('Google analytics')
|
|
21
|
-
expect(column_text(3)).to eq(
|
|
24
|
+
expect(column_text(3)).to eq(default_store.name)
|
|
25
|
+
expect(column_text(4)).to eq('Yes')
|
|
22
26
|
end
|
|
23
27
|
|
|
24
28
|
within_row(2) do
|
|
25
29
|
expect(column_text(1)).to eq('A200')
|
|
26
30
|
expect(column_text(2)).to eq('Google analytics')
|
|
27
|
-
expect(column_text(3)).to eq('
|
|
31
|
+
expect(column_text(3)).to eq('Eurozone')
|
|
32
|
+
expect(column_text(4)).to eq('Yes')
|
|
28
33
|
end
|
|
29
34
|
end
|
|
30
35
|
end
|
|
@@ -34,16 +39,22 @@ describe 'Analytics Tracker', type: :feature do
|
|
|
34
39
|
visit spree.admin_trackers_path
|
|
35
40
|
end
|
|
36
41
|
|
|
37
|
-
it 'should be able to create a new analytics tracker' do
|
|
38
|
-
|
|
42
|
+
it 'should be able to create a new analytics tracker', js: true do
|
|
43
|
+
within('.content-header') do
|
|
44
|
+
click_link 'admin_new_tracker_link'
|
|
45
|
+
end
|
|
39
46
|
fill_in 'tracker_analytics_id', with: 'A100'
|
|
47
|
+
|
|
48
|
+
select2 default_store.name, from: 'Store'
|
|
49
|
+
|
|
40
50
|
click_button 'Create'
|
|
41
51
|
|
|
42
52
|
expect(page).to have_content('successfully created!')
|
|
43
53
|
within_row(1) do
|
|
44
54
|
expect(column_text(1)).to eq('A100')
|
|
45
55
|
expect(column_text(2)).to eq('Google analytics')
|
|
46
|
-
expect(column_text(3)).to eq(
|
|
56
|
+
expect(column_text(3)).to eq(default_store.name)
|
|
57
|
+
expect(column_text(4)).to eq('Yes')
|
|
47
58
|
end
|
|
48
59
|
end
|
|
49
60
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe 'Visiting Checkout page', type: :feature, js: true do
|
|
4
|
+
let(:mug) { create(:product, name: 'RoR Mug') }
|
|
5
|
+
let(:user) { create(:user) }
|
|
6
|
+
|
|
7
|
+
before do
|
|
8
|
+
allow_any_instance_of(Spree::OrdersController).to receive_messages(try_spree_current_user: user)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'is available' do
|
|
12
|
+
add_to_cart(mug)
|
|
13
|
+
expect { visit spree.checkout_path }.not_to raise_error
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
require 'spec_helper'
|
|
3
|
+
|
|
4
|
+
describe 'Visiting Products', type: :feature, js: true do
|
|
5
|
+
include_context 'custom products'
|
|
6
|
+
|
|
7
|
+
let(:store) { Spree::Store.default }
|
|
8
|
+
|
|
9
|
+
let(:store_name) do
|
|
10
|
+
((first_store = Spree::Store.first) && first_store.name).to_s
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
let!(:ga_tracker) { create(:tracker) }
|
|
14
|
+
let!(:segment_tracker) { create(:tracker, engine: :segment) }
|
|
15
|
+
|
|
16
|
+
before do
|
|
17
|
+
visit spree.products_path
|
|
18
|
+
allow(ENV).to receive(:[]).and_call_original
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'is able to show the shopping cart after adding a product to it' do
|
|
22
|
+
click_link 'Ruby on Rails Ringer T-Shirt'
|
|
23
|
+
expect(page).to have_content('$159.99')
|
|
24
|
+
|
|
25
|
+
expect(page).to have_selector('form#add-to-cart-form')
|
|
26
|
+
expect(page).to have_selector(:button, id: 'add-to-cart-button', disabled: false)
|
|
27
|
+
click_button 'add-to-cart-button'
|
|
28
|
+
expect(page).to have_content(Spree.t(:added_to_cart))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
context 'using Russian Rubles as a currency' do
|
|
32
|
+
before do
|
|
33
|
+
store.update(default_currency: 'RUB')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
let!(:product) do
|
|
37
|
+
product = Spree::Product.find_by(name: 'Ruby on Rails Ringer T-Shirt')
|
|
38
|
+
product.master.prices.create(amount: 19.99, currency: 'RUB')
|
|
39
|
+
product.tap(&:save)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Regression tests for #2737
|
|
43
|
+
context 'uses руб as the currency symbol' do
|
|
44
|
+
it 'on products page' do
|
|
45
|
+
visit spree.products_path
|
|
46
|
+
within("#product_#{product.id}") do
|
|
47
|
+
within('.product-component-price') do
|
|
48
|
+
expect(page).to have_content('19.99 ₽')
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'on product page' do
|
|
54
|
+
visit spree.product_path(product)
|
|
55
|
+
within('.price') do
|
|
56
|
+
expect(page).to have_content('19.99 ₽')
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'when adding a product to the cart' do
|
|
61
|
+
add_to_cart(product)
|
|
62
|
+
|
|
63
|
+
within('.shopping-cart-total-amount') do
|
|
64
|
+
expect(page).to have_content('19.99 ₽')
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it 'is able to search for a product' do
|
|
71
|
+
find('.search-icons').click
|
|
72
|
+
fill_in 'keywords', with: 'shirt'
|
|
73
|
+
|
|
74
|
+
if Spree.version.to_f > 4.1
|
|
75
|
+
first('button[type=submit]').click
|
|
76
|
+
else
|
|
77
|
+
find('#search-dropdown label').click
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
expect(page).to have_css('.product-component-name').once
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
context 'a product with variants' do
|
|
84
|
+
let(:product) { Spree::Product.find_by(name: 'Ruby on Rails Baseball Jersey') }
|
|
85
|
+
let(:option_value) { create(:option_value) }
|
|
86
|
+
let!(:variant) { build(:variant, price: 5.59, product: product, option_values: []) }
|
|
87
|
+
|
|
88
|
+
before do
|
|
89
|
+
image = File.open(File.expand_path('../fixtures/thinking-cat.jpg', __dir__))
|
|
90
|
+
create_image(product, image)
|
|
91
|
+
|
|
92
|
+
product.option_types << option_value.option_type
|
|
93
|
+
variant.option_values << option_value
|
|
94
|
+
variant.save!
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'is displayed' do
|
|
98
|
+
expect { click_link product.name }.not_to raise_error
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
it 'displays price of first variant listed' do
|
|
102
|
+
click_link product.name
|
|
103
|
+
within('#product-price') do
|
|
104
|
+
expect(page).to have_content variant.price
|
|
105
|
+
expect(page).not_to have_content Spree.t(:out_of_stock)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "doesn't display out of stock for master product" do
|
|
110
|
+
product.master.stock_items.update_all count_on_hand: 0, backorderable: false
|
|
111
|
+
|
|
112
|
+
click_link product.name
|
|
113
|
+
within('#product-price') do
|
|
114
|
+
expect(page).not_to have_content Spree.t(:out_of_stock)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
it "doesn't display cart form if all variants (including master) are out of stock" do
|
|
119
|
+
product.variants_including_master.each { |v| v.stock_items.update_all count_on_hand: 0, backorderable: false }
|
|
120
|
+
|
|
121
|
+
click_link product.name
|
|
122
|
+
within('[data-hook=product_price]') do
|
|
123
|
+
expect(page).not_to have_content Spree.t(:add_to_cart)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
context 'a product with variants, images only for the variants' do
|
|
129
|
+
let(:product) { Spree::Product.find_by(name: 'Ruby on Rails Baseball Jersey') }
|
|
130
|
+
let(:variant1) { create(:variant, product: product, price: 9.99) }
|
|
131
|
+
let(:variant2) { create(:variant, product: product, price: 10.99) }
|
|
132
|
+
|
|
133
|
+
before do
|
|
134
|
+
image = File.open(File.expand_path('../fixtures/thinking-cat.jpg', __dir__))
|
|
135
|
+
create_image(variant1, image)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'does not display no image available' do
|
|
139
|
+
visit spree.products_path
|
|
140
|
+
expect(page).to have_selector("img[data-src$='thinking-cat.jpg']")
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
context 'an out of stock product without variants' do
|
|
145
|
+
let(:product) { Spree::Product.find_by(name: 'Ruby on Rails Tote') }
|
|
146
|
+
|
|
147
|
+
before do
|
|
148
|
+
product.master.stock_items.update_all count_on_hand: 0, backorderable: false
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it 'does display out of stock for master product' do
|
|
152
|
+
click_link product.name
|
|
153
|
+
within('#inside-product-cart-form') do
|
|
154
|
+
expect(page).to have_content Spree.t(:out_of_stock).upcase
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it "doesn't display cart form if master is out of stock" do
|
|
159
|
+
click_link product.name
|
|
160
|
+
within('[data-hook=product_price]') do
|
|
161
|
+
expect(page).not_to have_content Spree.t(:add_to_cart)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
context 'pagination' do
|
|
167
|
+
before { Spree::Config.products_per_page = 3 }
|
|
168
|
+
|
|
169
|
+
it 'is able to display products priced between 151 and 200 dollars across multiple pages' do
|
|
170
|
+
find(:css, '#filtersPrice').click
|
|
171
|
+
within(:css, '#collapseFilterPrice') { click_on '$151 - $200' }
|
|
172
|
+
expect(page).to have_css('.product-component-name').exactly(3).times
|
|
173
|
+
next_page = find_all(:css, '.next_page')
|
|
174
|
+
within(next_page[0]) { find(:css, '.page-link').click }
|
|
175
|
+
expect(page).to have_css('.product-component-name').once
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
it 'returns the correct title when displaying a single product' do
|
|
180
|
+
product = Spree::Product.find_by(name: 'Ruby on Rails Baseball Jersey')
|
|
181
|
+
click_link product.name
|
|
182
|
+
|
|
183
|
+
within('div#product-description') do
|
|
184
|
+
within('h1.product-details-title') do
|
|
185
|
+
expect(page).to have_content('Ruby on Rails Baseball Jersey')
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
context 'product is on sale' do
|
|
191
|
+
let(:product) do
|
|
192
|
+
FactoryBot.create(:base_product, description: 'Testing sample', name: 'Sample', price: '19.99')
|
|
193
|
+
end
|
|
194
|
+
let(:option_type) { create(:option_type) }
|
|
195
|
+
let(:option_value1) { create(:option_value, name: 'small', presentation: 'S', option_type: option_type) }
|
|
196
|
+
let(:option_value2) { create(:option_value, name: 'medium', presentation: 'M', option_type: option_type) }
|
|
197
|
+
let(:option_value3) { create(:option_value, name: 'large', presentation: 'L', option_type: option_type) }
|
|
198
|
+
let(:variant1) { create(:variant, product: product, option_values: [option_value1], price: '49.99') }
|
|
199
|
+
let(:variant2) { create(:variant, product: product, option_values: [option_value2], price: '69.99') }
|
|
200
|
+
let(:variant3) { create(:variant, product: product, option_values: [option_value3], price: '89.99') }
|
|
201
|
+
|
|
202
|
+
before do
|
|
203
|
+
if Spree.version.to_f > 4.1
|
|
204
|
+
price1 = Spree::Price.find_by(variant: variant1)
|
|
205
|
+
price2 = Spree::Price.find_by(variant: variant2)
|
|
206
|
+
price3 = Spree::Price.find_by(variant: variant3)
|
|
207
|
+
price1.update(compare_at_amount: '149.99')
|
|
208
|
+
price2.update(compare_at_amount: '169.99')
|
|
209
|
+
price3.update(compare_at_amount: '79.99')
|
|
210
|
+
price1.save
|
|
211
|
+
price2.save
|
|
212
|
+
price3.save
|
|
213
|
+
|
|
214
|
+
product.master.prices.first.update(compare_at_amount: 29.99)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
product.master.stock_items.update_all count_on_hand: 10, backorderable: true
|
|
218
|
+
product.option_types << option_type
|
|
219
|
+
product.variants << [variant1, variant2, variant3]
|
|
220
|
+
product.tap(&:save)
|
|
221
|
+
|
|
222
|
+
visit spree.product_path(product)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it 'shows pre sales price on PDP' do
|
|
226
|
+
expect(page).to have_content('$49.99')
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
it 'shows both pre sales and current prices on PDP' do
|
|
230
|
+
if Spree.version.to_f > 4.1
|
|
231
|
+
expect(page).to have_content('$49.99')
|
|
232
|
+
expect(page).to have_content('$149.99')
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
it 'shows prices for other variants' do
|
|
237
|
+
within('.product-variants-variant-values') do
|
|
238
|
+
find('li', text: 'M').click
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
expect(page).to have_content('$69.99')
|
|
242
|
+
|
|
243
|
+
if Spree.version.to_f > 4.1
|
|
244
|
+
expect(page).to have_content('$169.99')
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
it 'does not show pre sales price when it is bigger than price' do
|
|
249
|
+
if Spree.version.to_f > 4.1
|
|
250
|
+
within('.product-variants-variant-values') do
|
|
251
|
+
find('li', text: 'L').click
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
expect(page).to have_content('$89.99')
|
|
255
|
+
expect(page).not_to have_content('$79.99')
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
xdescribe 'When Requesting A Product By Variant Using URL Query String' do
|
|
261
|
+
let(:product) do
|
|
262
|
+
FactoryBot.create(:base_product, description: 'Testing sample', name: 'Sample', price: '19.99')
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
let(:option_type) { create(:option_type) }
|
|
266
|
+
let(:option_value1) { create(:option_value, name: 'small', presentation: 'S', option_type: option_type) }
|
|
267
|
+
let(:option_value2) { create(:option_value, name: 'medium', presentation: 'M', option_type: option_type) }
|
|
268
|
+
let(:option_value3) { create(:option_value, name: 'large', presentation: 'L', option_type: option_type) }
|
|
269
|
+
let(:variant1) { create(:variant, product: product, option_values: [option_value1], price: '49.99', sku: 'VAR-1') }
|
|
270
|
+
let(:variant2) { create(:variant, product: product, option_values: [option_value2], price: '69.99', sku: 'VAR-2') }
|
|
271
|
+
let(:variant3) { create(:variant, product: product, option_values: [option_value3], price: '89.99', sku: 'VAR-3') }
|
|
272
|
+
|
|
273
|
+
before do
|
|
274
|
+
product.option_types << option_type
|
|
275
|
+
product.variants << [variant1, variant2, variant3]
|
|
276
|
+
product.tap(&:save)
|
|
277
|
+
product.stock_items.last.update count_on_hand: 0, backorderable: false
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
context 'Make sure the requested variant' do
|
|
281
|
+
it 'shows the correct price in the HTML' do
|
|
282
|
+
visit spree.product_path(product) + '?variant=' + variant3.id.to_s
|
|
283
|
+
expect(page).to have_content(variant3.price.to_s)
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
it 'shows back-ordered in the HTML when product is on backorder' do
|
|
287
|
+
visit spree.product_path(product) + '?variant=' + variant2.id.to_s
|
|
288
|
+
expect(page).to have_content('BACKORDERED')
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
it 'shows out of stock in the HTML when the product is unavailable' do
|
|
292
|
+
visit spree.product_path(product) + '?variant=' + variant3.id.to_s
|
|
293
|
+
expect(page).to have_content('OUT OF STOCK')
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
it 'does not update the variant HTML details if no variant is matched' do
|
|
297
|
+
visit spree.product_path(product) + '?variant=9283923297832973283'
|
|
298
|
+
expect(page).to have_content(variant1.price.to_s)
|
|
299
|
+
expect(page).to have_content('BACKORDERED')
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
it 'sets JSON in the Schema.org SKU, URL, price and availability' do
|
|
303
|
+
visit spree.product_path(product) + '?variant=' + variant3.id.to_s
|
|
304
|
+
|
|
305
|
+
jsonld = page.find('script[type="application/ld+json"]', visible: false).text(:all)
|
|
306
|
+
jsonstring = Capybara.string(jsonld)
|
|
307
|
+
|
|
308
|
+
expect(jsonstring).to have_text('?variant=' + variant3.id.to_s)
|
|
309
|
+
expect(jsonstring).to have_text('"availability":"OutOfStock"')
|
|
310
|
+
expect(jsonstring).to have_text('"sku":"VAR-3"')
|
|
311
|
+
expect(jsonstring).to have_text('"price":"89.99"')
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
end
|
|
315
|
+
end
|