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.
- 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
|