twitter-ads 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +2 -0
- data/CONTRIBUTING.md +77 -0
- data/LICENSE +22 -0
- data/README.md +111 -0
- data/Rakefile +86 -0
- data/bin/twitter-ads +42 -0
- data/lib/twitter-ads.rb +54 -0
- data/lib/twitter-ads/account.rb +229 -0
- data/lib/twitter-ads/audiences/tailored_audience.rb +177 -0
- data/lib/twitter-ads/campaign/app_list.rb +42 -0
- data/lib/twitter-ads/campaign/campaign.rb +40 -0
- data/lib/twitter-ads/campaign/funding_instrument.rb +33 -0
- data/lib/twitter-ads/campaign/line_item.rb +91 -0
- data/lib/twitter-ads/campaign/promotable_user.rb +28 -0
- data/lib/twitter-ads/campaign/targeting_criteria.rb +77 -0
- data/lib/twitter-ads/campaign/tweet.rb +83 -0
- data/lib/twitter-ads/client.rb +92 -0
- data/lib/twitter-ads/creative/app_download_card.rb +44 -0
- data/lib/twitter-ads/creative/image_app_download_card.rb +44 -0
- data/lib/twitter-ads/creative/image_conversation_card.rb +44 -0
- data/lib/twitter-ads/creative/lead_gen_card.rb +46 -0
- data/lib/twitter-ads/creative/promoted_account.rb +38 -0
- data/lib/twitter-ads/creative/promoted_tweet.rb +87 -0
- data/lib/twitter-ads/creative/video.rb +43 -0
- data/lib/twitter-ads/creative/video_app_download_card.rb +47 -0
- data/lib/twitter-ads/creative/video_conversation_card.rb +46 -0
- data/lib/twitter-ads/creative/website_card.rb +48 -0
- data/lib/twitter-ads/cursor.rb +127 -0
- data/lib/twitter-ads/enum.rb +135 -0
- data/lib/twitter-ads/error.rb +93 -0
- data/lib/twitter-ads/http/request.rb +127 -0
- data/lib/twitter-ads/http/response.rb +74 -0
- data/lib/twitter-ads/http/ton_upload.rb +140 -0
- data/lib/twitter-ads/legacy.rb +7 -0
- data/lib/twitter-ads/resources/analytics.rb +90 -0
- data/lib/twitter-ads/resources/dsl.rb +108 -0
- data/lib/twitter-ads/resources/persistence.rb +43 -0
- data/lib/twitter-ads/resources/resource.rb +92 -0
- data/lib/twitter-ads/targeting/reach_estimate.rb +69 -0
- data/lib/twitter-ads/utils.rb +76 -0
- data/lib/twitter-ads/version.rb +6 -0
- data/spec/fixtures/accounts_all.json +65 -0
- data/spec/fixtures/accounts_features.json +18 -0
- data/spec/fixtures/accounts_load.json +19 -0
- data/spec/fixtures/app_lists_all.json +22 -0
- data/spec/fixtures/app_lists_load.json +31 -0
- data/spec/fixtures/campaigns_all.json +208 -0
- data/spec/fixtures/campaigns_load.json +27 -0
- data/spec/fixtures/funding_instruments_all.json +74 -0
- data/spec/fixtures/funding_instruments_load.json +28 -0
- data/spec/fixtures/line_items_all.json +292 -0
- data/spec/fixtures/line_items_load.json +36 -0
- data/spec/fixtures/placements.json +35 -0
- data/spec/fixtures/promotable_users_all.json +57 -0
- data/spec/fixtures/promotable_users_load.json +18 -0
- data/spec/fixtures/promoted_tweets_all.json +212 -0
- data/spec/fixtures/promoted_tweets_load.json +19 -0
- data/spec/fixtures/reach_estimate.json +19 -0
- data/spec/fixtures/tailored_audiences_all.json +67 -0
- data/spec/fixtures/tailored_audiences_load.json +29 -0
- data/spec/fixtures/tweet_preview.json +24 -0
- data/spec/fixtures/videos_all.json +50 -0
- data/spec/fixtures/videos_load.json +22 -0
- data/spec/quality_spec.rb +15 -0
- data/spec/shared/properties.rb +20 -0
- data/spec/spec_helper.rb +61 -0
- data/spec/support/helpers.rb +42 -0
- data/spec/twitter-ads/account_spec.rb +315 -0
- data/spec/twitter-ads/audiences/tailored_audience_spec.rb +45 -0
- data/spec/twitter-ads/campaign/app_list_spec.rb +108 -0
- data/spec/twitter-ads/campaign/line_item_spec.rb +95 -0
- data/spec/twitter-ads/campaign/reach_estimate_spec.rb +98 -0
- data/spec/twitter-ads/campaign/targeting_criteria_spec.rb +39 -0
- data/spec/twitter-ads/campaign/tweet_spec.rb +83 -0
- data/spec/twitter-ads/client_spec.rb +115 -0
- data/spec/twitter-ads/creative/app_download_card_spec.rb +44 -0
- data/spec/twitter-ads/creative/image_app_download_card_spec.rb +43 -0
- data/spec/twitter-ads/creative/image_conversation_card_spec.rb +40 -0
- data/spec/twitter-ads/creative/lead_gen_card_spec.rb +46 -0
- data/spec/twitter-ads/creative/promoted_account_spec.rb +30 -0
- data/spec/twitter-ads/creative/promoted_tweet_spec.rb +46 -0
- data/spec/twitter-ads/creative/video_app_download_card_spec.rb +42 -0
- data/spec/twitter-ads/creative/video_conversation_card_spec.rb +52 -0
- data/spec/twitter-ads/creative/video_legacy_spec.rb +43 -0
- data/spec/twitter-ads/creative/video_spec.rb +43 -0
- data/spec/twitter-ads/creative/website_card_spec.rb +37 -0
- data/spec/twitter-ads/cursor_spec.rb +67 -0
- data/spec/twitter-ads/placements_spec.rb +36 -0
- data/spec/twitter-ads/utils_spec.rb +101 -0
- data/twitter-ads.gemspec +37 -0
- metadata +247 -0
- 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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|