yt-andrewroth 0.25.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) hide show
  1. data/.gitignore +27 -0
  2. data/.rspec +3 -0
  3. data/.travis.yml +9 -0
  4. data/.yardopts +5 -0
  5. data/CHANGELOG.md +732 -0
  6. data/Gemfile +4 -0
  7. data/MIT-LICENSE +20 -0
  8. data/README.md +489 -0
  9. data/Rakefile +11 -0
  10. data/YOUTUBE_IT.md +835 -0
  11. data/bin/yt +30 -0
  12. data/gemfiles/Gemfile.activesupport-3.x +4 -0
  13. data/gemfiles/Gemfile.activesupport-4.x +4 -0
  14. data/lib/yt.rb +21 -0
  15. data/lib/yt/actions/base.rb +32 -0
  16. data/lib/yt/actions/delete.rb +19 -0
  17. data/lib/yt/actions/delete_all.rb +32 -0
  18. data/lib/yt/actions/insert.rb +42 -0
  19. data/lib/yt/actions/list.rb +139 -0
  20. data/lib/yt/actions/modify.rb +37 -0
  21. data/lib/yt/actions/patch.rb +19 -0
  22. data/lib/yt/actions/update.rb +19 -0
  23. data/lib/yt/associations/has_attribute.rb +55 -0
  24. data/lib/yt/associations/has_authentication.rb +214 -0
  25. data/lib/yt/associations/has_many.rb +22 -0
  26. data/lib/yt/associations/has_one.rb +22 -0
  27. data/lib/yt/associations/has_reports.rb +320 -0
  28. data/lib/yt/collections/advertising_options_sets.rb +34 -0
  29. data/lib/yt/collections/annotations.rb +62 -0
  30. data/lib/yt/collections/assets.rb +58 -0
  31. data/lib/yt/collections/authentications.rb +47 -0
  32. data/lib/yt/collections/base.rb +62 -0
  33. data/lib/yt/collections/channels.rb +31 -0
  34. data/lib/yt/collections/claim_histories.rb +34 -0
  35. data/lib/yt/collections/claims.rb +56 -0
  36. data/lib/yt/collections/content_details.rb +30 -0
  37. data/lib/yt/collections/content_owner_details.rb +34 -0
  38. data/lib/yt/collections/content_owners.rb +32 -0
  39. data/lib/yt/collections/device_flows.rb +23 -0
  40. data/lib/yt/collections/file_details.rb +30 -0
  41. data/lib/yt/collections/ids.rb +27 -0
  42. data/lib/yt/collections/live_streaming_details.rb +30 -0
  43. data/lib/yt/collections/ownerships.rb +34 -0
  44. data/lib/yt/collections/partnered_channels.rb +28 -0
  45. data/lib/yt/collections/players.rb +30 -0
  46. data/lib/yt/collections/playlist_items.rb +53 -0
  47. data/lib/yt/collections/playlists.rb +28 -0
  48. data/lib/yt/collections/policies.rb +28 -0
  49. data/lib/yt/collections/ratings.rb +23 -0
  50. data/lib/yt/collections/references.rb +46 -0
  51. data/lib/yt/collections/related_playlists.rb +43 -0
  52. data/lib/yt/collections/reports.rb +161 -0
  53. data/lib/yt/collections/resources.rb +57 -0
  54. data/lib/yt/collections/resumable_sessions.rb +51 -0
  55. data/lib/yt/collections/snippets.rb +27 -0
  56. data/lib/yt/collections/statistics_sets.rb +30 -0
  57. data/lib/yt/collections/statuses.rb +27 -0
  58. data/lib/yt/collections/subscribed_channels.rb +46 -0
  59. data/lib/yt/collections/subscribers.rb +33 -0
  60. data/lib/yt/collections/subscriptions.rb +50 -0
  61. data/lib/yt/collections/user_infos.rb +36 -0
  62. data/lib/yt/collections/video_categories.rb +35 -0
  63. data/lib/yt/collections/videos.rb +137 -0
  64. data/lib/yt/config.rb +54 -0
  65. data/lib/yt/errors/forbidden.rb +13 -0
  66. data/lib/yt/errors/missing_auth.rb +81 -0
  67. data/lib/yt/errors/no_items.rb +13 -0
  68. data/lib/yt/errors/request_error.rb +74 -0
  69. data/lib/yt/errors/server_error.rb +13 -0
  70. data/lib/yt/errors/unauthorized.rb +50 -0
  71. data/lib/yt/models/account.rb +216 -0
  72. data/lib/yt/models/advertising_options_set.rb +38 -0
  73. data/lib/yt/models/annotation.rb +132 -0
  74. data/lib/yt/models/asset.rb +111 -0
  75. data/lib/yt/models/asset_metadata.rb +38 -0
  76. data/lib/yt/models/asset_snippet.rb +46 -0
  77. data/lib/yt/models/authentication.rb +83 -0
  78. data/lib/yt/models/base.rb +32 -0
  79. data/lib/yt/models/channel.rb +302 -0
  80. data/lib/yt/models/claim.rb +156 -0
  81. data/lib/yt/models/claim_event.rb +67 -0
  82. data/lib/yt/models/claim_history.rb +29 -0
  83. data/lib/yt/models/configuration.rb +70 -0
  84. data/lib/yt/models/content_detail.rb +65 -0
  85. data/lib/yt/models/content_owner.rb +48 -0
  86. data/lib/yt/models/content_owner_detail.rb +18 -0
  87. data/lib/yt/models/description.rb +58 -0
  88. data/lib/yt/models/device_flow.rb +16 -0
  89. data/lib/yt/models/file_detail.rb +21 -0
  90. data/lib/yt/models/id.rb +9 -0
  91. data/lib/yt/models/iterator.rb +16 -0
  92. data/lib/yt/models/live_streaming_detail.rb +23 -0
  93. data/lib/yt/models/match_policy.rb +34 -0
  94. data/lib/yt/models/ownership.rb +75 -0
  95. data/lib/yt/models/player.rb +18 -0
  96. data/lib/yt/models/playlist.rb +218 -0
  97. data/lib/yt/models/playlist_item.rb +112 -0
  98. data/lib/yt/models/policy.rb +36 -0
  99. data/lib/yt/models/policy_rule.rb +124 -0
  100. data/lib/yt/models/rating.rb +37 -0
  101. data/lib/yt/models/reference.rb +172 -0
  102. data/lib/yt/models/resource.rb +136 -0
  103. data/lib/yt/models/resumable_session.rb +52 -0
  104. data/lib/yt/models/right_owner.rb +58 -0
  105. data/lib/yt/models/snippet.rb +50 -0
  106. data/lib/yt/models/statistics_set.rb +26 -0
  107. data/lib/yt/models/status.rb +32 -0
  108. data/lib/yt/models/subscription.rb +38 -0
  109. data/lib/yt/models/timestamp.rb +13 -0
  110. data/lib/yt/models/url.rb +90 -0
  111. data/lib/yt/models/user_info.rb +26 -0
  112. data/lib/yt/models/video.rb +630 -0
  113. data/lib/yt/models/video_category.rb +12 -0
  114. data/lib/yt/request.rb +278 -0
  115. data/lib/yt/version.rb +3 -0
  116. data/spec/collections/claims_spec.rb +30 -0
  117. data/spec/collections/playlist_items_spec.rb +44 -0
  118. data/spec/collections/playlists_spec.rb +27 -0
  119. data/spec/collections/policies_spec.rb +30 -0
  120. data/spec/collections/references_spec.rb +30 -0
  121. data/spec/collections/reports_spec.rb +30 -0
  122. data/spec/collections/subscriptions_spec.rb +25 -0
  123. data/spec/collections/videos_spec.rb +43 -0
  124. data/spec/errors/forbidden_spec.rb +10 -0
  125. data/spec/errors/missing_auth_spec.rb +24 -0
  126. data/spec/errors/no_items_spec.rb +10 -0
  127. data/spec/errors/request_error_spec.rb +44 -0
  128. data/spec/errors/server_error_spec.rb +10 -0
  129. data/spec/errors/unauthorized_spec.rb +10 -0
  130. data/spec/models/account_spec.rb +138 -0
  131. data/spec/models/annotation_spec.rb +180 -0
  132. data/spec/models/asset_spec.rb +20 -0
  133. data/spec/models/channel_spec.rb +127 -0
  134. data/spec/models/claim_event_spec.rb +62 -0
  135. data/spec/models/claim_history_spec.rb +27 -0
  136. data/spec/models/claim_spec.rb +211 -0
  137. data/spec/models/configuration_spec.rb +44 -0
  138. data/spec/models/content_detail_spec.rb +45 -0
  139. data/spec/models/content_owner_detail_spec.rb +6 -0
  140. data/spec/models/description_spec.rb +94 -0
  141. data/spec/models/file_detail_spec.rb +13 -0
  142. data/spec/models/live_streaming_detail_spec.rb +6 -0
  143. data/spec/models/ownership_spec.rb +59 -0
  144. data/spec/models/player_spec.rb +13 -0
  145. data/spec/models/playlist_item_spec.rb +120 -0
  146. data/spec/models/playlist_spec.rb +138 -0
  147. data/spec/models/policy_rule_spec.rb +63 -0
  148. data/spec/models/policy_spec.rb +41 -0
  149. data/spec/models/rating_spec.rb +12 -0
  150. data/spec/models/reference_spec.rb +249 -0
  151. data/spec/models/request_spec.rb +163 -0
  152. data/spec/models/resource_spec.rb +57 -0
  153. data/spec/models/right_owner_spec.rb +71 -0
  154. data/spec/models/snippet_spec.rb +13 -0
  155. data/spec/models/statistics_set_spec.rb +13 -0
  156. data/spec/models/status_spec.rb +13 -0
  157. data/spec/models/subscription_spec.rb +30 -0
  158. data/spec/models/url_spec.rb +78 -0
  159. data/spec/models/video_category_spec.rb +21 -0
  160. data/spec/models/video_spec.rb +669 -0
  161. data/spec/requests/as_account/account_spec.rb +125 -0
  162. data/spec/requests/as_account/authentications_spec.rb +139 -0
  163. data/spec/requests/as_account/channel_spec.rb +259 -0
  164. data/spec/requests/as_account/channels_spec.rb +18 -0
  165. data/spec/requests/as_account/playlist_item_spec.rb +56 -0
  166. data/spec/requests/as_account/playlist_spec.rb +244 -0
  167. data/spec/requests/as_account/resource_spec.rb +18 -0
  168. data/spec/requests/as_account/thumbnail.jpg +0 -0
  169. data/spec/requests/as_account/video.mp4 +0 -0
  170. data/spec/requests/as_account/video_spec.rb +408 -0
  171. data/spec/requests/as_content_owner/account_spec.rb +25 -0
  172. data/spec/requests/as_content_owner/advertising_options_set_spec.rb +15 -0
  173. data/spec/requests/as_content_owner/asset_spec.rb +20 -0
  174. data/spec/requests/as_content_owner/channel_spec.rb +1934 -0
  175. data/spec/requests/as_content_owner/claim_history_spec.rb +20 -0
  176. data/spec/requests/as_content_owner/content_owner_spec.rb +241 -0
  177. data/spec/requests/as_content_owner/match_policy_spec.rb +17 -0
  178. data/spec/requests/as_content_owner/ownership_spec.rb +25 -0
  179. data/spec/requests/as_content_owner/playlist_spec.rb +782 -0
  180. data/spec/requests/as_content_owner/video_spec.rb +1239 -0
  181. data/spec/requests/as_server_app/channel_spec.rb +74 -0
  182. data/spec/requests/as_server_app/playlist_item_spec.rb +30 -0
  183. data/spec/requests/as_server_app/playlist_spec.rb +53 -0
  184. data/spec/requests/as_server_app/video_spec.rb +58 -0
  185. data/spec/requests/as_server_app/videos_spec.rb +40 -0
  186. data/spec/requests/unauthenticated/video_spec.rb +22 -0
  187. data/spec/spec_helper.rb +20 -0
  188. data/spec/support/fail_matcher.rb +15 -0
  189. data/spec/support/global_hooks.rb +48 -0
  190. data/yt.gemspec +32 -0
  191. metadata +416 -0
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+ require 'yt/models/account'
4
+
5
+ describe Yt::Account, :partner do
6
+ subject(:account) { Yt::Account.new id: id, authentication: $content_owner.authentication }
7
+
8
+ describe '.content_owners' do
9
+ let(:content_owners) { account.content_owners }
10
+
11
+ context 'given a partenered account with content owners', :partner do
12
+ let(:id) { $content_owner.id }
13
+
14
+ specify 'returns the associated content owners' do
15
+ expect(content_owners.size).to be > 0
16
+ expect(content_owners.first).to be_a Yt::ContentOwner
17
+ end
18
+
19
+ specify 'ensures the content owners have the same credentials as the account' do
20
+ expect(content_owners.first.access_token).to eq account.access_token
21
+ expect(content_owners.first.refresh_token).to eq account.refresh_token
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/advertising_options_set'
3
+
4
+ describe Yt::AdvertisingOptionsSet, :partner do
5
+ subject(:advertising_options_set) { Yt::AdvertisingOptionsSet.new video_id: video_id, auth: $content_owner }
6
+
7
+ context 'given a video managed by the authenticated Content Owner' do
8
+ let(:video_id) { ENV['YT_TEST_PARTNER_CLAIMABLE_VIDEO_ID'] }
9
+
10
+ describe 'the advertising options can be updated' do
11
+ let(:params) { {ad_formats: %w(standard_instream long)} }
12
+ it { expect(advertising_options_set.update params).to be true }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/content_owner'
3
+
4
+ describe Yt::Asset, :partner do
5
+ describe '.ownership' do
6
+ let(:asset) { Yt::Asset.new id: asset_id, auth: $content_owner }
7
+ describe 'given an asset administered by the content owner' do
8
+ let(:asset_id) { ENV['YT_TEST_PARTNER_ASSET_ID'] }
9
+
10
+ specify 'the ownership can be obtained' do
11
+ expect(asset.ownership).to be_a Yt::Ownership
12
+ end
13
+
14
+ describe 'the asset can be updated' do
15
+ let(:attrs) { {metadata_mine: {notes: 'Yt notes'}} }
16
+ it { expect(asset.update attrs).to be true }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,1934 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+ require 'yt/models/channel'
4
+ require 'yt/models/playlist'
5
+
6
+ describe Yt::Channel, :partner do
7
+ subject(:channel) { Yt::Channel.new id: id, auth: $content_owner }
8
+
9
+ context 'given a partnered channel', :partner do
10
+ context 'managed by the authenticated Content Owner' do
11
+ let(:id) { ENV['YT_TEST_PARTNER_CHANNEL_ID'] }
12
+
13
+ describe 'multiple reports can be retrieved at once' do
14
+ metrics = {views: Integer, uniques: Integer,
15
+ estimated_minutes_watched: Integer, comments: Integer, likes: Integer,
16
+ dislikes: Integer, shares: Integer, subscribers_gained: Integer,
17
+ subscribers_lost: Integer, favorites_added: Integer,
18
+ favorites_removed: Integer, average_view_duration: Integer,
19
+ average_view_percentage: Float, annotation_clicks: Integer,
20
+ annotation_click_through_rate: Float,
21
+ annotation_close_rate: Float, earnings: Float, impressions: Integer,
22
+ monetized_playbacks: Integer, playback_based_cpm: Float}
23
+
24
+ specify 'by day, and are chronologically sorted' do
25
+ range = {since: 5.days.ago.to_date, until: 3.days.ago.to_date}
26
+ result = channel.reports range.merge(only: metrics, by: :day)
27
+ metrics.each do |metric, type|
28
+ expect(result[metric].keys).to all(be_a Date)
29
+ expect(result[metric].values).to all(be_a type)
30
+ expect(result[metric].keys.sort).to eq result[metric].keys
31
+ end
32
+ end
33
+
34
+ specify 'by month, and are chronologically sorted' do
35
+ result = channel.reports only: metrics, by: :month, since: 1.month.ago
36
+ metrics.each do |metric, type|
37
+ expect(result[metric].keys).to all(be_a Range)
38
+ expect(result[metric].keys.map &:first).to all(be_a Date)
39
+ expect(result[metric].keys.map &:first).to eq result[metric].keys.map(&:first).map(&:beginning_of_month)
40
+ expect(result[metric].keys.map &:last).to all(be_a Date)
41
+ expect(result[metric].keys.map &:last).to eq result[metric].keys.map(&:last).map(&:end_of_month)
42
+ expect(result[metric].values).to all(be_a type)
43
+ end
44
+ end
45
+
46
+ specify 'by week' do
47
+ range = {since: ENV['YT_TEST_PARTNER_VIDEO_DATE'], until: Date.parse(ENV['YT_TEST_PARTNER_VIDEO_DATE']) + 9}
48
+ result = channel.reports range.merge(only: metrics, by: :week)
49
+ metrics.each do |metric, type|
50
+ expect(result[metric].size).to be <= 2
51
+ expect(result[metric].keys).to all(be_a Range)
52
+ expect(result[metric].keys.map{|range| range.first.wday}.uniq).to be_one
53
+ expect(result[metric].keys.map{|range| range.last.wday}.uniq).to be_one
54
+ expect(result[metric].values).to all(be_a type)
55
+ end
56
+ end
57
+ end
58
+
59
+ [:views, :uniques, :comments, :likes, :dislikes, :shares,
60
+ :subscribers_gained, :subscribers_lost, :favorites_added,
61
+ :favorites_removed, :estimated_minutes_watched, :average_view_duration,
62
+ :average_view_percentage, :impressions, :monetized_playbacks,
63
+ :annotation_clicks, :annotation_click_through_rate, :playback_based_cpm,
64
+ :annotation_close_rate, :earnings].each do |metric|
65
+ describe "#{metric} can be retrieved for a range of days" do
66
+ let(:date_in) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
67
+ let(:date_out) { Date.parse(ENV['YT_TEST_PARTNER_VIDEO_DATE']) + 5 }
68
+ let(:metric) { metric }
69
+ let(:result) { channel.public_send metric, options }
70
+
71
+ context 'with a given start and end (:since/:until option)' do
72
+ let(:options) { {by: :day, since: date_in, until: date_out} }
73
+ specify do
74
+ expect(result.keys.min).to eq date_in.to_date
75
+ expect(result.keys.max).to eq date_out.to_date
76
+ end
77
+ end
78
+
79
+ context 'with a given start and end (:from/:to option)' do
80
+ let(:options) { {by: :day, from: date_in, to: date_out} }
81
+ specify do
82
+ expect(result.keys.min).to eq date_in.to_date
83
+ expect(result.keys.max).to eq date_out.to_date
84
+ end
85
+ end
86
+ end
87
+
88
+ describe "#{metric} can be grouped by month" do
89
+ let(:metric) { metric }
90
+
91
+ let(:result) { channel.public_send metric, by: :month, since: 3.months.ago }
92
+ specify do
93
+ expect(result.keys).to eq(result.keys.sort_by{|range| range.first})
94
+ expect(result.keys).to all(be_a Range)
95
+ expect(result.keys.map &:first).to all(be_a Date)
96
+ expect(result.keys.map &:first).to eq result.keys.map(&:first).map(&:beginning_of_month)
97
+ expect(result.keys.map &:last).to all(be_a Date)
98
+ expect(result.keys.map &:last).to eq result.keys.map(&:last).map(&:end_of_month)
99
+ end
100
+ end
101
+
102
+ describe "#{metric} can be grouped by week and returns non-overlapping periods" do
103
+ let(:metric) { metric }
104
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE'], until: Date.parse(ENV['YT_TEST_PARTNER_VIDEO_DATE']) + 9} }
105
+ let(:result) { channel.public_send metric, range.merge(by: :week)}
106
+ specify do
107
+ expect(result.size).to be <= 2
108
+ expect(result.keys).to all(be_a Range)
109
+ expect(result.keys.map{|range| range.first.wday}.uniq).to be_one
110
+ expect(result.keys.map{|range| range.last.wday}.uniq).to be_one
111
+ end
112
+ end
113
+ end
114
+
115
+ {views: Integer, comments: Integer, likes: Integer, dislikes: Integer,
116
+ subscribers_gained: Integer, subscribers_lost: Integer,
117
+ estimated_minutes_watched: Integer, average_view_duration: Integer,
118
+ annotation_clicks: Integer, annotation_click_through_rate: Float,
119
+ favorites_added: Integer, favorites_removed: Integer,
120
+ average_view_percentage: Float, impressions: Integer,
121
+ shares: Integer, playback_based_cpm: Float,
122
+ monetized_playbacks: Integer, annotation_close_rate: Float,
123
+ earnings: Float}.each do |metric, type|
124
+ describe "#{metric} can be retrieved for a specific day" do
125
+ let(:metric) { metric }
126
+ let(:result) { channel.public_send "#{metric}_on", date }
127
+
128
+ context 'in which the channel had data for the report' do
129
+ let(:date) { Date.parse(ENV['YT_TEST_PARTNER_VIDEO_DATE']) + 95 }
130
+ it { expect(result).to be_a type }
131
+ end
132
+
133
+ context 'in which the channel was not partnered' do
134
+ let(:date) { 5.days.from_now }
135
+ it { expect(result).to be_nil }
136
+ end
137
+ end
138
+
139
+ describe "#{metric} can be grouped by range" do
140
+ let(:metric) { metric }
141
+
142
+ context 'without a :by option (default)' do
143
+ let(:result) { channel.public_send metric }
144
+ specify do
145
+ expect(result.size).to be 1
146
+ expect(result[:total]).to be_a type
147
+ end
148
+ end
149
+
150
+ context 'with the :by option set to :range' do
151
+ let(:result) { channel.public_send metric, by: :range }
152
+ specify do
153
+ expect(result.size).to be 1
154
+ expect(result[:total]).to be_a type
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ describe 'earnings can be retrieved for a specific day' do
161
+ # NOTE: This test sounds redundant, but it’s actually a reflection of
162
+ # another irrational behavior of YouTube API. In short, if you ask for
163
+ # the "earnings" metric of a day in which a channel made 0 USD, then
164
+ # the API returns "nil". But if you also for the "earnings" metric AND
165
+ # the "estimatedMinutesWatched" metric, then the API returns the
166
+ # correct value of "0", while still returning nil for those days in
167
+ # which the earnings have not been estimated yet.
168
+ context 'in which the channel did not make any money' do
169
+ let(:zero_date) { ENV['YT_TEST_PARTNER_CHANNEL_NO_EARNINGS_DATE'] }
170
+ let(:earnings) { channel.earnings_on zero_date}
171
+ it { expect(earnings).to eq 0 }
172
+ end
173
+ end
174
+
175
+ describe 'earnings can be retrieved for a single country' do
176
+ let(:country_code) { 'US' }
177
+ let(:earnings) { channel.earnings since: date, by: by, in: location }
178
+ let(:date) { 4.days.ago }
179
+
180
+ context 'and grouped by day' do
181
+ let(:by) { :day }
182
+
183
+ context 'with the :in option set to the country code' do
184
+ let(:location) { country_code }
185
+ it { expect(earnings.keys.min).to eq date.to_date }
186
+ end
187
+
188
+ context 'with the :in option set to {country: country code}' do
189
+ let(:location) { {country: country_code} }
190
+ it { expect(earnings.keys.min).to eq date.to_date }
191
+ end
192
+ end
193
+
194
+ context 'and grouped by country' do
195
+ let(:by) { :country }
196
+
197
+ context 'with the :in option set to the country code' do
198
+ let(:location) { country_code }
199
+ it { expect(earnings.keys).to eq [country_code] }
200
+ end
201
+
202
+ context 'with the :in option set to {country: country code}' do
203
+ let(:location) { {country: country_code} }
204
+ it { expect(earnings.keys).to eq [country_code] }
205
+ end
206
+ end
207
+ end
208
+
209
+ describe 'earnings can be grouped by day' do
210
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
211
+ let(:keys) { range.values }
212
+
213
+ specify 'with the :by option set to :day' do
214
+ earnings = channel.earnings range.merge by: :day
215
+ expect(earnings.keys).to eq range.values
216
+ end
217
+ end
218
+
219
+ describe 'earnings can be grouped by country' do
220
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
221
+
222
+ specify 'with the :by option set to :country' do
223
+ earnings = channel.earnings range.merge by: :country
224
+ expect(earnings.keys).to all(be_a String)
225
+ expect(earnings.keys.map(&:length).uniq).to eq [2]
226
+ expect(earnings.values).to all(be_a Float)
227
+ end
228
+ end
229
+
230
+ describe 'views can be retrieved for a single country' do
231
+ let(:country_code) { 'US' }
232
+ let(:views) { channel.views since: date, by: by, in: location }
233
+ let(:date) { 4.days.ago }
234
+
235
+ context 'and grouped by day' do
236
+ let(:by) { :day }
237
+
238
+ context 'with the :in option set to the country code' do
239
+ let(:location) { country_code }
240
+ it { expect(views.keys.min).to eq date.to_date }
241
+ end
242
+
243
+ context 'with the :in option set to {country: country code}' do
244
+ let(:location) { {country: country_code} }
245
+ it { expect(views.keys.min).to eq date.to_date }
246
+ end
247
+ end
248
+
249
+ context 'and grouped by country' do
250
+ let(:by) { :country }
251
+
252
+ context 'with the :in option set to the country code' do
253
+ let(:location) { country_code }
254
+ it { expect(views.keys).to eq [country_code] }
255
+ end
256
+
257
+ context 'with the :in option set to {country: country code}' do
258
+ let(:location) { {country: country_code} }
259
+ it { expect(views.keys).to eq [country_code] }
260
+ end
261
+ end
262
+
263
+ context 'and grouped by state' do
264
+ let(:by) { :state }
265
+
266
+ context 'with the :in option set to the country code' do
267
+ let(:location) { country_code }
268
+ it { expect(views.keys.map(&:length).uniq).to eq [2] }
269
+ end
270
+
271
+ context 'with the :in option set to {country: country code}' do
272
+ let(:location) { {country: country_code} }
273
+ it { expect(views.keys.map(&:length).uniq).to eq [2] }
274
+ end
275
+ end
276
+ end
277
+
278
+ describe 'views can be retrieved for a single US state' do
279
+ let(:state_code) { 'NY' }
280
+ let(:result) { channel.views since: date, by: by, in: location }
281
+ let(:date) { 4.days.ago }
282
+
283
+ context 'and grouped by day' do
284
+ let(:by) { :day }
285
+
286
+ context 'with the :in option set to {state: state code}' do
287
+ let(:location) { {state: state_code} }
288
+ it { expect(result.keys.min).to eq date.to_date }
289
+ end
290
+
291
+ context 'with the :in option set to {country: "US", state: state code}' do
292
+ let(:location) { {country: 'US', state: state_code} }
293
+ it { expect(result.keys.min).to eq date.to_date }
294
+ end
295
+ end
296
+
297
+ context 'and grouped by US state' do
298
+ let(:by) { :state }
299
+
300
+ context 'with the :in option set to {state: state code}' do
301
+ let(:location) { {state: state_code} }
302
+ it { expect(result.keys).to eq [state_code] }
303
+ end
304
+
305
+ context 'with the :in option set to {country: "US", state: state code}' do
306
+ let(:location) { {country: 'US', state: state_code} }
307
+ it { expect(result.keys).to eq [state_code] }
308
+ end
309
+ end
310
+ end
311
+
312
+ describe 'views can be grouped by day' do
313
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
314
+ let(:keys) { range.values }
315
+
316
+ specify 'with the :by option set to :day' do
317
+ views = channel.views range.merge by: :day
318
+ expect(views.keys).to eq range.values
319
+ end
320
+
321
+ specify 'and are returned chronologically sorted' do
322
+ views = channel.views range.merge by: :day
323
+ expect(views.keys.sort).to eq views.keys
324
+ end
325
+ end
326
+
327
+ describe 'views can be grouped by traffic source' do
328
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
329
+ let(:keys) { Yt::Collections::Reports::TRAFFIC_SOURCES.keys }
330
+
331
+ specify 'with the :by option set to :traffic_source' do
332
+ views = channel.views range.merge by: :traffic_source
333
+ expect(views.keys - keys).to be_empty
334
+ end
335
+
336
+ specify 'and are returned sorted by descending views' do
337
+ views = channel.views range.merge by: :traffic_source
338
+ expect(views.values.sort.reverse).to eq views.values
339
+ end
340
+ end
341
+
342
+ describe 'views can be grouped by playback location' do
343
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
344
+ let(:keys) { Yt::Collections::Reports::PLAYBACK_LOCATIONS.keys }
345
+
346
+ specify 'with the :by option set to :playback_location' do
347
+ views = channel.views range.merge by: :playback_location
348
+ expect(views.keys - keys).to be_empty
349
+ end
350
+
351
+ specify 'and are returned sorted by descending views' do
352
+ views = channel.views range.merge by: :playback_location
353
+ expect(views.values.sort.reverse).to eq views.values
354
+ end
355
+ end
356
+
357
+ describe 'views can be grouped by embedded player location' do
358
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
359
+
360
+ specify 'with the :by option set to :embedded_player_location' do
361
+ views = channel.views range.merge by: :embedded_player_location
362
+ expect(views).not_to be_empty
363
+ end
364
+ end
365
+
366
+ describe 'views can be grouped by related video' do
367
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
368
+
369
+ specify 'with the :by option set to :related_video' do
370
+ views = channel.views range.merge by: :related_video
371
+ expect(views.keys).to all(be_instance_of Yt::Video)
372
+ end
373
+
374
+ specify 'and provided with an :includes option to preload parts' do
375
+ views = channel.views range.merge by: :related_video, includes: [:statistics]
376
+ expect(views.keys.map{|v| v.instance_variable_defined? :@status}).to all(be false)
377
+ expect(views.keys.map{|v| v.instance_variable_defined? :@statistics_set}).to all(be true)
378
+ end
379
+ end
380
+
381
+ describe 'views can be grouped by search term' do
382
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
383
+
384
+ specify 'with the :by option set to :search_term' do
385
+ views = channel.views range.merge by: :search_term
386
+ expect(views.keys).to all(be_a String)
387
+ end
388
+ end
389
+
390
+ describe 'views can be grouped by referrer' do
391
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
392
+
393
+ specify 'with the :by option set to :referrer' do
394
+ views = channel.views range.merge by: :referrer
395
+ expect(views.keys).to all(be_a String)
396
+ end
397
+ end
398
+
399
+ describe 'views can be grouped by video' do
400
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
401
+
402
+ specify 'with the :by option set to :video' do
403
+ views = channel.views range.merge by: :video
404
+ expect(views.keys).to all(be_instance_of Yt::Video)
405
+ end
406
+
407
+ specify 'and provided with an :includes option to preload parts' do
408
+ views = channel.views range.merge by: :video, includes: [:statistics]
409
+ expect(views.keys.map{|v| v.instance_variable_defined? :@status}).to all(be false)
410
+ expect(views.keys.map{|v| v.instance_variable_defined? :@statistics_set}).to all(be true)
411
+ end
412
+ end
413
+
414
+ describe 'views can be grouped by playlist' do
415
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
416
+
417
+ specify 'with the :by option set to :playlist' do
418
+ views = channel.views range.merge by: :playlist
419
+ expect(views.keys).to all(be_instance_of Yt::Playlist)
420
+ end
421
+
422
+ specify 'and provided with an :includes option to preload parts' do
423
+ views = channel.views range.merge by: :playlist, includes: [:status]
424
+ expect(views.keys.map{|playlist| playlist.instance_variable_defined? :@content_details}).to all(be false)
425
+ expect(views.keys.map{|playlist| playlist.instance_variable_defined? :@status}).to all(be true)
426
+ end
427
+ end
428
+
429
+ describe 'views can be grouped by device type' do
430
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
431
+
432
+ specify 'with the :by option set to :device_type' do
433
+ views = channel.views range.merge by: :device_type
434
+ expect(views.keys).to all(be_instance_of Symbol)
435
+ expect(views.values).to all(be_an Integer)
436
+ end
437
+
438
+ specify 'and are returned sorted by descending views' do
439
+ views = channel.views range.merge by: :device_type
440
+ expect(views.values.sort.reverse).to eq views.values
441
+ end
442
+ end
443
+
444
+ describe 'views can be grouped by country' do
445
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
446
+
447
+ specify 'with the :by option set to :country' do
448
+ views = channel.views range.merge by: :country
449
+ expect(views.keys).to all(be_a String)
450
+ expect(views.keys.map(&:length).uniq).to eq [2]
451
+ expect(views.values).to all(be_an Integer)
452
+ end
453
+
454
+ specify 'and are returned sorted by descending views' do
455
+ views = channel.views range.merge by: :country
456
+ expect(views.values.sort.reverse).to eq views.values
457
+ end
458
+ end
459
+
460
+ describe 'views can be grouped by state' do
461
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
462
+
463
+ specify 'with the :by option set to :state' do
464
+ views = channel.views range.merge by: :state
465
+ expect(views.keys).to all(be_a String)
466
+ expect(views.keys.map(&:length).uniq).to eq [2]
467
+ expect(views.values).to all(be_an Integer)
468
+ end
469
+
470
+ specify 'and are returned sorted by descending views' do
471
+ views = channel.views range.merge by: :state
472
+ expect(views.values.sort.reverse).to eq views.values
473
+ end
474
+ end
475
+
476
+ describe 'views can be limited to a subset of videos' do
477
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
478
+ let(:videos) { channel.videos.first(2) }
479
+ let(:video_views) { videos.inject(0){|total, video| total + video.views(range)[:total]} }
480
+
481
+ specify 'with the :videos option listing the video IDs' do
482
+ views = channel.views range.merge videos: videos.map(&:id)
483
+ expect(views[:total]).to eq video_views
484
+ end
485
+
486
+ specify 'with a maximum of 200 video IDs' do
487
+ views = channel.views range.merge videos: (videos*100).map(&:id)
488
+ expect(views[:total]).to eq video_views
489
+ end
490
+
491
+ specify 'but fails with more than 200 video IDs' do
492
+ expect{channel.views range.merge videos: (videos*101).map(&:id)}.to raise_error Yt::Errors::RequestError
493
+ end
494
+ end
495
+
496
+ describe 'uniques can be retrieved for a single country' do
497
+ let(:country_code) { 'US' }
498
+ let(:uniques) { channel.uniques since: date, by: by, in: location }
499
+ let(:date) { 4.days.ago }
500
+
501
+ context 'and grouped by day' do
502
+ let(:by) { :day }
503
+
504
+ context 'with the :in option set to the country code' do
505
+ let(:location) { country_code }
506
+ it { expect(uniques.keys.min).to eq date.to_date }
507
+ end
508
+
509
+ context 'with the :in option set to {country: country code}' do
510
+ let(:location) { {country: country_code} }
511
+ it { expect(uniques.keys.min).to eq date.to_date }
512
+ end
513
+ end
514
+ end
515
+
516
+ describe 'uniques can be retrieved for a single US state' do
517
+ let(:state_code) { 'NY' }
518
+ let(:result) { channel.uniques since: date, by: by, in: location }
519
+ let(:date) { 4.days.ago }
520
+
521
+ context 'and grouped by day' do
522
+ let(:by) { :day }
523
+
524
+ context 'with the :in option set to {state: state code}' do
525
+ let(:location) { {state: state_code} }
526
+ it { expect(result.keys.min).to eq date.to_date }
527
+ end
528
+
529
+ context 'with the :in option set to {country: "US", state: state code}' do
530
+ let(:location) { {country: 'US', state: state_code} }
531
+ it { expect(result.keys.min).to eq date.to_date }
532
+ end
533
+ end
534
+ end
535
+
536
+ describe 'uniques can be grouped by day' do
537
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
538
+ let(:keys) { range.values }
539
+
540
+ specify 'with the :by option set to :day' do
541
+ uniques = channel.uniques range.merge by: :day
542
+ expect(uniques.keys).to eq range.values
543
+ end
544
+ end
545
+
546
+ describe 'comments can be retrieved for a single country' do
547
+ let(:country_code) { 'US' }
548
+ let(:comments) { channel.comments since: date, by: by, in: location }
549
+ let(:date) { 4.days.ago }
550
+
551
+ context 'and grouped by day' do
552
+ let(:by) { :day }
553
+
554
+ context 'with the :in option set to the country code' do
555
+ let(:location) { country_code }
556
+ it { expect(comments.keys.min).to eq date.to_date }
557
+ end
558
+
559
+ context 'with the :in option set to {country: country code}' do
560
+ let(:location) { {country: country_code} }
561
+ it { expect(comments.keys.min).to eq date.to_date }
562
+ end
563
+ end
564
+
565
+ context 'and grouped by country' do
566
+ let(:by) { :country }
567
+
568
+ context 'with the :in option set to the country code' do
569
+ let(:location) { country_code }
570
+ it { expect(comments.keys).to eq [country_code] }
571
+ end
572
+
573
+ context 'with the :in option set to {country: country code}' do
574
+ let(:location) { {country: country_code} }
575
+ it { expect(comments.keys).to eq [country_code] }
576
+ end
577
+ end
578
+ end
579
+
580
+ describe 'comments can be grouped by day' do
581
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
582
+ let(:keys) { range.values }
583
+
584
+ specify 'with the :by option set to :day' do
585
+ comments = channel.comments range.merge by: :day
586
+ expect(comments.keys).to eq range.values
587
+ end
588
+ end
589
+
590
+ describe 'comments can be grouped by country' do
591
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
592
+
593
+ specify 'with the :by option set to :country' do
594
+ comments = channel.comments range.merge by: :country
595
+ expect(comments.keys).to all(be_a String)
596
+ expect(comments.keys.map(&:length).uniq).to eq [2]
597
+ expect(comments.values).to all(be_an Integer)
598
+ end
599
+ end
600
+
601
+ describe 'likes can be retrieved for a single country' do
602
+ let(:country_code) { 'US' }
603
+ let(:likes) { channel.likes since: date, by: by, in: location }
604
+ let(:date) { 4.days.ago }
605
+
606
+ context 'and grouped by day' do
607
+ let(:by) { :day }
608
+
609
+ context 'with the :in option set to the country code' do
610
+ let(:location) { country_code }
611
+ it { expect(likes.keys.min).to eq date.to_date }
612
+ end
613
+
614
+ context 'with the :in option set to {country: country code}' do
615
+ let(:location) { {country: country_code} }
616
+ it { expect(likes.keys.min).to eq date.to_date }
617
+ end
618
+ end
619
+
620
+ context 'and grouped by country' do
621
+ let(:by) { :country }
622
+
623
+ context 'with the :in option set to the country code' do
624
+ let(:location) { country_code }
625
+ it { expect(likes.keys).to eq [country_code] }
626
+ end
627
+
628
+ context 'with the :in option set to {country: country code}' do
629
+ let(:location) { {country: country_code} }
630
+ it { expect(likes.keys).to eq [country_code] }
631
+ end
632
+ end
633
+ end
634
+
635
+ describe 'likes can be grouped by day' do
636
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
637
+ let(:keys) { range.values }
638
+
639
+ specify 'with the :by option set to :day' do
640
+ likes = channel.likes range.merge by: :day
641
+ expect(likes.keys).to eq range.values
642
+ end
643
+ end
644
+
645
+ describe 'likes can be grouped by country' do
646
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
647
+
648
+ specify 'with the :by option set to :country' do
649
+ likes = channel.likes range.merge by: :country
650
+ expect(likes.keys).to all(be_a String)
651
+ expect(likes.keys.map(&:length).uniq).to eq [2]
652
+ expect(likes.values).to all(be_an Integer)
653
+ end
654
+ end
655
+
656
+ describe 'dislikes can be retrieved for a single country' do
657
+ let(:country_code) { 'US' }
658
+ let(:dislikes) { channel.dislikes since: date, by: by, in: location }
659
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
660
+
661
+ context 'and grouped by day' do
662
+ let(:by) { :day }
663
+
664
+ context 'with the :in option set to the country code' do
665
+ let(:location) { country_code }
666
+ it { expect(dislikes.keys.min).to eq date.to_date }
667
+ end
668
+
669
+ context 'with the :in option set to {country: country code}' do
670
+ let(:location) { {country: country_code} }
671
+ it { expect(dislikes.keys.min).to eq date.to_date }
672
+ end
673
+ end
674
+
675
+ context 'and grouped by country' do
676
+ let(:by) { :country }
677
+
678
+ context 'with the :in option set to the country code' do
679
+ let(:location) { country_code }
680
+ it { expect(dislikes.keys).to eq [country_code] }
681
+ end
682
+
683
+ context 'with the :in option set to {country: country code}' do
684
+ let(:location) { {country: country_code} }
685
+ it { expect(dislikes.keys).to eq [country_code] }
686
+ end
687
+ end
688
+ end
689
+
690
+ describe 'dislikes can be grouped by day' do
691
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
692
+ let(:keys) { range.values }
693
+
694
+ specify 'with the :by option set to :day' do
695
+ dislikes = channel.dislikes range.merge by: :day
696
+ expect(dislikes.keys).to eq range.values
697
+ end
698
+ end
699
+
700
+ describe 'dislikes can be grouped by country' do
701
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
702
+
703
+ specify 'with the :by option set to :country' do
704
+ dislikes = channel.dislikes range.merge by: :country
705
+ expect(dislikes.keys).to all(be_a String)
706
+ expect(dislikes.keys.map(&:length).uniq).to eq [2]
707
+ expect(dislikes.values).to all(be_an Integer)
708
+ end
709
+ end
710
+
711
+ describe 'shares can be retrieved for a single country' do
712
+ let(:country_code) { 'US' }
713
+ let(:shares) { channel.shares since: date, by: by, in: location }
714
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
715
+
716
+ context 'and grouped by day' do
717
+ let(:by) { :day }
718
+
719
+ context 'with the :in option set to the country code' do
720
+ let(:location) { country_code }
721
+ it { expect(shares.keys.min).to eq date.to_date }
722
+ end
723
+
724
+ context 'with the :in option set to {country: country code}' do
725
+ let(:location) { {country: country_code} }
726
+ it { expect(shares.keys.min).to eq date.to_date }
727
+ end
728
+ end
729
+
730
+ context 'and grouped by country' do
731
+ let(:by) { :country }
732
+
733
+ context 'with the :in option set to the country code' do
734
+ let(:location) { country_code }
735
+ it { expect(shares.keys).to eq [country_code] }
736
+ end
737
+
738
+ context 'with the :in option set to {country: country code}' do
739
+ let(:location) { {country: country_code} }
740
+ it { expect(shares.keys).to eq [country_code] }
741
+ end
742
+ end
743
+ end
744
+
745
+ describe 'shares can be grouped by day' do
746
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
747
+ let(:keys) { range.values }
748
+
749
+ specify 'with the :by option set to :day' do
750
+ shares = channel.shares range.merge by: :day
751
+ expect(shares.keys).to eq range.values
752
+ end
753
+ end
754
+
755
+ describe 'shares can be grouped by country' do
756
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
757
+
758
+ specify 'with the :by option set to :country' do
759
+ shares = channel.shares range.merge by: :country
760
+ expect(shares.keys).to all(be_a String)
761
+ expect(shares.keys.map(&:length).uniq).to eq [2]
762
+ expect(shares.values).to all(be_an Integer)
763
+ end
764
+ end
765
+
766
+ describe 'gained subscribers can be retrieved for a single country' do
767
+ let(:country_code) { 'US' }
768
+ let(:subscribers_gained) { channel.subscribers_gained since: date, by: by, in: location }
769
+ let(:date) { 4.days.ago }
770
+
771
+ context 'and grouped by day' do
772
+ let(:by) { :day }
773
+
774
+ context 'with the :in option set to the country code' do
775
+ let(:location) { country_code }
776
+ it { expect(subscribers_gained.keys.min).to eq date.to_date }
777
+ end
778
+
779
+ context 'with the :in option set to {country: country code}' do
780
+ let(:location) { {country: country_code} }
781
+ it { expect(subscribers_gained.keys.min).to eq date.to_date }
782
+ end
783
+ end
784
+
785
+ context 'and grouped by country' do
786
+ let(:by) { :country }
787
+
788
+ context 'with the :in option set to the country code' do
789
+ let(:location) { country_code }
790
+ it { expect(subscribers_gained.keys).to eq [country_code] }
791
+ end
792
+
793
+ context 'with the :in option set to {country: country code}' do
794
+ let(:location) { {country: country_code} }
795
+ it { expect(subscribers_gained.keys).to eq [country_code] }
796
+ end
797
+ end
798
+ end
799
+
800
+ describe 'gained subscribers can be grouped by day' do
801
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
802
+ let(:keys) { range.values }
803
+
804
+ specify 'with the :by option set to :day' do
805
+ subscribers_gained = channel.subscribers_gained range.merge by: :day
806
+ expect(subscribers_gained.keys).to eq range.values
807
+ end
808
+ end
809
+
810
+ describe 'gained subscribers can be grouped by country' do
811
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
812
+
813
+ specify 'with the :by option set to :country' do
814
+ subscribers_gained = channel.subscribers_gained range.merge by: :country
815
+ expect(subscribers_gained.keys).to all(be_a String)
816
+ expect(subscribers_gained.keys.map(&:length).uniq).to eq [2]
817
+ expect(subscribers_gained.values).to all(be_an Integer)
818
+ end
819
+ end
820
+
821
+ describe 'lost subscribers can be retrieved for a single country' do
822
+ let(:country_code) { 'US' }
823
+ let(:subscribers_lost) { channel.subscribers_lost since: date, by: by, in: location }
824
+ let(:date) { 4.days.ago }
825
+
826
+ context 'and grouped by day' do
827
+ let(:by) { :day }
828
+
829
+ context 'with the :in option set to the country code' do
830
+ let(:location) { country_code }
831
+ it { expect(subscribers_lost.keys.min).to eq date.to_date }
832
+ end
833
+
834
+ context 'with the :in option set to {country: country code}' do
835
+ let(:location) { {country: country_code} }
836
+ it { expect(subscribers_lost.keys.min).to eq date.to_date }
837
+ end
838
+ end
839
+
840
+ context 'and grouped by country' do
841
+ let(:by) { :country }
842
+
843
+ context 'with the :in option set to the country code' do
844
+ let(:location) { country_code }
845
+ it { expect(subscribers_lost.keys).to eq [country_code] }
846
+ end
847
+
848
+ context 'with the :in option set to {country: country code}' do
849
+ let(:location) { {country: country_code} }
850
+ it { expect(subscribers_lost.keys).to eq [country_code] }
851
+ end
852
+ end
853
+ end
854
+
855
+ describe 'lost subscribers can be grouped by day' do
856
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
857
+ let(:keys) { range.values }
858
+
859
+ specify 'with the :by option set to :day' do
860
+ subscribers_lost = channel.subscribers_lost range.merge by: :day
861
+ expect(subscribers_lost.keys).to eq range.values
862
+ end
863
+ end
864
+
865
+ describe 'lost subscribers can be grouped by country' do
866
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
867
+
868
+ specify 'with the :by option set to :country' do
869
+ subscribers_lost = channel.subscribers_lost range.merge by: :country
870
+ expect(subscribers_lost.keys).to all(be_a String)
871
+ expect(subscribers_lost.keys.map(&:length).uniq).to eq [2]
872
+ expect(subscribers_lost.values).to all(be_an Integer)
873
+ end
874
+ end
875
+
876
+ describe 'favorites added can be retrieved for a single country' do
877
+ let(:country_code) { 'US' }
878
+ let(:favorites_added) { channel.favorites_added since: date, by: by, in: location }
879
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
880
+
881
+ context 'and grouped by day' do
882
+ let(:by) { :day }
883
+
884
+ context 'with the :in option set to the country code' do
885
+ let(:location) { country_code }
886
+ it { expect(favorites_added.keys.min).to eq date.to_date }
887
+ end
888
+
889
+ context 'with the :in option set to {country: country code}' do
890
+ let(:location) { {country: country_code} }
891
+ it { expect(favorites_added.keys.min).to eq date.to_date }
892
+ end
893
+ end
894
+
895
+ context 'and grouped by country' do
896
+ let(:by) { :country }
897
+
898
+ context 'with the :in option set to the country code' do
899
+ let(:location) { country_code }
900
+ it { expect(favorites_added.keys).to eq [country_code] }
901
+ end
902
+
903
+ context 'with the :in option set to {country: country code}' do
904
+ let(:location) { {country: country_code} }
905
+ it { expect(favorites_added.keys).to eq [country_code] }
906
+ end
907
+ end
908
+ end
909
+
910
+ describe 'added favorites can be grouped by day' do
911
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
912
+ let(:keys) { range.values }
913
+
914
+ specify 'with the :by option set to :day' do
915
+ favorites_added = channel.favorites_added range.merge by: :day
916
+ expect(favorites_added.keys).to eq range.values
917
+ end
918
+ end
919
+
920
+ describe 'added favorites can be grouped by country' do
921
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
922
+
923
+ specify 'with the :by option set to :country' do
924
+ favorites_added = channel.favorites_added range.merge by: :country
925
+ expect(favorites_added.keys).to all(be_a String)
926
+ expect(favorites_added.keys.map(&:length).uniq).to eq [2]
927
+ expect(favorites_added.values).to all(be_an Integer)
928
+ end
929
+ end
930
+
931
+ describe 'favorites removed can be retrieved for a single country' do
932
+ let(:country_code) { 'US' }
933
+ let(:favorites_removed) { channel.favorites_removed since: date, by: by, in: location }
934
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
935
+
936
+ context 'and grouped by day' do
937
+ let(:by) { :day }
938
+
939
+ context 'with the :in option set to the country code' do
940
+ let(:location) { country_code }
941
+ it { expect(favorites_removed.keys.min).to eq date.to_date }
942
+ end
943
+
944
+ context 'with the :in option set to {country: country code}' do
945
+ let(:location) { {country: country_code} }
946
+ it { expect(favorites_removed.keys.min).to eq date.to_date }
947
+ end
948
+ end
949
+
950
+ context 'and grouped by country' do
951
+ let(:by) { :country }
952
+
953
+ context 'with the :in option set to the country code' do
954
+ let(:location) { country_code }
955
+ it { expect(favorites_removed.keys).to eq [country_code] }
956
+ end
957
+
958
+ context 'with the :in option set to {country: country code}' do
959
+ let(:location) { {country: country_code} }
960
+ it { expect(favorites_removed.keys).to eq [country_code] }
961
+ end
962
+ end
963
+ end
964
+
965
+ describe 'removed favorites can be grouped by day' do
966
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
967
+ let(:keys) { range.values }
968
+
969
+ specify 'with the :by option set to :day' do
970
+ favorites_removed = channel.favorites_removed range.merge by: :day
971
+ expect(favorites_removed.keys).to eq range.values
972
+ end
973
+ end
974
+
975
+ describe 'removed favorites can be grouped by country' do
976
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE'], until: Date.parse(ENV['YT_TEST_PARTNER_VIDEO_DATE']) + 5} }
977
+
978
+ specify 'with the :by option set to :country' do
979
+ favorites_removed = channel.favorites_removed range.merge by: :country
980
+ expect(favorites_removed.keys).to all(be_a String)
981
+ expect(favorites_removed.keys.map(&:length).uniq).to eq [2]
982
+ expect(favorites_removed.values).to all(be_an Integer)
983
+ end
984
+ end
985
+
986
+ describe 'estimated minutes watched can be retrieved for a single country' do
987
+ let(:country_code) { 'US' }
988
+ let(:estimated_minutes_watched) { channel.estimated_minutes_watched since: date, by: by, in: location }
989
+ let(:date) { 4.days.ago }
990
+
991
+ context 'and grouped by day' do
992
+ let(:by) { :day }
993
+
994
+ context 'with the :in option set to the country code' do
995
+ let(:location) { country_code }
996
+ it { expect(estimated_minutes_watched.keys.min).to eq date.to_date }
997
+ end
998
+
999
+ context 'with the :in option set to {country: country code}' do
1000
+ let(:location) { {country: country_code} }
1001
+ it { expect(estimated_minutes_watched.keys.min).to eq date.to_date }
1002
+ end
1003
+ end
1004
+
1005
+ context 'and grouped by country' do
1006
+ let(:by) { :country }
1007
+
1008
+ context 'with the :in option set to the country code' do
1009
+ let(:location) { country_code }
1010
+ it { expect(estimated_minutes_watched.keys).to eq [country_code] }
1011
+ end
1012
+
1013
+ context 'with the :in option set to {country: country code}' do
1014
+ let(:location) { {country: country_code} }
1015
+ it { expect(estimated_minutes_watched.keys).to eq [country_code] }
1016
+ end
1017
+ end
1018
+ end
1019
+
1020
+ describe 'estimated minutes watched can be retrieved for a single US state' do
1021
+ let(:state_code) { 'NY' }
1022
+ let(:result) { channel.estimated_minutes_watched since: date, by: by, in: location }
1023
+ let(:date) { 4.days.ago }
1024
+
1025
+ context 'and grouped by day' do
1026
+ let(:by) { :day }
1027
+
1028
+ context 'with the :in option set to {state: state code}' do
1029
+ let(:location) { {state: state_code} }
1030
+ it { expect(result.keys.min).to eq date.to_date }
1031
+ end
1032
+
1033
+ context 'with the :in option set to {country: "US", state: state code}' do
1034
+ let(:location) { {country: 'US', state: state_code} }
1035
+ it { expect(result.keys.min).to eq date.to_date }
1036
+ end
1037
+ end
1038
+
1039
+ context 'and grouped by US state' do
1040
+ let(:by) { :state }
1041
+
1042
+ context 'with the :in option set to {state: state code}' do
1043
+ let(:location) { {state: state_code} }
1044
+ it { expect(result.keys).to eq [state_code] }
1045
+ end
1046
+
1047
+ context 'with the :in option set to {country: "US", state: state code}' do
1048
+ let(:location) { {country: 'US', state: state_code} }
1049
+ it { expect(result.keys).to eq [state_code] }
1050
+ end
1051
+ end
1052
+ end
1053
+
1054
+ describe 'estimated minutes watched can be grouped by day' do
1055
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
1056
+ let(:keys) { range.values }
1057
+
1058
+ specify 'with the :by option set to :day' do
1059
+ estimated_minutes_watched = channel.estimated_minutes_watched range.merge by: :day
1060
+ expect(estimated_minutes_watched.keys).to eq range.values
1061
+ end
1062
+ end
1063
+
1064
+ describe 'estimated minutes watched can be grouped by traffic source' do
1065
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1066
+ let(:keys) { Yt::Collections::Reports::TRAFFIC_SOURCES.keys }
1067
+
1068
+ specify 'with the :by option set to :traffic_source' do
1069
+ estimated_minutes_watched = channel.estimated_minutes_watched range.merge by: :traffic_source
1070
+ expect(estimated_minutes_watched.keys - keys).to be_empty
1071
+ end
1072
+ end
1073
+
1074
+ describe 'estimated minutes watched can be grouped by playback location' do
1075
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1076
+ let(:keys) { Yt::Collections::Reports::PLAYBACK_LOCATIONS.keys }
1077
+
1078
+ specify 'with the :by option set to :playback_location' do
1079
+ estimated_minutes_watched = channel.estimated_minutes_watched range.merge by: :playback_location
1080
+ expect(estimated_minutes_watched.keys - keys).to be_empty
1081
+ end
1082
+ end
1083
+
1084
+ describe 'estimated minutes watched can be grouped by embedded player location' do
1085
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1086
+
1087
+ specify 'with the :by option set to :embedded_player_location' do
1088
+ estimated_minutes_watched = channel.estimated_minutes_watched range.merge by: :embedded_player_location
1089
+ expect(estimated_minutes_watched).not_to be_empty
1090
+ end
1091
+ end
1092
+
1093
+ describe 'estimated minutes watched can be grouped by related video' do
1094
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1095
+
1096
+ specify 'with the :by option set to :related_video' do
1097
+ estimated_minutes_watched = channel.estimated_minutes_watched range.merge by: :related_video
1098
+ expect(estimated_minutes_watched.keys).to all(be_instance_of Yt::Video)
1099
+ end
1100
+ end
1101
+
1102
+ describe 'estimated minutes watched can be grouped by search term' do
1103
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1104
+
1105
+ specify 'with the :by option set to :search_term' do
1106
+ estimated_minutes_watched = channel.estimated_minutes_watched range.merge by: :search_term
1107
+ expect(estimated_minutes_watched.keys).to all(be_a String)
1108
+ end
1109
+ end
1110
+
1111
+ describe 'estimated minutes watched can be grouped by referrer' do
1112
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1113
+
1114
+ specify 'with the :by option set to :referrer' do
1115
+ estimated_minutes_watched = channel.estimated_minutes_watched range.merge by: :referrer
1116
+ expect(estimated_minutes_watched.keys).to all(be_a String)
1117
+ end
1118
+ end
1119
+
1120
+ describe 'estimated minutes watched can be grouped by video' do
1121
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1122
+
1123
+ specify 'with the :by option set to :video' do
1124
+ estimated_minutes_watched = channel.estimated_minutes_watched range.merge by: :video
1125
+ expect(estimated_minutes_watched.keys).to all(be_instance_of Yt::Video)
1126
+ end
1127
+ end
1128
+
1129
+ describe 'estimated minutes watched can be grouped by playlist' do
1130
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1131
+
1132
+ specify 'with the :by option set to :playlist' do
1133
+ estimated_minutes_watched = channel.estimated_minutes_watched range.merge by: :playlist
1134
+ expect(estimated_minutes_watched.keys).to all(be_instance_of Yt::Playlist)
1135
+ end
1136
+ end
1137
+
1138
+ describe 'estimated minutes watched can be grouped by device type' do
1139
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1140
+
1141
+ specify 'with the :by option set to :device_type' do
1142
+ estimated_minutes_watched = channel.estimated_minutes_watched range.merge by: :device_type
1143
+ expect(estimated_minutes_watched.keys).to all(be_instance_of Symbol)
1144
+ expect(estimated_minutes_watched.values).to all(be_an Integer)
1145
+ end
1146
+ end
1147
+
1148
+ describe 'estimated minutes watched can be grouped by country' do
1149
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1150
+
1151
+ specify 'with the :by option set to :country' do
1152
+ minutes = channel.estimated_minutes_watched range.merge by: :country
1153
+ expect(minutes.keys).to all(be_a String)
1154
+ expect(minutes.keys.map(&:length).uniq).to eq [2]
1155
+ expect(minutes.values).to all(be_an Integer)
1156
+ end
1157
+ end
1158
+
1159
+ describe 'estimated minutes watched can be grouped by state' do
1160
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1161
+
1162
+ specify 'with the :by option set to :state' do
1163
+ minutes = channel.estimated_minutes_watched range.merge by: :state
1164
+ expect(minutes.keys).to all(be_a String)
1165
+ expect(minutes.keys.map(&:length).uniq).to eq [2]
1166
+ expect(minutes.values).to all(be_an Integer)
1167
+ end
1168
+ end
1169
+
1170
+ describe 'average view duration can be retrieved for a single country' do
1171
+ let(:country_code) { 'US' }
1172
+ let(:average_view_duration) { channel.average_view_duration since: date, by: by, in: location }
1173
+ let(:date) { 4.days.ago }
1174
+
1175
+ context 'and grouped by day' do
1176
+ let(:by) { :day }
1177
+
1178
+ context 'with the :in option set to the country code' do
1179
+ let(:location) { country_code }
1180
+ it { expect(average_view_duration.keys.min).to eq date.to_date }
1181
+ end
1182
+
1183
+ context 'with the :in option set to {country: country code}' do
1184
+ let(:location) { {country: country_code} }
1185
+ it { expect(average_view_duration.keys.min).to eq date.to_date }
1186
+ end
1187
+ end
1188
+
1189
+ context 'and grouped by country' do
1190
+ let(:by) { :country }
1191
+
1192
+ context 'with the :in option set to the country code' do
1193
+ let(:location) { country_code }
1194
+ it { expect(average_view_duration.keys).to eq [country_code] }
1195
+ end
1196
+
1197
+ context 'with the :in option set to {country: country code}' do
1198
+ let(:location) { {country: country_code} }
1199
+ it { expect(average_view_duration.keys).to eq [country_code] }
1200
+ end
1201
+ end
1202
+ end
1203
+
1204
+ describe 'average view duration can be retrieved for a single US state' do
1205
+ let(:state_code) { 'NY' }
1206
+ let(:result) { channel.average_view_duration since: date, by: by, in: location }
1207
+ let(:date) { 4.days.ago }
1208
+
1209
+ context 'and grouped by day' do
1210
+ let(:by) { :day }
1211
+
1212
+ context 'with the :in option set to {state: state code}' do
1213
+ let(:location) { {state: state_code} }
1214
+ it { expect(result.keys.min).to eq date.to_date }
1215
+ end
1216
+
1217
+ context 'with the :in option set to {country: "US", state: state code}' do
1218
+ let(:location) { {country: 'US', state: state_code} }
1219
+ it { expect(result.keys.min).to eq date.to_date }
1220
+ end
1221
+ end
1222
+
1223
+ context 'and grouped by US state' do
1224
+ let(:by) { :state }
1225
+
1226
+ context 'with the :in option set to {state: state code}' do
1227
+ let(:location) { {state: state_code} }
1228
+ it { expect(result.keys).to eq [state_code] }
1229
+ end
1230
+
1231
+ context 'with the :in option set to {country: "US", state: state code}' do
1232
+ let(:location) { {country: 'US', state: state_code} }
1233
+ it { expect(result.keys).to eq [state_code] }
1234
+ end
1235
+ end
1236
+ end
1237
+
1238
+ describe 'average view duration can be grouped by day' do
1239
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
1240
+ let(:keys) { range.values }
1241
+
1242
+ specify 'with the :by option set to :day' do
1243
+ average_view_duration = channel.average_view_duration range.merge by: :day
1244
+ expect(average_view_duration.keys).to eq range.values
1245
+ end
1246
+ end
1247
+
1248
+ describe 'average view duration can be grouped by country' do
1249
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1250
+
1251
+ specify 'with the :by option set to :country' do
1252
+ duration = channel.average_view_duration range.merge by: :country
1253
+ expect(duration.keys).to all(be_a String)
1254
+ expect(duration.keys.map(&:length).uniq).to eq [2]
1255
+ expect(duration.values).to all(be_an Integer)
1256
+ end
1257
+ end
1258
+
1259
+ describe 'average view duration can be grouped by state' do
1260
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1261
+
1262
+ specify 'with the :by option set to :state' do
1263
+ duration = channel.average_view_duration range.merge by: :state
1264
+ expect(duration.keys).to all(be_a String)
1265
+ expect(duration.keys.map(&:length).uniq).to eq [2]
1266
+ expect(duration.values).to all(be_an Integer)
1267
+ end
1268
+ end
1269
+
1270
+ describe 'average view percentage can be retrieved for a single country' do
1271
+ let(:country_code) { 'US' }
1272
+ let(:average_view_percentage) { channel.average_view_percentage since: date, by: by, in: location }
1273
+ let(:date) { 4.days.ago }
1274
+
1275
+ context 'and grouped by day' do
1276
+ let(:by) { :day }
1277
+
1278
+ context 'with the :in option set to the country code' do
1279
+ let(:location) { country_code }
1280
+ it { expect(average_view_percentage.keys.min).to eq date.to_date }
1281
+ end
1282
+
1283
+ context 'with the :in option set to {country: country code}' do
1284
+ let(:location) { {country: country_code} }
1285
+ it { expect(average_view_percentage.keys.min).to eq date.to_date }
1286
+ end
1287
+ end
1288
+
1289
+ context 'and grouped by country' do
1290
+ let(:by) { :country }
1291
+
1292
+ context 'with the :in option set to the country code' do
1293
+ let(:location) { country_code }
1294
+ it { expect(average_view_percentage.keys).to eq [country_code] }
1295
+ end
1296
+
1297
+ context 'with the :in option set to {country: country code}' do
1298
+ let(:location) { {country: country_code} }
1299
+ it { expect(average_view_percentage.keys).to eq [country_code] }
1300
+ end
1301
+ end
1302
+ end
1303
+
1304
+ describe 'average view percentage can be retrieved for a single US state' do
1305
+ let(:state_code) { 'NY' }
1306
+ let(:result) { channel.average_view_percentage since: date, by: by, in: location }
1307
+ let(:date) { 4.days.ago }
1308
+
1309
+ context 'and grouped by day' do
1310
+ let(:by) { :day }
1311
+
1312
+ context 'with the :in option set to {state: state code}' do
1313
+ let(:location) { {state: state_code} }
1314
+ it { expect(result.keys.min).to eq date.to_date }
1315
+ end
1316
+
1317
+ context 'with the :in option set to {country: "US", state: state code}' do
1318
+ let(:location) { {country: 'US', state: state_code} }
1319
+ it { expect(result.keys.min).to eq date.to_date }
1320
+ end
1321
+ end
1322
+
1323
+ context 'and grouped by US state' do
1324
+ let(:by) { :state }
1325
+
1326
+ context 'with the :in option set to {state: state code}' do
1327
+ let(:location) { {state: state_code} }
1328
+ it { expect(result.keys).to eq [state_code] }
1329
+ end
1330
+
1331
+ context 'with the :in option set to {country: "US", state: state code}' do
1332
+ let(:location) { {country: 'US', state: state_code} }
1333
+ it { expect(result.keys).to eq [state_code] }
1334
+ end
1335
+ end
1336
+ end
1337
+
1338
+ describe 'average view percentage can be grouped by day' do
1339
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
1340
+ let(:keys) { range.values }
1341
+
1342
+ specify 'with the :by option set to :day' do
1343
+ average_view_percentage = channel.average_view_percentage range.merge by: :day
1344
+ expect(average_view_percentage.keys).to eq range.values
1345
+ end
1346
+ end
1347
+
1348
+ describe 'average view percentage can be grouped by country' do
1349
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1350
+
1351
+ specify 'with the :by option set to :country' do
1352
+ percentage = channel.average_view_percentage range.merge by: :country
1353
+ expect(percentage.keys).to all(be_a String)
1354
+ expect(percentage.keys.map(&:length).uniq).to eq [2]
1355
+ expect(percentage.values).to all(be_a Float)
1356
+ end
1357
+ end
1358
+
1359
+ describe 'average view percentage can be grouped by state' do
1360
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1361
+
1362
+ specify 'with the :by option set to :state' do
1363
+ percentage = channel.average_view_percentage range.merge by: :state
1364
+ expect(percentage.keys).to all(be_a String)
1365
+ expect(percentage.keys.map(&:length).uniq).to eq [2]
1366
+ expect(percentage.values).to all(be_a Float)
1367
+ end
1368
+ end
1369
+
1370
+ describe 'impressions can be retrieved for a single country' do
1371
+ let(:country_code) { 'US' }
1372
+ let(:impressions) { channel.impressions since: date, by: by, in: location }
1373
+ let(:date) { ENV['YT_TEST_PARTNER_PLAYLIST_DATE'] }
1374
+
1375
+ context 'and grouped by day' do
1376
+ let(:by) { :day }
1377
+
1378
+ context 'with the :in option set to the country code' do
1379
+ let(:location) { country_code }
1380
+ it { expect(impressions.keys.min).to eq date.to_date }
1381
+ end
1382
+
1383
+ context 'with the :in option set to {country: country code}' do
1384
+ let(:location) { {country: country_code} }
1385
+ it { expect(impressions.keys.min).to eq date.to_date }
1386
+ end
1387
+ end
1388
+
1389
+ context 'and grouped by country' do
1390
+ let(:by) { :country }
1391
+
1392
+ context 'with the :in option set to the country code' do
1393
+ let(:location) { country_code }
1394
+ it { expect(impressions.keys).to eq [country_code] }
1395
+ end
1396
+
1397
+ context 'with the :in option set to {country: country code}' do
1398
+ let(:location) { {country: country_code} }
1399
+ it { expect(impressions.keys).to eq [country_code] }
1400
+ end
1401
+ end
1402
+ end
1403
+
1404
+ describe 'impressions can be grouped by day' do
1405
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
1406
+ let(:keys) { range.values }
1407
+
1408
+ specify 'with the :by option set to :day' do
1409
+ impressions = channel.impressions range.merge by: :day
1410
+ expect(impressions.keys).to eq range.values
1411
+ end
1412
+ end
1413
+
1414
+ describe 'impressions can be grouped by country' do
1415
+ let(:range) { {since: ENV['YT_TEST_PARTNER_PLAYLIST_DATE']} }
1416
+
1417
+ specify 'with the :by option set to :country' do
1418
+ impressions = channel.impressions range.merge by: :country
1419
+ expect(impressions.keys).to all(be_a String)
1420
+ expect(impressions.keys.map(&:length).uniq).to eq [2]
1421
+ expect(impressions.values).to all(be_an Integer)
1422
+ end
1423
+ end
1424
+
1425
+ describe 'monetized playbacks can be retrieved for a single country' do
1426
+ let(:country_code) { 'US' }
1427
+ let(:monetized_playbacks) { channel.monetized_playbacks since: date, by: by, in: location }
1428
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
1429
+
1430
+ context 'and grouped by day' do
1431
+ let(:by) { :day }
1432
+
1433
+ context 'with the :in option set to the country code' do
1434
+ let(:location) { country_code }
1435
+ it { expect(monetized_playbacks.keys.min).to eq date.to_date }
1436
+ end
1437
+
1438
+ context 'with the :in option set to {country: country code}' do
1439
+ let(:location) { {country: country_code} }
1440
+ it { expect(monetized_playbacks.keys.min).to eq date.to_date }
1441
+ end
1442
+ end
1443
+
1444
+ context 'and grouped by country' do
1445
+ let(:by) { :country }
1446
+
1447
+ context 'with the :in option set to the country code' do
1448
+ let(:location) { country_code }
1449
+ it { expect(monetized_playbacks.keys).to eq [country_code] }
1450
+ end
1451
+
1452
+ context 'with the :in option set to {country: country code}' do
1453
+ let(:location) { {country: country_code} }
1454
+ it { expect(monetized_playbacks.keys).to eq [country_code] }
1455
+ end
1456
+ end
1457
+ end
1458
+
1459
+ describe 'monetized_playbacks can be grouped by day' do
1460
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
1461
+ let(:keys) { range.values }
1462
+
1463
+ specify 'with the :by option set to :day' do
1464
+ monetized_playbacks = channel.monetized_playbacks range.merge by: :day
1465
+ expect(monetized_playbacks.keys).to eq range.values
1466
+ end
1467
+ end
1468
+
1469
+ describe 'monetized playbacks can be grouped by country' do
1470
+ let(:range) { {since: ENV['YT_TEST_PARTNER_PLAYLIST_DATE']} }
1471
+
1472
+ specify 'with the :by option set to :country' do
1473
+ playbacks = channel.monetized_playbacks range.merge by: :country
1474
+ expect(playbacks.keys).to all(be_a String)
1475
+ expect(playbacks.keys.map(&:length).uniq).to eq [2]
1476
+ expect(playbacks.values).to all(be_an Integer)
1477
+ end
1478
+ end
1479
+
1480
+ describe 'playback-based CPM can be retrieved for a single country' do
1481
+ let(:country_code) { 'US' }
1482
+ let(:playback_based_cpm) { channel.playback_based_cpm since: date, by: by, in: location }
1483
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
1484
+
1485
+ context 'and grouped by day' do
1486
+ let(:by) { :day }
1487
+
1488
+ context 'with the :in option set to the country code' do
1489
+ let(:location) { country_code }
1490
+ it { expect(playback_based_cpm.keys.min).to eq date.to_date }
1491
+ end
1492
+
1493
+ context 'with the :in option set to {country: country code}' do
1494
+ let(:location) { {country: country_code} }
1495
+ it { expect(playback_based_cpm.keys.min).to eq date.to_date }
1496
+ end
1497
+ end
1498
+
1499
+ context 'and grouped by country' do
1500
+ let(:by) { :country }
1501
+
1502
+ context 'with the :in option set to the country code' do
1503
+ let(:location) { country_code }
1504
+ it { expect(playback_based_cpm.keys).to eq [country_code] }
1505
+ end
1506
+
1507
+ context 'with the :in option set to {country: country code}' do
1508
+ let(:location) { {country: country_code} }
1509
+ it { expect(playback_based_cpm.keys).to eq [country_code] }
1510
+ end
1511
+ end
1512
+ end
1513
+
1514
+ describe 'playback-based CPM can be grouped by day' do
1515
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
1516
+ let(:keys) { range.values }
1517
+
1518
+ specify 'with the :by option set to :day' do
1519
+ playback_based_cpm = channel.playback_based_cpm range.merge by: :day
1520
+ expect(playback_based_cpm.keys).to eq range.values
1521
+ end
1522
+ end
1523
+
1524
+ describe 'playback-based CPM can be grouped by country' do
1525
+ let(:range) { {since: ENV['YT_TEST_PARTNER_PLAYLIST_DATE']} }
1526
+
1527
+ specify 'with the :by option set to :country' do
1528
+ playbacks = channel.playback_based_cpm range.merge by: :country
1529
+ expect(playbacks.keys).to all(be_a String)
1530
+ expect(playbacks.keys.map(&:length).uniq).to eq [2]
1531
+ expect(playbacks.values).to all(be_a Float)
1532
+ end
1533
+ end
1534
+
1535
+ describe 'annotation clicks can be retrieved for a single country' do
1536
+ let(:country_code) { 'US' }
1537
+ let(:annotation_clicks) { channel.annotation_clicks since: date, by: by, in: location }
1538
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
1539
+
1540
+ context 'and grouped by day' do
1541
+ let(:by) { :day }
1542
+
1543
+ context 'with the :in option set to the country code' do
1544
+ let(:location) { country_code }
1545
+ it { expect(annotation_clicks.keys.min).to eq date.to_date }
1546
+ end
1547
+
1548
+ context 'with the :in option set to {country: country code}' do
1549
+ let(:location) { {country: country_code} }
1550
+ it { expect(annotation_clicks.keys.min).to eq date.to_date }
1551
+ end
1552
+ end
1553
+
1554
+ context 'and grouped by country' do
1555
+ let(:by) { :country }
1556
+
1557
+ context 'with the :in option set to the country code' do
1558
+ let(:location) { country_code }
1559
+ it { expect(annotation_clicks.keys).to eq [country_code] }
1560
+ end
1561
+
1562
+ context 'with the :in option set to {country: country code}' do
1563
+ let(:location) { {country: country_code} }
1564
+ it { expect(annotation_clicks.keys).to eq [country_code] }
1565
+ end
1566
+ end
1567
+ end
1568
+
1569
+ describe 'annotation clicks can be retrieved for a single US state' do
1570
+ let(:state_code) { 'NY' }
1571
+ let(:result) { channel.annotation_clicks since: date, by: by, in: location }
1572
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
1573
+
1574
+ context 'and grouped by day' do
1575
+ let(:by) { :day }
1576
+
1577
+ context 'with the :in option set to {state: state code}' do
1578
+ let(:location) { {state: state_code} }
1579
+ it { expect(result.keys.min).to eq date.to_date }
1580
+ end
1581
+
1582
+ context 'with the :in option set to {country: "US", state: state code}' do
1583
+ let(:location) { {country: 'US', state: state_code} }
1584
+ it { expect(result.keys.min).to eq date.to_date }
1585
+ end
1586
+ end
1587
+
1588
+ context 'and grouped by US state' do
1589
+ let(:by) { :state }
1590
+
1591
+ context 'with the :in option set to {state: state code}' do
1592
+ let(:location) { {state: state_code} }
1593
+ it { expect(result.keys).to eq [state_code] }
1594
+ end
1595
+
1596
+ context 'with the :in option set to {country: "US", state: state code}' do
1597
+ let(:location) { {country: 'US', state: state_code} }
1598
+ it { expect(result.keys).to eq [state_code] }
1599
+ end
1600
+ end
1601
+ end
1602
+
1603
+ describe 'annotation clicks can be grouped by day' do
1604
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE'], until: Date.parse(ENV['YT_TEST_PARTNER_VIDEO_DATE']) + 5} }
1605
+
1606
+ specify 'with the :by option set to :day' do
1607
+ annotation_clicks = channel.annotation_clicks range.merge by: :day
1608
+ expect(annotation_clicks.values).to all(be_an Integer)
1609
+ end
1610
+ end
1611
+
1612
+ describe 'annotation clicks can be grouped by country' do
1613
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
1614
+
1615
+ specify 'with the :by option set to :country' do
1616
+ clicks = channel.annotation_clicks range.merge by: :country
1617
+ expect(clicks.keys).to all(be_a String)
1618
+ expect(clicks.keys.map(&:length).uniq).to eq [2]
1619
+ expect(clicks.values).to all(be_an Integer)
1620
+ end
1621
+ end
1622
+
1623
+ describe 'annotation clicks can be grouped by state' do
1624
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
1625
+
1626
+ specify 'with the :by option set to :state' do
1627
+ clicks = channel.annotation_clicks range.merge by: :state
1628
+ expect(clicks.keys).to all(be_a String)
1629
+ expect(clicks.keys.map(&:length).uniq).to eq [2]
1630
+ expect(clicks.values).to all(be_an Integer)
1631
+ end
1632
+ end
1633
+
1634
+ describe 'annotation click-through rate can be retrieved for a single country' do
1635
+ let(:country_code) { 'US' }
1636
+ let(:annotation_click_through_rate) { channel.annotation_click_through_rate since: date, by: by, in: location }
1637
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
1638
+
1639
+ context 'and grouped by day' do
1640
+ let(:by) { :day }
1641
+
1642
+ context 'with the :in option set to the country code' do
1643
+ let(:location) { country_code }
1644
+ it { expect(annotation_click_through_rate.keys.min).to eq date.to_date }
1645
+ end
1646
+
1647
+ context 'with the :in option set to {country: country code}' do
1648
+ let(:location) { {country: country_code} }
1649
+ it { expect(annotation_click_through_rate.keys.min).to eq date.to_date }
1650
+ end
1651
+ end
1652
+
1653
+ context 'and grouped by country' do
1654
+ let(:by) { :country }
1655
+
1656
+ context 'with the :in option set to the country code' do
1657
+ let(:location) { country_code }
1658
+ it { expect(annotation_click_through_rate.keys).to eq [country_code] }
1659
+ end
1660
+
1661
+ context 'with the :in option set to {country: country code}' do
1662
+ let(:location) { {country: country_code} }
1663
+ it { expect(annotation_click_through_rate.keys).to eq [country_code] }
1664
+ end
1665
+ end
1666
+ end
1667
+
1668
+ describe 'annotation click-through rate can be retrieved for a single US state' do
1669
+ let(:state_code) { 'NY' }
1670
+ let(:result) { channel.annotation_click_through_rate since: date, by: by, in: location }
1671
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
1672
+
1673
+ context 'and grouped by day' do
1674
+ let(:by) { :day }
1675
+
1676
+ context 'with the :in option set to {state: state code}' do
1677
+ let(:location) { {state: state_code} }
1678
+ it { expect(result.keys.min).to eq date.to_date }
1679
+ end
1680
+
1681
+ context 'with the :in option set to {country: "US", state: state code}' do
1682
+ let(:location) { {country: 'US', state: state_code} }
1683
+ it { expect(result.keys.min).to eq date.to_date }
1684
+ end
1685
+ end
1686
+
1687
+ context 'and grouped by US state' do
1688
+ let(:by) { :state }
1689
+
1690
+ context 'with the :in option set to {state: state code}' do
1691
+ let(:location) { {state: state_code} }
1692
+ it { expect(result.keys).to eq [state_code] }
1693
+ end
1694
+
1695
+ context 'with the :in option set to {country: "US", state: state code}' do
1696
+ let(:location) { {country: 'US', state: state_code} }
1697
+ it { expect(result.keys).to eq [state_code] }
1698
+ end
1699
+ end
1700
+ end
1701
+
1702
+ describe 'annotation click-through rate can be grouped by day' do
1703
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE'], until: Date.parse(ENV['YT_TEST_PARTNER_VIDEO_DATE']) + 5} }
1704
+
1705
+ specify 'with the :by option set to :day' do
1706
+ annotation_click_through_rate = channel.annotation_click_through_rate range.merge by: :day
1707
+ expect(annotation_click_through_rate.values).to all(be_instance_of Float)
1708
+ end
1709
+ end
1710
+
1711
+ describe 'annotation click-through rate can be grouped by country' do
1712
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
1713
+
1714
+ specify 'with the :by option set to :country' do
1715
+ rate = channel.annotation_click_through_rate range.merge by: :country
1716
+ expect(rate.keys).to all(be_a String)
1717
+ expect(rate.keys.map(&:length).uniq).to eq [2]
1718
+ expect(rate.values).to all(be_a Float)
1719
+ end
1720
+ end
1721
+
1722
+ describe 'annotation click-through rate can be grouped by state' do
1723
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
1724
+
1725
+ specify 'with the :by option set to :state' do
1726
+ rate = channel.annotation_click_through_rate range.merge by: :state
1727
+ expect(rate.keys).to all(be_a String)
1728
+ expect(rate.keys.map(&:length).uniq).to eq [2]
1729
+ expect(rate.values).to all(be_a Float)
1730
+ end
1731
+ end
1732
+
1733
+ describe 'annotation close rate can be retrieved for a single country' do
1734
+ let(:country_code) { 'US' }
1735
+ let(:annotation_close_rate) { channel.annotation_close_rate since: date, by: by, in: location }
1736
+ let(:date) { 4.days.ago }
1737
+
1738
+ context 'and grouped by day' do
1739
+ let(:by) { :day }
1740
+
1741
+ context 'with the :in option set to the country code' do
1742
+ let(:location) { country_code }
1743
+ it { expect(annotation_close_rate.keys.min).to eq date.to_date }
1744
+ end
1745
+
1746
+ context 'with the :in option set to {country: country code}' do
1747
+ let(:location) { {country: country_code} }
1748
+ it { expect(annotation_close_rate.keys.min).to eq date.to_date }
1749
+ end
1750
+ end
1751
+
1752
+ context 'and grouped by country' do
1753
+ let(:by) { :country }
1754
+
1755
+ context 'with the :in option set to the country code' do
1756
+ let(:location) { country_code }
1757
+ it { expect(annotation_close_rate.keys).to eq [country_code] }
1758
+ end
1759
+
1760
+ context 'with the :in option set to {country: country code}' do
1761
+ let(:location) { {country: country_code} }
1762
+ it { expect(annotation_close_rate.keys).to eq [country_code] }
1763
+ end
1764
+ end
1765
+ end
1766
+
1767
+ describe 'annotation close rate can be retrieved for a single US state' do
1768
+ let(:state_code) { 'NY' }
1769
+ let(:result) { channel.annotation_close_rate since: date, by: by, in: location }
1770
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
1771
+
1772
+ context 'and grouped by day' do
1773
+ let(:by) { :day }
1774
+
1775
+ context 'with the :in option set to {state: state code}' do
1776
+ let(:location) { {state: state_code} }
1777
+ it { expect(result.keys.min).to eq date.to_date }
1778
+ end
1779
+
1780
+ context 'with the :in option set to {country: "US", state: state code}' do
1781
+ let(:location) { {country: 'US', state: state_code} }
1782
+ it { expect(result.keys.min).to eq date.to_date }
1783
+ end
1784
+ end
1785
+
1786
+ context 'and grouped by US state' do
1787
+ let(:by) { :state }
1788
+
1789
+ context 'with the :in option set to {state: state code}' do
1790
+ let(:location) { {state: state_code} }
1791
+ it { expect(result.keys).to eq [state_code] }
1792
+ end
1793
+
1794
+ context 'with the :in option set to {country: "US", state: state code}' do
1795
+ let(:location) { {country: 'US', state: state_code} }
1796
+ it { expect(result.keys).to eq [state_code] }
1797
+ end
1798
+ end
1799
+ end
1800
+
1801
+ describe 'annotation close rate can be grouped by day' do
1802
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE'], until: Date.parse(ENV['YT_TEST_PARTNER_VIDEO_DATE']) + 5} }
1803
+
1804
+ specify 'with the :by option set to :day' do
1805
+ annotation_close_rate = channel.annotation_close_rate range.merge by: :day
1806
+ expect(annotation_close_rate.values).to all(be_instance_of Float)
1807
+ end
1808
+ end
1809
+
1810
+ describe 'annotation close rate can be grouped by country' do
1811
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1812
+
1813
+ specify 'with the :by option set to :country' do
1814
+ rate = channel.annotation_close_rate range.merge by: :country
1815
+ expect(rate.keys).to all(be_a String)
1816
+ expect(rate.keys.map(&:length).uniq).to eq [2]
1817
+ expect(rate.values).to all(be_a Float)
1818
+ end
1819
+ end
1820
+
1821
+ describe 'annotation close rate can be grouped by state' do
1822
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
1823
+
1824
+ specify 'with the :by option set to :state' do
1825
+ rate = channel.annotation_close_rate range.merge by: :state
1826
+ expect(rate.keys).to all(be_a String)
1827
+ expect(rate.keys.map(&:length).uniq).to eq [2]
1828
+ expect(rate.values).to all(be_a Float)
1829
+ end
1830
+ end
1831
+
1832
+ describe 'viewer percentage can be retrieved for a single country' do
1833
+ let(:country_code) { 'US' }
1834
+ let(:viewer_percentage) { channel.viewer_percentage in: location }
1835
+
1836
+ context 'with the :in option set to the country code' do
1837
+ let(:location) { country_code }
1838
+ it { expect(viewer_percentage.keys).to match_array [:female, :male] }
1839
+ end
1840
+
1841
+ context 'with the :in option set to {country: country code}' do
1842
+ let(:location) { {country: country_code} }
1843
+ it { expect(viewer_percentage.keys).to match_array [:female, :male] }
1844
+ end
1845
+ end
1846
+
1847
+ describe 'viewer percentage can be retrieved for a single US state' do
1848
+ let(:state_code) { 'TX' }
1849
+ let(:viewer_percentage) { channel.viewer_percentage since: date, in: location }
1850
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
1851
+
1852
+ context 'with the :in option set to {state: state code}' do
1853
+ let(:location) { {state: state_code} }
1854
+ it {expect(viewer_percentage.keys).to match_array [:female, :male] }
1855
+ end
1856
+
1857
+ context 'with the :in option set to {country: "US", state: state code}' do
1858
+ let(:location) { {country: 'US', state: state_code} }
1859
+ it { expect(viewer_percentage.keys).to match_array [:female, :male] }
1860
+ end
1861
+ end
1862
+
1863
+ describe 'viewer percentage can be retrieved for a range of days' do
1864
+ let(:viewer_percentage) { channel.viewer_percentage since: 1.year.ago, until: 10.days.ago}
1865
+ it { expect(viewer_percentage).to be_a Hash }
1866
+ end
1867
+
1868
+ describe 'viewer_percentage can be grouped by gender and age group' do
1869
+ let(:range) { {since: 1.year.ago.to_date, until: 1.week.ago.to_date} }
1870
+ let(:keys) { range.values }
1871
+
1872
+ specify 'without a :by option (default)' do
1873
+ viewer_percentage = channel.viewer_percentage range
1874
+ expect(viewer_percentage.keys).to match_array [:female, :male]
1875
+ expect(viewer_percentage[:female].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
1876
+ expect(viewer_percentage[:female].values).to all(be_instance_of Float)
1877
+ expect(viewer_percentage[:male].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
1878
+ expect(viewer_percentage[:male].values).to all(be_instance_of Float)
1879
+ end
1880
+
1881
+ specify 'with the :by option set to :gender_age_group' do
1882
+ viewer_percentage = channel.viewer_percentage range.merge by: :gender_age_group
1883
+ expect(viewer_percentage.keys).to match_array [:female, :male]
1884
+ expect(viewer_percentage[:female].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
1885
+ expect(viewer_percentage[:female].values).to all(be_instance_of Float)
1886
+ expect(viewer_percentage[:male].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
1887
+ expect(viewer_percentage[:male].values).to all(be_instance_of Float)
1888
+ end
1889
+ end
1890
+
1891
+ describe 'viewer_percentage can be grouped by gender' do
1892
+ let(:range) { {since: 1.year.ago.to_date, until: 1.week.ago.to_date} }
1893
+ let(:keys) { range.values }
1894
+
1895
+ specify 'with the :by option set to :gender' do
1896
+ viewer_percentage = channel.viewer_percentage range.merge by: :gender
1897
+ expect(viewer_percentage.keys).to match_array [:female, :male]
1898
+ expect(viewer_percentage[:female]).to be_a Float
1899
+ expect(viewer_percentage[:male]).to be_a Float
1900
+ end
1901
+ end
1902
+
1903
+ describe 'viewer_percentage can be grouped by age group' do
1904
+ let(:range) { {since: 1.year.ago.to_date, until: 1.week.ago.to_date} }
1905
+ let(:keys) { range.values }
1906
+
1907
+ specify 'with the :by option set to :age_group' do
1908
+ viewer_percentage = channel.viewer_percentage range.merge by: :age_group
1909
+ expect(viewer_percentage.keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
1910
+ expect(viewer_percentage.values).to all(be_instance_of Float)
1911
+ end
1912
+ end
1913
+
1914
+ specify 'information about its content owner can be retrieved' do
1915
+ expect(channel.content_owner).to be_a String
1916
+ expect(channel.linked_at).to be_a Time
1917
+ end
1918
+ end
1919
+
1920
+ context 'not managed by the authenticated Content Owner' do
1921
+ let(:id) { 'UCBR8-60-B28hp2BmDPdntcQ' }
1922
+
1923
+ specify 'earnings and impressions cannot be retrieved' do
1924
+ expect{channel.earnings}.to raise_error Yt::Errors::Forbidden
1925
+ expect{channel.views}.to raise_error Yt::Errors::Forbidden
1926
+ end
1927
+
1928
+ specify 'information about its content owner cannot be retrieved' do
1929
+ expect(channel.content_owner).to be_nil
1930
+ expect(channel.linked_at).to be_nil
1931
+ end
1932
+ end
1933
+ end
1934
+ end