solidus_core 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -0
  3. data/Rakefile +16 -0
  4. data/script/rails +9 -0
  5. data/solidus_core.gemspec +48 -0
  6. data/spec/fixtures/thinking-cat.jpg +0 -0
  7. data/spec/helpers/base_helper_spec.rb +173 -0
  8. data/spec/helpers/order_helper_spec.rb +12 -0
  9. data/spec/helpers/products_helper_spec.rb +208 -0
  10. data/spec/helpers/taxons_helper_spec.rb +17 -0
  11. data/spec/lib/calculated_adjustments_spec.rb +7 -0
  12. data/spec/lib/i18n_spec.rb +106 -0
  13. data/spec/lib/search/base_spec.rb +86 -0
  14. data/spec/lib/search/variant_spec.rb +112 -0
  15. data/spec/lib/spree/core/controller_helpers/auth_spec.rb +66 -0
  16. data/spec/lib/spree/core/controller_helpers/order_spec.rb +92 -0
  17. data/spec/lib/spree/core/controller_helpers/payment_parameters_spec.rb +80 -0
  18. data/spec/lib/spree/core/controller_helpers/search_spec.rb +17 -0
  19. data/spec/lib/spree/core/controller_helpers/store_spec.rb +16 -0
  20. data/spec/lib/spree/core/controller_helpers/strong_parameters_spec.rb +39 -0
  21. data/spec/lib/spree/core/current_store_spec.rb +36 -0
  22. data/spec/lib/spree/core/delegate_belongs_to_spec.rb +24 -0
  23. data/spec/lib/spree/core/importer/order_spec.rb +431 -0
  24. data/spec/lib/spree/core/role_configuration_spec.rb +156 -0
  25. data/spec/lib/spree/core/unreturned_item_charger_spec.rb +130 -0
  26. data/spec/lib/spree/core/validators/email_spec.rb +48 -0
  27. data/spec/lib/spree/localized_number_spec.rb +38 -0
  28. data/spec/lib/spree/migrations_spec.rb +36 -0
  29. data/spec/lib/spree/money_spec.rb +127 -0
  30. data/spec/lib/tasks/exchanges_spec.rb +231 -0
  31. data/spec/lib/tasks/migrations/copy_shipped_shipments_to_cartons_spec.rb +115 -0
  32. data/spec/lib/tasks/order_capturing_spec.rb +56 -0
  33. data/spec/mailers/carton_mailer_spec.rb +55 -0
  34. data/spec/mailers/order_mailer_spec.rb +135 -0
  35. data/spec/mailers/reimbursement_mailer_spec.rb +40 -0
  36. data/spec/mailers/test_mailer_spec.rb +15 -0
  37. data/spec/models/spree/ability_spec.rb +276 -0
  38. data/spec/models/spree/address_spec.rb +376 -0
  39. data/spec/models/spree/adjustment_reason_spec.rb +13 -0
  40. data/spec/models/spree/adjustment_spec.rb +169 -0
  41. data/spec/models/spree/app_configuration_spec.rb +24 -0
  42. data/spec/models/spree/asset_spec.rb +24 -0
  43. data/spec/models/spree/calculator/default_tax_spec.rb +127 -0
  44. data/spec/models/spree/calculator/flat_percent_item_total_spec.rb +25 -0
  45. data/spec/models/spree/calculator/flat_rate_spec.rb +47 -0
  46. data/spec/models/spree/calculator/flexi_rate_spec.rb +41 -0
  47. data/spec/models/spree/calculator/percent_on_line_item_spec.rb +15 -0
  48. data/spec/models/spree/calculator/price_sack_spec.rb +30 -0
  49. data/spec/models/spree/calculator/refunds/default_refund_amount_spec.rb +60 -0
  50. data/spec/models/spree/calculator/shipping/flat_percent_item_total_spec.rb +23 -0
  51. data/spec/models/spree/calculator/shipping/flat_rate_spec.rb +13 -0
  52. data/spec/models/spree/calculator/shipping/flexi_rate_spec.rb +52 -0
  53. data/spec/models/spree/calculator/shipping/per_item_spec.rb +20 -0
  54. data/spec/models/spree/calculator/shipping/price_sack_spec.rb +30 -0
  55. data/spec/models/spree/calculator/tiered_flat_rate_spec.rb +36 -0
  56. data/spec/models/spree/calculator/tiered_percent_spec.rb +47 -0
  57. data/spec/models/spree/calculator_spec.rb +36 -0
  58. data/spec/models/spree/carton_spec.rb +133 -0
  59. data/spec/models/spree/classification_spec.rb +93 -0
  60. data/spec/models/spree/concerns/display_money_spec.rb +43 -0
  61. data/spec/models/spree/concerns/ordered_property_value_list_spec.rb +25 -0
  62. data/spec/models/spree/concerns/user_address_book_spec.rb +332 -0
  63. data/spec/models/spree/concerns/user_methods_spec.rb +41 -0
  64. data/spec/models/spree/credit_card_spec.rb +341 -0
  65. data/spec/models/spree/customer_return_spec.rb +276 -0
  66. data/spec/models/spree/exchange_spec.rb +79 -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 +104 -0
  70. data/spec/models/spree/inventory_unit_spec.rb +307 -0
  71. data/spec/models/spree/item_adjustments_spec.rb +275 -0
  72. data/spec/models/spree/line_item_spec.rb +199 -0
  73. data/spec/models/spree/option_type_spec.rb +14 -0
  74. data/spec/models/spree/option_value_spec.rb +45 -0
  75. data/spec/models/spree/order/address_spec.rb +50 -0
  76. data/spec/models/spree/order/adjustments_spec.rb +27 -0
  77. data/spec/models/spree/order/callbacks_spec.rb +42 -0
  78. data/spec/models/spree/order/checkout_spec.rb +884 -0
  79. data/spec/models/spree/order/currency_updater_spec.rb +32 -0
  80. data/spec/models/spree/order/finalizing_spec.rb +110 -0
  81. data/spec/models/spree/order/payment_spec.rb +243 -0
  82. data/spec/models/spree/order/risk_assessment_spec.rb +68 -0
  83. data/spec/models/spree/order/state_machine_spec.rb +209 -0
  84. data/spec/models/spree/order/tax_spec.rb +84 -0
  85. data/spec/models/spree/order/totals_spec.rb +24 -0
  86. data/spec/models/spree/order/updating_spec.rb +18 -0
  87. data/spec/models/spree/order/validations_spec.rb +15 -0
  88. data/spec/models/spree/order_cancellations_spec.rb +120 -0
  89. data/spec/models/spree/order_capturing_spec.rb +150 -0
  90. data/spec/models/spree/order_contents_spec.rb +307 -0
  91. data/spec/models/spree/order_inventory_spec.rb +228 -0
  92. data/spec/models/spree/order_mutex_spec.rb +85 -0
  93. data/spec/models/spree/order_promotion_spec.rb +31 -0
  94. data/spec/models/spree/order_shipping_spec.rb +241 -0
  95. data/spec/models/spree/order_spec.rb +1482 -0
  96. data/spec/models/spree/order_stock_location_spec.rb +18 -0
  97. data/spec/models/spree/order_updater_spec.rb +283 -0
  98. data/spec/models/spree/payment_method/store_credit_spec.rb +294 -0
  99. data/spec/models/spree/payment_method_spec.rb +147 -0
  100. data/spec/models/spree/payment_spec.rb +1087 -0
  101. data/spec/models/spree/permission_sets/base_spec.rb +12 -0
  102. data/spec/models/spree/permission_sets/configuration_display.rb +82 -0
  103. data/spec/models/spree/permission_sets/configuration_management_spec.rb +50 -0
  104. data/spec/models/spree/permission_sets/dashboard_display_spec.rb +22 -0
  105. data/spec/models/spree/permission_sets/order_display_spec.rb +55 -0
  106. data/spec/models/spree/permission_sets/order_management_spec.rb +42 -0
  107. data/spec/models/spree/permission_sets/product_display_spec.rb +60 -0
  108. data/spec/models/spree/permission_sets/product_management_spec.rb +40 -0
  109. data/spec/models/spree/permission_sets/promotion_display_spec.rb +40 -0
  110. data/spec/models/spree/permission_sets/promotion_management_spec.rb +26 -0
  111. data/spec/models/spree/permission_sets/report_display_spec.rb +24 -0
  112. data/spec/models/spree/permission_sets/restricted_stock_display_spec.rb +41 -0
  113. data/spec/models/spree/permission_sets/restricted_stock_management_spec.rb +41 -0
  114. data/spec/models/spree/permission_sets/restricted_stock_transfer_display_spec.rb +50 -0
  115. data/spec/models/spree/permission_sets/restricted_stock_transfer_management_spec.rb +160 -0
  116. data/spec/models/spree/permission_sets/stock_display_spec.rb +24 -0
  117. data/spec/models/spree/permission_sets/stock_management_spec.rb +22 -0
  118. data/spec/models/spree/permission_sets/stock_transfer_display_spec.rb +24 -0
  119. data/spec/models/spree/permission_sets/stock_transfer_management_spec.rb +25 -0
  120. data/spec/models/spree/permission_sets/user_display_spec.rb +38 -0
  121. data/spec/models/spree/permission_sets/user_management_spec.rb +48 -0
  122. data/spec/models/spree/preference_spec.rb +80 -0
  123. data/spec/models/spree/preferences/configuration_spec.rb +30 -0
  124. data/spec/models/spree/preferences/preferable_spec.rb +294 -0
  125. data/spec/models/spree/preferences/scoped_store_spec.rb +60 -0
  126. data/spec/models/spree/preferences/static_model_preferences_spec.rb +78 -0
  127. data/spec/models/spree/preferences/statically_configurable_spec.rb +60 -0
  128. data/spec/models/spree/preferences/store_spec.rb +39 -0
  129. data/spec/models/spree/price_spec.rb +42 -0
  130. data/spec/models/spree/product/scopes_spec.rb +116 -0
  131. data/spec/models/spree/product_duplicator_spec.rb +103 -0
  132. data/spec/models/spree/product_filter_spec.rb +26 -0
  133. data/spec/models/spree/product_property_spec.rb +18 -0
  134. data/spec/models/spree/product_spec.rb +504 -0
  135. data/spec/models/spree/promotion/actions/create_adjustment_spec.rb +96 -0
  136. data/spec/models/spree/promotion/actions/create_item_adjustments_spec.rb +165 -0
  137. data/spec/models/spree/promotion/actions/create_quantity_adjustments_spec.rb +115 -0
  138. data/spec/models/spree/promotion/actions/free_shipping_spec.rb +40 -0
  139. data/spec/models/spree/promotion/rules/first_order_spec.rb +75 -0
  140. data/spec/models/spree/promotion/rules/first_repeat_purchase_since_spec.rb +69 -0
  141. data/spec/models/spree/promotion/rules/item_total_spec.rb +67 -0
  142. data/spec/models/spree/promotion/rules/nth_order_spec.rb +70 -0
  143. data/spec/models/spree/promotion/rules/one_use_per_user_spec.rb +42 -0
  144. data/spec/models/spree/promotion/rules/option_value_spec.rb +94 -0
  145. data/spec/models/spree/promotion/rules/product_spec.rb +143 -0
  146. data/spec/models/spree/promotion/rules/taxon_spec.rb +102 -0
  147. data/spec/models/spree/promotion/rules/user_logged_in_spec.rb +27 -0
  148. data/spec/models/spree/promotion/rules/user_spec.rb +37 -0
  149. data/spec/models/spree/promotion_builder_spec.rb +118 -0
  150. data/spec/models/spree/promotion_category_spec.rb +17 -0
  151. data/spec/models/spree/promotion_code/code_builder_spec.rb +79 -0
  152. data/spec/models/spree/promotion_code_spec.rb +187 -0
  153. data/spec/models/spree/promotion_handler/cart_spec.rb +130 -0
  154. data/spec/models/spree/promotion_handler/coupon_spec.rb +335 -0
  155. data/spec/models/spree/promotion_handler/free_shipping_spec.rb +47 -0
  156. data/spec/models/spree/promotion_handler/page_spec.rb +44 -0
  157. data/spec/models/spree/promotion_rule_spec.rb +28 -0
  158. data/spec/models/spree/promotion_spec.rb +776 -0
  159. data/spec/models/spree/refund_spec.rb +192 -0
  160. data/spec/models/spree/reimbursement/credit_spec.rb +36 -0
  161. data/spec/models/spree/reimbursement/reimbursement_type_engine_spec.rb +140 -0
  162. data/spec/models/spree/reimbursement/reimbursement_type_validator_spec.rb +83 -0
  163. data/spec/models/spree/reimbursement_performer_spec.rb +30 -0
  164. data/spec/models/spree/reimbursement_spec.rb +231 -0
  165. data/spec/models/spree/reimbursement_tax_calculator_spec.rb +51 -0
  166. data/spec/models/spree/reimbursement_type/credit_spec.rb +53 -0
  167. data/spec/models/spree/reimbursement_type/exchange_spec.rb +46 -0
  168. data/spec/models/spree/reimbursement_type/original_payment_spec.rb +107 -0
  169. data/spec/models/spree/reimbursement_type/store_credit_spec.rb +97 -0
  170. data/spec/models/spree/return_authorization_spec.rb +290 -0
  171. data/spec/models/spree/return_item/eligibility_validator/default_spec.rb +77 -0
  172. data/spec/models/spree/return_item/eligibility_validator/inventory_shipped_spec.rb +58 -0
  173. data/spec/models/spree/return_item/eligibility_validator/no_reimbursements_spec.rb +85 -0
  174. data/spec/models/spree/return_item/eligibility_validator/order_completed_spec.rb +32 -0
  175. data/spec/models/spree/return_item/eligibility_validator/rma_required_spec.rb +29 -0
  176. data/spec/models/spree/return_item/eligibility_validator/time_since_purchase_spec.rb +35 -0
  177. data/spec/models/spree/return_item/exchange_variant_eligibility/same_option_value_spec.rb +65 -0
  178. data/spec/models/spree/return_item/exchange_variant_eligibility/same_product_spec.rb +43 -0
  179. data/spec/models/spree/return_item_spec.rb +776 -0
  180. data/spec/models/spree/returns_calculator_spec.rb +14 -0
  181. data/spec/models/spree/shipment_spec.rb +753 -0
  182. data/spec/models/spree/shipping_calculator_spec.rb +45 -0
  183. data/spec/models/spree/shipping_manifest_spec.rb +94 -0
  184. data/spec/models/spree/shipping_method_spec.rb +88 -0
  185. data/spec/models/spree/shipping_rate_spec.rb +142 -0
  186. data/spec/models/spree/state_spec.rb +14 -0
  187. data/spec/models/spree/stock/availability_validator_spec.rb +83 -0
  188. data/spec/models/spree/stock/coordinator_spec.rb +116 -0
  189. data/spec/models/spree/stock/differentiator_spec.rb +39 -0
  190. data/spec/models/spree/stock/estimator_spec.rb +146 -0
  191. data/spec/models/spree/stock/inventory_unit_builder_spec.rb +38 -0
  192. data/spec/models/spree/stock/package_spec.rb +163 -0
  193. data/spec/models/spree/stock/packer_spec.rb +91 -0
  194. data/spec/models/spree/stock/prioritizer_spec.rb +125 -0
  195. data/spec/models/spree/stock/quantifier_spec.rb +115 -0
  196. data/spec/models/spree/stock/splitter/backordered_spec.rb +29 -0
  197. data/spec/models/spree/stock/splitter/base_spec.rb +21 -0
  198. data/spec/models/spree/stock/splitter/shipping_category_spec.rb +50 -0
  199. data/spec/models/spree/stock/splitter/weight_spec.rb +29 -0
  200. data/spec/models/spree/stock_item_spec.rb +444 -0
  201. data/spec/models/spree/stock_location_spec.rb +279 -0
  202. data/spec/models/spree/stock_movement_spec.rb +56 -0
  203. data/spec/models/spree/stock_transfer_spec.rb +290 -0
  204. data/spec/models/spree/store_credit_category_spec.rb +17 -0
  205. data/spec/models/spree/store_credit_event_spec.rb +314 -0
  206. data/spec/models/spree/store_credit_spec.rb +876 -0
  207. data/spec/models/spree/store_spec.rb +55 -0
  208. data/spec/models/spree/tax_category_spec.rb +27 -0
  209. data/spec/models/spree/tax_rate_spec.rb +378 -0
  210. data/spec/models/spree/taxon_spec.rb +74 -0
  211. data/spec/models/spree/taxonomy_spec.rb +18 -0
  212. data/spec/models/spree/tracker_spec.rb +21 -0
  213. data/spec/models/spree/transfer_item_spec.rb +264 -0
  214. data/spec/models/spree/unit_cancel_spec.rb +149 -0
  215. data/spec/models/spree/user_spec.rb +246 -0
  216. data/spec/models/spree/validations/db_maximum_length_validator_spec.rb +23 -0
  217. data/spec/models/spree/variant/scopes_spec.rb +55 -0
  218. data/spec/models/spree/variant_property_rule_condition_spec.rb +15 -0
  219. data/spec/models/spree/variant_property_rule_spec.rb +83 -0
  220. data/spec/models/spree/variant_property_rule_value_spec.rb +18 -0
  221. data/spec/models/spree/variant_spec.rb +601 -0
  222. data/spec/models/spree/zone_spec.rb +305 -0
  223. data/spec/spec_helper.rb +80 -0
  224. data/spec/support/big_decimal.rb +5 -0
  225. data/spec/support/concerns/default_price.rb +34 -0
  226. data/spec/support/dummy_ability.rb +4 -0
  227. data/spec/support/test_gateway.rb +2 -0
  228. metadata +242 -2
@@ -0,0 +1,246 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::LegacyUser, :type => :model do
4
+ context "#last_incomplete_order" do
5
+ let!(:user) { create(:user) }
6
+
7
+
8
+ it "excludes orders that are not frontend_viewable" do
9
+ order = create(:order, user: user, frontend_viewable: false)
10
+ expect(user.last_incomplete_spree_order).to eq nil
11
+ end
12
+
13
+ it "can include orders that are not frontend viewable" do
14
+ order = create(:order, user: user, frontend_viewable: false)
15
+ expect(user.last_incomplete_spree_order(only_frontend_viewable: false)).to eq order
16
+ end
17
+
18
+ it "can scope to a store" do
19
+ store = create(:store)
20
+ store_1_order = create(:order, user: user, store: store)
21
+ store_2_order = create(:order, user: user, store: create(:store))
22
+ expect(user.last_incomplete_spree_order(store: store)).to eq store_1_order
23
+ end
24
+
25
+ it "excludes completed orders" do
26
+ order = create(:completed_order_with_totals, user: user, created_by: user)
27
+ expect(user.last_incomplete_spree_order).to eq nil
28
+ end
29
+
30
+ it "excludes orders created prior to the user's last completed order" do
31
+ incomplete_order = create(:order, user: user, created_by: user, created_at: 1.second.ago)
32
+ completed_order = create(:completed_order_with_totals, user: user, created_by: user)
33
+ expect(user.last_incomplete_spree_order).to eq nil
34
+ end
35
+
36
+ context "with completable_order_created_cutoff set" do
37
+ before do
38
+ @original_order_cutoff_preference = Spree::Config.completable_order_created_cutoff_days
39
+ Spree::Config.completable_order_created_cutoff_days = 1
40
+ end
41
+
42
+ after { Spree::Config.completable_order_created_cutoff_days = @original_order_cutoff_preference }
43
+
44
+ it "excludes orders updated outside of the cutoff date" do
45
+ incomplete_order = create(:order, user: user, created_by: user, created_at: 3.days.ago, updated_at: 2.days.ago)
46
+ expect(user.last_incomplete_spree_order).to eq nil
47
+ end
48
+ end
49
+
50
+ context "with completable_order_created_cutoff set" do
51
+ before do
52
+ @original_order_cutoff_preference = Spree::Config.completable_order_updated_cutoff_days
53
+ Spree::Config.completable_order_updated_cutoff_days = 1
54
+ end
55
+
56
+ after { Spree::Config.completable_order_updated_cutoff_days = @original_order_cutoff_preference }
57
+
58
+ it "excludes orders updated outside of the cutoff date" do
59
+ incomplete_order = create(:order, user: user, created_by: user, created_at: 3.days.ago, updated_at: 2.days.ago)
60
+ expect(user.last_incomplete_spree_order).to eq nil
61
+ end
62
+ end
63
+
64
+ it "chooses the most recently created incomplete order" do
65
+ order_1 = create(:order, user: user, created_at: 1.second.ago)
66
+ order_2 = create(:order, user: user)
67
+ expect(user.last_incomplete_spree_order).to eq order_2
68
+ end
69
+
70
+ context "persists order address" do
71
+ let(:bill_address) { create(:address) }
72
+ let(:ship_address) { create(:address) }
73
+ let(:order) { create(:order, user: user, bill_address: bill_address, ship_address: ship_address) }
74
+
75
+ it "doesn't create new addresses" do
76
+ user.user_addresses.create(address: bill_address)
77
+ user.user_addresses.create(address: ship_address)
78
+ user.reload
79
+
80
+ expect {
81
+ user.persist_order_address(order)
82
+ }.not_to change { Spree::Address.count }
83
+ end
84
+
85
+ it "associates both the bill and ship address to the user" do
86
+ user.persist_order_address(order)
87
+ user.save!
88
+ user.user_addresses.reload
89
+
90
+ expect(user.user_addresses.find_first_by_address_values(order.bill_address.attributes)).to_not be_nil
91
+ expect(user.user_addresses.find_first_by_address_values(order.ship_address.attributes)).to_not be_nil
92
+ end
93
+ end
94
+
95
+ context "payment source" do
96
+ let(:payment_method) { create(:credit_card_payment_method) }
97
+ let!(:cc) do
98
+ create(:credit_card, user_id: user.id, payment_method: payment_method, gateway_customer_profile_id: "2342343")
99
+ end
100
+
101
+ it "has payment sources" do
102
+ expect(user.payment_sources.first.gateway_customer_profile_id).not_to be_empty
103
+ end
104
+
105
+ it "drops payment source" do
106
+ user.drop_payment_source cc
107
+ expect(cc.gateway_customer_profile_id).to be_nil
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ describe Spree.user_class, :type => :model do
114
+ context "reporting" do
115
+ let(:order_value) { BigDecimal.new("80.94") }
116
+ let(:order_count) { 4 }
117
+ let(:orders) { Array.new(order_count, double(total: order_value)) }
118
+
119
+ before do
120
+ allow(orders).to receive(:pluck).with(:total).and_return(orders.map(&:total))
121
+ allow(orders).to receive(:count).and_return(orders.length)
122
+ end
123
+
124
+ def load_orders
125
+ allow(subject).to receive(:spree_orders).and_return(double(complete: orders))
126
+ end
127
+
128
+ describe "#lifetime_value" do
129
+ context "with orders" do
130
+ before { load_orders }
131
+ it "returns the total of completed orders for the user" do
132
+ expect(subject.lifetime_value).to eq (order_count * order_value)
133
+ end
134
+ end
135
+ context "without orders" do
136
+ it "returns 0.00" do
137
+ expect(subject.lifetime_value).to eq BigDecimal("0.00")
138
+ end
139
+ end
140
+ end
141
+
142
+ describe "#display_lifetime_value" do
143
+ it "returns a Spree::Money version of lifetime_value" do
144
+ value = BigDecimal("500.05")
145
+ allow(subject).to receive(:lifetime_value).and_return(value)
146
+ expect(subject.display_lifetime_value).to eq Spree::Money.new(value)
147
+ end
148
+ end
149
+
150
+ describe "#order_count" do
151
+ before { load_orders }
152
+ it "returns the count of completed orders for the user" do
153
+ expect(subject.order_count).to eq BigDecimal(order_count)
154
+ end
155
+ end
156
+
157
+ describe "#average_order_value" do
158
+ context "with orders" do
159
+ before { load_orders }
160
+ it "returns the average completed order price for the user" do
161
+ expect(subject.average_order_value).to eq order_value
162
+ end
163
+ end
164
+ context "without orders" do
165
+ it "returns 0.00" do
166
+ expect(subject.average_order_value).to eq BigDecimal("0.00")
167
+ end
168
+ end
169
+ end
170
+
171
+ describe "#display_average_order_value" do
172
+ before { load_orders }
173
+ it "returns a Spree::Money version of average_order_value" do
174
+ value = BigDecimal("500.05")
175
+ allow(subject).to receive(:average_order_value).and_return(value)
176
+ expect(subject.display_average_order_value).to eq Spree::Money.new(value)
177
+ end
178
+ end
179
+ end
180
+
181
+ describe "#total_available_store_credit" do
182
+ context "user does not have any associated store credits" do
183
+ subject { create(:user) }
184
+
185
+ it "returns 0" do
186
+ expect(subject.total_available_store_credit).to be_zero
187
+ end
188
+ end
189
+
190
+ context "user has several associated store credits" do
191
+ let(:user) { create(:user) }
192
+ let(:amount) { 120.25 }
193
+ let(:additional_amount) { 55.75 }
194
+ let(:store_credit) { create(:store_credit, user: user, amount: amount, amount_used: 0.0) }
195
+ let!(:additional_store_credit) { create(:store_credit, user: user, amount: additional_amount, amount_used: 0.0) }
196
+
197
+ subject { store_credit.user }
198
+
199
+ context "part of the store credit has been used" do
200
+ let(:amount_used) { 35.00 }
201
+
202
+ before { store_credit.update_attributes(amount_used: amount_used) }
203
+
204
+ context "part of the store credit has been authorized" do
205
+ let(:authorized_amount) { 10 }
206
+
207
+ before { additional_store_credit.update_attributes(amount_authorized: authorized_amount) }
208
+
209
+ it "returns sum of amounts minus used amount and authorized amount" do
210
+ expect(subject.total_available_store_credit.to_f).to eq (amount + additional_amount - amount_used - authorized_amount)
211
+ end
212
+ end
213
+
214
+ context "there are no authorized amounts on any of the store credits" do
215
+ it "returns sum of amounts minus used amount" do
216
+ expect(subject.total_available_store_credit.to_f).to eq (amount + additional_amount - amount_used)
217
+ end
218
+ end
219
+ end
220
+
221
+ context "store credits have never been used" do
222
+ context "part of the store credit has been authorized" do
223
+ let(:authorized_amount) { 10 }
224
+
225
+ before { additional_store_credit.update_attributes(amount_authorized: authorized_amount) }
226
+
227
+ it "returns sum of amounts minus authorized amount" do
228
+ expect(subject.total_available_store_credit.to_f).to eq (amount + additional_amount - authorized_amount)
229
+ end
230
+ end
231
+
232
+ context "there are no authorized amounts on any of the store credits" do
233
+ it "returns sum of amounts" do
234
+ expect(subject.total_available_store_credit.to_f).to eq (amount + additional_amount)
235
+ end
236
+ end
237
+ end
238
+
239
+ context "all store credits have never been used or authorized" do
240
+ it "returns sum of amounts" do
241
+ expect(subject.total_available_store_credit.to_f).to eq (amount + additional_amount)
242
+ end
243
+ end
244
+ end
245
+ end
246
+ end
@@ -0,0 +1,23 @@
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
+ let(:limit) { 255 }
11
+ let(:product) { Spree::Product.new }
12
+ let(:slug) { "x" * (limit + 1)}
13
+
14
+ before do
15
+ product.slug = slug
16
+ end
17
+
18
+ it 'should maximum validate slug' do
19
+ product.valid?
20
+ expect(product.errors[:slug]).to include(I18n.t("errors.messages.too_long", count: limit))
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Variant scopes", :type => :model do
4
+ let!(:product) { create(:product) }
5
+ let!(:variant_1) { create(:variant, :product => product) }
6
+ let!(:variant_2) { create(:variant, :product => product) }
7
+
8
+ it ".descend_by_popularity" do
9
+ # Requires a product with at least two variants, where one has a higher number of
10
+ # orders than the other
11
+ Spree::LineItem.delete_all # FIXME leaky database - too many line_items
12
+ create(:line_item, :variant => variant_1)
13
+ expect(Spree::Variant.descend_by_popularity.first).to eq(variant_1)
14
+ end
15
+
16
+
17
+ context "finding by option values" do
18
+ let!(:option_type) { create(:option_type, :name => "bar") }
19
+ let!(:option_value_1) do
20
+ option_value = create(:option_value, :name => "foo", :option_type => option_type)
21
+ variant_1.option_values << option_value
22
+ option_value
23
+ end
24
+
25
+ let!(:option_value_2) do
26
+ option_value = create(:option_value, :name => "fizz", :option_type => option_type)
27
+ variant_1.option_values << option_value
28
+ option_value
29
+ end
30
+
31
+ let!(:product_variants) { product.variants_including_master }
32
+
33
+ it "by objects" do
34
+ variants = product_variants.has_option(option_type, option_value_1)
35
+ expect(variants).to include(variant_1)
36
+ expect(variants).not_to include(variant_2)
37
+ end
38
+
39
+ it "by names" do
40
+ variants = product_variants.has_option("bar", "foo")
41
+ expect(variants).to include(variant_1)
42
+ expect(variants).not_to include(variant_2)
43
+ end
44
+
45
+ it "by ids" do
46
+ variants = product_variants.has_option(option_type.id, option_value_1.id)
47
+ expect(variants).to include(variant_1)
48
+ expect(variants).not_to include(variant_2)
49
+ end
50
+
51
+ it "by mixed conditions" do
52
+ variants = product_variants.has_option(option_type.id, "foo", option_value_2)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::VariantPropertyRuleCondition, type: :model do
4
+ context "touching" do
5
+ let(:rule_condition) { create(:variant_property_rule_condition) }
6
+
7
+ before do
8
+ rule_condition.variant_property_rule.update_columns(updated_at: 1.day.ago)
9
+ end
10
+
11
+ it "should update the variant property rule" do
12
+ expect { rule_condition.touch }.to change { rule_condition.reload.variant_property_rule.updated_at }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::VariantPropertyRule, type: :model do
4
+ context "touching" do
5
+ let(:rule) { create(:variant_property_rule) }
6
+
7
+ before do
8
+ rule.product.update_columns(updated_at: 1.day.ago)
9
+ end
10
+
11
+ it "should update the product" do
12
+ expect { rule.touch }.to change { rule.reload.product.updated_at }
13
+ end
14
+ end
15
+
16
+ describe "#matches_option_value_ids?" do
17
+ let(:first_condition_option_value) { create(:option_value) }
18
+ let(:second_condition_option_value) { create(:option_value) }
19
+ let!(:second_condition) do
20
+ create(:variant_property_rule_condition,
21
+ variant_property_rule: rule,
22
+ option_value: second_condition_option_value)
23
+ end
24
+ let(:rule) { create(:variant_property_rule, option_value: first_condition_option_value) }
25
+
26
+ context "provided ids are the same as the rule's condition's option value ids" do
27
+ subject do
28
+ rule.matches_option_value_ids?([second_condition_option_value.id, first_condition_option_value.id])
29
+ end
30
+
31
+ it { is_expected.to eq true }
32
+ end
33
+
34
+ context "some of the provided ids are the same as the rule's condition's option value ids" do
35
+ subject do
36
+ rule.matches_option_value_ids?([first_condition_option_value.id])
37
+ end
38
+
39
+ it { is_expected.to eq false }
40
+ end
41
+
42
+ context "none of the provided ids are the same as the rule's condition's option value ids" do
43
+ let(:other_option_value) { create(:option_value) }
44
+
45
+ subject do
46
+ rule.matches_option_value_ids?([other_option_value.id])
47
+ end
48
+
49
+ it { is_expected.to eq false }
50
+ end
51
+ end
52
+
53
+ describe "#applies_to_variant?" do
54
+ let(:variant_option_value_1) { create(:option_value) }
55
+ let(:variant_option_value_2) { create(:option_value) }
56
+ let!(:variant) { create(:variant, option_values: option_values)}
57
+
58
+ let(:rule_option_value) { create(:option_value) }
59
+ let(:rule) { create(:variant_property_rule, option_value: rule_option_value) }
60
+ let!(:rule_condition_1) { create(:variant_property_rule_condition, variant_property_rule: rule, option_value: variant_option_value_1) }
61
+ let!(:rule_condition_2) { create(:variant_property_rule_condition, variant_property_rule: rule, option_value: variant_option_value_2) }
62
+
63
+ subject { rule.applies_to_variant?(variant) }
64
+
65
+ context "variant matches some of the rule's conditions" do
66
+ let(:option_values) { [variant_option_value_1, variant_option_value_2] }
67
+
68
+ it { is_expected.to eq true }
69
+ end
70
+
71
+ context "variant matches none of the rule's conditions" do
72
+ let(:option_values) { [create(:option_value)] }
73
+
74
+ it { is_expected.to eq false }
75
+ end
76
+
77
+ context "variant matches all of the rule's conditions" do
78
+ let(:option_values) { [rule_option_value, variant_option_value_1, variant_option_value_2] }
79
+
80
+ it { is_expected.to eq true }
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::VariantPropertyRuleValue, type: :model do
4
+ context "touching" do
5
+ let(:rule_value) { create(:variant_property_rule_value) }
6
+ let(:rule) { rule_value.variant_property_rule }
7
+
8
+ before do
9
+ rule.update_columns(updated_at: 1.day.ago)
10
+ end
11
+
12
+ subject { rule_value.touch }
13
+
14
+ it "touches the variant property rule" do
15
+ expect { subject }.to change { rule.reload.updated_at }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,601 @@
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 "validations" do
11
+ it "should validate price is greater than 0" do
12
+ variant.price = -1
13
+ expect(variant).to be_invalid
14
+ end
15
+
16
+ it "should validate price is 0" do
17
+ variant.price = 0
18
+ expect(variant).to be_valid
19
+ end
20
+ end
21
+
22
+ context "after create" do
23
+ let!(:product) { create(:product) }
24
+
25
+ it "propagate to stock items" do
26
+ expect_any_instance_of(Spree::StockLocation).to receive(:propagate_variant)
27
+ product.variants.create!
28
+ end
29
+
30
+ context "stock location has disable propagate all variants" do
31
+ before { Spree::StockLocation.update_all propagate_all_variants: false }
32
+
33
+ it "propagate to stock items" do
34
+ expect_any_instance_of(Spree::StockLocation).not_to receive(:propagate_variant)
35
+ product.variants.create!
36
+ end
37
+ end
38
+
39
+ describe 'mark_master_out_of_stock' do
40
+ before do
41
+ product.master.stock_items.first.set_count_on_hand(5)
42
+ end
43
+ context 'when product is created without variants but with stock' do
44
+ it { expect(product.master).to be_in_stock }
45
+ end
46
+
47
+ context 'when a variant is created' do
48
+ before(:each) do
49
+ product.variants.create!
50
+ end
51
+
52
+ it { expect(product.master).to_not be_in_stock }
53
+ end
54
+ end
55
+ end
56
+
57
+ context "product has other variants" do
58
+ describe "option value accessors" do
59
+ before {
60
+ @multi_variant = FactoryGirl.create :variant, :product => variant.product
61
+ variant.product.reload
62
+ }
63
+
64
+ let(:multi_variant) { @multi_variant }
65
+
66
+ it "should set option value" do
67
+ expect(multi_variant.option_value('media_type')).to be_nil
68
+
69
+ multi_variant.set_option_value('media_type', 'DVD')
70
+ expect(multi_variant.option_value('media_type')).to eql 'DVD'
71
+
72
+ multi_variant.set_option_value('media_type', 'CD')
73
+ expect(multi_variant.option_value('media_type')).to eql 'CD'
74
+ end
75
+
76
+ it "should not duplicate associated option values when set multiple times" do
77
+ multi_variant.set_option_value('media_type', 'CD')
78
+
79
+ expect {
80
+ multi_variant.set_option_value('media_type', 'DVD')
81
+ }.to_not change(multi_variant.option_values, :count)
82
+
83
+ expect {
84
+ multi_variant.set_option_value('coolness_type', 'awesome')
85
+ }.to change(multi_variant.option_values, :count).by(1)
86
+ end
87
+ end
88
+
89
+ context "product has other variants" do
90
+ describe "option value accessors" do
91
+ before {
92
+ @multi_variant = create(:variant, :product => variant.product)
93
+ variant.product.reload
94
+ }
95
+
96
+ let(:multi_variant) { @multi_variant }
97
+
98
+ it "should set option value" do
99
+ expect(multi_variant.option_value('media_type')).to be_nil
100
+
101
+ multi_variant.set_option_value('media_type', 'DVD')
102
+ expect(multi_variant.option_value('media_type')).to eql 'DVD'
103
+
104
+ multi_variant.set_option_value('media_type', 'CD')
105
+ expect(multi_variant.option_value('media_type')).to eql 'CD'
106
+ end
107
+
108
+ it "should not duplicate associated option values when set multiple times" do
109
+ multi_variant.set_option_value('media_type', 'CD')
110
+
111
+ expect {
112
+ multi_variant.set_option_value('media_type', 'DVD')
113
+ }.to_not change(multi_variant.option_values, :count)
114
+
115
+ expect {
116
+ multi_variant.set_option_value('coolness_type', 'awesome')
117
+ }.to change(multi_variant.option_values, :count).by(1)
118
+ end
119
+ end
120
+ end
121
+ end
122
+
123
+ context "#cost_price=" do
124
+ it "should use LocalizedNumber.parse" do
125
+ expect(Spree::LocalizedNumber).to receive(:parse).with('1,599.99')
126
+ subject.cost_price = '1,599.99'
127
+ end
128
+ end
129
+
130
+ context "#price=" do
131
+ it "should use LocalizedNumber.parse" do
132
+ expect(Spree::LocalizedNumber).to receive(:parse).with('1,599.99')
133
+ subject.price = '1,599.99'
134
+ end
135
+ end
136
+
137
+ context "#weight=" do
138
+ it "should use LocalizedNumber.parse" do
139
+ expect(Spree::LocalizedNumber).to receive(:parse).with('1,599.99')
140
+ subject.weight = '1,599.99'
141
+ end
142
+ end
143
+
144
+ context "#currency" do
145
+ it "returns the globally configured currency" do
146
+ expect(variant.currency).to eql "USD"
147
+ end
148
+ end
149
+
150
+ context "#display_amount" do
151
+ it "returns a Spree::Money" do
152
+ variant.price = 21.22
153
+ expect(variant.display_amount.to_s).to eql "$21.22"
154
+ end
155
+ end
156
+
157
+ context "#cost_currency" do
158
+ context "when cost currency is nil" do
159
+ before { variant.cost_currency = nil }
160
+ it "populates cost currency with the default value on save" do
161
+ variant.save!
162
+ expect(variant.cost_currency).to eql "USD"
163
+ end
164
+ end
165
+ end
166
+
167
+ context "#default_price" do
168
+ context "when multiple prices are present in addition to a default price" do
169
+ before do
170
+ variant.prices << create(:price, :variant => variant, :currency => "USD", :amount => 12.12, :is_default => false)
171
+ variant.prices << create(:price, :variant => variant, :currency => "EUR", :amount => 29.99)
172
+ variant.prices << create(:price, :variant => variant, :currency => "EUR", :amount => 10.00, :is_default => false)
173
+ variant.reload
174
+ end
175
+
176
+ it "the default stays the same" do
177
+ expect(variant.default_price.amount).to eq(19.99)
178
+ end
179
+
180
+ it "displays default price" do
181
+ expect(variant.price_in("USD").display_amount.to_s).to eq("$19.99")
182
+ expect(variant.price_in("EUR").display_amount.to_s).to eq("€29.99")
183
+ end
184
+ end
185
+
186
+ context "when adding multiple prices" do
187
+ it "it can reassign a default price" do
188
+ expect(variant.default_price.amount).to eq(19.99)
189
+ variant.prices << create(:price, :variant => variant, :currency => "USD", :amount => 12.12)
190
+ expect(variant.reload.default_price.amount).to eq(12.12)
191
+ end
192
+ end
193
+ end
194
+
195
+ describe '.price_in' do
196
+ before do
197
+ variant.prices << create(:price, :variant => variant, :currency => "EUR", :amount => 33.33)
198
+ end
199
+ subject { variant.price_in(currency).display_amount }
200
+
201
+ context "when currency is not specified" do
202
+ let(:currency) { nil }
203
+
204
+ it "returns 0" do
205
+ expect(subject.to_s).to eql "$0.00"
206
+ end
207
+ end
208
+
209
+ context "when currency is EUR" do
210
+ let(:currency) { 'EUR' }
211
+
212
+ it "returns the value in the EUR" do
213
+ expect(subject.to_s).to eql "€33.33"
214
+ end
215
+ end
216
+
217
+ context "when currency is USD" do
218
+ let(:currency) { 'USD' }
219
+
220
+ it "returns the value in the USD" do
221
+ expect(subject.to_s).to eql "$19.99"
222
+ end
223
+ end
224
+ end
225
+
226
+ describe '.amount_in' do
227
+ before do
228
+ variant.prices << create(:price, :variant => variant, :currency => "EUR", :amount => 33.33)
229
+ end
230
+
231
+ subject { variant.amount_in(currency) }
232
+
233
+ context "when currency is not specified" do
234
+ let(:currency) { nil }
235
+
236
+ it "returns nil" do
237
+ expect(subject).to be_nil
238
+ end
239
+ end
240
+
241
+ context "when currency is EUR" do
242
+ let(:currency) { 'EUR' }
243
+
244
+ it "returns the value in the EUR" do
245
+ expect(subject).to eql 33.33
246
+ end
247
+ end
248
+
249
+ context "when currency is USD" do
250
+ let(:currency) { 'USD' }
251
+
252
+ it "returns the value in the USD" do
253
+ expect(subject).to eql 19.99
254
+ end
255
+ end
256
+ end
257
+
258
+ # Regression test for #2432
259
+ describe 'options_text' do
260
+ let!(:variant) { create(:variant, option_values: []) }
261
+ let!(:master) { create(:master_variant) }
262
+
263
+ before do
264
+ # Order bar than foo
265
+ variant.option_values << create(:option_value, {name: 'Foo', presentation: 'Foo', option_type: create(:option_type, position: 2, name: 'Foo Type', presentation: 'Foo Type')})
266
+ variant.option_values << create(:option_value, {name: 'Bar', presentation: 'Bar', option_type: create(:option_type, position: 1, name: 'Bar Type', presentation: 'Bar Type')})
267
+ end
268
+
269
+ it 'should order by bar than foo' do
270
+ expect(variant.options_text).to eql 'Bar Type: Bar, Foo Type: Foo'
271
+ end
272
+
273
+ end
274
+
275
+ describe 'exchange_name' do
276
+ let!(:variant) { create(:variant, option_values: []) }
277
+ let!(:master) { create(:master_variant) }
278
+
279
+ before do
280
+ variant.option_values << create(:option_value, {
281
+ name: 'Foo',
282
+ presentation: 'Foo',
283
+ option_type: create(:option_type, position: 2, name: 'Foo Type', presentation: 'Foo Type')
284
+ })
285
+ end
286
+
287
+ context 'master variant' do
288
+ it 'should return name' do
289
+ expect(master.exchange_name).to eql master.name
290
+ end
291
+ end
292
+
293
+ context 'variant' do
294
+ it 'should return options text' do
295
+ expect(variant.exchange_name).to eql 'Foo Type: Foo'
296
+ end
297
+ end
298
+
299
+ end
300
+
301
+ describe 'exchange_name' do
302
+ let!(:variant) { create(:variant, option_values: []) }
303
+ let!(:master) { create(:master_variant) }
304
+
305
+ before do
306
+ variant.option_values << create(:option_value, {
307
+ name: 'Foo',
308
+ presentation: 'Foo',
309
+ option_type: create(:option_type, position: 2, name: 'Foo Type', presentation: 'Foo Type')
310
+ })
311
+ end
312
+
313
+ context 'master variant' do
314
+ it 'should return name' do
315
+ expect(master.exchange_name).to eql master.name
316
+ end
317
+ end
318
+
319
+ context 'variant' do
320
+ it 'should return options text' do
321
+ expect(variant.exchange_name).to eql 'Foo Type: Foo'
322
+ end
323
+ end
324
+
325
+ end
326
+
327
+ describe 'descriptive_name' do
328
+ let!(:variant) { create(:variant, option_values: []) }
329
+ let!(:master) { create(:master_variant) }
330
+
331
+ before do
332
+ variant.option_values << create(:option_value, {
333
+ name: 'Foo',
334
+ presentation: 'Foo',
335
+ option_type: create(:option_type, position: 2, name: 'Foo Type', presentation: 'Foo Type')
336
+ })
337
+ end
338
+
339
+ context 'master variant' do
340
+ it 'should return name with Master identifier' do
341
+ expect(master.descriptive_name).to eql master.name + ' - Master'
342
+ end
343
+ end
344
+
345
+ context 'variant' do
346
+ it 'should return options text with name' do
347
+ expect(variant.descriptive_name).to eql variant.name + ' - Foo Type: Foo'
348
+ end
349
+ end
350
+
351
+ end
352
+
353
+ # Regression test for #2744
354
+ describe "set_position" do
355
+ it "sets variant position after creation" do
356
+ variant = create(:variant)
357
+ expect(variant.position).to_not be_nil
358
+ end
359
+ end
360
+
361
+ describe '#in_stock?' do
362
+ before do
363
+ Spree::Config.track_inventory_levels = true
364
+ end
365
+
366
+ context 'when stock_items are not backorderable' do
367
+ before do
368
+ allow_any_instance_of(Spree::StockItem).to receive_messages(backorderable: false)
369
+ end
370
+
371
+ context 'when stock_items in stock' do
372
+ before do
373
+ variant.stock_items.first.update_column(:count_on_hand, 10)
374
+ end
375
+
376
+ it 'returns true if stock_items in stock' do
377
+ expect(variant.in_stock?).to be true
378
+ end
379
+ end
380
+
381
+ context 'when stock_items out of stock' do
382
+ before do
383
+ allow_any_instance_of(Spree::StockItem).to receive_messages(backorderable: false)
384
+ allow_any_instance_of(Spree::StockItem).to receive_messages(count_on_hand: 0)
385
+ end
386
+
387
+ it 'return false if stock_items out of stock' do
388
+ expect(variant.in_stock?).to be false
389
+ end
390
+ end
391
+ end
392
+
393
+ describe "#can_supply?" do
394
+ it "calls out to quantifier" do
395
+ expect(Spree::Stock::Quantifier).to receive(:new).and_return(quantifier = double)
396
+ expect(quantifier).to receive(:can_supply?).with(10)
397
+ variant.can_supply?(10)
398
+ end
399
+ end
400
+
401
+ context 'when stock_items are backorderable' do
402
+ before do
403
+ allow_any_instance_of(Spree::StockItem).to receive_messages(backorderable: true)
404
+ end
405
+
406
+ context 'when stock_items out of stock' do
407
+ before do
408
+ allow_any_instance_of(Spree::StockItem).to receive_messages(count_on_hand: 0)
409
+ end
410
+
411
+ it 'in_stock? returns false' do
412
+ expect(variant.in_stock?).to be false
413
+ end
414
+
415
+ it 'can_supply? return true' do
416
+ expect(variant.can_supply?).to be true
417
+ end
418
+ end
419
+ end
420
+ end
421
+
422
+ describe '#is_backorderable' do
423
+ let(:variant) { build(:variant) }
424
+ subject { variant.is_backorderable? }
425
+
426
+ it 'should invoke Spree::Stock::Quantifier' do
427
+ expect_any_instance_of(Spree::Stock::Quantifier).to receive(:backorderable?) { true }
428
+ subject
429
+ end
430
+ end
431
+
432
+ describe '#total_on_hand' do
433
+ it 'should be infinite if track_inventory_levels is false' do
434
+ Spree::Config[:track_inventory_levels] = false
435
+ expect(build(:variant).total_on_hand).to eql(Float::INFINITY)
436
+ end
437
+
438
+ it 'should match quantifier total_on_hand' do
439
+ variant = build(:variant)
440
+ expect(variant.total_on_hand).to eq(Spree::Stock::Quantifier.new(variant).total_on_hand)
441
+ end
442
+ end
443
+
444
+ describe '#tax_category' do
445
+ context 'when tax_category is nil' do
446
+ let(:product) { build(:product) }
447
+ let(:variant) { build(:variant, product: product, tax_category_id: nil) }
448
+ it 'returns the parent products tax_category' do
449
+ expect(variant.tax_category).to eq(product.tax_category)
450
+ end
451
+ end
452
+
453
+ context 'when tax_category is set' do
454
+ let(:tax_category) { create(:tax_category) }
455
+ let(:variant) { build(:variant, tax_category: tax_category) }
456
+ it 'returns the tax_category set on itself' do
457
+ expect(variant.tax_category).to eq(tax_category)
458
+ end
459
+ end
460
+ end
461
+
462
+ describe "touching" do
463
+ it "updates a product" do
464
+ variant.product.update_column(:updated_at, 1.day.ago)
465
+ variant.touch
466
+ expect(variant.product.reload.updated_at).to be_within(3.seconds).of(Time.now)
467
+ end
468
+
469
+ it "clears the in_stock cache key" do
470
+ expect(Rails.cache).to receive(:delete).with(variant.send(:in_stock_cache_key))
471
+ variant.touch
472
+ end
473
+ end
474
+
475
+ describe "#should_track_inventory?" do
476
+
477
+ it 'should not track inventory when global setting is off' do
478
+ Spree::Config[:track_inventory_levels] = false
479
+
480
+ expect(build(:variant).should_track_inventory?).to eq(false)
481
+ end
482
+
483
+ it 'should not track inventory when variant is turned off' do
484
+ Spree::Config[:track_inventory_levels] = true
485
+
486
+ expect(build(:on_demand_variant).should_track_inventory?).to eq(false)
487
+ end
488
+
489
+ it 'should track inventory when global and variant are on' do
490
+ Spree::Config[:track_inventory_levels] = true
491
+
492
+ expect(build(:variant).should_track_inventory?).to eq(true)
493
+ end
494
+ end
495
+
496
+ describe "deleted_at scope" do
497
+ before { variant.destroy && variant.reload }
498
+ it "should have a price if deleted" do
499
+ variant.price = 10
500
+ expect(variant.price).to eq(10)
501
+ end
502
+ end
503
+
504
+ describe "stock movements" do
505
+ let!(:movement) { create(:stock_movement, stock_item: variant.stock_items.first) }
506
+
507
+ it "builds out collection just fine through stock items" do
508
+ expect(variant.stock_movements.to_a).not_to be_empty
509
+ end
510
+ end
511
+
512
+ describe "in_stock scope" do
513
+ let!(:in_stock_variant) { create(:variant) }
514
+ let!(:out_of_stock_variant) { create(:variant) }
515
+ let!(:stock_location) { create(:stock_location) }
516
+
517
+ context "a stock location is provided" do
518
+ subject { Spree::Variant.in_stock([stock_location]) }
519
+
520
+ context "there's stock in the location" do
521
+ before do
522
+ in_stock_variant.
523
+ stock_items.find_by(stock_location: stock_location).
524
+ update_column(:count_on_hand, 10)
525
+ out_of_stock_variant.
526
+ stock_items.where.not(stock_location: stock_location).first.
527
+ update_column(:count_on_hand, 10)
528
+ end
529
+
530
+ it "returns all in stock variants in the provided stock location" do
531
+ expect(subject).to eq [in_stock_variant]
532
+ end
533
+ end
534
+
535
+ context "there's no stock in the location" do
536
+ it "returns an empty list" do
537
+ expect(subject).to eq []
538
+ end
539
+ end
540
+ end
541
+
542
+ context "a stock location is not provided" do
543
+ subject { Spree::Variant.in_stock }
544
+
545
+ before do
546
+ in_stock_variant.stock_items.first.update_column(:count_on_hand, 10)
547
+ end
548
+
549
+ it "returns all in stock variants" do
550
+ expect(subject).to eq [in_stock_variant]
551
+ end
552
+ end
553
+ end
554
+
555
+ describe "#display_image" do
556
+ subject { variant.display_image }
557
+
558
+ context "variant has associated images" do
559
+ let(:attachment) { File.open(File.expand_path('../../../fixtures/thinking-cat.jpg', __FILE__)) }
560
+ let(:image_params) { { viewable_id: variant.id, viewable_type: 'Spree::Variant', attachment: attachment, alt: "position 1", position: 1 } }
561
+ let!(:first_image) { Spree::Image.create(image_params) }
562
+ let!(:second_image) { image_params.merge(alt: "position 2", position: 2) }
563
+
564
+ it "returns the first image" do
565
+ expect(subject).to eq first_image
566
+ end
567
+ end
568
+
569
+ context "variant does not have any associated images" do
570
+ it "returns an image" do
571
+ expect(subject).to be_a(Spree::Image)
572
+ end
573
+ it "returns unpersisted record" do
574
+ expect(subject).to be_new_record
575
+ end
576
+ end
577
+ end
578
+
579
+ describe "#variant_properties" do
580
+ let(:option_value_1) { create(:option_value) }
581
+ let(:option_value_2) { create(:option_value) }
582
+ let(:variant) { create(:variant, option_values: [option_value_1, option_value_2]) }
583
+
584
+ subject { variant.variant_properties }
585
+
586
+ context "variant has properties" do
587
+ let!(:rule_1) { create(:variant_property_rule, product: variant.product, option_value: option_value_1) }
588
+ let!(:rule_2) { create(:variant_property_rule, product: variant.product, option_value: option_value_2) }
589
+
590
+ it "returns the variant property rule's values" do
591
+ expect(subject).to match_array rule_1.values + rule_2.values
592
+ end
593
+ end
594
+
595
+ context "variant doesn't have any properties" do
596
+ it "returns an empty list" do
597
+ expect(subject).to eq []
598
+ end
599
+ end
600
+ end
601
+ end