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,57 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/resource'
3
+
4
+ describe Yt::Resource do
5
+ subject(:resource) { Yt::Resource.new attrs }
6
+
7
+ context 'given a resource initialized with a URL (containing an ID)' do
8
+ let(:attrs) { {url: 'youtu.be/MESycYJytkU'} }
9
+
10
+ it { expect(resource.id).to eq 'MESycYJytkU' }
11
+ it { expect(resource.kind).to eq 'video' }
12
+ it { expect(resource.username).to be_nil }
13
+ end
14
+
15
+ context 'given a resource initialized with a URL (containing a username)' do
16
+ let(:attrs) { {url: 'youtube.com/fullscreen'} }
17
+
18
+ it { expect(resource.kind).to eq 'channel' }
19
+ it { expect(resource.username).to eq 'fullscreen' }
20
+ end
21
+
22
+ describe '#public?' do
23
+ context 'given fetching a status returns privacyStatus "public"' do
24
+ let(:attrs) { {status: {"privacyStatus"=>"public"}} }
25
+ it { expect(resource).to be_public }
26
+ end
27
+
28
+ context 'given fetching a status does not return privacyStatus "public"' do
29
+ let(:attrs) { {status: {}} }
30
+ it { expect(resource).not_to be_public }
31
+ end
32
+ end
33
+
34
+ describe '#private?' do
35
+ context 'given fetching a status returns privacyStatus "private"' do
36
+ let(:attrs) { {status: {"privacyStatus"=>"private"}} }
37
+ it { expect(resource).to be_private }
38
+ end
39
+
40
+ context 'given fetching a status does not return privacyStatus "private"' do
41
+ let(:attrs) { {status: {}} }
42
+ it { expect(resource).not_to be_private }
43
+ end
44
+ end
45
+
46
+ describe '#unlisted?' do
47
+ context 'given fetching a status returns privacyStatus "unlisted"' do
48
+ let(:attrs) { {status: {"privacyStatus"=>"unlisted"}} }
49
+ it { expect(resource).to be_unlisted }
50
+ end
51
+
52
+ context 'given fetching a status does not return privacyStatus "unlisted"' do
53
+ let(:attrs) { {status: {}} }
54
+ it { expect(resource).not_to be_unlisted }
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/right_owner'
3
+
4
+ describe Yt::RightOwner do
5
+ subject(:right_owner) { Yt::RightOwner.new data: data }
6
+ let(:data) { {} }
7
+
8
+ describe '#ratio' do
9
+ context 'given fetching an owner returns a ratio' do
10
+ let(:data) { {"ratio"=>"20.0"} }
11
+ it { expect(right_owner.ratio).to eq 20 }
12
+ end
13
+ end
14
+
15
+ describe '#owner' do
16
+ context 'given fetching an owner returns an owner name' do
17
+ let(:data) { {"owner"=>"FullScreen"} }
18
+ it { expect(right_owner.owner).to eq 'FullScreen' }
19
+ end
20
+ end
21
+
22
+ describe '#owner' do
23
+ context 'given fetching an owner returns a publisher name' do
24
+ let(:data) { {"publisher"=>"Third Party"} }
25
+ it { expect(right_owner.publisher).to eq 'Third Party' }
26
+ end
27
+
28
+ context 'given fetching an owner does not return a publisher name' do
29
+ it { expect(right_owner.publisher).to be_nil }
30
+ end
31
+ end
32
+
33
+ describe '#included_territories' do
34
+ context 'given fetching an owner returns included territories' do
35
+ let(:data) { {"type"=>"include", "territories"=>["US", "CA"]} }
36
+ it { expect(right_owner.included_territories).to eq %w(US CA) }
37
+ end
38
+
39
+ context 'given fetching an owner does not return included territories' do
40
+ it { expect(right_owner.included_territories).to be_nil }
41
+ end
42
+ end
43
+
44
+ describe '#excluded_territories' do
45
+ context 'given fetching an owner returns excluded territories' do
46
+ let(:data) { {"type"=>"exclude", "territories"=>["US", "CA"]} }
47
+ it { expect(right_owner.excluded_territories).to eq %w(US CA) }
48
+ end
49
+
50
+ context 'given fetching an owner does not return excluded territories' do
51
+ it { expect(right_owner.excluded_territories).to be_nil }
52
+ end
53
+ end
54
+
55
+ describe '#everywhere?' do
56
+ context 'given fetching an owner returns zero excluded territories' do
57
+ let(:data) { {"type"=>"exclude", "territories"=>[]} }
58
+ it { expect(right_owner).to be_everywhere }
59
+ end
60
+
61
+ context 'given fetching an owner returns no excluded territories' do
62
+ let(:data) { {"type"=>"exclude"} }
63
+ it { expect(right_owner).to be_everywhere }
64
+ end
65
+
66
+ context 'given fetching an owner returns included territories' do
67
+ let(:data) { {"type"=>"include", "territories"=>[]} }
68
+ it { expect(right_owner).not_to be_everywhere }
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/snippet'
3
+
4
+ describe Yt::Snippet do
5
+ subject(:snippet) { Yt::Snippet.new data: data }
6
+
7
+ describe '#data' do
8
+ let(:data) { {"key"=>"value"} }
9
+ specify 'returns the data the snippet was initialized with' do
10
+ expect(snippet.data).to eq data
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/statistics_set'
3
+
4
+ describe Yt::StatisticsSet do
5
+ subject(:statistics_set) { Yt::StatisticsSet.new data: data }
6
+
7
+ describe '#data' do
8
+ let(:data) { {"key"=>"value"} }
9
+ specify 'returns the data the statistics set was initialized with' do
10
+ expect(statistics_set.data).to eq data
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/status'
3
+
4
+ describe Yt::Status do
5
+ subject(:status) { Yt::Status.new data: data }
6
+
7
+ describe '#data' do
8
+ let(:data) { {"key"=>"value"} }
9
+ specify 'returns the data the status was initialized with' do
10
+ expect(status.data).to eq data
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/subscription'
3
+
4
+ describe Yt::Subscription do
5
+ subject(:subscription) { Yt::Subscription.new id: id }
6
+
7
+ describe '#exists?' do
8
+ context 'given a subscription with an id' do
9
+ let(:id) { 'CBl6OoF0BpiV' }
10
+ it { expect(subscription).to exist }
11
+ end
12
+
13
+ context 'given a subscription without an id' do
14
+ let(:id) { nil }
15
+ it { expect(subscription).not_to exist }
16
+ end
17
+ end
18
+
19
+ describe '#delete' do
20
+ let(:id) { 'CBl6OoF0BpiV' }
21
+ before { expect(subscription).to behave }
22
+
23
+ context 'given an existing subscription' do
24
+ let(:behave) { receive(:do_delete).and_yield }
25
+
26
+ it { expect(subscription.delete).to be true }
27
+ it { expect{subscription.delete}.to change{subscription.exists?} }
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,78 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+ require 'yt/models/url'
5
+
6
+ describe Yt::URL do
7
+ subject(:url) { Yt::URL.new text }
8
+
9
+ context 'given a long video URL' do
10
+ let(:text) { 'youtube.com/watch?v=MESycYJytkU' }
11
+ it {expect(url.kind).to eq :video }
12
+ it {expect(url.id).to eq 'MESycYJytkU' }
13
+ it {expect(url.username).to be_nil }
14
+ end
15
+
16
+ context 'given a short video URL' do
17
+ let(:text) { 'https://youtu.be/MESycYJytkU' }
18
+ it {expect(url.kind).to eq :video }
19
+ it {expect(url.id).to eq 'MESycYJytkU' }
20
+ end
21
+
22
+ context 'given an embed video URL' do
23
+ let(:text) { 'https://www.youtube.com/embed/MESycYJytkU' }
24
+ it {expect(url.kind).to eq :video }
25
+ it {expect(url.id).to eq 'MESycYJytkU' }
26
+ end
27
+
28
+ context 'given a playlist-embedded video URL' do
29
+ let(:text) { 'youtube.com/watch?v=MESycYJytkU&list=LLxO1tY8h1AhOz0T4ENwmpow' }
30
+ it {expect(url.kind).to eq :video }
31
+ it {expect(url.id).to eq 'MESycYJytkU' }
32
+ end
33
+
34
+ context 'given a long channel URL' do
35
+ let(:text) { 'http://youtube.com/channel/UCxO1tY8h1AhOz0T4ENwmpow' }
36
+ it {expect(url.kind).to eq :channel }
37
+ it {expect(url.id).to eq 'UCxO1tY8h1AhOz0T4ENwmpow' }
38
+ end
39
+
40
+ context 'given a short channel URL' do
41
+ let(:text) { 'https://www.youtube.com/Fullscreen' }
42
+ it {expect(url.kind).to eq :channel }
43
+ it {expect(url.username).to eq 'Fullscreen' }
44
+ it {expect(url.id).to be_nil }
45
+ end
46
+
47
+ context 'given a user’s channel URL' do
48
+ let(:text) { 'https://www.youtube.com/user/Fullscreen' }
49
+ it {expect(url.kind).to eq :channel }
50
+ it {expect(url.username).to eq 'Fullscreen' }
51
+ end
52
+
53
+ context 'given a subscription center URL' do
54
+ let(:text) { 'youtube.com/subscription_center?add_user=Fullscreen' }
55
+ it {expect(url.kind).to eq :subscription }
56
+ end
57
+
58
+ context 'given a subscription widget URL' do
59
+ let(:text) { 'youtube.com/subscribe_widget?p=Fullscreen' }
60
+ it {expect(url.kind).to eq :subscription }
61
+ end
62
+
63
+ context 'given a subscription confirmation URL' do
64
+ let(:text) { 'youtube.com/channel/UCxO1tY8h1AhOz0T4ENwmpow?sub_confirmation=1' }
65
+ it {expect(url.kind).to eq :subscription }
66
+ end
67
+
68
+ context 'given a long playlist URL' do
69
+ let(:text) { 'youtube.com/playlist?list=LLxO1tY8h1AhOz0T4ENwmpow' }
70
+ it {expect(url.kind).to eq :playlist }
71
+ it {expect(url.id).to eq 'LLxO1tY8h1AhOz0T4ENwmpow' }
72
+ end
73
+
74
+ context 'given a valid URL with a trailing slash' do
75
+ let(:text) { 'https://www.youtube.com/user/Fullscreen/' }
76
+ it {expect(url.kind).to eq :channel }
77
+ end
78
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/video_category'
3
+
4
+ describe Yt::VideoCategory do
5
+ subject(:video_category) { Yt::VideoCategory.new attrs }
6
+
7
+ describe '#id' do
8
+ context 'given fetching a video category returns an id' do
9
+ let(:attrs) { {id: "22"} }
10
+ it { expect(video_category.id).to eq '22' }
11
+ end
12
+ end
13
+
14
+ describe '#snippet' do
15
+ context 'given fetching a video category returns a snippet' do
16
+ let(:attrs) { {snippet: {"title": "People & Blogs", "assignable": true}} }
17
+
18
+ it { expect(video_category.snippet).to be_a Yt::Snippet }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,669 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/video'
3
+
4
+ describe Yt::Video do
5
+ subject(:video) { Yt::Video.new attrs }
6
+
7
+ describe '#snippet' do
8
+ context 'given fetching a video returns a snippet' do
9
+ let(:attrs) { {snippet: {"title"=>"Fullscreen Creator Platform"}} }
10
+ it { expect(video.snippet).to be_a Yt::Snippet }
11
+ end
12
+ end
13
+
14
+
15
+ describe '#title' do
16
+ context 'given a snippet with a title' do
17
+ let(:attrs) { {snippet: {"title"=>"Fullscreen Creator Platform"}} }
18
+ it { expect(video.title).to eq 'Fullscreen Creator Platform' }
19
+ end
20
+
21
+ context 'given a snippet without a title' do
22
+ let(:attrs) { {snippet: {}} }
23
+ it { expect(video.title).to eq '' }
24
+ end
25
+ end
26
+
27
+ describe '#description' do
28
+ context 'given a snippet with a description' do
29
+ let(:attrs) { {snippet: {"description"=>"A cool video."}} }
30
+ it { expect(video.description).to eq 'A cool video.' }
31
+ end
32
+
33
+ context 'given a snippet without a description' do
34
+ let(:attrs) { {snippet: {}} }
35
+ it { expect(video.description).to eq '' }
36
+ end
37
+ end
38
+
39
+ describe '#thumbnail_url' do
40
+ context 'given a snippet with thumbnails' do
41
+ let(:attrs) { {snippet: {"thumbnails"=>{
42
+ "default"=>{"url"=> "http://example.com/120x90.jpg"},
43
+ "medium"=>{"url"=> "http://example.com/320x180.jpg"},
44
+ }}} }
45
+ it { expect(video.thumbnail_url).to eq 'http://example.com/120x90.jpg' }
46
+ it { expect(video.thumbnail_url 'default').to eq 'http://example.com/120x90.jpg' }
47
+ it { expect(video.thumbnail_url :default).to eq 'http://example.com/120x90.jpg' }
48
+ it { expect(video.thumbnail_url :medium).to eq 'http://example.com/320x180.jpg' }
49
+ it { expect(video.thumbnail_url :high).to be_nil }
50
+ end
51
+
52
+ context 'given a snippet without thumbnails' do
53
+ let(:attrs) { {snippet: {}} }
54
+ it { expect(video.thumbnail_url).to be_nil }
55
+ end
56
+ end
57
+
58
+ describe '#published_at' do
59
+ context 'given a snippet with a timestamp' do
60
+ let(:attrs) { {snippet: {"publishedAt"=>"2014-04-22T19:14:49.000Z"}} }
61
+ it { expect(video.published_at.year).to be 2014 }
62
+ end
63
+ end
64
+
65
+ describe '#channel_id' do
66
+ context 'given a snippet with a channel ID' do
67
+ let(:attrs) { {snippet: {"channelId"=>"UCxO1tY8h1AhOz0T4ENwmpow"}} }
68
+ it { expect(video.channel_id).to eq 'UCxO1tY8h1AhOz0T4ENwmpow' }
69
+ end
70
+
71
+ context 'given a snippet without a channel ID' do
72
+ let(:attrs) { {snippet: {}} }
73
+ it { expect(video.channel_id).to be_nil }
74
+ end
75
+ end
76
+
77
+ describe '#channel_title' do
78
+ context 'given a snippet with a channel title' do
79
+ let(:attrs) { {snippet: {"channelTitle"=>"Fullscreen"}} }
80
+ it { expect(video.channel_title).to eq 'Fullscreen' }
81
+ end
82
+
83
+ context 'given a snippet without a channel title' do
84
+ let(:attrs) { {snippet: {}} }
85
+ it { expect(video.channel_title).to be_nil }
86
+ end
87
+ end
88
+
89
+ describe '#live_broadcast_content' do
90
+ context 'given a snippet with live broadcast content' do
91
+ let(:attrs) { {snippet: {"liveBroadcastContent"=>"live"}} }
92
+ it { expect(video.live_broadcast_content).to eq 'live' }
93
+ end
94
+
95
+ context 'given a snippet without live broadcast content' do
96
+ let(:attrs) { {snippet: {}} }
97
+ it { expect(video.live_broadcast_content).to be_nil }
98
+ end
99
+ end
100
+
101
+ describe '#tags' do
102
+ context 'given a snippet with tags' do
103
+ let(:attrs) { {snippet: {"tags"=>["promotion", "new media"]}} }
104
+ it { expect(video.tags).to eq ["promotion", "new media"] }
105
+ end
106
+
107
+ context 'given a snippet without tags' do
108
+ let(:attrs) { {snippet: {}} }
109
+ it { expect(video.tags).to eq [] }
110
+ end
111
+ end
112
+
113
+ describe '#category_id' do
114
+ context 'given a snippet with a category ID' do
115
+ let(:attrs) { {snippet: {"categoryId"=>"22"}} }
116
+ it { expect(video.category_id).to eq '22' }
117
+ end
118
+
119
+ context 'given a snippet without a category ID' do
120
+ let(:attrs) { {snippet: {}} }
121
+ it { expect(video.category_id).to be_nil }
122
+ end
123
+ end
124
+
125
+
126
+ describe '#deleted?' do
127
+ context 'given fetching a status returns uploadStatus "deleted"' do
128
+ let(:attrs) { {status: {"uploadStatus"=>"deleted"}} }
129
+ it { expect(video).to be_deleted }
130
+ end
131
+
132
+ context 'given fetching a status does not return uploadStatus "deleted"' do
133
+ let(:attrs) { {status: {"uploadStatus"=>"uploaded"}} }
134
+ it { expect(video).not_to be_deleted }
135
+ end
136
+ end
137
+
138
+ describe '#failed?' do
139
+ context 'given fetching a status returns uploadStatus "failed"' do
140
+ let(:attrs) { {status: {"uploadStatus"=>"failed"}} }
141
+ it { expect(video).to be_failed }
142
+ end
143
+
144
+ context 'given fetching a status does not return uploadStatus "failed"' do
145
+ let(:attrs) { {status: {"uploadStatus"=>"uploaded"}} }
146
+ it { expect(video).not_to be_failed }
147
+ end
148
+ end
149
+
150
+ describe '#processed?' do
151
+ context 'given fetching a status returns uploadStatus "processed"' do
152
+ let(:attrs) { {status: {"uploadStatus"=>"processed"}} }
153
+ it { expect(video).to be_processed }
154
+ end
155
+
156
+ context 'given fetching a status does not return uploadStatus "processed"' do
157
+ let(:attrs) { {status: {"uploadStatus"=>"uploaded"}} }
158
+ it { expect(video).not_to be_processed }
159
+ end
160
+ end
161
+
162
+ describe '#rejected?' do
163
+ context 'given fetching a status returns uploadStatus "rejected"' do
164
+ let(:attrs) { {status: {"uploadStatus"=>"rejected"}} }
165
+ it { expect(video).to be_rejected }
166
+ end
167
+
168
+ context 'given fetching a status does not return uploadStatus "rejected"' do
169
+ let(:attrs) { {status: {"uploadStatus"=>"uploaded"}} }
170
+ it { expect(video).not_to be_rejected }
171
+ end
172
+ end
173
+
174
+ describe '#uploading?' do
175
+ context 'given fetching a status returns uploadStatus "uploaded"' do
176
+ let(:attrs) { {status: {"uploadStatus"=>"uploaded"}} }
177
+ it { expect(video).to be_uploading }
178
+ end
179
+
180
+ context 'given fetching a status does not return uploadStatus "uploaded"' do
181
+ let(:attrs) { {status: {"uploadStatus"=>"failed"}} }
182
+ it { expect(video).not_to be_uploading }
183
+ end
184
+ end
185
+
186
+ describe '#uses_unsupported_codec?' do
187
+ context 'given fetching a status returns failureReason "codec"' do
188
+ let(:attrs) { {status: {"uploadStatus"=>"failed", "failureReason"=>"codec"}} }
189
+ it { expect(video.uses_unsupported_codec?).to be true }
190
+ end
191
+
192
+ context 'given fetching a status does not return failureReason "codec"' do
193
+ let(:attrs) { {status: {"uploadStatus"=>"failed"}} }
194
+ it { expect(video.uses_unsupported_codec?).to be false }
195
+ end
196
+ end
197
+
198
+ describe '#conversion_failed?' do
199
+ context 'given fetching a status returns failureReason "conversion"' do
200
+ let(:attrs) { {status: {"uploadStatus"=>"failed", "failureReason"=>"conversion"}} }
201
+ it { expect(video).to have_failed_conversion }
202
+ end
203
+
204
+ context 'given fetching a status does not return failureReason "conversion"' do
205
+ let(:attrs) { {status: {"uploadStatus"=>"failed"}} }
206
+ it { expect(video).not_to have_failed_conversion }
207
+ end
208
+ end
209
+
210
+ describe '#empty_file?' do
211
+ context 'given fetching a status returns failureReason "emptyFile"' do
212
+ let(:attrs) { {status: {"uploadStatus"=>"failed", "failureReason"=>"emptyFile"}} }
213
+ it { expect(video).to be_empty }
214
+ end
215
+
216
+ context 'given fetching a status does not return failureReason "emptyFile"' do
217
+ let(:attrs) { {status: {"uploadStatus"=>"failed"}} }
218
+ it { expect(video).not_to be_empty }
219
+ end
220
+ end
221
+
222
+ describe '#invalid_file?' do
223
+ context 'given fetching a status returns failureReason "invalidFile"' do
224
+ let(:attrs) { {status: {"uploadStatus"=>"failed", "failureReason"=>"invalidFile"}} }
225
+ it { expect(video).to be_invalid }
226
+ end
227
+
228
+ context 'given fetching a status does not return failureReason "invalidFile"' do
229
+ let(:attrs) { {status: {"uploadStatus"=>"failed"}} }
230
+ it { expect(video).not_to be_invalid }
231
+ end
232
+ end
233
+
234
+ describe '#too_small?' do
235
+ context 'given fetching a status returns failureReason "tooSmall"' do
236
+ let(:attrs) { {status: {"uploadStatus"=>"failed", "failureReason"=>"tooSmall"}} }
237
+ it { expect(video).to be_too_small }
238
+ end
239
+
240
+ context 'given fetching a status does not return failureReason "tooSmall"' do
241
+ let(:attrs) { {status: {"uploadStatus"=>"failed"}} }
242
+ it { expect(video).not_to be_too_small }
243
+ end
244
+ end
245
+
246
+ describe '#upload_aborted?' do
247
+ context 'given fetching a status returns failureReason "uploadAborted"' do
248
+ let(:attrs) { {status: {"uploadStatus"=>"failed", "failureReason"=>"uploadAborted"}} }
249
+ it { expect(video).to be_aborted }
250
+ end
251
+
252
+ context 'given fetching a status does not return failureReason "uploadAborted"' do
253
+ let(:attrs) { {status: {"uploadStatus"=>"failed"}} }
254
+ it { expect(video).not_to be_aborted }
255
+ end
256
+ end
257
+
258
+ describe '#claimed?' do
259
+ context 'given fetching a status returns rejectionReason "claim"' do
260
+ let(:attrs) { {status: {"uploadStatus"=>"rejected", "rejectionReason"=>"claim"}} }
261
+ it { expect(video).to be_claimed }
262
+ end
263
+
264
+ context 'given fetching a status does not return rejectionReason "claim"' do
265
+ let(:attrs) { {status: {"uploadStatus"=>"rejected"}} }
266
+ it { expect(video).not_to be_claimed }
267
+ end
268
+ end
269
+
270
+ describe '#infringes_copyright?' do
271
+ context 'given fetching a status returns rejectionReason "copyright"' do
272
+ let(:attrs) { {status: {"uploadStatus"=>"rejected", "rejectionReason"=>"copyright"}} }
273
+ it { expect(video.infringes_copyright?).to be true }
274
+ end
275
+
276
+ context 'given fetching a status does not return rejectionReason "copyright"' do
277
+ let(:attrs) { {status: {"uploadStatus"=>"rejected"}} }
278
+ it { expect(video.infringes_copyright?).to be false }
279
+ end
280
+ end
281
+
282
+ describe '#duplicate?' do
283
+ context 'given fetching a status returns rejectionReason "duplicate"' do
284
+ let(:attrs) { {status: {"uploadStatus"=>"rejected", "rejectionReason"=>"duplicate"}} }
285
+ it { expect(video).to be_duplicate }
286
+ end
287
+
288
+ context 'given fetching a status does not return rejectionReason "duplicate"' do
289
+ let(:attrs) { {status: {"uploadStatus"=>"rejected"}} }
290
+ it { expect(video).not_to be_duplicate }
291
+ end
292
+ end
293
+
294
+ describe '#inappropriate?' do
295
+ context 'given fetching a status returns rejectionReason "inappropriate"' do
296
+ let(:attrs) { {status: {"uploadStatus"=>"rejected", "rejectionReason"=>"inappropriate"}} }
297
+ it { expect(video).to be_inappropriate }
298
+ end
299
+
300
+ context 'given fetching a status does not return rejectionReason "inappropriate"' do
301
+ let(:attrs) { {status: {"uploadStatus"=>"rejected"}} }
302
+ it { expect(video).not_to be_inappropriate }
303
+ end
304
+ end
305
+
306
+ describe '#too_long?' do
307
+ context 'given fetching a status returns rejectionReason "length"' do
308
+ let(:attrs) { {status: {"uploadStatus"=>"rejected", "rejectionReason"=>"length"}} }
309
+ it { expect(video).to be_too_long }
310
+ end
311
+
312
+ context 'given fetching a status does not return rejectionReason "length"' do
313
+ let(:attrs) { {status: {"uploadStatus"=>"rejected"}} }
314
+ it { expect(video).not_to be_too_long }
315
+ end
316
+ end
317
+
318
+ describe '#violates_terms_of_use?' do
319
+ context 'given fetching a status returns rejectionReason "termsOfUse"' do
320
+ let(:attrs) { {status: {"uploadStatus"=>"rejected", "rejectionReason"=>"termsOfUse"}} }
321
+ it { expect(video.violates_terms_of_use?).to be true }
322
+ end
323
+
324
+ context 'given fetching a status does not return rejectionReason "termsOfUse"' do
325
+ let(:attrs) { {status: {"uploadStatus"=>"rejected"}} }
326
+ it { expect(video.violates_terms_of_use?).to be false }
327
+ end
328
+ end
329
+
330
+ describe '#infringes_trademark?' do
331
+ context 'given fetching a status returns rejectionReason "trademark"' do
332
+ let(:attrs) { {status: {"uploadStatus"=>"rejected", "rejectionReason"=>"trademark"}} }
333
+ it { expect(video.infringes_trademark?).to be true }
334
+ end
335
+
336
+ context 'given fetching a status does not return rejectionReason "trademark"' do
337
+ let(:attrs) { {status: {"uploadStatus"=>"rejected"}} }
338
+ it { expect(video.infringes_trademark?).to be false }
339
+ end
340
+ end
341
+
342
+ describe '#belongs_to_closed_account?' do
343
+ context 'given fetching a status returns rejectionReason "uploaderAccountClosed"' do
344
+ let(:attrs) { {status: {"uploadStatus"=>"rejected", "rejectionReason"=>"uploaderAccountClosed"}} }
345
+ it { expect(video.belongs_to_closed_account?).to be true }
346
+ end
347
+
348
+ context 'given fetching a status does not return rejectionReason "uploaderAccountClosed"' do
349
+ let(:attrs) { {status: {"uploadStatus"=>"rejected"}} }
350
+ it { expect(video.belongs_to_closed_account?).to be false }
351
+ end
352
+ end
353
+
354
+ describe '#belongs_to_suspended_account?' do
355
+ context 'given fetching a status returns rejectionReason "uploaderAccountSuspended"' do
356
+ let(:attrs) { {status: {"uploadStatus"=>"rejected", "rejectionReason"=>"uploaderAccountSuspended"}} }
357
+ it { expect(video.belongs_to_suspended_account?).to be true }
358
+ end
359
+
360
+ context 'given fetching a status does not return rejectionReason "uploaderAccountSuspended"' do
361
+ let(:attrs) { {status: {"uploadStatus"=>"rejected"}} }
362
+ it { expect(video.belongs_to_suspended_account?).to be false }
363
+ end
364
+ end
365
+
366
+ describe '#scheduled_at and #scheduled' do
367
+ context 'given fetching a status returns "publishAt"' do
368
+ let(:attrs) { {status: {"uploadStatus"=>"uploaded", "privacyStatus"=>"private", "publishAt"=>"2014-04-22T19:14:49.000Z"}} }
369
+ it { expect(video).to be_scheduled }
370
+ it { expect(video.scheduled_at.year).to be 2014 }
371
+ end
372
+
373
+ context 'given fetching a status does not returns "publishAt"' do
374
+ let(:attrs) { {status: {"uploadStatus"=>"uploaded", "privacyStatus"=>"private"}} }
375
+ it { expect(video).not_to be_scheduled }
376
+ it { expect(video.scheduled_at).not_to be }
377
+ end
378
+ end
379
+
380
+ describe '#licensed_as_creative_commons?' do
381
+ context 'given fetching a status returns license "creativeCommon"' do
382
+ let(:attrs) { {status: {"uploadStatus"=>"uploaded", "license"=>"creativeCommon"}} }
383
+ it { expect(video).to be_licensed_as_creative_commons }
384
+ end
385
+
386
+ context 'given fetching a status does not return license "creativeCommon"' do
387
+ let(:attrs) { {status: {"uploadStatus"=>"uploaded"}} }
388
+ it { expect(video).not_to be_licensed_as_creative_commons }
389
+ end
390
+ end
391
+
392
+ describe '#licensed_as_standard_youtube?' do
393
+ context 'given fetching a status returns license "youtube"' do
394
+ let(:attrs) { {status: {"uploadStatus"=>"uploaded", "license"=>"youtube"}} }
395
+ it { expect(video).to be_licensed_as_standard_youtube }
396
+ end
397
+
398
+ context 'given fetching a status does not return license "youtube"' do
399
+ let(:attrs) { {status: {"uploadStatus"=>"uploaded"}} }
400
+ it { expect(video).not_to be_licensed_as_standard_youtube }
401
+ end
402
+ end
403
+
404
+ describe '#embeddable?' do
405
+ context 'given fetching a status returns "embeddable" true' do
406
+ let(:attrs) { {status: {"uploadStatus"=>"uploaded", "embeddable"=>true}} }
407
+ it { expect(video).to be_embeddable }
408
+ end
409
+
410
+ context 'given fetching a status returns "embeddable" false' do
411
+ let(:attrs) { {status: {"uploadStatus"=>"uploaded", "embeddable"=>false}} }
412
+ it { expect(video).not_to be_embeddable }
413
+ end
414
+ end
415
+
416
+ describe '#has_public_stats_viewable?' do
417
+ context 'given fetching a status returns "publicStatsViewable" true' do
418
+ let(:attrs) { {status: {"publicStatsViewable"=>true}} }
419
+ it { expect(video).to have_public_stats_viewable }
420
+ end
421
+
422
+ context 'given fetching a status returns "publicStatsViewable" false' do
423
+ let(:attrs) { {status: {"publicStatsViewable"=>false}} }
424
+ it { expect(video).not_to have_public_stats_viewable }
425
+ end
426
+ end
427
+
428
+ describe '#stereoscopic?' do
429
+ context 'given a 3D video' do
430
+ let(:attrs) { {content_details: {"dimension"=>"3d"}} }
431
+ it { expect(video).to be_stereoscopic }
432
+ end
433
+
434
+ context 'given a 2D video' do
435
+ let(:attrs) { {content_details: {"dimension"=>"2d"}} }
436
+ it { expect(video).not_to be_stereoscopic }
437
+ end
438
+ end
439
+
440
+ describe '#hd?' do
441
+ context 'given a high-definition video' do
442
+ let(:attrs) { {content_details: {"definition"=>"hd"}} }
443
+ it { expect(video).to be_hd }
444
+ end
445
+
446
+ context 'given a standard-definition video' do
447
+ let(:attrs) { {content_details: {"definition"=>"sd"}} }
448
+ it { expect(video).not_to be_hd }
449
+ end
450
+ end
451
+
452
+ describe '#captioned?' do
453
+ context 'given a video with captions' do
454
+ let(:attrs) { {content_details: {"caption"=>"true"}} }
455
+ it { expect(video).to be_captioned }
456
+ end
457
+
458
+ context 'given a video without captions' do
459
+ let(:attrs) { {content_details: {"caption"=>"false"}} }
460
+ it { expect(video).not_to be_captioned }
461
+ end
462
+ end
463
+
464
+ describe '#licensed?' do
465
+ context 'given a video with licensed content' do
466
+ let(:attrs) { {content_details: {"licensedContent"=>true}} }
467
+ it { expect(video).to be_licensed }
468
+ end
469
+
470
+ context 'given a video without licensed content' do
471
+ let(:attrs) { {content_details: {"licensedContent"=>false}} }
472
+ it { expect(video).not_to be_licensed }
473
+ end
474
+ end
475
+
476
+ describe '#age_restricted?' do
477
+ context 'given a video with age restricted content' do
478
+ let(:attrs) { {content_details: {"contentRating"=>{"ytRating"=>"ytAgeRestricted"}}} }
479
+ it { expect(video).to be_age_restricted }
480
+ end
481
+
482
+ context 'given a video without age restricted content' do
483
+ let(:attrs) { {content_details: {}} }
484
+ it { expect(video).not_to be_age_restricted }
485
+ end
486
+
487
+ context 'given a video with a content rating but not ytRating' do
488
+ let(:attrs) { {content_details: {"contentRating"=>{"acbRating": "PG"}}} }
489
+ it { expect(video).not_to be_age_restricted }
490
+ end
491
+ end
492
+
493
+ describe '#file_size' do
494
+ context 'given a video with fileSize' do
495
+ let(:attrs) { {file_details: {"fileSize"=>"8000000"}} }
496
+ it { expect(video.file_size).to be 8_000_000 }
497
+ end
498
+ end
499
+
500
+ describe '#file_type' do
501
+ context 'given a video with fileType' do
502
+ let(:attrs) { {file_details: {"fileType"=>"video"}} }
503
+ it { expect(video.file_type).to eq 'video' }
504
+ end
505
+ end
506
+
507
+ describe '#container' do
508
+ context 'given a video with container' do
509
+ let(:attrs) { {file_details: {"container"=>"mov"}} }
510
+ it { expect(video.container).to eq 'mov' }
511
+ end
512
+ end
513
+
514
+ describe '#actual_start_time' do
515
+ context 'given a non-live streaming video' do
516
+ let(:attrs) { {live_streaming_details: {}} }
517
+ it { expect(video.actual_start_time).to be_nil }
518
+ end
519
+
520
+ context 'given a live streaming video that has not started yet' do
521
+ let(:attrs) { {live_streaming_details: {"scheduledStartTime"=>"2017-07-10T00:00:00.000Z"}} }
522
+ it { expect(video.actual_start_time).to be_nil }
523
+ end
524
+
525
+ context 'given a live streaming video that has started' do
526
+ let(:attrs) { {live_streaming_details: {"actualStartTime"=>"2014-08-01T17:48:40.678Z"}} }
527
+ it { expect(video.actual_start_time.year).to be 2014 }
528
+ end
529
+ end
530
+
531
+ describe '#actual_end_time' do
532
+ context 'given a non-live streaming video' do
533
+ let(:attrs) { {live_streaming_details: {}} }
534
+ it { expect(video.actual_end_time).to be_nil }
535
+ end
536
+
537
+ context 'given a live streaming video that has not ended yet' do
538
+ let(:attrs) { {live_streaming_details: {"scheduledStartTime"=>"2017-07-10T00:00:00.000Z"}} }
539
+ it { expect(video.actual_end_time).to be_nil }
540
+ end
541
+
542
+ context 'given a live streaming video that has ended' do
543
+ let(:attrs) { {live_streaming_details: {"actualEndTime"=>"2014-08-01T17:48:40.678Z"}} }
544
+ it { expect(video.actual_end_time.year).to be 2014 }
545
+ end
546
+ end
547
+
548
+ describe '#scheduled_start_time' do
549
+ context 'given a non-live streaming video' do
550
+ let(:attrs) { {live_streaming_details: {}} }
551
+ it { expect(video.scheduled_start_time).to be_nil }
552
+ end
553
+
554
+ context 'given a live streaming video' do
555
+ let(:attrs) { {live_streaming_details: {"scheduledStartTime"=>"2017-07-10T00:00:00.000Z"}} }
556
+ it { expect(video.scheduled_start_time.year).to be 2017 }
557
+ end
558
+ end
559
+
560
+ describe '#scheduled_end_time' do
561
+ context 'given a non-live streaming video' do
562
+ let(:attrs) { {live_streaming_details: {}} }
563
+ it { expect(video.scheduled_end_time).to be_nil }
564
+ end
565
+
566
+ context 'given a live streaming video that broadcasts indefinitely' do
567
+ let(:attrs) { {live_streaming_details: {"scheduledStartTime"=>"2017-07-10T00:00:00.000Z"}} }
568
+ it { expect(video.scheduled_end_time).to be_nil }
569
+ end
570
+
571
+ context 'given a live streaming video with a scheduled ednd' do
572
+ let(:attrs) { {live_streaming_details: {"scheduledEndTime"=>"2014-08-01T17:48:40.678Z"}} }
573
+ it { expect(video.scheduled_end_time.year).to be 2014 }
574
+ end
575
+ end
576
+
577
+ describe '#concurrent_viewers' do
578
+ context 'given a non-live streaming video' do
579
+ let(:attrs) { {live_streaming_details: {}} }
580
+ it { expect(video.concurrent_viewers).to be_nil }
581
+ end
582
+
583
+ context 'given a current live streaming video with viewers' do
584
+ let(:attrs) { {live_streaming_details: {"concurrentViewers"=>"1"}} }
585
+ it { expect(video.concurrent_viewers).to be 1 }
586
+ end
587
+
588
+ context 'given a past live streaming video' do
589
+ let(:attrs) { {live_streaming_details: {"actualEndTime"=>"2013-08-01T17:48:40.678Z"}} }
590
+ it { expect(video.concurrent_viewers).to be_nil }
591
+ end
592
+ end
593
+
594
+ describe '#view_count' do
595
+ context 'given a video with views' do
596
+ let(:attrs) { {statistics: { "viewCount"=>"123"}} }
597
+ it { expect(video.view_count).to be 123 }
598
+ end
599
+ end
600
+
601
+ describe '#comment_count' do
602
+ context 'given a video with comments' do
603
+ let(:attrs) { {statistics: { "commentCount"=>"45"}} }
604
+ it { expect(video.comment_count).to be 45 }
605
+ end
606
+ end
607
+
608
+ describe '#like_count' do
609
+ context 'given a video with likes' do
610
+ let(:attrs) { {statistics: { "likeCount"=>"6"}} }
611
+ it { expect(video.like_count).to be 6 }
612
+ end
613
+ end
614
+
615
+ describe '#dislike_count' do
616
+ context 'given a video with dislikes' do
617
+ let(:attrs) { {statistics: { "dislikeCount"=>"9"}} }
618
+ it { expect(video.dislike_count).to be 9 }
619
+ end
620
+ end
621
+
622
+ describe '#favorite_count' do
623
+ context 'given a video with favorites' do
624
+ let(:attrs) { {statistics: { "favoriteCount"=>"44"}} }
625
+ it { expect(video.favorite_count).to be 44 }
626
+ end
627
+ end
628
+
629
+ describe '#embed_html' do
630
+ context 'given a video with embedHtml' do
631
+ let(:html) { "<iframe type='text/html' src='http://www.youtube.com/embed/BPNYv0vd78A' width='640' height='360' frameborder='0' allowfullscreen='true'/>" }
632
+ let(:attrs) { {player: {"embedHtml"=>html}} }
633
+ it { expect(video.embed_html).to be html }
634
+ end
635
+ end
636
+
637
+ describe '#statistics_set' do
638
+ context 'given fetching a video returns statistics' do
639
+ let(:attrs) { {statistics: {"viewCount"=>"202"}} }
640
+ it { expect(video.statistics_set).to be_a Yt::StatisticsSet }
641
+ end
642
+ end
643
+
644
+ describe '#content_details' do
645
+ context 'given fetching a video returns content details' do
646
+ let(:attrs) { {content_details: {"definition"=>"hd"}} }
647
+ it { expect(video.content_detail).to be_a Yt::ContentDetail }
648
+ end
649
+ end
650
+
651
+ describe '#update' do
652
+ let(:attrs) { {id: 'MESycYJytkU', snippet: {'title'=>'old'}} }
653
+ before { expect(video).to receive(:do_update).and_yield 'snippet'=>{'title'=>'new'} }
654
+
655
+ it { expect(video.update title: 'new').to be true }
656
+ it { expect{video.update title: 'new'}.to change{video.title} }
657
+ end
658
+
659
+ describe '#delete' do
660
+ let(:attrs) { {id: 'video-id'} }
661
+
662
+ context 'given an existing video' do
663
+ before { expect(video).to receive(:do_delete).and_yield }
664
+
665
+ it { expect(video.delete).to be true }
666
+ it { expect{video.delete}.to change{video.exists?} }
667
+ end
668
+ end
669
+ end