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,474 @@
1
+ # coding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ module ThirdParty
6
+ class Extension < Spree::Base
7
+ # nasty hack so we don't have to create a table to back this fake model
8
+ self.table_name = 'spree_products'
9
+ end
10
+ end
11
+
12
+ describe Spree::Product, :type => :model do
13
+
14
+ context 'product instance' do
15
+ let(:product) { create(:product) }
16
+ let(:variant) { create(:variant, :product => product) }
17
+
18
+ context '#duplicate' do
19
+ before do
20
+ allow(product).to receive_messages :taxons => [create(:taxon)]
21
+ end
22
+
23
+ it 'duplicates product' do
24
+ clone = product.duplicate
25
+ expect(clone.name).to eq('COPY OF ' + product.name)
26
+ expect(clone.master.sku).to eq('COPY OF ' + product.master.sku)
27
+ expect(clone.taxons).to eq(product.taxons)
28
+ expect(clone.images.size).to eq(product.images.size)
29
+ end
30
+
31
+ it 'calls #duplicate_extra' do
32
+ expect_any_instance_of(Spree::Product).to receive(:duplicate_extra)
33
+ .with(product)
34
+ expect(product).to_not receive(:duplicate_extra)
35
+ product.duplicate
36
+ end
37
+ end
38
+
39
+ context "master variant" do
40
+
41
+ context "when master variant changed" do
42
+ before do
43
+ product.master.sku = "Something changed"
44
+ end
45
+
46
+ it "saves the master" do
47
+ expect(product.master).to receive(:save!)
48
+ product.save
49
+ end
50
+ end
51
+
52
+ context "when master default price changed" do
53
+ before do
54
+ master = product.master
55
+ master.default_price.price = 11
56
+ master.save!
57
+ product.master.default_price.price = 12
58
+ end
59
+
60
+ it "saves the master" do
61
+ expect(product.master).to receive(:save!)
62
+ product.save
63
+ end
64
+
65
+ it "saves the default price" do
66
+ expect(product.master.default_price).to receive(:save)
67
+ product.save
68
+ end
69
+ end
70
+
71
+ context "when master variant and price haven't changed" do
72
+ it "does not save the master" do
73
+ expect(product.master).not_to receive(:save!)
74
+ product.save
75
+ end
76
+ end
77
+ end
78
+
79
+ context "product has no variants" do
80
+ context "#destroy" do
81
+ it "should set deleted_at value" do
82
+ product.destroy
83
+ expect(product.deleted_at).not_to be_nil
84
+ expect(product.master.reload.deleted_at).not_to be_nil
85
+ end
86
+ end
87
+ end
88
+
89
+ context "product has variants" do
90
+ before do
91
+ create(:variant, :product => product)
92
+ end
93
+
94
+ context "#destroy" do
95
+ it "should set deleted_at value" do
96
+ product.destroy
97
+ expect(product.deleted_at).not_to be_nil
98
+ expect(product.variants_including_master.all? { |v| !v.deleted_at.nil? }).to be true
99
+ end
100
+ end
101
+ end
102
+
103
+ context "#price" do
104
+ # Regression test for #1173
105
+ it 'strips non-price characters' do
106
+ product.price = "$10"
107
+ expect(product.price).to eq(10.0)
108
+ end
109
+ end
110
+
111
+ context "#display_price" do
112
+ before { product.price = 10.55 }
113
+
114
+ it "shows the amount" do
115
+ expect(product.display_price.to_s).to eq("$10.55")
116
+ end
117
+
118
+ context "with currency set to JPY" do
119
+ before do
120
+ product.master.default_price.currency = 'JPY'
121
+ product.master.default_price.save!
122
+ Spree::Config[:currency] = 'JPY'
123
+ end
124
+
125
+ it "displays the currency in yen" do
126
+ expect(product.display_price.to_s).to eq("¥11")
127
+ end
128
+ end
129
+ end
130
+
131
+ context "#available?" do
132
+ it "should be available if date is in the past" do
133
+ product.available_on = 1.day.ago
134
+ expect(product).to be_available
135
+ end
136
+
137
+ it "should not be available if date is nil or in the future" do
138
+ product.available_on = nil
139
+ expect(product).not_to be_available
140
+
141
+ product.available_on = 1.day.from_now
142
+ expect(product).not_to be_available
143
+ end
144
+
145
+ it "should not be available if destroyed" do
146
+ product.destroy
147
+ expect(product).not_to be_available
148
+ end
149
+ end
150
+
151
+ context "variants_and_option_values" do
152
+ let!(:high) { create(:variant, product: product) }
153
+ let!(:low) { create(:variant, product: product) }
154
+
155
+ before { high.option_values.destroy_all }
156
+
157
+ it "returns only variants with option values" do
158
+ expect(product.variants_and_option_values).to eq([low])
159
+ end
160
+ end
161
+
162
+ describe 'Variants sorting' do
163
+ context 'without master variant' do
164
+ it 'sorts variants by position' do
165
+ expect(product.variants.to_sql).to match(/ORDER BY (\`|\")spree_variants(\`|\").position ASC/)
166
+ end
167
+ end
168
+
169
+ context 'with master variant' do
170
+ it 'sorts variants by position' do
171
+ expect(product.variants_including_master.to_sql).to match(/ORDER BY (\`|\")spree_variants(\`|\").position ASC/)
172
+ end
173
+ end
174
+ end
175
+
176
+ context "has stock movements" do
177
+ let(:variant) { product.master }
178
+ let(:stock_item) { variant.stock_items.first }
179
+
180
+ it "doesnt raise ReadOnlyRecord error" do
181
+ Spree::StockMovement.create!(stock_item: stock_item, quantity: 1)
182
+ expect { product.destroy }.not_to raise_error
183
+ end
184
+ end
185
+
186
+ # Regression test for #3737
187
+ context "has stock items" do
188
+ it "can retrieve stock items" do
189
+ expect(product.master.stock_items.first).not_to be_nil
190
+ expect(product.stock_items.first).not_to be_nil
191
+ end
192
+ end
193
+
194
+ context "slugs" do
195
+
196
+ it "normalizes slug on update validation" do
197
+ product.slug = "hey//joe"
198
+ product.valid?
199
+ expect(product.slug).not_to match "/"
200
+ end
201
+
202
+ context "when product destroyed" do
203
+
204
+ it "renames slug" do
205
+ expect { product.destroy }.to change { product.slug }
206
+ end
207
+
208
+ context "when slug is already at or near max length" do
209
+
210
+ before do
211
+ product.slug = "x" * 255
212
+ product.save!
213
+ end
214
+
215
+ it "truncates renamed slug to ensure it remains within length limit" do
216
+ product.destroy
217
+ expect(product.slug.length).to eq 255
218
+ end
219
+
220
+ end
221
+
222
+ end
223
+
224
+ it "validates slug uniqueness" do
225
+ existing_product = product
226
+ new_product = create(:product)
227
+ new_product.slug = existing_product.slug
228
+
229
+ expect(new_product.valid?).to eq false
230
+ end
231
+
232
+ it "falls back to 'name-sku' for slug if regular name-based slug already in use" do
233
+ product1 = build(:product)
234
+ product1.name = "test"
235
+ product1.sku = "123"
236
+ product1.save!
237
+
238
+ product2 = build(:product)
239
+ product2.name = "test"
240
+ product2.sku = "456"
241
+ product2.save!
242
+
243
+ expect(product2.slug).to eq 'test-456'
244
+ end
245
+ end
246
+
247
+ context "hard deletion" do
248
+ it "doesnt raise ActiveRecordError error" do
249
+ expect { product.really_destroy! }.to_not raise_error
250
+ end
251
+ end
252
+
253
+ context 'history' do
254
+ before(:each) do
255
+ @product = create(:product)
256
+ end
257
+
258
+ it 'should keep the history when the product is destroyed' do
259
+ @product.destroy
260
+
261
+ expect(@product.slugs.with_deleted).to_not be_empty
262
+ end
263
+
264
+ it 'should update the history when the product is restored' do
265
+ @product.destroy
266
+
267
+ @product.restore(recursive: true)
268
+
269
+ latest_slug = @product.slugs.find_by slug: @product.slug
270
+ expect(latest_slug).to_not be_nil
271
+ end
272
+ end
273
+ end
274
+
275
+ context "properties" do
276
+ let(:product) { create(:product) }
277
+
278
+ it "should properly assign properties" do
279
+ product.set_property('the_prop', 'value1')
280
+ expect(product.property('the_prop')).to eq('value1')
281
+
282
+ product.set_property('the_prop', 'value2')
283
+ expect(product.property('the_prop')).to eq('value2')
284
+ end
285
+
286
+ it "should not create duplicate properties when set_property is called" do
287
+ expect {
288
+ product.set_property('the_prop', 'value2')
289
+ product.save
290
+ product.reload
291
+ }.not_to change(product.properties, :length)
292
+
293
+ expect {
294
+ product.set_property('the_prop_new', 'value')
295
+ product.save
296
+ product.reload
297
+ expect(product.property('the_prop_new')).to eq('value')
298
+ }.to change { product.properties.length }.by(1)
299
+ end
300
+
301
+ # Regression test for #2455
302
+ it "should not overwrite properties' presentation names" do
303
+ Spree::Property.where(:name => 'foo').first_or_create!(:presentation => "Foo's Presentation Name")
304
+ product.set_property('foo', 'value1')
305
+ product.set_property('bar', 'value2')
306
+ expect(Spree::Property.where(:name => 'foo').first.presentation).to eq("Foo's Presentation Name")
307
+ expect(Spree::Property.where(:name => 'bar').first.presentation).to eq("bar")
308
+ end
309
+
310
+ # Regression test for #4416
311
+ context "#possible_promotions" do
312
+ let!(:promotion) do
313
+ create(:promotion, advertise: true, starts_at: 1.day.ago)
314
+ end
315
+ let!(:rule) do
316
+ Spree::Promotion::Rules::Product.create(
317
+ promotion: promotion,
318
+ products: [product]
319
+ )
320
+ end
321
+
322
+ it "lists the promotion as a possible promotion" do
323
+ expect(product.possible_promotions).to include(promotion)
324
+ end
325
+ end
326
+ end
327
+
328
+ context '#create' do
329
+ let!(:prototype) { create(:prototype) }
330
+ let!(:product) { Spree::Product.new(name: "Foo", price: 1.99, shipping_category_id: create(:shipping_category).id) }
331
+
332
+ before { product.prototype_id = prototype.id }
333
+
334
+ context "when prototype is supplied" do
335
+ it "should create properties based on the prototype" do
336
+ product.save
337
+ expect(product.properties.count).to eq(1)
338
+ end
339
+ end
340
+
341
+ context "when prototype with option types is supplied" do
342
+ def build_option_type_with_values(name, values)
343
+ ot = create(:option_type, :name => name)
344
+ values.each do |val|
345
+ ot.option_values.create(:name => val.downcase, :presentation => val)
346
+ end
347
+ ot
348
+ end
349
+
350
+ let(:prototype) do
351
+ size = build_option_type_with_values("size", %w(Small Medium Large))
352
+ create(:prototype, :name => "Size", :option_types => [ size ])
353
+ end
354
+
355
+ let(:option_values_hash) do
356
+ hash = {}
357
+ prototype.option_types.each do |i|
358
+ hash[i.id.to_s] = i.option_value_ids
359
+ end
360
+ hash
361
+ end
362
+
363
+ it "should create option types based on the prototype" do
364
+ product.save
365
+ expect(product.option_type_ids.length).to eq(1)
366
+ expect(product.option_type_ids).to eq(prototype.option_type_ids)
367
+ end
368
+
369
+ it "should create product option types based on the prototype" do
370
+ product.save
371
+ expect(product.product_option_types.pluck(:option_type_id)).to eq(prototype.option_type_ids)
372
+ end
373
+
374
+ it "should create variants from an option values hash with one option type" do
375
+ product.option_values_hash = option_values_hash
376
+ product.save
377
+ expect(product.variants.length).to eq(3)
378
+ end
379
+
380
+ it "should still create variants when option_values_hash is given but prototype id is nil" do
381
+ product.option_values_hash = option_values_hash
382
+ product.prototype_id = nil
383
+ product.save
384
+ expect(product.option_type_ids.length).to eq(1)
385
+ expect(product.option_type_ids).to eq(prototype.option_type_ids)
386
+ expect(product.variants.length).to eq(3)
387
+ end
388
+
389
+ it "should create variants from an option values hash with multiple option types" do
390
+ color = build_option_type_with_values("color", %w(Red Green Blue))
391
+ logo = build_option_type_with_values("logo", %w(Ruby Rails Nginx))
392
+ option_values_hash[color.id.to_s] = color.option_value_ids
393
+ option_values_hash[logo.id.to_s] = logo.option_value_ids
394
+ product.option_values_hash = option_values_hash
395
+ product.save
396
+ product.reload
397
+ expect(product.option_type_ids.length).to eq(3)
398
+ expect(product.variants.length).to eq(27)
399
+ end
400
+ end
401
+ end
402
+
403
+ context "#images" do
404
+ let(:product) { create(:product) }
405
+ let(:image) { File.open(File.expand_path('../../../fixtures/thinking-cat.jpg', __FILE__)) }
406
+ let(:params) { {:viewable_id => product.master.id, :viewable_type => 'Spree::Variant', :attachment => image, :alt => "position 2", :position => 2} }
407
+
408
+ before do
409
+ Spree::Image.create(params)
410
+ Spree::Image.create(params.merge({:alt => "position 1", :position => 1}))
411
+ Spree::Image.create(params.merge({:viewable_type => 'ThirdParty::Extension', :alt => "position 1", :position => 2}))
412
+ end
413
+
414
+ it "only looks for variant images" do
415
+ expect(product.images.size).to eq(2)
416
+ end
417
+
418
+ it "should be sorted by position" do
419
+ expect(product.images.pluck(:alt)).to eq(["position 1", "position 2"])
420
+ end
421
+ end
422
+
423
+ # Regression tests for #2352
424
+ context "classifications and taxons" do
425
+ it "is joined through classifications" do
426
+ reflection = Spree::Product.reflect_on_association(:taxons)
427
+ expect(reflection.options[:through]).to eq(:classifications)
428
+ end
429
+
430
+ it "will delete all classifications" do
431
+ reflection = Spree::Product.reflect_on_association(:classifications)
432
+ expect(reflection.options[:dependent]).to eq(:delete_all)
433
+ end
434
+ end
435
+
436
+ context '#total_on_hand' do
437
+ let(:product) { create(:product) }
438
+
439
+ it 'should be infinite if track_inventory_levels is false' do
440
+ Spree::Config[:track_inventory_levels] = false
441
+ expect(build(:product, :variants_including_master => [build(:master_variant)]).total_on_hand).to eql(Float::INFINITY)
442
+ end
443
+
444
+ it 'should be infinite if variant is on demand' do
445
+ Spree::Config[:track_inventory_levels] = true
446
+ expect(build(:product, :variants_including_master => [build(:on_demand_master_variant)]).total_on_hand).to eql(Float::INFINITY)
447
+ end
448
+
449
+ it 'should return sum of stock items count_on_hand' do
450
+ product.stock_items.first.set_count_on_hand 5
451
+ product.variants_including_master(true) # force load association
452
+ expect(product.total_on_hand).to eql(5)
453
+ end
454
+
455
+ it 'should return sum of stock items count_on_hand when variants_including_master is not loaded' do
456
+ product.stock_items.first.set_count_on_hand 5
457
+ expect(product.reload.total_on_hand).to eql(5)
458
+ end
459
+ end
460
+
461
+ # Regression spec for https://github.com/spree/spree/issues/5588
462
+ context '#validate_master when duplicate SKUs entered' do
463
+ let!(:first_product) { create(:product, sku: 'a-sku') }
464
+ let(:second_product) { build(:product, sku: 'a-sku') }
465
+
466
+ subject { second_product }
467
+ it { is_expected.to be_invalid }
468
+ end
469
+
470
+ it "initializes a master variant when building a product" do
471
+ product = Spree::Product.new
472
+ expect(product.master.is_master).to be true
473
+ end
474
+ end