yt 0.0.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +24 -0
- data/.rspec +2 -0
- data/.travis.yml +10 -0
- data/.yardopts +1 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +78 -0
- data/HISTORY.md +37 -0
- data/MIT-LICENSE +20 -0
- data/README.md +325 -0
- data/Rakefile +1 -0
- data/TODO.md +11 -0
- data/bin/yt +31 -0
- data/lib/yt.rb +2 -0
- data/lib/yt/actions/delete.rb +27 -0
- data/lib/yt/actions/delete_all.rb +28 -0
- data/lib/yt/actions/insert.rb +29 -0
- data/lib/yt/actions/list.rb +65 -0
- data/lib/yt/actions/update.rb +25 -0
- data/lib/yt/associations.rb +33 -0
- data/lib/yt/associations/annotations.rb +15 -0
- data/lib/yt/associations/channels.rb +20 -0
- data/lib/yt/associations/details_sets.rb +20 -0
- data/lib/yt/associations/playlist_items.rb +26 -0
- data/lib/yt/associations/playlists.rb +22 -0
- data/lib/yt/associations/ratings.rb +39 -0
- data/lib/yt/associations/snippets.rb +20 -0
- data/lib/yt/associations/statuses.rb +14 -0
- data/lib/yt/associations/subscriptions.rb +38 -0
- data/lib/yt/associations/user_infos.rb +21 -0
- data/lib/yt/associations/videos.rb +14 -0
- data/lib/yt/collections/annotations.rb +43 -0
- data/lib/yt/collections/base.rb +13 -0
- data/lib/yt/collections/channels.rb +32 -0
- data/lib/yt/collections/details_sets.rb +32 -0
- data/lib/yt/collections/playlist_items.rb +50 -0
- data/lib/yt/collections/playlists.rb +56 -0
- data/lib/yt/collections/ratings.rb +32 -0
- data/lib/yt/collections/snippets.rb +38 -0
- data/lib/yt/collections/subscriptions.rb +67 -0
- data/lib/yt/collections/user_infos.rb +41 -0
- data/lib/yt/collections/videos.rb +32 -0
- data/lib/yt/config.rb +55 -0
- data/lib/yt/models/account.rb +68 -0
- data/lib/yt/models/annotation.rb +137 -0
- data/lib/yt/models/base.rb +11 -0
- data/lib/yt/models/channel.rb +17 -0
- data/lib/yt/models/configuration.rb +29 -0
- data/lib/yt/models/description.rb +98 -0
- data/lib/yt/models/details_set.rb +31 -0
- data/lib/yt/models/playlist.rb +65 -0
- data/lib/yt/models/playlist_item.rb +42 -0
- data/lib/yt/models/rating.rb +28 -0
- data/lib/yt/models/snippet.rb +48 -0
- data/lib/yt/models/status.rb +26 -0
- data/lib/yt/models/subscription.rb +35 -0
- data/lib/yt/models/user_info.rb +66 -0
- data/lib/yt/models/video.rb +16 -0
- data/lib/yt/utils/request.rb +85 -0
- data/lib/yt/version.rb +3 -0
- data/spec/associations/device_auth/channels_spec.rb +10 -0
- data/spec/associations/device_auth/details_sets_spec.rb +19 -0
- data/spec/associations/device_auth/playlist_items_spec.rb +42 -0
- data/spec/associations/device_auth/playlists_spec.rb +42 -0
- data/spec/associations/device_auth/ratings_spec.rb +30 -0
- data/spec/associations/device_auth/snippets_spec.rb +30 -0
- data/spec/associations/device_auth/subscriptions_spec.rb +27 -0
- data/spec/associations/device_auth/user_infos_spec.rb +10 -0
- data/spec/associations/device_auth/videos_spec.rb +22 -0
- data/spec/associations/no_auth/annotations_spec.rb +15 -0
- data/spec/associations/server_auth/channels_spec.rb +2 -0
- data/spec/associations/server_auth/details_sets_spec.rb +18 -0
- data/spec/associations/server_auth/playlist_items_spec.rb +17 -0
- data/spec/associations/server_auth/playlists_spec.rb +17 -0
- data/spec/associations/server_auth/ratings_spec.rb +2 -0
- data/spec/associations/server_auth/snippets_spec.rb +28 -0
- data/spec/associations/server_auth/subscriptions_spec.rb +2 -0
- data/spec/associations/server_auth/user_infos_spec.rb +2 -0
- data/spec/associations/server_auth/videos_spec.rb +20 -0
- data/spec/collections/annotations_spec.rb +6 -0
- data/spec/collections/channels_spec.rb +6 -0
- data/spec/collections/details_sets_spec.rb +6 -0
- data/spec/collections/playlist_items_spec.rb +23 -0
- data/spec/collections/playlists_spec.rb +26 -0
- data/spec/collections/ratings_spec.rb +6 -0
- data/spec/collections/snippets_spec.rb +6 -0
- data/spec/collections/subscriptions_spec.rb +30 -0
- data/spec/collections/user_infos_spec.rb +6 -0
- data/spec/collections/videos_spec.rb +6 -0
- data/spec/models/annotation_spec.rb +131 -0
- data/spec/models/channel_spec.rb +13 -0
- data/spec/models/description_spec.rb +94 -0
- data/spec/models/details_set_spec.rb +23 -0
- data/spec/models/playlist_item_spec.rb +32 -0
- data/spec/models/playlist_spec.rb +52 -0
- data/spec/models/rating_spec.rb +13 -0
- data/spec/models/snippet_spec.rb +66 -0
- data/spec/models/status_spec.rb +42 -0
- data/spec/models/subscription_spec.rb +37 -0
- data/spec/models/user_info_spec.rb +69 -0
- data/spec/models/video_spec.rb +13 -0
- data/spec/spec_helper.rb +15 -0
- data/spec/support/device_app.rb +16 -0
- data/spec/support/server_app.rb +10 -0
- data/yt.gemspec +30 -0
- metadata +209 -17
@@ -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,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&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
|