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,1239 @@
1
+ # encoding: UTF-8
2
+ require 'spec_helper'
3
+ require 'yt/models/video'
4
+
5
+ describe Yt::Video, :partner do
6
+ subject(:video) { Yt::Video.new id: id, auth: $content_owner }
7
+
8
+ context 'given a video of a partnered channel', :partner do
9
+ context 'managed by the authenticated Content Owner' do
10
+ let(:id) { ENV['YT_TEST_PARTNER_VIDEO_ID'] }
11
+
12
+ describe 'advertising options can be retrieved' do
13
+ it { expect{video.advertising_options_set}.not_to raise_error }
14
+ end
15
+
16
+ [:views, :uniques, :comments, :likes, :dislikes, :shares,
17
+ :subscribers_gained, :subscribers_lost, :favorites_added,
18
+ :favorites_removed, :estimated_minutes_watched, :average_view_duration,
19
+ :average_view_percentage, :impressions, :monetized_playbacks,
20
+ :annotation_clicks, :annotation_click_through_rate, :playback_based_cpm,
21
+ :annotation_close_rate, :earnings].each do |metric|
22
+ describe "#{metric} can be retrieved for a range of days" do
23
+ let(:date_in) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
24
+ let(:date_out) { Date.parse(ENV['YT_TEST_PARTNER_VIDEO_DATE']) + 5 }
25
+ let(:metric) { metric }
26
+ let(:result) { video.public_send metric, options }
27
+
28
+ context 'with a given start and end (:since/:until option)' do
29
+ let(:options) { {by: :day, since: date_in, until: date_out} }
30
+ specify do
31
+ expect(result.keys.min).to eq date_in.to_date
32
+ expect(result.keys.max).to eq date_out.to_date
33
+ end
34
+ end
35
+
36
+ context 'with a given start and end (:from/:to option)' do
37
+ let(:options) { {by: :day, from: date_in, to: date_out} }
38
+ specify do
39
+ expect(result.keys.min).to eq date_in.to_date
40
+ expect(result.keys.max).to eq date_out.to_date
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "#{metric} can be grouped by month" do
46
+ let(:metric) { metric }
47
+
48
+ let(:result) { video.public_send metric, by: :month, since: 1.month.ago }
49
+ specify do
50
+ expect(result.keys).to all(be_a Range)
51
+ expect(result.keys.map &:first).to all(be_a Date)
52
+ expect(result.keys.map &:first).to eq result.keys.map(&:first).map(&:beginning_of_month)
53
+ expect(result.keys.map &:last).to all(be_a Date)
54
+ expect(result.keys.map &:last).to eq result.keys.map(&:last).map(&:end_of_month)
55
+ end
56
+ end
57
+
58
+ describe "#{metric} can be grouped by week and returns non-overlapping periods" do
59
+ let(:metric) { metric }
60
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE'], until: Date.parse(ENV['YT_TEST_PARTNER_VIDEO_DATE']) + 9} }
61
+ let(:result) { video.public_send metric, range.merge(by: :week)}
62
+ specify do
63
+ expect(result.size).to be <= 2
64
+ expect(result.keys).to all(be_a Range)
65
+ expect(result.keys.map{|range| range.first.wday}.uniq).to be_one
66
+ expect(result.keys.map{|range| range.last.wday}.uniq).to be_one
67
+ end
68
+ end
69
+
70
+ describe "#{metric} can be retrieved for a single country" do
71
+ let(:result) { video.public_send metric, options }
72
+
73
+ context 'and grouped by day' do
74
+ let(:date_in) { 5.days.ago }
75
+ let(:options) { {by: :day, since: date_in, in: location} }
76
+
77
+ context 'with the :in option set to the country code' do
78
+ let(:location) { 'US' }
79
+ it { expect(result.keys.min).to eq date_in.to_date }
80
+ end
81
+
82
+ context 'with the :in option set to {country: country code}' do
83
+ let(:location) { {country: 'US'} }
84
+ it { expect(result.keys.min).to eq date_in.to_date }
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ {views: Integer, comments: Integer, dislikes: Integer,
91
+ estimated_minutes_watched: Integer, average_view_duration: Integer,
92
+ average_view_percentage: Float, impressions: Integer,
93
+ subscribers_lost: Integer, subscribers_gained: Integer, likes: Integer,
94
+ monetized_playbacks: Integer, earnings: Float}.each do |metric, type|
95
+ describe "#{metric} can be retrieved for a specific day" do
96
+ let(:metric) { metric }
97
+ let(:result) { video.public_send "#{metric}_on", date }
98
+
99
+ context 'in which the video had data' do
100
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
101
+ it { expect(result).to be_a type }
102
+ end
103
+
104
+ context 'in the future' do
105
+ let(:date) { 5.days.from_now }
106
+ it { expect(result).to be_nil }
107
+ end
108
+ end
109
+ end
110
+
111
+ {views: Integer, comments: Integer, likes: Integer, dislikes: Integer,
112
+ shares: Integer, subscribers_gained: Integer, subscribers_lost: Integer,
113
+ favorites_added: Integer,
114
+ estimated_minutes_watched: Integer, average_view_duration: Integer,
115
+ average_view_percentage: Float, impressions: Integer,
116
+ monetized_playbacks: Integer, annotation_clicks: Integer,
117
+ annotation_click_through_rate: Float, annotation_close_rate: Float,
118
+ earnings: Float}.each do |metric, type|
119
+ describe "#{metric} can be grouped by range" do
120
+ let(:metric) { metric }
121
+
122
+ context 'without a :by option (default)' do
123
+ let(:result) { video.public_send metric }
124
+ specify do
125
+ expect(result.size).to be 1
126
+ expect(result[:total]).to be_a type
127
+ end
128
+ end
129
+
130
+ context 'with the :by option set to :range' do
131
+ let(:result) { video.public_send metric, by: :range }
132
+ specify do
133
+ expect(result.size).to be 1
134
+ expect(result[:total]).to be_a type
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ {favorites_removed: Integer}.each do |metric, type|
141
+ describe "#{metric} can be grouped by range" do
142
+ let(:id) { 'NeMlqbX2Ifg' }
143
+ let(:metric) { metric }
144
+
145
+ context 'without a :by option (default)' do
146
+ let(:result) { video.public_send metric }
147
+ specify do
148
+ expect(result.size).to be 1
149
+ expect(result[:total]).to be_a type
150
+ end
151
+ end
152
+
153
+ context 'with the :by option set to :range' do
154
+ let(:result) { video.public_send metric, by: :range }
155
+ specify do
156
+ expect(result.size).to be 1
157
+ expect(result[:total]).to be_a type
158
+ end
159
+ end
160
+ end
161
+ end
162
+
163
+ [:views, :comments, :likes, :dislikes, :shares,
164
+ :subscribers_gained, :subscribers_lost, :favorites_added,
165
+ :estimated_minutes_watched, :average_view_duration,
166
+ :average_view_percentage, :impressions, :monetized_playbacks,
167
+ :annotation_clicks, :annotation_click_through_rate,
168
+ :annotation_close_rate, :earnings].each do |metric|
169
+ describe "#{metric} can be retrieved for a single country" do
170
+ let(:result) { video.public_send metric, options }
171
+
172
+ context 'and grouped by country' do
173
+ let(:options) { {by: :country, in: location} }
174
+
175
+ context 'with the :in option set to the country code' do
176
+ let(:location) { 'US' }
177
+ it { expect(result.keys).to eq ['US'] }
178
+ end
179
+
180
+ context 'with the :in option set to {country: country code}' do
181
+ let(:location) { {country: 'US'} }
182
+ it { expect(result.keys).to eq ['US'] }
183
+ end
184
+ end
185
+ end
186
+ end
187
+
188
+ [:views, :annotation_clicks, :annotation_click_through_rate,
189
+ :annotation_close_rate].each do |metric|
190
+ describe "#{metric} can be retrieved for a single country" do
191
+ let(:result) { video.public_send metric, options }
192
+
193
+ context 'and grouped by state' do
194
+ let(:options) { {by: :state, in: location} }
195
+
196
+ context 'with the :in option set to the country code' do
197
+ let(:location) { 'US' }
198
+ it { expect(result.keys.map(&:length).uniq).to eq [2] }
199
+ end
200
+
201
+ context 'with the :in option set to {country: country code}' do
202
+ let(:location) { {country: 'US'} }
203
+ it { expect(result.keys.map(&:length).uniq).to eq [2] }
204
+ end
205
+ end
206
+ end
207
+ end
208
+
209
+ describe 'multiple reports can be retrieved at once' do
210
+ metrics = {views: Integer, uniques: Integer,
211
+ estimated_minutes_watched: Integer, comments: Integer, likes: Integer,
212
+ dislikes: Integer, shares: Integer, subscribers_gained: Integer,
213
+ subscribers_lost: Integer, favorites_added: Integer,
214
+ favorites_removed: Integer, average_view_duration: Integer,
215
+ average_view_percentage: Float, annotation_clicks: Integer,
216
+ annotation_click_through_rate: Float,
217
+ annotation_close_rate: Float, earnings: Float, impressions: Integer,
218
+ monetized_playbacks: Integer}
219
+
220
+ specify 'by day' do
221
+ range = {since: 5.days.ago.to_date, until: 3.days.ago.to_date}
222
+ result = video.reports range.merge(only: metrics, by: :day)
223
+ metrics.each do |metric, type|
224
+ expect(result[metric].keys).to all(be_a Date)
225
+ expect(result[metric].values).to all(be_a type)
226
+ end
227
+ end
228
+
229
+ specify 'by month' do
230
+ result = video.reports only: metrics, by: :month, since: 1.month.ago
231
+ metrics.each do |metric, type|
232
+ expect(result[metric].keys).to all(be_a Range)
233
+ expect(result[metric].keys.map &:first).to all(be_a Date)
234
+ expect(result[metric].keys.map &:first).to eq result[metric].keys.map(&:first).map(&:beginning_of_month)
235
+ expect(result[metric].keys.map &:last).to all(be_a Date)
236
+ expect(result[metric].keys.map &:last).to eq result[metric].keys.map(&:last).map(&:end_of_month)
237
+ expect(result[metric].values).to all(be_a type)
238
+ end
239
+ end
240
+
241
+ specify 'by week' do
242
+ range = {since: ENV['YT_TEST_PARTNER_VIDEO_DATE'], until: Date.parse(ENV['YT_TEST_PARTNER_VIDEO_DATE']) + 9}
243
+ result = video.reports range.merge(only: metrics, by: :week)
244
+ metrics.each do |metric, type|
245
+ expect(result[metric].size).to be <= 2
246
+ expect(result[metric].keys).to all(be_a Range)
247
+ expect(result[metric].keys.map{|range| range.first.wday}.uniq).to be_one
248
+ expect(result[metric].keys.map{|range| range.last.wday}.uniq).to be_one
249
+ expect(result[metric].values).to all(be_a type)
250
+ end
251
+ end
252
+ end
253
+
254
+ describe 'earnings can be grouped by day' do
255
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
256
+ let(:keys) { range.values }
257
+
258
+ specify 'with the :by option set to :day' do
259
+ earnings = video.earnings range.merge by: :day
260
+ expect(earnings.keys).to eq range.values
261
+ end
262
+ end
263
+
264
+ describe 'earnings can be grouped by country' do
265
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
266
+
267
+ specify 'with the :by option set to :country' do
268
+ earnings = video.earnings range.merge by: :country
269
+ expect(earnings.keys).to all(be_a String)
270
+ expect(earnings.keys.map(&:length).uniq).to eq [2]
271
+ expect(earnings.values).to all(be_a Float)
272
+ end
273
+ end
274
+
275
+ describe 'views can be retrieved for a single US state' do
276
+ let(:state_code) { 'NY' }
277
+ let(:views) { video.views since: date, by: by, in: location }
278
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
279
+
280
+ context 'and grouped by day' do
281
+ let(:by) { :day }
282
+
283
+ context 'with the :in option set to {state: state code}' do
284
+ let(:location) { {state: state_code} }
285
+ it { expect(views.keys.min).to eq date.to_date }
286
+ end
287
+
288
+ context 'with the :in option set to {country: "US", state: state code}' do
289
+ let(:location) { {country: 'US', state: state_code} }
290
+ it { expect(views.keys.min).to eq date.to_date }
291
+ end
292
+ end
293
+
294
+ context 'and grouped by US state' do
295
+ let(:by) { :state }
296
+
297
+ context 'with the :in option set to {state: state code}' do
298
+ let(:location) { {state: state_code} }
299
+ it { expect(views.keys).to eq [state_code] }
300
+ end
301
+
302
+ context 'with the :in option set to {country: "US", state: state code}' do
303
+ let(:location) { {country: 'US', state: state_code} }
304
+ it { expect(views.keys).to eq [state_code] }
305
+ end
306
+ end
307
+ end
308
+
309
+ describe 'views can be grouped by day' do
310
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
311
+ let(:keys) { range.values }
312
+
313
+ specify 'with the :by option set to :day' do
314
+ views = video.views range.merge by: :day
315
+ expect(views.keys).to eq range.values
316
+ end
317
+ end
318
+
319
+ describe 'views can be grouped by traffic source' do
320
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
321
+ let(:keys) { Yt::Collections::Reports::TRAFFIC_SOURCES.keys }
322
+
323
+ specify 'with the :by option set to :traffic_source' do
324
+ views = video.views range.merge by: :traffic_source
325
+ expect(views.keys - keys).to be_empty
326
+ end
327
+
328
+ specify 'and are returned sorted by descending views' do
329
+ views = video.views range.merge by: :traffic_source
330
+ expect(views.values.sort.reverse).to eq views.values
331
+ end
332
+ end
333
+
334
+ describe 'views can be grouped by playback location' do
335
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
336
+ let(:keys) { Yt::Collections::Reports::PLAYBACK_LOCATIONS.keys }
337
+
338
+ specify 'with the :by option set to :playback_location' do
339
+ views = video.views range.merge by: :playback_location
340
+ expect(views.keys - keys).to be_empty
341
+ end
342
+ end
343
+
344
+ describe 'views can be grouped by embedded player location' do
345
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
346
+
347
+ specify 'with the :by option set to :embedded_player_location' do
348
+ views = video.views range.merge by: :embedded_player_location
349
+ expect(views).not_to be_empty
350
+ end
351
+ end
352
+
353
+ describe 'views can be grouped by related video' do
354
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
355
+
356
+ specify 'with the :by option set to :related_video' do
357
+ views = video.views range.merge by: :related_video
358
+ expect(views.keys).to all(be_instance_of Yt::Video)
359
+ end
360
+ end
361
+
362
+ describe 'views can be grouped by search term' do
363
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
364
+
365
+ specify 'with the :by option set to :search_term' do
366
+ views = video.views range.merge by: :search_term
367
+ expect(views.keys).to all(be_a String)
368
+ end
369
+ end
370
+
371
+ describe 'views can be grouped by referrer' do
372
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
373
+
374
+ specify 'with the :by option set to :referrer' do
375
+ views = video.views range.merge by: :referrer
376
+ expect(views.keys).to all(be_a String)
377
+ end
378
+ end
379
+
380
+ describe 'views can be grouped by device type' do
381
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
382
+
383
+ specify 'with the :by option set to :device_type' do
384
+ views = video.views range.merge by: :device_type
385
+ expect(views.keys).to all(be_instance_of Symbol)
386
+ expect(views.values).to all(be_an Integer)
387
+ end
388
+ end
389
+
390
+ describe 'views can be grouped by country' do
391
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
392
+
393
+ specify 'with the :by option set to :country' do
394
+ views = video.views range.merge by: :country
395
+ expect(views.keys).to all(be_a String)
396
+ expect(views.keys.map(&:length).uniq).to eq [2]
397
+ expect(views.values).to all(be_an Integer)
398
+ end
399
+
400
+ specify 'and are returned sorted by descending views' do
401
+ views = video.views range.merge by: :country
402
+ expect(views.values.sort.reverse).to eq views.values
403
+ end
404
+ end
405
+
406
+ describe 'views can be grouped by state' do
407
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
408
+
409
+ specify 'with the :by option set to :state' do
410
+ views = video.views range.merge by: :state
411
+ expect(views.keys).to all(be_a String)
412
+ expect(views.keys.map(&:length).uniq).to eq [2]
413
+ expect(views.values).to all(be_an Integer)
414
+ end
415
+
416
+ specify 'and are returned sorted by descending views' do
417
+ views = video.views range.merge by: :state
418
+ expect(views.values.sort.reverse).to eq views.values
419
+ end
420
+ end
421
+
422
+ describe 'uniques can be retrieved for a single US state' do
423
+ let(:state_code) { 'NY' }
424
+ let(:result) { video.uniques since: date, by: by, in: location }
425
+ let(:date) { 4.days.ago }
426
+
427
+ context 'and grouped by day' do
428
+ let(:by) { :day }
429
+
430
+ context 'with the :in option set to {state: state code}' do
431
+ let(:location) { {state: state_code} }
432
+ it { expect(result.keys.min).to eq date.to_date }
433
+ end
434
+
435
+ context 'with the :in option set to {country: "US", state: state code}' do
436
+ let(:location) { {country: 'US', state: state_code} }
437
+ it { expect(result.keys.min).to eq date.to_date }
438
+ end
439
+ end
440
+ end
441
+
442
+ describe 'uniques can be grouped by day' do
443
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
444
+ let(:keys) { range.values }
445
+
446
+ specify 'with the :by option set to :day' do
447
+ uniques = video.uniques range.merge by: :day
448
+ expect(uniques.keys).to eq range.values
449
+ end
450
+ end
451
+
452
+ describe 'comments can be grouped by day' do
453
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
454
+ let(:keys) { range.values }
455
+
456
+ specify 'with the :by option set to :day' do
457
+ comments = video.comments range.merge by: :day
458
+ expect(comments.keys).to eq range.values
459
+ end
460
+ end
461
+
462
+ describe 'comments can be grouped by country' do
463
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
464
+
465
+ specify 'with the :by option set to :country' do
466
+ comments = video.comments range.merge by: :country
467
+ expect(comments.keys).to all(be_a String)
468
+ expect(comments.keys.map(&:length).uniq).to eq [2]
469
+ expect(comments.values).to all(be_an Integer)
470
+ end
471
+ end
472
+
473
+ describe 'likes can be grouped by day' do
474
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
475
+ let(:keys) { range.values }
476
+
477
+ specify 'with the :by option set to :day' do
478
+ likes = video.likes range.merge by: :day
479
+ expect(likes.keys).to eq range.values
480
+ end
481
+ end
482
+
483
+ describe 'likes can be grouped by country' do
484
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
485
+
486
+ specify 'with the :by option set to :country' do
487
+ likes = video.likes range.merge by: :country
488
+ expect(likes.keys).to all(be_a String)
489
+ expect(likes.keys.map(&:length).uniq).to eq [2]
490
+ expect(likes.values).to all(be_an Integer)
491
+ end
492
+ end
493
+
494
+ describe 'dislikes can be grouped by day' do
495
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
496
+ let(:keys) { range.values }
497
+
498
+ specify 'with the :by option set to :day' do
499
+ dislikes = video.dislikes range.merge by: :day
500
+ expect(dislikes.keys).to eq range.values
501
+ end
502
+ end
503
+
504
+ describe 'dislikes can be grouped by country' do
505
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
506
+
507
+ specify 'with the :by option set to :country' do
508
+ dislikes = video.dislikes range.merge by: :country
509
+ expect(dislikes.keys).to all(be_a String)
510
+ expect(dislikes.keys.map(&:length).uniq).to eq [2]
511
+ expect(dislikes.values).to all(be_an Integer)
512
+ end
513
+ end
514
+
515
+ describe 'shares can be grouped by day' do
516
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
517
+ let(:keys) { range.values }
518
+
519
+ specify 'with the :by option set to :day' do
520
+ shares = video.shares range.merge by: :day
521
+ expect(shares.keys).to eq range.values
522
+ end
523
+ end
524
+
525
+ describe 'shares can be grouped by country' do
526
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
527
+
528
+ specify 'with the :by option set to :country' do
529
+ shares = video.shares range.merge by: :country
530
+ expect(shares.keys).to all(be_a String)
531
+ expect(shares.keys.map(&:length).uniq).to eq [2]
532
+ expect(shares.values).to all(be_an Integer)
533
+ end
534
+ end
535
+
536
+ describe 'gained subscribers 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
+ subscribers_gained = video.subscribers_gained range.merge by: :day
542
+ expect(subscribers_gained.keys).to eq range.values
543
+ end
544
+ end
545
+
546
+ describe 'gained subscribers can be grouped by country' do
547
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
548
+
549
+ specify 'with the :by option set to :country' do
550
+ subscribers_gained = video.subscribers_gained range.merge by: :country
551
+ expect(subscribers_gained.keys).to all(be_a String)
552
+ expect(subscribers_gained.keys.map(&:length).uniq).to eq [2]
553
+ expect(subscribers_gained.values).to all(be_an Integer)
554
+ end
555
+ end
556
+
557
+ describe 'lost subscribers can be grouped by day' do
558
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
559
+ let(:keys) { range.values }
560
+
561
+ specify 'with the :by option set to :day' do
562
+ subscribers_lost = video.subscribers_lost range.merge by: :day
563
+ expect(subscribers_lost.keys).to eq range.values
564
+ end
565
+ end
566
+
567
+ describe 'lost subscribers can be grouped by country' do
568
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
569
+
570
+ specify 'with the :by option set to :country' do
571
+ subscribers_lost = video.subscribers_lost range.merge by: :country
572
+ expect(subscribers_lost.keys).to all(be_a String)
573
+ expect(subscribers_lost.keys.map(&:length).uniq).to eq [2]
574
+ expect(subscribers_lost.values).to all(be_an Integer)
575
+ end
576
+ end
577
+
578
+ describe 'added favorites can be grouped by day' do
579
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
580
+ let(:keys) { range.values }
581
+
582
+ specify 'with the :by option set to :day' do
583
+ favorites_added = video.favorites_added range.merge by: :day
584
+ expect(favorites_added.keys).to eq range.values
585
+ end
586
+ end
587
+
588
+ describe 'added favorites can be grouped by country' do
589
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
590
+
591
+ specify 'with the :by option set to :country' do
592
+ favorites_added = video.favorites_added range.merge by: :country
593
+ expect(favorites_added.keys).to all(be_a String)
594
+ expect(favorites_added.keys.map(&:length).uniq).to eq [2]
595
+ expect(favorites_added.values).to all(be_an Integer)
596
+ end
597
+ end
598
+
599
+ describe 'removed favorites can be grouped by day' do
600
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
601
+ let(:keys) { range.values }
602
+
603
+ specify 'with the :by option set to :day' do
604
+ favorites_removed = video.favorites_removed range.merge by: :day
605
+ expect(favorites_removed.keys).to eq range.values
606
+ end
607
+ end
608
+
609
+ # TODO: Remove "removed favorites" since it’s deprecated!
610
+ # describe 'removed favorites can be grouped by country' do
611
+ # let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
612
+ #
613
+ # specify 'with the :by option set to :country' do
614
+ # favorites_removed = video.favorites_removed range.merge by: :country
615
+ # expect(favorites_removed.keys).to all(be_a String)
616
+ # expect(favorites_removed.keys.map(&:length).uniq).to eq [2]
617
+ # expect(favorites_removed.values).to all(be_an Integer)
618
+ # end
619
+ # end
620
+
621
+ describe 'estimated minutes watched can be retrieved for a single US state' do
622
+ let(:state_code) { 'NY' }
623
+ let(:minutes) { video.estimated_minutes_watched since: date, by: by, in: location }
624
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
625
+
626
+ context 'and grouped by day' do
627
+ let(:by) { :day }
628
+
629
+ context 'with the :in option set to {state: state code}' do
630
+ let(:location) { {state: state_code} }
631
+ it { expect(minutes.keys.min).to eq date.to_date }
632
+ end
633
+
634
+ context 'with the :in option set to {country: "US", state: state code}' do
635
+ let(:location) { {country: 'US', state: state_code} }
636
+ it { expect(minutes.keys.min).to eq date.to_date }
637
+ end
638
+ end
639
+
640
+ context 'and grouped by US state' do
641
+ let(:by) { :state }
642
+
643
+ context 'with the :in option set to {state: state code}' do
644
+ let(:location) { {state: state_code} }
645
+ it { expect(minutes.keys).to eq [state_code] }
646
+ end
647
+
648
+ context 'with the :in option set to {country: "US", state: state code}' do
649
+ let(:location) { {country: 'US', state: state_code} }
650
+ it { expect(minutes.keys).to eq [state_code] }
651
+ end
652
+ end
653
+ end
654
+
655
+ describe 'estimated minutes watched can be grouped by day' do
656
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
657
+ let(:keys) { range.values }
658
+
659
+ specify 'with the :by option set to :day' do
660
+ estimated_minutes_watched = video.estimated_minutes_watched range.merge by: :day
661
+ expect(estimated_minutes_watched.keys).to eq range.values
662
+ end
663
+ end
664
+
665
+ describe 'estimated minutes watched can be grouped by traffic source' do
666
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
667
+ let(:keys) { Yt::Collections::Reports::TRAFFIC_SOURCES.keys }
668
+
669
+ specify 'with the :by option set to :traffic_source' do
670
+ estimated_minutes_watched = video.estimated_minutes_watched range.merge by: :traffic_source
671
+ expect(estimated_minutes_watched.keys - keys).to be_empty
672
+ end
673
+ end
674
+
675
+ describe 'estimated minutes watched can be grouped by playback location' do
676
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
677
+ let(:keys) { Yt::Collections::Reports::PLAYBACK_LOCATIONS.keys }
678
+
679
+ specify 'with the :by option set to :playback_location' do
680
+ estimated_minutes_watched = video.estimated_minutes_watched range.merge by: :playback_location
681
+ expect(estimated_minutes_watched.keys - keys).to be_empty
682
+ end
683
+ end
684
+
685
+ describe 'estimated minutes watched can be grouped by embedded player location' do
686
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
687
+
688
+ specify 'with the :by option set to :embedded_player_location' do
689
+ estimated_minutes_watched = video.estimated_minutes_watched range.merge by: :embedded_player_location
690
+ expect(estimated_minutes_watched).not_to be_empty
691
+ end
692
+ end
693
+
694
+ describe 'estimated minutes watched can be grouped by related video' do
695
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
696
+
697
+ specify 'with the :by option set to :related_video' do
698
+ estimated_minutes_watched = video.estimated_minutes_watched range.merge by: :related_video
699
+ expect(estimated_minutes_watched.keys).to all(be_instance_of Yt::Video)
700
+ end
701
+ end
702
+
703
+ describe 'estimated minutes watched can be grouped by search term' do
704
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
705
+
706
+ specify 'with the :by option set to :search_term' do
707
+ estimated_minutes_watched = video.estimated_minutes_watched range.merge by: :search_term
708
+ expect(estimated_minutes_watched.keys).to all(be_a String)
709
+ end
710
+ end
711
+
712
+ describe 'estimated minutes watched can be grouped by referrer' do
713
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
714
+
715
+ specify 'with the :by option set to :referrer' do
716
+ estimated_minutes_watched = video.estimated_minutes_watched range.merge by: :referrer
717
+ expect(estimated_minutes_watched.keys).to all(be_a String)
718
+ end
719
+ end
720
+
721
+ describe 'estimated minutes watched can be grouped by device type' do
722
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
723
+
724
+ specify 'with the :by option set to :device_type' do
725
+ estimated_minutes_watched = video.estimated_minutes_watched range.merge by: :device_type
726
+ expect(estimated_minutes_watched.keys).to all(be_instance_of Symbol)
727
+ expect(estimated_minutes_watched.values).to all(be_an Integer)
728
+ end
729
+ end
730
+
731
+ describe 'estimated minutes watched can be grouped by country' do
732
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
733
+
734
+ specify 'with the :by option set to :country' do
735
+ minutes = video.estimated_minutes_watched range.merge by: :country
736
+ expect(minutes.keys).to all(be_a String)
737
+ expect(minutes.keys.map(&:length).uniq).to eq [2]
738
+ expect(minutes.values).to all(be_an Integer)
739
+ end
740
+ end
741
+
742
+ describe 'estimated minutes watched can be grouped by state' do
743
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
744
+
745
+ specify 'with the :by option set to :state' do
746
+ minutes = video.estimated_minutes_watched range.merge by: :state
747
+ expect(minutes.keys).to all(be_a String)
748
+ expect(minutes.keys.map(&:length).uniq).to eq [2]
749
+ expect(minutes.values).to all(be_an Integer)
750
+ end
751
+ end
752
+
753
+ describe 'average view duration can be retrieved for a single US state' do
754
+ let(:state_code) { 'NY' }
755
+ let(:duration) { video.average_view_duration since: date, by: by, in: location }
756
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
757
+
758
+ context 'and grouped by day' do
759
+ let(:by) { :day }
760
+
761
+ context 'with the :in option set to {state: state code}' do
762
+ let(:location) { {state: state_code} }
763
+ it { expect(duration.keys.min).to eq date.to_date }
764
+ end
765
+
766
+ context 'with the :in option set to {country: "US", state: state code}' do
767
+ let(:location) { {country: 'US', state: state_code} }
768
+ it { expect(duration.keys.min).to eq date.to_date }
769
+ end
770
+ end
771
+
772
+ context 'and grouped by US state' do
773
+ let(:by) { :state }
774
+
775
+ context 'with the :in option set to {state: state code}' do
776
+ let(:location) { {state: state_code} }
777
+ it { expect(duration.keys).to eq [state_code] }
778
+ end
779
+
780
+ context 'with the :in option set to {country: "US", state: state code}' do
781
+ let(:location) { {country: 'US', state: state_code} }
782
+ it { expect(duration.keys).to eq [state_code] }
783
+ end
784
+ end
785
+ end
786
+
787
+ describe 'average view duration can be grouped by day' do
788
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
789
+ let(:keys) { range.values }
790
+
791
+ specify 'with the :by option set to :day' do
792
+ average_view_duration = video.average_view_duration range.merge by: :day
793
+ expect(average_view_duration.keys).to eq range.values
794
+ end
795
+ end
796
+
797
+ describe 'average view duration can be grouped by country' do
798
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
799
+
800
+ specify 'with the :by option set to :country' do
801
+ duration = video.average_view_duration range.merge by: :country
802
+ expect(duration.keys).to all(be_a String)
803
+ expect(duration.keys.map(&:length).uniq).to eq [2]
804
+ expect(duration.values).to all(be_an Integer)
805
+ end
806
+ end
807
+
808
+ describe 'average view duration can be grouped by state' do
809
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
810
+
811
+ specify 'with the :by option set to :state' do
812
+ duration = video.average_view_duration range.merge by: :state
813
+ expect(duration.keys).to all(be_a String)
814
+ expect(duration.keys.map(&:length).uniq).to eq [2]
815
+ expect(duration.values).to all(be_an Integer)
816
+ end
817
+ end
818
+
819
+ describe 'average view percentage can be retrieved for a single US state' do
820
+ let(:state_code) { 'NY' }
821
+ let(:percentage) { video.average_view_percentage since: date, by: by, in: location }
822
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
823
+
824
+ context 'and grouped by day' do
825
+ let(:by) { :day }
826
+
827
+ context 'with the :in option set to {state: state code}' do
828
+ let(:location) { {state: state_code} }
829
+ it { expect(percentage.keys.min).to eq date.to_date }
830
+ end
831
+
832
+ context 'with the :in option set to {country: "US", state: state code}' do
833
+ let(:location) { {country: 'US', state: state_code} }
834
+ it { expect(percentage.keys.min).to eq date.to_date }
835
+ end
836
+ end
837
+
838
+ context 'and grouped by US state' do
839
+ let(:by) { :state }
840
+
841
+ context 'with the :in option set to {state: state code}' do
842
+ let(:location) { {state: state_code} }
843
+ it { expect(percentage.keys).to eq [state_code] }
844
+ end
845
+
846
+ context 'with the :in option set to {country: "US", state: state code}' do
847
+ let(:location) { {country: 'US', state: state_code} }
848
+ it { expect(percentage.keys).to eq [state_code] }
849
+ end
850
+ end
851
+ end
852
+
853
+ describe 'average view percentage can be grouped by day' do
854
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
855
+ let(:keys) { range.values }
856
+
857
+ specify 'with the :by option set to :day' do
858
+ average_view_percentage = video.average_view_percentage range.merge by: :day
859
+ expect(average_view_percentage.keys).to eq range.values
860
+ end
861
+ end
862
+
863
+ describe 'average view percentage can be grouped by country' do
864
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
865
+
866
+ specify 'with the :by option set to :country' do
867
+ percentage = video.average_view_percentage range.merge by: :country
868
+ expect(percentage.keys).to all(be_a String)
869
+ expect(percentage.keys.map(&:length).uniq).to eq [2]
870
+ expect(percentage.values).to all(be_a Float)
871
+ end
872
+ end
873
+
874
+ describe 'average view percentage can be grouped by state' do
875
+ let(:range) { {since: 4.days.ago, until: 3.days.ago} }
876
+
877
+ specify 'with the :by option set to :state' do
878
+ percentage = video.average_view_percentage range.merge by: :state
879
+ expect(percentage.keys).to all(be_a String)
880
+ expect(percentage.keys.map(&:length).uniq).to eq [2]
881
+ expect(percentage.values).to all(be_a Float)
882
+ end
883
+ end
884
+
885
+ describe 'impressions can be grouped by day' do
886
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
887
+ let(:keys) { range.values }
888
+
889
+ specify 'with the :by option set to :day' do
890
+ impressions = video.impressions range.merge by: :day
891
+ expect(impressions.keys).to eq range.values
892
+ end
893
+ end
894
+
895
+ describe 'impressions can be grouped by country' do
896
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
897
+
898
+ specify 'with the :by option set to :country' do
899
+ impressions = video.impressions range.merge by: :country
900
+ expect(impressions.keys).to all(be_a String)
901
+ expect(impressions.keys.map(&:length).uniq).to eq [2]
902
+ expect(impressions.values).to all(be_an Integer)
903
+ end
904
+ end
905
+
906
+ describe 'monetized_playbacks can be grouped by day' do
907
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
908
+ let(:keys) { range.values }
909
+
910
+ specify 'with the :by option set to :day' do
911
+ monetized_playbacks = video.monetized_playbacks range.merge by: :day
912
+ expect(monetized_playbacks.keys).to eq range.values
913
+ end
914
+ end
915
+
916
+ describe 'monetized playbacks can be grouped by country' do
917
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
918
+
919
+ specify 'with the :by option set to :country' do
920
+ playbacks = video.monetized_playbacks range.merge by: :country
921
+ expect(playbacks.keys).to all(be_a String)
922
+ expect(playbacks.keys.map(&:length).uniq).to eq [2]
923
+ expect(playbacks.values).to all(be_an Integer)
924
+ end
925
+ end
926
+
927
+ describe 'playback-based CPM can be grouped by day' do
928
+ let(:range) { {since: 4.days.ago.to_date, until: 3.days.ago.to_date} }
929
+ let(:keys) { range.values }
930
+
931
+ specify 'with the :by option set to :day' do
932
+ playback_based_cpm = video.playback_based_cpm range.merge by: :day
933
+ expect(playback_based_cpm.keys).to eq range.values
934
+ end
935
+ end
936
+
937
+ describe 'playback-based CPM can be grouped by country' do
938
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
939
+
940
+ specify 'with the :by option set to :country' do
941
+ playbacks = video.playback_based_cpm range.merge by: :country
942
+ expect(playbacks.keys).to all(be_a String)
943
+ expect(playbacks.keys.map(&:length).uniq).to eq [2]
944
+ expect(playbacks.values).to all(be_a Float)
945
+ end
946
+ end
947
+
948
+ describe 'annotation clicks can be retrieved for a single US state' do
949
+ let(:state_code) { 'CA' }
950
+ let(:clicks) { video.annotation_clicks since: date, by: by, in: location }
951
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
952
+
953
+ context 'and grouped by day' do
954
+ let(:by) { :day }
955
+
956
+ context 'with the :in option set to {state: state code}' do
957
+ let(:location) { {state: state_code} }
958
+ it { expect(clicks.keys.min).to eq date.to_date }
959
+ end
960
+
961
+ context 'with the :in option set to {country: "US", state: state code}' do
962
+ let(:location) { {country: 'US', state: state_code} }
963
+ it { expect(clicks.keys.min).to eq date.to_date }
964
+ end
965
+ end
966
+
967
+ context 'and grouped by US state' do
968
+ let(:by) { :state }
969
+
970
+ context 'with the :in option set to {state: state code}' do
971
+ let(:location) { {state: state_code} }
972
+ it { expect(clicks.keys).to eq [state_code] }
973
+ end
974
+
975
+ context 'with the :in option set to {country: "US", state: state code}' do
976
+ let(:location) { {country: 'US', state: state_code} }
977
+ it { expect(clicks.keys).to eq [state_code] }
978
+ end
979
+ end
980
+ end
981
+
982
+ describe 'annotation clicks can be grouped by day' do
983
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE'], until: Date.parse(ENV['YT_TEST_PARTNER_VIDEO_DATE']) + 5} }
984
+
985
+ specify 'with the :by option set to :day' do
986
+ annotation_clicks = video.annotation_clicks range.merge by: :day
987
+ expect(annotation_clicks.values).to all(be_an Integer)
988
+ end
989
+ end
990
+
991
+ describe 'annotation clicks can be grouped by country' do
992
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
993
+
994
+ specify 'with the :by option set to :country' do
995
+ clicks = video.annotation_clicks range.merge by: :country
996
+ expect(clicks.keys).to all(be_a String)
997
+ expect(clicks.keys.map(&:length).uniq).to eq [2]
998
+ expect(clicks.values).to all(be_an Integer)
999
+ end
1000
+ end
1001
+
1002
+ describe 'annotation clicks can be grouped by state' do
1003
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
1004
+
1005
+ specify 'with the :by option set to :state' do
1006
+ clicks = video.annotation_clicks range.merge by: :state
1007
+ expect(clicks.keys).to all(be_a String)
1008
+ expect(clicks.keys.map(&:length).uniq).to eq [2]
1009
+ expect(clicks.values).to all(be_an Integer)
1010
+ end
1011
+ end
1012
+
1013
+ describe 'annotation click_through_rate can be retrieved for a single US state' do
1014
+ let(:state_code) { 'CA' }
1015
+ let(:click_through_rate) { video.annotation_click_through_rate since: date, by: by, in: location }
1016
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
1017
+
1018
+ context 'and grouped by day' do
1019
+ let(:by) { :day }
1020
+
1021
+ context 'with the :in option set to {state: state code}' do
1022
+ let(:location) { {state: state_code} }
1023
+ it { expect(click_through_rate.keys.min).to eq date.to_date }
1024
+ end
1025
+
1026
+ context 'with the :in option set to {country: "US", state: state code}' do
1027
+ let(:location) { {country: 'US', state: state_code} }
1028
+ it { expect(click_through_rate.keys.min).to eq date.to_date }
1029
+ end
1030
+ end
1031
+
1032
+ context 'and grouped by US state' do
1033
+ let(:by) { :state }
1034
+
1035
+ context 'with the :in option set to {state: state code}' do
1036
+ let(:location) { {state: state_code} }
1037
+ it { expect(click_through_rate.keys).to eq [state_code] }
1038
+ end
1039
+
1040
+ context 'with the :in option set to {country: "US", state: state code}' do
1041
+ let(:location) { {country: 'US', state: state_code} }
1042
+ it { expect(click_through_rate.keys).to eq [state_code] }
1043
+ end
1044
+ end
1045
+ end
1046
+
1047
+ describe 'annotation click-through rate can be grouped by day' do
1048
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE'], until: Date.parse(ENV['YT_TEST_PARTNER_VIDEO_DATE']) + 5} }
1049
+
1050
+ specify 'with the :by option set to :day' do
1051
+ annotation_click_through_rate = video.annotation_click_through_rate range.merge by: :day
1052
+ expect(annotation_click_through_rate.values).to all(be_instance_of Float)
1053
+ end
1054
+ end
1055
+
1056
+ describe 'annotation click-through rate can be grouped by country' do
1057
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
1058
+
1059
+ specify 'with the :by option set to :country' do
1060
+ rate = video.annotation_click_through_rate range.merge by: :country
1061
+ expect(rate.keys).to all(be_a String)
1062
+ expect(rate.keys.map(&:length).uniq).to eq [2]
1063
+ expect(rate.values).to all(be_a Float)
1064
+ end
1065
+ end
1066
+
1067
+ describe 'annotation click-through rate can be grouped by state' do
1068
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
1069
+
1070
+ specify 'with the :by option set to :state' do
1071
+ rate = video.annotation_click_through_rate range.merge by: :state
1072
+ expect(rate.keys).to all(be_a String)
1073
+ expect(rate.keys.map(&:length).uniq).to eq [2]
1074
+ expect(rate.values).to all(be_a Float)
1075
+ end
1076
+ end
1077
+
1078
+ describe 'annotation close_rate can be retrieved for a single US state' do
1079
+ let(:state_code) { 'CA' }
1080
+ let(:close_rate) { video.annotation_close_rate since: date, by: by, in: location }
1081
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
1082
+
1083
+ context 'and grouped by day' do
1084
+ let(:by) { :day }
1085
+
1086
+ context 'with the :in option set to {state: state code}' do
1087
+ let(:location) { {state: state_code} }
1088
+ it { expect(close_rate.keys.min).to eq date.to_date }
1089
+ end
1090
+
1091
+ context 'with the :in option set to {country: "US", state: state code}' do
1092
+ let(:location) { {country: 'US', state: state_code} }
1093
+ it { expect(close_rate.keys.min).to eq date.to_date }
1094
+ end
1095
+ end
1096
+
1097
+ context 'and grouped by US state' do
1098
+ let(:by) { :state }
1099
+
1100
+ context 'with the :in option set to {state: state code}' do
1101
+ let(:location) { {state: state_code} }
1102
+ it { expect(close_rate.keys).to eq [state_code] }
1103
+ end
1104
+
1105
+ context 'with the :in option set to {country: "US", state: state code}' do
1106
+ let(:location) { {country: 'US', state: state_code} }
1107
+ it { expect(close_rate.keys).to eq [state_code] }
1108
+ end
1109
+ end
1110
+ end
1111
+
1112
+ describe 'annotation close rate can be grouped by day' do
1113
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE'], until: Date.parse(ENV['YT_TEST_PARTNER_VIDEO_DATE']) + 5} }
1114
+
1115
+ specify 'with the :by option set to :day' do
1116
+ annotation_close_rate = video.annotation_close_rate range.merge by: :day
1117
+ expect(annotation_close_rate.values).to all(be_instance_of Float)
1118
+ end
1119
+ end
1120
+
1121
+ describe 'annotation close rate can be grouped by country' do
1122
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
1123
+
1124
+ specify 'with the :by option set to :country' do
1125
+ rate = video.annotation_close_rate range.merge by: :country
1126
+ expect(rate.keys).to all(be_a String)
1127
+ expect(rate.keys.map(&:length).uniq).to eq [2]
1128
+ expect(rate.values).to all(be_a Float)
1129
+ end
1130
+ end
1131
+
1132
+ describe 'annotation close rate can be grouped by state' do
1133
+ let(:range) { {since: ENV['YT_TEST_PARTNER_VIDEO_DATE']} }
1134
+
1135
+ specify 'with the :by option set to :state' do
1136
+ rate = video.annotation_close_rate range.merge by: :state
1137
+ expect(rate.keys).to all(be_a String)
1138
+ expect(rate.keys.map(&:length).uniq).to eq [2]
1139
+ expect(rate.values).to all(be_a Float)
1140
+ end
1141
+ end
1142
+
1143
+ describe 'viewer percentage can be retrieved for a range of days' do
1144
+ let(:viewer_percentage) { video.viewer_percentage since: 1.year.ago, until: 10.days.ago}
1145
+ it { expect(viewer_percentage).to be_a Hash }
1146
+ end
1147
+
1148
+ describe 'viewer_percentage can be grouped by gender and age group' do
1149
+ let(:range) { {since: 1.year.ago.to_date, until: 1.week.ago.to_date} }
1150
+ let(:keys) { range.values }
1151
+
1152
+ specify 'without a :by option (default)' do
1153
+ viewer_percentage = video.viewer_percentage range
1154
+ expect(viewer_percentage.keys).to match_array [:female, :male]
1155
+ expect(viewer_percentage[:female].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
1156
+ expect(viewer_percentage[:female].values).to all(be_instance_of Float)
1157
+ expect(viewer_percentage[:male].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
1158
+ expect(viewer_percentage[:male].values).to all(be_instance_of Float)
1159
+ end
1160
+
1161
+ specify 'with the :by option set to :gender_age_group' do
1162
+ viewer_percentage = video.viewer_percentage range.merge by: :gender_age_group
1163
+ expect(viewer_percentage.keys).to match_array [:female, :male]
1164
+ expect(viewer_percentage[:female].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
1165
+ expect(viewer_percentage[:female].values).to all(be_instance_of Float)
1166
+ expect(viewer_percentage[:male].keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
1167
+ expect(viewer_percentage[:male].values).to all(be_instance_of Float)
1168
+ end
1169
+ end
1170
+
1171
+ describe 'viewer percentage can be retrieved for a single country' do
1172
+ let(:country_code) { 'US' }
1173
+ let(:viewer_percentage) { video.viewer_percentage since: date, in: location }
1174
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
1175
+
1176
+ context 'with the :in option set to the country code' do
1177
+ let(:location) { country_code }
1178
+ it { expect(viewer_percentage.keys).to match_array [:female, :male] }
1179
+ end
1180
+
1181
+ context 'with the :in option set to {country: country code}' do
1182
+ let(:location) { {country: country_code} }
1183
+ it { expect(viewer_percentage.keys).to match_array [:female, :male] }
1184
+ end
1185
+ end
1186
+
1187
+ describe 'viewer percentage can be retrieved for a single US state' do
1188
+ let(:state_code) { 'CA' }
1189
+ let(:viewer_percentage) { video.viewer_percentage since: date, in: location }
1190
+ let(:date) { ENV['YT_TEST_PARTNER_VIDEO_DATE'] }
1191
+
1192
+ context 'with the :in option set to {state: state code}' do
1193
+ let(:location) { {state: state_code} }
1194
+ it { expect(viewer_percentage.keys).to match_array [:female, :male] }
1195
+ end
1196
+
1197
+ context 'with the :in option set to {country: "US", state: state code}' do
1198
+ let(:location) { {country: 'US', state: state_code} }
1199
+ it { expect(viewer_percentage.keys).to match_array [:female, :male] }
1200
+ end
1201
+ end
1202
+
1203
+ describe 'viewer percentage can be grouped by gender' do
1204
+ let(:range) { {since: 1.year.ago.to_date, until: 1.week.ago.to_date} }
1205
+ let(:keys) { range.values }
1206
+
1207
+ specify 'with the :by option set to :gender' do
1208
+ viewer_percentage = video.viewer_percentage range.merge by: :gender
1209
+ expect(viewer_percentage.keys).to match_array [:female, :male]
1210
+ expect(viewer_percentage[:female]).to be_a Float
1211
+ expect(viewer_percentage[:male]).to be_a Float
1212
+ end
1213
+ end
1214
+
1215
+ describe 'viewer_percentage can be grouped by age group' do
1216
+ let(:range) { {since: 1.year.ago.to_date, until: 1.week.ago.to_date} }
1217
+ let(:keys) { range.values }
1218
+
1219
+ specify 'with the :by option set to :age_group' do
1220
+ viewer_percentage = video.viewer_percentage range.merge by: :age_group
1221
+ expect(viewer_percentage.keys - %w(65- 35-44 45-54 13-17 25-34 55-64 18-24)).to be_empty
1222
+ expect(viewer_percentage.values).to all(be_instance_of Float)
1223
+ end
1224
+ end
1225
+ end
1226
+
1227
+ context 'given a video claimable by the authenticated Content Owner' do
1228
+ let(:id) { ENV['YT_TEST_PARTNER_CLAIMABLE_VIDEO_ID'] }
1229
+
1230
+ describe 'the advertising formats can be updated and retrieved' do
1231
+ let!(:old_formats) { video.ad_formats }
1232
+ let!(:new_formats) { %w(standard_instream overlay trueview_instream).sample(2) }
1233
+ before { video.advertising_options_set.update ad_formats: new_formats }
1234
+ it { expect(video.ad_formats).to match_array new_formats }
1235
+ after { video.advertising_options_set.update ad_formats: old_formats }
1236
+ end
1237
+ end
1238
+ end
1239
+ end