From a10db92c8ae1236edc4110754a06e41464195a95 Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Sun, 18 Nov 2018 12:33:22 -0300 Subject: [PATCH 01/27] Update page model to support search requests and to keep query params --- src/lib/models/paging/page.ts | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/lib/models/paging/page.ts b/src/lib/models/paging/page.ts index 4ce5709c..9c048899 100644 --- a/src/lib/models/paging/page.ts +++ b/src/lib/models/paging/page.ts @@ -17,9 +17,13 @@ class Page<T> { total: number; - constructor(json: any, t: new (json: any) => T) { + wrapper?: string; + + constructor(json: any, t: new (json: any) => T, wrapper?: string) { + this.wrapper = wrapper; + if (wrapper) json = json[wrapper]; this.t = t; - this.href = json.href.split('?')[0]; + this.href = json.href; this.items = json.items.map((json: any) => new t(json)); this.limit = json.limit; this.next = json.next ? json.next.split('?')[1] : null; @@ -28,9 +32,22 @@ class Page<T> { this.total = json.total; } + get queryParams(): any { + const queryString = this.href.split('?')[1]; + const paramsString = queryString.split('&'); + let queryParams: any = {}; + + for (const param of paramsString) { + const [name, value] = param.split('='); + queryParams[name] = value; + } + + return queryParams; + } + private getAxiosPageInstance() { const instance = getAxiosSpotifyInstance(); - instance.defaults.baseURL = this.href; + instance.defaults.baseURL = this.href.split('?')[0]; return instance; } @@ -44,9 +61,13 @@ class Page<T> { async getNextPage() { if (!this.hasNext()) throw new Error('There are no more pages'); - const params = { limit: this.limit, offset: this.offset + this.limit }; + const params = { + ...this.queryParams, + limit: this.limit, + offset: this.offset + this.limit, + }; const response = await this.getAxiosPageInstance().get('/', { params }); - return new Page<T>(response.data, this.t); + return new Page<T>(response.data, this.t, this.wrapper); } async getPreviousPage(forceLimit = false) { @@ -55,11 +76,11 @@ class Page<T> { if (this.offset < this.limit && !forceLimit) { limit = this.offset; } - const params = { limit, offset: 0 }; + const params = { ...this.queryParams, limit, offset: 0 }; const response = await this.getAxiosPageInstance().get('/', { params, }); - return new Page<T>(response.data, this.t); + return new Page<T>(response.data, this.t, this.wrapper); } } From fbcd91bf3425eef04b65514e03c89cfacffe301c Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Sun, 18 Nov 2018 12:33:53 -0300 Subject: [PATCH 02/27] Correct failing test --- test/common/matching-attributes.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/common/matching-attributes.test.ts b/test/common/matching-attributes.test.ts index 3242f2fe..ea85d125 100644 --- a/test/common/matching-attributes.test.ts +++ b/test/common/matching-attributes.test.ts @@ -97,7 +97,7 @@ export const checkMatchingPagingObjectAttributes = ( response: any, mock: any ) => { - expect(response.href).to.be.equal(mock.href.split('?')[0]); + expect(response.href).to.be.equal(mock.href); expect(response.items).to.have.lengthOf(mock.items.length); expect(response.limit).to.be.equal(mock.limit); expect(response.offset).to.be.equal(mock.offset); From a321d81ab0aaeb08707f9096b05ae4a058a0283e Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Sun, 18 Nov 2018 14:00:58 -0300 Subject: [PATCH 03/27] Fix bug in getPreviousPage and rename parameter --- src/lib/models/paging/page.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lib/models/paging/page.ts b/src/lib/models/paging/page.ts index 9c048899..01c9d049 100644 --- a/src/lib/models/paging/page.ts +++ b/src/lib/models/paging/page.ts @@ -70,13 +70,12 @@ class Page<T> { return new Page<T>(response.data, this.t, this.wrapper); } - async getPreviousPage(forceLimit = false) { + async getPreviousPage(includeRepeated = false) { if (!this.hasPrevious()) throw new Error('There are no more pages'); let limit = this.limit; - if (this.offset < this.limit && !forceLimit) { - limit = this.offset; - } - const params = { ...this.queryParams, limit, offset: 0 }; + if (this.offset < this.limit && !includeRepeated) limit = this.offset; + let offset = Math.max(this.offset - this.limit, 0); + const params = { ...this.queryParams, limit, offset }; const response = await this.getAxiosPageInstance().get('/', { params, }); From cd8b00e92968f4fe2b97dd1e64662b687774f298 Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Sun, 18 Nov 2018 14:02:54 -0300 Subject: [PATCH 04/27] Implement generic search and search for artists --- src/lib/search.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/lib/search.ts diff --git a/src/lib/search.ts b/src/lib/search.ts new file mode 100644 index 00000000..61d19aab --- /dev/null +++ b/src/lib/search.ts @@ -0,0 +1,23 @@ +import { getAxiosSpotifyInstance } from './driver'; + +import Artist from './models/artist/artist'; +import Page from './models/paging/page'; + +const genericSearch = async (params: { + q: string; + type: string; + market?: string; + limit?: number; + offset?: number; +}) => { + return getAxiosSpotifyInstance().get(`/search`, { params }); +}; + +export const searchArtists = async ( + query: string, + options?: { market?: string; limit?: number; offset?: number } +) => { + const params = { q: query, type: 'artist', ...options }; + const searchResults = await genericSearch(params); + return new Page<Artist>(searchResults.data, Artist, 'artists'); +}; From ce94aff486be726a1ba4c2f43038ea1892311662 Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Sun, 18 Nov 2018 14:26:03 -0300 Subject: [PATCH 05/27] Add missing implementation of search requests --- src/lib/search.ts | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/lib/search.ts b/src/lib/search.ts index 61d19aab..fbf1d92e 100644 --- a/src/lib/search.ts +++ b/src/lib/search.ts @@ -1,7 +1,10 @@ import { getAxiosSpotifyInstance } from './driver'; +import AlbumSimplified from './models/album/album-simplified'; import Artist from './models/artist/artist'; import Page from './models/paging/page'; +import PlaylistSimplified from './models/playlist/playlist-simplified'; +import Track from './models/track/track'; const genericSearch = async (params: { q: string; @@ -10,9 +13,23 @@ const genericSearch = async (params: { limit?: number; offset?: number; }) => { + console.log(params.type); return getAxiosSpotifyInstance().get(`/search`, { params }); }; +export const searchAlbums = async ( + query: string, + options?: { market?: string; limit?: number; offset?: number } +) => { + const params = { q: query, type: 'album', ...options }; + const searchResults = await genericSearch(params); + return new Page<AlbumSimplified>( + searchResults.data, + AlbumSimplified, + 'albums' + ); +}; + export const searchArtists = async ( query: string, options?: { market?: string; limit?: number; offset?: number } @@ -21,3 +38,25 @@ export const searchArtists = async ( const searchResults = await genericSearch(params); return new Page<Artist>(searchResults.data, Artist, 'artists'); }; + +export const searchPlaylists = async ( + query: string, + options?: { market?: string; limit?: number; offset?: number } +) => { + const params = { q: query, type: 'playlist', ...options }; + const searchResults = await genericSearch(params); + return new Page<PlaylistSimplified>( + searchResults.data, + PlaylistSimplified, + 'playlists' + ); +}; + +export const searchTracks = async ( + query: string, + options?: { market?: string; limit?: number; offset?: number } +) => { + const params = { q: query, type: 'track', ...options }; + const searchResults = await genericSearch(params); + return new Page<Track>(searchResults.data, Track, 'tracks'); +}; From 701c4188ca46b039d846ff54c5180409a51da14c Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Sun, 18 Nov 2018 14:27:00 -0300 Subject: [PATCH 06/27] Make some of user's attributes optional due to possibly missing data --- src/lib/models/user/user-public.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lib/models/user/user-public.ts b/src/lib/models/user/user-public.ts index a740da34..07418fbc 100644 --- a/src/lib/models/user/user-public.ts +++ b/src/lib/models/user/user-public.ts @@ -6,13 +6,13 @@ class PublicUser { externalUrls: any; - followers: Followers; + followers?: Followers; href: string; id: string; - images: Image[]; + images?: Image[]; type: 'user'; @@ -21,10 +21,13 @@ class PublicUser { constructor(json: any) { this.displayName = json.display_name; this.externalUrls = json.external_urls; - this.followers = new Followers(json.followers); + if (json.followers) this.followers = new Followers(json.followers); this.href = json.href; this.id = json.id; - this.images = json.images.map((imageJson: any) => new Image(imageJson)); + if (json.images) + this.images = json.images.map( + (imageJson: any) => new Image(imageJson) + ); this.type = json.type; this.uri = json.uri; } From 917aa4fe29bdc9fea03c06cea8136fb34da2a60d Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Sun, 18 Nov 2018 14:27:44 -0300 Subject: [PATCH 07/27] Export search requests through index.ts --- src/lib/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/index.ts b/src/lib/index.ts index 32a86a9c..ad012a64 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,6 +1,7 @@ +export * from './albums'; +export * from './artists'; export * from './driver'; -export * from './tracks'; export * from './follow'; -export * from './artists'; -export * from './albums'; export * from './playlists'; +export * from './search'; +export * from './tracks'; From bf1203ab7b35f44e9915b1d2ef9e22f9c4af696a Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Sun, 18 Nov 2018 15:01:19 -0300 Subject: [PATCH 08/27] Implement tests for search requests --- src/lib/search.ts | 1 - test/mocks/search-albums.mock.ts | 138 ++++++++++++++++++ test/mocks/search-artists.mock.ts | 82 +++++++++++ test/mocks/search-playlists.mock.ts | 94 ++++++++++++ test/mocks/search-tracks.mock.ts | 214 ++++++++++++++++++++++++++++ test/search.test.ts | 101 +++++++++++++ 6 files changed, 629 insertions(+), 1 deletion(-) create mode 100644 test/mocks/search-albums.mock.ts create mode 100644 test/mocks/search-artists.mock.ts create mode 100644 test/mocks/search-playlists.mock.ts create mode 100644 test/mocks/search-tracks.mock.ts create mode 100644 test/search.test.ts diff --git a/src/lib/search.ts b/src/lib/search.ts index fbf1d92e..15479353 100644 --- a/src/lib/search.ts +++ b/src/lib/search.ts @@ -13,7 +13,6 @@ const genericSearch = async (params: { limit?: number; offset?: number; }) => { - console.log(params.type); return getAxiosSpotifyInstance().get(`/search`, { params }); }; diff --git a/test/mocks/search-albums.mock.ts b/test/mocks/search-albums.mock.ts new file mode 100644 index 00000000..ad13841c --- /dev/null +++ b/test/mocks/search-albums.mock.ts @@ -0,0 +1,138 @@ +export const searchAlbumsMock = { + albums: { + href: + 'https://api.spotify.com/v1/search?query=sia&type=album&market=BR&offset=0&limit=2', + items: [ + { + album_type: 'album', + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/5WUlDfRSoLAfcVSX1WnrxN', + }, + href: + 'https://api.spotify.com/v1/artists/5WUlDfRSoLAfcVSX1WnrxN', + id: '5WUlDfRSoLAfcVSX1WnrxN', + name: 'Sia', + type: 'artist', + uri: 'spotify:artist:5WUlDfRSoLAfcVSX1WnrxN', + }, + ], + external_urls: { + spotify: + 'https://open.spotify.com/album/3xFSl9lIRaYXIYkIn3OIl9', + }, + href: + 'https://api.spotify.com/v1/albums/3xFSl9lIRaYXIYkIn3OIl9', + id: '3xFSl9lIRaYXIYkIn3OIl9', + images: [ + { + height: 640, + url: + 'https://i.scdn.co/image/03d97de27e99fd8099506ea172531cea0da59654', + width: 640, + }, + { + height: 300, + url: + 'https://i.scdn.co/image/58595889bbd362182cd3ef5c3633e1c56823d859', + width: 300, + }, + { + height: 64, + url: + 'https://i.scdn.co/image/b8b3036b13b0db9108aeca2b8b779da317973c16', + width: 64, + }, + ], + name: '1000 Forms Of Fear', + release_date: '2014-07-04', + release_date_precision: 'day', + total_tracks: 12, + type: 'album', + uri: 'spotify:album:3xFSl9lIRaYXIYkIn3OIl9', + }, + { + album_type: 'single', + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/5WUlDfRSoLAfcVSX1WnrxN', + }, + href: + 'https://api.spotify.com/v1/artists/5WUlDfRSoLAfcVSX1WnrxN', + id: '5WUlDfRSoLAfcVSX1WnrxN', + name: 'Sia', + type: 'artist', + uri: 'spotify:artist:5WUlDfRSoLAfcVSX1WnrxN', + }, + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/5fMUXHkw8R8eOP2RNVYEZX', + }, + href: + 'https://api.spotify.com/v1/artists/5fMUXHkw8R8eOP2RNVYEZX', + id: '5fMUXHkw8R8eOP2RNVYEZX', + name: 'Diplo', + type: 'artist', + uri: 'spotify:artist:5fMUXHkw8R8eOP2RNVYEZX', + }, + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/2feDdbD5araYcm6JhFHHw7', + }, + href: + 'https://api.spotify.com/v1/artists/2feDdbD5araYcm6JhFHHw7', + id: '2feDdbD5araYcm6JhFHHw7', + name: 'Labrinth', + type: 'artist', + uri: 'spotify:artist:2feDdbD5araYcm6JhFHHw7', + }, + ], + external_urls: { + spotify: + 'https://open.spotify.com/album/3dB0bCgmpEgCSr3aU1bOtv', + }, + href: + 'https://api.spotify.com/v1/albums/3dB0bCgmpEgCSr3aU1bOtv', + id: '3dB0bCgmpEgCSr3aU1bOtv', + images: [ + { + height: 640, + url: + 'https://i.scdn.co/image/43224d4f43e14d3a97e20e79e9c662accc80019f', + width: 640, + }, + { + height: 300, + url: + 'https://i.scdn.co/image/e6e6d19f5def281ea2b1be6f8199774c3660ed84', + width: 300, + }, + { + height: 64, + url: + 'https://i.scdn.co/image/8b6a5449cba2593b5fd2a7dd6e434366f8b0c100', + width: 64, + }, + ], + name: 'Mountains', + release_date: '2018-11-01', + release_date_precision: 'day', + total_tracks: 4, + type: 'album', + uri: 'spotify:album:3dB0bCgmpEgCSr3aU1bOtv', + }, + ], + limit: 2, + next: + 'https://api.spotify.com/v1/search?query=sia&type=album&market=BR&offset=2&limit=2', + offset: 0, + previous: null, + total: 708, + }, +}; diff --git a/test/mocks/search-artists.mock.ts b/test/mocks/search-artists.mock.ts new file mode 100644 index 00000000..c156c75a --- /dev/null +++ b/test/mocks/search-artists.mock.ts @@ -0,0 +1,82 @@ +export const searchArtistsMock = { + artists: { + href: + 'https://api.spotify.com/v1/search?query=sia&type=artist&market=BR&offset=0&limit=2', + items: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/5WUlDfRSoLAfcVSX1WnrxN', + }, + followers: { + href: null, + total: 9001196, + }, + genres: [ + 'australian dance', + 'australian pop', + 'dance pop', + 'pop', + ], + href: + 'https://api.spotify.com/v1/artists/5WUlDfRSoLAfcVSX1WnrxN', + id: '5WUlDfRSoLAfcVSX1WnrxN', + images: [ + { + height: 1333, + url: + 'https://i.scdn.co/image/652b6bb0dfaf8aa444f4414ee018699260e74306', + width: 1000, + }, + { + height: 853, + url: + 'https://i.scdn.co/image/a82822ab211cbe28a0a1dbcb16902a1a8a2ea791', + width: 640, + }, + { + height: 267, + url: + 'https://i.scdn.co/image/dd3e336d456172bbda56b543c5389e1490903a30', + width: 200, + }, + { + height: 85, + url: + 'https://i.scdn.co/image/95a2aa98384b31336b8d56f8b470c45b12dcd550', + width: 64, + }, + ], + name: 'Sia', + popularity: 90, + type: 'artist', + uri: 'spotify:artist:5WUlDfRSoLAfcVSX1WnrxN', + }, + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/6vYYsmA7fdkIGOCPjAM7hM', + }, + followers: { + href: null, + total: 628, + }, + genres: [], + href: + 'https://api.spotify.com/v1/artists/6vYYsmA7fdkIGOCPjAM7hM', + id: '6vYYsmA7fdkIGOCPjAM7hM', + images: [], + name: 'Sia Furler', + popularity: 26, + type: 'artist', + uri: 'spotify:artist:6vYYsmA7fdkIGOCPjAM7hM', + }, + ], + limit: 2, + next: + 'https://api.spotify.com/v1/search?query=sia&type=artist&market=BR&offset=2&limit=2', + offset: 0, + previous: null, + total: 81, + }, +}; diff --git a/test/mocks/search-playlists.mock.ts b/test/mocks/search-playlists.mock.ts new file mode 100644 index 00000000..0cbc938d --- /dev/null +++ b/test/mocks/search-playlists.mock.ts @@ -0,0 +1,94 @@ +export const searchPlaylistsMock = { + playlists: { + href: + 'https://api.spotify.com/v1/search?query=sia&type=playlist&market=BR&offset=0&limit=2', + items: [ + { + collaborative: false, + external_urls: { + spotify: + 'https://open.spotify.com/playlist/37i9dQZF1DX7wWaJYweK28', + }, + href: + 'https://api.spotify.com/v1/playlists/37i9dQZF1DX7wWaJYweK28', + id: '37i9dQZF1DX7wWaJYweK28', + images: [ + { + height: 300, + url: + 'https://i.scdn.co/image/4422bab7cebb64aeeefcb9f738a3c7402351138c', + width: 300, + }, + ], + name: 'This Is Sia', + owner: { + display_name: 'Spotify', + external_urls: { + spotify: 'https://open.spotify.com/user/spotify', + }, + href: 'https://api.spotify.com/v1/users/spotify', + id: 'spotify', + type: 'user', + uri: 'spotify:user:spotify', + }, + primary_color: null, + public: null, + snapshot_id: + 'MTUzMzg2MzU1MiwwMDAwMDAwZjAwMDAwMTY1MjE2NDY1ZWUwMDAwMDE2MmYyYjBlOGQ4', + tracks: { + href: + 'https://api.spotify.com/v1/playlists/37i9dQZF1DX7wWaJYweK28/tracks', + total: 50, + }, + type: 'playlist', + uri: 'spotify:user:spotify:playlist:37i9dQZF1DX7wWaJYweK28', + }, + { + collaborative: false, + external_urls: { + spotify: + 'https://open.spotify.com/playlist/05T1KuTCkjxifLHkawRNVX', + }, + href: + 'https://api.spotify.com/v1/playlists/05T1KuTCkjxifLHkawRNVX', + id: '05T1KuTCkjxifLHkawRNVX', + images: [ + { + height: null, + url: + 'https://pl.scdn.co/images/pl/default/b15fb9124d86e7a027b9a66f09a58fc00d056a65', + width: null, + }, + ], + name: 'Sia Discography', + owner: { + display_name: 'Sia', + external_urls: { + spotify: 'https://open.spotify.com/user/siaofficial', + }, + href: 'https://api.spotify.com/v1/users/siaofficial', + id: 'siaofficial', + type: 'user', + uri: 'spotify:user:siaofficial', + }, + primary_color: null, + public: null, + snapshot_id: + 'MTY1LDc4YzVhY2MwYTAzMWIxNjhmNWFlMWI4OTE3MmVlZjM1ZmE5ZTViZjQ=', + tracks: { + href: + 'https://api.spotify.com/v1/playlists/05T1KuTCkjxifLHkawRNVX/tracks', + total: 263, + }, + type: 'playlist', + uri: 'spotify:user:siaofficial:playlist:05T1KuTCkjxifLHkawRNVX', + }, + ], + limit: 2, + next: + 'https://api.spotify.com/v1/search?query=sia&type=playlist&market=BR&offset=2&limit=2', + offset: 0, + previous: null, + total: 27596, + }, +}; diff --git a/test/mocks/search-tracks.mock.ts b/test/mocks/search-tracks.mock.ts new file mode 100644 index 00000000..56b2f6cb --- /dev/null +++ b/test/mocks/search-tracks.mock.ts @@ -0,0 +1,214 @@ +export const searchTracksMock = { + tracks: { + href: + 'https://api.spotify.com/v1/search?query=sia&type=track&market=BR&offset=0&limit=2', + items: [ + { + album: { + album_type: 'single', + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/5ZsFI1h6hIdQRw2ti0hz81', + }, + href: + 'https://api.spotify.com/v1/artists/5ZsFI1h6hIdQRw2ti0hz81', + id: '5ZsFI1h6hIdQRw2ti0hz81', + name: 'ZAYN', + type: 'artist', + uri: 'spotify:artist:5ZsFI1h6hIdQRw2ti0hz81', + }, + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/5WUlDfRSoLAfcVSX1WnrxN', + }, + href: + 'https://api.spotify.com/v1/artists/5WUlDfRSoLAfcVSX1WnrxN', + id: '5WUlDfRSoLAfcVSX1WnrxN', + name: 'Sia', + type: 'artist', + uri: 'spotify:artist:5WUlDfRSoLAfcVSX1WnrxN', + }, + ], + external_urls: { + spotify: + 'https://open.spotify.com/album/5l5gR4rh26QI3fijGFTDrp', + }, + href: + 'https://api.spotify.com/v1/albums/5l5gR4rh26QI3fijGFTDrp', + id: '5l5gR4rh26QI3fijGFTDrp', + images: [ + { + height: 640, + url: + 'https://i.scdn.co/image/53779ee060cd1618e62e7a47bf3c7645ced2ba70', + width: 640, + }, + { + height: 300, + url: + 'https://i.scdn.co/image/492de7f87c8a94d7af46a332eb3688a41f1e0b0d', + width: 300, + }, + { + height: 64, + url: + 'https://i.scdn.co/image/eb0909052dc1bf9eb2da86a79996fd1766d921d6', + width: 64, + }, + ], + name: 'Dusk Till Dawn (Radio Edit)', + release_date: '2017-09-07', + release_date_precision: 'day', + total_tracks: 1, + type: 'album', + uri: 'spotify:album:5l5gR4rh26QI3fijGFTDrp', + }, + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/5ZsFI1h6hIdQRw2ti0hz81', + }, + href: + 'https://api.spotify.com/v1/artists/5ZsFI1h6hIdQRw2ti0hz81', + id: '5ZsFI1h6hIdQRw2ti0hz81', + name: 'ZAYN', + type: 'artist', + uri: 'spotify:artist:5ZsFI1h6hIdQRw2ti0hz81', + }, + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/5WUlDfRSoLAfcVSX1WnrxN', + }, + href: + 'https://api.spotify.com/v1/artists/5WUlDfRSoLAfcVSX1WnrxN', + id: '5WUlDfRSoLAfcVSX1WnrxN', + name: 'Sia', + type: 'artist', + uri: 'spotify:artist:5WUlDfRSoLAfcVSX1WnrxN', + }, + ], + disc_number: 1, + duration_ms: 239000, + explicit: false, + external_ids: { + isrc: 'USRC11702155', + }, + external_urls: { + spotify: + 'https://open.spotify.com/track/1j4kHkkpqZRBwE0A4CN4Yv', + }, + href: + 'https://api.spotify.com/v1/tracks/1j4kHkkpqZRBwE0A4CN4Yv', + id: '1j4kHkkpqZRBwE0A4CN4Yv', + is_local: false, + is_playable: true, + name: 'Dusk Till Dawn - Radio Edit', + popularity: 84, + preview_url: + 'https://p.scdn.co/mp3-preview/e2e03acfd38d7cfa2baa924e0e9c7a80f9b49137?cid=774b29d4f13844c495f206cafdad9c86', + track_number: 1, + type: 'track', + uri: 'spotify:track:1j4kHkkpqZRBwE0A4CN4Yv', + }, + { + album: { + album_type: 'album', + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/5WUlDfRSoLAfcVSX1WnrxN', + }, + href: + 'https://api.spotify.com/v1/artists/5WUlDfRSoLAfcVSX1WnrxN', + id: '5WUlDfRSoLAfcVSX1WnrxN', + name: 'Sia', + type: 'artist', + uri: 'spotify:artist:5WUlDfRSoLAfcVSX1WnrxN', + }, + ], + external_urls: { + spotify: + 'https://open.spotify.com/album/3xFSl9lIRaYXIYkIn3OIl9', + }, + href: + 'https://api.spotify.com/v1/albums/3xFSl9lIRaYXIYkIn3OIl9', + id: '3xFSl9lIRaYXIYkIn3OIl9', + images: [ + { + height: 640, + url: + 'https://i.scdn.co/image/03d97de27e99fd8099506ea172531cea0da59654', + width: 640, + }, + { + height: 300, + url: + 'https://i.scdn.co/image/58595889bbd362182cd3ef5c3633e1c56823d859', + width: 300, + }, + { + height: 64, + url: + 'https://i.scdn.co/image/b8b3036b13b0db9108aeca2b8b779da317973c16', + width: 64, + }, + ], + name: '1000 Forms Of Fear', + release_date: '2014-07-04', + release_date_precision: 'day', + total_tracks: 12, + type: 'album', + uri: 'spotify:album:3xFSl9lIRaYXIYkIn3OIl9', + }, + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/5WUlDfRSoLAfcVSX1WnrxN', + }, + href: + 'https://api.spotify.com/v1/artists/5WUlDfRSoLAfcVSX1WnrxN', + id: '5WUlDfRSoLAfcVSX1WnrxN', + name: 'Sia', + type: 'artist', + uri: 'spotify:artist:5WUlDfRSoLAfcVSX1WnrxN', + }, + ], + disc_number: 1, + duration_ms: 216120, + explicit: false, + external_ids: { + isrc: 'USRC11400498', + }, + external_urls: { + spotify: + 'https://open.spotify.com/track/4VrWlk8IQxevMvERoX08iC', + }, + href: + 'https://api.spotify.com/v1/tracks/4VrWlk8IQxevMvERoX08iC', + id: '4VrWlk8IQxevMvERoX08iC', + is_local: false, + is_playable: true, + name: 'Chandelier', + popularity: 79, + preview_url: + 'https://p.scdn.co/mp3-preview/f1898cf5fd5640d4f0d7faafa6c572fc29db3db6?cid=774b29d4f13844c495f206cafdad9c86', + track_number: 1, + type: 'track', + uri: 'spotify:track:4VrWlk8IQxevMvERoX08iC', + }, + ], + limit: 2, + next: + 'https://api.spotify.com/v1/search?query=sia&type=track&market=BR&offset=2&limit=2', + offset: 0, + previous: null, + total: 6581, + }, +}; diff --git a/test/search.test.ts b/test/search.test.ts new file mode 100644 index 00000000..bb76d1b0 --- /dev/null +++ b/test/search.test.ts @@ -0,0 +1,101 @@ +import { searchTracksMock } from './mocks/search-tracks.mock'; +import { searchPlaylistsMock } from './mocks/search-playlists.mock'; +import nock from 'nock'; + +import { searchAlbumsMock } from './mocks/search-albums.mock'; +import { checkMatchingPagingObjectAttributes } from './common/matching-attributes.test'; + +import { + init, + searchAlbums, + searchArtists, + searchPlaylists, + searchTracks, +} from '../src/lib'; +import { searchArtistsMock } from './mocks/search-artists.mock'; + +describe('Search requests', () => { + beforeEach(() => { + init('SomeToken'); + }); + + describe('#searchAlbums()', () => { + beforeEach(() => { + nock('https://api.spotify.com/v1') + .get('/search') + .query({ q: 'sia', type: 'album', market: 'BR', limit: 2 }) + .reply(200, searchAlbumsMock); + }); + + it('response should match all paging object attributes', async () => { + const searchAlbumsResponse = await searchAlbums('sia', { + market: 'BR', + limit: 2, + }); + checkMatchingPagingObjectAttributes( + searchAlbumsResponse, + searchAlbumsMock.albums + ); + }); + }); + + describe('#searchArtists()', () => { + beforeEach(() => { + nock('https://api.spotify.com/v1') + .get('/search') + .query({ q: 'sia', type: 'artist', market: 'BR', limit: 2 }) + .reply(200, searchArtistsMock); + }); + + it('response should match all paging object attributes', async () => { + const searchArtistsResponse = await searchArtists('sia', { + market: 'BR', + limit: 2, + }); + checkMatchingPagingObjectAttributes( + searchArtistsResponse, + searchArtistsMock.artists + ); + }); + }); + + describe('#searchPlaylists()', () => { + beforeEach(() => { + nock('https://api.spotify.com/v1') + .get('/search') + .query({ q: 'sia', type: 'playlist', market: 'BR', limit: 2 }) + .reply(200, searchPlaylistsMock); + }); + + it('response should match all paging object attributes', async () => { + const searchPlaylistsResponse = await searchPlaylists('sia', { + market: 'BR', + limit: 2, + }); + checkMatchingPagingObjectAttributes( + searchPlaylistsResponse, + searchPlaylistsMock.playlists + ); + }); + }); + + describe('#searchTracks()', () => { + beforeEach(() => { + nock('https://api.spotify.com/v1') + .get('/search') + .query({ q: 'sia', type: 'track', market: 'BR', limit: 2 }) + .reply(200, searchTracksMock); + }); + + it('response should match all paging object attributes', async () => { + const searchTracksResponse = await searchTracks('sia', { + market: 'BR', + limit: 2, + }); + checkMatchingPagingObjectAttributes( + searchTracksResponse, + searchTracksMock.tracks + ); + }); + }); +}); From 9ad3ec7d37756908780d0c1d25c3bc5ba1869b13 Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Sun, 18 Nov 2018 15:13:19 -0300 Subject: [PATCH 09/27] Fix lint errors --- src/lib/models/paging/page.ts | 25 +++++++++++++++---------- src/lib/models/user/user-public.ts | 3 ++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/lib/models/paging/page.ts b/src/lib/models/paging/page.ts index 01c9d049..966e9d2f 100644 --- a/src/lib/models/paging/page.ts +++ b/src/lib/models/paging/page.ts @@ -21,21 +21,26 @@ class Page<T> { constructor(json: any, t: new (json: any) => T, wrapper?: string) { this.wrapper = wrapper; - if (wrapper) json = json[wrapper]; + let unwrappedJson = json; + if (wrapper) unwrappedJson = unwrappedJson[wrapper]; this.t = t; - this.href = json.href; - this.items = json.items.map((json: any) => new t(json)); - this.limit = json.limit; - this.next = json.next ? json.next.split('?')[1] : null; - this.offset = json.offset; - this.previous = json.previous ? json.previous.split('?')[1] : null; - this.total = json.total; + this.href = unwrappedJson.href; + this.items = unwrappedJson.items.map((json: any) => new t(json)); + this.limit = unwrappedJson.limit; + this.next = unwrappedJson.next + ? unwrappedJson.next.split('?')[1] + : null; + this.offset = unwrappedJson.offset; + this.previous = unwrappedJson.previous + ? unwrappedJson.previous.split('?')[1] + : null; + this.total = unwrappedJson.total; } get queryParams(): any { const queryString = this.href.split('?')[1]; const paramsString = queryString.split('&'); - let queryParams: any = {}; + const queryParams: any = {}; for (const param of paramsString) { const [name, value] = param.split('='); @@ -74,7 +79,7 @@ class Page<T> { if (!this.hasPrevious()) throw new Error('There are no more pages'); let limit = this.limit; if (this.offset < this.limit && !includeRepeated) limit = this.offset; - let offset = Math.max(this.offset - this.limit, 0); + const offset = Math.max(this.offset - this.limit, 0); const params = { ...this.queryParams, limit, offset }; const response = await this.getAxiosPageInstance().get('/', { params, diff --git a/src/lib/models/user/user-public.ts b/src/lib/models/user/user-public.ts index 07418fbc..321bfc25 100644 --- a/src/lib/models/user/user-public.ts +++ b/src/lib/models/user/user-public.ts @@ -24,10 +24,11 @@ class PublicUser { if (json.followers) this.followers = new Followers(json.followers); this.href = json.href; this.id = json.id; - if (json.images) + if (json.images) { this.images = json.images.map( (imageJson: any) => new Image(imageJson) ); + } this.type = json.type; this.uri = json.uri; } From 1202a43b9b069e7f04dc089330008050409e492f Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Sun, 18 Nov 2018 15:48:20 -0300 Subject: [PATCH 10/27] Implement personalization requests --- src/lib/personalization.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/lib/personalization.ts diff --git a/src/lib/personalization.ts b/src/lib/personalization.ts new file mode 100644 index 00000000..8beca6e1 --- /dev/null +++ b/src/lib/personalization.ts @@ -0,0 +1,29 @@ +import { getAxiosSpotifyInstance } from './driver'; + +import Page from './models/paging/page'; +import Artist from './models/artist/artist'; +import Track from './models/track/track'; + +export const getCurrentUserTopArtists = async (params?: { + limit?: number; + offset?: number; + range?: string; +}) => { + const response = await getAxiosSpotifyInstance().get( + 'https://api.spotify.com/v1/me/top/artists', + { params } + ); + return new Page<Artist>(response.data, Artist); +}; + +export const getCurrentUserTopTracks = async (params?: { + limit?: number; + offset?: number; + range?: string; +}) => { + const response = await getAxiosSpotifyInstance().get( + 'https://api.spotify.com/v1/me/top/tracks', + { params } + ); + return new Page<Track>(response.data, Track); +}; From c3557e0be36442e93358ac7ae09038f652d38560 Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Sun, 18 Nov 2018 15:48:44 -0300 Subject: [PATCH 11/27] Export personalization requests through index.ts --- src/lib/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib/index.ts b/src/lib/index.ts index 32a86a9c..a1436ff1 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,6 +1,7 @@ +export * from './albums'; +export * from './artists'; export * from './driver'; -export * from './tracks'; export * from './follow'; -export * from './artists'; -export * from './albums'; +export * from './tracks'; +export * from './personalization'; export * from './playlists'; From 07eb45178f3cdcce82837986f4ca7258960a7a68 Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Sun, 18 Nov 2018 20:45:02 -0300 Subject: [PATCH 12/27] Implement tests for personalization requests --- test/mocks/top-artists.mock.ts | 90 ++++++++++++++++ test/mocks/top-tracks.mock.ts | 181 +++++++++++++++++++++++++++++++++ test/personalization.test.ts | 55 ++++++++++ 3 files changed, 326 insertions(+) create mode 100644 test/mocks/top-artists.mock.ts create mode 100644 test/mocks/top-tracks.mock.ts create mode 100644 test/personalization.test.ts diff --git a/test/mocks/top-artists.mock.ts b/test/mocks/top-artists.mock.ts new file mode 100644 index 00000000..e1d46fea --- /dev/null +++ b/test/mocks/top-artists.mock.ts @@ -0,0 +1,90 @@ +export const topArtistsMock = { + items: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/06HL4z0CvFAxyc27GXpf02', + }, + followers: { + href: null, + total: 16168356, + }, + genres: ['dance pop', 'pop', 'post-teen pop'], + href: 'https://api.spotify.com/v1/artists/06HL4z0CvFAxyc27GXpf02', + id: '06HL4z0CvFAxyc27GXpf02', + images: [ + { + height: 640, + url: + 'https://i.scdn.co/image/bdaeccb035a8af87b7a70b62217ff5c633ba6c7c', + width: 640, + }, + { + height: 320, + url: + 'https://i.scdn.co/image/cec43c2fb746ea2a0c7546aa3408fe2f94887fe4', + width: 320, + }, + { + height: 160, + url: + 'https://i.scdn.co/image/33bc9128ad82f7d39847b6db6a49d5416502e7e7', + width: 160, + }, + ], + name: 'Taylor Swift', + popularity: 89, + type: 'artist', + uri: 'spotify:artist:06HL4z0CvFAxyc27GXpf02', + }, + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/6Xb4ezwoAQC4516kI89nWz', + }, + followers: { + href: null, + total: 260793, + }, + genres: ['etherpop', 'folk-pop', 'lilith', 'metropopolis'], + href: 'https://api.spotify.com/v1/artists/6Xb4ezwoAQC4516kI89nWz', + id: '6Xb4ezwoAQC4516kI89nWz', + images: [ + { + height: 1504, + url: + 'https://i.scdn.co/image/d5bfa4c4fee8cf427bc15d33817764293eda8f1d', + width: 1000, + }, + { + height: 963, + url: + 'https://i.scdn.co/image/cc19f21be016979de6cc0b85dc873f22b1681979', + width: 640, + }, + { + height: 301, + url: + 'https://i.scdn.co/image/0b6016ef61b2d3c2dcb2b2277f4db99700cad1e8', + width: 200, + }, + { + height: 96, + url: + 'https://i.scdn.co/image/0b8b63873bbaec4e6c4094778b41528e632ddb9e', + width: 64, + }, + ], + name: 'Imogen Heap', + popularity: 61, + type: 'artist', + uri: 'spotify:artist:6Xb4ezwoAQC4516kI89nWz', + }, + ], + total: 50, + limit: 2, + offset: 0, + href: 'https://api.spotify.com/v1/me/top/artists?limit=2&offset=0', + previous: null, + next: 'https://api.spotify.com/v1/me/top/artists?limit=2&offset=2', +}; diff --git a/test/mocks/top-tracks.mock.ts b/test/mocks/top-tracks.mock.ts new file mode 100644 index 00000000..9d1f8d24 --- /dev/null +++ b/test/mocks/top-tracks.mock.ts @@ -0,0 +1,181 @@ +export const topTracksMock = { + items: [ + { + album: { + album_type: 'SINGLE', + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/1WgXqy2Dd70QQOU7Ay074N', + }, + href: + 'https://api.spotify.com/v1/artists/1WgXqy2Dd70QQOU7Ay074N', + id: '1WgXqy2Dd70QQOU7Ay074N', + name: 'AURORA', + type: 'artist', + uri: 'spotify:artist:1WgXqy2Dd70QQOU7Ay074N', + }, + ], + external_urls: { + spotify: + 'https://open.spotify.com/album/1KqlKDBl2kuVhyXsX1vrVz', + }, + href: + 'https://api.spotify.com/v1/albums/1KqlKDBl2kuVhyXsX1vrVz', + id: '1KqlKDBl2kuVhyXsX1vrVz', + images: [ + { + height: 640, + url: + 'https://i.scdn.co/image/6f4d26e065f85fd14fcafa59d43c3b5570e9f650', + width: 640, + }, + { + height: 300, + url: + 'https://i.scdn.co/image/8283ace6d5e5989f2b0127dc983f67d9531a25d5', + width: 300, + }, + { + height: 64, + url: + 'https://i.scdn.co/image/e5296c6ed295ac85746ad0ff90de18d9065fa142', + width: 64, + }, + ], + name: 'Forgotten Love', + release_date: '2018-08-17', + release_date_precision: 'day', + type: 'album', + uri: 'spotify:album:1KqlKDBl2kuVhyXsX1vrVz', + }, + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/1WgXqy2Dd70QQOU7Ay074N', + }, + href: + 'https://api.spotify.com/v1/artists/1WgXqy2Dd70QQOU7Ay074N', + id: '1WgXqy2Dd70QQOU7Ay074N', + name: 'AURORA', + type: 'artist', + uri: 'spotify:artist:1WgXqy2Dd70QQOU7Ay074N', + }, + ], + disc_number: 1, + duration_ms: 208552, + explicit: false, + external_ids: { + isrc: 'GBUM71803967', + }, + external_urls: { + spotify: + 'https://open.spotify.com/track/0nRNqasPnpHXsHbOighfaj', + }, + href: 'https://api.spotify.com/v1/tracks/0nRNqasPnpHXsHbOighfaj', + id: '0nRNqasPnpHXsHbOighfaj', + is_local: false, + is_playable: true, + name: 'Forgotten Love', + popularity: 50, + preview_url: null, + track_number: 1, + type: 'track', + uri: 'spotify:track:0nRNqasPnpHXsHbOighfaj', + }, + { + album: { + album_type: 'ALBUM', + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/6Xb4ezwoAQC4516kI89nWz', + }, + href: + 'https://api.spotify.com/v1/artists/6Xb4ezwoAQC4516kI89nWz', + id: '6Xb4ezwoAQC4516kI89nWz', + name: 'Imogen Heap', + type: 'artist', + uri: 'spotify:artist:6Xb4ezwoAQC4516kI89nWz', + }, + ], + external_urls: { + spotify: + 'https://open.spotify.com/album/1H8velOQ9zUFqpuQPd2bkO', + }, + href: + 'https://api.spotify.com/v1/albums/1H8velOQ9zUFqpuQPd2bkO', + id: '1H8velOQ9zUFqpuQPd2bkO', + images: [ + { + height: 640, + url: + 'https://i.scdn.co/image/e1287ba626f74622edfe7ed83f7c162cd6153c0b', + width: 640, + }, + { + height: 300, + url: + 'https://i.scdn.co/image/42e168927779f498faee2cc8550c67ae1974e7c1', + width: 300, + }, + { + height: 64, + url: + 'https://i.scdn.co/image/ea8067a3e9e586674942a434b3362b860e5e891e', + width: 64, + }, + ], + name: 'Ellipse', + release_date: '2009-08-20', + release_date_precision: 'day', + type: 'album', + uri: 'spotify:album:1H8velOQ9zUFqpuQPd2bkO', + }, + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/6Xb4ezwoAQC4516kI89nWz', + }, + href: + 'https://api.spotify.com/v1/artists/6Xb4ezwoAQC4516kI89nWz', + id: '6Xb4ezwoAQC4516kI89nWz', + name: 'Imogen Heap', + type: 'artist', + uri: 'spotify:artist:6Xb4ezwoAQC4516kI89nWz', + }, + ], + disc_number: 1, + duration_ms: 253480, + explicit: false, + external_ids: { + isrc: 'GBJPX0900064', + }, + external_urls: { + spotify: + 'https://open.spotify.com/track/6Qy5mfrRPveNNXjEDOG2iV', + }, + href: 'https://api.spotify.com/v1/tracks/6Qy5mfrRPveNNXjEDOG2iV', + id: '6Qy5mfrRPveNNXjEDOG2iV', + is_local: false, + is_playable: true, + name: 'First Train Home', + popularity: 39, + preview_url: + 'https://p.scdn.co/mp3-preview/62be3c5622a0845468524c6c2aaa061768dc2393?cid=null', + track_number: 1, + type: 'track', + uri: 'spotify:track:6Qy5mfrRPveNNXjEDOG2iV', + }, + ], + total: 50, + limit: 2, + offset: 0, + href: 'https://api.spotify.com/v1/me/top/tracks?limit=2&offset=0', + previous: null, + next: 'https://api.spotify.com/v1/me/top/tracks?limit=2&offset=2', +}; diff --git a/test/personalization.test.ts b/test/personalization.test.ts new file mode 100644 index 00000000..2d9a37b1 --- /dev/null +++ b/test/personalization.test.ts @@ -0,0 +1,55 @@ +import nock from 'nock'; + +import { topArtistsMock } from './mocks/top-artists.mock'; +import { topTracksMock } from './mocks/top-tracks.mock'; +import { checkMatchingPagingObjectAttributes } from './common/matching-attributes.test'; + +import { + init, + getCurrentUserTopArtists, + getCurrentUserTopTracks, +} from '../src/lib'; + +describe('Personalization requests', () => { + beforeEach(() => { + init('SomeToken'); + }); + + describe('#getCurrentUserTopArtists()', () => { + beforeEach(() => { + nock('https://api.spotify.com/v1') + .get('/me/top/artists') + .query({ limit: 2 }) + .reply(200, topArtistsMock); + }); + + it('response should match all paging object attributes', async () => { + const topArtistsResponse = await getCurrentUserTopArtists({ + limit: 2, + }); + checkMatchingPagingObjectAttributes( + topArtistsResponse, + topArtistsMock + ); + }); + }); + + describe('#getCurrentUserTopTracks()', () => { + beforeEach(() => { + nock('https://api.spotify.com/v1') + .get('/me/top/tracks') + .query({ limit: 2 }) + .reply(200, topTracksMock); + }); + + it('response should match all paging object attributes', async () => { + const topTracksResponse = await getCurrentUserTopTracks({ + limit: 2, + }); + checkMatchingPagingObjectAttributes( + topTracksResponse, + topTracksMock + ); + }); + }); +}); From 542d902d0b8ecb785f879f0fe53d2c952f31c6a6 Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Sun, 18 Nov 2018 21:08:08 -0300 Subject: [PATCH 13/27] Correct play history model name --- src/lib/models/player/play-history.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/models/player/play-history.ts b/src/lib/models/player/play-history.ts index ffca1416..fb5eb25e 100644 --- a/src/lib/models/player/play-history.ts +++ b/src/lib/models/player/play-history.ts @@ -1,7 +1,7 @@ import Context from './context'; import Track from '../track/track'; -class PlaylistTrack { +class PlayHistory { track: Track; playedAt: string; // Timestamp @@ -15,4 +15,4 @@ class PlaylistTrack { } } -export default PlaylistTrack; +export default PlayHistory; From cdca63b4ccc756cd9a8d1e539279510ed8895954 Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Sun, 18 Nov 2018 21:45:04 -0300 Subject: [PATCH 14/27] Implement cursor-based paging object model --- src/lib/models/paging/cursor-based-page.ts | 52 ++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 src/lib/models/paging/cursor-based-page.ts diff --git a/src/lib/models/paging/cursor-based-page.ts b/src/lib/models/paging/cursor-based-page.ts new file mode 100644 index 00000000..accbf263 --- /dev/null +++ b/src/lib/models/paging/cursor-based-page.ts @@ -0,0 +1,52 @@ +import { getAxiosSpotifyInstance } from '../../driver'; + +class CursorBasedPage<T> { + private t: new (json: any) => T; + href: string; + items: T[]; + limit: number; + next: string; + cursors: any; + total: number; + + constructor(json: any, t: new (json: any) => T) { + this.t = t; + this.href = json.href; + this.items = json.items.map((json: any) => new t(json)); + this.limit = json.limit; + this.next = json.next ? json.next.split('?')[1] : null; + this.cursors = json.cursors; + this.total = json.total; + } + + get queryParams(): any { + const queryString = this.href.split('?')[1]; + const paramsString = queryString.split('&'); + const queryParams: any = {}; + + for (const param of paramsString) { + const [name, value] = param.split('='); + queryParams[name] = value; + } + + return queryParams; + } + + private getAxiosPageInstance() { + const instance = getAxiosSpotifyInstance(); + instance.defaults.baseURL = this.href.split('?')[0]; + return instance; + } + + hasNext() { + return Boolean(this.next); + } + + async getNextPage() { + if (!this.hasNext()) throw new Error('There are no more pages'); + const response = await this.getAxiosPageInstance().get(`?${this.next}`); + return new CursorBasedPage<T>(response.data, this.t); + } +} + +export default CursorBasedPage; From 4166787b53dbf51ab6328e3bf8c39cf6766ae1ca Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Sun, 18 Nov 2018 21:45:38 -0300 Subject: [PATCH 15/27] Implement function to request recently played tracks --- src/lib/index.ts | 1 + src/lib/player.ts | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 src/lib/player.ts diff --git a/src/lib/index.ts b/src/lib/index.ts index 32a86a9c..ce5ed783 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -4,3 +4,4 @@ export * from './follow'; export * from './artists'; export * from './albums'; export * from './playlists'; +export * from './player'; diff --git a/src/lib/player.ts b/src/lib/player.ts new file mode 100644 index 00000000..7fb306e8 --- /dev/null +++ b/src/lib/player.ts @@ -0,0 +1,18 @@ +import { getAxiosSpotifyInstance } from './driver'; + +import PlayHistory from './models/player/play-history'; +import CursorBasedPage from './models/paging/cursor-based-page'; + +export const getCurrentUserRecentlyPlayedTracks = async (params?: { + limit?: number; + before?: string; + after?: string; +}) => { + if (params && params.before && params.after) + throw new Error("Only one of 'before' or 'after' should be specified"); + const response = await getAxiosSpotifyInstance().get( + `/me/player/recently-played`, + { params } + ); + return new CursorBasedPage<PlayHistory>(response.data, PlayHistory); +}; From d24a9e95762b5b4bc9bd8fedfea29a3d8e555eaa Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Mon, 19 Nov 2018 21:57:21 -0300 Subject: [PATCH 16/27] Create currently playing object model --- src/lib/models/player/currently-playing.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/lib/models/player/currently-playing.ts diff --git a/src/lib/models/player/currently-playing.ts b/src/lib/models/player/currently-playing.ts new file mode 100644 index 00000000..69b34285 --- /dev/null +++ b/src/lib/models/player/currently-playing.ts @@ -0,0 +1,22 @@ +import Context from './context'; +import Track from '../track/track'; + +class CurrentlyPlaying { + context: Context | null; + currentlyPlayingType: string; + isPlaying: boolean; + item: Track | null; + progressMs: number; + timestamp: number; + + constructor(json: any) { + this.context = json.context ? new Context(json.context) : null; + this.currentlyPlayingType = json.currently_playing_type; + this.isPlaying = json.is_playing; + this.item = json.item ? new Track(json.item) : null; + this.progressMs = json.progress_ms; + this.timestamp = json.timestamp; + } +} + +export default CurrentlyPlaying; From d267b9fb1d4ac93bf1ce35b69781a20222d85b4c Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Mon, 19 Nov 2018 21:57:32 -0300 Subject: [PATCH 17/27] Implement request for current user's currently playing object --- src/lib/player.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/lib/player.ts b/src/lib/player.ts index 7fb306e8..8780eeca 100644 --- a/src/lib/player.ts +++ b/src/lib/player.ts @@ -2,6 +2,7 @@ import { getAxiosSpotifyInstance } from './driver'; import PlayHistory from './models/player/play-history'; import CursorBasedPage from './models/paging/cursor-based-page'; +import CurrentlyPlaying from './models/player/currently-playing'; export const getCurrentUserRecentlyPlayedTracks = async (params?: { limit?: number; @@ -11,8 +12,18 @@ export const getCurrentUserRecentlyPlayedTracks = async (params?: { if (params && params.before && params.after) throw new Error("Only one of 'before' or 'after' should be specified"); const response = await getAxiosSpotifyInstance().get( - `/me/player/recently-played`, + '/me/player/recently-played', { params } ); return new CursorBasedPage<PlayHistory>(response.data, PlayHistory); }; + +export const getCurrentUserCurrentlyPlayingTrack = async (params?: { + market?: string; +}) => { + const response = await getAxiosSpotifyInstance().get( + '/me/player/currently-playing', + { params } + ); + return new CurrentlyPlaying(response.data); +}; From e3ad6a8915da6315169db6d4f1a513efa4bc8448 Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Mon, 19 Nov 2018 23:06:58 -0300 Subject: [PATCH 18/27] Implement tests for recently played tracks and currently playing track --- test/common/matching-attributes.test.ts | 44 ++- test/mocks/currently-playing.mock.ts | 99 +++++ test/mocks/recently-played-tracks.mock.ts | 453 ++++++++++++++++++++++ test/player.test.ts | 58 +++ 4 files changed, 653 insertions(+), 1 deletion(-) create mode 100644 test/mocks/currently-playing.mock.ts create mode 100644 test/mocks/recently-played-tracks.mock.ts create mode 100644 test/player.test.ts diff --git a/test/common/matching-attributes.test.ts b/test/common/matching-attributes.test.ts index 3242f2fe..79e61a19 100644 --- a/test/common/matching-attributes.test.ts +++ b/test/common/matching-attributes.test.ts @@ -4,6 +4,11 @@ import Album from '../../src/lib/models/album/album'; import AlbumSimplified from '../../src/lib/models/album/album-simplified'; import Artist from '../../src/lib/models/artist/artist'; import Track from '../../src/lib/models/track/track'; +import Page from '../../src/lib/models/paging/page'; +import CurrentlyPlaying from '../../src/lib/models/player/currently-playing'; +import Context from '../../src/lib/models/player/context'; +import CursorBasedPage from '../../src/lib/models/paging/cursor-based-page'; + import { AlbumMock } from '../mocks/album.mock'; import { ArtistMock } from '../mocks/artist.mock'; import { TrackMock } from './../mocks/artist-top-tracks.mock'; @@ -94,7 +99,7 @@ export const checkMatchingTrackAttributes = ( }; export const checkMatchingPagingObjectAttributes = ( - response: any, + response: Page<any>, mock: any ) => { expect(response.href).to.be.equal(mock.href.split('?')[0]); @@ -103,3 +108,40 @@ export const checkMatchingPagingObjectAttributes = ( expect(response.offset).to.be.equal(mock.offset); expect(response.total).to.be.equal(mock.total); }; + +export const checkMatchingCurrentlyPlayingAttributes = ( + response: CurrentlyPlaying, + mock: any +) => { + if (response.context) + checkMatchingContextAttributes(response.context, mock.context); + expect(response.currentlyPlayingType).to.be.equal( + mock.currently_playing_type + ); + expect(response.isPlaying).to.be.equal(mock.is_playing); + if (response.item) checkMatchingTrackAttributes(response.item, mock.item); + expect(response.progressMs).to.be.equal(mock.progress_ms); + expect(response.timestamp).to.be.equal(mock.timestamp); +}; + +export const checkMatchingContextAttributes = ( + response: Context, + mock: any +) => { + expect(response.externalUrls).to.be.eql(mock.external_urls); + expect(response.href).to.be.equal(mock.href); + expect(response.type).to.be.equal(mock.type); + expect(response.uri).to.be.equal(mock.uri); +}; + +export const checkMatchingCursorBasedPageAttributes = ( + response: CursorBasedPage<any>, + mock: any +) => { + expect(response.cursors).to.be.eql(mock.cursors); + expect(response.href).to.be.equal(mock.href); + expect(response.items).to.have.lengthOf(mock.items.length); + expect(response.limit).to.be.equal(mock.limit); + expect(response.next).to.be.equal(mock.next.split('?')[1]); + expect(response.total).to.be.equal(mock.total); +}; diff --git a/test/mocks/currently-playing.mock.ts b/test/mocks/currently-playing.mock.ts new file mode 100644 index 00000000..eb6c4400 --- /dev/null +++ b/test/mocks/currently-playing.mock.ts @@ -0,0 +1,99 @@ +export const currentlyPlayingMock = { + timestamp: 1542673521238, + context: { + external_urls: { + spotify: 'https://open.spotify.com/playlist/5D0F6KQtqd5HuAVX2sOouy', + }, + href: 'https://api.spotify.com/v1/playlists/5D0F6KQtqd5HuAVX2sOouy', + type: 'playlist', + uri: 'spotify:user:jrobsonjr:playlist:5D0F6KQtqd5HuAVX2sOouy', + }, + progress_ms: 17908, + item: { + album: { + album_type: 'album', + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/1WgXqy2Dd70QQOU7Ay074N', + }, + href: + 'https://api.spotify.com/v1/artists/1WgXqy2Dd70QQOU7Ay074N', + id: '1WgXqy2Dd70QQOU7Ay074N', + name: 'AURORA', + type: 'artist', + uri: 'spotify:artist:1WgXqy2Dd70QQOU7Ay074N', + }, + ], + external_urls: { + spotify: + 'https://open.spotify.com/album/2iv4eCuGKJYsso1mDR48dt', + }, + href: 'https://api.spotify.com/v1/albums/2iv4eCuGKJYsso1mDR48dt', + id: '2iv4eCuGKJYsso1mDR48dt', + images: [ + { + height: 640, + url: + 'https://i.scdn.co/image/53a31a48a6db327175be725301ba758bb3bed354', + width: 640, + }, + { + height: 300, + url: + 'https://i.scdn.co/image/0c4210e5b5a44ed2dc6bb42260a4b97c0b0b46a9', + width: 300, + }, + { + height: 64, + url: + 'https://i.scdn.co/image/0e874917c51fe5ab7413f364b1ef1054920b9372', + width: 64, + }, + ], + name: 'Infections Of A Different Kind (Step I)', + release_date: '2018-09-28', + release_date_precision: 'day', + total_tracks: 8, + type: 'album', + uri: 'spotify:album:2iv4eCuGKJYsso1mDR48dt', + }, + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/1WgXqy2Dd70QQOU7Ay074N', + }, + href: + 'https://api.spotify.com/v1/artists/1WgXqy2Dd70QQOU7Ay074N', + id: '1WgXqy2Dd70QQOU7Ay074N', + name: 'AURORA', + type: 'artist', + uri: 'spotify:artist:1WgXqy2Dd70QQOU7Ay074N', + }, + ], + disc_number: 1, + duration_ms: 227320, + explicit: false, + external_ids: { + isrc: 'GBUM71804527', + }, + external_urls: { + spotify: 'https://open.spotify.com/track/0m8D2ocNLLDMh0N9UOECMs', + }, + href: 'https://api.spotify.com/v1/tracks/0m8D2ocNLLDMh0N9UOECMs', + id: '0m8D2ocNLLDMh0N9UOECMs', + is_local: false, + is_playable: true, + name: 'Gentle Earthquakes', + popularity: 49, + preview_url: + 'https://p.scdn.co/mp3-preview/66abfd919a67b646529a171cc35f23b4d3dc64e9?cid=774b29d4f13844c495f206cafdad9c86', + track_number: 3, + type: 'track', + uri: 'spotify:track:0m8D2ocNLLDMh0N9UOECMs', + }, + currently_playing_type: 'track', + is_playing: true, +}; diff --git a/test/mocks/recently-played-tracks.mock.ts b/test/mocks/recently-played-tracks.mock.ts new file mode 100644 index 00000000..541f88dd --- /dev/null +++ b/test/mocks/recently-played-tracks.mock.ts @@ -0,0 +1,453 @@ +export const recentlyPlayedTracksMock = { + items: [ + { + track: { + album: { + album_type: 'album', + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/6Xb4ezwoAQC4516kI89nWz', + }, + href: + 'https://api.spotify.com/v1/artists/6Xb4ezwoAQC4516kI89nWz', + id: '6Xb4ezwoAQC4516kI89nWz', + name: 'Imogen Heap', + type: 'artist', + uri: 'spotify:artist:6Xb4ezwoAQC4516kI89nWz', + }, + ], + available_markets: [ + 'AD', + 'AR', + 'AT', + 'AU', + 'BE', + 'BG', + 'BO', + 'BR', + 'CA', + 'CH', + 'CL', + 'CO', + 'CR', + 'CY', + 'CZ', + 'DE', + 'DK', + 'DO', + 'EC', + 'EE', + 'ES', + 'FI', + 'FR', + 'GB', + 'GR', + 'GT', + 'HK', + 'HN', + 'HU', + 'ID', + 'IE', + 'IS', + 'IT', + 'JP', + 'LI', + 'LT', + 'LU', + 'LV', + 'MC', + 'MT', + 'MX', + 'MY', + 'NI', + 'NL', + 'NO', + 'NZ', + 'PA', + 'PE', + 'PH', + 'PL', + 'PT', + 'PY', + 'SE', + 'SG', + 'SK', + 'SV', + 'TR', + 'TW', + 'US', + 'UY', + ], + external_urls: { + spotify: + 'https://open.spotify.com/album/0S4rmBsXso00O0Lp3mJOr9', + }, + href: + 'https://api.spotify.com/v1/albums/0S4rmBsXso00O0Lp3mJOr9', + id: '0S4rmBsXso00O0Lp3mJOr9', + images: [ + { + height: 640, + url: + 'https://i.scdn.co/image/6a957a85953eb2f2f77b1cd83cba7d0acbde1155', + width: 640, + }, + { + height: 300, + url: + 'https://i.scdn.co/image/df2b0f824b9b93365e21ef4c9d7fa9354d2eff3f', + width: 300, + }, + { + height: 64, + url: + 'https://i.scdn.co/image/d19523118f3b0695a6c9ade8bd4a8bcb7fe6eb83', + width: 64, + }, + ], + name: 'Sparks', + type: 'album', + uri: 'spotify:album:0S4rmBsXso00O0Lp3mJOr9', + }, + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/6Xb4ezwoAQC4516kI89nWz', + }, + href: + 'https://api.spotify.com/v1/artists/6Xb4ezwoAQC4516kI89nWz', + id: '6Xb4ezwoAQC4516kI89nWz', + name: 'Imogen Heap', + type: 'artist', + uri: 'spotify:artist:6Xb4ezwoAQC4516kI89nWz', + }, + ], + available_markets: [ + 'AD', + 'AR', + 'AT', + 'AU', + 'BE', + 'BG', + 'BO', + 'BR', + 'CA', + 'CH', + 'CL', + 'CO', + 'CR', + 'CY', + 'CZ', + 'DE', + 'DK', + 'DO', + 'EC', + 'EE', + 'ES', + 'FI', + 'FR', + 'GB', + 'GR', + 'GT', + 'HK', + 'HN', + 'HU', + 'ID', + 'IE', + 'IS', + 'IT', + 'JP', + 'LI', + 'LT', + 'LU', + 'LV', + 'MC', + 'MT', + 'MX', + 'MY', + 'NI', + 'NL', + 'NO', + 'NZ', + 'PA', + 'PE', + 'PH', + 'PL', + 'PT', + 'PY', + 'SE', + 'SG', + 'SK', + 'SV', + 'TR', + 'TW', + 'US', + 'UY', + ], + disc_number: 1, + duration_ms: 324046, + explicit: false, + external_ids: { + isrc: 'GBJPX1300134', + }, + external_urls: { + spotify: + 'https://open.spotify.com/track/3t11m4SVPlTlwF9Ojz99uy', + }, + href: + 'https://api.spotify.com/v1/tracks/3t11m4SVPlTlwF9Ojz99uy', + id: '3t11m4SVPlTlwF9Ojz99uy', + name: 'The Listening Chair', + popularity: 12, + preview_url: + 'https://p.scdn.co/mp3-preview/a4696097530075efc816f709890b22158a272f15?cid=774b29d4f13844c495f206cafdad9c86', + track_number: 3, + type: 'track', + uri: 'spotify:track:3t11m4SVPlTlwF9Ojz99uy', + }, + played_at: '2018-11-19T20:44:30.408Z', + context: { + uri: 'spotify:playlist:1JJAC9FwjfaGfcWRWbFNTY', + external_urls: { + spotify: + 'https://open.spotify.com/playlist/1JJAC9FwjfaGfcWRWbFNTY', + }, + href: + 'https://api.spotify.com/v1/playlists/1JJAC9FwjfaGfcWRWbFNTY', + type: 'playlist_v2', + }, + }, + { + track: { + album: { + album_type: 'album', + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/6Xb4ezwoAQC4516kI89nWz', + }, + href: + 'https://api.spotify.com/v1/artists/6Xb4ezwoAQC4516kI89nWz', + id: '6Xb4ezwoAQC4516kI89nWz', + name: 'Imogen Heap', + type: 'artist', + uri: 'spotify:artist:6Xb4ezwoAQC4516kI89nWz', + }, + ], + available_markets: [ + 'AD', + 'AR', + 'AT', + 'AU', + 'BE', + 'BG', + 'BO', + 'BR', + 'CA', + 'CH', + 'CL', + 'CO', + 'CR', + 'CY', + 'CZ', + 'DE', + 'DK', + 'DO', + 'EC', + 'EE', + 'ES', + 'FI', + 'FR', + 'GB', + 'GR', + 'GT', + 'HK', + 'HN', + 'HU', + 'ID', + 'IE', + 'IS', + 'IT', + 'JP', + 'LI', + 'LT', + 'LU', + 'LV', + 'MC', + 'MT', + 'MX', + 'MY', + 'NI', + 'NL', + 'NO', + 'NZ', + 'PA', + 'PE', + 'PH', + 'PL', + 'PT', + 'PY', + 'SE', + 'SG', + 'SK', + 'SV', + 'TR', + 'TW', + 'US', + 'UY', + ], + external_urls: { + spotify: + 'https://open.spotify.com/album/1H8velOQ9zUFqpuQPd2bkO', + }, + href: + 'https://api.spotify.com/v1/albums/1H8velOQ9zUFqpuQPd2bkO', + id: '1H8velOQ9zUFqpuQPd2bkO', + images: [ + { + height: 640, + url: + 'https://i.scdn.co/image/e1287ba626f74622edfe7ed83f7c162cd6153c0b', + width: 640, + }, + { + height: 300, + url: + 'https://i.scdn.co/image/42e168927779f498faee2cc8550c67ae1974e7c1', + width: 300, + }, + { + height: 64, + url: + 'https://i.scdn.co/image/ea8067a3e9e586674942a434b3362b860e5e891e', + width: 64, + }, + ], + name: 'Ellipse', + type: 'album', + uri: 'spotify:album:1H8velOQ9zUFqpuQPd2bkO', + }, + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/6Xb4ezwoAQC4516kI89nWz', + }, + href: + 'https://api.spotify.com/v1/artists/6Xb4ezwoAQC4516kI89nWz', + id: '6Xb4ezwoAQC4516kI89nWz', + name: 'Imogen Heap', + type: 'artist', + uri: 'spotify:artist:6Xb4ezwoAQC4516kI89nWz', + }, + ], + available_markets: [ + 'AD', + 'AR', + 'AT', + 'AU', + 'BE', + 'BG', + 'BO', + 'BR', + 'CA', + 'CH', + 'CL', + 'CO', + 'CR', + 'CY', + 'CZ', + 'DE', + 'DK', + 'DO', + 'EC', + 'EE', + 'ES', + 'FI', + 'FR', + 'GB', + 'GR', + 'GT', + 'HK', + 'HN', + 'HU', + 'ID', + 'IE', + 'IS', + 'IT', + 'JP', + 'LI', + 'LT', + 'LU', + 'LV', + 'MC', + 'MT', + 'MX', + 'MY', + 'NI', + 'NL', + 'NO', + 'NZ', + 'PA', + 'PE', + 'PH', + 'PL', + 'PT', + 'PY', + 'SE', + 'SG', + 'SK', + 'SV', + 'TR', + 'TW', + 'US', + 'UY', + ], + disc_number: 1, + duration_ms: 237453, + explicit: false, + external_ids: { + isrc: 'GBJPX0900065', + }, + external_urls: { + spotify: + 'https://open.spotify.com/track/3RP28uWsdXhqxJ3rguyRJb', + }, + href: + 'https://api.spotify.com/v1/tracks/3RP28uWsdXhqxJ3rguyRJb', + id: '3RP28uWsdXhqxJ3rguyRJb', + name: 'Wait It Out', + popularity: 40, + preview_url: + 'https://p.scdn.co/mp3-preview/d71fd4cf00e031daccaceceb82ad23e27e1d006b?cid=774b29d4f13844c495f206cafdad9c86', + track_number: 2, + type: 'track', + uri: 'spotify:track:3RP28uWsdXhqxJ3rguyRJb', + }, + played_at: '2018-11-19T20:39:05.819Z', + context: { + uri: 'spotify:playlist:1JJAC9FwjfaGfcWRWbFNTY', + external_urls: { + spotify: + 'https://open.spotify.com/playlist/1JJAC9FwjfaGfcWRWbFNTY', + }, + href: + 'https://api.spotify.com/v1/playlists/1JJAC9FwjfaGfcWRWbFNTY', + type: 'playlist_v2', + }, + }, + ], + next: + 'https://api.spotify.com/v1/me/player/recently-played?before=1542659945819&limit=2', + cursors: { + after: '1542660270408', + before: '1542659945819', + }, + limit: 2, + href: + 'https://api.spotify.com/v1/me/player/recently-played?before=1542660552899&limit=2', +}; diff --git a/test/player.test.ts b/test/player.test.ts new file mode 100644 index 00000000..0bb636e5 --- /dev/null +++ b/test/player.test.ts @@ -0,0 +1,58 @@ +import nock from 'nock'; + +import { currentlyPlayingMock } from './mocks/currently-playing.mock'; +import { recentlyPlayedTracksMock } from './mocks/recently-played-tracks.mock'; +import { + checkMatchingCurrentlyPlayingAttributes, + checkMatchingCursorBasedPageAttributes, +} from './common/matching-attributes.test'; + +import { + init, + getCurrentUserRecentlyPlayedTracks, + getCurrentUserCurrentlyPlayingTrack, +} from '../src/lib'; + +describe('Player requests', () => { + beforeEach(() => { + init('SomeToken'); + }); + + describe('#getCurrentUserRecentlyPlayedTracks()', () => { + beforeEach(() => { + nock('https://api.spotify.com/v1') + .get('/me/player/recently-played') + .query({ limit: 2 }) + .reply(200, recentlyPlayedTracksMock); + }); + + it('response should match all cursor-based paging attributes', async () => { + const recentlyPlayedTracksResponse = await getCurrentUserRecentlyPlayedTracks( + { limit: 2 } + ); + checkMatchingCursorBasedPageAttributes( + recentlyPlayedTracksResponse, + recentlyPlayedTracksMock + ); + }); + }); + + describe('#getCurrentUserCurrentlyPlayingTrack()', () => { + beforeEach(() => { + nock('https://api.spotify.com/v1') + .get('/me/player/currently-playing') + .query({ market: 'BR' }) + .reply(200, currentlyPlayingMock); + }); + + it('response should match all currently playing object attributes', async () => { + const currentlyPlayingResponse = await getCurrentUserCurrentlyPlayingTrack( + { market: 'BR' } + ); + checkMatchingCurrentlyPlayingAttributes( + currentlyPlayingResponse, + currentlyPlayingMock + ); + }); + }); +}); From 3c28e2500c4734b35bdd6b39b3e087f57cee59bc Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Mon, 19 Nov 2018 23:20:20 -0300 Subject: [PATCH 19/27] Fix lint error --- src/lib/player.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/player.ts b/src/lib/player.ts index 8780eeca..b4474a34 100644 --- a/src/lib/player.ts +++ b/src/lib/player.ts @@ -9,8 +9,9 @@ export const getCurrentUserRecentlyPlayedTracks = async (params?: { before?: string; after?: string; }) => { - if (params && params.before && params.after) + if (params && params.before && params.after) { throw new Error("Only one of 'before' or 'after' should be specified"); + } const response = await getAxiosSpotifyInstance().get( '/me/player/recently-played', { params } From 03fc042696f3ccf702db1c9bbc188a90a12310eb Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Tue, 20 Nov 2018 00:45:04 -0300 Subject: [PATCH 20/27] Create models for audio analysis and its attributes --- src/lib/models/track/audio-analysis.ts | 145 +++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 src/lib/models/track/audio-analysis.ts diff --git a/src/lib/models/track/audio-analysis.ts b/src/lib/models/track/audio-analysis.ts new file mode 100644 index 00000000..f0193286 --- /dev/null +++ b/src/lib/models/track/audio-analysis.ts @@ -0,0 +1,145 @@ +class AudioAnalysis { + bars: TimeInterval[]; + beats: TimeInterval[]; + sections: Section[]; + segments: Segment[]; + tatums: TimeInterval[]; + track: TrackAnalysisData; + + constructor(json: any) { + this.bars = json.bars.map((bar: any) => new TimeInterval(bar)); + this.beats = json.beats.map((beat: any) => new TimeInterval(beat)); + this.sections = json.sections.map( + (section: any) => new Section(section) + ); + this.segments = json.segments.map( + (segment: any) => new Segment(segment) + ); + this.tatums = json.tatums.map((tatum: any) => new TimeInterval(tatum)); + this.track = new TrackAnalysisData(json.track); + } +} + +class TimeInterval { + start: number; + duration: number; + confidence: number; + + constructor(json: any) { + this.start = json.start; + this.duration = json.duration; + this.confidence = json.confidence; + } +} + +class Section { + start: number; + duration: number; + confidence: number; + loudness: number; + tempo: number; + tempoConfidence: number; + key: number; + keyConfidence: number; + mode: number; + modeConfidence: number; + timeSignature: number; + timeSignatureConfidence: number; + + constructor(json: any) { + this.start = json.start; + this.duration = json.duration; + this.confidence = json.confidence; + this.loudness = json.loudness; + this.tempo = json.tempo; + this.tempoConfidence = json.tempo_confidence; + this.key = json.key; + this.keyConfidence = json.key_confidence; + this.mode = json.mode; + this.modeConfidence = json.mode_confidence; + this.timeSignature = json.time_signature; + this.timeSignatureConfidence = json.time_signature_confidence; + } +} + +class Segment { + start: number; + duration: number; + confidence: number; + loudnessStart: number; + loudnessMaxTime: number; + loudnessMax: number; + loudnessEnd: number; + pitches: number[]; + timbre: number[]; + + constructor(json: any) { + this.start = json.start; + this.duration = json.duration; + this.confidence = json.confidence; + this.loudnessStart = json.loudness_start; + this.loudnessMaxTime = json.loudness_max_time; + this.loudnessMax = json.loudness_max; + this.loudnessEnd = json.loudness_end; + this.pitches = json.pitches; + this.timbre = json.timbre; + } +} + +class TrackAnalysisData { + duration: number; + sampleMd5: string; + offsetSeconds: number; + windowSeconds: number; + analysisSampleRate: number; + analysisChannels: number; + endOfFadeIn: number; + startOfFadeOut: number; + loudness: number; + tempo: number; + tempoConfidence: number; + timeSignature: number; + timeSignatureConfidence: number; + key: number; + keyConfidence: number; + mode: number; + modeConfidence: number; + codeString: string; + codeVersion: number; + echoprintString: string; + echoprintVersion: number; + synchString: string; + synchVersion: number; + rhythmString: string; + rhythmVersion: number; + + constructor(json: any) { + this.duration = json.duration; + this.sampleMd5 = json.sample_md5; + this.offsetSeconds = json.offset_seconds; + this.windowSeconds = json.window_seconds; + this.analysisSampleRate = json.analysis_sample_rate; + this.analysisChannels = json.analysis_channels; + this.endOfFadeIn = json.end_of_fade_in; + this.startOfFadeOut = json.start_of_fade_out; + this.loudness = json.loudness; + this.tempo = json.tempo; + this.tempoConfidence = json.tempo_confidence; + this.timeSignature = json.time_signature; + this.timeSignatureConfidence = json.time_signature_confidence; + this.key = json.key; + this.keyConfidence = json.key_confidence; + this.mode = json.mode; + this.modeConfidence = json.mode_confidence; + this.codeString = json.codestring; + this.codeVersion = json.code_version; + this.echoprintString = json.echoprintstring; + this.echoprintVersion = json.echoprint_version; + this.synchString = json.synchstring; + this.synchVersion = json.synch_version; + this.rhythmString = json.rhythmstring; + this.rhythmVersion = json.rhythm_version; + } +} + +export default AudioAnalysis; From 4f55ed67ee0eb1516e8fedd117a2ce17cd7f27f0 Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Tue, 20 Nov 2018 00:45:37 -0300 Subject: [PATCH 21/27] Implement missing track-related requests --- src/lib/tracks.ts | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/lib/tracks.ts b/src/lib/tracks.ts index 157fb759..e1c24d07 100644 --- a/src/lib/tracks.ts +++ b/src/lib/tracks.ts @@ -1,5 +1,8 @@ import { getAxiosSpotifyInstance } from './driver'; + import Track from './models/track/track'; +import AudioAnalysis from './models/track/audio-analysis'; +import AudioFeatures from './models/track/audio-features'; export const getSeveralTracks = async (ids: string[]) => { if (ids.length > 50) { @@ -9,8 +12,8 @@ export const getSeveralTracks = async (ids: string[]) => { `The maximum number of tracks is 50. See ${exceptionLink} for details` ); } - const params = { params: { ids } }; - const response = await getAxiosSpotifyInstance().get('/tracks', params); + const params = { ids: ids.join(',') }; + const response = await getAxiosSpotifyInstance().get('/tracks', { params }); return response.data.tracks.map((trackJson: any) => new Track(trackJson)); }; @@ -18,3 +21,27 @@ export const getTrack = async (id: string) => { const response = await getAxiosSpotifyInstance().get(`/tracks/${id}`); return new Track(response.data); }; + +export const getAudioAnalysisForTrack = async (id: string) => { + const response = await getAxiosSpotifyInstance().get( + `/audio-analysis/${id}` + ); + return new AudioAnalysis(response.data); +}; + +export const getAudioFeaturesForTrack = async (id: string) => { + const response = await getAxiosSpotifyInstance().get( + `/audio-features/${id}` + ); + return new AudioFeatures(response.data); +}; + +export const getAudioFeaturesForSeveralTracks = async (ids: string[]) => { + const params = { ids: ids.join(',') }; + const response = await getAxiosSpotifyInstance().get(`/audio-features`, { + params, + }); + return response.data.audio_features.map( + (audioFeaturesJson: any) => new AudioFeatures(audioFeaturesJson) + ); +}; From fb1f7cd7cc8d11d4431dbecae5e4b263f43c5c32 Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Tue, 20 Nov 2018 00:47:15 -0300 Subject: [PATCH 22/27] Fix typo in request --- src/lib/playlists.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/playlists.ts b/src/lib/playlists.ts index 01d8ff10..26076796 100644 --- a/src/lib/playlists.ts +++ b/src/lib/playlists.ts @@ -5,7 +5,7 @@ import Page from './models/paging/page'; import PlaylistSimplified from './models/playlist/playlist-simplified'; export const getPlaylist = async (id: string) => { - const response = await getAxiosSpotifyInstance().get(`/plylists/${id}`); + const response = await getAxiosSpotifyInstance().get(`/playlists/${id}`); return new Playlist(response.data); }; From a17340afa130b81c0770284ef5909a9497382ab2 Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Tue, 20 Nov 2018 20:35:41 -0300 Subject: [PATCH 23/27] Add optional parameters to track-related requests --- src/lib/tracks.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/lib/tracks.ts b/src/lib/tracks.ts index e1c24d07..f16681ed 100644 --- a/src/lib/tracks.ts +++ b/src/lib/tracks.ts @@ -4,7 +4,10 @@ import Track from './models/track/track'; import AudioAnalysis from './models/track/audio-analysis'; import AudioFeatures from './models/track/audio-features'; -export const getSeveralTracks = async (ids: string[]) => { +export const getSeveralTracks = async ( + ids: string[], + params?: { market?: string } +) => { if (ids.length > 50) { const exceptionLink = 'https://developer.spotify.com/documentation/web-api/reference/tracks/get-several-tracks/'; @@ -12,13 +15,16 @@ export const getSeveralTracks = async (ids: string[]) => { `The maximum number of tracks is 50. See ${exceptionLink} for details` ); } - const params = { ids: ids.join(',') }; - const response = await getAxiosSpotifyInstance().get('/tracks', { params }); + const response = await getAxiosSpotifyInstance().get('/tracks', { + params: { ids: ids.join(','), ...params }, + }); return response.data.tracks.map((trackJson: any) => new Track(trackJson)); }; -export const getTrack = async (id: string) => { - const response = await getAxiosSpotifyInstance().get(`/tracks/${id}`); +export const getTrack = async (id: string, params?: { market?: string }) => { + const response = await getAxiosSpotifyInstance().get(`/tracks/${id}`, { + params, + }); return new Track(response.data); }; From 27e7a05d1fe36da97d4143b2dada7cb7e810166d Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Tue, 20 Nov 2018 20:35:57 -0300 Subject: [PATCH 24/27] Implement tests for getTrack and getSeveralTracks --- test/mocks/several-tracks.mock.ts | 178 ++++++++++++++++++++++++++++++ test/mocks/track.mock.ts | 83 ++++++++++++++ test/track.test.ts | 51 +++++++++ 3 files changed, 312 insertions(+) create mode 100644 test/mocks/several-tracks.mock.ts create mode 100644 test/mocks/track.mock.ts create mode 100644 test/track.test.ts diff --git a/test/mocks/several-tracks.mock.ts b/test/mocks/several-tracks.mock.ts new file mode 100644 index 00000000..fd622990 --- /dev/null +++ b/test/mocks/several-tracks.mock.ts @@ -0,0 +1,178 @@ +export const severalTracksMock = { + tracks: [ + { + album: { + album_type: 'album', + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/1WgXqy2Dd70QQOU7Ay074N', + }, + href: + 'https://api.spotify.com/v1/artists/1WgXqy2Dd70QQOU7Ay074N', + id: '1WgXqy2Dd70QQOU7Ay074N', + name: 'AURORA', + type: 'artist', + uri: 'spotify:artist:1WgXqy2Dd70QQOU7Ay074N', + }, + ], + external_urls: { + spotify: + 'https://open.spotify.com/album/2iv4eCuGKJYsso1mDR48dt', + }, + href: + 'https://api.spotify.com/v1/albums/2iv4eCuGKJYsso1mDR48dt', + id: '2iv4eCuGKJYsso1mDR48dt', + images: [ + { + height: 640, + url: + 'https://i.scdn.co/image/53a31a48a6db327175be725301ba758bb3bed354', + width: 640, + }, + { + height: 300, + url: + 'https://i.scdn.co/image/0c4210e5b5a44ed2dc6bb42260a4b97c0b0b46a9', + width: 300, + }, + { + height: 64, + url: + 'https://i.scdn.co/image/0e874917c51fe5ab7413f364b1ef1054920b9372', + width: 64, + }, + ], + name: 'Infections Of A Different Kind (Step I)', + release_date: '2018-09-28', + release_date_precision: 'day', + total_tracks: 8, + type: 'album', + uri: 'spotify:album:2iv4eCuGKJYsso1mDR48dt', + }, + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/1WgXqy2Dd70QQOU7Ay074N', + }, + href: + 'https://api.spotify.com/v1/artists/1WgXqy2Dd70QQOU7Ay074N', + id: '1WgXqy2Dd70QQOU7Ay074N', + name: 'AURORA', + type: 'artist', + uri: 'spotify:artist:1WgXqy2Dd70QQOU7Ay074N', + }, + ], + disc_number: 1, + duration_ms: 239533, + explicit: false, + external_ids: { + isrc: 'GBUM71801202', + }, + external_urls: { + spotify: + 'https://open.spotify.com/track/6HmBWGDgxHNlE3AvX5aNN5', + }, + href: 'https://api.spotify.com/v1/tracks/6HmBWGDgxHNlE3AvX5aNN5', + id: '6HmBWGDgxHNlE3AvX5aNN5', + is_local: false, + is_playable: true, + name: 'Soft Universe', + popularity: 49, + preview_url: + 'https://p.scdn.co/mp3-preview/b0ffc1be502212bf4832c10f063b5986ec1b11f7?cid=774b29d4f13844c495f206cafdad9c86', + track_number: 7, + type: 'track', + uri: 'spotify:track:6HmBWGDgxHNlE3AvX5aNN5', + }, + { + album: { + album_type: 'album', + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/1WgXqy2Dd70QQOU7Ay074N', + }, + href: + 'https://api.spotify.com/v1/artists/1WgXqy2Dd70QQOU7Ay074N', + id: '1WgXqy2Dd70QQOU7Ay074N', + name: 'AURORA', + type: 'artist', + uri: 'spotify:artist:1WgXqy2Dd70QQOU7Ay074N', + }, + ], + external_urls: { + spotify: + 'https://open.spotify.com/album/2iv4eCuGKJYsso1mDR48dt', + }, + href: + 'https://api.spotify.com/v1/albums/2iv4eCuGKJYsso1mDR48dt', + id: '2iv4eCuGKJYsso1mDR48dt', + images: [ + { + height: 640, + url: + 'https://i.scdn.co/image/53a31a48a6db327175be725301ba758bb3bed354', + width: 640, + }, + { + height: 300, + url: + 'https://i.scdn.co/image/0c4210e5b5a44ed2dc6bb42260a4b97c0b0b46a9', + width: 300, + }, + { + height: 64, + url: + 'https://i.scdn.co/image/0e874917c51fe5ab7413f364b1ef1054920b9372', + width: 64, + }, + ], + name: 'Infections Of A Different Kind (Step I)', + release_date: '2018-09-28', + release_date_precision: 'day', + total_tracks: 8, + type: 'album', + uri: 'spotify:album:2iv4eCuGKJYsso1mDR48dt', + }, + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/1WgXqy2Dd70QQOU7Ay074N', + }, + href: + 'https://api.spotify.com/v1/artists/1WgXqy2Dd70QQOU7Ay074N', + id: '1WgXqy2Dd70QQOU7Ay074N', + name: 'AURORA', + type: 'artist', + uri: 'spotify:artist:1WgXqy2Dd70QQOU7Ay074N', + }, + ], + disc_number: 1, + duration_ms: 326918, + explicit: false, + external_ids: { + isrc: 'GBUM71804531', + }, + external_urls: { + spotify: + 'https://open.spotify.com/track/1ppv75fAfo5i5vB3e7kp4l', + }, + href: 'https://api.spotify.com/v1/tracks/1ppv75fAfo5i5vB3e7kp4l', + id: '1ppv75fAfo5i5vB3e7kp4l', + is_local: false, + is_playable: true, + name: 'Infections Of A Different Kind', + popularity: 47, + preview_url: + 'https://p.scdn.co/mp3-preview/aa3327b064438deb444fb6bd21208ca9d7cec72d?cid=774b29d4f13844c495f206cafdad9c86', + track_number: 8, + type: 'track', + uri: 'spotify:track:1ppv75fAfo5i5vB3e7kp4l', + }, + ], +}; diff --git a/test/mocks/track.mock.ts b/test/mocks/track.mock.ts new file mode 100644 index 00000000..97ce42fa --- /dev/null +++ b/test/mocks/track.mock.ts @@ -0,0 +1,83 @@ +export const trackMock = { + album: { + album_type: 'album', + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/1WgXqy2Dd70QQOU7Ay074N', + }, + href: + 'https://api.spotify.com/v1/artists/1WgXqy2Dd70QQOU7Ay074N', + id: '1WgXqy2Dd70QQOU7Ay074N', + name: 'AURORA', + type: 'artist', + uri: 'spotify:artist:1WgXqy2Dd70QQOU7Ay074N', + }, + ], + external_urls: { + spotify: 'https://open.spotify.com/album/2iv4eCuGKJYsso1mDR48dt', + }, + href: 'https://api.spotify.com/v1/albums/2iv4eCuGKJYsso1mDR48dt', + id: '2iv4eCuGKJYsso1mDR48dt', + images: [ + { + height: 640, + url: + 'https://i.scdn.co/image/53a31a48a6db327175be725301ba758bb3bed354', + width: 640, + }, + { + height: 300, + url: + 'https://i.scdn.co/image/0c4210e5b5a44ed2dc6bb42260a4b97c0b0b46a9', + width: 300, + }, + { + height: 64, + url: + 'https://i.scdn.co/image/0e874917c51fe5ab7413f364b1ef1054920b9372', + width: 64, + }, + ], + name: 'Infections Of A Different Kind (Step I)', + release_date: '2018-09-28', + release_date_precision: 'day', + total_tracks: 8, + type: 'album', + uri: 'spotify:album:2iv4eCuGKJYsso1mDR48dt', + }, + artists: [ + { + external_urls: { + spotify: + 'https://open.spotify.com/artist/1WgXqy2Dd70QQOU7Ay074N', + }, + href: 'https://api.spotify.com/v1/artists/1WgXqy2Dd70QQOU7Ay074N', + id: '1WgXqy2Dd70QQOU7Ay074N', + name: 'AURORA', + type: 'artist', + uri: 'spotify:artist:1WgXqy2Dd70QQOU7Ay074N', + }, + ], + disc_number: 1, + duration_ms: 239533, + explicit: false, + external_ids: { + isrc: 'GBUM71801202', + }, + external_urls: { + spotify: 'https://open.spotify.com/track/6HmBWGDgxHNlE3AvX5aNN5', + }, + href: 'https://api.spotify.com/v1/tracks/6HmBWGDgxHNlE3AvX5aNN5', + id: '6HmBWGDgxHNlE3AvX5aNN5', + is_local: false, + is_playable: true, + name: 'Soft Universe', + popularity: 49, + preview_url: + 'https://p.scdn.co/mp3-preview/b0ffc1be502212bf4832c10f063b5986ec1b11f7?cid=774b29d4f13844c495f206cafdad9c86', + track_number: 7, + type: 'track', + uri: 'spotify:track:6HmBWGDgxHNlE3AvX5aNN5', +}; diff --git a/test/track.test.ts b/test/track.test.ts new file mode 100644 index 00000000..0ff0f3b0 --- /dev/null +++ b/test/track.test.ts @@ -0,0 +1,51 @@ +import nock from 'nock'; + +import { trackMock } from './mocks/track.mock'; +import { severalTracksMock } from './mocks/several-tracks.mock'; +import { checkMatchingTrackAttributes } from './common/matching-attributes.test'; + +import { init, getTrack, getSeveralTracks } from '../src/lib'; + +describe('Track requests', () => { + beforeEach(() => { + init('SomeToken'); + }); + + describe('#getTrack()', () => { + const trackId = '6HmBWGDgxHNlE3AvX5aNN5'; + const params = { market: 'BR' }; + + beforeEach(() => { + nock('https://api.spotify.com/v1') + .get(`/tracks/${trackId}`) + .query(params) + .reply(200, trackMock); + }); + + it('response should match all track attributes', async () => { + const trackResponse = await getTrack(trackId, params); + checkMatchingTrackAttributes(trackResponse, trackMock); + }); + }); + + describe('#getSeveralTracks()', () => { + const tracks = ['6HmBWGDgxHNlE3AvX5aNN5', '1ppv75fAfo5i5vB3e7kp4l']; + const params = { ids: tracks.join(','), market: 'BR' }; + + beforeEach(() => { + nock('https://api.spotify.com/v1') + .get(`/tracks`) + .query(params) + .reply(200, severalTracksMock); + }); + + it('response should match all tracks attributes', async () => { + const tracksResponse = await getSeveralTracks(tracks, params); + for (let i = 0; i < tracksResponse.length; i++) { + const trackResponse = tracksResponse[i]; + const trackMock = severalTracksMock.tracks[i]; + checkMatchingTrackAttributes(trackResponse, trackMock); + } + }); + }); +}); From b48fcb9ddf483249f86e257ed6c6fabb877f5bc1 Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Tue, 20 Nov 2018 22:13:22 -0300 Subject: [PATCH 25/27] Rename test files to match files under test --- test/{album.test.ts => albums.test.ts} | 0 test/{artist.test.ts => artists.test.ts} | 0 test/{track.test.ts => tracks.test.ts} | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename test/{album.test.ts => albums.test.ts} (100%) rename test/{artist.test.ts => artists.test.ts} (100%) rename test/{track.test.ts => tracks.test.ts} (100%) diff --git a/test/album.test.ts b/test/albums.test.ts similarity index 100% rename from test/album.test.ts rename to test/albums.test.ts diff --git a/test/artist.test.ts b/test/artists.test.ts similarity index 100% rename from test/artist.test.ts rename to test/artists.test.ts diff --git a/test/track.test.ts b/test/tracks.test.ts similarity index 100% rename from test/track.test.ts rename to test/tracks.test.ts From 67c9b68aa059170de1f2f2c16242646822025d96 Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Tue, 20 Nov 2018 22:21:20 -0300 Subject: [PATCH 26/27] Separate mocks in different folders and update imports --- test/albums.test.ts | 6 +++--- test/artists.test.ts | 13 ++++++++----- test/common/matching-attributes.test.ts | 6 +++--- test/mocks/{ => albums}/album-tracks.mock.ts | 0 test/mocks/{ => albums}/album.mock.ts | 0 test/mocks/{ => albums}/several-albums.mock.ts | 0 test/mocks/{ => artists}/artist-albums.mock.ts | 0 .../{ => artists}/artist-related-artists.mock.ts | 0 test/mocks/{ => artists}/artist-top-tracks.mock.ts | 0 test/mocks/{ => artists}/artist.mock.ts | 0 test/mocks/{ => artists}/several-artists.mock.ts | 0 .../mocks/{ => personalization}/top-artists.mock.ts | 0 test/mocks/{ => personalization}/top-tracks.mock.ts | 0 test/mocks/{ => player}/currently-playing.mock.ts | 0 .../{ => player}/recently-played-tracks.mock.ts | 0 test/mocks/{ => search}/search-albums.mock.ts | 0 test/mocks/{ => search}/search-artists.mock.ts | 0 test/mocks/{ => search}/search-playlists.mock.ts | 0 test/mocks/{ => search}/search-tracks.mock.ts | 0 test/mocks/{ => tracks}/several-tracks.mock.ts | 0 test/mocks/{ => tracks}/track.mock.ts | 0 test/personalization.test.ts | 4 ++-- test/player.test.ts | 4 ++-- test/search.test.ts | 8 ++++---- test/tracks.test.ts | 4 ++-- 25 files changed, 24 insertions(+), 21 deletions(-) rename test/mocks/{ => albums}/album-tracks.mock.ts (100%) rename test/mocks/{ => albums}/album.mock.ts (100%) rename test/mocks/{ => albums}/several-albums.mock.ts (100%) rename test/mocks/{ => artists}/artist-albums.mock.ts (100%) rename test/mocks/{ => artists}/artist-related-artists.mock.ts (100%) rename test/mocks/{ => artists}/artist-top-tracks.mock.ts (100%) rename test/mocks/{ => artists}/artist.mock.ts (100%) rename test/mocks/{ => artists}/several-artists.mock.ts (100%) rename test/mocks/{ => personalization}/top-artists.mock.ts (100%) rename test/mocks/{ => personalization}/top-tracks.mock.ts (100%) rename test/mocks/{ => player}/currently-playing.mock.ts (100%) rename test/mocks/{ => player}/recently-played-tracks.mock.ts (100%) rename test/mocks/{ => search}/search-albums.mock.ts (100%) rename test/mocks/{ => search}/search-artists.mock.ts (100%) rename test/mocks/{ => search}/search-playlists.mock.ts (100%) rename test/mocks/{ => search}/search-tracks.mock.ts (100%) rename test/mocks/{ => tracks}/several-tracks.mock.ts (100%) rename test/mocks/{ => tracks}/track.mock.ts (100%) diff --git a/test/albums.test.ts b/test/albums.test.ts index c724036c..b9755c43 100644 --- a/test/albums.test.ts +++ b/test/albums.test.ts @@ -1,9 +1,9 @@ import { expect } from 'chai'; import nock from 'nock'; -import { albumMock, AlbumMock } from './mocks/album.mock'; -import { severalAlbumsMock } from './mocks/several-albums.mock'; -import { albumTracksMock } from './mocks/album-tracks.mock'; +import { albumMock, AlbumMock } from './mocks/albums/album.mock'; +import { severalAlbumsMock } from './mocks/albums/several-albums.mock'; +import { albumTracksMock } from './mocks/albums/album-tracks.mock'; import { checkMatchingAlbumAttributes, checkMatchingPagingObjectAttributes, diff --git a/test/artists.test.ts b/test/artists.test.ts index 84ca58ca..dd0351e1 100644 --- a/test/artists.test.ts +++ b/test/artists.test.ts @@ -1,10 +1,13 @@ import nock from 'nock'; -import { artistMock, ArtistMock } from './mocks/artist.mock'; -import { severalArtistsMock } from './mocks/several-artists.mock'; -import { artistAlbumsMock } from './mocks/artist-albums.mock'; -import { artistRelatedArtistsMock } from './mocks/artist-related-artists.mock'; -import { artistTopTracksMock, TrackMock } from './mocks/artist-top-tracks.mock'; +import { artistMock, ArtistMock } from './mocks/artists/artist.mock'; +import { severalArtistsMock } from './mocks/artists/several-artists.mock'; +import { artistAlbumsMock } from './mocks/artists/artist-albums.mock'; +import { artistRelatedArtistsMock } from './mocks/artists/artist-related-artists.mock'; +import { + artistTopTracksMock, + TrackMock, +} from './mocks/artists/artist-top-tracks.mock'; import { checkMatchingArtistAttributes, checkMatchingPagingObjectAttributes, diff --git a/test/common/matching-attributes.test.ts b/test/common/matching-attributes.test.ts index eed548e4..c2bcad46 100644 --- a/test/common/matching-attributes.test.ts +++ b/test/common/matching-attributes.test.ts @@ -9,9 +9,9 @@ import CurrentlyPlaying from '../../src/lib/models/player/currently-playing'; import Context from '../../src/lib/models/player/context'; import CursorBasedPage from '../../src/lib/models/paging/cursor-based-page'; -import { AlbumMock } from '../mocks/album.mock'; -import { ArtistMock } from '../mocks/artist.mock'; -import { TrackMock } from './../mocks/artist-top-tracks.mock'; +import { AlbumMock } from '../mocks/albums/album.mock'; +import { ArtistMock } from '../mocks/artists/artist.mock'; +import { TrackMock } from '../mocks/artists/artist-top-tracks.mock'; export const checkMatchingAlbumAttributes = ( response: Album, diff --git a/test/mocks/album-tracks.mock.ts b/test/mocks/albums/album-tracks.mock.ts similarity index 100% rename from test/mocks/album-tracks.mock.ts rename to test/mocks/albums/album-tracks.mock.ts diff --git a/test/mocks/album.mock.ts b/test/mocks/albums/album.mock.ts similarity index 100% rename from test/mocks/album.mock.ts rename to test/mocks/albums/album.mock.ts diff --git a/test/mocks/several-albums.mock.ts b/test/mocks/albums/several-albums.mock.ts similarity index 100% rename from test/mocks/several-albums.mock.ts rename to test/mocks/albums/several-albums.mock.ts diff --git a/test/mocks/artist-albums.mock.ts b/test/mocks/artists/artist-albums.mock.ts similarity index 100% rename from test/mocks/artist-albums.mock.ts rename to test/mocks/artists/artist-albums.mock.ts diff --git a/test/mocks/artist-related-artists.mock.ts b/test/mocks/artists/artist-related-artists.mock.ts similarity index 100% rename from test/mocks/artist-related-artists.mock.ts rename to test/mocks/artists/artist-related-artists.mock.ts diff --git a/test/mocks/artist-top-tracks.mock.ts b/test/mocks/artists/artist-top-tracks.mock.ts similarity index 100% rename from test/mocks/artist-top-tracks.mock.ts rename to test/mocks/artists/artist-top-tracks.mock.ts diff --git a/test/mocks/artist.mock.ts b/test/mocks/artists/artist.mock.ts similarity index 100% rename from test/mocks/artist.mock.ts rename to test/mocks/artists/artist.mock.ts diff --git a/test/mocks/several-artists.mock.ts b/test/mocks/artists/several-artists.mock.ts similarity index 100% rename from test/mocks/several-artists.mock.ts rename to test/mocks/artists/several-artists.mock.ts diff --git a/test/mocks/top-artists.mock.ts b/test/mocks/personalization/top-artists.mock.ts similarity index 100% rename from test/mocks/top-artists.mock.ts rename to test/mocks/personalization/top-artists.mock.ts diff --git a/test/mocks/top-tracks.mock.ts b/test/mocks/personalization/top-tracks.mock.ts similarity index 100% rename from test/mocks/top-tracks.mock.ts rename to test/mocks/personalization/top-tracks.mock.ts diff --git a/test/mocks/currently-playing.mock.ts b/test/mocks/player/currently-playing.mock.ts similarity index 100% rename from test/mocks/currently-playing.mock.ts rename to test/mocks/player/currently-playing.mock.ts diff --git a/test/mocks/recently-played-tracks.mock.ts b/test/mocks/player/recently-played-tracks.mock.ts similarity index 100% rename from test/mocks/recently-played-tracks.mock.ts rename to test/mocks/player/recently-played-tracks.mock.ts diff --git a/test/mocks/search-albums.mock.ts b/test/mocks/search/search-albums.mock.ts similarity index 100% rename from test/mocks/search-albums.mock.ts rename to test/mocks/search/search-albums.mock.ts diff --git a/test/mocks/search-artists.mock.ts b/test/mocks/search/search-artists.mock.ts similarity index 100% rename from test/mocks/search-artists.mock.ts rename to test/mocks/search/search-artists.mock.ts diff --git a/test/mocks/search-playlists.mock.ts b/test/mocks/search/search-playlists.mock.ts similarity index 100% rename from test/mocks/search-playlists.mock.ts rename to test/mocks/search/search-playlists.mock.ts diff --git a/test/mocks/search-tracks.mock.ts b/test/mocks/search/search-tracks.mock.ts similarity index 100% rename from test/mocks/search-tracks.mock.ts rename to test/mocks/search/search-tracks.mock.ts diff --git a/test/mocks/several-tracks.mock.ts b/test/mocks/tracks/several-tracks.mock.ts similarity index 100% rename from test/mocks/several-tracks.mock.ts rename to test/mocks/tracks/several-tracks.mock.ts diff --git a/test/mocks/track.mock.ts b/test/mocks/tracks/track.mock.ts similarity index 100% rename from test/mocks/track.mock.ts rename to test/mocks/tracks/track.mock.ts diff --git a/test/personalization.test.ts b/test/personalization.test.ts index 2d9a37b1..9b77d7f1 100644 --- a/test/personalization.test.ts +++ b/test/personalization.test.ts @@ -1,7 +1,7 @@ import nock from 'nock'; -import { topArtistsMock } from './mocks/top-artists.mock'; -import { topTracksMock } from './mocks/top-tracks.mock'; +import { topArtistsMock } from './mocks/personalization/top-artists.mock'; +import { topTracksMock } from './mocks/personalization/top-tracks.mock'; import { checkMatchingPagingObjectAttributes } from './common/matching-attributes.test'; import { diff --git a/test/player.test.ts b/test/player.test.ts index 0bb636e5..07a532bf 100644 --- a/test/player.test.ts +++ b/test/player.test.ts @@ -1,7 +1,7 @@ import nock from 'nock'; -import { currentlyPlayingMock } from './mocks/currently-playing.mock'; -import { recentlyPlayedTracksMock } from './mocks/recently-played-tracks.mock'; +import { currentlyPlayingMock } from './mocks/player/currently-playing.mock'; +import { recentlyPlayedTracksMock } from './mocks/player/recently-played-tracks.mock'; import { checkMatchingCurrentlyPlayingAttributes, checkMatchingCursorBasedPageAttributes, diff --git a/test/search.test.ts b/test/search.test.ts index bb76d1b0..ba8b6198 100644 --- a/test/search.test.ts +++ b/test/search.test.ts @@ -1,8 +1,9 @@ -import { searchTracksMock } from './mocks/search-tracks.mock'; -import { searchPlaylistsMock } from './mocks/search-playlists.mock'; import nock from 'nock'; -import { searchAlbumsMock } from './mocks/search-albums.mock'; +import { searchAlbumsMock } from './mocks/search/search-albums.mock'; +import { searchArtistsMock } from './mocks/search/search-artists.mock'; +import { searchPlaylistsMock } from './mocks/search/search-playlists.mock'; +import { searchTracksMock } from './mocks/search/search-tracks.mock'; import { checkMatchingPagingObjectAttributes } from './common/matching-attributes.test'; import { @@ -12,7 +13,6 @@ import { searchPlaylists, searchTracks, } from '../src/lib'; -import { searchArtistsMock } from './mocks/search-artists.mock'; describe('Search requests', () => { beforeEach(() => { diff --git a/test/tracks.test.ts b/test/tracks.test.ts index 0ff0f3b0..f57a8d37 100644 --- a/test/tracks.test.ts +++ b/test/tracks.test.ts @@ -1,7 +1,7 @@ import nock from 'nock'; -import { trackMock } from './mocks/track.mock'; -import { severalTracksMock } from './mocks/several-tracks.mock'; +import { trackMock } from './mocks/tracks/track.mock'; +import { severalTracksMock } from './mocks/tracks/several-tracks.mock'; import { checkMatchingTrackAttributes } from './common/matching-attributes.test'; import { init, getTrack, getSeveralTracks } from '../src/lib'; From 29f5563e7b14188b24a95999c39c4232c164c2c1 Mon Sep 17 00:00:00 2001 From: JRobsonJr <jrobsonjr16@gmail.com> Date: Tue, 20 Nov 2018 22:46:20 -0300 Subject: [PATCH 27/27] Update version in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f233a023..78afa8dc 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "Robson Junior <jrobsonjr16@gmail.com> (https://github.com/JRobsonJr)" ], "license": "MIT", - "version": "0.1.3", + "version": "0.2.0", "dependencies": { "axios": "^0.18.0" },