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,44 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ module PromotionHandler
5
+ describe Page, :type => :model do
6
+ let(:order) { create(:order_with_line_items, :line_items_count => 1) }
7
+
8
+ let(:promotion) { Promotion.create(name: "10% off", :path => '10off') }
9
+ before do
10
+ calculator = Calculator::FlatPercentItemTotal.new(preferred_flat_percent: 10)
11
+ action = Promotion::Actions::CreateItemAdjustments.create(:calculator => calculator)
12
+ promotion.actions << action
13
+ end
14
+
15
+ it "activates at the right path" do
16
+ expect(order.line_item_adjustments.count).to eq(0)
17
+ Spree::PromotionHandler::Page.new(order, '10off').activate
18
+ expect(order.line_item_adjustments.count).to eq(1)
19
+ end
20
+
21
+ context "when promotion is expired" do
22
+ before do
23
+ promotion.update_columns(
24
+ :starts_at => 1.week.ago,
25
+ :expires_at => 1.day.ago
26
+ )
27
+ end
28
+
29
+ it "is not activated" do
30
+ expect(order.line_item_adjustments.count).to eq(0)
31
+ Spree::PromotionHandler::Page.new(order, '10off').activate
32
+ expect(order.line_item_adjustments.count).to eq(0)
33
+ end
34
+ end
35
+
36
+ it "does not activate at the wrong path" do
37
+ expect(order.line_item_adjustments.count).to eq(0)
38
+ Spree::PromotionHandler::Page.new(order, 'wrongpath').activate
39
+ expect(order.line_item_adjustments.count).to eq(0)
40
+ end
41
+ end
42
+ end
43
+ end
44
+
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ module Spree
4
+ describe Spree::PromotionRule, :type => :model do
5
+
6
+ class BadTestRule < Spree::PromotionRule; end
7
+
8
+ class TestRule < Spree::PromotionRule
9
+ def eligible?
10
+ true
11
+ end
12
+ end
13
+
14
+ it "should force developer to implement eligible? method" do
15
+ expect { BadTestRule.new.eligible? }.to raise_error
16
+ end
17
+
18
+ it "validates unique rules for a promotion" do
19
+ p1 = TestRule.new
20
+ p1.promotion_id = 1
21
+ p1.save
22
+
23
+ p2 = TestRule.new
24
+ p2.promotion_id = 1
25
+ expect(p2).not_to be_valid
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,603 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Promotion, :type => :model do
4
+ let(:promotion) { Spree::Promotion.new }
5
+
6
+ describe "validations" do
7
+ before :each do
8
+ @valid_promotion = Spree::Promotion.new :name => "A promotion"
9
+ end
10
+
11
+ it "valid_promotion is valid" do
12
+ expect(@valid_promotion).to be_valid
13
+ end
14
+
15
+ it "validates usage limit" do
16
+ @valid_promotion.usage_limit = -1
17
+ expect(@valid_promotion).not_to be_valid
18
+
19
+ @valid_promotion.usage_limit = 100
20
+ expect(@valid_promotion).to be_valid
21
+ end
22
+
23
+ it "validates name" do
24
+ @valid_promotion.name = nil
25
+ expect(@valid_promotion).not_to be_valid
26
+ end
27
+ end
28
+
29
+ describe ".coupons" do
30
+ it "scopes promotions with coupon code present only" do
31
+ promotion = Spree::Promotion.create! name: "test", code: ''
32
+ expect(Spree::Promotion.coupons).to be_empty
33
+
34
+ promotion.update_column :code, "check"
35
+ expect(Spree::Promotion.coupons.first).to eq promotion
36
+ end
37
+ end
38
+
39
+ describe ".applied" do
40
+ it "scopes promotions that have been applied to an order only" do
41
+ promotion = Spree::Promotion.create! name: "test", code: ''
42
+ expect(Spree::Promotion.applied).to be_empty
43
+
44
+ promotion.orders << create(:order)
45
+ expect(Spree::Promotion.applied.first).to eq promotion
46
+ end
47
+ end
48
+
49
+ describe ".advertised" do
50
+ let(:promotion) { create(:promotion) }
51
+ let(:advertised_promotion) { create(:promotion, :advertise => true) }
52
+
53
+ it "only shows advertised promotions" do
54
+ advertised = Spree::Promotion.advertised
55
+ expect(advertised).to include(advertised_promotion)
56
+ expect(advertised).not_to include(promotion)
57
+ end
58
+ end
59
+
60
+ describe "#destroy" do
61
+ let(:promotion) { Spree::Promotion.create(:name => "delete me") }
62
+
63
+ before(:each) do
64
+ promotion.actions << Spree::Promotion::Actions::CreateAdjustment.new
65
+ promotion.rules << Spree::Promotion::Rules::FirstOrder.new
66
+ promotion.save!
67
+ promotion.destroy
68
+ end
69
+
70
+ it "should delete actions" do
71
+ expect(Spree::PromotionAction.count).to eq(0)
72
+ end
73
+
74
+ it "should delete rules" do
75
+ expect(Spree::PromotionRule.count).to eq(0)
76
+ end
77
+ end
78
+
79
+ describe "#save" do
80
+ let(:promotion) { Spree::Promotion.create(:name => "delete me") }
81
+
82
+ before(:each) do
83
+ promotion.actions << Spree::Promotion::Actions::CreateAdjustment.new
84
+ promotion.rules << Spree::Promotion::Rules::FirstOrder.new
85
+ promotion.save!
86
+ end
87
+
88
+ it "should deeply autosave records and preferences" do
89
+ promotion.actions[0].calculator.preferred_flat_percent = 10
90
+ promotion.save!
91
+ expect(Spree::Calculator.first.preferred_flat_percent).to eq(10)
92
+ end
93
+ end
94
+
95
+ describe "#activate" do
96
+ before do
97
+ @action1 = Spree::Promotion::Actions::CreateAdjustment.create!
98
+ @action2 = Spree::Promotion::Actions::CreateAdjustment.create!
99
+ allow(@action1).to receive_messages perform: true
100
+ allow(@action2).to receive_messages perform: true
101
+
102
+ promotion.promotion_actions = [@action1, @action2]
103
+ promotion.created_at = 2.days.ago
104
+
105
+ @user = stub_model(Spree::LegacyUser, :email => "spree@example.com")
106
+ @order = Spree::Order.create user: @user
107
+ @payload = { :order => @order, :user => @user }
108
+ end
109
+
110
+ it "should check path if present" do
111
+ promotion.path = 'content/cvv'
112
+ @payload[:path] = 'content/cvv'
113
+ expect(@action1).to receive(:perform).with(@payload)
114
+ expect(@action2).to receive(:perform).with(@payload)
115
+ promotion.activate(@payload)
116
+ end
117
+
118
+ it "does not perform actions against an order in a finalized state" do
119
+ expect(@action1).not_to receive(:perform).with(@payload)
120
+
121
+ @order.state = 'complete'
122
+ promotion.activate(@payload)
123
+
124
+ @order.state = 'awaiting_return'
125
+ promotion.activate(@payload)
126
+
127
+ @order.state = 'returned'
128
+ promotion.activate(@payload)
129
+ end
130
+
131
+ it "does activate if newer then order" do
132
+ expect(@action1).to receive(:perform).with(@payload)
133
+ promotion.created_at = DateTime.now + 2
134
+ expect(promotion.activate(@payload)).to be true
135
+ end
136
+
137
+ context "keeps track of the orders" do
138
+ context "when activated" do
139
+ it "assigns the order" do
140
+ expect(promotion.orders).to be_empty
141
+ expect(promotion.activate(@payload)).to be true
142
+ expect(promotion.orders.first).to eql @order
143
+ end
144
+ end
145
+ context "when not activated" do
146
+ it "will not assign the order" do
147
+ @order.state = 'complete'
148
+ expect(promotion.orders).to be_empty
149
+ expect(promotion.activate(@payload)).to be_falsey
150
+ expect(promotion.orders).to be_empty
151
+ end
152
+ end
153
+
154
+ end
155
+
156
+ end
157
+
158
+ context "#usage_limit_exceeded" do
159
+ let(:promotable) { double('Promotable') }
160
+ it "should not have its usage limit exceeded with no usage limit" do
161
+ promotion.usage_limit = 0
162
+ expect(promotion.usage_limit_exceeded?(promotable)).to be false
163
+ end
164
+
165
+ it "should have its usage limit exceeded" do
166
+ promotion.usage_limit = 2
167
+ allow(promotion).to receive_messages(:adjusted_credits_count => 2)
168
+ expect(promotion.usage_limit_exceeded?(promotable)).to be true
169
+
170
+ allow(promotion).to receive_messages(:adjusted_credits_count => 3)
171
+ expect(promotion.usage_limit_exceeded?(promotable)).to be true
172
+ end
173
+ end
174
+
175
+ context "#expired" do
176
+ it "should not be exipired" do
177
+ expect(promotion).not_to be_expired
178
+ end
179
+
180
+ it "should be expired if it hasn't started yet" do
181
+ promotion.starts_at = Time.now + 1.day
182
+ expect(promotion).to be_expired
183
+ end
184
+
185
+ it "should be expired if it has already ended" do
186
+ promotion.expires_at = Time.now - 1.day
187
+ expect(promotion).to be_expired
188
+ end
189
+
190
+ it "should not be expired if it has started already" do
191
+ promotion.starts_at = Time.now - 1.day
192
+ expect(promotion).not_to be_expired
193
+ end
194
+
195
+ it "should not be expired if it has not ended yet" do
196
+ promotion.expires_at = Time.now + 1.day
197
+ expect(promotion).not_to be_expired
198
+ end
199
+
200
+ it "should not be expired if current time is within starts_at and expires_at range" do
201
+ promotion.starts_at = Time.now - 1.day
202
+ promotion.expires_at = Time.now + 1.day
203
+ expect(promotion).not_to be_expired
204
+ end
205
+
206
+ it "should not be expired if usage limit is not exceeded" do
207
+ promotion.usage_limit = 2
208
+ allow(promotion).to receive_messages(:credits_count => 1)
209
+ expect(promotion).not_to be_expired
210
+ end
211
+ end
212
+
213
+ context "#credits_count" do
214
+ let!(:promotion) do
215
+ promotion = Spree::Promotion.new
216
+ promotion.name = "Foo"
217
+ promotion.code = "XXX"
218
+ calculator = Spree::Calculator::FlatRate.new
219
+ promotion.tap(&:save)
220
+ end
221
+
222
+ let!(:action) do
223
+ calculator = Spree::Calculator::FlatRate.new
224
+ action_params = { :promotion => promotion, :calculator => calculator }
225
+ action = Spree::Promotion::Actions::CreateAdjustment.create(action_params)
226
+ promotion.actions << action
227
+ action
228
+ end
229
+
230
+ let!(:adjustment) do
231
+ order = create(:order)
232
+ Spree::Adjustment.create!(
233
+ order: order,
234
+ adjustable: order,
235
+ source: action,
236
+ amount: 10,
237
+ label: 'Promotional adjustment'
238
+ )
239
+ end
240
+
241
+ it "counts eligible adjustments" do
242
+ adjustment.update_column(:eligible, true)
243
+ expect(promotion.credits_count).to eq(1)
244
+ end
245
+
246
+ # Regression test for #4112
247
+ it "does not count ineligible adjustments" do
248
+ adjustment.update_column(:eligible, false)
249
+ expect(promotion.credits_count).to eq(0)
250
+ end
251
+ end
252
+
253
+ context "#adjusted_credits_count" do
254
+ let(:order) { create :order }
255
+ let(:line_item) { create :line_item, order: order }
256
+ let(:promotion) { Spree::Promotion.create name: "promo", :code => "10off" }
257
+ let(:order_action) {
258
+ action = Spree::Promotion::Actions::CreateAdjustment.create(calculator: Spree::Calculator::FlatPercentItemTotal.new)
259
+ promotion.actions << action
260
+ action
261
+ }
262
+ let(:item_action) {
263
+ action = Spree::Promotion::Actions::CreateItemAdjustments.create(calculator: Spree::Calculator::FlatPercentItemTotal.new)
264
+ promotion.actions << action
265
+ action
266
+ }
267
+ let(:order_adjustment) do
268
+ Spree::Adjustment.create!(
269
+ :source => order_action,
270
+ :amount => 10,
271
+ :adjustable => order,
272
+ :order => order,
273
+ :label => "Promotional adjustment"
274
+ )
275
+ end
276
+ let(:item_adjustment) do
277
+ Spree::Adjustment.create!(
278
+ :source => item_action,
279
+ :amount => 10,
280
+ :adjustable => line_item,
281
+ :order => order,
282
+ :label => "Promotional adjustment"
283
+ )
284
+ end
285
+
286
+ it "counts order level adjustments" do
287
+ expect(order_adjustment.adjustable).to eq(order)
288
+ expect(promotion.credits_count).to eq(1)
289
+ expect(promotion.adjusted_credits_count(order)).to eq(0)
290
+ end
291
+
292
+ it "counts item level adjustments" do
293
+ expect(item_adjustment.adjustable).to eq(line_item)
294
+ expect(promotion.credits_count).to eq(1)
295
+ expect(promotion.adjusted_credits_count(order)).to eq(0)
296
+ end
297
+ end
298
+
299
+ context "#products" do
300
+ let(:promotion) { create(:promotion) }
301
+
302
+ context "when it has product rules with products associated" do
303
+ let(:promotion_rule) { Spree::Promotion::Rules::Product.new }
304
+
305
+ before do
306
+ promotion_rule.promotion = promotion
307
+ promotion_rule.products << create(:product)
308
+ promotion_rule.save
309
+ end
310
+
311
+ it "should have products" do
312
+ expect(promotion.reload.products.size).to eq(1)
313
+ end
314
+ end
315
+
316
+ context "when there's no product rule associated" do
317
+ it "should not have products but still return an empty array" do
318
+ expect(promotion.products).to be_blank
319
+ end
320
+ end
321
+ end
322
+
323
+ context "#eligible?" do
324
+ let(:promotable) { create :order }
325
+ subject { promotion.eligible?(promotable) }
326
+ context "when promotion is expired" do
327
+ before { promotion.expires_at = Time.now - 10.days }
328
+ it { is_expected.to be false }
329
+ end
330
+ context "when promotable is a Spree::LineItem" do
331
+ let(:promotable) { create :line_item }
332
+ let(:product) { promotable.product }
333
+ before do
334
+ product.promotionable = promotionable
335
+ end
336
+ context "and product is promotionable" do
337
+ let(:promotionable) { true }
338
+ it { is_expected.to be true }
339
+ end
340
+ context "and product is not promotionable" do
341
+ let(:promotionable) { false }
342
+ it { is_expected.to be false }
343
+ end
344
+ end
345
+ context "when promotable is a Spree::Order" do
346
+ let(:promotable) { create :order }
347
+ context "and it is empty" do
348
+ it { is_expected.to be true }
349
+ end
350
+ context "and it contains items" do
351
+ let!(:line_item) { create(:line_item, order: promotable) }
352
+ context "and the items are all non-promotionable" do
353
+ before do
354
+ line_item.product.update_column(:promotionable, false)
355
+ end
356
+ it { is_expected.to be false }
357
+ end
358
+ context "and at least one item is promotionable" do
359
+ it { is_expected.to be true }
360
+ end
361
+ end
362
+ end
363
+ end
364
+
365
+ context "#eligible_rules" do
366
+ let(:promotable) { double('Promotable') }
367
+ it "true if there are no rules" do
368
+ expect(promotion.eligible_rules(promotable)).to eq []
369
+ end
370
+
371
+ it "true if there are no applicable rules" do
372
+ promotion.promotion_rules = [stub_model(Spree::PromotionRule, :eligible? => true, :applicable? => false)]
373
+ allow(promotion.promotion_rules).to receive(:for).and_return([])
374
+ expect(promotion.eligible_rules(promotable)).to eq []
375
+ end
376
+
377
+ context "with 'all' match policy" do
378
+ let(:promo1) { Spree::PromotionRule.create! }
379
+ let(:promo2) { Spree::PromotionRule.create! }
380
+
381
+ before { promotion.match_policy = 'all' }
382
+
383
+ context "when all rules are eligible" do
384
+ before do
385
+ allow(promo1).to receive_messages(eligible?: true, applicable?: true)
386
+ allow(promo2).to receive_messages(eligible?: true, applicable?: true)
387
+
388
+ promotion.promotion_rules = [promo1, promo2]
389
+ allow(promotion.promotion_rules).to receive(:for).and_return(promotion.promotion_rules)
390
+ end
391
+ it "returns the eligible rules" do
392
+ expect(promotion.eligible_rules(promotable)).to eq [promo1, promo2]
393
+ end
394
+ it "does set anything to eligiblity errors" do
395
+ promotion.eligible_rules(promotable)
396
+ expect(promotion.eligibility_errors).to be_nil
397
+ end
398
+ end
399
+
400
+ context "when any of the rules is not eligible" do
401
+ let(:errors) { double ActiveModel::Errors, empty?: false }
402
+ before do
403
+ allow(promo1).to receive_messages(eligible?: true, applicable?: true, eligibility_errors: nil)
404
+ allow(promo2).to receive_messages(eligible?: false, applicable?: true, eligibility_errors: errors)
405
+
406
+ promotion.promotion_rules = [promo1, promo2]
407
+ allow(promotion.promotion_rules).to receive(:for).and_return(promotion.promotion_rules)
408
+ end
409
+ it "returns nil" do
410
+ expect(promotion.eligible_rules(promotable)).to be_nil
411
+ end
412
+ it "sets eligibility errors to the first non-nil one" do
413
+ promotion.eligible_rules(promotable)
414
+ expect(promotion.eligibility_errors).to eq errors
415
+ end
416
+ end
417
+ end
418
+
419
+ context "with 'any' match policy" do
420
+ let(:promotion) { Spree::Promotion.create(:name => "Promo", :match_policy => 'any') }
421
+ let(:promotable) { double('Promotable') }
422
+
423
+ it "should have eligible rules if any of the rules are eligible" do
424
+ allow_any_instance_of(Spree::PromotionRule).to receive_messages(:applicable? => true)
425
+ true_rule = Spree::PromotionRule.create(:promotion => promotion)
426
+ allow(true_rule).to receive_messages(:eligible? => true)
427
+ allow(promotion).to receive_messages(:rules => [true_rule])
428
+ allow(promotion).to receive_message_chain(:rules, :for).and_return([true_rule])
429
+ expect(promotion.eligible_rules(promotable)).to eq [true_rule]
430
+ end
431
+
432
+ context "when none of the rules are eligible" do
433
+ let(:promo) { Spree::PromotionRule.create! }
434
+ let(:errors) { double ActiveModel::Errors, empty?: false }
435
+ before do
436
+ allow(promo).to receive_messages(eligible?: false, applicable?: true, eligibility_errors: errors)
437
+
438
+ promotion.promotion_rules = [promo]
439
+ allow(promotion.promotion_rules).to receive(:for).and_return(promotion.promotion_rules)
440
+ end
441
+ it "returns nil" do
442
+ expect(promotion.eligible_rules(promotable)).to be_nil
443
+ end
444
+ it "sets eligibility errors to the first non-nil one" do
445
+ promotion.eligible_rules(promotable)
446
+ expect(promotion.eligibility_errors).to eq errors
447
+ end
448
+ end
449
+ end
450
+ end
451
+
452
+ describe '#line_item_actionable?' do
453
+ let(:order) { double Spree::Order }
454
+ let(:line_item) { double Spree::LineItem}
455
+ let(:true_rule) { double Spree::PromotionRule, eligible?: true, applicable?: true, actionable?: true }
456
+ let(:false_rule) { double Spree::PromotionRule, eligible?: true, applicable?: true, actionable?: false }
457
+ let(:rules) { [] }
458
+
459
+ before do
460
+ allow(promotion).to receive(:rules) { rules }
461
+ allow(rules).to receive(:for) { rules }
462
+ end
463
+
464
+ subject { promotion.line_item_actionable? order, line_item }
465
+
466
+ context 'when the order is eligible for promotion' do
467
+ context 'when there are no rules' do
468
+ it { is_expected.to be }
469
+ end
470
+
471
+ context 'when there are rules' do
472
+ context 'when the match policy is all' do
473
+ before { promotion.match_policy = 'all' }
474
+
475
+ context 'when all rules allow action on the line item' do
476
+ let(:rules) { [true_rule] }
477
+ it { is_expected.to be}
478
+ end
479
+
480
+ context 'when at least one rule does not allow action on the line item' do
481
+ let(:rules) { [true_rule, false_rule] }
482
+ it { is_expected.not_to be}
483
+ end
484
+ end
485
+
486
+ context 'when the match policy is any' do
487
+ before { promotion.match_policy = 'any' }
488
+
489
+ context 'when at least one rule allows action on the line item' do
490
+ let(:rules) { [true_rule, false_rule] }
491
+ it { is_expected.to be }
492
+ end
493
+
494
+ context 'when no rules allow action on the line item' do
495
+ let(:rules) { [false_rule] }
496
+ it { is_expected.not_to be}
497
+ end
498
+ end
499
+ end
500
+ end
501
+
502
+ context 'when the order is not eligible for the promotion' do
503
+ before { promotion.starts_at = Time.current + 2.days }
504
+ it { is_expected.not_to be }
505
+ end
506
+ end
507
+
508
+ # regression for #4059
509
+ # admin form posts the code and path as empty string
510
+ describe "normalize blank values for code & path" do
511
+ it "will save blank value as nil value instead" do
512
+ promotion = Spree::Promotion.create(:name => "A promotion", :code => "", :path => "")
513
+ expect(promotion.code).to be_nil
514
+ expect(promotion.path).to be_nil
515
+ end
516
+ end
517
+
518
+ # Regression test for #4081
519
+ describe "#with_coupon_code" do
520
+ context "and code stored in uppercase" do
521
+ let!(:promotion) { create(:promotion, :code => "MY-COUPON-123") }
522
+ it "finds the code with lowercase" do
523
+ expect(Spree::Promotion.with_coupon_code("my-coupon-123")).to eql promotion
524
+ end
525
+ end
526
+ end
527
+
528
+ describe '#used_by?' do
529
+ subject { promotion.used_by? user, [excluded_order] }
530
+
531
+ let(:promotion) { create :promotion, :with_order_adjustment }
532
+ let(:user) { create :user }
533
+ let(:order) { create :order_with_line_items, user: user }
534
+ let(:excluded_order) { create :order_with_line_items, user: user }
535
+
536
+ before do
537
+ order.user_id = user.id
538
+ order.save!
539
+ end
540
+
541
+ context 'when the user has used this promo' do
542
+ before do
543
+ promotion.activate(order: order)
544
+ order.update!
545
+ order.completed_at = Time.now
546
+ order.save!
547
+ end
548
+
549
+ context 'when the order is complete' do
550
+ it { is_expected.to be true }
551
+
552
+ context 'when the promotion was not eligible' do
553
+ let(:adjustment) { order.adjustments.first }
554
+
555
+ before do
556
+ adjustment.eligible = false
557
+ adjustment.save!
558
+ end
559
+
560
+ it { is_expected.to be false }
561
+ end
562
+
563
+ context 'when the only matching order is the excluded order' do
564
+ let(:excluded_order) { order }
565
+ it { is_expected.to be false }
566
+ end
567
+ end
568
+
569
+ context 'when the order is not complete' do
570
+ let(:order) { create :order, user: user }
571
+ it { is_expected.to be false }
572
+ end
573
+ end
574
+
575
+ context 'when the user has not used this promo' do
576
+ it { is_expected.to be false }
577
+ end
578
+ end
579
+
580
+ describe "adding items to the cart" do
581
+ let(:order) { create :order }
582
+ let(:line_item) { create :line_item, order: order }
583
+ let(:promo) { create :promotion_with_item_adjustment, adjustment_rate: 5, code: 'promo' }
584
+ let(:variant) { create :variant }
585
+
586
+ it "updates the promotions for new line items" do
587
+ expect(line_item.adjustments).to be_empty
588
+ expect(order.adjustment_total).to eq 0
589
+
590
+ promo.activate order: order
591
+ order.update!
592
+
593
+ expect(line_item.adjustments.size).to eq(1)
594
+ expect(order.adjustment_total).to eq -5
595
+
596
+ other_line_item = order.contents.add(variant, 1, currency: order.currency)
597
+
598
+ expect(other_line_item).not_to eq line_item
599
+ expect(other_line_item.adjustments.size).to eq(1)
600
+ expect(order.adjustment_total).to eq -10
601
+ end
602
+ end
603
+ end