twitter-ads 0.3.4

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 (94) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -0
  4. data/CONTRIBUTING.md +77 -0
  5. data/LICENSE +22 -0
  6. data/README.md +111 -0
  7. data/Rakefile +86 -0
  8. data/bin/twitter-ads +42 -0
  9. data/lib/twitter-ads.rb +54 -0
  10. data/lib/twitter-ads/account.rb +229 -0
  11. data/lib/twitter-ads/audiences/tailored_audience.rb +177 -0
  12. data/lib/twitter-ads/campaign/app_list.rb +42 -0
  13. data/lib/twitter-ads/campaign/campaign.rb +40 -0
  14. data/lib/twitter-ads/campaign/funding_instrument.rb +33 -0
  15. data/lib/twitter-ads/campaign/line_item.rb +91 -0
  16. data/lib/twitter-ads/campaign/promotable_user.rb +28 -0
  17. data/lib/twitter-ads/campaign/targeting_criteria.rb +77 -0
  18. data/lib/twitter-ads/campaign/tweet.rb +83 -0
  19. data/lib/twitter-ads/client.rb +92 -0
  20. data/lib/twitter-ads/creative/app_download_card.rb +44 -0
  21. data/lib/twitter-ads/creative/image_app_download_card.rb +44 -0
  22. data/lib/twitter-ads/creative/image_conversation_card.rb +44 -0
  23. data/lib/twitter-ads/creative/lead_gen_card.rb +46 -0
  24. data/lib/twitter-ads/creative/promoted_account.rb +38 -0
  25. data/lib/twitter-ads/creative/promoted_tweet.rb +87 -0
  26. data/lib/twitter-ads/creative/video.rb +43 -0
  27. data/lib/twitter-ads/creative/video_app_download_card.rb +47 -0
  28. data/lib/twitter-ads/creative/video_conversation_card.rb +46 -0
  29. data/lib/twitter-ads/creative/website_card.rb +48 -0
  30. data/lib/twitter-ads/cursor.rb +127 -0
  31. data/lib/twitter-ads/enum.rb +135 -0
  32. data/lib/twitter-ads/error.rb +93 -0
  33. data/lib/twitter-ads/http/request.rb +127 -0
  34. data/lib/twitter-ads/http/response.rb +74 -0
  35. data/lib/twitter-ads/http/ton_upload.rb +140 -0
  36. data/lib/twitter-ads/legacy.rb +7 -0
  37. data/lib/twitter-ads/resources/analytics.rb +90 -0
  38. data/lib/twitter-ads/resources/dsl.rb +108 -0
  39. data/lib/twitter-ads/resources/persistence.rb +43 -0
  40. data/lib/twitter-ads/resources/resource.rb +92 -0
  41. data/lib/twitter-ads/targeting/reach_estimate.rb +69 -0
  42. data/lib/twitter-ads/utils.rb +76 -0
  43. data/lib/twitter-ads/version.rb +6 -0
  44. data/spec/fixtures/accounts_all.json +65 -0
  45. data/spec/fixtures/accounts_features.json +18 -0
  46. data/spec/fixtures/accounts_load.json +19 -0
  47. data/spec/fixtures/app_lists_all.json +22 -0
  48. data/spec/fixtures/app_lists_load.json +31 -0
  49. data/spec/fixtures/campaigns_all.json +208 -0
  50. data/spec/fixtures/campaigns_load.json +27 -0
  51. data/spec/fixtures/funding_instruments_all.json +74 -0
  52. data/spec/fixtures/funding_instruments_load.json +28 -0
  53. data/spec/fixtures/line_items_all.json +292 -0
  54. data/spec/fixtures/line_items_load.json +36 -0
  55. data/spec/fixtures/placements.json +35 -0
  56. data/spec/fixtures/promotable_users_all.json +57 -0
  57. data/spec/fixtures/promotable_users_load.json +18 -0
  58. data/spec/fixtures/promoted_tweets_all.json +212 -0
  59. data/spec/fixtures/promoted_tweets_load.json +19 -0
  60. data/spec/fixtures/reach_estimate.json +19 -0
  61. data/spec/fixtures/tailored_audiences_all.json +67 -0
  62. data/spec/fixtures/tailored_audiences_load.json +29 -0
  63. data/spec/fixtures/tweet_preview.json +24 -0
  64. data/spec/fixtures/videos_all.json +50 -0
  65. data/spec/fixtures/videos_load.json +22 -0
  66. data/spec/quality_spec.rb +15 -0
  67. data/spec/shared/properties.rb +20 -0
  68. data/spec/spec_helper.rb +61 -0
  69. data/spec/support/helpers.rb +42 -0
  70. data/spec/twitter-ads/account_spec.rb +315 -0
  71. data/spec/twitter-ads/audiences/tailored_audience_spec.rb +45 -0
  72. data/spec/twitter-ads/campaign/app_list_spec.rb +108 -0
  73. data/spec/twitter-ads/campaign/line_item_spec.rb +95 -0
  74. data/spec/twitter-ads/campaign/reach_estimate_spec.rb +98 -0
  75. data/spec/twitter-ads/campaign/targeting_criteria_spec.rb +39 -0
  76. data/spec/twitter-ads/campaign/tweet_spec.rb +83 -0
  77. data/spec/twitter-ads/client_spec.rb +115 -0
  78. data/spec/twitter-ads/creative/app_download_card_spec.rb +44 -0
  79. data/spec/twitter-ads/creative/image_app_download_card_spec.rb +43 -0
  80. data/spec/twitter-ads/creative/image_conversation_card_spec.rb +40 -0
  81. data/spec/twitter-ads/creative/lead_gen_card_spec.rb +46 -0
  82. data/spec/twitter-ads/creative/promoted_account_spec.rb +30 -0
  83. data/spec/twitter-ads/creative/promoted_tweet_spec.rb +46 -0
  84. data/spec/twitter-ads/creative/video_app_download_card_spec.rb +42 -0
  85. data/spec/twitter-ads/creative/video_conversation_card_spec.rb +52 -0
  86. data/spec/twitter-ads/creative/video_legacy_spec.rb +43 -0
  87. data/spec/twitter-ads/creative/video_spec.rb +43 -0
  88. data/spec/twitter-ads/creative/website_card_spec.rb +37 -0
  89. data/spec/twitter-ads/cursor_spec.rb +67 -0
  90. data/spec/twitter-ads/placements_spec.rb +36 -0
  91. data/spec/twitter-ads/utils_spec.rb +101 -0
  92. data/twitter-ads.gemspec +37 -0
  93. metadata +247 -0
  94. metadata.gz.sig +0 -0
@@ -0,0 +1,29 @@
1
+ {
2
+ "data_type": "tailored_audiences",
3
+ "data": {
4
+ "targetable": false,
5
+ "name": "TA #2",
6
+ "targetable_types": [
7
+ "WEB",
8
+ "EXCLUDED_WEB"
9
+ ],
10
+ "audience_type": "WEB",
11
+ "id": "abc2",
12
+ "reasons_not_targetable": [
13
+ "TOO_SMALL"
14
+ ],
15
+ "list_type": null,
16
+ "created_at": "2014-03-09T20:35:41Z",
17
+ "updated_at": "2014-06-11T09:38:06Z",
18
+ "partner_source": "OTHER",
19
+ "deleted": false,
20
+ "audience_size": null
21
+ },
22
+ "request": {
23
+ "params": {
24
+ "account_id": "2iqph",
25
+ "name": "TA #2",
26
+ "list_type": "EMAIL"
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "data_type": "tweet_preview",
3
+ "data": [
4
+ {
5
+ "platform": "web",
6
+ "preview": " <div><img><div><div><div><a>AdsAPI</a><a>@AdsAPI</a></div></div><div>Hello World!</div><div><div><img><div><div></div><div><span> Ratings/pricing not available for preview </span><span> Ratings/pricing not available for preview </span></div><div></div> INSTALL </div></div></div><a><img></a><div><div><span></span><span>Promoted by AdsAPI</span></div></div></div></div>"
7
+ },
8
+ {
9
+ "platform": "android",
10
+ "preview": " <div><img><div><div><div><a>AdsAPI</a><a>@AdsAPI</a></div></div><div>Hello World!</div><div><div><img><div><div></div><div><span> Ratings/pricing not available for preview </span><span> Ratings/pricing not available for preview </span></div><div></div> INSTALL </div></div></div><a><img></a><div><div><span></span><span>Promoted by AdsAPI</span></div></div></div></div>"
11
+ },
12
+ {
13
+ "platform": "iphone",
14
+ "preview": " <div><img><div><div><div><a>AdsAPI</a><a>@AdsAPI</a></div></div><div>Hello World!</div><div><div><img><div><div></div><div><span> Ratings/pricing not available for preview </span><span> Ratings/pricing not available for preview </span></div><div></div> INSTALL </div></div></div><a><img></a><div><div><span></span><span>Promoted by AdsAPI</span></div></div></div></div>"
15
+ }
16
+ ],
17
+ "request": {
18
+ "params": {
19
+ "status": "Hello World!",
20
+ "card_id": "pfs",
21
+ "account_id": "2iqph"
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,50 @@
1
+ {
2
+ "data_type": "video",
3
+ "data": [
4
+ {
5
+ "tweeted": false,
6
+ "ready_to_tweet": true,
7
+ "duration": 289149,
8
+ "reasons_not_servable": [],
9
+ "description": "youtubz",
10
+ "preview_url": "https://amp.twimg.com/prod/multibr_v_1/video/2015/09/24/21/d5e3ae4d-34c4-4f49-894e-0c17735916a4-libx264-baseline-2528k.mp4?rYDB9GoYJcfWyizjXnBZra4UnGQMTkzUkIuW6OKjlXw%3D",
11
+ "id": "d5e3ae4d-34c4-4f49-894e-0c17735916a4",
12
+ "created_at": "2015-09-24T21:21:00Z",
13
+ "title": "video #1",
14
+ "updated_at": "2015-09-24T21:25:42Z",
15
+ "deleted": false
16
+ },
17
+ {
18
+ "tweeted": false,
19
+ "ready_to_tweet": true,
20
+ "duration": 16517,
21
+ "reasons_not_servable": [],
22
+ "description": "my favorite video",
23
+ "preview_url": "https://amp.twimg.com/prod/multibr_v_1/video/2015/09/24/21/a6d50810-c0bd-408e-b703-b789a82981ce-libx264-baseline-2528k.mp4?rYDB9GoYJcfWyizjXnBZra4UnGQMTkzUkIuW6OKjlXw%3D",
24
+ "id": "a6d50810-c0bd-408e-b703-b789a82981ce",
25
+ "created_at": "2015-09-24T21:11:01Z",
26
+ "title": "video #2",
27
+ "updated_at": "2015-09-24T21:11:41Z",
28
+ "deleted": false
29
+ },
30
+ {
31
+ "tweeted": false,
32
+ "ready_to_tweet": false,
33
+ "duration": 16517,
34
+ "reasons_not_servable": ["ERROR"],
35
+ "description": "another great video",
36
+ "preview_url": "https://amp.twimg.com/prod/multibr_v_1/video/2015/09/24/21/3beaeaca-0f2f-4aff-a025-e0d53a0aef2c-libx264-baseline-2528k.mp4?rYDB9GoYJcfWyizjXnBZra4UnGQMTkzUkIuW6OKjlXw%3D",
37
+ "id": "3beaeaca-0f2f-4aff-a025-e0d53a0aef2c",
38
+ "created_at": "2015-09-24T21:08:55Z",
39
+ "title": "video #3",
40
+ "updated_at": "2015-09-24T21:10:01Z",
41
+ "deleted": true
42
+ }
43
+ ],
44
+ "request": {
45
+ "params": {
46
+ "account_id": "2iqph"
47
+ }
48
+ },
49
+ "next_cursor": null
50
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "data_type": "video",
3
+ "data": {
4
+ "tweeted": false,
5
+ "ready_to_tweet": true,
6
+ "duration": 289149,
7
+ "reasons_not_servable": [],
8
+ "description": "youtubz",
9
+ "preview_url": "https://amp.twimg.com/prod/multibr_v_1/video/2015/09/24/21/d5e3ae4d-34c4-4f49-894e-0c17735916a4-libx264-baseline-2528k.mp4?rYDB9GoYJcfWyizjXnBZra4UnGQMTkzUkIuW6OKjlXw%3D",
10
+ "id": "d5e3ae4d-34c4-4f49-894e-0c17735916a4",
11
+ "created_at": "2015-09-24T21:21:00Z",
12
+ "title": "video #1",
13
+ "updated_at": "2015-09-24T21:25:42Z",
14
+ "deleted": false
15
+ },
16
+ "request": {
17
+ "params": {
18
+ "video_id": "d5e3ae4d-34c4-4f49-894e-0c17735916a4",
19
+ "account_id": "2iqph"
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ require 'spec_helper'
5
+
6
+ describe 'Code Style & Quality', :quality do
7
+
8
+ # If this test fails, you need to run 'rubocop' to see what needs to be fixed.
9
+ # Please see the CONTRIBUTING.md file for additional style guide information.
10
+ it 'has no style guide or quality violations', :style do
11
+ result = silence { RuboCop::CLI.new.run([]) }
12
+ expect(result).to eq(0)
13
+ end unless RUBY_ENGINE =~ /rbx/
14
+
15
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ RSpec.shared_examples 'object property check' do |read, write|
5
+
6
+ it 'has all the correct write properties' do
7
+ write.each do |prop|
8
+ expect(subject.respond_to?(prop)).to be true
9
+ expect(subject.respond_to?("#{prop}=")).to be true
10
+ end
11
+ end
12
+
13
+ it 'has all the correct read-only properties' do
14
+ read.each do |prop|
15
+ expect(subject.respond_to?(prop)).to be true
16
+ expect(subject.respond_to?("#{prop}=")).not_to be true
17
+ end
18
+ end
19
+
20
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ unless RUBY_PLATFORM =~ /java/ || RUBY_ENGINE =~ /rbx/
5
+ require 'simplecov'
6
+ require 'codeclimate-test-reporter'
7
+
8
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
9
+ SimpleCov::Formatter::HTMLFormatter,
10
+ CodeClimate::TestReporter::Formatter
11
+ ]
12
+
13
+ SimpleCov.start do
14
+ add_filter '/spec/'
15
+ minimum_coverage(80.00)
16
+ end
17
+ end
18
+
19
+ require 'rubocop'
20
+ require 'faker'
21
+
22
+ require 'webmock/rspec'
23
+ WebMock.disable_net_connect!(allow: 'codeclimate.com')
24
+
25
+ require 'twitter-ads'
26
+ include TwitterAds
27
+
28
+ # Require All Support Files
29
+ Dir['./spec/support/*.rb'].sort.each { |file| require file }
30
+
31
+ ADS_API = 'https://ads-api.twitter.com/0'.freeze
32
+ TON_API = 'https://ton.twitter.com'.freeze
33
+ UPLOAD_API = 'https://upload.twitter.com/1.1'.freeze
34
+
35
+ RSpec.configure do |config|
36
+ # Helpers & Custom Matchers
37
+ include Helpers
38
+
39
+ # General
40
+ config.color = true
41
+ config.fail_fast = true unless ENV['CI']
42
+ config.formatter = ENV['CI'] ? 'documentation' : 'progress'
43
+ config.profile_examples = 5
44
+
45
+ # Expectations
46
+ config.expect_with :rspec do |expectations|
47
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
48
+ end
49
+
50
+ # Mocking
51
+ config.mock_with :rspec do |mocks|
52
+ mocks.verify_partial_doubles = true
53
+ end
54
+
55
+ # Test Order & Filtering
56
+ config.order = :random
57
+ Kernel.srand config.seed
58
+ end
59
+
60
+ # Require All Shared Examples
61
+ Dir['./spec/shared/*.rb'].sort.each { |file| require file }
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ # Helper module for custom test matchers and utilities.
5
+ module Helpers
6
+
7
+ # Helper method to temporarily redirect stdout and stderr for
8
+ # a given block of code.
9
+ #
10
+ # @param &block [Block] The code block to be executed.
11
+ #
12
+ # @return [Object] The result of the block.
13
+ def silence(&block)
14
+ original_stdout = $stdout
15
+ original_stderr = $stderr
16
+ $stdout = $stderr = File.new('/dev/null', 'w')
17
+ yield block
18
+ ensure
19
+ $stdout = original_stdout
20
+ $stderr = original_stderr
21
+ end
22
+
23
+ # Helper method for returning a JSON fixture file.
24
+ #
25
+ # @param name [Symbol] The name of the fixture file to be returned.
26
+ #
27
+ # @return [File] The file instance of the fixture specified.
28
+ def fixture(name)
29
+ File.new(File.expand_path("../../fixtures/#{name}.json", __FILE__))
30
+ end
31
+
32
+ # Helper method to stub a request with a fixture by name.
33
+ #
34
+ # @param method [Symbol] The HTTP method (:get, :put, :post, :delete)
35
+ # @param name [String] The fixture name.
36
+ # @param url [String] The URL match pattern or regular expression.
37
+ # @param status [Integer] The response code to return with the fixture.
38
+ def stub_fixture(method, name, url, status = 200)
39
+ stub_request(method, url).to_return(body: fixture(name), status: status)
40
+ end
41
+
42
+ end
@@ -0,0 +1,315 @@
1
+ # frozen_string_literal: true
2
+ # Copyright (C) 2015 Twitter, Inc.
3
+
4
+ require 'spec_helper'
5
+
6
+ describe TwitterAds::Account do
7
+
8
+ before(:each) do
9
+ stub_fixture(:get, :accounts_all, "#{ADS_API}/accounts")
10
+ stub_fixture(:get, :accounts_load, "#{ADS_API}/accounts/2iqph")
11
+ end
12
+
13
+ let(:client) do
14
+ Client.new(
15
+ Faker::Lorem.characters(15),
16
+ Faker::Lorem.characters(40),
17
+ "123456-#{Faker::Lorem.characters(40)}",
18
+ Faker::Lorem.characters(40)
19
+ )
20
+ end
21
+
22
+ let(:account) { client.accounts.first }
23
+
24
+ describe '#initialize' do
25
+
26
+ let(:account) { TwitterAds::Account.new(client) }
27
+ let(:methods) {
28
+ %w(features promotable_users funding_instruments campaigns line_items)
29
+ }
30
+
31
+ it 'raises an ArgumentError error if used without initialization' do
32
+ expect(account).to receive(:validate_loaded).and_call_original.exactly(methods.size).times
33
+ methods.each { |meth| expect { account.send(meth) }.to raise_error(ArgumentError) }
34
+ end
35
+
36
+ end
37
+
38
+ describe '#inspect' do
39
+
40
+ it 'includes the object id value' do
41
+ expect(account.inspect).to include(account.object_id.to_s)
42
+ end
43
+
44
+ it 'includes the class.name value' do
45
+ expect(account.inspect).to include(account.class.name)
46
+ end
47
+
48
+ it 'includes the Account ID value' do
49
+ expect(account.inspect).to include(account.id)
50
+ end
51
+
52
+ end
53
+
54
+ describe '#features' do
55
+
56
+ before(:each) do
57
+ stub_fixture(:get, :accounts_features, "#{ADS_API}/accounts/#{account.id}/features")
58
+ end
59
+
60
+ it 'successfully returns a list of features available' do
61
+ features = account.features
62
+ expect(features).not_to be_nil
63
+ expect(features).to all(be_a(String))
64
+ end
65
+
66
+ end
67
+
68
+ describe '#promotable_users' do
69
+
70
+ before(:each) do
71
+ resource_collection = "#{ADS_API}/accounts/#{account.id}/promotable_users"
72
+ stub_fixture(:get, :promotable_users_all, resource_collection)
73
+
74
+ resource = "#{ADS_API}/accounts/#{account.id}/promotable_users/4k"
75
+ stub_fixture(:get, :promotable_users_load, /#{resource}\?.*/)
76
+ end
77
+
78
+ context 'with an id specified' do
79
+
80
+ it 'succesfully loads the specified promoted tweet' do
81
+ result = account.promotable_users('4k')
82
+ expect(result).not_to be_nil
83
+ expect(result.class).to eq(TwitterAds::PromotableUser)
84
+ expect(result.id).to eq('4k')
85
+ end
86
+
87
+ end
88
+
89
+ context 'without an id specified' do
90
+
91
+ it 'succesfully returns a Cursor with all promoted tweets' do
92
+ result = account.promotable_users
93
+ expect(result.size).to eq(5)
94
+ expect(result.class).to eq(TwitterAds::Cursor)
95
+ expect(result.first.class).to eq(TwitterAds::PromotableUser)
96
+ expect(result.first.id).to eq('4k')
97
+ end
98
+
99
+ end
100
+
101
+ end
102
+
103
+ describe '#funding_instruments' do
104
+
105
+ before(:each) do
106
+ resource_collection = "#{ADS_API}/accounts/#{account.id}/funding_instruments"
107
+ stub_fixture(:get, :funding_instruments_all, resource_collection)
108
+
109
+ resource = "#{ADS_API}/accounts/#{account.id}/funding_instruments/5aa0p"
110
+ stub_fixture(:get, :funding_instruments_load, /#{resource}\?.*/)
111
+ end
112
+
113
+ context 'with an id specified' do
114
+
115
+ it 'succesfully loads the specified promoted tweet' do
116
+ result = account.funding_instruments('5aa0p')
117
+ expect(result).not_to be_nil
118
+ expect(result.class).to eq(TwitterAds::FundingInstrument)
119
+ expect(result.id).to eq('5aa0p')
120
+ end
121
+
122
+ end
123
+
124
+ context 'without an id specified' do
125
+
126
+ it 'succesfully returns a Cursor with all promoted tweets' do
127
+ result = account.funding_instruments
128
+ expect(result.size).to eq(3)
129
+ expect(result.class).to eq(TwitterAds::Cursor)
130
+ expect(result.first.class).to eq(TwitterAds::FundingInstrument)
131
+ expect(result.first.id).to eq('5aa0p')
132
+ end
133
+
134
+ end
135
+
136
+ end
137
+
138
+ describe '#campaigns' do
139
+
140
+ before(:each) do
141
+ resource_collection = "#{ADS_API}/accounts/#{account.id}/campaigns"
142
+ stub_fixture(:get, :campaigns_all, resource_collection)
143
+
144
+ resource = "#{ADS_API}/accounts/#{account.id}/campaigns/2wap7"
145
+ stub_fixture(:get, :campaigns_load, /#{resource}\?.*/)
146
+ end
147
+
148
+ context 'with an id specified' do
149
+
150
+ it 'succesfully loads the specified promoted tweet' do
151
+ result = account.campaigns('2wap7')
152
+ expect(result).not_to be_nil
153
+ expect(result.class).to eq(TwitterAds::Campaign)
154
+ expect(result.id).to eq('2wap7')
155
+ end
156
+
157
+ end
158
+
159
+ context 'without an id specified' do
160
+
161
+ it 'succesfully returns a Cursor with all promoted tweets' do
162
+ result = account.campaigns
163
+ expect(result.size).to eq(10)
164
+ expect(result.class).to eq(TwitterAds::Cursor)
165
+ expect(result.first.class).to eq(TwitterAds::Campaign)
166
+ expect(result.first.id).to eq('2wap7')
167
+ end
168
+
169
+ end
170
+
171
+ end
172
+
173
+ describe '#line_items' do
174
+
175
+ before(:each) do
176
+ resource_collection = "#{ADS_API}/accounts/#{account.id}/line_items"
177
+ stub_fixture(:get, :line_items_all, resource_collection)
178
+
179
+ resource = "#{ADS_API}/accounts/#{account.id}/line_items/bw2"
180
+ stub_fixture(:get, :line_items_load, /#{resource}\?.*/)
181
+ end
182
+
183
+ context 'with an id specified' do
184
+
185
+ it 'succesfully loads the specified promoted tweet' do
186
+ result = account.line_items('bw2')
187
+ expect(result).not_to be_nil
188
+ expect(result.class).to eq(TwitterAds::LineItem)
189
+ expect(result.id).to eq('bw2')
190
+ end
191
+
192
+ end
193
+
194
+ context 'without an id specified' do
195
+
196
+ it 'succesfully returns a Cursor with all promoted tweets' do
197
+ result = account.line_items
198
+ expect(result.size).to eq(10)
199
+ expect(result.class).to eq(TwitterAds::Cursor)
200
+ expect(result.first.class).to eq(TwitterAds::LineItem)
201
+ expect(result.first.id).to eq('bw2')
202
+ end
203
+
204
+ end
205
+
206
+ end
207
+
208
+ describe '#app_lists' do
209
+
210
+ before(:each) do
211
+ resource_collection = "#{ADS_API}/accounts/#{account.id}/app_lists"
212
+ stub_fixture(:get, :app_lists_all, resource_collection)
213
+
214
+ resource = "#{ADS_API}/accounts/#{account.id}/app_lists/abc2"
215
+ stub_fixture(:get, :app_lists_load, /#{resource}\?.*/)
216
+ end
217
+
218
+ context 'with an id specified' do
219
+
220
+ it 'successfully loads the specified app list' do
221
+ result = account.app_lists('abc2')
222
+ expect(result).not_to be_nil
223
+ expect(result.class).to eq(TwitterAds::AppList)
224
+ expect(result.id).to eq('abc2')
225
+ end
226
+
227
+ end
228
+
229
+ context 'without an id specified' do
230
+
231
+ it 'succesfully returns a cursor with all app lists' do
232
+ result = account.app_lists
233
+ expect(result.to_a.size).to eq(3)
234
+ expect(result.class).to eq(TwitterAds::Cursor)
235
+ expect(result.first.class).to eq(TwitterAds::AppList)
236
+ expect(result.first.id).to eq('abc2')
237
+ end
238
+
239
+ end
240
+
241
+ end
242
+
243
+ describe '#tailored_audiences' do
244
+
245
+ before(:each) do
246
+ resource_collection = "#{ADS_API}/accounts/#{account.id}/tailored_audiences"
247
+ stub_fixture(:get, :tailored_audiences_all, resource_collection)
248
+
249
+ resource = "#{ADS_API}/accounts/#{account.id}/tailored_audiences/abc2"
250
+ stub_fixture(:get, :tailored_audiences_load, /#{resource}\?.*/)
251
+ end
252
+
253
+ context 'with an id specified' do
254
+
255
+ it 'successfully loads the specified tailored audience' do
256
+ result = account.tailored_audiences('abc2')
257
+ expect(result).not_to be_nil
258
+ expect(result.class).to eq(TwitterAds::TailoredAudience)
259
+ expect(result.id).to eq('abc2')
260
+ end
261
+
262
+ end
263
+
264
+ context 'without an id specified' do
265
+
266
+ it 'succesfully returns a cursor with all tailored audiences' do
267
+ result = account.tailored_audiences
268
+ expect(result.to_a.size).to eq(3)
269
+ expect(result.class).to eq(TwitterAds::Cursor)
270
+ expect(result.first.class).to eq(TwitterAds::TailoredAudience)
271
+ expect(result.first.id).to eq('abc2')
272
+ end
273
+
274
+ end
275
+
276
+ end
277
+
278
+ describe '#videos' do
279
+
280
+ let!(:video_id) { 'd5e3ae4d-34c4-4f49-894e-0c17735916a4' }
281
+
282
+ before(:each) do
283
+ resource_collection = "#{ADS_API}/accounts/#{account.id}/videos"
284
+ stub_fixture(:get, :videos_all, resource_collection)
285
+
286
+ resource = "#{ADS_API}/accounts/#{account.id}/videos/#{video_id}"
287
+ stub_fixture(:get, :videos_load, /#{resource}\?.*/)
288
+ end
289
+
290
+ context 'with an id specified' do
291
+
292
+ it 'successfully loads the specified video' do
293
+ result = account.videos(video_id)
294
+ expect(result).not_to be_nil
295
+ expect(result.class).to eq(TwitterAds::Video)
296
+ expect(result.id).to eq(video_id)
297
+ end
298
+
299
+ end
300
+
301
+ context 'without an id specified' do
302
+
303
+ it 'succesfully returns a cursor with all videos' do
304
+ result = account.videos
305
+ expect(result.to_a.size).to eq(3)
306
+ expect(result.class).to eq(TwitterAds::Cursor)
307
+ expect(result.first.class).to eq(TwitterAds::Video)
308
+ expect(result.first.id).to eq(video_id)
309
+ end
310
+
311
+ end
312
+
313
+ end
314
+
315
+ end