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,26 @@
1
+ require 'spec_helper'
2
+ require 'spree/core/product_filters'
3
+
4
+ describe 'product filters', :type => :model do
5
+ # Regression test for #1709
6
+ context 'finds products filtered by brand' do
7
+ let(:product) { create(:product) }
8
+ before do
9
+ property = Spree::Property.create!(:name => "brand", :presentation => "brand")
10
+ product.set_property("brand", "Nike")
11
+ end
12
+
13
+ it "does not attempt to call value method on Arel::Table" do
14
+ expect { Spree::Core::ProductFilters.brand_filter }.not_to raise_error
15
+ end
16
+
17
+ it "can find products in the 'Nike' brand" do
18
+ expect(Spree::Product.brand_any("Nike")).to include(product)
19
+ end
20
+ it "sorts products without brand specified" do
21
+ product.set_property("brand", "Nike")
22
+ create(:product).set_property("brand", nil)
23
+ expect { Spree::Core::ProductFilters.brand_filter[:labels] }.not_to raise_error
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::ProductProperty, :type => :model do
4
+
5
+ context "validations" do
6
+ it "should validate length of value" do
7
+ pp = create(:product_property)
8
+ pp.value = "x" * 256
9
+ expect(pp).not_to be_valid
10
+ end
11
+ end
12
+
13
+ context "touching" do
14
+ it "should update product" do
15
+ pp = create(:product_property)
16
+ expect(pp.product).to receive(:touch)
17
+ pp.touch
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,437 @@
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 'Variants sorting' do
165
+ context 'without master variant' do
166
+ it 'sorts variants by position' do
167
+ expect(product.variants.to_sql).to match(/ORDER BY (\`|\")spree_variants(\`|\").position ASC/)
168
+ end
169
+ end
170
+
171
+ context 'with master variant' do
172
+ it 'sorts variants by position' do
173
+ expect(product.variants_including_master.to_sql).to match(/ORDER BY (\`|\")spree_variants(\`|\").position ASC/)
174
+ end
175
+ end
176
+ end
177
+
178
+ context "has stock movements" do
179
+ let(:product) { create(:product) }
180
+ let(:variant) { product.master }
181
+ let(:stock_item) { variant.stock_items.first }
182
+
183
+ it "doesnt raise ReadOnlyRecord error" do
184
+ Spree::StockMovement.create!(stock_item: stock_item, quantity: 1)
185
+ expect { product.destroy }.not_to raise_error
186
+ end
187
+ end
188
+
189
+ # Regression test for #3737
190
+ context "has stock items" do
191
+ let(:product) { create(:product) }
192
+ it "can retrieve stock items" do
193
+ expect(product.master.stock_items.first).not_to be_nil
194
+ expect(product.stock_items.first).not_to be_nil
195
+ end
196
+ end
197
+
198
+ context "slugs" do
199
+
200
+ it "normalizes slug on update validation" do
201
+ product.slug = "hey//joe"
202
+ product.valid?
203
+ expect(product.slug).not_to match "/"
204
+ end
205
+
206
+ it "renames slug on destroy" do
207
+ old_slug = product.slug
208
+ product.destroy
209
+ expect(old_slug).to_not eq product.slug
210
+ end
211
+
212
+ it "validates slug uniqueness" do
213
+ existing_product = product
214
+ new_product = create(:product)
215
+ new_product.slug = existing_product.slug
216
+
217
+ expect(new_product.valid?).to eq false
218
+ end
219
+
220
+ it "falls back to 'name-sku' for slug if regular name-based slug already in use" do
221
+ product1 = build(:product)
222
+ product1.name = "test"
223
+ product1.sku = "123"
224
+ product1.save!
225
+
226
+ product2 = build(:product)
227
+ product2.name = "test"
228
+ product2.sku = "456"
229
+ product2.save!
230
+
231
+ expect(product2.slug).to eq 'test-456'
232
+ end
233
+
234
+ end
235
+
236
+ end
237
+
238
+ context "properties" do
239
+ let(:product) { create(:product) }
240
+
241
+ it "should properly assign properties" do
242
+ product.set_property('the_prop', 'value1')
243
+ expect(product.property('the_prop')).to eq('value1')
244
+
245
+ product.set_property('the_prop', 'value2')
246
+ expect(product.property('the_prop')).to eq('value2')
247
+ end
248
+
249
+ it "should not create duplicate properties when set_property is called" do
250
+ expect {
251
+ product.set_property('the_prop', 'value2')
252
+ product.save
253
+ product.reload
254
+ }.not_to change(product.properties, :length)
255
+
256
+ expect {
257
+ product.set_property('the_prop_new', 'value')
258
+ product.save
259
+ product.reload
260
+ expect(product.property('the_prop_new')).to eq('value')
261
+ }.to change { product.properties.length }.by(1)
262
+ end
263
+
264
+ # Regression test for #2455
265
+ it "should not overwrite properties' presentation names" do
266
+ Spree::Property.where(:name => 'foo').first_or_create!(:presentation => "Foo's Presentation Name")
267
+ product.set_property('foo', 'value1')
268
+ product.set_property('bar', 'value2')
269
+ expect(Spree::Property.where(:name => 'foo').first.presentation).to eq("Foo's Presentation Name")
270
+ expect(Spree::Property.where(:name => 'bar').first.presentation).to eq("bar")
271
+ end
272
+
273
+ # Regression test for #4416
274
+ context "#possible_promotions" do
275
+ let!(:promotion) do
276
+ create(:promotion, advertise: true, starts_at: 1.day.ago)
277
+ end
278
+ let!(:rule) do
279
+ Spree::Promotion::Rules::Product.create(
280
+ promotion: promotion,
281
+ products: [product]
282
+ )
283
+ end
284
+
285
+ it "lists the promotion as a possible promotion" do
286
+ expect(product.possible_promotions).to include(promotion)
287
+ end
288
+ end
289
+ end
290
+
291
+ context '#create' do
292
+ let!(:prototype) { create(:prototype) }
293
+ let!(:product) { Spree::Product.new(name: "Foo", price: 1.99, shipping_category_id: create(:shipping_category).id) }
294
+
295
+ before { product.prototype_id = prototype.id }
296
+
297
+ context "when prototype is supplied" do
298
+ it "should create properties based on the prototype" do
299
+ product.save
300
+ expect(product.properties.count).to eq(1)
301
+ end
302
+ end
303
+
304
+ context "when prototype with option types is supplied" do
305
+ def build_option_type_with_values(name, values)
306
+ ot = create(:option_type, :name => name)
307
+ values.each do |val|
308
+ ot.option_values.create(:name => val.downcase, :presentation => val)
309
+ end
310
+ ot
311
+ end
312
+
313
+ let(:prototype) do
314
+ size = build_option_type_with_values("size", %w(Small Medium Large))
315
+ create(:prototype, :name => "Size", :option_types => [ size ])
316
+ end
317
+
318
+ let(:option_values_hash) do
319
+ hash = {}
320
+ prototype.option_types.each do |i|
321
+ hash[i.id.to_s] = i.option_value_ids
322
+ end
323
+ hash
324
+ end
325
+
326
+ it "should create option types based on the prototype" do
327
+ product.save
328
+ expect(product.option_type_ids.length).to eq(1)
329
+ expect(product.option_type_ids).to eq(prototype.option_type_ids)
330
+ end
331
+
332
+ it "should create product option types based on the prototype" do
333
+ product.save
334
+ expect(product.product_option_types.pluck(:option_type_id)).to eq(prototype.option_type_ids)
335
+ end
336
+
337
+ it "should create variants from an option values hash with one option type" do
338
+ product.option_values_hash = option_values_hash
339
+ product.save
340
+ expect(product.variants.length).to eq(3)
341
+ end
342
+
343
+ it "should still create variants when option_values_hash is given but prototype id is nil" do
344
+ product.option_values_hash = option_values_hash
345
+ product.prototype_id = nil
346
+ product.save
347
+ expect(product.option_type_ids.length).to eq(1)
348
+ expect(product.option_type_ids).to eq(prototype.option_type_ids)
349
+ expect(product.variants.length).to eq(3)
350
+ end
351
+
352
+ it "should create variants from an option values hash with multiple option types" do
353
+ color = build_option_type_with_values("color", %w(Red Green Blue))
354
+ logo = build_option_type_with_values("logo", %w(Ruby Rails Nginx))
355
+ option_values_hash[color.id.to_s] = color.option_value_ids
356
+ option_values_hash[logo.id.to_s] = logo.option_value_ids
357
+ product.option_values_hash = option_values_hash
358
+ product.save
359
+ product.reload
360
+ expect(product.option_type_ids.length).to eq(3)
361
+ expect(product.variants.length).to eq(27)
362
+ end
363
+ end
364
+ end
365
+
366
+ context "#images" do
367
+ let(:product) { create(:product) }
368
+ let(:image) { File.open(File.expand_path('../../../fixtures/thinking-cat.jpg', __FILE__)) }
369
+ let(:params) { {:viewable_id => product.master.id, :viewable_type => 'Spree::Variant', :attachment => image, :alt => "position 2", :position => 2} }
370
+
371
+ before do
372
+ Spree::Image.create(params)
373
+ Spree::Image.create(params.merge({:alt => "position 1", :position => 1}))
374
+ Spree::Image.create(params.merge({:viewable_type => 'ThirdParty::Extension', :alt => "position 1", :position => 2}))
375
+ end
376
+
377
+ it "only looks for variant images" do
378
+ expect(product.images.size).to eq(2)
379
+ end
380
+
381
+ it "should be sorted by position" do
382
+ expect(product.images.pluck(:alt)).to eq(["position 1", "position 2"])
383
+ end
384
+ end
385
+
386
+ # Regression tests for #2352
387
+ context "classifications and taxons" do
388
+ it "is joined through classifications" do
389
+ reflection = Spree::Product.reflect_on_association(:taxons)
390
+ expect(reflection.options[:through]).to eq(:classifications)
391
+ end
392
+
393
+ it "will delete all classifications" do
394
+ reflection = Spree::Product.reflect_on_association(:classifications)
395
+ expect(reflection.options[:dependent]).to eq(:delete_all)
396
+ end
397
+ end
398
+
399
+ context '#total_on_hand' do
400
+ it 'should be infinite if track_inventory_levels is false' do
401
+ Spree::Config[:track_inventory_levels] = false
402
+ expect(build(:product, :variants_including_master => [build(:master_variant)]).total_on_hand).to eql(Float::INFINITY)
403
+ end
404
+
405
+ it 'should be infinite if variant is on demand' do
406
+ Spree::Config[:track_inventory_levels] = true
407
+ expect(build(:product, :variants_including_master => [build(:on_demand_master_variant)]).total_on_hand).to eql(Float::INFINITY)
408
+ end
409
+
410
+ it 'should return sum of stock items count_on_hand' do
411
+ product = create(:product)
412
+ product.stock_items.first.set_count_on_hand 5
413
+ product.variants_including_master(true) # force load association
414
+ expect(product.total_on_hand).to eql(5)
415
+ end
416
+
417
+ it 'should return sum of stock items count_on_hand when variants_including_master is not loaded' do
418
+ product = create(:product)
419
+ product.stock_items.first.set_count_on_hand 5
420
+ expect(product.reload.total_on_hand).to eql(5)
421
+ end
422
+ end
423
+
424
+ # Regression spec for https://github.com/spree/spree/issues/5588
425
+ context '#validate_master when duplicate SKUs entered' do
426
+ let!(:first_product) { create(:product, sku: 'a-sku') }
427
+ let(:second_product) { build(:product, sku: 'a-sku') }
428
+
429
+ subject { second_product }
430
+ it { is_expected.to be_invalid }
431
+ end
432
+
433
+ it "initializes a master variant when building a product" do
434
+ product = Spree::Product.new
435
+ expect(product.master.is_master).to be true
436
+ end
437
+ end