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
@@ -0,0 +1,90 @@
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::Files do
9
+ include_context 'with the dummy API token'
10
+
11
+ let(:files) { described_class.new(client: MailerLite::Client.new) }
12
+ let(:url) { "#{MAILERLITE_API_URL}/files" }
13
+
14
+ def stub_files(query: nil, status: 200, response: load_fixture('files'))
15
+ stub = stub_request(:get, url).with(headers: { 'Authorization' => 'Bearer dummy_token' }) { |req| req.body == '' }
16
+ stub = stub.with(query: query) if query
17
+ stub.to_return(status: status, body: response.to_json, headers: { 'Content-Type' => 'application/json' })
18
+ end
19
+
20
+ # WebMock normalizes the URI (for example `+` becomes `%20`), so keep the raw URI that http.rb sends.
21
+ def capture_raw_uris
22
+ uris = []
23
+ allow_any_instance_of(HTTP::Client).to receive(:perform).and_wrap_original do |method, request, options|
24
+ uris << request.uri.to_s
25
+ method.call(request, options)
26
+ end
27
+ uris
28
+ end
29
+
30
+ it 'has the constants of the API' do
31
+ expect([described_class::FileType::IMAGE, described_class::FileType::PDF, described_class::FileType::EPUB]).to eq %w[image pdf epub]
32
+ sorts = described_class::FileSort
33
+ expect([sorts::CREATED_AT, sorts::CREATED_AT_DESC, sorts::NAME, sorts::NAME_DESC, sorts::SIZE, sorts::SIZE_DESC])
34
+ .to eq %w[created_at -created_at name -name size -size]
35
+ end
36
+
37
+ describe '#list' do
38
+ it 'sends no query without arguments' do
39
+ uris = capture_raw_uris
40
+ stub = stub_files
41
+
42
+ response = files.list
43
+
44
+ expect(stub).to have_been_requested
45
+ expect(uris).to eq [url]
46
+ data = JSON.parse(response.body.to_s)
47
+ expect(data['data'].map { |file| file['name'] }).to eq ['summer sale banner.png', 'summer sale terms.pdf']
48
+ expect(data['data'][0]['width']).to eq 1200
49
+ expect(data['data'][1]).to have_key('width')
50
+ expect(data['data'][1]['width']).to be_nil
51
+ expect(data['data'][1]['height']).to be_nil
52
+ expect(data['links']).to have_key('next')
53
+ expect(data['links']['next']).to be_nil
54
+ end
55
+
56
+ it 'sends the filter, the sort and the pages, and encodes a space as +' do
57
+ uris = capture_raw_uris
58
+ stub = stub_files(query: { 'filter[name]' => 'summer sale', 'filter[type]' => 'image', 'sort' => '-size', 'page' => '1', 'limit' => '25' })
59
+
60
+ files.list(filter: { name: 'summer sale', type: described_class::FileType::IMAGE }, sort: described_class::FileSort::SIZE_DESC, page: 1, limit: 25)
61
+
62
+ expect(stub).to have_been_requested
63
+ expect(uris).to eq ["#{url}?filter%5Bname%5D=summer+sale&filter%5Btype%5D=image&sort=-size&page=1&limit=25"]
64
+ end
65
+
66
+ it 'drops a nil filter value and keeps an empty one' do
67
+ uris = capture_raw_uris
68
+ stub_files(query: { 'filter[type]' => '' })
69
+
70
+ files.list(filter: { name: nil, type: '' })
71
+
72
+ expect(uris).to eq ["#{url}?filter%5Btype%5D="]
73
+ end
74
+
75
+ it 'returns the 422 of a file type that is not valid' do
76
+ stub_files(query: { 'filter[type]' => 'video' }, status: 422,
77
+ response: { message: 'The selected filter.type is invalid.', errors: { 'filter.type' => ['The selected filter.type is invalid.'] } })
78
+
79
+ response = files.list(filter: { type: 'video' })
80
+
81
+ expect(response.code).to eq 422
82
+ expect(response.api_error.errors).to eq('filter.type' => ['The selected filter.type is invalid.'])
83
+ end
84
+
85
+ it 'checks the filter and sends no request' do
86
+ expect { files.list(filter: 'image') }.to raise_error(TypeError, /`filter`/)
87
+ expect(a_request(:any, /.*/)).not_to have_been_made
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,50 @@
1
+ {
2
+ "data": {
3
+ "brand": {
4
+ "identifier": "account",
5
+ "name": "Brand styles",
6
+ "font_family": "Inter, sans-serif",
7
+ "primary_color": "#2cb191",
8
+ "secondary_color": "#1f2937",
9
+ "heading_color": "#111827",
10
+ "text_color": "#374151",
11
+ "border_color": "#e5e7eb",
12
+ "background_color": "#ffffff"
13
+ },
14
+ "default": {
15
+ "identifier": "default-brand",
16
+ "name": "Default",
17
+ "font_family": "Inter, sans-serif",
18
+ "primary_color": "#09c269",
19
+ "secondary_color": "#000000",
20
+ "heading_color": "#000000",
21
+ "text_color": "#000000",
22
+ "border_color": "#e5e7eb",
23
+ "background_color": "#ffffff"
24
+ },
25
+ "presets": [
26
+ {
27
+ "identifier": "preset-business-casual",
28
+ "name": "Business casual",
29
+ "font_family": "Georgia, serif",
30
+ "primary_color": "#1d4ed8",
31
+ "secondary_color": "#1e3a8a",
32
+ "heading_color": "#0f172a",
33
+ "text_color": "#334155",
34
+ "border_color": "#cbd5e1",
35
+ "background_color": "#f8fafc"
36
+ },
37
+ {
38
+ "identifier": "preset-retro",
39
+ "name": "Retro",
40
+ "font_family": "Courier New, monospace",
41
+ "primary_color": "#d97706",
42
+ "secondary_color": "#92400e",
43
+ "heading_color": "#451a03",
44
+ "text_color": "#78350f",
45
+ "border_color": "#fde68a",
46
+ "background_color": "#fffbeb"
47
+ }
48
+ ]
49
+ }
50
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "data": {
3
+ "id": "456",
4
+ "account_id": "1",
5
+ "name": "Simple editor campaign",
6
+ "type": "regular",
7
+ "status": "draft",
8
+ "default_email_id": "123",
9
+ "emails": [
10
+ {
11
+ "id": "123",
12
+ "account_id": "1",
13
+ "emailable_id": "456",
14
+ "emailable_type": "campaigns",
15
+ "type": "builder_inline_email",
16
+ "from": "sender@example.com",
17
+ "from_name": "Sender",
18
+ "reply_to": "reply@example.com",
19
+ "subject": "Hello",
20
+ "is_designed": true
21
+ }
22
+ ]
23
+ }
24
+ }
@@ -0,0 +1,81 @@
1
+ {
2
+ "data": {
3
+ "id": "123",
4
+ "type": "builder_inline_email",
5
+ "emailable_type": "campaigns",
6
+ "emailable_id": "456",
7
+ "settings": {
8
+ "brand": {
9
+ "mode": "account",
10
+ "font_family": "Inter, sans-serif",
11
+ "background_color": "#ffffff",
12
+ "text_color": "#000000",
13
+ "border_color": "#e5e7eb"
14
+ },
15
+ "layout": {"width_mode": "default", "alignment": "center"},
16
+ "typography": {
17
+ "link": {"color": "#2cb191"},
18
+ "heading": {
19
+ "font_family": "Inter, sans-serif",
20
+ "font_weight": "700",
21
+ "line_height": "150%",
22
+ "letter_spacing": "0",
23
+ "h1": {"color": "#000000", "size": "32"},
24
+ "h2": {"color": "#000000", "size": "24"},
25
+ "h3": {"color": "#000000", "size": "20"}
26
+ },
27
+ "paragraph": {
28
+ "font_family": "Inter, sans-serif",
29
+ "font_weight": "400",
30
+ "line_height": "150%",
31
+ "letter_spacing": "0",
32
+ "p1": {"color": "#000000", "size": "16"}
33
+ }
34
+ },
35
+ "button": {
36
+ "font_family": "Inter, sans-serif",
37
+ "font_weight": "600",
38
+ "letter_spacing": "0",
39
+ "background_color": "#2cb191",
40
+ "text_color": "#ffffff",
41
+ "border_radius": "4"
42
+ },
43
+ "footer": {
44
+ "show_preferences": false,
45
+ "company_name": {
46
+ "editor_content": {
47
+ "content": [{"type": "text", "text": "Acme", "marks": [{"type": "bold"}]}]
48
+ }
49
+ },
50
+ "address": {
51
+ "editor_content": {"content": [{"text": "1 Main Street", "type": "text"}]}
52
+ },
53
+ "info_text": {
54
+ "editor_content": {"content": [{"text": "You signed up on our website.", "type": "text"}]}
55
+ },
56
+ "unsubscribe_text": "Unsubscribe",
57
+ "preferences_text": "Update your preferences",
58
+ "social_links": [
59
+ {"url": "https://example.com", "type": "website", "label": "Website", "link_id": 7}
60
+ ]
61
+ }
62
+ },
63
+ "blocks": [
64
+ {
65
+ "id": "1",
66
+ "type": "paragraph",
67
+ "editor_content": {
68
+ "attrs": {"textAlign": "left"},
69
+ "content": [{"text": "Hello", "type": "text"}]
70
+ }
71
+ },
72
+ {
73
+ "id": "2",
74
+ "type": "bulleted_list",
75
+ "editor_content": {},
76
+ "children": [{"id": "3", "type": "paragraph", "editor_content": {}}]
77
+ }
78
+ ]
79
+ },
80
+ "etag": "42"
81
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "data": {
3
+ "id": "57",
4
+ "type": "paragraph",
5
+ "editor_content": {
6
+ "attrs": {"textAlign": "left"},
7
+ "content": [{"text": "Hello", "type": "text"}]
8
+ },
9
+ "placement": {"after": "56", "before": null}
10
+ },
11
+ "etag": "43"
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "data": {
3
+ "id": "58",
4
+ "type": "paragraph",
5
+ "editor_content": {
6
+ "attrs": {"textAlign": "center"},
7
+ "content": [{"text": "Hi", "type": "text"}]
8
+ },
9
+ "placement": {"after": "57", "before": null}
10
+ },
11
+ "etag": "43"
12
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "data": {
3
+ "id": "58",
4
+ "type": "paragraph",
5
+ "editor_content": {
6
+ "content": [{"text": "Hi", "type": "text"}]
7
+ },
8
+ "placement": {"after": "57", "before": null}
9
+ },
10
+ "etag": "43",
11
+ "rejected": {
12
+ "message": "Some attributes were rejected.",
13
+ "keys": ["editor_content.attrs.onclick"]
14
+ }
15
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "data": {
3
+ "id": "57",
4
+ "type": "paragraph",
5
+ "editor_content": {
6
+ "attrs": {"textAlign": "left"},
7
+ "content": [{"text": "Hello", "type": "text"}]
8
+ },
9
+ "placement": {"after": null, "before": "1"}
10
+ },
11
+ "etag": "46"
12
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "data": {
3
+ "id": "57",
4
+ "type": "paragraph",
5
+ "editor_content": {"content": [{"text": "Hello again", "type": "text"}]},
6
+ "placement": {"after": "56", "before": "58"}
7
+ },
8
+ "etag": "44"
9
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "data": {
3
+ "id": "57",
4
+ "type": "paragraph",
5
+ "editor_content": {"content": [{"text": "Hello again", "type": "text"}]},
6
+ "placement": {"after": "56", "before": "58"}
7
+ },
8
+ "etag": "44",
9
+ "rejected": {
10
+ "message": "Some attributes were rejected.",
11
+ "keys": ["editor_content.mobile.onclick"]
12
+ }
13
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "valid": false,
3
+ "errors": {"type": ["The selected type is invalid."]},
4
+ "schema": {"version": "2.73.2", "hash": "0f3a9c"}
5
+ }
@@ -0,0 +1 @@
1
+ {"etag": "45"}
@@ -0,0 +1 @@
1
+ {"data": {"html": "<!doctype html><html><body><p>Hello</p></body></html>"}, "etag": "42"}
@@ -0,0 +1,111 @@
1
+ {
2
+ "data": {
3
+ "brand": {
4
+ "mode": "custom",
5
+ "font_family": "Inter, sans-serif",
6
+ "background_color": "#f4f4f4",
7
+ "text_color": "#000000",
8
+ "border_color": "#e5e7eb"
9
+ },
10
+ "layout": {
11
+ "width_mode": "default",
12
+ "alignment": "center"
13
+ },
14
+ "typography": {
15
+ "link": {
16
+ "color": "#2cb191"
17
+ },
18
+ "heading": {
19
+ "font_family": "Inter, sans-serif",
20
+ "font_weight": "700",
21
+ "line_height": "150%",
22
+ "letter_spacing": "0",
23
+ "h1": {
24
+ "color": "#000000",
25
+ "size": "32"
26
+ },
27
+ "h2": {
28
+ "color": "#000000",
29
+ "size": "24"
30
+ },
31
+ "h3": {
32
+ "color": "#000000",
33
+ "size": "20"
34
+ }
35
+ },
36
+ "paragraph": {
37
+ "font_family": "Inter, sans-serif",
38
+ "font_weight": "400",
39
+ "line_height": "150%",
40
+ "letter_spacing": "0",
41
+ "p1": {
42
+ "color": "#000000",
43
+ "size": "16"
44
+ }
45
+ }
46
+ },
47
+ "button": {
48
+ "font_family": "Inter, sans-serif",
49
+ "font_weight": "600",
50
+ "letter_spacing": "0",
51
+ "background_color": "#2cb191",
52
+ "text_color": "#ffffff",
53
+ "border_radius": "4"
54
+ },
55
+ "footer": {
56
+ "show_preferences": false,
57
+ "company_name": {
58
+ "editor_content": {
59
+ "content": [
60
+ {
61
+ "type": "text",
62
+ "text": "Acme",
63
+ "marks": [
64
+ {
65
+ "type": "bold"
66
+ }
67
+ ]
68
+ }
69
+ ]
70
+ }
71
+ },
72
+ "address": {
73
+ "editor_content": {
74
+ "content": [
75
+ {
76
+ "text": "1 Main Street",
77
+ "type": "text"
78
+ }
79
+ ]
80
+ }
81
+ },
82
+ "info_text": {
83
+ "editor_content": {
84
+ "content": [
85
+ {
86
+ "text": "You signed up on our website.",
87
+ "type": "text"
88
+ }
89
+ ]
90
+ }
91
+ },
92
+ "unsubscribe_text": "Unsubscribe",
93
+ "preferences_text": "Update your preferences",
94
+ "social_links": [
95
+ {
96
+ "url": "https://example.com",
97
+ "type": "website",
98
+ "label": "Website",
99
+ "link_id": 7
100
+ }
101
+ ]
102
+ }
103
+ },
104
+ "etag": "47",
105
+ "rejected": {
106
+ "message": "Some attributes were rejected.",
107
+ "keys": [
108
+ "footer.social_links[0].url"
109
+ ]
110
+ }
111
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "valid": false,
3
+ "errors": {"layout.width": ["The layout.width field cannot be changed."]},
4
+ "schema": {"version": "2.73.2", "hash": "0f3a9c"},
5
+ "rejected": {
6
+ "message": "Some attributes were rejected.",
7
+ "keys": ["footer.social_links[0].url"]
8
+ }
9
+ }
@@ -0,0 +1 @@
1
+ {"data": {"id": "789", "name": "Newsletter", "type": "email", "created_at": "2026-10-28 12:00:00"}}
@@ -0,0 +1 @@
1
+ {"data": {"id": "903", "created_at": "2026-10-28 12:10:00", "current": true}}
@@ -0,0 +1,26 @@
1
+ {
2
+ "data": [
3
+ {"id": "902", "created_at": "2026-10-28 12:05:00", "current": true},
4
+ {"id": "901", "created_at": "2026-10-28 12:00:00", "current": false}
5
+ ],
6
+ "links": {
7
+ "first": "https://connect.mailerlite.com/api/emails/123/versions?page=1",
8
+ "last": "https://connect.mailerlite.com/api/emails/123/versions?page=1",
9
+ "prev": null,
10
+ "next": null
11
+ },
12
+ "meta": {
13
+ "current_page": 1,
14
+ "from": 1,
15
+ "last_page": 1,
16
+ "links": [
17
+ {"url": null, "label": "&laquo; Previous", "page": null, "active": false},
18
+ {"url": "https://connect.mailerlite.com/api/emails/123/versions?page=1", "label": "1", "page": 1, "active": true},
19
+ {"url": null, "label": "Next &raquo;", "page": null, "active": false}
20
+ ],
21
+ "path": "https://connect.mailerlite.com/api/emails/123/versions",
22
+ "per_page": 25,
23
+ "to": 2,
24
+ "total": 2
25
+ }
26
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "data": [
3
+ {
4
+ "id": "301",
5
+ "type": "image",
6
+ "name": "summer sale banner.png",
7
+ "url": "https://storage.example.com/files/summer-sale-banner.png",
8
+ "size": 48213,
9
+ "width": 1200,
10
+ "height": 600,
11
+ "created_at": "2026-10-20 09:30:00"
12
+ },
13
+ {
14
+ "id": "302",
15
+ "type": "pdf",
16
+ "name": "summer sale terms.pdf",
17
+ "url": "https://storage.example.com/files/summer-sale-terms.pdf",
18
+ "size": 10240,
19
+ "width": null,
20
+ "height": null,
21
+ "created_at": "2026-10-21 10:00:00"
22
+ }
23
+ ],
24
+ "links": {
25
+ "first": "https://connect.mailerlite.com/api/files?page=1",
26
+ "last": "https://connect.mailerlite.com/api/files?page=1",
27
+ "prev": null,
28
+ "next": null
29
+ },
30
+ "meta": {
31
+ "current_page": 1,
32
+ "from": 1,
33
+ "last_page": 1,
34
+ "links": [
35
+ {"url": null, "label": "&laquo; Previous", "page": null, "active": false},
36
+ {"url": "https://connect.mailerlite.com/api/files?page=1", "label": "1", "page": 1, "active": true},
37
+ {"url": null, "label": "Next &raquo;", "page": null, "active": false}
38
+ ],
39
+ "path": "https://connect.mailerlite.com/api/files",
40
+ "per_page": 25,
41
+ "to": 2,
42
+ "total": 2
43
+ }
44
+ }
data/spec/groups_rspec.rb CHANGED
@@ -94,4 +94,25 @@ RSpec.describe MailerLite::Groups do
94
94
  end
95
95
  end
96
96
  end
97
+
98
+ describe '#get_subscribers query string' do
99
+ it 'sends the params in the query string with no body' do
100
+ stub = stub_api(:get, '/groups/1/subscribers', query: { 'filter[status]' => 'active', 'limit' => '10', 'cursor' => 'abc', 'include' => 'groups' })
101
+
102
+ groups.get_subscribers(group_id: 1, filter: { status: 'active' }, limit: 10, cursor: 'abc', include: 'groups')
103
+
104
+ expect(stub).to have_been_requested
105
+ end
106
+ end
107
+
108
+ describe '#import_subscribers' do
109
+ it 'sends POST /groups/{group}/import-subscribers with the body' do
110
+ list = [{ 'email' => 'a@b.com', 'name' => 'Ann', 'fields' => { 'city' => 'Vilnius' } }]
111
+ stub = stub_api(:post, '/groups/1/import-subscribers', body: { 'subscribers' => list, 'resubscribe' => true, 'trigger_automations' => false })
112
+
113
+ groups.import_subscribers(group_id: 1, subscribers: list, resubscribe: true, trigger_automations: false)
114
+
115
+ expect(stub).to have_been_requested
116
+ end
117
+ end
97
118
  end