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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -0
- data/Gemfile +3 -0
- data/Rakefile +16 -0
- data/script/rails +9 -0
- data/solidus_core.gemspec +48 -0
- data/spec/fixtures/thinking-cat.jpg +0 -0
- data/spec/helpers/base_helper_spec.rb +173 -0
- data/spec/helpers/order_helper_spec.rb +12 -0
- data/spec/helpers/products_helper_spec.rb +220 -0
- data/spec/helpers/taxons_helper_spec.rb +17 -0
- data/spec/lib/calculated_adjustments_spec.rb +7 -0
- data/spec/lib/i18n_spec.rb +123 -0
- data/spec/lib/search/base_spec.rb +86 -0
- data/spec/lib/search/variant_spec.rb +92 -0
- data/spec/lib/spree/core/controller_helpers/auth_spec.rb +66 -0
- data/spec/lib/spree/core/controller_helpers/order_spec.rb +92 -0
- data/spec/lib/spree/core/controller_helpers/search_spec.rb +17 -0
- data/spec/lib/spree/core/controller_helpers/store_spec.rb +16 -0
- data/spec/lib/spree/core/controller_helpers/strong_parameters_spec.rb +39 -0
- data/spec/lib/spree/core/current_store_spec.rb +36 -0
- data/spec/lib/spree/core/delegate_belongs_to_spec.rb +22 -0
- data/spec/lib/spree/core/importer/order_spec.rb +431 -0
- data/spec/lib/spree/core/role_configuration_spec.rb +138 -0
- data/spec/lib/spree/core/validators/email_spec.rb +48 -0
- data/spec/lib/spree/localized_number_spec.rb +38 -0
- data/spec/lib/spree/migrations_spec.rb +36 -0
- data/spec/lib/spree/money_spec.rb +127 -0
- data/spec/lib/tasks/exchanges_spec.rb +231 -0
- data/spec/lib/tasks/migrations/copy_shipped_shipments_to_cartons_spec.rb +115 -0
- data/spec/lib/tasks/order_capturing_spec.rb +56 -0
- data/spec/mailers/carton_mailer_spec.rb +43 -0
- data/spec/mailers/order_mailer_spec.rb +122 -0
- data/spec/mailers/reimbursement_mailer_spec.rb +40 -0
- data/spec/mailers/test_mailer_spec.rb +15 -0
- data/spec/models/spree/ability_spec.rb +276 -0
- data/spec/models/spree/address_spec.rb +250 -0
- data/spec/models/spree/adjustment_reason_spec.rb +13 -0
- data/spec/models/spree/adjustment_spec.rb +177 -0
- data/spec/models/spree/app_configuration_spec.rb +20 -0
- data/spec/models/spree/asset_spec.rb +24 -0
- data/spec/models/spree/calculator/default_tax_spec.rb +127 -0
- data/spec/models/spree/calculator/flat_percent_item_total_spec.rb +25 -0
- data/spec/models/spree/calculator/flat_rate_spec.rb +47 -0
- data/spec/models/spree/calculator/flexi_rate_spec.rb +41 -0
- data/spec/models/spree/calculator/percent_on_line_item_spec.rb +15 -0
- data/spec/models/spree/calculator/price_sack_spec.rb +30 -0
- data/spec/models/spree/calculator/refunds/default_refund_amount_spec.rb +51 -0
- data/spec/models/spree/calculator/shipping/flat_percent_item_total_spec.rb +23 -0
- data/spec/models/spree/calculator/shipping/flat_rate_spec.rb +13 -0
- data/spec/models/spree/calculator/shipping/flexi_rate_spec.rb +52 -0
- data/spec/models/spree/calculator/shipping/per_item_spec.rb +20 -0
- data/spec/models/spree/calculator/shipping/price_sack_spec.rb +30 -0
- data/spec/models/spree/calculator/tiered_flat_rate_spec.rb +36 -0
- data/spec/models/spree/calculator/tiered_percent_spec.rb +47 -0
- data/spec/models/spree/calculator_spec.rb +36 -0
- data/spec/models/spree/carton_spec.rb +133 -0
- data/spec/models/spree/classification_spec.rb +15 -0
- data/spec/models/spree/concerns/display_money_spec.rb +43 -0
- data/spec/models/spree/concerns/user_methods_spec.rb +41 -0
- data/spec/models/spree/credit_card_spec.rb +334 -0
- data/spec/models/spree/customer_return_spec.rb +276 -0
- data/spec/models/spree/exchange_spec.rb +79 -0
- data/spec/models/spree/gateway/bogus_simple.rb +20 -0
- data/spec/models/spree/gateway/bogus_spec.rb +13 -0
- data/spec/models/spree/gateway_spec.rb +82 -0
- data/spec/models/spree/inventory_unit_spec.rb +307 -0
- data/spec/models/spree/item_adjustments_spec.rb +256 -0
- data/spec/models/spree/line_item_spec.rb +191 -0
- data/spec/models/spree/option_type_spec.rb +14 -0
- data/spec/models/spree/option_value_spec.rb +22 -0
- data/spec/models/spree/order/address_spec.rb +50 -0
- data/spec/models/spree/order/adjustments_spec.rb +39 -0
- data/spec/models/spree/order/callbacks_spec.rb +42 -0
- data/spec/models/spree/order/checkout_spec.rb +902 -0
- data/spec/models/spree/order/currency_updater_spec.rb +32 -0
- data/spec/models/spree/order/finalizing_spec.rb +111 -0
- data/spec/models/spree/order/payment_spec.rb +210 -0
- data/spec/models/spree/order/risk_assessment_spec.rb +68 -0
- data/spec/models/spree/order/state_machine_spec.rb +221 -0
- data/spec/models/spree/order/tax_spec.rb +84 -0
- data/spec/models/spree/order/totals_spec.rb +24 -0
- data/spec/models/spree/order/updating_spec.rb +18 -0
- data/spec/models/spree/order/validations_spec.rb +15 -0
- data/spec/models/spree/order_cancellations_spec.rb +120 -0
- data/spec/models/spree/order_capturing_spec.rb +116 -0
- data/spec/models/spree/order_contents_spec.rb +265 -0
- data/spec/models/spree/order_inventory_spec.rb +228 -0
- data/spec/models/spree/order_mutex_spec.rb +85 -0
- data/spec/models/spree/order_promotion_spec.rb +31 -0
- data/spec/models/spree/order_shipping_spec.rb +247 -0
- data/spec/models/spree/order_spec.rb +1412 -0
- data/spec/models/spree/order_stock_location_spec.rb +18 -0
- data/spec/models/spree/order_updater_spec.rb +299 -0
- data/spec/models/spree/payment_method/store_credit_spec.rb +294 -0
- data/spec/models/spree/payment_method_spec.rb +96 -0
- data/spec/models/spree/payment_spec.rb +1044 -0
- data/spec/models/spree/permission_sets/base_spec.rb +12 -0
- data/spec/models/spree/permission_sets/configuration_display.rb +82 -0
- data/spec/models/spree/permission_sets/configuration_management_spec.rb +50 -0
- data/spec/models/spree/permission_sets/dashboard_display_spec.rb +22 -0
- data/spec/models/spree/permission_sets/order_display_spec.rb +49 -0
- data/spec/models/spree/permission_sets/order_management_spec.rb +36 -0
- data/spec/models/spree/permission_sets/product_display_spec.rb +60 -0
- data/spec/models/spree/permission_sets/product_management_spec.rb +40 -0
- data/spec/models/spree/permission_sets/promotion_display_spec.rb +34 -0
- data/spec/models/spree/permission_sets/promotion_management_spec.rb +26 -0
- data/spec/models/spree/permission_sets/report_display_spec.rb +24 -0
- data/spec/models/spree/permission_sets/restricted_transfer_management_spec.rb +132 -0
- data/spec/models/spree/permission_sets/stock_display_spec.rb +26 -0
- data/spec/models/spree/permission_sets/stock_management_spec.rb +24 -0
- data/spec/models/spree/permission_sets/user_display_spec.rb +36 -0
- data/spec/models/spree/permission_sets/user_management_spec.rb +28 -0
- data/spec/models/spree/preference_spec.rb +80 -0
- data/spec/models/spree/preferences/configuration_spec.rb +30 -0
- data/spec/models/spree/preferences/preferable_spec.rb +294 -0
- data/spec/models/spree/preferences/scoped_store_spec.rb +58 -0
- data/spec/models/spree/preferences/static_model_preferences_spec.rb +78 -0
- data/spec/models/spree/preferences/statically_configurable_spec.rb +60 -0
- data/spec/models/spree/preferences/store_spec.rb +39 -0
- data/spec/models/spree/price_spec.rb +42 -0
- data/spec/models/spree/product/scopes_spec.rb +148 -0
- data/spec/models/spree/product_duplicator_spec.rb +103 -0
- data/spec/models/spree/product_filter_spec.rb +26 -0
- data/spec/models/spree/product_property_spec.rb +20 -0
- data/spec/models/spree/product_spec.rb +437 -0
- data/spec/models/spree/promotion/actions/create_adjustment_spec.rb +96 -0
- data/spec/models/spree/promotion/actions/create_item_adjustments_spec.rb +165 -0
- data/spec/models/spree/promotion/actions/create_quantity_adjustments_spec.rb +115 -0
- data/spec/models/spree/promotion/actions/free_shipping_spec.rb +40 -0
- data/spec/models/spree/promotion/rules/first_order_spec.rb +75 -0
- data/spec/models/spree/promotion/rules/item_total_spec.rb +67 -0
- data/spec/models/spree/promotion/rules/nth_order_spec.rb +70 -0
- data/spec/models/spree/promotion/rules/one_use_per_user_spec.rb +42 -0
- data/spec/models/spree/promotion/rules/option_value_spec.rb +94 -0
- data/spec/models/spree/promotion/rules/product_spec.rb +143 -0
- data/spec/models/spree/promotion/rules/taxon_spec.rb +102 -0
- data/spec/models/spree/promotion/rules/user_logged_in_spec.rb +27 -0
- data/spec/models/spree/promotion/rules/user_spec.rb +37 -0
- data/spec/models/spree/promotion_builder_spec.rb +118 -0
- data/spec/models/spree/promotion_category_spec.rb +17 -0
- data/spec/models/spree/promotion_code/code_builder_spec.rb +79 -0
- data/spec/models/spree/promotion_code_spec.rb +187 -0
- data/spec/models/spree/promotion_handler/cart_spec.rb +114 -0
- data/spec/models/spree/promotion_handler/coupon_spec.rb +335 -0
- data/spec/models/spree/promotion_handler/free_shipping_spec.rb +47 -0
- data/spec/models/spree/promotion_handler/page_spec.rb +44 -0
- data/spec/models/spree/promotion_rule_spec.rb +28 -0
- data/spec/models/spree/promotion_spec.rb +767 -0
- data/spec/models/spree/refund_spec.rb +204 -0
- data/spec/models/spree/reimbursement/credit_spec.rb +36 -0
- data/spec/models/spree/reimbursement/reimbursement_type_engine_spec.rb +140 -0
- data/spec/models/spree/reimbursement/reimbursement_type_validator_spec.rb +83 -0
- data/spec/models/spree/reimbursement_performer_spec.rb +30 -0
- data/spec/models/spree/reimbursement_spec.rb +231 -0
- data/spec/models/spree/reimbursement_tax_calculator_spec.rb +51 -0
- data/spec/models/spree/reimbursement_type/credit_spec.rb +53 -0
- data/spec/models/spree/reimbursement_type/exchange_spec.rb +46 -0
- data/spec/models/spree/reimbursement_type/original_payment_spec.rb +107 -0
- data/spec/models/spree/reimbursement_type/store_credit_spec.rb +97 -0
- data/spec/models/spree/return_authorization_spec.rb +290 -0
- data/spec/models/spree/return_item/eligibility_validator/default_spec.rb +77 -0
- data/spec/models/spree/return_item/eligibility_validator/inventory_shipped_spec.rb +58 -0
- data/spec/models/spree/return_item/eligibility_validator/no_reimbursements_spec.rb +85 -0
- data/spec/models/spree/return_item/eligibility_validator/order_completed_spec.rb +32 -0
- data/spec/models/spree/return_item/eligibility_validator/rma_required_spec.rb +29 -0
- data/spec/models/spree/return_item/eligibility_validator/time_since_purchase_spec.rb +35 -0
- data/spec/models/spree/return_item/exchange_variant_eligibility/same_option_value_spec.rb +65 -0
- data/spec/models/spree/return_item/exchange_variant_eligibility/same_product_spec.rb +43 -0
- data/spec/models/spree/return_item_spec.rb +775 -0
- data/spec/models/spree/returns_calculator_spec.rb +14 -0
- data/spec/models/spree/shipment_spec.rb +709 -0
- data/spec/models/spree/shipping_calculator_spec.rb +45 -0
- data/spec/models/spree/shipping_method_spec.rb +88 -0
- data/spec/models/spree/shipping_rate_spec.rb +142 -0
- data/spec/models/spree/state_spec.rb +14 -0
- data/spec/models/spree/stock/availability_validator_spec.rb +83 -0
- data/spec/models/spree/stock/coordinator_spec.rb +116 -0
- data/spec/models/spree/stock/differentiator_spec.rb +39 -0
- data/spec/models/spree/stock/estimator_spec.rb +146 -0
- data/spec/models/spree/stock/inventory_unit_builder_spec.rb +38 -0
- data/spec/models/spree/stock/package_spec.rb +163 -0
- data/spec/models/spree/stock/packer_spec.rb +91 -0
- data/spec/models/spree/stock/prioritizer_spec.rb +125 -0
- data/spec/models/spree/stock/quantifier_spec.rb +115 -0
- data/spec/models/spree/stock/splitter/backordered_spec.rb +29 -0
- data/spec/models/spree/stock/splitter/base_spec.rb +21 -0
- data/spec/models/spree/stock/splitter/shipping_category_spec.rb +50 -0
- data/spec/models/spree/stock/splitter/weight_spec.rb +29 -0
- data/spec/models/spree/stock_item_spec.rb +426 -0
- data/spec/models/spree/stock_location_spec.rb +279 -0
- data/spec/models/spree/stock_movement_spec.rb +56 -0
- data/spec/models/spree/stock_transfer_spec.rb +290 -0
- data/spec/models/spree/store_credit_category_spec.rb +17 -0
- data/spec/models/spree/store_credit_event_spec.rb +314 -0
- data/spec/models/spree/store_credit_spec.rb +876 -0
- data/spec/models/spree/store_spec.rb +55 -0
- data/spec/models/spree/tax_category_spec.rb +27 -0
- data/spec/models/spree/tax_rate_spec.rb +378 -0
- data/spec/models/spree/taxon_spec.rb +74 -0
- data/spec/models/spree/taxonomy_spec.rb +18 -0
- data/spec/models/spree/tracker_spec.rb +21 -0
- data/spec/models/spree/transfer_item_spec.rb +264 -0
- data/spec/models/spree/unit_cancel_spec.rb +148 -0
- data/spec/models/spree/user_spec.rb +223 -0
- data/spec/models/spree/validations/db_maximum_length_validator_spec.rb +23 -0
- data/spec/models/spree/variant/scopes_spec.rb +55 -0
- data/spec/models/spree/variant_spec.rb +546 -0
- data/spec/models/spree/zone_spec.rb +305 -0
- data/spec/spec_helper.rb +78 -0
- data/spec/support/big_decimal.rb +5 -0
- data/spec/support/concerns/default_price.rb +34 -0
- data/spec/support/dummy_ability.rb +4 -0
- data/spec/support/test_gateway.rb +2 -0
- metadata +229 -3
- data/lib/spree/testing_support/rspec-activemodel-mocks_patch.rb +0 -8
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Spree::LegacyUser, :type => :model do
|
|
4
|
+
context "#last_incomplete_order" do
|
|
5
|
+
let!(:user) { create(:user) }
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
it "excludes orders that are not frontend_viewable" do
|
|
9
|
+
order = create(:order, user: user, frontend_viewable: false)
|
|
10
|
+
expect(user.last_incomplete_spree_order).to eq nil
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "can include orders that are not frontend viewable" do
|
|
14
|
+
order = create(:order, user: user, frontend_viewable: false)
|
|
15
|
+
expect(user.last_incomplete_spree_order(only_frontend_viewable: false)).to eq order
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "can scope to a store" do
|
|
19
|
+
store = create(:store)
|
|
20
|
+
store_1_order = create(:order, user: user, store: store)
|
|
21
|
+
store_2_order = create(:order, user: user, store: create(:store))
|
|
22
|
+
expect(user.last_incomplete_spree_order(store: store)).to eq store_1_order
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "excludes completed orders" do
|
|
26
|
+
order = create(:completed_order_with_totals, user: user, created_by: user)
|
|
27
|
+
expect(user.last_incomplete_spree_order).to eq nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "excludes orders created prior to the user's last completed order" do
|
|
31
|
+
incomplete_order = create(:order, user: user, created_by: user, created_at: 1.second.ago)
|
|
32
|
+
completed_order = create(:completed_order_with_totals, user: user, created_by: user)
|
|
33
|
+
expect(user.last_incomplete_spree_order).to eq nil
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "chooses the most recently created incomplete order" do
|
|
37
|
+
order_1 = create(:order, user: user, created_at: 1.second.ago)
|
|
38
|
+
order_2 = create(:order, user: user)
|
|
39
|
+
expect(user.last_incomplete_spree_order).to eq order_2
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
context "persists order address" do
|
|
43
|
+
let!(:order) { create(:order, bill_address: create(:address), ship_address: create(:address)) }
|
|
44
|
+
|
|
45
|
+
it "copies over order addresses" do
|
|
46
|
+
expect {
|
|
47
|
+
user.persist_order_address(order)
|
|
48
|
+
}.to change { Spree::Address.count }.by(2)
|
|
49
|
+
|
|
50
|
+
expect(user.bill_address).to eq order.bill_address
|
|
51
|
+
expect(user.ship_address).to eq order.ship_address
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "doesnt create new addresses if user has already" do
|
|
55
|
+
user.update_column(:bill_address_id, create(:address))
|
|
56
|
+
user.update_column(:ship_address_id, create(:address))
|
|
57
|
+
user.reload
|
|
58
|
+
|
|
59
|
+
expect {
|
|
60
|
+
user.persist_order_address(order)
|
|
61
|
+
}.not_to change { Spree::Address.count }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "set both bill and ship address id on subject" do
|
|
65
|
+
user.persist_order_address(order)
|
|
66
|
+
|
|
67
|
+
expect(user.bill_address_id).not_to be_blank
|
|
68
|
+
expect(user.ship_address_id).not_to be_blank
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
context "payment source" do
|
|
73
|
+
let(:payment_method) { create(:credit_card_payment_method) }
|
|
74
|
+
let!(:cc) do
|
|
75
|
+
create(:credit_card, user_id: user.id, payment_method: payment_method, gateway_customer_profile_id: "2342343")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "has payment sources" do
|
|
79
|
+
expect(user.payment_sources.first.gateway_customer_profile_id).not_to be_empty
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "drops payment source" do
|
|
83
|
+
user.drop_payment_source cc
|
|
84
|
+
expect(cc.gateway_customer_profile_id).to be_nil
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
describe Spree.user_class, :type => :model do
|
|
91
|
+
context "reporting" do
|
|
92
|
+
let(:order_value) { BigDecimal.new("80.94") }
|
|
93
|
+
let(:order_count) { 4 }
|
|
94
|
+
let(:orders) { Array.new(order_count, double(total: order_value)) }
|
|
95
|
+
|
|
96
|
+
before do
|
|
97
|
+
allow(orders).to receive(:pluck).with(:total).and_return(orders.map(&:total))
|
|
98
|
+
allow(orders).to receive(:count).and_return(orders.length)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def load_orders
|
|
102
|
+
allow(subject).to receive(:spree_orders).and_return(double(complete: orders))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe "#lifetime_value" do
|
|
106
|
+
context "with orders" do
|
|
107
|
+
before { load_orders }
|
|
108
|
+
it "returns the total of completed orders for the user" do
|
|
109
|
+
expect(subject.lifetime_value).to eq (order_count * order_value)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
context "without orders" do
|
|
113
|
+
it "returns 0.00" do
|
|
114
|
+
expect(subject.lifetime_value).to eq BigDecimal("0.00")
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe "#display_lifetime_value" do
|
|
120
|
+
it "returns a Spree::Money version of lifetime_value" do
|
|
121
|
+
value = BigDecimal("500.05")
|
|
122
|
+
allow(subject).to receive(:lifetime_value).and_return(value)
|
|
123
|
+
expect(subject.display_lifetime_value).to eq Spree::Money.new(value)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
describe "#order_count" do
|
|
128
|
+
before { load_orders }
|
|
129
|
+
it "returns the count of completed orders for the user" do
|
|
130
|
+
expect(subject.order_count).to eq BigDecimal(order_count)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
describe "#average_order_value" do
|
|
135
|
+
context "with orders" do
|
|
136
|
+
before { load_orders }
|
|
137
|
+
it "returns the average completed order price for the user" do
|
|
138
|
+
expect(subject.average_order_value).to eq order_value
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
context "without orders" do
|
|
142
|
+
it "returns 0.00" do
|
|
143
|
+
expect(subject.average_order_value).to eq BigDecimal("0.00")
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
describe "#display_average_order_value" do
|
|
149
|
+
before { load_orders }
|
|
150
|
+
it "returns a Spree::Money version of average_order_value" do
|
|
151
|
+
value = BigDecimal("500.05")
|
|
152
|
+
allow(subject).to receive(:average_order_value).and_return(value)
|
|
153
|
+
expect(subject.display_average_order_value).to eq Spree::Money.new(value)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
describe "#total_available_store_credit" do
|
|
159
|
+
context "user does not have any associated store credits" do
|
|
160
|
+
subject { create(:user) }
|
|
161
|
+
|
|
162
|
+
it "returns 0" do
|
|
163
|
+
expect(subject.total_available_store_credit).to be_zero
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
context "user has several associated store credits" do
|
|
168
|
+
let(:user) { create(:user) }
|
|
169
|
+
let(:amount) { 120.25 }
|
|
170
|
+
let(:additional_amount) { 55.75 }
|
|
171
|
+
let(:store_credit) { create(:store_credit, user: user, amount: amount, amount_used: 0.0) }
|
|
172
|
+
let!(:additional_store_credit) { create(:store_credit, user: user, amount: additional_amount, amount_used: 0.0) }
|
|
173
|
+
|
|
174
|
+
subject { store_credit.user }
|
|
175
|
+
|
|
176
|
+
context "part of the store credit has been used" do
|
|
177
|
+
let(:amount_used) { 35.00 }
|
|
178
|
+
|
|
179
|
+
before { store_credit.update_attributes(amount_used: amount_used) }
|
|
180
|
+
|
|
181
|
+
context "part of the store credit has been authorized" do
|
|
182
|
+
let(:authorized_amount) { 10 }
|
|
183
|
+
|
|
184
|
+
before { additional_store_credit.update_attributes(amount_authorized: authorized_amount) }
|
|
185
|
+
|
|
186
|
+
it "returns sum of amounts minus used amount and authorized amount" do
|
|
187
|
+
expect(subject.total_available_store_credit.to_f).to eq (amount + additional_amount - amount_used - authorized_amount)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
context "there are no authorized amounts on any of the store credits" do
|
|
192
|
+
it "returns sum of amounts minus used amount" do
|
|
193
|
+
expect(subject.total_available_store_credit.to_f).to eq (amount + additional_amount - amount_used)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
context "store credits have never been used" do
|
|
199
|
+
context "part of the store credit has been authorized" do
|
|
200
|
+
let(:authorized_amount) { 10 }
|
|
201
|
+
|
|
202
|
+
before { additional_store_credit.update_attributes(amount_authorized: authorized_amount) }
|
|
203
|
+
|
|
204
|
+
it "returns sum of amounts minus authorized amount" do
|
|
205
|
+
expect(subject.total_available_store_credit.to_f).to eq (amount + additional_amount - authorized_amount)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
context "there are no authorized amounts on any of the store credits" do
|
|
210
|
+
it "returns sum of amounts" do
|
|
211
|
+
expect(subject.total_available_store_credit.to_f).to eq (amount + additional_amount)
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
context "all store credits have never been used or authorized" do
|
|
217
|
+
it "returns sum of amounts" do
|
|
218
|
+
expect(subject.total_available_store_credit.to_f).to eq (amount + additional_amount)
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Spree::Validations::DbMaximumLengthValidator, type: :model do
|
|
4
|
+
context 'when Spree::Product' do
|
|
5
|
+
Spree::Product.class_eval do
|
|
6
|
+
attribute :slug, ActiveRecord::Type::String.new(limit: 255)
|
|
7
|
+
# Slug currently has no validation for maximum length
|
|
8
|
+
validates_with Spree::Validations::DbMaximumLengthValidator, field: :slug
|
|
9
|
+
end
|
|
10
|
+
let(:limit) { 255 }
|
|
11
|
+
let(:product) { Spree::Product.new }
|
|
12
|
+
let(:slug) { "x" * (limit + 1)}
|
|
13
|
+
|
|
14
|
+
before do
|
|
15
|
+
product.slug = slug
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it 'should maximum validate slug' do
|
|
19
|
+
product.valid?
|
|
20
|
+
expect(product.errors[:slug]).to include(I18n.t("errors.messages.too_long", count: limit))
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe "Variant scopes", :type => :model do
|
|
4
|
+
let!(:product) { create(:product) }
|
|
5
|
+
let!(:variant_1) { create(:variant, :product => product) }
|
|
6
|
+
let!(:variant_2) { create(:variant, :product => product) }
|
|
7
|
+
|
|
8
|
+
it ".descend_by_popularity" do
|
|
9
|
+
# Requires a product with at least two variants, where one has a higher number of
|
|
10
|
+
# orders than the other
|
|
11
|
+
Spree::LineItem.delete_all # FIXME leaky database - too many line_items
|
|
12
|
+
create(:line_item, :variant => variant_1)
|
|
13
|
+
expect(Spree::Variant.descend_by_popularity.first).to eq(variant_1)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
context "finding by option values" do
|
|
18
|
+
let!(:option_type) { create(:option_type, :name => "bar") }
|
|
19
|
+
let!(:option_value_1) do
|
|
20
|
+
option_value = create(:option_value, :name => "foo", :option_type => option_type)
|
|
21
|
+
variant_1.option_values << option_value
|
|
22
|
+
option_value
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
let!(:option_value_2) do
|
|
26
|
+
option_value = create(:option_value, :name => "fizz", :option_type => option_type)
|
|
27
|
+
variant_1.option_values << option_value
|
|
28
|
+
option_value
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
let!(:product_variants) { product.variants_including_master }
|
|
32
|
+
|
|
33
|
+
it "by objects" do
|
|
34
|
+
variants = product_variants.has_option(option_type, option_value_1)
|
|
35
|
+
expect(variants).to include(variant_1)
|
|
36
|
+
expect(variants).not_to include(variant_2)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "by names" do
|
|
40
|
+
variants = product_variants.has_option("bar", "foo")
|
|
41
|
+
expect(variants).to include(variant_1)
|
|
42
|
+
expect(variants).not_to include(variant_2)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "by ids" do
|
|
46
|
+
variants = product_variants.has_option(option_type.id, option_value_1.id)
|
|
47
|
+
expect(variants).to include(variant_1)
|
|
48
|
+
expect(variants).not_to include(variant_2)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "by mixed conditions" do
|
|
52
|
+
variants = product_variants.has_option(option_type.id, "foo", option_value_2)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Spree::Variant, :type => :model do
|
|
6
|
+
let!(:variant) { create(:variant) }
|
|
7
|
+
|
|
8
|
+
it_behaves_like 'default_price'
|
|
9
|
+
|
|
10
|
+
context "validations" do
|
|
11
|
+
it "should validate price is greater than 0" do
|
|
12
|
+
variant.price = -1
|
|
13
|
+
expect(variant).to be_invalid
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should validate price is 0" do
|
|
17
|
+
variant.price = 0
|
|
18
|
+
expect(variant).to be_valid
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context "after create" do
|
|
23
|
+
let!(:product) { create(:product) }
|
|
24
|
+
|
|
25
|
+
it "propagate to stock items" do
|
|
26
|
+
expect_any_instance_of(Spree::StockLocation).to receive(:propagate_variant)
|
|
27
|
+
product.variants.create(:name => "Foobar")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
context "stock location has disable propagate all variants" do
|
|
31
|
+
before { Spree::StockLocation.update_all propagate_all_variants: false }
|
|
32
|
+
|
|
33
|
+
it "propagate to stock items" do
|
|
34
|
+
expect_any_instance_of(Spree::StockLocation).not_to receive(:propagate_variant)
|
|
35
|
+
product.variants.create(:name => "Foobar")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe 'mark_master_out_of_stock' do
|
|
40
|
+
before do
|
|
41
|
+
product.master.stock_items.first.set_count_on_hand(5)
|
|
42
|
+
end
|
|
43
|
+
context 'when product is created without variants but with stock' do
|
|
44
|
+
it { expect(product.master).to be_in_stock }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context 'when a variant is created' do
|
|
48
|
+
before(:each) do
|
|
49
|
+
product.variants.create!(:name => 'any-name')
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it { expect(product.master).to_not be_in_stock }
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
context "product has other variants" do
|
|
58
|
+
describe "option value accessors" do
|
|
59
|
+
before {
|
|
60
|
+
@multi_variant = FactoryGirl.create :variant, :product => variant.product
|
|
61
|
+
variant.product.reload
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let(:multi_variant) { @multi_variant }
|
|
65
|
+
|
|
66
|
+
it "should set option value" do
|
|
67
|
+
expect(multi_variant.option_value('media_type')).to be_nil
|
|
68
|
+
|
|
69
|
+
multi_variant.set_option_value('media_type', 'DVD')
|
|
70
|
+
expect(multi_variant.option_value('media_type')).to eql 'DVD'
|
|
71
|
+
|
|
72
|
+
multi_variant.set_option_value('media_type', 'CD')
|
|
73
|
+
expect(multi_variant.option_value('media_type')).to eql 'CD'
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "should not duplicate associated option values when set multiple times" do
|
|
77
|
+
multi_variant.set_option_value('media_type', 'CD')
|
|
78
|
+
|
|
79
|
+
expect {
|
|
80
|
+
multi_variant.set_option_value('media_type', 'DVD')
|
|
81
|
+
}.to_not change(multi_variant.option_values, :count)
|
|
82
|
+
|
|
83
|
+
expect {
|
|
84
|
+
multi_variant.set_option_value('coolness_type', 'awesome')
|
|
85
|
+
}.to change(multi_variant.option_values, :count).by(1)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
context "product has other variants" do
|
|
90
|
+
describe "option value accessors" do
|
|
91
|
+
before {
|
|
92
|
+
@multi_variant = create(:variant, :product => variant.product)
|
|
93
|
+
variant.product.reload
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
let(:multi_variant) { @multi_variant }
|
|
97
|
+
|
|
98
|
+
it "should set option value" do
|
|
99
|
+
expect(multi_variant.option_value('media_type')).to be_nil
|
|
100
|
+
|
|
101
|
+
multi_variant.set_option_value('media_type', 'DVD')
|
|
102
|
+
expect(multi_variant.option_value('media_type')).to eql 'DVD'
|
|
103
|
+
|
|
104
|
+
multi_variant.set_option_value('media_type', 'CD')
|
|
105
|
+
expect(multi_variant.option_value('media_type')).to eql 'CD'
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
it "should not duplicate associated option values when set multiple times" do
|
|
109
|
+
multi_variant.set_option_value('media_type', 'CD')
|
|
110
|
+
|
|
111
|
+
expect {
|
|
112
|
+
multi_variant.set_option_value('media_type', 'DVD')
|
|
113
|
+
}.to_not change(multi_variant.option_values, :count)
|
|
114
|
+
|
|
115
|
+
expect {
|
|
116
|
+
multi_variant.set_option_value('coolness_type', 'awesome')
|
|
117
|
+
}.to change(multi_variant.option_values, :count).by(1)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
context "#cost_price=" do
|
|
124
|
+
it "should use LocalizedNumber.parse" do
|
|
125
|
+
expect(Spree::LocalizedNumber).to receive(:parse).with('1,599.99')
|
|
126
|
+
subject.cost_price = '1,599.99'
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
context "#price=" do
|
|
131
|
+
it "should use LocalizedNumber.parse" do
|
|
132
|
+
expect(Spree::LocalizedNumber).to receive(:parse).with('1,599.99')
|
|
133
|
+
subject.price = '1,599.99'
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
context "#weight=" do
|
|
138
|
+
it "should use LocalizedNumber.parse" do
|
|
139
|
+
expect(Spree::LocalizedNumber).to receive(:parse).with('1,599.99')
|
|
140
|
+
subject.weight = '1,599.99'
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
context "#currency" do
|
|
145
|
+
it "returns the globally configured currency" do
|
|
146
|
+
expect(variant.currency).to eql "USD"
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
context "#display_amount" do
|
|
151
|
+
it "returns a Spree::Money" do
|
|
152
|
+
variant.price = 21.22
|
|
153
|
+
expect(variant.display_amount.to_s).to eql "$21.22"
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
context "#cost_currency" do
|
|
158
|
+
context "when cost currency is nil" do
|
|
159
|
+
before { variant.cost_currency = nil }
|
|
160
|
+
it "populates cost currency with the default value on save" do
|
|
161
|
+
variant.save!
|
|
162
|
+
expect(variant.cost_currency).to eql "USD"
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
context "#default_price" do
|
|
168
|
+
context "when multiple prices are present in addition to a default price" do
|
|
169
|
+
before do
|
|
170
|
+
variant.prices << create(:price, :variant => variant, :currency => "USD", :amount => 12.12, :is_default => false)
|
|
171
|
+
variant.prices << create(:price, :variant => variant, :currency => "EUR", :amount => 29.99)
|
|
172
|
+
variant.prices << create(:price, :variant => variant, :currency => "EUR", :amount => 10.00, :is_default => false)
|
|
173
|
+
variant.reload
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "the default stays the same" do
|
|
177
|
+
expect(variant.default_price.amount).to eq(19.99)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
it "displays default price" do
|
|
181
|
+
expect(variant.price_in("USD").display_amount.to_s).to eq("$19.99")
|
|
182
|
+
expect(variant.price_in("EUR").display_amount.to_s).to eq("€29.99")
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
context "when adding multiple prices" do
|
|
187
|
+
it "it can reassign a default price" do
|
|
188
|
+
expect(variant.default_price.amount).to eq(19.99)
|
|
189
|
+
variant.prices << create(:price, :variant => variant, :currency => "USD", :amount => 12.12)
|
|
190
|
+
expect(variant.reload.default_price.amount).to eq(12.12)
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
describe '.price_in' do
|
|
196
|
+
before do
|
|
197
|
+
variant.prices << create(:price, :variant => variant, :currency => "EUR", :amount => 33.33)
|
|
198
|
+
end
|
|
199
|
+
subject { variant.price_in(currency).display_amount }
|
|
200
|
+
|
|
201
|
+
context "when currency is not specified" do
|
|
202
|
+
let(:currency) { nil }
|
|
203
|
+
|
|
204
|
+
it "returns 0" do
|
|
205
|
+
expect(subject.to_s).to eql "$0.00"
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
context "when currency is EUR" do
|
|
210
|
+
let(:currency) { 'EUR' }
|
|
211
|
+
|
|
212
|
+
it "returns the value in the EUR" do
|
|
213
|
+
expect(subject.to_s).to eql "€33.33"
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
context "when currency is USD" do
|
|
218
|
+
let(:currency) { 'USD' }
|
|
219
|
+
|
|
220
|
+
it "returns the value in the USD" do
|
|
221
|
+
expect(subject.to_s).to eql "$19.99"
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
describe '.amount_in' do
|
|
227
|
+
before do
|
|
228
|
+
variant.prices << create(:price, :variant => variant, :currency => "EUR", :amount => 33.33)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
subject { variant.amount_in(currency) }
|
|
232
|
+
|
|
233
|
+
context "when currency is not specified" do
|
|
234
|
+
let(:currency) { nil }
|
|
235
|
+
|
|
236
|
+
it "returns nil" do
|
|
237
|
+
expect(subject).to be_nil
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
context "when currency is EUR" do
|
|
242
|
+
let(:currency) { 'EUR' }
|
|
243
|
+
|
|
244
|
+
it "returns the value in the EUR" do
|
|
245
|
+
expect(subject).to eql 33.33
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
context "when currency is USD" do
|
|
250
|
+
let(:currency) { 'USD' }
|
|
251
|
+
|
|
252
|
+
it "returns the value in the USD" do
|
|
253
|
+
expect(subject).to eql 19.99
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Regression test for #2432
|
|
259
|
+
describe 'options_text' do
|
|
260
|
+
let!(:variant) { create(:variant, option_values: []) }
|
|
261
|
+
let!(:master) { create(:master_variant) }
|
|
262
|
+
|
|
263
|
+
before do
|
|
264
|
+
# Order bar than foo
|
|
265
|
+
variant.option_values << create(:option_value, {name: 'Foo', presentation: 'Foo', option_type: create(:option_type, position: 2, name: 'Foo Type', presentation: 'Foo Type')})
|
|
266
|
+
variant.option_values << create(:option_value, {name: 'Bar', presentation: 'Bar', option_type: create(:option_type, position: 1, name: 'Bar Type', presentation: 'Bar Type')})
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
it 'should order by bar than foo' do
|
|
270
|
+
expect(variant.options_text).to eql 'Bar Type: Bar, Foo Type: Foo'
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
describe 'exchange_name' do
|
|
276
|
+
let!(:variant) { create(:variant, option_values: []) }
|
|
277
|
+
let!(:master) { create(:master_variant) }
|
|
278
|
+
|
|
279
|
+
before do
|
|
280
|
+
variant.option_values << create(:option_value, {
|
|
281
|
+
name: 'Foo',
|
|
282
|
+
presentation: 'Foo',
|
|
283
|
+
option_type: create(:option_type, position: 2, name: 'Foo Type', presentation: 'Foo Type')
|
|
284
|
+
})
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
context 'master variant' do
|
|
288
|
+
it 'should return name' do
|
|
289
|
+
expect(master.exchange_name).to eql master.name
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
context 'variant' do
|
|
294
|
+
it 'should return options text' do
|
|
295
|
+
expect(variant.exchange_name).to eql 'Foo Type: Foo'
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
describe 'exchange_name' do
|
|
302
|
+
let!(:variant) { create(:variant, option_values: []) }
|
|
303
|
+
let!(:master) { create(:master_variant) }
|
|
304
|
+
|
|
305
|
+
before do
|
|
306
|
+
variant.option_values << create(:option_value, {
|
|
307
|
+
name: 'Foo',
|
|
308
|
+
presentation: 'Foo',
|
|
309
|
+
option_type: create(:option_type, position: 2, name: 'Foo Type', presentation: 'Foo Type')
|
|
310
|
+
})
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
context 'master variant' do
|
|
314
|
+
it 'should return name' do
|
|
315
|
+
expect(master.exchange_name).to eql master.name
|
|
316
|
+
end
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
context 'variant' do
|
|
320
|
+
it 'should return options text' do
|
|
321
|
+
expect(variant.exchange_name).to eql 'Foo Type: Foo'
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
describe 'descriptive_name' do
|
|
328
|
+
let!(:variant) { create(:variant, option_values: []) }
|
|
329
|
+
let!(:master) { create(:master_variant) }
|
|
330
|
+
|
|
331
|
+
before do
|
|
332
|
+
variant.option_values << create(:option_value, {
|
|
333
|
+
name: 'Foo',
|
|
334
|
+
presentation: 'Foo',
|
|
335
|
+
option_type: create(:option_type, position: 2, name: 'Foo Type', presentation: 'Foo Type')
|
|
336
|
+
})
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
context 'master variant' do
|
|
340
|
+
it 'should return name with Master identifier' do
|
|
341
|
+
expect(master.descriptive_name).to eql master.name + ' - Master'
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
context 'variant' do
|
|
346
|
+
it 'should return options text with name' do
|
|
347
|
+
expect(variant.descriptive_name).to eql variant.name + ' - Foo Type: Foo'
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# Regression test for #2744
|
|
354
|
+
describe "set_position" do
|
|
355
|
+
it "sets variant position after creation" do
|
|
356
|
+
variant = create(:variant)
|
|
357
|
+
expect(variant.position).to_not be_nil
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
describe '#in_stock?' do
|
|
362
|
+
before do
|
|
363
|
+
Spree::Config.track_inventory_levels = true
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
context 'when stock_items are not backorderable' do
|
|
367
|
+
before do
|
|
368
|
+
allow_any_instance_of(Spree::StockItem).to receive_messages(backorderable: false)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
context 'when stock_items in stock' do
|
|
372
|
+
before do
|
|
373
|
+
variant.stock_items.first.update_column(:count_on_hand, 10)
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
it 'returns true if stock_items in stock' do
|
|
377
|
+
expect(variant.in_stock?).to be true
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
context 'when stock_items out of stock' do
|
|
382
|
+
before do
|
|
383
|
+
allow_any_instance_of(Spree::StockItem).to receive_messages(backorderable: false)
|
|
384
|
+
allow_any_instance_of(Spree::StockItem).to receive_messages(count_on_hand: 0)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
it 'return false if stock_items out of stock' do
|
|
388
|
+
expect(variant.in_stock?).to be false
|
|
389
|
+
end
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
describe "#can_supply?" do
|
|
394
|
+
it "calls out to quantifier" do
|
|
395
|
+
expect(Spree::Stock::Quantifier).to receive(:new).and_return(quantifier = double)
|
|
396
|
+
expect(quantifier).to receive(:can_supply?).with(10)
|
|
397
|
+
variant.can_supply?(10)
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
context 'when stock_items are backorderable' do
|
|
402
|
+
before do
|
|
403
|
+
allow_any_instance_of(Spree::StockItem).to receive_messages(backorderable: true)
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
context 'when stock_items out of stock' do
|
|
407
|
+
before do
|
|
408
|
+
allow_any_instance_of(Spree::StockItem).to receive_messages(count_on_hand: 0)
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
it 'in_stock? returns false' do
|
|
412
|
+
expect(variant.in_stock?).to be false
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
it 'can_supply? return true' do
|
|
416
|
+
expect(variant.can_supply?).to be true
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
end
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
describe '#is_backorderable' do
|
|
423
|
+
let(:variant) { build(:variant) }
|
|
424
|
+
subject { variant.is_backorderable? }
|
|
425
|
+
|
|
426
|
+
it 'should invoke Spree::Stock::Quantifier' do
|
|
427
|
+
expect_any_instance_of(Spree::Stock::Quantifier).to receive(:backorderable?) { true }
|
|
428
|
+
subject
|
|
429
|
+
end
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
describe '#total_on_hand' do
|
|
433
|
+
it 'should be infinite if track_inventory_levels is false' do
|
|
434
|
+
Spree::Config[:track_inventory_levels] = false
|
|
435
|
+
expect(build(:variant).total_on_hand).to eql(Float::INFINITY)
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
it 'should match quantifier total_on_hand' do
|
|
439
|
+
variant = build(:variant)
|
|
440
|
+
expect(variant.total_on_hand).to eq(Spree::Stock::Quantifier.new(variant).total_on_hand)
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
describe '#tax_category' do
|
|
445
|
+
context 'when tax_category is nil' do
|
|
446
|
+
let(:product) { build(:product) }
|
|
447
|
+
let(:variant) { build(:variant, product: product, tax_category_id: nil) }
|
|
448
|
+
it 'returns the parent products tax_category' do
|
|
449
|
+
expect(variant.tax_category).to eq(product.tax_category)
|
|
450
|
+
end
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
context 'when tax_category is set' do
|
|
454
|
+
let(:tax_category) { create(:tax_category) }
|
|
455
|
+
let(:variant) { build(:variant, tax_category: tax_category) }
|
|
456
|
+
it 'returns the tax_category set on itself' do
|
|
457
|
+
expect(variant.tax_category).to eq(tax_category)
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
describe "touching" do
|
|
463
|
+
it "updates a product" do
|
|
464
|
+
variant.product.update_column(:updated_at, 1.day.ago)
|
|
465
|
+
variant.touch
|
|
466
|
+
expect(variant.product.reload.updated_at).to be_within(3.seconds).of(Time.now)
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
it "clears the in_stock cache key" do
|
|
470
|
+
expect(Rails.cache).to receive(:delete).with(variant.send(:in_stock_cache_key))
|
|
471
|
+
variant.touch
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
describe "#should_track_inventory?" do
|
|
476
|
+
|
|
477
|
+
it 'should not track inventory when global setting is off' do
|
|
478
|
+
Spree::Config[:track_inventory_levels] = false
|
|
479
|
+
|
|
480
|
+
expect(build(:variant).should_track_inventory?).to eq(false)
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
it 'should not track inventory when variant is turned off' do
|
|
484
|
+
Spree::Config[:track_inventory_levels] = true
|
|
485
|
+
|
|
486
|
+
expect(build(:on_demand_variant).should_track_inventory?).to eq(false)
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
it 'should track inventory when global and variant are on' do
|
|
490
|
+
Spree::Config[:track_inventory_levels] = true
|
|
491
|
+
|
|
492
|
+
expect(build(:variant).should_track_inventory?).to eq(true)
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
describe "deleted_at scope" do
|
|
497
|
+
before { variant.destroy && variant.reload }
|
|
498
|
+
it "should have a price if deleted" do
|
|
499
|
+
variant.price = 10
|
|
500
|
+
expect(variant.price).to eq(10)
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
describe "stock movements" do
|
|
505
|
+
let!(:movement) { create(:stock_movement, stock_item: variant.stock_items.first) }
|
|
506
|
+
|
|
507
|
+
it "builds out collection just fine through stock items" do
|
|
508
|
+
expect(variant.stock_movements.to_a).not_to be_empty
|
|
509
|
+
end
|
|
510
|
+
end
|
|
511
|
+
|
|
512
|
+
describe "in_stock scope" do
|
|
513
|
+
it "returns all in stock variants" do
|
|
514
|
+
in_stock_variant = create(:variant)
|
|
515
|
+
out_of_stock_variant = create(:variant)
|
|
516
|
+
|
|
517
|
+
in_stock_variant.stock_items.first.update_column(:count_on_hand, 10)
|
|
518
|
+
|
|
519
|
+
expect(Spree::Variant.in_stock).to eq [in_stock_variant]
|
|
520
|
+
end
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
describe "#display_image" do
|
|
524
|
+
subject { variant.display_image }
|
|
525
|
+
|
|
526
|
+
context "variant has associated images" do
|
|
527
|
+
let(:attachment) { File.open(File.expand_path('../../../fixtures/thinking-cat.jpg', __FILE__)) }
|
|
528
|
+
let(:image_params) { { viewable_id: variant.id, viewable_type: 'Spree::Variant', attachment: attachment, alt: "position 1", position: 1 } }
|
|
529
|
+
let!(:first_image) { Spree::Image.create(image_params) }
|
|
530
|
+
let!(:second_image) { image_params.merge(alt: "position 2", position: 2) }
|
|
531
|
+
|
|
532
|
+
it "returns the first image" do
|
|
533
|
+
expect(subject).to eq first_image
|
|
534
|
+
end
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
context "variant does not have any associated images" do
|
|
538
|
+
it "returns an image" do
|
|
539
|
+
expect(subject).to be_a(Spree::Image)
|
|
540
|
+
end
|
|
541
|
+
it "returns unpersisted record" do
|
|
542
|
+
expect(subject).to be_new_record
|
|
543
|
+
end
|
|
544
|
+
end
|
|
545
|
+
end
|
|
546
|
+
end
|