yt 0.0.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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