solidus_easypost 2.0.0 → 3.0.0

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 (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