solidus_seo 1.0.7 → 1.0.8

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 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