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