hallon 0.15.0 → 0.16.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.
- data/CHANGELOG.md +40 -0
- data/README.markdown +1 -1
- data/dev/application_key_converter.rb +11 -0
- data/examples/example_support.rb +4 -0
- data/examples/playing_audio.rb +1 -1
- data/lib/hallon.rb +11 -0
- data/lib/hallon/album_browse.rb +3 -3
- data/lib/hallon/artist_browse.rb +20 -4
- data/lib/hallon/blob.rb +11 -0
- data/lib/hallon/enumerator.rb +5 -0
- data/lib/hallon/ext/spotify.rb +2 -0
- data/lib/hallon/image.rb +7 -1
- data/lib/hallon/link.rb +5 -2
- data/lib/hallon/observable.rb +48 -1
- data/lib/hallon/player.rb +15 -26
- data/lib/hallon/playlist.rb +16 -25
- data/lib/hallon/playlist_container.rb +38 -0
- data/lib/hallon/search.rb +75 -4
- data/lib/hallon/session.rb +31 -42
- data/lib/hallon/toplist.rb +3 -3
- data/lib/hallon/track.rb +28 -10
- data/lib/hallon/version.rb +1 -1
- data/spec/hallon/album_browse_spec.rb +68 -18
- data/spec/hallon/album_spec.rb +62 -27
- data/spec/hallon/artist_browse_spec.rb +106 -31
- data/spec/hallon/artist_spec.rb +32 -18
- data/spec/hallon/blob_spec.rb +6 -0
- data/spec/hallon/enumerator_spec.rb +10 -0
- data/spec/hallon/error_spec.rb +4 -4
- data/spec/hallon/hallon_spec.rb +1 -1
- data/spec/hallon/image_spec.rb +58 -47
- data/spec/hallon/link_spec.rb +51 -43
- data/spec/hallon/observable/album_browse_spec.rb +1 -1
- data/spec/hallon/observable/artist_browse_spec.rb +1 -1
- data/spec/hallon/observable/image_spec.rb +1 -1
- data/spec/hallon/observable/playlist_container_spec.rb +4 -4
- data/spec/hallon/observable/playlist_spec.rb +14 -14
- data/spec/hallon/observable/post_spec.rb +1 -1
- data/spec/hallon/observable/search_spec.rb +1 -1
- data/spec/hallon/observable/session_spec.rb +17 -17
- data/spec/hallon/observable/toplist_spec.rb +1 -1
- data/spec/hallon/observable_spec.rb +40 -6
- data/spec/hallon/player_spec.rb +1 -1
- data/spec/hallon/playlist_container_spec.rb +96 -13
- data/spec/hallon/playlist_spec.rb +180 -45
- data/spec/hallon/search_spec.rb +211 -28
- data/spec/hallon/session_spec.rb +44 -38
- data/spec/hallon/toplist_spec.rb +31 -14
- data/spec/hallon/track_spec.rb +159 -50
- data/spec/hallon/user_post_spec.rb +10 -5
- data/spec/hallon/user_spec.rb +60 -50
- data/spec/spec_helper.rb +40 -15
- data/spec/support/album_mocks.rb +30 -0
- data/spec/support/artist_mocks.rb +36 -0
- data/spec/support/common_objects.rb +0 -201
- data/spec/support/image_mocks.rb +34 -0
- data/spec/support/playlist_container_mocks.rb +36 -0
- data/spec/support/playlist_mocks.rb +70 -0
- data/spec/support/search_mocks.rb +23 -0
- data/spec/support/session_mocks.rb +33 -0
- data/spec/support/toplist_mocks.rb +19 -0
- data/spec/support/track_mocks.rb +28 -0
- data/spec/support/user_mocks.rb +20 -0
- metadata +40 -18
- data/spec/support/context_stub_session.rb +0 -5
data/spec/hallon/search_spec.rb
CHANGED
@@ -2,27 +2,31 @@
|
|
2
2
|
require 'cgi'
|
3
3
|
|
4
4
|
describe Hallon::Search do
|
5
|
-
|
6
|
-
|
7
|
-
let(:custom_object) { "http://open.spotify.com/search/my+%C3%A5+utf8+%EF%A3%BF+query" }
|
8
|
-
let(:described_class) { stub_session(Hallon::Search) }
|
5
|
+
let(:search) do
|
6
|
+
Hallon::Search.new("my å utf8 query")
|
9
7
|
end
|
10
8
|
|
11
|
-
|
9
|
+
let(:empty_search) do
|
10
|
+
Hallon::Search.new("")
|
11
|
+
end
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
specify { search.should be_a Hallon::Loadable }
|
14
|
+
specify { search.should be_a Hallon::Observable }
|
15
|
+
|
16
|
+
it_should_behave_like "a Linkable object" do
|
17
|
+
let(:spotify_uri) { "spotify:search:my+%C3%A5+utf8+%EF%A3%BF+query" }
|
18
|
+
let(:custom_object) { "http://open.spotify.com/search/my+%C3%A5+utf8+%EF%A3%BF+query" }
|
19
|
+
let(:described_class) { Hallon::Search }
|
16
20
|
end
|
17
21
|
|
18
22
|
describe ".new" do
|
19
23
|
it "should have some sane defaults" do
|
20
24
|
Spotify.should_receive(:search_create).with(session.pointer, "my å utf8 query", 0, 25, 0, 25, 0, 25, 0, 25, :standard, anything, anything).and_return(mock_search)
|
21
|
-
|
25
|
+
Hallon::Search.new("my å utf8 query")
|
22
26
|
end
|
23
27
|
|
24
28
|
it "should allow you to customize the defaults" do
|
25
|
-
Spotify.should_receive(:search_create).with(session.pointer, "my å utf8 query", 1, 2, 3, 4, 5, 6, 7, 8, :
|
29
|
+
Spotify.should_receive(:search_create).with(session.pointer, "my å utf8 query", 1, 2, 3, 4, 5, 6, 7, 8, :suggest, anything, anything).and_return(mock_search)
|
26
30
|
my_params = {
|
27
31
|
:tracks_offset => 1,
|
28
32
|
:tracks => 2,
|
@@ -31,35 +35,214 @@ describe Hallon::Search do
|
|
31
35
|
:artists_offset => 5,
|
32
36
|
:artists => 6,
|
33
37
|
:playlists_offset => 7,
|
34
|
-
:playlists => 8
|
38
|
+
:playlists => 8,
|
39
|
+
:type => :suggest
|
35
40
|
}
|
36
41
|
|
37
|
-
|
42
|
+
Hallon::Search.new("my å utf8 query", my_params)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should raise an error given an invalid search type" do
|
46
|
+
expect { Hallon::Search.new("my å utf8 query", type: :hulabandola) }.to raise_error(ArgumentError)
|
38
47
|
end
|
39
48
|
|
40
49
|
it "should raise an error if the search failed" do
|
41
50
|
Spotify.should_receive(:search_create).and_return(null_pointer)
|
42
|
-
expect {
|
51
|
+
expect { Hallon::Search.new("omgwtfbbq") }.to raise_error(/search (.*?) failed/)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#loaded?" do
|
56
|
+
it "returns true if the search is complete" do
|
57
|
+
search.should be_loaded
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#status" do
|
62
|
+
it "returns the status of the search" do
|
63
|
+
search.status.should eq :ok
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#query" do
|
68
|
+
it "returns the search query" do
|
69
|
+
search.query.should eq "my å utf8 query"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#did_you_mean" do
|
74
|
+
it "returns a suggestion for what the query might have intended to be" do
|
75
|
+
search.did_you_mean.should eq "another thing"
|
76
|
+
end
|
77
|
+
|
78
|
+
it "returns an empty string if there is no suggestion available" do
|
79
|
+
empty_search.did_you_mean.should be_empty
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "#tracks" do
|
84
|
+
it "returns an enumerator of the search’s track" do
|
85
|
+
search.tracks.to_a.should eq instantiate(Hallon::Track, mock_track, mock_track_two)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "returns an empty enumerator if there are no search results" do
|
89
|
+
empty_search.tracks.should be_empty
|
90
|
+
end
|
91
|
+
|
92
|
+
describe ".total" do
|
93
|
+
it "returns the total number of track search results" do
|
94
|
+
search.tracks.total.should eq 1337
|
95
|
+
end
|
96
|
+
|
97
|
+
it "returns zero if there are no search results whatsoever" do
|
98
|
+
empty_search.tracks.total.should eq 0
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe "#albums" do
|
104
|
+
it "returns an enumerator of the search’s albums" do
|
105
|
+
search.albums.to_a.should eq instantiate(Hallon::Album, mock_album)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "returns an empty enumerator if there are no search results" do
|
109
|
+
empty_search.albums.should be_empty
|
110
|
+
end
|
111
|
+
|
112
|
+
describe ".total" do
|
113
|
+
it "returns the total number of album search results" do
|
114
|
+
search.albums.total.should eq 42
|
115
|
+
end
|
116
|
+
|
117
|
+
it "returns zero if there are no search results whatsoever" do
|
118
|
+
empty_search.albums.total.should eq 0
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "#artists" do
|
124
|
+
it "returns an enumerator of the search’s artists" do
|
125
|
+
search.artists.to_a.should eq instantiate(Hallon::Artist, mock_artist, mock_artist_two)
|
126
|
+
end
|
127
|
+
|
128
|
+
it "returns an empty enumerator if there are no search results" do
|
129
|
+
empty_search.artists.should be_empty
|
130
|
+
end
|
131
|
+
|
132
|
+
describe ".total" do
|
133
|
+
it "returns the total number of artist search results" do
|
134
|
+
search.artists.total.should eq 81104
|
135
|
+
end
|
136
|
+
|
137
|
+
it "returns zero if there are no search results whatsoever" do
|
138
|
+
empty_search.artists.total.should eq 0
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe "#playlist_names" do
|
144
|
+
it "returns an enumerator of the search’s playlist names" do
|
145
|
+
search.playlist_names.to_a.should eq %w(Dunderlist)
|
146
|
+
end
|
147
|
+
|
148
|
+
it "returns an empty enumerator of there are no search results" do
|
149
|
+
empty_search.playlist_names.should be_empty
|
150
|
+
end
|
151
|
+
|
152
|
+
describe ".total" do
|
153
|
+
it "returns the total number of search results" do
|
154
|
+
search.playlist_names.total.should eq 462
|
155
|
+
end
|
156
|
+
|
157
|
+
it "returns zero if there are no search results whatsoever" do
|
158
|
+
empty_search.playlist_names.total.should eq 0
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe "#playlist_uris" do
|
164
|
+
it "returns an enumerator of the search’s playlist uris" do
|
165
|
+
search.playlist_uris.to_a.should eq %w(spotify:user:burgestrand:playlist:megaplaylist)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "returns an empty enumerator of there are no search results" do
|
169
|
+
empty_search.playlist_uris.should be_empty
|
170
|
+
end
|
171
|
+
|
172
|
+
describe ".total" do
|
173
|
+
it "returns the total number of search results" do
|
174
|
+
search.playlist_uris.total.should eq 462
|
175
|
+
end
|
176
|
+
|
177
|
+
it "returns zero if there are no search results whatsoever" do
|
178
|
+
empty_search.playlist_uris.total.should eq 0
|
179
|
+
end
|
43
180
|
end
|
44
181
|
end
|
45
182
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
183
|
+
describe "#playlist_image_uris" do
|
184
|
+
it "returns an enumerator of the search’s playlist image uris" do
|
185
|
+
search.playlist_image_uris.to_a.should eq %w(spotify:image:3ad93423add99766e02d563605c6e76ed2b0e400)
|
186
|
+
end
|
187
|
+
|
188
|
+
it "returns an empty enumerator of there are no search results" do
|
189
|
+
empty_search.playlist_image_uris.should be_empty
|
190
|
+
end
|
191
|
+
|
192
|
+
describe ".total" do
|
193
|
+
it "returns the total number of search results" do
|
194
|
+
search.playlist_image_uris.total.should eq 462
|
195
|
+
end
|
196
|
+
|
197
|
+
it "returns zero if there are no search results whatsoever" do
|
198
|
+
empty_search.playlist_image_uris.total.should eq 0
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "#playlists" do
|
204
|
+
it "returns an enumerator of the search’s playlists" do
|
205
|
+
search.playlists.to_a.should eq instantiate(Hallon::Playlist, mock_playlist_two)
|
206
|
+
end
|
207
|
+
|
208
|
+
it "returns an empty enumerator of there are no search results" do
|
209
|
+
empty_search.playlists.should be_empty
|
210
|
+
end
|
51
211
|
|
52
|
-
|
53
|
-
|
54
|
-
|
212
|
+
describe ".total" do
|
213
|
+
it "returns the total number of search results" do
|
214
|
+
search.playlists.total.should eq 462
|
215
|
+
end
|
55
216
|
|
56
|
-
|
57
|
-
|
58
|
-
|
217
|
+
it "returns zero if there are no search results whatsoever" do
|
218
|
+
empty_search.playlists.total.should eq 0
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
describe "#playlist_images" do
|
224
|
+
it "returns an enumerator of the search’s playlist images" do
|
225
|
+
search.playlist_images.to_a.should eq instantiate(Hallon::Image, mock_image)
|
226
|
+
end
|
227
|
+
|
228
|
+
it "returns an empty enumerator of there are no search results" do
|
229
|
+
empty_search.playlist_images.should be_empty
|
230
|
+
end
|
59
231
|
|
60
|
-
|
61
|
-
|
62
|
-
|
232
|
+
describe ".total" do
|
233
|
+
it "returns the total number of search results" do
|
234
|
+
search.playlist_images.total.should eq 462
|
235
|
+
end
|
63
236
|
|
64
|
-
|
237
|
+
it "returns zero if there are no search results whatsoever" do
|
238
|
+
empty_search.playlist_images.total.should eq 0
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
describe "#to_link" do
|
244
|
+
it "contains the search query" do
|
245
|
+
search.to_link.should eq Hallon::Link.new("spotify:search:#{CGI.escape(search.query)}")
|
246
|
+
end
|
247
|
+
end
|
65
248
|
end
|
data/spec/hallon/session_spec.rb
CHANGED
@@ -68,35 +68,6 @@ describe Hallon::Session do
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
-
describe "#process_events_on" do
|
72
|
-
it "should not call given block on :notify_main_thread implicitly" do
|
73
|
-
notified = false
|
74
|
-
|
75
|
-
session.should_receive(:process_events).twice.and_return do
|
76
|
-
unless notified
|
77
|
-
session.class.send(:notify_main_thread_callback, session.pointer)
|
78
|
-
notified = true
|
79
|
-
else
|
80
|
-
session.class.send(:logged_in_callback, session.pointer, :ok)
|
81
|
-
end
|
82
|
-
|
83
|
-
0
|
84
|
-
end
|
85
|
-
|
86
|
-
session.process_events_on(:logged_in) { |e| e == :ok }.should be_true
|
87
|
-
end
|
88
|
-
|
89
|
-
it "should time out if waiting for events too long" do
|
90
|
-
session.should_receive(:process_events).once.and_return(1) # and do nothing
|
91
|
-
session.wait_for(:logged_in) { |x| x }.should eq :timeout
|
92
|
-
end
|
93
|
-
|
94
|
-
it "should call the given block once before waiting" do
|
95
|
-
session.should_not_receive(:process_events)
|
96
|
-
session.process_events_on { true }
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
71
|
describe "#relogin" do
|
101
72
|
it "should raise if no credentials have been saved" do
|
102
73
|
expect { session.relogin }.to raise_error(Hallon::Error)
|
@@ -135,6 +106,16 @@ describe Hallon::Session do
|
|
135
106
|
expect { session.login '', 'pass' }.to raise_error(ArgumentError)
|
136
107
|
expect { session.login 'Kim', '' }.to raise_error(ArgumentError)
|
137
108
|
end
|
109
|
+
|
110
|
+
it "should login with a blob when given a blob" do
|
111
|
+
Spotify.should_receive(:session_login).with(anything, 'Kim', nil, false, 'blob')
|
112
|
+
session.login 'Kim', Hallon::Blob('blob')
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should not login with a blob when not given a blob" do
|
116
|
+
Spotify.should_receive(:session_login).with(anything, 'Kim', 'pass', false, nil)
|
117
|
+
session.login 'Kim', 'pass'
|
118
|
+
end
|
138
119
|
end
|
139
120
|
|
140
121
|
describe "#logout" do
|
@@ -232,16 +213,35 @@ describe Hallon::Session do
|
|
232
213
|
end
|
233
214
|
|
234
215
|
describe "offline settings readers" do
|
235
|
-
|
216
|
+
let(:session) { mock_session_object }
|
236
217
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
218
|
+
describe "#offline_time_left" do
|
219
|
+
it "returns the time left until libspotify must go online" do
|
220
|
+
session.offline_time_left.should eq 60 * 60 * 24 * 30
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe "#offline_sync_status" do
|
225
|
+
it "returns a hash of offline sync status details" do
|
226
|
+
session.offline_sync_status.should eq mock_offline_sync_status_hash
|
227
|
+
end
|
228
|
+
|
229
|
+
it "returns an empty hash when offline sync status details are unavailable" do
|
230
|
+
Spotify.should_receive(:offline_sync_get_status).and_return(false)
|
231
|
+
session.offline_sync_status.should eq Hash.new
|
232
|
+
end
|
233
|
+
end
|
241
234
|
|
242
|
-
|
243
|
-
|
244
|
-
|
235
|
+
describe "#offline_playlists_count" do
|
236
|
+
it "returns the number of playlists marked for offline synchronization" do
|
237
|
+
session.offline_playlists_count.should eq 7
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
describe "#offline_tracks_to_sync" do
|
242
|
+
it "returns the number of tracks that still need to be synchronized" do
|
243
|
+
session.offline_tracks_to_sync.should eq 3
|
244
|
+
end
|
245
245
|
end
|
246
246
|
end
|
247
247
|
|
@@ -267,7 +267,7 @@ describe Hallon::Session do
|
|
267
267
|
|
268
268
|
|
269
269
|
describe "#starred" do
|
270
|
-
let(:starred) {
|
270
|
+
let(:starred) { Hallon::Playlist.new("spotify:user:burgestrand:starred") }
|
271
271
|
|
272
272
|
it "should return the sessions (current users) starred playlist" do
|
273
273
|
session.login 'burgestrand', 'pass'
|
@@ -298,4 +298,10 @@ describe Hallon::Session do
|
|
298
298
|
session.inbox.should be_nil
|
299
299
|
end
|
300
300
|
end
|
301
|
+
|
302
|
+
describe "#flush_caches" do
|
303
|
+
it "flushes the session cache to disk" do
|
304
|
+
session.flush_caches # or, actually, it does not crash
|
305
|
+
end
|
306
|
+
end
|
301
307
|
end
|
data/spec/hallon/toplist_spec.rb
CHANGED
@@ -1,36 +1,50 @@
|
|
1
|
-
|
2
|
-
it { should be_a Hallon::Loadable }
|
1
|
+
# coding: utf-8
|
3
2
|
|
3
|
+
describe Hallon::Toplist do
|
4
4
|
let(:toplist) do
|
5
5
|
Spotify.registry_add 'spotify:toplist:artists:everywhere', mock_toplistbrowse
|
6
|
-
|
6
|
+
Hallon::Toplist.new(:artists)
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:empty_toplist) do
|
10
|
+
Spotify.registry_add 'spotify:toplist:tracks:everywhere', mock_empty_toplistbrowse
|
11
|
+
Hallon::Toplist.new(:tracks)
|
7
12
|
end
|
8
|
-
subject { toplist }
|
9
13
|
|
10
|
-
|
14
|
+
specify { toplist.should be_a Hallon::Loadable }
|
15
|
+
specify { toplist.should be_a Hallon::Observable }
|
11
16
|
|
12
17
|
describe ".new" do
|
13
18
|
it "should fail given an invalid type" do
|
14
|
-
expect {
|
19
|
+
expect { Hallon::Toplist.new(:invalid_type) }.to raise_error(ArgumentError, /invalid enum value/)
|
15
20
|
end
|
16
21
|
|
17
22
|
it "should pass the username given a string to libspotify" do
|
18
23
|
Spotify.registry_add 'spotify:toplist:user:Kim:tracks', mock_toplistbrowse
|
19
|
-
|
24
|
+
Hallon::Toplist.new(:tracks, "Kim").should be_loaded
|
20
25
|
end
|
21
26
|
|
22
27
|
it "should pass the correct region to libspotify" do
|
23
28
|
Spotify.registry_add 'spotify:toplist:tracks:SE', mock_toplistbrowse
|
24
|
-
|
29
|
+
Hallon::Toplist.new(:tracks, :se).should be_loaded
|
25
30
|
end
|
26
31
|
end
|
27
32
|
|
28
|
-
|
29
|
-
|
33
|
+
describe "#loaded?" do
|
34
|
+
it "returns true if the toplist is loaded" do
|
35
|
+
toplist.should be_loaded
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#status" do
|
40
|
+
it "returns the toplist’s status" do
|
41
|
+
toplist.status.should eq :ok
|
42
|
+
end
|
43
|
+
end
|
30
44
|
|
31
45
|
describe "#type" do
|
32
46
|
it "should be the same as the type given to .new" do
|
33
|
-
toplist =
|
47
|
+
toplist = Hallon::Toplist.new(:tracks, :se)
|
34
48
|
toplist.type.should eq :tracks
|
35
49
|
end
|
36
50
|
end
|
@@ -50,6 +64,10 @@ describe Hallon::Toplist do
|
|
50
64
|
toplist.should_receive(:type).and_return(:tracks)
|
51
65
|
toplist.results.to_a.should eq instantiate(Hallon::Track, mock_track, mock_track_two)
|
52
66
|
end
|
67
|
+
|
68
|
+
it "returns an empty enumerator when there are no results" do
|
69
|
+
empty_toplist.results.should be_empty
|
70
|
+
end
|
53
71
|
end
|
54
72
|
|
55
73
|
describe "#request_duration" do
|
@@ -57,9 +75,8 @@ describe Hallon::Toplist do
|
|
57
75
|
toplist.request_duration.should eq 2.751
|
58
76
|
end
|
59
77
|
|
60
|
-
it "should be
|
61
|
-
|
62
|
-
toplist.request_duration.should be_nil
|
78
|
+
it "should be zero if the request was fetched from local cache" do
|
79
|
+
empty_toplist.request_duration.should eq 0
|
63
80
|
end
|
64
81
|
end
|
65
82
|
end
|