solidus_easypost 2.0.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +6 -0
  3. data/.github/stale.yml +4 -4
  4. data/.github_changelog_generator +2 -0
  5. data/.gitignore +1 -0
  6. data/.rubocop.yml +3 -0
  7. data/.rubocop_todo.yml +12 -47
  8. data/CHANGELOG.md +113 -0
  9. data/LICENSE +1 -1
  10. data/README.md +111 -21
  11. data/app/controllers/spree/admin/postage_labels_controller.rb +20 -0
  12. data/app/decorators/models/solidus_easypost/spree/carton_decorator.rb +38 -0
  13. data/app/decorators/models/solidus_easypost/spree/shipment_decorator.rb +18 -23
  14. data/app/models/solidus_easypost/parcel_dimension.rb +34 -0
  15. data/app/models/solidus_easypost/return_authorization.rb +2 -13
  16. data/app/overrides/spree/admin/orders/_shipment/add_postage_label.html.erb.deface +10 -0
  17. data/bin/rails +0 -1
  18. data/bin/rails-engine +0 -2
  19. data/bin/rails-sandbox +1 -2
  20. data/bin/sandbox +2 -0
  21. data/config/initializers/webhooks.rb +7 -0
  22. data/config/locales/en.yml +7 -4
  23. data/config/routes.rb +5 -1
  24. data/db/migrate/20201025110912_add_tracker_id_to_cartons.rb +6 -0
  25. data/lib/generators/solidus_easypost/install/install_generator.rb +7 -1
  26. data/lib/generators/solidus_easypost/install/templates/initializer.rb +26 -0
  27. data/lib/solidus_easypost/address_builder.rb +45 -0
  28. data/lib/solidus_easypost/calculator/base_dimension_calculator.rb +28 -0
  29. data/lib/solidus_easypost/calculator/weight_dimension_calculator.rb +22 -0
  30. data/lib/solidus_easypost/configuration.rb +25 -2
  31. data/lib/solidus_easypost/errors/unknown_partial_resource_error.rb +11 -0
  32. data/lib/solidus_easypost/estimator.rb +37 -0
  33. data/lib/solidus_easypost/parcel_builder.rb +27 -0
  34. data/lib/solidus_easypost/shipment_builder.rb +32 -0
  35. data/lib/solidus_easypost/shipping_method_selector.rb +17 -0
  36. data/lib/solidus_easypost/shipping_rate_calculator.rb +9 -0
  37. data/lib/solidus_easypost/testing_support/factories/address_factory.rb +10 -0
  38. data/lib/solidus_easypost/testing_support/factories/product_factory.rb +7 -0
  39. data/lib/solidus_easypost/testing_support/factories/shipment_factory.rb +30 -0
  40. data/lib/solidus_easypost/testing_support/factories/shipping_method_factory.rb +8 -0
  41. data/lib/solidus_easypost/testing_support/factories/stock_location_factory.rb +10 -0
  42. data/lib/solidus_easypost/testing_support/factories/variant_factory.rb +7 -0
  43. data/lib/solidus_easypost/testing_support/factories.rb +3 -0
  44. data/lib/solidus_easypost/tracker_webhook_handler.rb +14 -0
  45. data/lib/solidus_easypost/version.rb +1 -1
  46. data/lib/solidus_easypost.rb +11 -0
  47. data/solidus_easypost.gemspec +8 -5
  48. data/spec/cassettes/{Spree_Address/_easypost_address/is_an_EasyPost_Address_object.yml → address_builder/from_address.yml} +19 -23
  49. data/spec/cassettes/{Spree_StockLocation/_easypost_address/is_an_EasyPost_Address_object.yml → address_builder/from_stock_location.yml} +19 -23
  50. data/spec/cassettes/{Spree_Stock_Estimator/_shipping_rates/when_rates_are_found/when_shipping_methods_exist/when_shipping_methods_are_front_end_visible/has_the_correct_costs.yml → estimator.yml} +0 -0
  51. data/spec/cassettes/integration/checkout.yml +377 -0
  52. data/spec/cassettes/integration/order_shipping/with_purchase_labels.yml +373 -0
  53. data/spec/cassettes/integration/order_shipping/without_purchase_labels.yml +251 -0
  54. data/spec/cassettes/{Spree_Stock_Package/_easypost_parcel/is_an_EasyPost_Parcel_object.yml → parcel_builder/from_package.yml} +17 -22
  55. data/spec/cassettes/{Spree_Stock_Package/_easypost_parcel/has_the_correct_attributes.yml → parcel_builder/from_return_authorization.yml} +17 -22
  56. data/spec/cassettes/postage_labels/show.yml +72 -0
  57. data/spec/cassettes/return_authorization.yml +310 -0
  58. data/spec/cassettes/{Spree_Stock_Package/_easypost_shipment/is_an_EasyPost_Shipment_object.yml → shipment_builder/from_package.yml} +74 -92
  59. data/spec/cassettes/{Spree_Stock_Estimator/_shipping_rates/when_rates_are_found/when_shipping_methods_exist/create_no_new_shipping_methods.yml → shipment_builder/from_return_authorization.yml} +77 -95
  60. data/spec/cassettes/shipment_builder/from_shipment.yml +250 -0
  61. data/spec/controllers/spree/admin/postage_labels_controller_spec.rb +18 -0
  62. data/spec/features/admin/postage_labels_spec.rb +18 -0
  63. data/spec/integration/checkout_spec.rb +45 -0
  64. data/spec/integration/order_shipping_spec.rb +33 -0
  65. data/spec/models/solidus_easypost/parcel_dimension_spec.rb +80 -0
  66. data/spec/models/solidus_easypost/return_authorization_spec.rb +7 -40
  67. data/spec/models/spree/carton_spec.rb +57 -0
  68. data/spec/models/spree/shipment_spec.rb +48 -59
  69. data/spec/models/spree/shipping_rate_spec.rb +14 -7
  70. data/spec/solidus_easypost/address_builder_spec.rb +17 -0
  71. data/spec/solidus_easypost/calculator/weight_dimension_calculator_spec.rb +39 -0
  72. data/spec/solidus_easypost/estimator_spec.rb +52 -0
  73. data/spec/solidus_easypost/parcel_builder_spec.rb +113 -0
  74. data/spec/solidus_easypost/shipment_builder_spec.rb +28 -0
  75. data/spec/solidus_easypost/shipping_method_selector_spec.rb +33 -0
  76. data/spec/solidus_easypost/shipping_rate_calculator_spec.rb +12 -0
  77. data/spec/solidus_easypost/tracker_webhook_handler_spec.rb +58 -0
  78. data/spec/spec_helper.rb +11 -7
  79. data/spec/support/easypost.rb +3 -1
  80. data/spec/support/helpers/api_stubs.rb +40 -0
  81. data/spec/support/helpers/configuration.rb +28 -0
  82. data/spec/support/helpers/shipping_methods.rb +26 -0
  83. data/spec/support/solidus.rb +1 -0
  84. data/spec/support/vcr.rb +1 -0
  85. metadata +118 -85
  86. data/app/decorators/models/solidus_easypost/spree/address_decorator.rb +0 -30
  87. data/app/decorators/models/solidus_easypost/spree/stock/estimator_decorator.rb +0 -15
  88. data/app/decorators/models/solidus_easypost/spree/stock/package_decorator.rb +0 -27
  89. data/app/decorators/models/solidus_easypost/spree/stock_location_decorator.rb +0 -26
  90. data/app/models/solidus_easypost/estimator.rb +0 -51
  91. data/lib/solidus_easypost/factories.rb +0 -24
  92. data/spec/cassettes/SolidusEasypost_ReturnAuthorization/_easypost_shipment/is_an_EasyPost_Shipment_object.yml +0 -271
  93. data/spec/cassettes/SolidusEasypost_ReturnAuthorization/_return_label/has_the_correct_fields.yml +0 -336
  94. data/spec/cassettes/SolidusEasypost_ReturnAuthorization/_return_label/is_an_EasyPost_PackageLabel_object.yml +0 -336
  95. data/spec/cassettes/Spree_Address/_easypost_address/has_the_correct_attributes.yml +0 -69
  96. data/spec/cassettes/Spree_Order/_refresh_shipment_rates/can_get_rates_from_easy_post.yml +0 -271
  97. data/spec/cassettes/Spree_Order/_refresh_shipment_rates/create_shipping_methods_for_the_rates.yml +0 -269
  98. data/spec/cassettes/Spree_Shipment/_buys_a_shipping_rate_after_transitioning_to_ship.yml +0 -403
  99. data/spec/cassettes/Spree_Shipment/_easypost_shipment/when_it_is_a_new_shipment/behaves_like_an_easypost_shipment/is_an_EasyPost_Shipment_object.yml +0 -271
  100. data/spec/cassettes/Spree_Shipment/_easypost_shipment/when_it_is_a_new_shipment/calls_the_api.yml +0 -200
  101. data/spec/cassettes/Spree_Shipment/_easypost_shipment/when_it_is_an_existing_shipment/behaves_like_an_easypost_shipment/is_an_EasyPost_Shipment_object.yml +0 -334
  102. data/spec/cassettes/Spree_Shipment/_easypost_shipment/when_it_is_an_existing_shipment/loads_the_existing_shipment.yml +0 -271
  103. data/spec/cassettes/Spree_StockLocation/_easypost_address/has_the_correct_attributes.yml +0 -69
  104. data/spec/cassettes/Spree_Stock_Estimator/_shipping_rates/when_rates_are_found/when_shipping_methods_dont_exist/creates_new_shipping_methods.yml +0 -269
  105. data/spec/cassettes/Spree_Stock_Estimator/_shipping_rates/when_rates_are_found/when_shipping_methods_dont_exist/is_empty.yml +0 -269
  106. data/spec/cassettes/Spree_Stock_Estimator/_shipping_rates/when_rates_are_found/when_shipping_methods_exist/when_shipping_methods_are_front_end_visible/has_the_correct_names.yml +0 -269
  107. data/spec/cassettes/Spree_Stock_Estimator/_shipping_rates/when_rates_are_found/when_shipping_methods_exist/when_shipping_methods_are_not_front_end_visible/is_empty.yml +0 -269
  108. data/spec/cassettes/Spree_Stock_Package/_easypost_shipment/calls_the_api.yml +0 -200
  109. data/spec/factories/spree_modification.rb +0 -25
  110. data/spec/models/spree/address_spec.rb +0 -31
  111. data/spec/models/spree/order_spec.rb +0 -31
  112. data/spec/models/spree/stock/estimator_spec.rb +0 -80
  113. data/spec/models/spree/stock/package_spec.rb +0 -35
  114. data/spec/models/spree/stock_location_spec.rb +0 -31
  115. data/spec/support/shipping_method_helpers.rb +0 -43
@@ -0,0 +1,45 @@
1
+ RSpec.describe 'Checkout' do
2
+ it 'retrieves the rates from EasyPost', vcr: { cassette_name: 'integration/checkout' } do
3
+ stub_easypost_config(purchase_labels: true)
4
+ stub_spree_preferences(require_payment_to_ship: false, track_inventory_levels: false)
5
+ use_easypost_estimator
6
+ create_easypost_shipping_methods
7
+
8
+ order = Spree::TestingSupport::OrderWalkthrough.up_to(:complete)
9
+ shipment = order.shipments.first
10
+ shipment.ship!
11
+
12
+ expect(order.state).to eq('complete')
13
+ expect(shipment.shipping_rates).to match_array([
14
+ have_attributes(
15
+ selected: true,
16
+ cost: 0.393e1,
17
+ name: 'USPS First',
18
+ easy_post_shipment_id: /shp_/,
19
+ easy_post_rate_id: /rate_/,
20
+ ),
21
+ have_attributes(
22
+ selected: false,
23
+ cost: 0.692e1,
24
+ name: 'USPS ParcelSelect',
25
+ easy_post_shipment_id: /shp_/,
26
+ easy_post_rate_id: /rate_/,
27
+ ),
28
+ have_attributes(
29
+ selected: false,
30
+ cost: 0.702e1,
31
+ name: 'USPS Priority',
32
+ easy_post_shipment_id: /shp_/,
33
+ easy_post_rate_id: /rate_/,
34
+ ),
35
+ have_attributes(
36
+ selected: false,
37
+ cost: 0.23e2,
38
+ name: 'USPS Express',
39
+ easy_post_shipment_id: /shp_/,
40
+ easy_post_rate_id: /rate_/,
41
+ ),
42
+ ])
43
+ expect(shipment.tracking).to be_present
44
+ end
45
+ end
@@ -0,0 +1,33 @@
1
+ RSpec.describe 'Order shipping' do
2
+ context 'with purchase_labels set to true', vcr: { cassette_name: 'integration/order_shipping/with_purchase_labels' } do
3
+ it 'buys the rate in EasyPost' do
4
+ stub_easypost_config(purchase_labels: true)
5
+ stub_spree_preferences(require_payment_to_ship: false, track_inventory_levels: false)
6
+ use_easypost_estimator
7
+ create_easypost_shipping_methods
8
+
9
+ order = Spree::TestingSupport::OrderWalkthrough.up_to(:complete)
10
+ shipment = order.shipments.first
11
+ shipment.ship!
12
+
13
+ expect(shipment).to be_shipped
14
+ expect(shipment.tracking).to be_present
15
+ end
16
+ end
17
+
18
+ context 'with purchase_labels set to false', vcr: { cassette_name: 'integration/order_shipping/without_purchase_labels' } do
19
+ it 'does not buy the rate in EasyPost' do
20
+ stub_easypost_config(purchase_labels: false)
21
+ stub_spree_preferences(require_payment_to_ship: false, track_inventory_levels: false)
22
+ use_easypost_estimator
23
+ create_easypost_shipping_methods
24
+
25
+ order = Spree::TestingSupport::OrderWalkthrough.up_to(:complete)
26
+ shipment = order.shipments.first
27
+ shipment.ship!
28
+
29
+ expect(shipment).to be_shipped
30
+ expect(shipment.tracking).not_to be_present
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe SolidusEasypost::ParcelDimension do
6
+ describe '.new' do
7
+ subject(:instance) { described_class.new(params) }
8
+
9
+ let(:params) do
10
+ { height: 3 }
11
+ end
12
+
13
+ context 'when the weight is not passed' do
14
+ let(:params) do
15
+ { height: 3 }
16
+ end
17
+
18
+ it 'raises an argument error exception' do
19
+ expect {
20
+ instance
21
+ }.to raise_error(ArgumentError)
22
+ end
23
+ end
24
+
25
+ context 'when the passed weight is zero' do
26
+ let(:params) do
27
+ { height: 3, weight: 0 }
28
+ end
29
+
30
+ it 'raises an argument error exception' do
31
+ expect {
32
+ instance
33
+ }.to raise_error(ArgumentError)
34
+ end
35
+ end
36
+ end
37
+
38
+ describe '#to_h' do
39
+ subject(:instance_hash) { described_class.new(params).to_h }
40
+
41
+ context 'when the params contains zero values' do
42
+ let(:params) do
43
+ { weight: 3, width: 0, height: 0, unknown: -1 }
44
+ end
45
+
46
+ it 'returns the correct parcel dimension hash' do
47
+ expect(instance_hash).to eq({
48
+ weight: 3
49
+ })
50
+ end
51
+ end
52
+
53
+ context 'when the params contains the wrong params' do
54
+ let(:params) do
55
+ { weight: 3, unknown: -1, another: 11 }
56
+ end
57
+
58
+ it 'returns the correct parcel dimension hash' do
59
+ expect(instance_hash).to eq({
60
+ weight: 3
61
+ })
62
+ end
63
+ end
64
+
65
+ context 'when the params contains the correct params' do
66
+ let(:params) do
67
+ { weight: 3, width: 1, depth: 6, height: 11 }
68
+ end
69
+
70
+ it 'returns the correct parcel dimension hash' do
71
+ expect(instance_hash).to eq({
72
+ height: 11,
73
+ weight: 3,
74
+ width: 1,
75
+ length: 6,
76
+ })
77
+ end
78
+ end
79
+ end
80
+ end
@@ -2,49 +2,16 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- RSpec.describe SolidusEasypost::ReturnAuthorization, :vcr do
6
- let(:auth) { described_class.new return_authorization }
7
- let(:return_authorization) do
8
- create :return_authorization do |ra|
9
- ra.return_items << create(:return_item)
10
- end
11
- end
12
- before { create :shipping_method, admin_name: 'USPS Priority' }
13
-
14
- before { create :store }
15
-
16
- describe '#easypost_shipment' do
17
- subject { auth.easypost_shipment }
18
-
19
- it 'is an EasyPost::Shipment object' do
20
- expect(subject).to be_a(EasyPost::Shipment)
21
- end
22
- end
23
-
5
+ RSpec.describe SolidusEasypost::ReturnAuthorization, vcr: { cassette_name: 'return_authorization' } do
24
6
  describe '#return_label' do
25
- subject { auth.return_label shipment.rates.first }
7
+ it 'returns a valid return label' do
8
+ return_authorization = create(:return_item).return_authorization
26
9
 
27
- let(:shipment) { auth.easypost_shipment }
28
-
29
- it 'is an EasyPost::PackageLabel object' do
30
- expect(subject).to be_a(EasyPost::PostageLabel)
31
- end
10
+ easypost_return_authorization = described_class.new(return_authorization)
11
+ selected_easypost_rate = easypost_return_authorization.easypost_shipment.rates.first
12
+ return_label = easypost_return_authorization.return_label(selected_easypost_rate)
32
13
 
33
- it 'has the correct fields' do
34
- expect(subject).to have_attributes(
35
- id: "pl_37f765f275dd46c2866d0515694a306c",
36
- object: "PostageLabel",
37
- created_at: "2019-11-12T08:36:50Z",
38
- updated_at: "2019-11-12T08:36:51Z",
39
- date_advance: 0,
40
- integrated_form: "none",
41
- label_date: "2019-11-12T08:36:50Z",
42
- label_resolution: 300,
43
- label_size: "4x6",
44
- label_type: "default",
45
- label_url: "https://easypost-files.s3-us-west-2.amazonaws.com/files/postage_label/20191112/ebe045d367a742dd8bf718e63a58c390.png",
46
- label_file_type: "image/png"
47
- )
14
+ expect(return_label).to have_attributes(object: 'PostageLabel')
48
15
  end
49
16
  end
50
17
  end
@@ -0,0 +1,57 @@
1
+ RSpec.describe Spree::Carton do
2
+ describe '.create' do
3
+ context 'when track_all_cartons is true' do
4
+ it 'tracks the package automatically' do
5
+ stub_easypost_config(track_all_cartons: true)
6
+ tracker = instance_double(EasyPost::Tracker, id: 'trk_test')
7
+ allow(EasyPost::Tracker).to receive(:create).and_return(tracker)
8
+
9
+ carton = create(:carton)
10
+
11
+ expect(carton.easy_post_tracker_id).to eq('trk_test')
12
+ end
13
+ end
14
+
15
+ context 'when track_all_cartons is false' do
16
+ it 'does not track all packages automatically' do
17
+ stub_easypost_config(track_all_cartons: false)
18
+
19
+ carton = create(:carton)
20
+
21
+ expect(carton.easy_post_tracker_id).to eq(nil)
22
+ end
23
+ end
24
+ end
25
+
26
+ describe '#easypost_tracker' do
27
+ context 'when a tracker was already created' do
28
+ it 'returns the existing tracker' do
29
+ carton = create(:carton, easy_post_tracker_id: 'trk_test')
30
+ tracker = instance_double(EasyPost::Tracker)
31
+ allow(EasyPost::Tracker).to receive(:retrieve).with('trk_test').and_return(tracker)
32
+
33
+ expect(carton.easypost_tracker).to eq(tracker)
34
+ end
35
+ end
36
+
37
+ context 'when a tracker was not created' do
38
+ it 'creates a new tracker' do
39
+ carton = create(:carton, tracking: 'TESTTRACKING', shipping_method: create(:shipping_method, carrier: 'FedEx'))
40
+ tracker = instance_double(EasyPost::Tracker, id: 'trk_test')
41
+ allow(EasyPost::Tracker).to receive(:create).with(tracking_code: 'TESTTRACKING', carrier: 'FedEx').and_return(tracker)
42
+
43
+ expect(carton.easypost_tracker).to eq(tracker)
44
+ end
45
+
46
+ it "sets the tracker's ID on the carton" do
47
+ carton = create(:carton)
48
+ tracker = instance_double(EasyPost::Tracker, id: 'trk_test')
49
+ allow(EasyPost::Tracker).to receive(:create).and_return(tracker)
50
+
51
+ carton.easypost_tracker
52
+
53
+ expect(carton.easy_post_tracker_id).to eq('trk_test')
54
+ end
55
+ end
56
+ end
57
+ end
@@ -2,78 +2,67 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- RSpec.describe Spree::Shipment, :vcr do
6
- let!(:shipment) { order.shipments.first }
7
- let!(:order) do
8
- create(:order_with_line_items, line_items_count: 1, ship_address: to) do |order|
9
- order.variants.each { |v| v.update! weight: 10 }
10
- end
11
- end
12
-
13
- let(:to) do
14
- Spree::Address.create(
15
- firstname: 'Newt',
16
- lastname: 'Scamander',
17
- address1: '200 19th St',
18
- city: 'Birmingham',
19
- state: Spree::State.first || create(:state),
20
- country: Spree::Country.first || create(:country),
21
- zipcode: 35_203,
22
- phone: '123456789'
23
- )
24
- end
25
-
26
- before do
27
- create_shipping_methods
28
- shipment.stock_location.update(
29
- address1: '2630 Cahaba Rd',
30
- city: 'Birmingham',
31
- state: Spree::State.first,
32
- country: Spree::Country.first,
33
- zipcode: 35_223,
34
- )
35
- end
36
-
37
- it "'buys' a shipping rate after transitioning to ship" do
38
- shipment.refresh_rates
39
- shipment.state = 'ready'
40
-
41
- shipment.ship!
42
- expect(shipment.tracking).to be_present
43
- end
44
-
5
+ RSpec.describe Spree::Shipment do
45
6
  describe '#easypost_shipment' do
46
- subject { shipment.easypost_shipment }
7
+ context 'when no shipping rate was selected' do
8
+ it 'returns nil' do
9
+ shipment = create(:shipment)
47
10
 
48
- shared_examples 'an easypost shipment' do
49
- it 'is an EasyPost::Shipment object' do
50
- expect(subject).to be_a(EasyPost::Shipment)
11
+ expect(shipment.easypost_shipment).to be_nil
51
12
  end
52
13
  end
53
14
 
54
- context 'when it is a new shipment' do
55
- it_behaves_like 'an easypost shipment'
15
+ context 'when a shipping rate was selected' do
16
+ it 'returns the shipment associated with the shipping rate' do
17
+ easypost_shipment = stub_easypost_shipment
18
+ shipment = create(
19
+ :shipment,
20
+ :with_easypost,
21
+ easypost_shipment_id: easypost_shipment.id,
22
+ )
56
23
 
57
- it 'calls the api' do
58
- expect(EasyPost::Shipment).to receive(:create).with(anything)
59
- subject
24
+ expect(shipment.easypost_shipment).to eq(easypost_shipment)
60
25
  end
61
26
  end
27
+ end
28
+
29
+ describe '#ship!' do
30
+ context 'when purchase_labels is true' do
31
+ it 'buys the selected rate' do
32
+ stub_easypost_config(purchase_labels: true)
33
+ easypost_shipment = stub_easypost_shipment
34
+ selected_easypost_rate = easypost_shipment.rates.first
62
35
 
63
- context 'when it is an existing shipment' do
64
- let(:new_shipment) { create :shipment }
36
+ shipment = create(
37
+ :shipment,
38
+ :with_easypost,
39
+ state: 'ready',
40
+ easypost_shipment_id: easypost_shipment.id,
41
+ easypost_rate_id: selected_easypost_rate.id,
42
+ )
43
+ shipment.ship!
65
44
 
66
- before do
67
- ep_id = shipment.easypost_shipment.id
68
- shipment.shipping_rates.first.update selected: true, easy_post_shipment_id: ep_id
69
- shipment.instance_variable_set('@ep_shipment', nil)
45
+ expect(easypost_shipment).to have_received(:buy).with(selected_easypost_rate)
70
46
  end
47
+ end
48
+
49
+ context 'when purchase_labels is false' do
50
+ it 'does not buy rates automatically' do
51
+ stub_easypost_config(purchase_labels: false)
52
+ easypost_shipment = stub_easypost_shipment
53
+ selected_easypost_rate = easypost_shipment.rates.first
54
+
55
+ shipment = create(
56
+ :shipment,
57
+ :with_easypost,
58
+ state: 'ready',
59
+ easypost_shipment_id: easypost_shipment.id,
60
+ easypost_rate_id: selected_easypost_rate.id,
61
+ )
71
62
 
72
- it_behaves_like 'an easypost shipment'
63
+ shipment.ship!
73
64
 
74
- it 'loads the existing shipment' do
75
- expect(EasyPost::Shipment).to receive(:retrieve).with(anything)
76
- subject
65
+ expect(easypost_shipment).not_to have_received(:buy)
77
66
  end
78
67
  end
79
68
  end
@@ -2,15 +2,22 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- RSpec.describe Spree::ShippingRate, :vcr do
6
- let(:rate) { described_class.new name: name }
7
- let(:name) { 'SuperAwesomeCool' }
8
-
5
+ RSpec.describe Spree::ShippingRate do
9
6
  describe '#name' do
10
- subject { rate.name }
7
+ context 'when the shipping rate has its own name' do
8
+ it 'returns the name on the shipping rate' do
9
+ shipping_rate = build_stubbed(:shipping_rate, name: 'USPS Express')
10
+
11
+ expect(shipping_rate.name).to eq('USPS Express')
12
+ end
13
+ end
14
+
15
+ context 'when the shipping rate does not have its own name' do
16
+ it 'returns the name on the shipping method' do
17
+ shipping_rate = build_stubbed(:shipping_rate, name: nil)
11
18
 
12
- it 'has the right name' do
13
- expect(subject).to eq(name)
19
+ expect(shipping_rate.name).to eq(shipping_rate.shipping_method.name)
20
+ end
14
21
  end
15
22
  end
16
23
  end
@@ -0,0 +1,17 @@
1
+ RSpec.describe SolidusEasypost::AddressBuilder do
2
+ describe '.from_address', vcr: { cassette_name: 'address_builder/from_address' } do
3
+ it 'builds an address with the correct attributes' do
4
+ address = described_class.from_address(build_stubbed(:address))
5
+
6
+ expect(address).to have_attributes(object: 'Address')
7
+ end
8
+ end
9
+
10
+ describe '.from_stock_location', vcr: { cassette_name: 'address_builder/from_stock_location' } do
11
+ it 'builds an address with the correct attributes' do
12
+ address = described_class.from_stock_location(create(:stock_location))
13
+
14
+ expect(address).to have_attributes(object: 'Address')
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe SolidusEasypost::Calculator::WeightDimensionCalculator do
4
+ describe '#compute' do
5
+ subject(:compute) { described_class.new.compute(resource) }
6
+
7
+ context 'when a wrong resource is passed' do
8
+ let(:resource) { create(:shipment) }
9
+
10
+ it 'raises an unknown partial resource error' do
11
+ expect { compute }.to raise_error(SolidusEasypost::Errors::UnknownPartialResourceError)
12
+ end
13
+ end
14
+
15
+ context 'when a Spree::Stock::Package is passed' do
16
+ let(:resource) { create(:shipment).to_package }
17
+
18
+ before { allow(SolidusEasypost::ParcelDimension).to receive(:new) }
19
+
20
+ it 'build a parcel dimension' do
21
+ compute
22
+
23
+ expect(SolidusEasypost::ParcelDimension).to have_received(:new).with({ weight: 10.to_f })
24
+ end
25
+ end
26
+
27
+ context 'when a SolidusEasypost::ReturnAuthorization is passed' do
28
+ let(:resource) { SolidusEasypost::ReturnAuthorization.new(create(:return_authorization)) }
29
+
30
+ before { allow(SolidusEasypost::ParcelDimension).to receive(:new) }
31
+
32
+ it 'build a parcel dimension' do
33
+ compute
34
+
35
+ expect(SolidusEasypost::ParcelDimension).to have_received(:new).with({ weight: 0.to_f })
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe SolidusEasypost::Estimator, vcr: { cassette_name: 'estimator' } do
6
+ describe '#shipping_rates' do
7
+ context 'when the shipping methods are available to users' do
8
+ it 'returns the shipping rates' do
9
+ stub_shipping_method_selector(available_to_users: true)
10
+
11
+ rates = described_class.new.shipping_rates(create(:shipment).to_package)
12
+
13
+ hash = {
14
+ "USPS Express" => 22.74,
15
+ "USPS First" => 3.82,
16
+ "USPS ParcelSelect" => 6.85,
17
+ "USPS Priority" => 6.95,
18
+ }
19
+
20
+ expect(rates.sort_by(&:name).map { |r| [r.name, r.cost] }.to_h).to eq(hash)
21
+ end
22
+ end
23
+
24
+ context 'when the shipping methods are not available to users' do
25
+ it 'returns no rates' do
26
+ stub_shipping_method_selector(available_to_users: false)
27
+
28
+ rates = described_class.new.shipping_rates(create(:shipment).to_package)
29
+
30
+ expect(rates).to be_empty
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def stub_shipping_method_selector(available_to_users:)
38
+ shipping_method_selector = Class.new.tap do |klass|
39
+ klass.define_method(:shipping_method_for) do |rate|
40
+ Spree::ShippingMethod.new(
41
+ name: "#{rate.carrier} #{rate.service}",
42
+ available_to_users: available_to_users,
43
+ )
44
+ end
45
+ end
46
+
47
+ stub_const('FakeShippingMethodSelector', shipping_method_selector)
48
+
49
+ allow(SolidusEasypost.configuration).to receive(:shipping_method_selector_class)
50
+ .and_return(FakeShippingMethodSelector)
51
+ end
52
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe SolidusEasypost::ParcelBuilder do
4
+ describe '.from_package', vcr: { cassette_name: 'parcel_builder/from_package' } do
5
+ let(:shipment) { create(:shipment) }
6
+ let(:package) { shipment.to_package }
7
+ let(:parcel_dimension_calculator) { instance_spy('SolidusEasypost.configuration.parcel_dimension_calculator_class') }
8
+ let(:parcel_dimension) { instance_spy('SolidusEasypost::ParcelDimension') }
9
+
10
+ before do
11
+ allow(SolidusEasypost.configuration.parcel_dimension_calculator_class)
12
+ .to receive(:new)
13
+ .and_return(parcel_dimension_calculator)
14
+
15
+ allow(parcel_dimension_calculator).to receive(:compute).and_return(parcel_dimension)
16
+ allow(parcel_dimension).to receive(:to_h).and_return(dimension_hash)
17
+ allow(EasyPost::Parcel).to receive(:create).and_call_original
18
+ end
19
+
20
+ context 'when there is only the weight set' do
21
+ let(:dimension_hash) { { weight: 10.to_f } }
22
+
23
+ it 'builds a parcel with the correct attributes' do
24
+ parcel = described_class.from_package(package)
25
+
26
+ expect(parcel_dimension_calculator)
27
+ .to have_received(:compute)
28
+ .with(package)
29
+
30
+ expect(EasyPost::Parcel)
31
+ .to have_received(:create)
32
+ .with({ weight: 10.to_f })
33
+
34
+ expect(parcel).to have_attributes(object: 'Parcel')
35
+ end
36
+ end
37
+
38
+ context 'when all the properties are set' do
39
+ let(:dimension_hash) do
40
+ { weight: 10.to_f, height: 2.to_f, width: 3.to_f, depth: 4.to_f }
41
+ end
42
+
43
+ it 'builds a parcel with the correct attributes' do
44
+ parcel = described_class.from_package(package)
45
+
46
+ expect(parcel_dimension_calculator)
47
+ .to have_received(:compute)
48
+ .with(package)
49
+
50
+ expect(EasyPost::Parcel)
51
+ .to have_received(:create)
52
+ .with({ weight: 10.to_f, height: 2.to_f, width: 3.to_f, depth: 4.to_f })
53
+
54
+ expect(parcel).to have_attributes(object: 'Parcel')
55
+ end
56
+ end
57
+ end
58
+
59
+ describe '.from_return_authorization', vcr: { cassette_name: 'parcel_builder/from_return_authorization' } do
60
+ let(:return_item) { create(:return_item) }
61
+ let(:return_authorization) { return_item.return_authorization }
62
+ let(:parcel_dimension_calculator) { instance_spy('SolidusEasypost.configuration.parcel_dimension_calculator_class') }
63
+ let(:parcel_dimension) { instance_spy('SolidusEasypost::ParcelDimension') }
64
+
65
+ before do
66
+ allow(SolidusEasypost.configuration.parcel_dimension_calculator_class)
67
+ .to receive(:new)
68
+ .and_return(parcel_dimension_calculator)
69
+
70
+ allow(parcel_dimension_calculator).to receive(:compute).and_return(parcel_dimension)
71
+ allow(parcel_dimension).to receive(:to_h).and_return(dimension_hash)
72
+ allow(EasyPost::Parcel).to receive(:create).and_call_original
73
+ end
74
+
75
+ context 'when there is only the weight set' do
76
+ let(:dimension_hash) { { weight: 10.to_f } }
77
+
78
+ it 'builds a parcel with the correct attributes' do
79
+ parcel = described_class.from_return_authorization(return_authorization)
80
+
81
+ expect(parcel_dimension_calculator)
82
+ .to have_received(:compute)
83
+ .with(return_authorization)
84
+
85
+ expect(EasyPost::Parcel)
86
+ .to have_received(:create)
87
+ .with({ weight: 10.to_f })
88
+
89
+ expect(parcel).to have_attributes(object: 'Parcel')
90
+ end
91
+ end
92
+
93
+ context 'when all the properties are set' do
94
+ let(:dimension_hash) do
95
+ { weight: 10.to_f, height: 2.to_f, width: 3.to_f, depth: 4.to_f }
96
+ end
97
+
98
+ it 'builds a parcel with the correct attributes' do
99
+ parcel = described_class.from_return_authorization(return_authorization)
100
+
101
+ expect(parcel_dimension_calculator)
102
+ .to have_received(:compute)
103
+ .with(return_authorization)
104
+
105
+ expect(EasyPost::Parcel)
106
+ .to have_received(:create)
107
+ .with({ weight: 10.to_f, height: 2.to_f, width: 3.to_f, depth: 4.to_f })
108
+
109
+ expect(parcel).to have_attributes(object: 'Parcel')
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe SolidusEasypost::ShipmentBuilder do
4
+ describe '.from_package', vcr: { cassette_name: 'shipment_builder/from_package' } do
5
+ it 'builds a shipment with the correct attributes' do
6
+ shipment = described_class.from_package(create(:shipment).to_package)
7
+
8
+ expect(shipment).to have_attributes(object: 'Shipment')
9
+ end
10
+ end
11
+
12
+ describe '.from_shipment', vcr: { cassette_name: 'shipment_builder/from_shipment' } do
13
+ it 'builds a shipment with the correct attributes' do
14
+ shipment = described_class.from_shipment(create(:shipment))
15
+
16
+ expect(shipment).to have_attributes(object: 'Shipment')
17
+ end
18
+ end
19
+
20
+ describe '.from_return_authorization', vcr: { cassette_name: 'shipment_builder/from_return_authorization' } do
21
+ it 'builds a shipment with the correct attributes' do
22
+ solidus_return_authorization = create(:return_item).return_authorization
23
+ shipment = described_class.from_return_authorization(SolidusEasypost::ReturnAuthorization.new(solidus_return_authorization))
24
+
25
+ expect(shipment).to have_attributes(object: 'Shipment')
26
+ end
27
+ end
28
+ end