solidus_seo 1.0.7 → 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 91977b19b9d261263477d03108cbcbd5062b6b1a0e19f56f02da4f1d9e3d6d89
4
- data.tar.gz: 7293522b84d2c7780bbc12b753b39af45ed2acfcc86e2d411fa01446de0e9690
3
+ metadata.gz: 3626084993746b4fd03d1ac122e5e5fd15e2c425e24c09ff66e48c85a8f58fd8
4
+ data.tar.gz: dc2e82b2dae8291d44ef540c3085b5ccc85682819acb3c405308e33e879f987b
5
5
  SHA512:
6
- metadata.gz: 3143fa46338c323818466c31af1a3531512d1976d84562f61ef61aa7e24838252a413f26f16300ed9c0fc147d2e7b261f29625b298162a46b319e41daba8f103
7
- data.tar.gz: ea778900d7f14049f4234a36849e3731ec1c6a4a60b33303203e024fb1bbfb54c0ce8052e617f62a2f0bc56bcfee9a3de27dfd606a99ac92bde56db6b43d774b
6
+ metadata.gz: 5523b7a476f2e1f0fb42ea23ca16c926ecbff1f6c32f65438d431bfcb55c4ad84bdb3a4599356235f949b1c7dbafdfb1289d7edd98222851bd2d9d8aa6309471
7
+ data.tar.gz: 8b63780e4f7d9619c0c48dfda3f026a39e0b193fcda651df108d3ef5f60cbcc873d21155f1355ead1eeb8d60ec5249c536b469ec88ee69c51835e67a1183ad62
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ module OrdersControllerDecorator
5
+ def self.prepended(base)
6
+ base.class_eval do
7
+ around_action :calculate_cart_diff, only: :populate
8
+ end
9
+ end
10
+
11
+ private
12
+
13
+ def calculate_cart_diff
14
+ previous_cart_items = order_contents_hash
15
+
16
+ yield
17
+
18
+ current_cart_items = order_contents_hash
19
+
20
+ cart_diff = current_cart_items.map do |variant_sku, quantity|
21
+ added_quantity = quantity - (previous_cart_items[variant_sku] || 0)
22
+
23
+ [variant_sku, added_quantity.positive? ? added_quantity : nil]
24
+ end.to_h.compact
25
+
26
+ cart_diff = cart_diff.map do |variant_sku, quantity|
27
+ variant = Spree::Variant.find_by(sku: variant_sku)
28
+
29
+ [
30
+ variant_sku,
31
+ {
32
+ id: variant.product.master.sku,
33
+ name: variant.product.name,
34
+ variant: variant.options_text,
35
+ price: variant.price,
36
+ quantity: quantity
37
+ }
38
+ ]
39
+ end.to_h
40
+
41
+ flash[:added_to_cart] = cart_diff if cart_diff.present?
42
+ end
43
+
44
+ def order_contents_hash
45
+ return {} if current_order.blank?
46
+
47
+ current_order.line_items.each_with_object({}) do |li, acc|
48
+ acc[li.variant.sku] ||= 0
49
+ acc[li.variant.sku] += li.quantity
50
+ end
51
+ end
52
+
53
+ ::Spree::StoreController.prepend(self)
54
+ end
55
+ end
@@ -40,14 +40,8 @@ module Spree
40
40
  master.default_price.amount
41
41
  end
42
42
 
43
- def any_in_stock?
44
- return variants_including_master.any? unless Spree::Config.track_inventory_levels
45
- arel_conditions = [
46
- variants_including_master.arel_table[:track_inventory].eq(false),
47
- Spree::StockItem.arel_table[:count_on_hand].gt(0)
48
- ]
49
- in_stock_variants = variants_including_master.joins(:stock_items).where(arel_conditions.inject(:or))
50
- in_stock_variants.any?
43
+ def in_stock?
44
+ available? && variants_including_master.suppliable.any?
51
45
  end
52
46
 
53
47
  def seo_data
@@ -98,7 +92,7 @@ module Spree
98
92
  "priceCurrency": seo_currency,
99
93
  "price": seo_price,
100
94
  "itemCondition": "http://schema.org/NewCondition",
101
- "availability": "http://schema.org/#{ any_in_stock? ? 'InStock' : 'OutOfStock'}",
95
+ "availability": "http://schema.org/#{ in_stock? ? 'InStock' : 'OutOfStock'}",
102
96
  }
103
97
  }
104
98
  end
@@ -1,159 +1,10 @@
1
- <%
2
- just_purchased = @order && order_just_completed?(@order)
3
- if ENV.values_at('GOOGLE_TAG_MANAGER_ID', 'GOOGLE_ANALYTICS_ID', 'FACEBOOK_PIXEL_ID', 'PINTEREST_TAG_ID').any? \
4
- && just_purchased
5
-
6
- shared_order_data = {
7
- affiliation: current_store.name,
8
- currency: @order.currency,
9
- tax: @order.tax_total,
10
- shipping: @order.ship_total
11
- }
12
-
13
- purchased_items = @order.line_items.map do |line_item|
14
- variant = line_item.variant
15
-
16
- next unless variant
17
-
18
- {
19
- id: variant.sku,
20
- name: variant.name,
21
- price: line_item.total,
22
- variant: variant.options_text,
23
- quantity: line_item.quantity
24
- }
25
- end.compact
26
-
27
- end %>
28
-
29
1
  <script>
30
- window.solidusSeoListener = function (tagName, eventName) {
2
+ window.solidusSeoDataLayer = function (tagName, eventName) {
31
3
  var tag = document.querySelector('script[data-tag=' + tagName + ']');
32
- if(!tag) return;
33
4
  tag.dataset.firedEvents = eventName;
34
5
  }
35
6
  </script>
36
7
 
37
- <% if ENV['GOOGLE_TAG_MANAGER_ID'].present? %>
38
-
39
- <script type="text/javascript" data-tag="google-tag-manager">
40
- window.dataLayer = window.dataLayer || [];
41
-
42
- <% if just_purchased %>
43
- let orderData = Object.assign({}, <%= raw shared_order_data.to_json %>, {
44
- 'id': '<%= j @order.number %>', // Transaction ID. Required for purchases and refunds.
45
- 'revenue': '<%= @order.total %>', // Total transaction value (incl. tax and shipping)
46
- 'coupon': '' // TODO: Add coupon code if present
47
- });
48
-
49
- window.dataLayer.push({
50
- 'ecommerce': {
51
- 'purchase': {
52
- 'actionField': orderData,
53
- 'products': <%= raw purchased_items.to_json %>
54
- }
55
- }
56
- });
57
- window.solidusSeoListener('google-tag-manager', 'purchase');
58
- <% end %>
59
-
60
- (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
61
- new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
62
- j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
63
- 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
64
- })(window,document,'script','dataLayer','<%= ENV['GOOGLE_TAG_MANAGER_ID'] %>');
65
- </script>
66
-
67
- <% elsif ENV['GOOGLE_ANALYTICS_ID'].present? %>
68
-
69
- <script async src="https://www.googletagmanager.com/gtag/js?id=<%= ENV['GOOGLE_ANALYTICS_ID'] %>"></script>
70
- <script type="text/javascript" data-tag="google-analytics">
71
- window.dataLayer = window.dataLayer || [];
72
- function gtag(){ dataLayer.push(arguments); }
73
- gtag('js', new Date());
74
- gtag('config', '<%= ENV['GOOGLE_ANALYTICS_ID'] %>');
75
-
76
- <% if just_purchased %>
77
- let purchaseData = Object.assign({}, <%= raw shared_order_data.to_json %>, {
78
- 'transaction_id': '<%= @order.number %>',
79
- 'value': '<%= @order.total %>',
80
- 'items': <%= raw purchased_items.to_json %>
81
- });
82
-
83
- gtag('event', 'purchase', purchaseData);
84
- window.solidusSeoListener('google-analytics', 'purchase');
85
- <% end %>
86
- </script>
87
- <% end %>
88
-
89
- <% if ENV['FACEBOOK_PIXEL_ID'].present? %>
90
- <script type="text/javascript" data-tag="facebook">
91
- !function(f,b,e,v,n,t,s) {
92
- if (f.fbq) return;
93
- n = f.fbq = function() { n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments) };
94
- if (!f._fbq) f._fbq=n;
95
- n.push = n; n.loaded = !0; n.version = '2.0'; n.queue=[]; t = b.createElement(e); t.async = !0; t.src = v;
96
- s = b.getElementsByTagName(e)[0]; s.parentNode.insertBefore(t, s);
97
- }(window, document,'script', 'https://connect.facebook.net/en_US/fbevents.js');
98
-
99
- fbq('init', '<%= ENV['FACEBOOK_PIXEL_ID'] %>');
100
- fbq('track', 'PageView');
101
-
102
- <% if just_purchased %>
103
- <% items = purchased_items.map { |li| li.slice(:id, :quantity) } %>
104
-
105
- fbq('track', 'Purchase', {
106
- value: <%= @order.total %>,
107
- currency: 'USD',
108
- contents: <%= raw items.to_json %>,
109
- content_type: 'product',
110
-
111
- // custom properties
112
- order_number: '<%= @order.number %>',
113
- item_total: <%= @order.item_total %>,
114
- tax_total: <%= @order.tax_total %>,
115
- ship_total: <%= @order.ship_total %>,
116
- promo_total: <%= @order.promo_total %>,
117
- });
118
- window.solidusSeoListener('facebook', 'purchase');
119
- <% end %>
120
- </script>
121
- <noscript>
122
- <img height="1" width="1" src="https://www.facebook.com/tr?id=<%= ENV['FACEBOOK_PIXEL_ID'] %>&ev=PageView&noscript=1" />
123
- </noscript>
124
- <% end %>
125
-
126
- <% if ENV['PINTEREST_TAG_ID'].present? %>
127
- <script type="text/javascript" data-tag="pinterest">
128
- !function(e){if(!window.pintrk){window.pintrk=function(){window.pintrk.queue.push(
129
- Array.prototype.slice.call(arguments))};var
130
- n=window.pintrk;n.queue=[],n.version="3.0";var
131
- t=document.createElement("script");t.async=!0,t.src=e;var
132
- r=document.getElementsByTagName("script")[0];r.parentNode.insertBefore(t,r)}}("https://s.pinimg.com/ct/core.js");
133
- pintrk('load', '<%= ENV['PINTEREST_TAG_ID'] %>');
134
- pintrk('page');
135
-
136
- <% if just_purchased %>
137
- <% items = purchased_items.map do |li|
138
- {
139
- product_id: li[:id],
140
- product_name: li[:name],
141
- product_quantity: li[:quantity],
142
- product_price: li[:price],
143
- }
144
- end %>
145
-
146
- pintrk('track', 'checkout', {
147
- value: <%= @order.total %>,
148
- order_quantity: <%= @order.line_items.sum(&:quantity) %>,
149
- currency: 'USD',
150
- line_items: <%= raw items.to_json %>
151
- });
152
-
153
- window.solidusSeoListener('pinterest', 'purchase');
154
- <% end %>
155
- </script>
156
- <noscript>
157
- <img height="1" width="1" style="display:none;" alt="" src="https://ct.pinterest.com/v3/?tid=<%= ENV['PINTEREST_TAG_ID'] %>&event=init&noscript=1" />
158
- </noscript>
8
+ <% %w[google-tag-manager google-analytics facebook pinterest].each do |tag_name| %>
9
+ <%= render "solidus_seo/#{tag_name}", just_purchased: (@order && order_just_completed?(@order)), order: @order %>
159
10
  <% end %>
@@ -0,0 +1,37 @@
1
+ <%
2
+ return if ENV['FACEBOOK_PIXEL_ID'].blank?
3
+
4
+ if just_purchased
5
+ order_data = {
6
+ value: order.total,
7
+ currency: order.currency,
8
+ content_type: 'product',
9
+ contents: order.line_items.map do |line_item|
10
+ next unless line_item.variant
11
+
12
+ { id: line_item.variant.sku, quantity: line_item.quantity }
13
+ end.compact,
14
+
15
+ # custom properties
16
+ order_number: order.number,
17
+ item_total: order.item_total,
18
+ tax_total: order.tax_total,
19
+ ship_total: order.ship_total,
20
+ promo_total: order.promo_total
21
+ }
22
+ end
23
+ %>
24
+ <script type="text/javascript" data-tag="facebook">
25
+ !function(f,b,e,v,n,t,s) {if (f.fbq) return;n = f.fbq = function() { n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments) };if (!f._fbq) f._fbq=n;n.push = n; n.loaded = !0; n.version = '2.0'; n.queue=[]; t = b.createElement(e); t.async = !0; t.src = v;s = b.getElementsByTagName(e)[0]; s.parentNode.insertBefore(t, s);}(window, document,'script', 'https://connect.facebook.net/en_US/fbevents.js');
26
+
27
+ fbq('init', '<%= ENV['FACEBOOK_PIXEL_ID'] %>');
28
+ fbq('track', 'PageView');
29
+
30
+ <% if just_purchased %>
31
+ fbq('track', 'Purchase', <%== order_data.to_json %>);
32
+ window.solidusSeoDataLayer('facebook', 'purchase');
33
+ <% end %>
34
+ </script>
35
+ <noscript>
36
+ <img height="1" width="1" src="https://www.facebook.com/tr?id=<%= ENV['FACEBOOK_PIXEL_ID'] %>&ev=PageView&noscript=1" />
37
+ </noscript>
@@ -0,0 +1,40 @@
1
+ <%
2
+ return if ENV['GOOGLE_TAG_MANAGER_ID'].present? || ENV['GOOGLE_ANALYTICS_ID'].blank?
3
+
4
+ if just_purchased
5
+ order_data = {
6
+ transaction_id: order.number,
7
+ value: order.total,
8
+ items: order.line_items.map do |line_item|
9
+ next unless line_item.variant
10
+
11
+ {
12
+ id: line_item.variant.sku,
13
+ name: line_item.variant.name,
14
+ price: line_item.price,
15
+ variant: line_item.variant.options_text,
16
+ quantity: line_item.quantity
17
+ }
18
+ end.compact,
19
+
20
+ affiliation: current_store.name,
21
+ currency: order.currency,
22
+ tax: order.tax_total,
23
+ shipping: order.ship_total
24
+ }
25
+ end
26
+ %>
27
+ <script async src="https://www.googletagmanager.com/gtag/js?id=<%= ENV['GOOGLE_ANALYTICS_ID'] %>"></script>
28
+ <script type="text/javascript" data-tag="google-analytics">
29
+ window.dataLayer = window.dataLayer || [];
30
+
31
+ function gtag(){ dataLayer.push(arguments); }
32
+
33
+ gtag('js', new Date());
34
+ gtag('config', '<%= ENV['GOOGLE_ANALYTICS_ID'] %>');
35
+
36
+ <% if just_purchased %>
37
+ gtag('event', 'purchase', <%== order_data.to_json %>);
38
+ window.solidusSeoDataLayer('google-analytics', 'purchase');
39
+ <% end %>
40
+ </script>
@@ -0,0 +1,50 @@
1
+ <%
2
+ return if ENV['GOOGLE_ANALYTICS_ID'].present? || ENV['GOOGLE_TAG_MANAGER_ID'].blank?
3
+
4
+ if just_purchased
5
+ order_data = {
6
+ id: order.number,
7
+ revenue: order.total,
8
+ coupon: '', # TODO: Add coupon code if present
9
+
10
+ affiliation: current_store.name,
11
+ currency: order.currency,
12
+ tax: order.tax_total,
13
+ shipping: order.ship_total
14
+ }
15
+
16
+ purchased_items = order.line_items.map do |line_item|
17
+ next unless line_item.variant
18
+
19
+ {
20
+ id: line_item.variant.sku,
21
+ name: line_item.variant.name,
22
+ price: line_item.total,
23
+ variant: line_item.variant.options_text,
24
+ quantity: line_item.quantity
25
+ }
26
+ end.compact
27
+ end
28
+ %>
29
+ <script type="text/javascript" data-tag="google-tag-manager">
30
+ window.dataLayer = window.dataLayer || [];
31
+
32
+ <% if just_purchased %>
33
+ window.dataLayer.push({
34
+ 'ecommerce': {
35
+ 'purchase': {
36
+ 'actionField': <%== order_data.to_json %>,
37
+ 'products': <%== purchased_items.to_json %>
38
+ }
39
+ }
40
+ });
41
+
42
+ window.solidusSeoDataLayer('google-tag-manager', 'purchase');
43
+ <% end %>
44
+
45
+ (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
46
+ new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
47
+ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
48
+ 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
49
+ })(window,document,'script','dataLayer','<%= ENV['GOOGLE_TAG_MANAGER_ID'] %>');
50
+ </script>
@@ -0,0 +1,84 @@
1
+ <%
2
+ return if ENV['PINTEREST_TAG_ID'].blank?
3
+
4
+ product_data = {}
5
+ add_to_cart_data = {}
6
+ order_data = {}
7
+ em_data = {}
8
+
9
+ user_email = current_order&.email || try_spree_current_user&.email
10
+ if user_email.present?
11
+ em_data = { em: user_email }
12
+ end
13
+
14
+ if just_purchased
15
+ order_data = {
16
+ value: order.total,
17
+ currency: order.currency,
18
+ order_quantity: order.line_items.sum(&:quantity),
19
+ line_items: order.line_items.map do |line_item|
20
+ next unless line_item.variant
21
+
22
+ {
23
+ product_id: line_item.variant.product.master.sku,
24
+ product_name: line_item.variant.name,
25
+ product_variant_id: line_item.variant.sku,
26
+ product_variant: line_item.variant.options_text,
27
+ product_quantity: line_item.quantity,
28
+ product_price: line_item.price
29
+ }
30
+ end.compact
31
+ }
32
+ end
33
+
34
+ if @product
35
+ product_data = {
36
+ line_items: [
37
+ {
38
+ product_name: @product.name,
39
+ product_id: @product.master.sku
40
+ }
41
+ ]
42
+ }
43
+ end
44
+
45
+ if flash[:added_to_cart].present?
46
+ add_to_cart_data = {
47
+ value: order.total,
48
+ currency: order.currency,
49
+ order_id: order.number,
50
+ line_items: flash[:added_to_cart].map do |variant_sku, variant|
51
+ {
52
+ product_id: variant['id'],
53
+ product_name: variant['name'],
54
+ product_variant_id: variant_sku,
55
+ product_variant: variant['variant'],
56
+ product_price: variant['price'],
57
+ product_quantity: variant['quantity']
58
+ }
59
+ end
60
+ }
61
+ end
62
+ %>
63
+ <script type="text/javascript" data-tag="pinterest">
64
+ !function(e){if(!window.pintrk){window.pintrk=function(){window.pintrk.queue.push(Array.prototype.slice.call(arguments))};var n=window.pintrk;n.queue=[],n.version="3.0";var t=document.createElement("script");t.async=!0,t.src=e;var r=document.getElementsByTagName("script")[0];r.parentNode.insertBefore(t,r)}}("https://s.pinimg.com/ct/core.js");
65
+
66
+ pintrk('load', '<%= ENV['PINTEREST_TAG_ID'] %>' <%== em_data.present? ? ", #{em_data.to_json}" : '' %>);
67
+ pintrk('page');
68
+
69
+ pintrk('track', 'pagevisit'<%== product_data.present? ? ", #{product_data.to_json}" : '' %>);
70
+ window.solidusSeoDataLayer('pinterest', 'pagevisit');
71
+
72
+ <% if add_to_cart_data.present? %>
73
+ pintrk('track', 'addtocart', <%== add_to_cart_data.to_json %>);
74
+ window.solidusSeoDataLayer('pinterest', 'addtocart');
75
+ <% end %>
76
+
77
+ <% if order_data.present? %>
78
+ pintrk('track', 'checkout', <%== order_data.to_json %>);
79
+ window.solidusSeoDataLayer('pinterest', 'purchase');
80
+ <% end %>
81
+ </script>
82
+ <noscript>
83
+ <img height="1" width="1" style="display:none;" alt="" src="https://ct.pinterest.com/v3/?tid=<%= ENV['PINTEREST_TAG_ID'] %>&event=init&noscript=1" />
84
+ </noscript>
@@ -34,6 +34,7 @@ module SolidusSeo
34
34
  copy_file 'remove_original_title_tag.deface', 'app/overrides/spree/shared/_head/remove_original_title_tag.deface'
35
35
  copy_file 'insert_product_list_helper.html.erb.deface', 'app/overrides/spree/shared/_products/insert_product_list_helper.html.erb.deface'
36
36
  copy_file 'insert_analytics_in_layout.html.erb.deface', 'app/overrides/spree/layouts/spree_application/insert_analytics_in_layout.html.erb.deface'
37
+ copy_file 'replace_taxon_breadcrumbs_helper.html.erb.deface', 'app/overrides/spree/layouts/spree_application/replace_taxon_breadcrumbs_helper.html.erb.deface'
37
38
  end
38
39
  end
39
40
  end
@@ -0,0 +1,5 @@
1
+ <!--
2
+ replace %{erb[loud]:contains("taxon_breadcrumbs")}
3
+ original "<%= taxon_breadcrumbs(@taxon) %>"
4
+ -->
5
+ <%= taxon_breadcrumbs_jsonld(@taxon) %>
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require_dependency 'solidus_seo/jsonld/tag_helper'
3
+ require_dependency 'solidus_seo/helpers/base_helper'
3
4
 
4
5
  module SolidusSeo
5
6
  class Engine < Rails::Engine
@@ -21,6 +22,7 @@ module SolidusSeo
21
22
  initializer "solidus_seo.view_helpers" do
22
23
  ActiveSupport.on_load(:action_view) do
23
24
  ActionView::Base.send :include, ::SolidusSeo::Jsonld::TagHelper
25
+ ActionView::Base.send :include, ::SolidusSeo::Helpers::BaseHelper
24
26
  end
25
27
  end
26
28
 
@@ -4,6 +4,32 @@ module SolidusSeo
4
4
  def plain_text(text)
5
5
  ActionController::Base.helpers.strip_tags(text.to_s).gsub(/\s+/, ' ')
6
6
  end
7
+
8
+ def breadcrumb_pairs(taxon)
9
+ crumbs = []
10
+
11
+ if taxon
12
+ crumbs << [Spree.t(:products), products_url]
13
+ crumbs += taxon.ancestors.collect { |a| [a.name, spree.nested_taxons_url(a.permalink)] } unless taxon.ancestors.empty?
14
+ crumbs << [taxon.name, spree.nested_taxons_url(taxon.permalink)]
15
+ else
16
+ crumbs << [Spree.t(:products), products_url]
17
+ end
18
+
19
+ crumbs
20
+ end
21
+
22
+ def taxon_breadcrumbs_jsonld(taxon, separator = '&nbsp;&raquo;&nbsp;', list_class = 'list-inline', list_item_class = 'list-inline-item')
23
+ return '' if current_page?('/') || taxon.nil?
24
+
25
+ separator = tag.span(separator.html_safe, class: 'breadcrumb-separator')
26
+ original_output = Nokogiri::HTML::DocumentFragment.parse(taxon_breadcrumbs(taxon, separator, list_class).to_s)
27
+ original_output.xpath('@itemscope|@itemtype|@itemprop|.//@itemscope|.//@itemtype|.//@itemprop').remove
28
+ original_output.search('.columns').first['class'] = ''
29
+ original_output.search('li').attr('class', list_item_class)
30
+
31
+ jsonld_breadcrumbs(breadcrumb_pairs(taxon)) + original_output.to_s.html_safe
32
+ end
7
33
  end
8
34
  end
9
35
  end
@@ -2,7 +2,7 @@ require_dependency 'solidus_seo/jsonld/list'
2
2
 
3
3
  module SolidusSeo
4
4
  module Jsonld
5
- class Breadcrumbs < List
5
+ class Breadcrumbs < SolidusSeo::Jsonld::List
6
6
  private
7
7
 
8
8
  def list_type
@@ -15,7 +15,7 @@ module SolidusSeo
15
15
 
16
16
  def as_list_item(item)
17
17
  # `item` in the form of [text, url]
18
- breadcrumb_text, breadcrumb_url = *item
18
+ breadcrumb_text, breadcrumb_url = item
19
19
 
20
20
  {
21
21
  '@type': 'ListItem',
@@ -2,7 +2,7 @@ require_dependency 'solidus_seo/jsonld/base'
2
2
 
3
3
  module SolidusSeo
4
4
  module Jsonld
5
- class List < Base
5
+ class List < SolidusSeo::Jsonld::Base
6
6
  attr_accessor :data, :list_items
7
7
 
8
8
  def initialize(data)
@@ -1,4 +1,3 @@
1
- require_dependency 'spree/base_helper_decorator'
2
1
  require_dependency 'solidus_seo/helpers/base_helper'
3
2
 
4
3
  module SolidusSeo
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SolidusSeo
4
- VERSION = '1.0.7'
4
+ VERSION = '1.0.8'
5
5
  end
@@ -1,30 +1,32 @@
1
1
  example_id | status | run_time |
2
2
  ------------------------------------------------ | ------ | --------------- |
3
- ./spec/features/checkout_complete_spec.rb[1:1:1] | passed | 1.92 seconds |
4
- ./spec/features/checkout_complete_spec.rb[1:2:1] | passed | 0.89422 seconds |
5
- ./spec/features/checkout_complete_spec.rb[1:3:1] | passed | 0.79879 seconds |
6
- ./spec/features/checkout_complete_spec.rb[1:4:1] | passed | 0.86202 seconds |
7
- ./spec/features/homepage_spec.rb[1:1:1] | passed | 0.55572 seconds |
8
- ./spec/features/homepage_spec.rb[1:1:2] | passed | 1.12 seconds |
9
- ./spec/features/homepage_spec.rb[1:2:1] | passed | 0.50916 seconds |
10
- ./spec/features/homepage_spec.rb[1:2:2] | passed | 0.61185 seconds |
11
- ./spec/features/homepage_spec.rb[1:3:1] | passed | 0.73532 seconds |
12
- ./spec/features/homepage_spec.rb[1:3:2] | passed | 0.66687 seconds |
13
- ./spec/features/product_page_spec.rb[1:1:1] | passed | 0.57637 seconds |
14
- ./spec/features/product_page_spec.rb[1:1:2] | passed | 0.70075 seconds |
15
- ./spec/features/product_page_spec.rb[1:1:3] | passed | 0.76786 seconds |
16
- ./spec/features/product_page_spec.rb[1:2:1] | passed | 0.8813 seconds |
17
- ./spec/features/product_page_spec.rb[1:2:2:1] | passed | 0.67679 seconds |
18
- ./spec/features/product_page_spec.rb[1:2:3:1] | passed | 0.69868 seconds |
19
- ./spec/features/product_page_spec.rb[1:3:1] | passed | 0.65377 seconds |
20
- ./spec/features/product_page_spec.rb[1:3:2] | passed | 0.7571 seconds |
21
- ./spec/features/product_page_spec.rb[1:3:3] | passed | 0.66879 seconds |
22
- ./spec/features/taxon_page_spec.rb[1:1:1] | passed | 1.34 seconds |
23
- ./spec/features/taxon_page_spec.rb[1:1:2] | passed | 0.7829 seconds |
24
- ./spec/features/taxon_page_spec.rb[1:1:3] | passed | 0.81693 seconds |
25
- ./spec/features/taxon_page_spec.rb[1:2:1] | passed | 7.61 seconds |
26
- ./spec/features/taxon_page_spec.rb[1:2:2:1] | passed | 0.94849 seconds |
27
- ./spec/features/taxon_page_spec.rb[1:2:3:1] | passed | 0.95931 seconds |
28
- ./spec/features/taxon_page_spec.rb[1:3:1] | passed | 0.76726 seconds |
29
- ./spec/features/taxon_page_spec.rb[1:3:2] | passed | 1.01 seconds |
30
- ./spec/features/taxon_page_spec.rb[1:4:1] | passed | 2.08 seconds |
3
+ ./spec/features/add_to_cart_spec.rb[1:1:1] | passed | 1.6 seconds |
4
+ ./spec/features/checkout_complete_spec.rb[1:1:1] | passed | 0.65012 seconds |
5
+ ./spec/features/checkout_complete_spec.rb[1:2:1] | passed | 0.56665 seconds |
6
+ ./spec/features/checkout_complete_spec.rb[1:3:1] | passed | 0.66929 seconds |
7
+ ./spec/features/checkout_complete_spec.rb[1:4:1] | passed | 0.44838 seconds |
8
+ ./spec/features/homepage_spec.rb[1:1:1] | passed | 2.3 seconds |
9
+ ./spec/features/homepage_spec.rb[1:1:2] | passed | 0.22388 seconds |
10
+ ./spec/features/homepage_spec.rb[1:2:1] | passed | 0.2209 seconds |
11
+ ./spec/features/homepage_spec.rb[1:2:2] | passed | 0.18137 seconds |
12
+ ./spec/features/homepage_spec.rb[1:3:1] | passed | 0.20727 seconds |
13
+ ./spec/features/homepage_spec.rb[1:3:2] | passed | 0.20346 seconds |
14
+ ./spec/features/product_page_spec.rb[1:1:1] | passed | 0.29118 seconds |
15
+ ./spec/features/product_page_spec.rb[1:1:2] | passed | 0.30857 seconds |
16
+ ./spec/features/product_page_spec.rb[1:1:3] | passed | 0.28452 seconds |
17
+ ./spec/features/product_page_spec.rb[1:2:1] | passed | 0.47017 seconds |
18
+ ./spec/features/product_page_spec.rb[1:2:2:1] | passed | 0.3369 seconds |
19
+ ./spec/features/product_page_spec.rb[1:2:3:1] | passed | 0.37361 seconds |
20
+ ./spec/features/product_page_spec.rb[1:3:1] | passed | 0.33857 seconds |
21
+ ./spec/features/product_page_spec.rb[1:3:2] | passed | 0.29488 seconds |
22
+ ./spec/features/product_page_spec.rb[1:3:3] | passed | 0.26324 seconds |
23
+ ./spec/features/product_page_spec.rb[1:4:1:1] | passed | 0.4502 seconds |
24
+ ./spec/features/taxon_page_spec.rb[1:1:1] | passed | 1.04 seconds |
25
+ ./spec/features/taxon_page_spec.rb[1:1:2] | passed | 0.31403 seconds |
26
+ ./spec/features/taxon_page_spec.rb[1:1:3] | passed | 0.30921 seconds |
27
+ ./spec/features/taxon_page_spec.rb[1:2:1] | passed | 0.32058 seconds |
28
+ ./spec/features/taxon_page_spec.rb[1:2:2:1] | passed | 0.30031 seconds |
29
+ ./spec/features/taxon_page_spec.rb[1:2:3:1] | passed | 0.31003 seconds |
30
+ ./spec/features/taxon_page_spec.rb[1:3:1] | passed | 0.30324 seconds |
31
+ ./spec/features/taxon_page_spec.rb[1:3:2] | passed | 0.3043 seconds |
32
+ ./spec/features/taxon_page_spec.rb[1:4:1] | passed | 0.5174 seconds |
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'Add to cart', type: :system do
4
+ let!(:store) { Spree::Store.default }
5
+ let!(:order) { create :completed_order_with_totals }
6
+ let!(:line_item) { order.line_items.first }
7
+
8
+ stub_authorization!
9
+
10
+ before do
11
+ current_order_stubs(order)
12
+ stub_const 'ENV', ENV.to_h.merge(env_variable => 'XXX-YYYYY')
13
+
14
+ visit spree.product_path(line_item.product)
15
+ find('#add-to-cart-button').click
16
+ end
17
+
18
+ context 'when PINTEREST_TAG_ID environment variable is present' do
19
+ let(:env_variable) { 'PINTEREST_TAG_ID' }
20
+
21
+ it 'tracks "add to cart" event' do
22
+ expect(page).to track_analytics_event :pinterest, 'addtocart', [
23
+ 'track', 'addtocart', order.total, order.number, line_item.variant.product.master.sku,
24
+ line_item.name, line_item.variant.sku, line_item.variant.price
25
+ ]
26
+ end
27
+ end
28
+ end
29
+
@@ -1,14 +1,17 @@
1
- describe "Checkout complete", type: :system do
1
+ # frozen_string_literal: true
2
+
3
+ describe 'Checkout complete', type: :system do
2
4
  let!(:store) { Spree::Store.default }
3
5
  let!(:taxon) { create :taxon, name: 'MyTaxon' }
4
6
  let!(:order) { create :completed_order_with_totals }
5
7
  let!(:line_item) { order.line_items.first }
6
- let(:user) { Spree::User.first }
8
+
9
+ stub_authorization!
7
10
 
8
11
  subject { visit spree.order_path(order) }
9
12
 
10
13
  before do
11
- checkout_stubs(order)
14
+ current_order_stubs(order)
12
15
  stub_const 'ENV', ENV.to_h.merge(env_variable => 'XXX-YYYYY')
13
16
  allow_any_instance_of(Spree::OrdersHelper).to receive(:order_just_completed?).with(order) { true }
14
17
  end
@@ -18,8 +21,7 @@ describe "Checkout complete", type: :system do
18
21
 
19
22
  it 'includes and executes a purchase event script' do
20
23
  subject
21
-
22
- expect(page).to matcher_for 'google-tag-manager', [order.number, order.total, 'ecommerce', 'purchase', line_item.sku, line_item.total]
24
+ expect(page).to track_analytics_event 'google-tag-manager', 'purchase', ['ecommerce', 'purchase', order.number, order.total, line_item.sku]
23
25
  end
24
26
  end
25
27
 
@@ -28,7 +30,11 @@ describe "Checkout complete", type: :system do
28
30
 
29
31
  it 'includes and executes a purchase event script' do
30
32
  subject
31
- expect(page).to matcher_for 'google-analytics', ['transaction_id', order.number, order.total, line_item.sku, line_item.total, 'event', 'purchase']
33
+ expect(page).to track_analytics_event 'google-analytics', 'purchase', [
34
+ 'event', 'purchase', 'transaction_id', order.number,
35
+ order.total, line_item.sku, line_item.variant.name,
36
+ line_item.price, line_item.variant.options_text
37
+ ]
32
38
  end
33
39
  end
34
40
 
@@ -37,7 +43,7 @@ describe "Checkout complete", type: :system do
37
43
 
38
44
  it 'includes and executes a purchase event script' do
39
45
  subject
40
- expect(page).to matcher_for :facebook, ['track', 'Purchase', order.total, line_item.sku, line_item.quantity, order.number]
46
+ expect(page).to track_analytics_event :facebook, 'purchase', ['track', 'Purchase', order.total, line_item.sku, line_item.quantity, order.number]
41
47
  end
42
48
  end
43
49
 
@@ -46,16 +52,7 @@ describe "Checkout complete", type: :system do
46
52
 
47
53
  it 'includes and executes a purchase event script' do
48
54
  subject
49
- expect(page).to matcher_for :pinterest, ['track', 'checkout', order.total, line_item.sku, line_item.name, line_item.price]
55
+ expect(page).to track_analytics_event :pinterest, 'purchase', ['track', 'checkout', order.total, line_item.sku, line_item.name, line_item.price]
50
56
  end
51
57
  end
52
58
  end
53
-
54
- # Builds a regex matcher for script tag contents
55
- # @param tag_name [String] Value of script's data-tag attribute
56
- # @param matches [Array] Keywords that must appear in script contents (order matters!)
57
- # @return [Object] Returns a capybara matcher for a script tag with specific attributes and contents
58
- def matcher_for(tag_name, matches)
59
- matches = matches.flatten.map {|v| Regexp.escape(v.to_s) }.join('.+')
60
- have_selector :css, "script[data-tag=#{tag_name}][data-fired-events=purchase]", visible: false, text: /#{matches}/i
61
- end
@@ -26,7 +26,6 @@ describe "Product page", type: :system do
26
26
 
27
27
  subject { visit spree.product_path(product) }
28
28
 
29
-
30
29
  context 'jsonld markup output' do
31
30
  it "contains 'Store' entity type" do
32
31
  subject
@@ -92,4 +91,21 @@ describe "Product page", type: :system do
92
91
  expect(page).to have_css "meta[property='product:price:amount'][content='#{seo_price}']", visible: false
93
92
  end
94
93
  end
94
+
95
+ context 'analytics event tracking' do
96
+ before do
97
+ stub_const 'ENV', ENV.to_h.merge(env_variable => 'XXX-YYYYY')
98
+ end
99
+
100
+ context 'when PINTEREST_TAG_ID environment variable is present' do
101
+ let(:env_variable) { 'PINTEREST_TAG_ID' }
102
+
103
+ it 'tracks "pagevisit" event with product data' do
104
+ subject
105
+ expect(page).to track_analytics_event :pinterest, 'pagevisit', [
106
+ 'track', 'pagevisit', product.name, product.master.sku
107
+ ]
108
+ end
109
+ end
110
+ end
95
111
  end
@@ -10,13 +10,17 @@ module TestingHelpers
10
10
  end
11
11
  end
12
12
 
13
- def checkout_stubs(order, user = nil)
14
- user ||= order.user
13
+ def current_order_stubs(order)
15
14
  allow_any_instance_of(Spree::CheckoutController).to receive_messages(current_order: order)
15
+ allow_any_instance_of(Spree::OrdersController).to receive_messages(current_order: order)
16
+ end
16
17
 
17
- if user
18
- allow_any_instance_of(Spree::CheckoutController).to receive_messages(try_spree_current_user: user)
19
- allow_any_instance_of(Spree::OrdersController).to receive_messages(try_spree_current_user: user)
20
- end
18
+ # Builds a regex matcher for script tag contents
19
+ # @param tag_name [String] Value of script's data-tag attribute
20
+ # @param matches [Array] Keywords that must appear in script contents (order matters!)
21
+ # @return [Object] Returns a capybara matcher for a script tag with specific attributes and contents
22
+ def track_analytics_event(tag_name, event_name, matches)
23
+ matches = matches.flatten.map {|v| Regexp.escape(v.to_s) }.join('.+')
24
+ have_selector :css, "script[data-tag=#{tag_name}][data-fired-events=#{event_name}]", visible: false, text: /#{matches}/i
21
25
  end
22
26
  end
@@ -1,15 +1,17 @@
1
1
  # frozen_string_literal: true
2
- RSpec.configure do |config|
3
- config.when_first_matching_example_defined(type: :system) do
4
- config.before :suite do
2
+ RSpec.configure do |c|
3
+ Capybara.disable_animation = true
4
+
5
+ c.before(:example, type: :system) do
6
+ driven_by ENV['DEBUG_CAPYBARA'] ? :selenium_chrome : :selenium_chrome_headless
7
+ end
8
+
9
+ c.when_first_matching_example_defined(type: :system) do
10
+ c.before :suite do
5
11
  # Preload assets
6
12
  if Rails.application.respond_to?(:precompiled_assets)
7
13
  Rails.application.precompiled_assets
8
14
  end
9
15
  end
10
-
11
- config.before(:example, type: :system) do
12
- driven_by :apparition
13
- end
14
16
  end
15
17
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solidus_seo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karma Creative
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-05 00:00:00.000000000 Z
11
+ date: 2021-01-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: solidus_core
@@ -337,12 +337,16 @@ files:
337
337
  - app/assets/javascripts/spree/frontend/solidus_seo.js
338
338
  - app/assets/stylesheets/spree/backend/solidus_seo.css
339
339
  - app/assets/stylesheets/spree/frontend/solidus_seo.css
340
+ - app/decorators/controllers/spree/orders_controller_decorator.rb
340
341
  - app/decorators/controllers/spree/store_controller_decorator.rb
341
- - app/decorators/helpers/spree/base_helper_decorator.rb
342
342
  - app/decorators/helpers/spree/core/controller_helpers/common_decorator.rb
343
343
  - app/decorators/models/spree/product_decorator.rb
344
344
  - app/decorators/models/spree/store_decorator.rb
345
345
  - app/views/solidus_seo/_analytics.html.erb
346
+ - app/views/solidus_seo/_facebook.html.erb
347
+ - app/views/solidus_seo/_google-analytics.html.erb
348
+ - app/views/solidus_seo/_google-tag-manager.html.erb
349
+ - app/views/solidus_seo/_pinterest.html.erb
346
350
  - config/locales/en.yml
347
351
  - config/routes.rb
348
352
  - lib/generators/solidus_seo/install/install_generator.rb
@@ -354,6 +358,7 @@ files:
354
358
  - lib/generators/solidus_seo/install/templates/insert_product_list_helper.html.erb.deface
355
359
  - lib/generators/solidus_seo/install/templates/paperclip_optimizer.rb
356
360
  - lib/generators/solidus_seo/install/templates/remove_original_title_tag.deface
361
+ - lib/generators/solidus_seo/install/templates/replace_taxon_breadcrumbs_helper.html.erb.deface
357
362
  - lib/solidus_seo.rb
358
363
  - lib/solidus_seo/engine.rb
359
364
  - lib/solidus_seo/factories.rb
@@ -373,6 +378,7 @@ files:
373
378
  - lib/solidus_seo/model.rb
374
379
  - lib/solidus_seo/version.rb
375
380
  - spec/examples.txt
381
+ - spec/features/add_to_cart_spec.rb
376
382
  - spec/features/checkout_complete_spec.rb
377
383
  - spec/features/homepage_spec.rb
378
384
  - spec/features/product_page_spec.rb
@@ -410,6 +416,7 @@ test_files:
410
416
  - spec/features/checkout_complete_spec.rb
411
417
  - spec/features/homepage_spec.rb
412
418
  - spec/features/taxon_page_spec.rb
419
+ - spec/features/add_to_cart_spec.rb
413
420
  - spec/features/product_page_spec.rb
414
421
  - spec/support/testing_helpers.rb
415
422
  - spec/system_helper.rb
@@ -1,39 +0,0 @@
1
- module Spree
2
- module BaseHelperDecorator
3
- extend ActiveSupport::Concern
4
-
5
- def self.prepended(base)
6
- base.class_eval do
7
- def breadcrumb_pairs(taxon)
8
- crumbs = []
9
-
10
- if taxon
11
- crumbs << [Spree.t(:products), products_url]
12
- crumbs += taxon.ancestors.collect { |a| [a.name, spree.nested_taxons_url(a.permalink)] } unless taxon.ancestors.empty?
13
- crumbs << [taxon.name, spree.nested_taxons_url(taxon.permalink)]
14
- else
15
- crumbs << [Spree.t(:products), products_url]
16
- end
17
-
18
- crumbs
19
- end
20
-
21
- # TODO: Make this method replace original `taxon_breadcrumbs` so we can use super;
22
- # prepending a module with anothe doesn't seem to be working as intended.
23
- def taxon_breadcrumbs_jsonld(taxon, separator = '&nbsp;&raquo;&nbsp;', list_class = 'list-inline', list_item_class = 'list-inline-item')
24
- return '' if current_page?('/') || taxon.nil?
25
-
26
- separator = tag.span(separator.html_safe, class: 'breadcrumb-separator')
27
- original_output = Nokogiri::HTML::DocumentFragment.parse(taxon_breadcrumbs(taxon, separator, list_class).to_s)
28
- original_output.xpath('@itemscope|@itemtype|@itemprop|.//@itemscope|.//@itemtype|.//@itemprop').remove
29
- original_output.search('.columns').first['class'] = ''
30
- original_output.search('li').attr('class', list_item_class)
31
-
32
- jsonld_breadcrumbs(breadcrumb_pairs(taxon)) + original_output.to_s.html_safe
33
- end
34
- end
35
- end
36
-
37
- ::Spree::BaseHelper.prepend(self)
38
- end
39
- end