solidus_core 1.0.2 → 1.0.3

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 (216) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1 -0
  3. data/Gemfile +3 -0
  4. data/Rakefile +16 -0
  5. data/script/rails +9 -0
  6. data/solidus_core.gemspec +48 -0
  7. data/spec/fixtures/thinking-cat.jpg +0 -0
  8. data/spec/helpers/base_helper_spec.rb +173 -0
  9. data/spec/helpers/order_helper_spec.rb +12 -0
  10. data/spec/helpers/products_helper_spec.rb +220 -0
  11. data/spec/helpers/taxons_helper_spec.rb +17 -0
  12. data/spec/lib/calculated_adjustments_spec.rb +7 -0
  13. data/spec/lib/i18n_spec.rb +123 -0
  14. data/spec/lib/search/base_spec.rb +86 -0
  15. data/spec/lib/search/variant_spec.rb +92 -0
  16. data/spec/lib/spree/core/controller_helpers/auth_spec.rb +66 -0
  17. data/spec/lib/spree/core/controller_helpers/order_spec.rb +92 -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 +22 -0
  23. data/spec/lib/spree/core/importer/order_spec.rb +431 -0
  24. data/spec/lib/spree/core/role_configuration_spec.rb +138 -0
  25. data/spec/lib/spree/core/validators/email_spec.rb +48 -0
  26. data/spec/lib/spree/localized_number_spec.rb +38 -0
  27. data/spec/lib/spree/migrations_spec.rb +36 -0
  28. data/spec/lib/spree/money_spec.rb +127 -0
  29. data/spec/lib/tasks/exchanges_spec.rb +231 -0
  30. data/spec/lib/tasks/migrations/copy_shipped_shipments_to_cartons_spec.rb +115 -0
  31. data/spec/lib/tasks/order_capturing_spec.rb +56 -0
  32. data/spec/mailers/carton_mailer_spec.rb +43 -0
  33. data/spec/mailers/order_mailer_spec.rb +122 -0
  34. data/spec/mailers/reimbursement_mailer_spec.rb +40 -0
  35. data/spec/mailers/test_mailer_spec.rb +15 -0
  36. data/spec/models/spree/ability_spec.rb +276 -0
  37. data/spec/models/spree/address_spec.rb +250 -0
  38. data/spec/models/spree/adjustment_reason_spec.rb +13 -0
  39. data/spec/models/spree/adjustment_spec.rb +177 -0
  40. data/spec/models/spree/app_configuration_spec.rb +20 -0
  41. data/spec/models/spree/asset_spec.rb +24 -0
  42. data/spec/models/spree/calculator/default_tax_spec.rb +127 -0
  43. data/spec/models/spree/calculator/flat_percent_item_total_spec.rb +25 -0
  44. data/spec/models/spree/calculator/flat_rate_spec.rb +47 -0
  45. data/spec/models/spree/calculator/flexi_rate_spec.rb +41 -0
  46. data/spec/models/spree/calculator/percent_on_line_item_spec.rb +15 -0
  47. data/spec/models/spree/calculator/price_sack_spec.rb +30 -0
  48. data/spec/models/spree/calculator/refunds/default_refund_amount_spec.rb +51 -0
  49. data/spec/models/spree/calculator/shipping/flat_percent_item_total_spec.rb +23 -0
  50. data/spec/models/spree/calculator/shipping/flat_rate_spec.rb +13 -0
  51. data/spec/models/spree/calculator/shipping/flexi_rate_spec.rb +52 -0
  52. data/spec/models/spree/calculator/shipping/per_item_spec.rb +20 -0
  53. data/spec/models/spree/calculator/shipping/price_sack_spec.rb +30 -0
  54. data/spec/models/spree/calculator/tiered_flat_rate_spec.rb +36 -0
  55. data/spec/models/spree/calculator/tiered_percent_spec.rb +47 -0
  56. data/spec/models/spree/calculator_spec.rb +36 -0
  57. data/spec/models/spree/carton_spec.rb +133 -0
  58. data/spec/models/spree/classification_spec.rb +15 -0
  59. data/spec/models/spree/concerns/display_money_spec.rb +43 -0
  60. data/spec/models/spree/concerns/user_methods_spec.rb +41 -0
  61. data/spec/models/spree/credit_card_spec.rb +334 -0
  62. data/spec/models/spree/customer_return_spec.rb +276 -0
  63. data/spec/models/spree/exchange_spec.rb +79 -0
  64. data/spec/models/spree/gateway/bogus_simple.rb +20 -0
  65. data/spec/models/spree/gateway/bogus_spec.rb +13 -0
  66. data/spec/models/spree/gateway_spec.rb +82 -0
  67. data/spec/models/spree/inventory_unit_spec.rb +307 -0
  68. data/spec/models/spree/item_adjustments_spec.rb +256 -0
  69. data/spec/models/spree/line_item_spec.rb +191 -0
  70. data/spec/models/spree/option_type_spec.rb +14 -0
  71. data/spec/models/spree/option_value_spec.rb +22 -0
  72. data/spec/models/spree/order/address_spec.rb +50 -0
  73. data/spec/models/spree/order/adjustments_spec.rb +39 -0
  74. data/spec/models/spree/order/callbacks_spec.rb +42 -0
  75. data/spec/models/spree/order/checkout_spec.rb +902 -0
  76. data/spec/models/spree/order/currency_updater_spec.rb +32 -0
  77. data/spec/models/spree/order/finalizing_spec.rb +111 -0
  78. data/spec/models/spree/order/payment_spec.rb +210 -0
  79. data/spec/models/spree/order/risk_assessment_spec.rb +68 -0
  80. data/spec/models/spree/order/state_machine_spec.rb +221 -0
  81. data/spec/models/spree/order/tax_spec.rb +84 -0
  82. data/spec/models/spree/order/totals_spec.rb +24 -0
  83. data/spec/models/spree/order/updating_spec.rb +18 -0
  84. data/spec/models/spree/order/validations_spec.rb +15 -0
  85. data/spec/models/spree/order_cancellations_spec.rb +120 -0
  86. data/spec/models/spree/order_capturing_spec.rb +116 -0
  87. data/spec/models/spree/order_contents_spec.rb +265 -0
  88. data/spec/models/spree/order_inventory_spec.rb +228 -0
  89. data/spec/models/spree/order_mutex_spec.rb +85 -0
  90. data/spec/models/spree/order_promotion_spec.rb +31 -0
  91. data/spec/models/spree/order_shipping_spec.rb +247 -0
  92. data/spec/models/spree/order_spec.rb +1412 -0
  93. data/spec/models/spree/order_stock_location_spec.rb +18 -0
  94. data/spec/models/spree/order_updater_spec.rb +299 -0
  95. data/spec/models/spree/payment_method/store_credit_spec.rb +294 -0
  96. data/spec/models/spree/payment_method_spec.rb +96 -0
  97. data/spec/models/spree/payment_spec.rb +1044 -0
  98. data/spec/models/spree/permission_sets/base_spec.rb +12 -0
  99. data/spec/models/spree/permission_sets/configuration_display.rb +82 -0
  100. data/spec/models/spree/permission_sets/configuration_management_spec.rb +50 -0
  101. data/spec/models/spree/permission_sets/dashboard_display_spec.rb +22 -0
  102. data/spec/models/spree/permission_sets/order_display_spec.rb +49 -0
  103. data/spec/models/spree/permission_sets/order_management_spec.rb +36 -0
  104. data/spec/models/spree/permission_sets/product_display_spec.rb +60 -0
  105. data/spec/models/spree/permission_sets/product_management_spec.rb +40 -0
  106. data/spec/models/spree/permission_sets/promotion_display_spec.rb +34 -0
  107. data/spec/models/spree/permission_sets/promotion_management_spec.rb +26 -0
  108. data/spec/models/spree/permission_sets/report_display_spec.rb +24 -0
  109. data/spec/models/spree/permission_sets/restricted_transfer_management_spec.rb +132 -0
  110. data/spec/models/spree/permission_sets/stock_display_spec.rb +26 -0
  111. data/spec/models/spree/permission_sets/stock_management_spec.rb +24 -0
  112. data/spec/models/spree/permission_sets/user_display_spec.rb +36 -0
  113. data/spec/models/spree/permission_sets/user_management_spec.rb +28 -0
  114. data/spec/models/spree/preference_spec.rb +80 -0
  115. data/spec/models/spree/preferences/configuration_spec.rb +30 -0
  116. data/spec/models/spree/preferences/preferable_spec.rb +294 -0
  117. data/spec/models/spree/preferences/scoped_store_spec.rb +58 -0
  118. data/spec/models/spree/preferences/static_model_preferences_spec.rb +78 -0
  119. data/spec/models/spree/preferences/statically_configurable_spec.rb +60 -0
  120. data/spec/models/spree/preferences/store_spec.rb +39 -0
  121. data/spec/models/spree/price_spec.rb +42 -0
  122. data/spec/models/spree/product/scopes_spec.rb +148 -0
  123. data/spec/models/spree/product_duplicator_spec.rb +103 -0
  124. data/spec/models/spree/product_filter_spec.rb +26 -0
  125. data/spec/models/spree/product_property_spec.rb +20 -0
  126. data/spec/models/spree/product_spec.rb +437 -0
  127. data/spec/models/spree/promotion/actions/create_adjustment_spec.rb +96 -0
  128. data/spec/models/spree/promotion/actions/create_item_adjustments_spec.rb +165 -0
  129. data/spec/models/spree/promotion/actions/create_quantity_adjustments_spec.rb +115 -0
  130. data/spec/models/spree/promotion/actions/free_shipping_spec.rb +40 -0
  131. data/spec/models/spree/promotion/rules/first_order_spec.rb +75 -0
  132. data/spec/models/spree/promotion/rules/item_total_spec.rb +67 -0
  133. data/spec/models/spree/promotion/rules/nth_order_spec.rb +70 -0
  134. data/spec/models/spree/promotion/rules/one_use_per_user_spec.rb +42 -0
  135. data/spec/models/spree/promotion/rules/option_value_spec.rb +94 -0
  136. data/spec/models/spree/promotion/rules/product_spec.rb +143 -0
  137. data/spec/models/spree/promotion/rules/taxon_spec.rb +102 -0
  138. data/spec/models/spree/promotion/rules/user_logged_in_spec.rb +27 -0
  139. data/spec/models/spree/promotion/rules/user_spec.rb +37 -0
  140. data/spec/models/spree/promotion_builder_spec.rb +118 -0
  141. data/spec/models/spree/promotion_category_spec.rb +17 -0
  142. data/spec/models/spree/promotion_code/code_builder_spec.rb +79 -0
  143. data/spec/models/spree/promotion_code_spec.rb +187 -0
  144. data/spec/models/spree/promotion_handler/cart_spec.rb +114 -0
  145. data/spec/models/spree/promotion_handler/coupon_spec.rb +335 -0
  146. data/spec/models/spree/promotion_handler/free_shipping_spec.rb +47 -0
  147. data/spec/models/spree/promotion_handler/page_spec.rb +44 -0
  148. data/spec/models/spree/promotion_rule_spec.rb +28 -0
  149. data/spec/models/spree/promotion_spec.rb +767 -0
  150. data/spec/models/spree/refund_spec.rb +204 -0
  151. data/spec/models/spree/reimbursement/credit_spec.rb +36 -0
  152. data/spec/models/spree/reimbursement/reimbursement_type_engine_spec.rb +140 -0
  153. data/spec/models/spree/reimbursement/reimbursement_type_validator_spec.rb +83 -0
  154. data/spec/models/spree/reimbursement_performer_spec.rb +30 -0
  155. data/spec/models/spree/reimbursement_spec.rb +231 -0
  156. data/spec/models/spree/reimbursement_tax_calculator_spec.rb +51 -0
  157. data/spec/models/spree/reimbursement_type/credit_spec.rb +53 -0
  158. data/spec/models/spree/reimbursement_type/exchange_spec.rb +46 -0
  159. data/spec/models/spree/reimbursement_type/original_payment_spec.rb +107 -0
  160. data/spec/models/spree/reimbursement_type/store_credit_spec.rb +97 -0
  161. data/spec/models/spree/return_authorization_spec.rb +290 -0
  162. data/spec/models/spree/return_item/eligibility_validator/default_spec.rb +77 -0
  163. data/spec/models/spree/return_item/eligibility_validator/inventory_shipped_spec.rb +58 -0
  164. data/spec/models/spree/return_item/eligibility_validator/no_reimbursements_spec.rb +85 -0
  165. data/spec/models/spree/return_item/eligibility_validator/order_completed_spec.rb +32 -0
  166. data/spec/models/spree/return_item/eligibility_validator/rma_required_spec.rb +29 -0
  167. data/spec/models/spree/return_item/eligibility_validator/time_since_purchase_spec.rb +35 -0
  168. data/spec/models/spree/return_item/exchange_variant_eligibility/same_option_value_spec.rb +65 -0
  169. data/spec/models/spree/return_item/exchange_variant_eligibility/same_product_spec.rb +43 -0
  170. data/spec/models/spree/return_item_spec.rb +775 -0
  171. data/spec/models/spree/returns_calculator_spec.rb +14 -0
  172. data/spec/models/spree/shipment_spec.rb +709 -0
  173. data/spec/models/spree/shipping_calculator_spec.rb +45 -0
  174. data/spec/models/spree/shipping_method_spec.rb +88 -0
  175. data/spec/models/spree/shipping_rate_spec.rb +142 -0
  176. data/spec/models/spree/state_spec.rb +14 -0
  177. data/spec/models/spree/stock/availability_validator_spec.rb +83 -0
  178. data/spec/models/spree/stock/coordinator_spec.rb +116 -0
  179. data/spec/models/spree/stock/differentiator_spec.rb +39 -0
  180. data/spec/models/spree/stock/estimator_spec.rb +146 -0
  181. data/spec/models/spree/stock/inventory_unit_builder_spec.rb +38 -0
  182. data/spec/models/spree/stock/package_spec.rb +163 -0
  183. data/spec/models/spree/stock/packer_spec.rb +91 -0
  184. data/spec/models/spree/stock/prioritizer_spec.rb +125 -0
  185. data/spec/models/spree/stock/quantifier_spec.rb +115 -0
  186. data/spec/models/spree/stock/splitter/backordered_spec.rb +29 -0
  187. data/spec/models/spree/stock/splitter/base_spec.rb +21 -0
  188. data/spec/models/spree/stock/splitter/shipping_category_spec.rb +50 -0
  189. data/spec/models/spree/stock/splitter/weight_spec.rb +29 -0
  190. data/spec/models/spree/stock_item_spec.rb +426 -0
  191. data/spec/models/spree/stock_location_spec.rb +279 -0
  192. data/spec/models/spree/stock_movement_spec.rb +56 -0
  193. data/spec/models/spree/stock_transfer_spec.rb +290 -0
  194. data/spec/models/spree/store_credit_category_spec.rb +17 -0
  195. data/spec/models/spree/store_credit_event_spec.rb +314 -0
  196. data/spec/models/spree/store_credit_spec.rb +876 -0
  197. data/spec/models/spree/store_spec.rb +55 -0
  198. data/spec/models/spree/tax_category_spec.rb +27 -0
  199. data/spec/models/spree/tax_rate_spec.rb +378 -0
  200. data/spec/models/spree/taxon_spec.rb +74 -0
  201. data/spec/models/spree/taxonomy_spec.rb +18 -0
  202. data/spec/models/spree/tracker_spec.rb +21 -0
  203. data/spec/models/spree/transfer_item_spec.rb +264 -0
  204. data/spec/models/spree/unit_cancel_spec.rb +148 -0
  205. data/spec/models/spree/user_spec.rb +223 -0
  206. data/spec/models/spree/validations/db_maximum_length_validator_spec.rb +23 -0
  207. data/spec/models/spree/variant/scopes_spec.rb +55 -0
  208. data/spec/models/spree/variant_spec.rb +546 -0
  209. data/spec/models/spree/zone_spec.rb +305 -0
  210. data/spec/spec_helper.rb +78 -0
  211. data/spec/support/big_decimal.rb +5 -0
  212. data/spec/support/concerns/default_price.rb +34 -0
  213. data/spec/support/dummy_ability.rb +4 -0
  214. data/spec/support/test_gateway.rb +2 -0
  215. metadata +229 -3
  216. data/lib/spree/testing_support/rspec-activemodel-mocks_patch.rb +0 -8
@@ -0,0 +1,223 @@
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
+ it "chooses the most recently created incomplete order" do
37
+ order_1 = create(:order, user: user, created_at: 1.second.ago)
38
+ order_2 = create(:order, user: user)
39
+ expect(user.last_incomplete_spree_order).to eq order_2
40
+ end
41
+
42
+ context "persists order address" do
43
+ let!(:order) { create(:order, bill_address: create(:address), ship_address: create(:address)) }
44
+
45
+ it "copies over order addresses" do
46
+ expect {
47
+ user.persist_order_address(order)
48
+ }.to change { Spree::Address.count }.by(2)
49
+
50
+ expect(user.bill_address).to eq order.bill_address
51
+ expect(user.ship_address).to eq order.ship_address
52
+ end
53
+
54
+ it "doesnt create new addresses if user has already" do
55
+ user.update_column(:bill_address_id, create(:address))
56
+ user.update_column(:ship_address_id, create(:address))
57
+ user.reload
58
+
59
+ expect {
60
+ user.persist_order_address(order)
61
+ }.not_to change { Spree::Address.count }
62
+ end
63
+
64
+ it "set both bill and ship address id on subject" do
65
+ user.persist_order_address(order)
66
+
67
+ expect(user.bill_address_id).not_to be_blank
68
+ expect(user.ship_address_id).not_to be_blank
69
+ end
70
+ end
71
+
72
+ context "payment source" do
73
+ let(:payment_method) { create(:credit_card_payment_method) }
74
+ let!(:cc) do
75
+ create(:credit_card, user_id: user.id, payment_method: payment_method, gateway_customer_profile_id: "2342343")
76
+ end
77
+
78
+ it "has payment sources" do
79
+ expect(user.payment_sources.first.gateway_customer_profile_id).not_to be_empty
80
+ end
81
+
82
+ it "drops payment source" do
83
+ user.drop_payment_source cc
84
+ expect(cc.gateway_customer_profile_id).to be_nil
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ describe Spree.user_class, :type => :model do
91
+ context "reporting" do
92
+ let(:order_value) { BigDecimal.new("80.94") }
93
+ let(:order_count) { 4 }
94
+ let(:orders) { Array.new(order_count, double(total: order_value)) }
95
+
96
+ before do
97
+ allow(orders).to receive(:pluck).with(:total).and_return(orders.map(&:total))
98
+ allow(orders).to receive(:count).and_return(orders.length)
99
+ end
100
+
101
+ def load_orders
102
+ allow(subject).to receive(:spree_orders).and_return(double(complete: orders))
103
+ end
104
+
105
+ describe "#lifetime_value" do
106
+ context "with orders" do
107
+ before { load_orders }
108
+ it "returns the total of completed orders for the user" do
109
+ expect(subject.lifetime_value).to eq (order_count * order_value)
110
+ end
111
+ end
112
+ context "without orders" do
113
+ it "returns 0.00" do
114
+ expect(subject.lifetime_value).to eq BigDecimal("0.00")
115
+ end
116
+ end
117
+ end
118
+
119
+ describe "#display_lifetime_value" do
120
+ it "returns a Spree::Money version of lifetime_value" do
121
+ value = BigDecimal("500.05")
122
+ allow(subject).to receive(:lifetime_value).and_return(value)
123
+ expect(subject.display_lifetime_value).to eq Spree::Money.new(value)
124
+ end
125
+ end
126
+
127
+ describe "#order_count" do
128
+ before { load_orders }
129
+ it "returns the count of completed orders for the user" do
130
+ expect(subject.order_count).to eq BigDecimal(order_count)
131
+ end
132
+ end
133
+
134
+ describe "#average_order_value" do
135
+ context "with orders" do
136
+ before { load_orders }
137
+ it "returns the average completed order price for the user" do
138
+ expect(subject.average_order_value).to eq order_value
139
+ end
140
+ end
141
+ context "without orders" do
142
+ it "returns 0.00" do
143
+ expect(subject.average_order_value).to eq BigDecimal("0.00")
144
+ end
145
+ end
146
+ end
147
+
148
+ describe "#display_average_order_value" do
149
+ before { load_orders }
150
+ it "returns a Spree::Money version of average_order_value" do
151
+ value = BigDecimal("500.05")
152
+ allow(subject).to receive(:average_order_value).and_return(value)
153
+ expect(subject.display_average_order_value).to eq Spree::Money.new(value)
154
+ end
155
+ end
156
+ end
157
+
158
+ describe "#total_available_store_credit" do
159
+ context "user does not have any associated store credits" do
160
+ subject { create(:user) }
161
+
162
+ it "returns 0" do
163
+ expect(subject.total_available_store_credit).to be_zero
164
+ end
165
+ end
166
+
167
+ context "user has several associated store credits" do
168
+ let(:user) { create(:user) }
169
+ let(:amount) { 120.25 }
170
+ let(:additional_amount) { 55.75 }
171
+ let(:store_credit) { create(:store_credit, user: user, amount: amount, amount_used: 0.0) }
172
+ let!(:additional_store_credit) { create(:store_credit, user: user, amount: additional_amount, amount_used: 0.0) }
173
+
174
+ subject { store_credit.user }
175
+
176
+ context "part of the store credit has been used" do
177
+ let(:amount_used) { 35.00 }
178
+
179
+ before { store_credit.update_attributes(amount_used: amount_used) }
180
+
181
+ context "part of the store credit has been authorized" do
182
+ let(:authorized_amount) { 10 }
183
+
184
+ before { additional_store_credit.update_attributes(amount_authorized: authorized_amount) }
185
+
186
+ it "returns sum of amounts minus used amount and authorized amount" do
187
+ expect(subject.total_available_store_credit.to_f).to eq (amount + additional_amount - amount_used - authorized_amount)
188
+ end
189
+ end
190
+
191
+ context "there are no authorized amounts on any of the store credits" do
192
+ it "returns sum of amounts minus used amount" do
193
+ expect(subject.total_available_store_credit.to_f).to eq (amount + additional_amount - amount_used)
194
+ end
195
+ end
196
+ end
197
+
198
+ context "store credits have never been used" do
199
+ context "part of the store credit has been authorized" do
200
+ let(:authorized_amount) { 10 }
201
+
202
+ before { additional_store_credit.update_attributes(amount_authorized: authorized_amount) }
203
+
204
+ it "returns sum of amounts minus authorized amount" do
205
+ expect(subject.total_available_store_credit.to_f).to eq (amount + additional_amount - authorized_amount)
206
+ end
207
+ end
208
+
209
+ context "there are no authorized amounts on any of the store credits" do
210
+ it "returns sum of amounts" do
211
+ expect(subject.total_available_store_credit.to_f).to eq (amount + additional_amount)
212
+ end
213
+ end
214
+ end
215
+
216
+ context "all store credits have never been used or authorized" do
217
+ it "returns sum of amounts" do
218
+ expect(subject.total_available_store_credit.to_f).to eq (amount + additional_amount)
219
+ end
220
+ end
221
+ end
222
+ end
223
+ 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,546 @@
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(:name => "Foobar")
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(:name => "Foobar")
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!(:name => 'any-name')
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
+ it "returns all in stock variants" do
514
+ in_stock_variant = create(:variant)
515
+ out_of_stock_variant = create(:variant)
516
+
517
+ in_stock_variant.stock_items.first.update_column(:count_on_hand, 10)
518
+
519
+ expect(Spree::Variant.in_stock).to eq [in_stock_variant]
520
+ end
521
+ end
522
+
523
+ describe "#display_image" do
524
+ subject { variant.display_image }
525
+
526
+ context "variant has associated images" do
527
+ let(:attachment) { File.open(File.expand_path('../../../fixtures/thinking-cat.jpg', __FILE__)) }
528
+ let(:image_params) { { viewable_id: variant.id, viewable_type: 'Spree::Variant', attachment: attachment, alt: "position 1", position: 1 } }
529
+ let!(:first_image) { Spree::Image.create(image_params) }
530
+ let!(:second_image) { image_params.merge(alt: "position 2", position: 2) }
531
+
532
+ it "returns the first image" do
533
+ expect(subject).to eq first_image
534
+ end
535
+ end
536
+
537
+ context "variant does not have any associated images" do
538
+ it "returns an image" do
539
+ expect(subject).to be_a(Spree::Image)
540
+ end
541
+ it "returns unpersisted record" do
542
+ expect(subject).to be_new_record
543
+ end
544
+ end
545
+ end
546
+ end