yt 0.0.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +24 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +10 -0
  5. data/.yardopts +1 -0
  6. data/Gemfile +9 -0
  7. data/Gemfile.lock +78 -0
  8. data/HISTORY.md +37 -0
  9. data/MIT-LICENSE +20 -0
  10. data/README.md +325 -0
  11. data/Rakefile +1 -0
  12. data/TODO.md +11 -0
  13. data/bin/yt +31 -0
  14. data/lib/yt.rb +2 -0
  15. data/lib/yt/actions/delete.rb +27 -0
  16. data/lib/yt/actions/delete_all.rb +28 -0
  17. data/lib/yt/actions/insert.rb +29 -0
  18. data/lib/yt/actions/list.rb +65 -0
  19. data/lib/yt/actions/update.rb +25 -0
  20. data/lib/yt/associations.rb +33 -0
  21. data/lib/yt/associations/annotations.rb +15 -0
  22. data/lib/yt/associations/channels.rb +20 -0
  23. data/lib/yt/associations/details_sets.rb +20 -0
  24. data/lib/yt/associations/playlist_items.rb +26 -0
  25. data/lib/yt/associations/playlists.rb +22 -0
  26. data/lib/yt/associations/ratings.rb +39 -0
  27. data/lib/yt/associations/snippets.rb +20 -0
  28. data/lib/yt/associations/statuses.rb +14 -0
  29. data/lib/yt/associations/subscriptions.rb +38 -0
  30. data/lib/yt/associations/user_infos.rb +21 -0
  31. data/lib/yt/associations/videos.rb +14 -0
  32. data/lib/yt/collections/annotations.rb +43 -0
  33. data/lib/yt/collections/base.rb +13 -0
  34. data/lib/yt/collections/channels.rb +32 -0
  35. data/lib/yt/collections/details_sets.rb +32 -0
  36. data/lib/yt/collections/playlist_items.rb +50 -0
  37. data/lib/yt/collections/playlists.rb +56 -0
  38. data/lib/yt/collections/ratings.rb +32 -0
  39. data/lib/yt/collections/snippets.rb +38 -0
  40. data/lib/yt/collections/subscriptions.rb +67 -0
  41. data/lib/yt/collections/user_infos.rb +41 -0
  42. data/lib/yt/collections/videos.rb +32 -0
  43. data/lib/yt/config.rb +55 -0
  44. data/lib/yt/models/account.rb +68 -0
  45. data/lib/yt/models/annotation.rb +137 -0
  46. data/lib/yt/models/base.rb +11 -0
  47. data/lib/yt/models/channel.rb +17 -0
  48. data/lib/yt/models/configuration.rb +29 -0
  49. data/lib/yt/models/description.rb +98 -0
  50. data/lib/yt/models/details_set.rb +31 -0
  51. data/lib/yt/models/playlist.rb +65 -0
  52. data/lib/yt/models/playlist_item.rb +42 -0
  53. data/lib/yt/models/rating.rb +28 -0
  54. data/lib/yt/models/snippet.rb +48 -0
  55. data/lib/yt/models/status.rb +26 -0
  56. data/lib/yt/models/subscription.rb +35 -0
  57. data/lib/yt/models/user_info.rb +66 -0
  58. data/lib/yt/models/video.rb +16 -0
  59. data/lib/yt/utils/request.rb +85 -0
  60. data/lib/yt/version.rb +3 -0
  61. data/spec/associations/device_auth/channels_spec.rb +10 -0
  62. data/spec/associations/device_auth/details_sets_spec.rb +19 -0
  63. data/spec/associations/device_auth/playlist_items_spec.rb +42 -0
  64. data/spec/associations/device_auth/playlists_spec.rb +42 -0
  65. data/spec/associations/device_auth/ratings_spec.rb +30 -0
  66. data/spec/associations/device_auth/snippets_spec.rb +30 -0
  67. data/spec/associations/device_auth/subscriptions_spec.rb +27 -0
  68. data/spec/associations/device_auth/user_infos_spec.rb +10 -0
  69. data/spec/associations/device_auth/videos_spec.rb +22 -0
  70. data/spec/associations/no_auth/annotations_spec.rb +15 -0
  71. data/spec/associations/server_auth/channels_spec.rb +2 -0
  72. data/spec/associations/server_auth/details_sets_spec.rb +18 -0
  73. data/spec/associations/server_auth/playlist_items_spec.rb +17 -0
  74. data/spec/associations/server_auth/playlists_spec.rb +17 -0
  75. data/spec/associations/server_auth/ratings_spec.rb +2 -0
  76. data/spec/associations/server_auth/snippets_spec.rb +28 -0
  77. data/spec/associations/server_auth/subscriptions_spec.rb +2 -0
  78. data/spec/associations/server_auth/user_infos_spec.rb +2 -0
  79. data/spec/associations/server_auth/videos_spec.rb +20 -0
  80. data/spec/collections/annotations_spec.rb +6 -0
  81. data/spec/collections/channels_spec.rb +6 -0
  82. data/spec/collections/details_sets_spec.rb +6 -0
  83. data/spec/collections/playlist_items_spec.rb +23 -0
  84. data/spec/collections/playlists_spec.rb +26 -0
  85. data/spec/collections/ratings_spec.rb +6 -0
  86. data/spec/collections/snippets_spec.rb +6 -0
  87. data/spec/collections/subscriptions_spec.rb +30 -0
  88. data/spec/collections/user_infos_spec.rb +6 -0
  89. data/spec/collections/videos_spec.rb +6 -0
  90. data/spec/models/annotation_spec.rb +131 -0
  91. data/spec/models/channel_spec.rb +13 -0
  92. data/spec/models/description_spec.rb +94 -0
  93. data/spec/models/details_set_spec.rb +23 -0
  94. data/spec/models/playlist_item_spec.rb +32 -0
  95. data/spec/models/playlist_spec.rb +52 -0
  96. data/spec/models/rating_spec.rb +13 -0
  97. data/spec/models/snippet_spec.rb +66 -0
  98. data/spec/models/status_spec.rb +42 -0
  99. data/spec/models/subscription_spec.rb +37 -0
  100. data/spec/models/user_info_spec.rb +69 -0
  101. data/spec/models/video_spec.rb +13 -0
  102. data/spec/spec_helper.rb +15 -0
  103. data/spec/support/device_app.rb +16 -0
  104. data/spec/support/server_app.rb +10 -0
  105. data/yt.gemspec +30 -0
  106. metadata +209 -17
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+ require 'yt/collections/ratings'
3
+
4
+ describe Yt::Collections::Ratings do
5
+ # Nothing to test
6
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+ require 'yt/collections/snippets'
3
+
4
+ describe Yt::Collections::Snippets do
5
+ # Nothing to test
6
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+ require 'yt/collections/subscriptions'
3
+
4
+ describe Yt::Collections::Subscriptions do
5
+ subject(:collection) { Yt::Collections::Subscriptions.new }
6
+ before { collection.stub :throttle }
7
+
8
+ describe '#insert' do
9
+ context 'given a new subscription' do
10
+ let(:subscription) { Yt::Subscription.new }
11
+ before { collection.stub(:do_insert).and_return subscription }
12
+
13
+ it { expect(collection.insert).to eq subscription }
14
+ end
15
+
16
+ context 'given a duplicate subscription' do
17
+ let(:msg) { '{"error"=>{"errors"=>[{"reason"=>"subscriptionDuplicate"}]}}' }
18
+ before { collection.stub(:do_insert).and_raise Yt::RequestError, msg }
19
+
20
+ it { expect{collection.insert}.to raise_error Yt::RequestError, msg }
21
+ it { expect{collection.insert ignore_duplicates: true}.not_to raise_error }
22
+ end
23
+ end
24
+
25
+ describe '#delete_all' do
26
+ before { collection.stub(:do_delete_all).and_return [true] }
27
+
28
+ it { expect(collection.delete_all).to eq [true] }
29
+ end
30
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+ require 'yt/collections/user_infos'
3
+
4
+ describe Yt::Collections::UserInfos do
5
+ # Nothing to test
6
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+ require 'yt/collections/videos'
3
+
4
+ describe Yt::Collections::Videos do
5
+ # Nothing to test
6
+ end
@@ -0,0 +1,131 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/annotation'
3
+
4
+ describe Yt::Annotation do
5
+ subject(:annotation) { Yt::Annotation.new data: Hash.from_xml(xml) }
6
+
7
+ describe '#above? and #below?' do
8
+ let(:xml) { %Q{
9
+ <segment>
10
+ <movingRegion type="rect">
11
+ <rectRegion d="0" h="17.7779998779" t="0:04.000" w="25.0" x="7.117000103" y="5.07000017166"/>
12
+ <rectRegion d="0" h="17.7779998779" t="0:05.000" w="25.0" x="7.117000103" y="5.07000017166"/>
13
+ </movingRegion>
14
+ </segment>
15
+ } }
16
+
17
+ context 'given an annotation located above N% of the video height' do
18
+ it { expect(annotation.above? 50).to be_true }
19
+ it { expect(annotation.below? 50).to be_false }
20
+ end
21
+
22
+ context 'given an annotation located below N% of the video height' do
23
+ it { expect(annotation.above? 5).to be_false }
24
+ it { expect(annotation.below? 5).to be_true }
25
+ end
26
+
27
+ context 'given an annotation without explicit location' do
28
+ let(:xml) { '<segment></segment>' }
29
+ it { expect(annotation.above? 50).to be_false }
30
+ it { expect(annotation.below? 50).to be_false }
31
+ end
32
+ end
33
+
34
+ describe '#has_link_to_subscribe?' do
35
+ context 'given an annotation with a link of class 5' do
36
+ let(:xml) { '<action type="openUrl"><url link_class="5"/></action>' }
37
+ it { expect(annotation).to have_link_to_subscribe }
38
+ end
39
+
40
+ context 'given an annotation without a link of class 5' do
41
+ let(:xml) { '<action type="openUrl"><url link_class="3"/></action>' }
42
+ it { expect(annotation).not_to have_link_to_subscribe }
43
+ end
44
+ end
45
+
46
+ describe '#has_link_to_video?' do
47
+ context 'given an annotation with a link of class 1' do
48
+ let(:xml) { '<action type="openUrl"><url link_class="1"/></action>' }
49
+ it { expect(annotation).to have_link_to_video }
50
+ end
51
+
52
+ context 'given an annotation with a "featured video" invideo programming' do
53
+ let(:xml) { '<type>promotion</type>' }
54
+ it { expect(annotation).to have_link_to_video }
55
+ end
56
+
57
+ context 'given an annotation without a link of class 1' do
58
+ let(:xml) { '<action type="openUrl"><url link_class="3"/></action>' }
59
+ it { expect(annotation).not_to have_link_to_video }
60
+ end
61
+ end
62
+
63
+ describe '#has_link_to_playlist?' do
64
+ context 'given an annotation with a link of class 2' do
65
+ let(:xml) { '<action type="openUrl"><url link_class="2"/></action>' }
66
+ it { expect(annotation).to have_link_to_playlist }
67
+ end
68
+
69
+ context 'given an annotation with an embedded playlist link' do
70
+ let(:xml) { '<TEXT>https://www.youtube.com/watch?v=MESycYJytkU&amp;list=LLxO1tY8h1AhOz0T4ENwmpow"</TEXT>' }
71
+ it { expect(annotation).to have_link_to_playlist }
72
+ end
73
+
74
+ context 'given an annotation without a link of class 2' do
75
+ let(:xml) { '<action type="openUrl"><url link_class="3"/></action>' }
76
+ it { expect(annotation).not_to have_link_to_playlist }
77
+ end
78
+ end
79
+
80
+ describe '#has_link_to_same_window?' do
81
+ context 'given an annotation with a "current" target' do
82
+ let(:xml) { '<action type="openUrl"><url target="current"/></action>' }
83
+ it { expect(annotation).to have_link_to_same_window }
84
+ end
85
+
86
+ context 'given an annotation without a "current" target' do
87
+ let(:xml) { '<action type="openUrl"><url target="new"/></action>' }
88
+ it { expect(annotation).not_to have_link_to_same_window }
89
+ end
90
+ end
91
+
92
+ describe '#has_invideo_programming?' do
93
+ context 'given an annotation with a "featured video" invideo programming' do
94
+ let(:xml) { '<type>promotion</type>' }
95
+ it { expect(annotation).to have_invideo_programming }
96
+ end
97
+
98
+ context 'given an annotation with a "branding intro" invideo programming' do
99
+ let(:xml) { '<type>branding</type>' }
100
+ it { expect(annotation).to have_invideo_programming }
101
+ end
102
+
103
+ context 'given an annotation without an invideo programming' do
104
+ let(:xml) { '<segment></segment>' }
105
+ it { expect(annotation).not_to have_invideo_programming }
106
+ end
107
+ end
108
+
109
+ describe '#starts_after and #starts_before' do
110
+ context 'given an annotation with the first timestamp equal to 240 seconds' do
111
+ let(:xml) { %Q{
112
+ <segment>
113
+ <movingRegion type="rect">
114
+ <rectRegion d="0" h="17.7779998779" t="0:04.000" w="25.0" x="7.117000103" y="5.07000017166"/>
115
+ <rectRegion d="0" h="17.7779998779" t="0:05.000" w="25.0" x="7.117000103" y="5.07000017166"/>
116
+ </movingRegion>
117
+ </segment>
118
+ } }
119
+ it { expect(annotation.starts_after? 230).to be_true }
120
+ it { expect(annotation.starts_after? 250).to be_false }
121
+ it { expect(annotation.starts_before? 230).to be_false }
122
+ it { expect(annotation.starts_before? 250).to be_true }
123
+ end
124
+
125
+ context 'given an annotation without timestamps' do
126
+ let(:xml) { '<segment></segment>' }
127
+ it { expect(annotation.starts_after? 0).to be_nil }
128
+ it { expect(annotation.starts_before? 0).to be_nil }
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/channel'
3
+
4
+ describe Yt::Channel do
5
+ subject(:channel) { Yt::Channel.new attrs }
6
+
7
+ describe '#snippet' do
8
+ context 'given fetching a channel returns a snippet' do
9
+ let(:attrs) { {snippet: {"title"=>"Fullscreen"}} }
10
+ it { expect(channel.snippet).to be_a Yt::Snippet }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/description'
3
+
4
+ describe Yt::Description do
5
+ subject(:description) { Yt::Description.new text }
6
+
7
+ describe '#text' do
8
+ let(:text) { 'this is a description' }
9
+ it { expect(description).to eq 'this is a description' }
10
+ end
11
+
12
+ describe '#length' do
13
+ let(:text) { 'twenty one characters' }
14
+ it { expect(description.length).to eq 21 }
15
+ end
16
+
17
+ describe '#has_link_to_video?' do
18
+ context 'without a video URL' do
19
+ let(:text) { 'Link to channel: youtube.com/fullscreen' }
20
+ it { expect(description).not_to have_link_to_video }
21
+ end
22
+
23
+ context 'with a video long URL' do
24
+ let(:text) { 'Link to video: youtube.com/watch?v=MESycYJytkU' }
25
+ it { expect(description).to have_link_to_video }
26
+ end
27
+
28
+ context 'with a video short URL' do
29
+ let(:text) { 'Link to video: youtu.be/MESycYJytkU' }
30
+ it { expect(description).to have_link_to_video }
31
+ end
32
+ end
33
+
34
+ describe '#has_link_to_channel?' do
35
+ context 'without a channel URL' do
36
+ let(:text) { 'Link to video: youtu.be/MESycYJytkU' }
37
+ it { expect(description).not_to have_link_to_channel }
38
+ end
39
+
40
+ context 'with a channel long URL' do
41
+ let(:text) { 'Link to channel: youtube.com/channel/UCxO1tY8h1AhOz0T4ENwmpow' }
42
+ it { expect(description).to have_link_to_channel }
43
+ end
44
+
45
+ context 'with a channel short URL' do
46
+ let(:text) { 'Link to channel: youtube.com/fullscreen' }
47
+ it { expect(description).to have_link_to_channel }
48
+ end
49
+
50
+ context 'with a channel user URL' do
51
+ let(:text) { 'Link to channel: youtube.com/user/fullscreen' }
52
+ it { expect(description).to have_link_to_channel }
53
+ end
54
+ end
55
+
56
+ describe '#has_link_to_subscribe?' do
57
+ context 'without a subscribe URL' do
58
+ let(:text) { 'Link to video: youtu.be/MESycYJytkU' }
59
+ it { expect(description).not_to have_link_to_subscribe }
60
+ end
61
+
62
+ context 'with a subscribe center URL' do
63
+ let(:text) { 'Link to subscribe: youtube.com/subscription_center?add_user=fullscreen' }
64
+ it { expect(description).to have_link_to_subscribe }
65
+ end
66
+
67
+ context 'with a subscribe short URL' do
68
+ let(:text) { 'Link to subscribe: youtube.com/subscribe_widget?p=fullscreen' }
69
+ it { expect(description).to have_link_to_subscribe }
70
+ end
71
+
72
+ context 'with a subscribe confirm URL' do
73
+ let(:text) { 'Link to subscribe: youtube.com/channel/UCxO1tY8h1AhOz0T4ENwmpow?sub_confirmation=1' }
74
+ it { expect(description).to have_link_to_subscribe }
75
+ end
76
+ end
77
+
78
+ describe '#has_link_to_playlist?' do
79
+ context 'without a playlist URL' do
80
+ let(:text) { 'Link to video: youtu.be/MESycYJytkU' }
81
+ it { expect(description).not_to have_link_to_playlist }
82
+ end
83
+
84
+ context 'with a playlist long URL' do
85
+ let(:text) { 'Link to playlist: youtube.com/playlist?list=LLxO1tY8h1AhOz0T4ENwmpow' }
86
+ it { expect(description).to have_link_to_playlist }
87
+ end
88
+
89
+ context 'with a playlist embed URL' do
90
+ let(:text) { 'Link to video in playlist: youtube.com/watch?v=MESycYJytkU&index=619&list=LLxO1tY8h1AhOz0T4ENwmpow' }
91
+ it { expect(description).to have_link_to_playlist }
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/details_set'
3
+
4
+ describe Yt::DetailsSet do
5
+ subject(:details_set) { Yt::DetailsSet.new data: data }
6
+
7
+ describe '#duration' do
8
+ context 'given a details_set with duration in minutes and seconds' do
9
+ let(:data) { {"duration"=>"PT2M51S"} }
10
+ it { expect(details_set.duration).to eq 171 }
11
+ end
12
+
13
+ context 'given a details_set with duration in minutes' do
14
+ let(:data) { {"duration"=>"PT2M"} }
15
+ it { expect(details_set.duration).to eq 120 }
16
+ end
17
+
18
+ context 'given a details_set with duration in seconds' do
19
+ let(:data) { {"duration"=>"PT51S"} }
20
+ it { expect(details_set.duration).to eq 51 }
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/playlist_item'
3
+
4
+ describe Yt::PlaylistItem do
5
+ subject(:playlist_item) { Yt::PlaylistItem.new attrs }
6
+
7
+ describe '#position' do
8
+ context 'given fetching a playlist item returns a snippet' do
9
+ let(:attrs) { {snippet: {"position"=>0}} }
10
+ it { expect(playlist_item.position).to be 0 }
11
+ end
12
+ end
13
+
14
+ describe '#video' do
15
+ context 'given fetching a playlist item returns a snippet' do
16
+ let(:attrs) { {snippet: {"title"=>"Fullscreen"}} }
17
+ it { expect(playlist_item.video).to be_a Yt::Video }
18
+ it { expect(playlist_item.video.title).to eq "Fullscreen" }
19
+ end
20
+ end
21
+
22
+ describe '#delete' do
23
+ let(:attrs) { {id: 'playlist-item-id'} }
24
+
25
+ context 'given an existing playlist item' do
26
+ before { playlist_item.stub(:do_delete).and_yield }
27
+
28
+ it { expect(playlist_item.delete).to be_true }
29
+ it { expect{playlist_item.delete}.to change{playlist_item.exists?} }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/playlist'
3
+
4
+ describe Yt::Playlist do
5
+ subject(:playlist) { Yt::Playlist.new attrs }
6
+
7
+ describe '#exists?' do
8
+ context 'given a playlist with an id' do
9
+ let(:attrs) { {id: 'PLSWYkYzOr'} }
10
+ it { expect(playlist).to exist }
11
+ end
12
+
13
+ context 'given a playlist without an id' do
14
+ let(:attrs) { {} }
15
+ it { expect(playlist).not_to exist }
16
+ end
17
+ end
18
+
19
+ describe '#snippet' do
20
+ context 'given fetching a playlist returns a snippet' do
21
+ let(:attrs) { {snippet: {"title"=>"Fullscreen"}} }
22
+ it { expect(playlist.snippet).to be_a Yt::Snippet }
23
+ end
24
+ end
25
+
26
+ describe '#status' do
27
+ context 'given fetching a playlist returns a status' do
28
+ let(:attrs) { {status: {"privacyStatus"=>"public"}} }
29
+ it { expect(playlist.status).to be_a Yt::Status }
30
+ end
31
+ end
32
+
33
+ describe '#update' do
34
+ # TODO: separate stubs to show options translate into do_insert params
35
+ let(:attrs) { {id: 'PLSWYkYzOr', snippet: {'title'=>'old'}} }
36
+ before { playlist.stub(:do_update).and_yield 'snippet'=>{'title'=>'new'} }
37
+
38
+ it { expect(playlist.update title: 'new').to be_true }
39
+ it { expect{playlist.update title: 'new'}.to change{playlist.title} }
40
+ end
41
+
42
+ describe '#delete' do
43
+ let(:attrs) { {id: 'PLSWYkYzOr'} }
44
+
45
+ context 'given an existing playlist' do
46
+ before { playlist.stub(:do_delete).and_yield }
47
+
48
+ it { expect(playlist.delete).to be_true }
49
+ it { expect{playlist.delete}.to change{playlist.exists?} }
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/rating'
3
+
4
+ describe Yt::Rating do
5
+ subject(:rating) { Yt::Rating.new }
6
+
7
+ describe '#update' do
8
+ before { rating.stub(:do_update).and_yield }
9
+
10
+ it { expect(rating.update :like).to be_true }
11
+ it { expect{rating.update :like}.to change{rating.rating} }
12
+ end
13
+ end
@@ -0,0 +1,66 @@
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 '#title' do
8
+ context 'given fetching a snippet returns a title' do
9
+ let(:data) { {"title"=>"Fullscreen"} }
10
+ it { expect(snippet.title).to eq 'Fullscreen' }
11
+ end
12
+
13
+ context 'given fetching a snippet does not return a title' do
14
+ let(:data) { {"description"=>"The first media company for the connected generation."} }
15
+ it { expect(snippet.title).to eq '' }
16
+ end
17
+ end
18
+
19
+ describe '#published_at' do # publishedAt is always returned by YouTube
20
+ let(:data) { {"publishedAt"=>"2014-04-22T19:14:49.000Z"} }
21
+ it { expect(snippet.published_at.year).to be 2014 }
22
+ end
23
+
24
+ describe '#description' do
25
+ context 'given fetching a snippet returns a description' do
26
+ let(:data) { {"description"=>"The first media company for the connected generation."} }
27
+ it { expect(snippet.description).to eq 'The first media company for the connected generation.' }
28
+ end
29
+
30
+ context 'given fetching a snippet does not return a description' do
31
+ let(:data) { {"title"=>"Fullscreen"} }
32
+ it { expect(snippet.description).to eq '' }
33
+ end
34
+ end
35
+
36
+ describe '#tags' do
37
+ context 'given fetching a snippet returns some tags' do
38
+ let(:data) { {"tags"=>["promotion", "new media"]} }
39
+ it { expect(snippet.tags).to eq ["promotion", "new media"] }
40
+ end
41
+
42
+ context 'given fetching a snippet does not return any tag' do
43
+ let(:data) { {"title"=>"Fullscreen"} }
44
+ it { expect(snippet.tags).to eq [] }
45
+ end
46
+ end
47
+
48
+ describe '#thumbnail_url' do
49
+ context 'given fetching a snippet returns some thumbnails' do
50
+ let(:data) { {"thumbnails"=>{
51
+ "default"=>{"url"=> "http://example.com/88x88.jpg"},
52
+ "medium"=>{"url"=> "http://example.com/240x240.jpg"},
53
+ }} }
54
+ it { expect(snippet.thumbnail_url).to eq 'http://example.com/88x88.jpg' }
55
+ it { expect(snippet.thumbnail_url 'default').to eq 'http://example.com/88x88.jpg' }
56
+ it { expect(snippet.thumbnail_url :default).to eq 'http://example.com/88x88.jpg' }
57
+ it { expect(snippet.thumbnail_url :medium).to eq 'http://example.com/240x240.jpg' }
58
+ it { expect(snippet.thumbnail_url :large).to be_nil }
59
+ end
60
+
61
+ context 'given fetching a snippet returns any thumbnail' do
62
+ let(:data) { {"title"=>"Fullscreen"} }
63
+ it { expect(snippet.thumbnail_url).to be_nil }
64
+ end
65
+ end
66
+ end