mailerlite-ruby 1.0.6 → 2.1.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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +2 -0
  3. data/.github/workflows/main.yml +4 -4
  4. data/.github/workflows/publish_gem.yml +5 -5
  5. data/.gitignore +2 -1
  6. data/.rubocop.yml +1 -1
  7. data/CHANGELOG.md +21 -0
  8. data/README.md +583 -1
  9. data/flake.lock +27 -0
  10. data/flake.nix +25 -0
  11. data/lefthook.yml +14 -0
  12. data/lib/mailerlite/api_response.rb +24 -0
  13. data/lib/mailerlite/automations/automation_emails.rb +57 -0
  14. data/lib/mailerlite/automations/automation_reports.rb +114 -0
  15. data/lib/mailerlite/automations/automation_steps.rb +105 -0
  16. data/lib/mailerlite/automations/automation_templates.rb +34 -0
  17. data/lib/mailerlite/automations/automation_triggers.rb +62 -0
  18. data/lib/mailerlite/automations/automations.rb +177 -8
  19. data/lib/mailerlite/brand_styles/brand_styles.rb +24 -0
  20. data/lib/mailerlite/campaigns/campaigns.rb +33 -1
  21. data/lib/mailerlite/client.rb +15 -5
  22. data/lib/mailerlite/ecommerce/ecommerce.rb +316 -0
  23. data/lib/mailerlite/emails/emails.rb +306 -0
  24. data/lib/mailerlite/errors.rb +88 -0
  25. data/lib/mailerlite/files/files.rb +45 -0
  26. data/lib/mailerlite/groups/groups.rb +16 -8
  27. data/lib/mailerlite/params.rb +58 -0
  28. data/lib/mailerlite/query_params.rb +33 -0
  29. data/lib/mailerlite/request.rb +24 -0
  30. data/lib/mailerlite/segments/segments.rb +10 -10
  31. data/lib/mailerlite/subscribers/subscribers.rb +63 -8
  32. data/lib/mailerlite/version.rb +1 -1
  33. data/lib/mailerlite.rb +14 -0
  34. data/mailerlite-ruby.gemspec +8 -8
  35. data/spec/api_response_rspec.rb +140 -0
  36. data/spec/automation_emails_rspec.rb +40 -0
  37. data/spec/automation_reports_rspec.rb +92 -0
  38. data/spec/automation_steps_rspec.rb +76 -0
  39. data/spec/automation_templates_rspec.rb +24 -0
  40. data/spec/automation_triggers_rspec.rb +48 -0
  41. data/spec/automations_rspec.rb +143 -0
  42. data/spec/brand_styles_rspec.rb +49 -0
  43. data/spec/campaigns_rspec.rb +42 -0
  44. data/spec/ecommerce_rspec.rb +141 -0
  45. data/spec/emails_rspec.rb +692 -0
  46. data/spec/files_rspec.rb +90 -0
  47. data/spec/fixtures/brand_styles.json +50 -0
  48. data/spec/fixtures/campaign_simple_editor.json +24 -0
  49. data/spec/fixtures/email.json +81 -0
  50. data/spec/fixtures/email_block.json +12 -0
  51. data/spec/fixtures/email_block_created.json +12 -0
  52. data/spec/fixtures/email_block_created_rejected.json +15 -0
  53. data/spec/fixtures/email_block_moved.json +12 -0
  54. data/spec/fixtures/email_block_updated.json +9 -0
  55. data/spec/fixtures/email_block_updated_rejected.json +13 -0
  56. data/spec/fixtures/email_block_validation_invalid.json +5 -0
  57. data/spec/fixtures/email_etag.json +1 -0
  58. data/spec/fixtures/email_html.json +1 -0
  59. data/spec/fixtures/email_settings.json +111 -0
  60. data/spec/fixtures/email_settings_validation.json +9 -0
  61. data/spec/fixtures/email_template.json +1 -0
  62. data/spec/fixtures/email_version.json +1 -0
  63. data/spec/fixtures/email_versions.json +26 -0
  64. data/spec/fixtures/files.json +44 -0
  65. data/spec/groups_rspec.rb +21 -0
  66. data/spec/params_rspec.rb +126 -0
  67. data/spec/request_rspec.rb +139 -0
  68. data/spec/segments_rspec.rb +39 -0
  69. data/spec/spec_helper.rb +1 -0
  70. data/spec/subscribers_rspec.rb +57 -0
  71. data/spec/support/json_fixture.rb +27 -0
  72. data/spec/support/request_stub.rb +25 -0
  73. metadata +69 -22
  74. /data/{renovate.json → .github/renovate.json} +0 -0
@@ -60,4 +60,147 @@ RSpec.describe MailerLite::Automations do
60
60
  end
61
61
  end
62
62
  end
63
+
64
+ describe '#get with every filter' do
65
+ it 'sends the documented query params' do
66
+ stub = stub_api(:get, '/automations', query: {
67
+ 'filter[status]' => 'true', 'filter[name]' => 'Welcome', 'filter[group]' => '1', 'filter[folder]' => '2',
68
+ 'filter[enabled]' => '1', 'filter[automation_state]' => 'workflows', 'filter[drafts_state]' => 'complete_workflow',
69
+ 'limit' => '10', 'page' => '2', 'sort' => '-created_at', 'with_steps' => '0'
70
+ })
71
+
72
+ automations.get(
73
+ filter: { status: true, name: 'Welcome', group: 1, folder: 2, enabled: 1, automation_state: 'workflows', drafts_state: 'complete_workflow' },
74
+ limit: 10, page: 2, sort: '-created_at', with_steps: false
75
+ )
76
+
77
+ expect(stub).to have_been_requested
78
+ end
79
+ end
80
+
81
+ describe '#create' do
82
+ it 'sends POST /automations' do
83
+ stub = stub_api(:post, '/automations', body: { 'name' => 'Welcome' })
84
+ automations.create(name: 'Welcome')
85
+ expect(stub).to have_been_requested
86
+ end
87
+ end
88
+
89
+ describe '#update' do
90
+ it 'sends PUT /automations/{automation}' do
91
+ stub = stub_api(:put, '/automations/1', body: { 'name' => 'Welcome', 'trigger_data' => { 'group_ids' => ['2'] }, 'triggers' => [{ 'type' => 'manual' }] })
92
+ automations.update(automation_id: 1, name: 'Welcome', trigger_data: { 'group_ids' => ['2'] }, triggers: [{ 'type' => 'manual' }])
93
+ expect(stub).to have_been_requested
94
+ end
95
+ end
96
+
97
+ describe '#delete' do
98
+ it 'sends DELETE /automations/{automation}' do
99
+ stub = stub_api(:delete, '/automations/1')
100
+ automations.delete(1)
101
+ expect(stub).to have_been_requested
102
+ end
103
+ end
104
+
105
+ describe '#copy' do
106
+ it 'sends POST /automations/{automation}/copy' do
107
+ stub = stub_api(:post, '/automations/1/copy')
108
+ automations.copy(1)
109
+ expect(stub).to have_been_requested
110
+ end
111
+ end
112
+
113
+ describe '#enable' do
114
+ it 'sends POST /automations/{automation}/enable' do
115
+ stub = stub_api(:post, '/automations/1/enable')
116
+ automations.enable(1)
117
+ expect(stub).to have_been_requested
118
+ end
119
+ end
120
+
121
+ describe '#disable' do
122
+ it 'sends POST /automations/{automation}/disable' do
123
+ stub = stub_api(:post, '/automations/1/disable')
124
+ automations.disable(1)
125
+ expect(stub).to have_been_requested
126
+ end
127
+ end
128
+
129
+ describe '#test' do
130
+ it 'sends POST /automations/{automation}/test' do
131
+ stub = stub_api(:post, '/automations/1/test', body: { 'email' => 'a@b.com' })
132
+ automations.test(automation_id: 1, email: 'a@b.com')
133
+ expect(stub).to have_been_requested
134
+ end
135
+ end
136
+
137
+ describe '#get_subscriber_activity search' do
138
+ it 'sends filter[search] and sort' do
139
+ stub = stub_api(:get, '/automations/1/activity', query: { 'filter[status]' => 'completed', 'filter[search]' => 'a@b.com', 'sort' => '-id' })
140
+ automations.get_subscriber_activity(automation_id: 1, filter: { status: 'completed', search: 'a@b.com' }, sort: '-id')
141
+ expect(stub).to have_been_requested
142
+ end
143
+
144
+ it 'sends the old filter[:keyword] as filter[search]' do
145
+ stub = stub_api(:get, '/automations/1/activity', query: { 'filter[status]' => 'completed', 'filter[search]' => 'a@b.com' })
146
+ automations.get_subscriber_activity(automation_id: 1, filter: { status: 'completed', keyword: 'a@b.com' })
147
+ expect(stub).to have_been_requested
148
+ end
149
+ end
150
+
151
+ describe '#get_subscriber_activity with_subscribers' do
152
+ it 'sends with_subscribers=1 when true' do
153
+ stub = stub_api(:get, '/automations/1/activity', query: { 'filter[status]' => 'active', 'with_subscribers' => '1' })
154
+ automations.get_subscriber_activity(automation_id: 1, filter: { status: 'active' }, with_subscribers: true)
155
+ expect(stub).to have_been_requested
156
+ end
157
+
158
+ it 'omits with_subscribers when false' do
159
+ stub = stub_api(:get, '/automations/1/activity', query: { 'filter[status]' => 'active' })
160
+ automations.get_subscriber_activity(automation_id: 1, filter: { status: 'active' }, with_subscribers: false)
161
+ expect(stub).to have_been_requested
162
+ end
163
+ end
164
+
165
+ describe '#add_subscribers' do
166
+ it 'sends POST /automations/{automation}/add-subscribers' do
167
+ stub = stub_api(:post, '/automations/1/add-subscribers', body: { 'step_id' => 2, 'emails' => ['a@b.com'], 'groups' => ['3'] })
168
+ automations.add_subscribers(automation_id: 1, step_id: 2, emails: ['a@b.com'], groups: ['3'])
169
+ expect(stub).to have_been_requested
170
+ end
171
+ end
172
+
173
+ describe '#readd' do
174
+ it 'sends POST /automations/{automation}/readd' do
175
+ stub = stub_api(:post, '/automations/1/readd', body: {
176
+ 'readd_subscribers_to' => 'selected_step', 'selected_step_id' => '2', 'filter' => { 'status' => 'completed', 'id' => ['5'] }
177
+ })
178
+ automations.readd(automation_id: 1, readd_subscribers_to: 'selected_step', selected_step_id: '2', filter: { status: 'completed', id: ['5'] })
179
+ expect(stub).to have_been_requested
180
+ end
181
+ end
182
+
183
+ describe '#add_activity_to_group' do
184
+ it 'sends POST /automations/{automation}/activity/add-to-group' do
185
+ stub = stub_api(:post, '/automations/1/activity/add-to-group', body: { 'destination_groups' => ['3'], 'filter' => { 'status' => 'active' } })
186
+ automations.add_activity_to_group(automation_id: 1, destination_groups: ['3'], filter: { status: 'active' })
187
+ expect(stub).to have_been_requested
188
+ end
189
+ end
190
+
191
+ describe '#cancel_activity' do
192
+ it 'sends POST /automations/{automation}/activity/cancel-automation' do
193
+ stub = stub_api(:post, '/automations/1/activity/cancel-automation', body: { 'filter' => { 'status' => 'active', 'next_step' => 4 } })
194
+ automations.cancel_activity(automation_id: 1, filter: { status: 'active', next_step: 4 })
195
+ expect(stub).to have_been_requested
196
+ end
197
+ end
198
+
199
+ describe '#skip_activity_delay' do
200
+ it 'sends POST /automations/{automation}/activity/skip-delay' do
201
+ stub = stub_api(:post, '/automations/1/activity/skip-delay', body: { 'filter' => { 'status' => 'active', 'search' => 'a@b.com' } })
202
+ automations.skip_activity_delay(automation_id: 1, filter: { status: 'active', search: 'a@b.com' })
203
+ expect(stub).to have_been_requested
204
+ end
205
+ end
63
206
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'webmock/rspec'
5
+ require 'json'
6
+ require_relative 'support/json_fixture'
7
+
8
+ RSpec.describe MailerLite::BrandStyles do
9
+ include_context 'with the dummy API token'
10
+
11
+ let(:brand_styles) { described_class.new(client: MailerLite::Client.new) }
12
+ let(:url) { "#{MAILERLITE_API_URL}/brand-styles" }
13
+
14
+ def stub_brand_styles(response)
15
+ stub_request(:get, url)
16
+ .with(headers: { 'Authorization' => 'Bearer dummy_token' }) { |req| req.body == '' && !req.headers.key?('Idempotency-Key') }
17
+ .to_return(status: 200, body: response.to_json, headers: { 'Content-Type' => 'application/json' })
18
+ end
19
+
20
+ describe '#get' do
21
+ it 'gets the palettes' do
22
+ stub = stub_brand_styles(load_fixture('brand_styles'))
23
+
24
+ response = brand_styles.get
25
+
26
+ expect(stub).to have_been_requested
27
+ expect(response).to be_a(HTTP::Response)
28
+ expect(response.api_error).to be_nil
29
+ data = JSON.parse(response.body.to_s)['data']
30
+ expect(data['brand']['identifier']).to eq 'account'
31
+ expect(data['brand']['primary_color']).to eq '#2cb191'
32
+ expect(data['default']['identifier']).to eq 'default-brand'
33
+ expect(data['presets'].map { |preset| preset['identifier'] }).to eq %w[preset-business-casual preset-retro]
34
+ expect(data['presets'][1]['font_family']).to eq 'Courier New, monospace'
35
+ end
36
+
37
+ it 'returns a null brand when the account has none' do
38
+ body = load_fixture('brand_styles')
39
+ body['data']['brand'] = nil
40
+ stub_brand_styles(body)
41
+
42
+ data = JSON.parse(brand_styles.get.body.to_s)['data']
43
+
44
+ expect(data).to have_key('brand')
45
+ expect(data['brand']).to be_nil
46
+ expect(data['default']['identifier']).to eq 'default-brand'
47
+ end
48
+ end
49
+ end
@@ -4,6 +4,8 @@
4
4
  require 'spec_helper'
5
5
  require 'vcr'
6
6
  require 'json'
7
+ require 'webmock/rspec'
8
+ require_relative 'support/json_fixture'
7
9
 
8
10
  # require "webmock/rspec"
9
11
  # Import the `Campaigns` class
@@ -152,4 +154,44 @@ RSpec.describe MailerLite::Campaigns do
152
154
  end
153
155
  end
154
156
  end
157
+
158
+ describe '#create with the simple editor' do
159
+ include_context 'with the dummy API token'
160
+
161
+ let(:url) { "#{MAILERLITE_API_URL}/campaigns" }
162
+
163
+ def stub_create(body)
164
+ stub_request(:post, url)
165
+ .with(headers: { 'Authorization' => 'Bearer dummy_token' }) { |req| req.body == body }
166
+ .to_return(status: 201, body: load_fixture('campaign_simple_editor').to_json, headers: { 'Content-Type' => 'application/json' })
167
+ end
168
+
169
+ it 'has the value of the simple editor' do
170
+ expect(MailerLite::Campaigns::EDITOR_SIMPLE).to eq 'simple'
171
+ end
172
+
173
+ it 'sends editor, template_id and reply_to, and no content' do
174
+ stub = stub_create('{"name":"Simple editor campaign","type":"regular","emails":[{"subject":"Hello","from_name":"Sender",' \
175
+ '"from":"sender@example.com","reply_to":"reply@example.com","editor":"simple","template_id":789}]}')
176
+
177
+ response = campaigns.create(name: 'Simple editor campaign', type: 'regular',
178
+ emails: [{ subject: 'Hello', from_name: 'Sender', from: 'sender@example.com',
179
+ reply_to: 'reply@example.com', editor: MailerLite::Campaigns::EDITOR_SIMPLE, template_id: 789 }])
180
+
181
+ expect(stub).to have_been_requested
182
+ email = JSON.parse(response.body.to_s)['data']['emails'][0]
183
+ expect(email['id']).to eq '123'
184
+ expect(email['type']).to eq 'builder_inline_email'
185
+ end
186
+
187
+ it 'sends the same body as before without editor' do
188
+ stub = stub_create('{"name":"HTML campaign","type":"regular","emails":[{"subject":"Hello","from_name":"Sender",' \
189
+ '"from":"sender@example.com","content":"<p>Hi</p>"}]}')
190
+
191
+ campaigns.create(name: 'HTML campaign', type: 'regular',
192
+ emails: [{ subject: 'Hello', from_name: 'Sender', from: 'sender@example.com', content: '<p>Hi</p>' }])
193
+
194
+ expect(stub).to have_been_requested
195
+ end
196
+ end
155
197
  end
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'webmock/rspec'
5
+ require 'json'
6
+
7
+ RSpec.describe MailerLite::Ecommerce do
8
+ let(:client) { MailerLite::Client.new }
9
+ let(:api) { MAILERLITE_API_URL }
10
+ let(:shop) { "#{MAILERLITE_API_URL}/ecommerce/shops/1" }
11
+ let(:variant) { { name: 'Large', attributes: { size: 'L' } } }
12
+
13
+ def expect_request(method, url, body: nil)
14
+ stub = stub_request(method, url)
15
+ stub = stub.with(body: body.to_json) if body
16
+ stub.to_return(status: 200, body: '{}')
17
+ yield
18
+ expect(stub).to have_been_requested
19
+ end
20
+
21
+ it 'calls the shop endpoints' do
22
+ shops = MailerLite::Ecommerce::Shops.new(client: client)
23
+
24
+ expect_request(:get, "#{api}/ecommerce/shops?limit=10") { shops.list(limit: 10) }
25
+ expect_request(:get, shop) { shops.get('1') }
26
+ expect_request(:post, "#{api}/ecommerce/shops", body: { name: 'Shop' }) { shops.create({ name: 'Shop' }) }
27
+ expect_request(:put, shop, body: { name: 'New' }) { shops.update(shop_id: '1', params: { name: 'New' }) }
28
+ expect_request(:delete, shop) { shops.delete('1') }
29
+ expect_request(:get, "#{shop}/reports/stats?start_date=2026-10-01&end_date=2026-10-31") do
30
+ shops.stats(shop_id: '1', start_date: '2026-10-01', end_date: '2026-10-31')
31
+ end
32
+ end
33
+
34
+ it 'calls the product and variant endpoints' do
35
+ products = MailerLite::Ecommerce::Products.new(client: client)
36
+ variants = MailerLite::Ecommerce::ProductVariants.new(client: client)
37
+
38
+ expect_request(:get, "#{shop}/products?filter%5Benabled%5D=true&sort=-name") do
39
+ products.list(shop_id: '1', filter: { enabled: true }, sort: '-name')
40
+ end
41
+ expect_request(:get, "#{shop}/products/7") { products.get(shop_id: '1', product_id: '7') }
42
+ expect_request(:post, "#{shop}/products", body: { name: 'Hat' }) { products.create(shop_id: '1', params: { name: 'Hat' }) }
43
+ expect_request(:put, "#{shop}/products/7", body: { name: 'Cap' }) { products.update(shop_id: '1', product_id: '7', params: { name: 'Cap' }) }
44
+ expect_request(:delete, "#{shop}/products/7") { products.delete(shop_id: '1', product_id: '7') }
45
+ expect_request(:put, "#{shop}/products/7/change-status", body: { status: false }) do
46
+ products.change_status(shop_id: '1', product_id: '7', enabled: false)
47
+ end
48
+ expect_request(:put, "#{shop}/products/7/categories/multiple", body: { categories: ['5'], replace: true }) do
49
+ products.assign_categories(shop_id: '1', product_id: '7', category_ids: ['5'], replace: true)
50
+ end
51
+
52
+ expect_request(:get, "#{shop}/products/7/variants") { variants.list(shop_id: '1', product_id: '7') }
53
+ expect_request(:post, "#{shop}/products/7/variants", body: [variant]) { variants.sync(shop_id: '1', product_id: '7', variants: [variant]) }
54
+ expect_request(:put, "#{shop}/products/7/variants/3", body: variant) do
55
+ variants.update(shop_id: '1', product_id: '7', variant_id: '3', params: variant)
56
+ end
57
+ expect_request(:delete, "#{shop}/products/7/variants/3") { variants.delete(shop_id: '1', product_id: '7', variant_id: '3') }
58
+ end
59
+
60
+ it 'calls the category endpoints' do
61
+ categories = MailerLite::Ecommerce::Categories.new(client: client)
62
+
63
+ expect_request(:get, "#{shop}/categories") { categories.list(shop_id: '1') }
64
+ expect_request(:get, "#{shop}/categories/5") { categories.get(shop_id: '1', category_id: '5') }
65
+ expect_request(:post, "#{shop}/categories", body: { name: 'Hats' }) { categories.create(shop_id: '1', params: { name: 'Hats' }) }
66
+ expect_request(:put, "#{shop}/categories/5", body: { name: 'Caps' }) { categories.update(shop_id: '1', category_id: '5', params: { name: 'Caps' }) }
67
+ expect_request(:delete, "#{shop}/categories/5") { categories.delete(shop_id: '1', category_id: '5') }
68
+ expect_request(:get, "#{shop}/categories/5/products") { categories.products(shop_id: '1', category_id: '5') }
69
+ expect_request(:put, "#{shop}/categories/5/products/7") { categories.assign_product(shop_id: '1', category_id: '5', product_id: '7') }
70
+ expect_request(:delete, "#{shop}/categories/5/products/7") { categories.unassign_product(shop_id: '1', category_id: '5', product_id: '7') }
71
+ end
72
+
73
+ it 'calls the customer, order and cart endpoints' do
74
+ customers = MailerLite::Ecommerce::Customers.new(client: client)
75
+ orders = MailerLite::Ecommerce::Orders.new(client: client)
76
+ carts = MailerLite::Ecommerce::Carts.new(client: client)
77
+ items = MailerLite::Ecommerce::CartItems.new(client: client)
78
+
79
+ expect_request(:get, "#{shop}/customers?filter%5Bemail%5D=a%40b.test") { customers.list(shop_id: '1', filter: { email: 'a@b.test' }) }
80
+ expect_request(:get, "#{shop}/customers/3") { customers.get(shop_id: '1', customer_id: '3') }
81
+ expect_request(:post, "#{shop}/customers", body: { email: 'a@b.test' }) { customers.create(shop_id: '1', params: { email: 'a@b.test' }) }
82
+ expect_request(:put, "#{shop}/customers/3", body: { name: 'Jane' }) { customers.update(shop_id: '1', customer_id: '3', params: { name: 'Jane' }) }
83
+ expect_request(:delete, "#{shop}/customers/3") { customers.delete(shop_id: '1', customer_id: '3') }
84
+
85
+ expect_request(:get, "#{shop}/orders?filter%5Battributed%5D=true&filter%5Bemail_id%5D=55") do
86
+ orders.list(shop_id: '1', filter: { attributed: true, email_id: '55' })
87
+ end
88
+ expect_request(:get, "#{shop}/orders/9") { orders.get(shop_id: '1', order_id: '9') }
89
+ expect_request(:put, "#{shop}/orders/9", body: { status: 'complete' }) { orders.update(shop_id: '1', order_id: '9', params: { status: 'complete' }) }
90
+ expect_request(:delete, "#{shop}/orders/9") { orders.delete(shop_id: '1', order_id: '9') }
91
+
92
+ expect_request(:get, "#{shop}/carts") { carts.list(shop_id: '1') }
93
+ expect_request(:get, "#{shop}/carts/4") { carts.get(shop_id: '1', cart_id: '4') }
94
+ expect_request(:put, "#{shop}/carts/4", body: { cart_total: 10 }) { carts.update(shop_id: '1', cart_id: '4', params: { cart_total: 10 }) }
95
+ expect_request(:get, "#{shop}/carts/4/items") { items.list(shop_id: '1', cart_id: '4') }
96
+ expect_request(:get, "#{shop}/carts/4/items/8") { items.get(shop_id: '1', cart_id: '4', item_id: '8') }
97
+ expect_request(:post, "#{shop}/carts/4/items", body: { ecommerce_product_id: '7' }) do
98
+ items.create(shop_id: '1', cart_id: '4', params: { ecommerce_product_id: '7' })
99
+ end
100
+ expect_request(:put, "#{shop}/carts/4/items/8", body: { quantity: 2 }) { items.update(shop_id: '1', cart_id: '4', item_id: '8', params: { quantity: 2 }) }
101
+ expect_request(:delete, "#{shop}/carts/4/items/8") { items.delete(shop_id: '1', cart_id: '4', item_id: '8') }
102
+ end
103
+
104
+ it 'creates an order with its customer and cart' do
105
+ order = { customer: { email: 'a@b.test' }, cart: { items: [{ ecommerce_product_id: '7' }] } }
106
+
107
+ expect_request(:post, "#{shop}/orders", body: order) do
108
+ MailerLite::Ecommerce::Orders.new(client: client).create(shop_id: '1', params: order)
109
+ end
110
+ end
111
+
112
+ it 'calls the import and settings endpoints' do
113
+ imports = MailerLite::Ecommerce::Imports.new(client: client)
114
+
115
+ expect_request(:post, "#{shop}/products/import", body: [{ name: 'Hat' }]) { imports.products(shop_id: '1', products: [{ name: 'Hat' }]) }
116
+ expect_request(:post, "#{shop}/categories/import", body: [{ name: 'Hats' }]) { imports.categories(shop_id: '1', categories: [{ name: 'Hats' }]) }
117
+ expect_request(:post, "#{shop}/customers/import", body: [{ email: 'a@b.test' }]) { imports.customers(shop_id: '1', customers: [{ email: 'a@b.test' }]) }
118
+ order = { customer: { email: 'a@b.test' }, cart: { items: [{ ecommerce_product_id: '7' }] } }
119
+ expect_request(:post, "#{shop}/orders/import", body: { orders: [order] }) { imports.orders(shop_id: '1', orders: [order]) }
120
+ expect_request(:get, "#{api}/ecommerce/settings") { MailerLite::Ecommerce::Settings.new(client: client).get }
121
+ end
122
+
123
+ it 'calls the revenue report endpoints' do
124
+ campaigns = MailerLite::Campaigns.new(client: client)
125
+ subscribers = MailerLite::Subscribers.new(client: client)
126
+ automations = MailerLite::Automations.new(client: client)
127
+
128
+ expect_request(:get, "#{api}/campaigns/2/reports/ecommerce?shop_id=1") { campaigns.ecommerce_stats(campaign_id: '2', shop_id: '1') }
129
+ expect_request(:get, "#{api}/campaigns/2/reports/ecommerce/orders?shop_id=1&page=2") { campaigns.ecommerce_orders(campaign_id: '2', shop_id: '1', page: 2) }
130
+ expect_request(:get, "#{api}/subscribers/6/reports/ecommerce?shop_id=1") { subscribers.ecommerce_stats(subscriber_id: '6', shop_id: '1') }
131
+ expect_request(:get, "#{api}/subscribers/6/reports/ecommerce/orders?shop_id=1") { subscribers.ecommerce_orders(subscriber_id: '6', shop_id: '1') }
132
+ expect_request(:get, "#{api}/automations/8/monthly-totals/ecommerce") { automations.ecommerce_stats(automation_id: '8') }
133
+ expect_request(:get, "#{api}/automations/8/emails/9/ecommerce-overview-stats?shop_id=1") do
134
+ automations.email_ecommerce_stats(automation_id: '8', email_id: '9', shop_id: '1')
135
+ end
136
+ expect_request(:get, "#{api}/automations/8/order-activity?time_frame=all_time") { automations.ecommerce_orders(automation_id: '8', time_frame: 'all_time') }
137
+ expect_request(:get, "#{api}/automations/8/order-activity?time_frame=custom&time_frame_start=2026-10-01&time_frame_end=2026-10-31&ecommerce_shop_id=1&page=2&limit=5") do
138
+ automations.ecommerce_orders(automation_id: '8', time_frame: 'custom', time_frame_start: '2026-10-01', time_frame_end: '2026-10-31', shop_id: '1', page: 2, limit: 5)
139
+ end
140
+ end
141
+ end