spree_core 3.0.5 → 3.0.6

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.
Files changed (194) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/Gemfile +3 -0
  4. data/Rakefile +30 -0
  5. data/app/assets/javascripts/spree.js.coffee.erb +1 -1
  6. data/app/models/spree/ability.rb +1 -1
  7. data/app/models/spree/base.rb +3 -1
  8. data/app/models/spree/order_updater.rb +2 -1
  9. data/app/models/spree/price.rb +7 -12
  10. data/app/models/spree/product.rb +3 -2
  11. data/app/models/spree/reimbursement.rb +1 -1
  12. data/app/models/spree/state.rb +2 -0
  13. data/app/models/spree/zone.rb +1 -1
  14. data/lib/spree/core/version.rb +1 -1
  15. data/lib/spree/testing_support/shoulda_matcher_configuration.rb +6 -0
  16. data/script/rails +9 -0
  17. data/spec/fixtures/thinking-cat.jpg +0 -0
  18. data/spec/helpers/base_helper_spec.rb +137 -0
  19. data/spec/helpers/products_helper_spec.rb +224 -0
  20. data/spec/lib/calculated_adjustments_spec.rb +7 -0
  21. data/spec/lib/i18n_spec.rb +123 -0
  22. data/spec/lib/search/base_spec.rb +86 -0
  23. data/spec/lib/spree/core/controller_helpers/auth_spec.rb +101 -0
  24. data/spec/lib/spree/core/controller_helpers/order_spec.rb +95 -0
  25. data/spec/lib/spree/core/controller_helpers/search_spec.rb +17 -0
  26. data/spec/lib/spree/core/controller_helpers/store_spec.rb +16 -0
  27. data/spec/lib/spree/core/controller_helpers/strong_parameters_spec.rb +39 -0
  28. data/spec/lib/spree/core/delegate_belongs_to_spec.rb +22 -0
  29. data/spec/lib/spree/core/importer/order_spec.rb +502 -0
  30. data/spec/lib/spree/core/validators/email_spec.rb +53 -0
  31. data/spec/lib/spree/localized_number_spec.rb +38 -0
  32. data/spec/lib/spree/migrations_spec.rb +34 -0
  33. data/spec/lib/spree/money_spec.rb +122 -0
  34. data/spec/lib/tasks/exchanges_spec.rb +136 -0
  35. data/spec/mailers/order_mailer_spec.rb +124 -0
  36. data/spec/mailers/reimbursement_mailer_spec.rb +47 -0
  37. data/spec/mailers/shipment_mailer_spec.rb +63 -0
  38. data/spec/mailers/test_mailer_spec.rb +24 -0
  39. data/spec/models/spree/ability_spec.rb +246 -0
  40. data/spec/models/spree/address_spec.rb +291 -0
  41. data/spec/models/spree/adjustable/adjustments_updater_spec.rb +286 -0
  42. data/spec/models/spree/adjustment_spec.rb +163 -0
  43. data/spec/models/spree/app_configuration_spec.rb +23 -0
  44. data/spec/models/spree/asset_spec.rb +25 -0
  45. data/spec/models/spree/calculator/default_tax_spec.rb +127 -0
  46. data/spec/models/spree/calculator/flat_percent_item_total_spec.rb +25 -0
  47. data/spec/models/spree/calculator/flat_rate_spec.rb +47 -0
  48. data/spec/models/spree/calculator/flexi_rate_spec.rb +41 -0
  49. data/spec/models/spree/calculator/percent_on_line_item_spec.rb +15 -0
  50. data/spec/models/spree/calculator/price_sack_spec.rb +30 -0
  51. data/spec/models/spree/calculator/refunds/default_refund_amount_spec.rb +51 -0
  52. data/spec/models/spree/calculator/shipping.rb +8 -0
  53. data/spec/models/spree/calculator/shipping/flat_percent_item_total_spec.rb +23 -0
  54. data/spec/models/spree/calculator/shipping/flat_rate_spec.rb +13 -0
  55. data/spec/models/spree/calculator/shipping/flexi_rate_spec.rb +52 -0
  56. data/spec/models/spree/calculator/shipping/per_item_spec.rb +20 -0
  57. data/spec/models/spree/calculator/shipping/price_sack_spec.rb +29 -0
  58. data/spec/models/spree/calculator/tiered_flat_rate_spec.rb +40 -0
  59. data/spec/models/spree/calculator/tiered_percent_spec.rb +51 -0
  60. data/spec/models/spree/calculator_spec.rb +69 -0
  61. data/spec/models/spree/classification_spec.rb +93 -0
  62. data/spec/models/spree/concerns/display_money_spec.rb +43 -0
  63. data/spec/models/spree/country_spec.rb +18 -0
  64. data/spec/models/spree/credit_card_spec.rb +324 -0
  65. data/spec/models/spree/customer_return_spec.rb +262 -0
  66. data/spec/models/spree/exchange_spec.rb +75 -0
  67. data/spec/models/spree/gateway/bogus_simple.rb +20 -0
  68. data/spec/models/spree/gateway/bogus_spec.rb +13 -0
  69. data/spec/models/spree/gateway_spec.rb +54 -0
  70. data/spec/models/spree/image_spec.rb +5 -0
  71. data/spec/models/spree/inventory_unit_spec.rb +242 -0
  72. data/spec/models/spree/line_item_spec.rb +267 -0
  73. data/spec/models/spree/option_type_spec.rb +14 -0
  74. data/spec/models/spree/option_value_spec.rb +13 -0
  75. data/spec/models/spree/order/address_spec.rb +50 -0
  76. data/spec/models/spree/order/adjustments_spec.rb +29 -0
  77. data/spec/models/spree/order/callbacks_spec.rb +42 -0
  78. data/spec/models/spree/order/checkout_spec.rb +764 -0
  79. data/spec/models/spree/order/currency_updater_spec.rb +32 -0
  80. data/spec/models/spree/order/finalizing_spec.rb +117 -0
  81. data/spec/models/spree/order/helpers_spec.rb +5 -0
  82. data/spec/models/spree/order/payment_spec.rb +214 -0
  83. data/spec/models/spree/order/risk_assessment_spec.rb +84 -0
  84. data/spec/models/spree/order/shipments_spec.rb +43 -0
  85. data/spec/models/spree/order/state_machine_spec.rb +216 -0
  86. data/spec/models/spree/order/tax_spec.rb +84 -0
  87. data/spec/models/spree/order/totals_spec.rb +24 -0
  88. data/spec/models/spree/order/updating_spec.rb +18 -0
  89. data/spec/models/spree/order/validations_spec.rb +15 -0
  90. data/spec/models/spree/order_contents_spec.rb +256 -0
  91. data/spec/models/spree/order_inventory_spec.rb +228 -0
  92. data/spec/models/spree/order_merger_spec.rb +133 -0
  93. data/spec/models/spree/order_spec.rb +954 -0
  94. data/spec/models/spree/order_updater_spec.rb +283 -0
  95. data/spec/models/spree/payment/gateway_options_spec.rb +119 -0
  96. data/spec/models/spree/payment_method_spec.rb +95 -0
  97. data/spec/models/spree/payment_spec.rb +926 -0
  98. data/spec/models/spree/preference_spec.rb +80 -0
  99. data/spec/models/spree/preferences/configuration_spec.rb +30 -0
  100. data/spec/models/spree/preferences/preferable_spec.rb +348 -0
  101. data/spec/models/spree/preferences/scoped_store_spec.rb +58 -0
  102. data/spec/models/spree/preferences/store_spec.rb +46 -0
  103. data/spec/models/spree/price_spec.rb +42 -0
  104. data/spec/models/spree/product/scopes_spec.rb +148 -0
  105. data/spec/models/spree/product_duplicator_spec.rb +103 -0
  106. data/spec/models/spree/product_filter_spec.rb +26 -0
  107. data/spec/models/spree/product_option_type_spec.rb +5 -0
  108. data/spec/models/spree/product_property_spec.rb +11 -0
  109. data/spec/models/spree/product_spec.rb +474 -0
  110. data/spec/models/spree/promotion/actions/create_adjustment_spec.rb +50 -0
  111. data/spec/models/spree/promotion/actions/create_item_adjustments_spec.rb +148 -0
  112. data/spec/models/spree/promotion/actions/create_line_items_spec.rb +86 -0
  113. data/spec/models/spree/promotion/actions/free_shipping_spec.rb +36 -0
  114. data/spec/models/spree/promotion/rules/first_order_spec.rb +75 -0
  115. data/spec/models/spree/promotion/rules/item_total_spec.rb +282 -0
  116. data/spec/models/spree/promotion/rules/one_use_per_user_spec.rb +42 -0
  117. data/spec/models/spree/promotion/rules/option_value_spec.rb +90 -0
  118. data/spec/models/spree/promotion/rules/product_spec.rb +143 -0
  119. data/spec/models/spree/promotion/rules/taxon_spec.rb +102 -0
  120. data/spec/models/spree/promotion/rules/user_logged_in_spec.rb +27 -0
  121. data/spec/models/spree/promotion/rules/user_spec.rb +37 -0
  122. data/spec/models/spree/promotion_action_spec.rb +10 -0
  123. data/spec/models/spree/promotion_category_spec.rb +17 -0
  124. data/spec/models/spree/promotion_handler/cart_spec.rb +102 -0
  125. data/spec/models/spree/promotion_handler/coupon_spec.rb +323 -0
  126. data/spec/models/spree/promotion_handler/free_shipping_spec.rb +48 -0
  127. data/spec/models/spree/promotion_handler/page_spec.rb +44 -0
  128. data/spec/models/spree/promotion_rule_spec.rb +29 -0
  129. data/spec/models/spree/promotion_spec.rb +603 -0
  130. data/spec/models/spree/property_spec.rb +5 -0
  131. data/spec/models/spree/prototype_spec.rb +5 -0
  132. data/spec/models/spree/refund_spec.rb +195 -0
  133. data/spec/models/spree/reimbursement/credit_spec.rb +36 -0
  134. data/spec/models/spree/reimbursement/reimbursement_type_engine_spec.rb +140 -0
  135. data/spec/models/spree/reimbursement/reimbursement_type_validator_spec.rb +83 -0
  136. data/spec/models/spree/reimbursement_performer_spec.rb +30 -0
  137. data/spec/models/spree/reimbursement_spec.rb +215 -0
  138. data/spec/models/spree/reimbursement_tax_calculator_spec.rb +51 -0
  139. data/spec/models/spree/reimbursement_type/credit_spec.rb +53 -0
  140. data/spec/models/spree/reimbursement_type/exchange_spec.rb +46 -0
  141. data/spec/models/spree/reimbursement_type/original_payment_spec.rb +55 -0
  142. data/spec/models/spree/return_authorization_spec.rb +250 -0
  143. data/spec/models/spree/return_item/eligibility_validator/default_spec.rb +77 -0
  144. data/spec/models/spree/return_item/eligibility_validator/inventory_shipped_spec.rb +58 -0
  145. data/spec/models/spree/return_item/eligibility_validator/no_reimbursements_spec.rb +61 -0
  146. data/spec/models/spree/return_item/eligibility_validator/order_completed_spec.rb +32 -0
  147. data/spec/models/spree/return_item/eligibility_validator/rma_required_spec.rb +29 -0
  148. data/spec/models/spree/return_item/eligibility_validator/time_since_purchase_spec.rb +35 -0
  149. data/spec/models/spree/return_item/exchange_variant_eligibility/same_option_value_spec.rb +65 -0
  150. data/spec/models/spree/return_item/exchange_variant_eligibility/same_product_spec.rb +43 -0
  151. data/spec/models/spree/return_item_spec.rb +682 -0
  152. data/spec/models/spree/returns_calculator_spec.rb +14 -0
  153. data/spec/models/spree/shipment_spec.rb +740 -0
  154. data/spec/models/spree/shipping_calculator_spec.rb +45 -0
  155. data/spec/models/spree/shipping_category_spec.rb +5 -0
  156. data/spec/models/spree/shipping_method_spec.rb +88 -0
  157. data/spec/models/spree/shipping_rate_spec.rb +141 -0
  158. data/spec/models/spree/state_spec.rb +18 -0
  159. data/spec/models/spree/stock/availability_validator_spec.rb +36 -0
  160. data/spec/models/spree/stock/content_item_spec.rb +22 -0
  161. data/spec/models/spree/stock/coordinator_spec.rb +51 -0
  162. data/spec/models/spree/stock/differentiator_spec.rb +39 -0
  163. data/spec/models/spree/stock/estimator_spec.rb +154 -0
  164. data/spec/models/spree/stock/inventory_unit_builder_spec.rb +38 -0
  165. data/spec/models/spree/stock/package_spec.rb +194 -0
  166. data/spec/models/spree/stock/packer_spec.rb +70 -0
  167. data/spec/models/spree/stock/prioritizer_spec.rb +125 -0
  168. data/spec/models/spree/stock/quantifier_spec.rb +97 -0
  169. data/spec/models/spree/stock/splitter/backordered_spec.rb +29 -0
  170. data/spec/models/spree/stock/splitter/base_spec.rb +21 -0
  171. data/spec/models/spree/stock/splitter/shipping_category_spec.rb +47 -0
  172. data/spec/models/spree/stock/splitter/weight_spec.rb +32 -0
  173. data/spec/models/spree/stock_item_spec.rb +410 -0
  174. data/spec/models/spree/stock_location_spec.rb +243 -0
  175. data/spec/models/spree/stock_movement_spec.rb +56 -0
  176. data/spec/models/spree/stock_transfer_spec.rb +50 -0
  177. data/spec/models/spree/store_spec.rb +50 -0
  178. data/spec/models/spree/tax_category_spec.rb +27 -0
  179. data/spec/models/spree/tax_rate_spec.rb +382 -0
  180. data/spec/models/spree/taxon_spec.rb +74 -0
  181. data/spec/models/spree/taxonomy_spec.rb +18 -0
  182. data/spec/models/spree/tracker_spec.rb +21 -0
  183. data/spec/models/spree/user_spec.rb +130 -0
  184. data/spec/models/spree/validations/db_maximum_length_validator_spec.rb +24 -0
  185. data/spec/models/spree/variant_spec.rb +523 -0
  186. data/spec/models/spree/zone_spec.rb +444 -0
  187. data/spec/spec_helper.rb +74 -0
  188. data/spec/support/big_decimal.rb +5 -0
  189. data/spec/support/concerns/adjustment_source_spec.rb +23 -0
  190. data/spec/support/concerns/default_price_spec.rb +28 -0
  191. data/spec/support/rake.rb +13 -0
  192. data/spec/support/test_gateway.rb +2 -0
  193. data/spree_core.gemspec +48 -0
  194. metadata +185 -4
@@ -0,0 +1,74 @@
1
+ # coding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Spree::Taxon, :type => :model do
6
+ let(:taxon) { FactoryGirl.build(:taxon, :name => "Ruby on Rails") }
7
+
8
+ describe '#to_param' do
9
+ subject { super().to_param }
10
+ it { is_expected.to eql taxon.permalink }
11
+ end
12
+
13
+ context "set_permalink" do
14
+
15
+ it "should set permalink correctly when no parent present" do
16
+ taxon.set_permalink
17
+ expect(taxon.permalink).to eql "ruby-on-rails"
18
+ end
19
+
20
+ it "should support Chinese characters" do
21
+ taxon.name = "你好"
22
+ taxon.set_permalink
23
+ expect(taxon.permalink).to eql 'ni-hao'
24
+ end
25
+
26
+ context "with parent taxon" do
27
+ let(:parent) { FactoryGirl.build(:taxon, :permalink => "brands") }
28
+ before { allow(taxon).to receive_messages parent: parent }
29
+
30
+ it "should set permalink correctly when taxon has parent" do
31
+ taxon.set_permalink
32
+ expect(taxon.permalink).to eql "brands/ruby-on-rails"
33
+ end
34
+
35
+ it "should set permalink correctly with existing permalink present" do
36
+ taxon.permalink = "b/rubyonrails"
37
+ taxon.set_permalink
38
+ expect(taxon.permalink).to eql "brands/rubyonrails"
39
+ end
40
+
41
+ it "should support Chinese characters" do
42
+ taxon.name = "我"
43
+ taxon.set_permalink
44
+ expect(taxon.permalink).to eql "brands/wo"
45
+ end
46
+
47
+ # Regression test for #3390
48
+ context "setting a new node sibling position via :child_index=" do
49
+ let(:idx) { rand(0..100) }
50
+ before { allow(parent).to receive(:move_to_child_with_index) }
51
+
52
+ context "taxon is not new" do
53
+ before { allow(taxon).to receive(:new_record?).and_return(false) }
54
+
55
+ it "passes the desired index move_to_child_with_index of :parent " do
56
+ expect(taxon).to receive(:move_to_child_with_index).with(parent, idx)
57
+
58
+ taxon.child_index = idx
59
+ end
60
+ end
61
+ end
62
+
63
+ end
64
+ end
65
+
66
+ # Regression test for #2620
67
+ context "creating a child node using first_or_create" do
68
+ let(:taxonomy) { create(:taxonomy) }
69
+
70
+ it "does not error out" do
71
+ expect { taxonomy.root.children.unscoped.where(:name => "Some name").first_or_create }.not_to raise_error
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Taxonomy, :type => :model do
4
+ context "#destroy" do
5
+ before do
6
+ @taxonomy = create(:taxonomy)
7
+ @root_taxon = @taxonomy.root
8
+ @child_taxon = create(:taxon, :taxonomy_id => @taxonomy.id, :parent => @root_taxon)
9
+ end
10
+
11
+ it "should destroy all associated taxons" do
12
+ @taxonomy.destroy
13
+ expect{ Spree::Taxon.find(@root_taxon.id) }.to raise_error(ActiveRecord::RecordNotFound)
14
+ expect{ Spree::Taxon.find(@child_taxon.id) }.to raise_error(ActiveRecord::RecordNotFound)
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Tracker, :type => :model do
4
+ describe "current" do
5
+ before(:each) { @tracker = create(:tracker) }
6
+
7
+ it "returns the first active tracker" do
8
+ expect(Spree::Tracker.current).to eq(@tracker)
9
+ end
10
+
11
+ it "does not return a tracker with a blank analytics_id" do
12
+ @tracker.update_attribute(:analytics_id, '')
13
+ expect(Spree::Tracker.current).to be_nil
14
+ end
15
+
16
+ it "does not return an inactive tracker" do
17
+ @tracker.update_attribute(:active, false)
18
+ expect(Spree::Tracker.current).to be_nil
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::LegacyUser, :type => :model do
4
+ # Regression test for #2844 + #3346
5
+ context "#last_incomplete_order" do
6
+ let!(:user) { create(:user) }
7
+ let!(:order) { create(:order, bill_address: create(:address), ship_address: create(:address)) }
8
+
9
+ let!(:order_1) { create(:order, :created_at => 1.day.ago, :user => user, :created_by => user) }
10
+ let!(:order_2) { create(:order, :user => user, :created_by => user) }
11
+ let!(:order_3) { create(:order, :user => user, :created_by => create(:user)) }
12
+
13
+ it "returns correct order" do
14
+ expect(user.last_incomplete_spree_order).to eq order_3
15
+ end
16
+
17
+ context "persists order address" do
18
+ it "copies over order addresses" do
19
+ expect {
20
+ user.persist_order_address(order)
21
+ }.to change { Spree::Address.count }.by(2)
22
+
23
+ expect(user.bill_address).to eq order.bill_address
24
+ expect(user.ship_address).to eq order.ship_address
25
+ end
26
+
27
+ it "doesnt create new addresses if user has already" do
28
+ user.update_column(:bill_address_id, create(:address))
29
+ user.update_column(:ship_address_id, create(:address))
30
+ user.reload
31
+
32
+ expect {
33
+ user.persist_order_address(order)
34
+ }.not_to change { Spree::Address.count }
35
+ end
36
+
37
+ it "set both bill and ship address id on subject" do
38
+ user.persist_order_address(order)
39
+
40
+ expect(user.bill_address_id).not_to be_blank
41
+ expect(user.ship_address_id).not_to be_blank
42
+ end
43
+ end
44
+
45
+ context "payment source" do
46
+ let(:payment_method) { create(:credit_card_payment_method) }
47
+ let!(:cc) do
48
+ create(:credit_card, user_id: user.id, payment_method: payment_method, gateway_customer_profile_id: "2342343")
49
+ end
50
+
51
+ it "has payment sources" do
52
+ expect(user.payment_sources.first.gateway_customer_profile_id).not_to be_empty
53
+ end
54
+
55
+ it "drops payment source" do
56
+ user.drop_payment_source cc
57
+ expect(cc.gateway_customer_profile_id).to be_nil
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ describe Spree.user_class, :type => :model do
64
+ context "reporting" do
65
+ let(:order_value) { BigDecimal.new("80.94") }
66
+ let(:order_count) { 4 }
67
+ let(:orders) { Array.new(order_count, double(total: order_value)) }
68
+
69
+ before do
70
+ allow(orders).to receive(:pluck).with(:total).and_return(orders.map(&:total))
71
+ allow(orders).to receive(:count).and_return(orders.length)
72
+ end
73
+
74
+ def load_orders
75
+ allow(subject).to receive(:orders).and_return(double(complete: orders))
76
+ end
77
+
78
+ describe "#lifetime_value" do
79
+ context "with orders" do
80
+ before { load_orders }
81
+ it "returns the total of completed orders for the user" do
82
+ expect(subject.lifetime_value).to eq (order_count * order_value)
83
+ end
84
+ end
85
+ context "without orders" do
86
+ it "returns 0.00" do
87
+ expect(subject.lifetime_value).to eq BigDecimal("0.00")
88
+ end
89
+ end
90
+ end
91
+
92
+ describe "#display_lifetime_value" do
93
+ it "returns a Spree::Money version of lifetime_value" do
94
+ value = BigDecimal("500.05")
95
+ allow(subject).to receive(:lifetime_value).and_return(value)
96
+ expect(subject.display_lifetime_value).to eq Spree::Money.new(value)
97
+ end
98
+ end
99
+
100
+ describe "#order_count" do
101
+ before { load_orders }
102
+ it "returns the count of completed orders for the user" do
103
+ expect(subject.order_count).to eq BigDecimal(order_count)
104
+ end
105
+ end
106
+
107
+ describe "#average_order_value" do
108
+ context "with orders" do
109
+ before { load_orders }
110
+ it "returns the average completed order price for the user" do
111
+ expect(subject.average_order_value).to eq order_value
112
+ end
113
+ end
114
+ context "without orders" do
115
+ it "returns 0.00" do
116
+ expect(subject.average_order_value).to eq BigDecimal("0.00")
117
+ end
118
+ end
119
+ end
120
+
121
+ describe "#display_average_order_value" do
122
+ before { load_orders }
123
+ it "returns a Spree::Money version of average_order_value" do
124
+ value = BigDecimal("500.05")
125
+ allow(subject).to receive(:average_order_value).and_return(value)
126
+ expect(subject.display_average_order_value).to eq Spree::Money.new(value)
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Validations::DbMaximumLengthValidator, :type => :model do
4
+ context 'when Spree::Product' do
5
+ Spree::Product.class_eval do
6
+ attribute :slug, ActiveRecord::Type::String.new(limit: 255)
7
+ # Slug currently has no validation for maximum length
8
+ validates_with Spree::Validations::DbMaximumLengthValidator, field: :slug
9
+ end
10
+
11
+ let(:limit) { 255 }
12
+ let(:product) { Spree::Product.new }
13
+ let(:slug) { "x" * (limit + 1)}
14
+
15
+ before do
16
+ product.slug = slug
17
+ end
18
+
19
+ it 'should maximum validate slug' do
20
+ product.valid?
21
+ expect(product.errors[:slug]).to include(I18n.t("errors.messages.too_long", count: limit))
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,523 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Spree::Variant, :type => :model do
6
+ let!(:variant) { create(:variant) }
7
+
8
+ it_behaves_like 'default_price'
9
+
10
+ context 'sorting' do
11
+ it 'responds to set_list_position' do
12
+ expect(variant.respond_to?(:set_list_position)).to eq(true)
13
+ end
14
+ end
15
+
16
+ context "validations" do
17
+ it "should validate price is greater than 0" do
18
+ variant.price = -1
19
+ expect(variant).to be_invalid
20
+ end
21
+
22
+ it "should validate price is 0" do
23
+ variant.price = 0
24
+ expect(variant).to be_valid
25
+ end
26
+ end
27
+
28
+ context "after create" do
29
+ let!(:product) { create(:product) }
30
+
31
+ it "propagate to stock items" do
32
+ expect_any_instance_of(Spree::StockLocation).to receive(:propagate_variant)
33
+ product.variants.create(:name => "Foobar")
34
+ end
35
+
36
+ context "stock location has disable propagate all variants" do
37
+ before { Spree::StockLocation.update_all propagate_all_variants: false }
38
+
39
+ it "propagate to stock items" do
40
+ expect_any_instance_of(Spree::StockLocation).not_to receive(:propagate_variant)
41
+ product.variants.create(:name => "Foobar")
42
+ end
43
+ end
44
+
45
+ describe 'mark_master_out_of_stock' do
46
+ before do
47
+ product.master.stock_items.first.set_count_on_hand(5)
48
+ end
49
+ context 'when product is created without variants but with stock' do
50
+ it { expect(product.master).to be_in_stock }
51
+ end
52
+
53
+ context 'when a variant is created' do
54
+ before(:each) do
55
+ product.variants.create!(:name => 'any-name')
56
+ end
57
+
58
+ it { expect(product.master).to_not be_in_stock }
59
+ end
60
+ end
61
+ end
62
+
63
+ context "product has other variants" do
64
+ describe "option value accessors" do
65
+ before {
66
+ @multi_variant = FactoryGirl.create :variant, :product => variant.product
67
+ variant.product.reload
68
+ }
69
+
70
+ let(:multi_variant) { @multi_variant }
71
+
72
+ it "should set option value" do
73
+ expect(multi_variant.option_value('media_type')).to be_nil
74
+
75
+ multi_variant.set_option_value('media_type', 'DVD')
76
+ expect(multi_variant.option_value('media_type')).to eql 'DVD'
77
+
78
+ multi_variant.set_option_value('media_type', 'CD')
79
+ expect(multi_variant.option_value('media_type')).to eql 'CD'
80
+ end
81
+
82
+ it "should not duplicate associated option values when set multiple times" do
83
+ multi_variant.set_option_value('media_type', 'CD')
84
+
85
+ expect {
86
+ multi_variant.set_option_value('media_type', 'DVD')
87
+ }.to_not change(multi_variant.option_values, :count)
88
+
89
+ expect {
90
+ multi_variant.set_option_value('coolness_type', 'awesome')
91
+ }.to change(multi_variant.option_values, :count).by(1)
92
+ end
93
+ end
94
+
95
+ context "product has other variants" do
96
+ describe "option value accessors" do
97
+ before {
98
+ @multi_variant = create(:variant, :product => variant.product)
99
+ variant.product.reload
100
+ }
101
+
102
+ let(:multi_variant) { @multi_variant }
103
+
104
+ it "should set option value" do
105
+ expect(multi_variant.option_value('media_type')).to be_nil
106
+
107
+ multi_variant.set_option_value('media_type', 'DVD')
108
+ expect(multi_variant.option_value('media_type')).to eql 'DVD'
109
+
110
+ multi_variant.set_option_value('media_type', 'CD')
111
+ expect(multi_variant.option_value('media_type')).to eql 'CD'
112
+ end
113
+
114
+ it "should not duplicate associated option values when set multiple times" do
115
+ multi_variant.set_option_value('media_type', 'CD')
116
+
117
+ expect {
118
+ multi_variant.set_option_value('media_type', 'DVD')
119
+ }.to_not change(multi_variant.option_values, :count)
120
+
121
+ expect {
122
+ multi_variant.set_option_value('coolness_type', 'awesome')
123
+ }.to change(multi_variant.option_values, :count).by(1)
124
+ end
125
+ end
126
+ end
127
+ end
128
+
129
+ context "#cost_price=" do
130
+ it "should use LocalizedNumber.parse" do
131
+ expect(Spree::LocalizedNumber).to receive(:parse).with('1,599.99')
132
+ subject.cost_price = '1,599.99'
133
+ end
134
+ end
135
+
136
+ context "#price=" do
137
+ it "should use LocalizedNumber.parse" do
138
+ expect(Spree::LocalizedNumber).to receive(:parse).with('1,599.99')
139
+ subject.price = '1,599.99'
140
+ end
141
+ end
142
+
143
+ context "#weight=" do
144
+ it "should use LocalizedNumber.parse" do
145
+ expect(Spree::LocalizedNumber).to receive(:parse).with('1,599.99')
146
+ subject.weight = '1,599.99'
147
+ end
148
+ end
149
+
150
+ context "#currency" do
151
+ it "returns the globally configured currency" do
152
+ expect(variant.currency).to eql "USD"
153
+ end
154
+ end
155
+
156
+ context "#display_amount" do
157
+ it "returns a Spree::Money" do
158
+ variant.price = 21.22
159
+ expect(variant.display_amount.to_s).to eql "$21.22"
160
+ end
161
+ end
162
+
163
+ context "#cost_currency" do
164
+ context "when cost currency is nil" do
165
+ before { variant.cost_currency = nil }
166
+ it "populates cost currency with the default value on save" do
167
+ variant.save!
168
+ expect(variant.cost_currency).to eql "USD"
169
+ end
170
+ end
171
+ end
172
+
173
+ describe '.price_in' do
174
+ before do
175
+ variant.prices << create(:price, :variant => variant, :currency => "EUR", :amount => 33.33)
176
+ end
177
+ subject { variant.price_in(currency).display_amount }
178
+
179
+ context "when currency is not specified" do
180
+ let(:currency) { nil }
181
+
182
+ it "returns 0" do
183
+ expect(subject.to_s).to eql "$0.00"
184
+ end
185
+ end
186
+
187
+ context "when currency is EUR" do
188
+ let(:currency) { 'EUR' }
189
+
190
+ it "returns the value in the EUR" do
191
+ expect(subject.to_s).to eql "€33.33"
192
+ end
193
+ end
194
+
195
+ context "when currency is USD" do
196
+ let(:currency) { 'USD' }
197
+
198
+ it "returns the value in the USD" do
199
+ expect(subject.to_s).to eql "$19.99"
200
+ end
201
+ end
202
+ end
203
+
204
+ describe '.amount_in' do
205
+ before do
206
+ variant.prices << create(:price, :variant => variant, :currency => "EUR", :amount => 33.33)
207
+ end
208
+
209
+ subject { variant.amount_in(currency) }
210
+
211
+ context "when currency is not specified" do
212
+ let(:currency) { nil }
213
+
214
+ it "returns nil" do
215
+ expect(subject).to be_nil
216
+ end
217
+ end
218
+
219
+ context "when currency is EUR" do
220
+ let(:currency) { 'EUR' }
221
+
222
+ it "returns the value in the EUR" do
223
+ expect(subject).to eql 33.33
224
+ end
225
+ end
226
+
227
+ context "when currency is USD" do
228
+ let(:currency) { 'USD' }
229
+
230
+ it "returns the value in the USD" do
231
+ expect(subject).to eql 19.99
232
+ end
233
+ end
234
+ end
235
+
236
+ # Regression test for #2432
237
+ describe 'options_text' do
238
+ let!(:variant) { create(:variant, option_values: []) }
239
+ let!(:master) { create(:master_variant) }
240
+
241
+ before do
242
+ # Order bar than foo
243
+ variant.option_values << create(:option_value, {name: 'Foo', presentation: 'Foo', option_type: create(:option_type, position: 2, name: 'Foo Type', presentation: 'Foo Type')})
244
+ variant.option_values << create(:option_value, {name: 'Bar', presentation: 'Bar', option_type: create(:option_type, position: 1, name: 'Bar Type', presentation: 'Bar Type')})
245
+ end
246
+
247
+ it 'should order by bar than foo' do
248
+ expect(variant.options_text).to eql 'Bar Type: Bar, Foo Type: Foo'
249
+ end
250
+
251
+ end
252
+
253
+ describe 'exchange_name' do
254
+ let!(:variant) { create(:variant, option_values: []) }
255
+ let!(:master) { create(:master_variant) }
256
+
257
+ before do
258
+ variant.option_values << create(:option_value, {
259
+ name: 'Foo',
260
+ presentation: 'Foo',
261
+ option_type: create(:option_type, position: 2, name: 'Foo Type', presentation: 'Foo Type')
262
+ })
263
+ end
264
+
265
+ context 'master variant' do
266
+ it 'should return name' do
267
+ expect(master.exchange_name).to eql master.name
268
+ end
269
+ end
270
+
271
+ context 'variant' do
272
+ it 'should return options text' do
273
+ expect(variant.exchange_name).to eql 'Foo Type: Foo'
274
+ end
275
+ end
276
+
277
+ end
278
+
279
+ describe 'exchange_name' do
280
+ let!(:variant) { create(:variant, option_values: []) }
281
+ let!(:master) { create(:master_variant) }
282
+
283
+ before do
284
+ variant.option_values << create(:option_value, {
285
+ name: 'Foo',
286
+ presentation: 'Foo',
287
+ option_type: create(:option_type, position: 2, name: 'Foo Type', presentation: 'Foo Type')
288
+ })
289
+ end
290
+
291
+ context 'master variant' do
292
+ it 'should return name' do
293
+ expect(master.exchange_name).to eql master.name
294
+ end
295
+ end
296
+
297
+ context 'variant' do
298
+ it 'should return options text' do
299
+ expect(variant.exchange_name).to eql 'Foo Type: Foo'
300
+ end
301
+ end
302
+
303
+ end
304
+
305
+ describe 'descriptive_name' do
306
+ let!(:variant) { create(:variant, option_values: []) }
307
+ let!(:master) { create(:master_variant) }
308
+
309
+ before do
310
+ variant.option_values << create(:option_value, {
311
+ name: 'Foo',
312
+ presentation: 'Foo',
313
+ option_type: create(:option_type, position: 2, name: 'Foo Type', presentation: 'Foo Type')
314
+ })
315
+ end
316
+
317
+ context 'master variant' do
318
+ it 'should return name with Master identifier' do
319
+ expect(master.descriptive_name).to eql master.name + ' - Master'
320
+ end
321
+ end
322
+
323
+ context 'variant' do
324
+ it 'should return options text with name' do
325
+ expect(variant.descriptive_name).to eql variant.name + ' - Foo Type: Foo'
326
+ end
327
+ end
328
+
329
+ end
330
+
331
+ # Regression test for #2744
332
+ describe "set_position" do
333
+ it "sets variant position after creation" do
334
+ variant = create(:variant)
335
+ expect(variant.position).to_not be_nil
336
+ end
337
+ end
338
+
339
+ describe '#in_stock?' do
340
+ before do
341
+ Spree::Config.track_inventory_levels = true
342
+ end
343
+
344
+ context 'when stock_items are not backorderable' do
345
+ before do
346
+ allow_any_instance_of(Spree::StockItem).to receive_messages(backorderable: false)
347
+ end
348
+
349
+ context 'when stock_items in stock' do
350
+ before do
351
+ variant.stock_items.first.update_column(:count_on_hand, 10)
352
+ end
353
+
354
+ it 'returns true if stock_items in stock' do
355
+ expect(variant.in_stock?).to be true
356
+ end
357
+ end
358
+
359
+ context 'when stock_items out of stock' do
360
+ before do
361
+ allow_any_instance_of(Spree::StockItem).to receive_messages(backorderable: false)
362
+ allow_any_instance_of(Spree::StockItem).to receive_messages(count_on_hand: 0)
363
+ end
364
+
365
+ it 'return false if stock_items out of stock' do
366
+ expect(variant.in_stock?).to be false
367
+ end
368
+ end
369
+ end
370
+
371
+ describe "#can_supply?" do
372
+ it "calls out to quantifier" do
373
+ expect(Spree::Stock::Quantifier).to receive(:new).and_return(quantifier = double)
374
+ expect(quantifier).to receive(:can_supply?).with(10)
375
+ variant.can_supply?(10)
376
+ end
377
+ end
378
+
379
+ context 'when stock_items are backorderable' do
380
+ before do
381
+ allow_any_instance_of(Spree::StockItem).to receive_messages(backorderable: true)
382
+ end
383
+
384
+ context 'when stock_items out of stock' do
385
+ before do
386
+ allow_any_instance_of(Spree::StockItem).to receive_messages(count_on_hand: 0)
387
+ end
388
+
389
+ it 'in_stock? returns false' do
390
+ expect(variant.in_stock?).to be false
391
+ end
392
+
393
+ it 'can_supply? return true' do
394
+ expect(variant.can_supply?).to be true
395
+ end
396
+ end
397
+ end
398
+ end
399
+
400
+ describe '#is_backorderable' do
401
+ let(:variant) { build(:variant) }
402
+ subject { variant.is_backorderable? }
403
+
404
+ it 'should invoke Spree::Stock::Quantifier' do
405
+ expect_any_instance_of(Spree::Stock::Quantifier).to receive(:backorderable?) { true }
406
+ subject
407
+ end
408
+ end
409
+
410
+ describe '#total_on_hand' do
411
+ it 'should be infinite if track_inventory_levels is false' do
412
+ Spree::Config[:track_inventory_levels] = false
413
+ expect(build(:variant).total_on_hand).to eql(Float::INFINITY)
414
+ end
415
+
416
+ it 'should match quantifier total_on_hand' do
417
+ variant = build(:variant)
418
+ expect(variant.total_on_hand).to eq(Spree::Stock::Quantifier.new(variant).total_on_hand)
419
+ end
420
+ end
421
+
422
+ describe '#tax_category' do
423
+ context 'when tax_category is nil' do
424
+ let(:product) { build(:product) }
425
+ let(:variant) { build(:variant, product: product, tax_category_id: nil) }
426
+ it 'returns the parent products tax_category' do
427
+ expect(variant.tax_category).to eq(product.tax_category)
428
+ end
429
+ end
430
+
431
+ context 'when tax_category is set' do
432
+ let(:tax_category) { create(:tax_category) }
433
+ let(:variant) { build(:variant, tax_category: tax_category) }
434
+ it 'returns the tax_category set on itself' do
435
+ expect(variant.tax_category).to eq(tax_category)
436
+ end
437
+ end
438
+ end
439
+
440
+ describe "touching" do
441
+ it "updates a product" do
442
+ variant.product.update_column(:updated_at, 1.day.ago)
443
+ variant.touch
444
+ expect(variant.product.reload.updated_at).to be_within(3.seconds).of(Time.now)
445
+ end
446
+
447
+ it "clears the in_stock cache key" do
448
+ expect(Rails.cache).to receive(:delete).with(variant.send(:in_stock_cache_key))
449
+ variant.touch
450
+ end
451
+ end
452
+
453
+ describe "#should_track_inventory?" do
454
+
455
+ it 'should not track inventory when global setting is off' do
456
+ Spree::Config[:track_inventory_levels] = false
457
+
458
+ expect(build(:variant).should_track_inventory?).to eq(false)
459
+ end
460
+
461
+ it 'should not track inventory when variant is turned off' do
462
+ Spree::Config[:track_inventory_levels] = true
463
+
464
+ expect(build(:on_demand_variant).should_track_inventory?).to eq(false)
465
+ end
466
+
467
+ it 'should track inventory when global and variant are on' do
468
+ Spree::Config[:track_inventory_levels] = true
469
+
470
+ expect(build(:variant).should_track_inventory?).to eq(true)
471
+ end
472
+ end
473
+
474
+ describe "deleted_at scope" do
475
+ before { variant.destroy && variant.reload }
476
+ it "should have a price if deleted" do
477
+ variant.price = 10
478
+ expect(variant.price).to eq(10)
479
+ end
480
+ end
481
+
482
+ describe "stock movements" do
483
+ let!(:movement) { create(:stock_movement, stock_item: variant.stock_items.first) }
484
+
485
+ it "builds out collection just fine through stock items" do
486
+ expect(variant.stock_movements.to_a).not_to be_empty
487
+ end
488
+ end
489
+
490
+ describe "in_stock scope" do
491
+ it "returns all in stock variants" do
492
+ in_stock_variant = create(:variant)
493
+ out_of_stock_variant = create(:variant)
494
+
495
+ in_stock_variant.stock_items.first.update_column(:count_on_hand, 10)
496
+
497
+ expect(Spree::Variant.in_stock).to eq [in_stock_variant]
498
+ end
499
+ end
500
+
501
+ context "#volume" do
502
+ let(:variant_zero_width) { create(:variant, width: 0) }
503
+ let(:variant) { create(:variant) }
504
+
505
+ it "it is zero if any dimension parameter is zero" do
506
+ expect(variant_zero_width.volume).to eq 0
507
+ end
508
+
509
+ it "return the volume if the dimension parameters are different of zero" do
510
+ volume_expected = variant.width * variant.depth * variant.height
511
+ expect(variant.volume).to eq (volume_expected)
512
+ end
513
+ end
514
+
515
+ context "#dimension" do
516
+ let(:variant) { create(:variant) }
517
+
518
+ it "return the dimension if the dimension parameters are different of zero" do
519
+ dimension_expected = variant.width + variant.depth + variant.height
520
+ expect(variant.dimension).to eq (dimension_expected)
521
+ end
522
+ end
523
+ end