diff --git a/package.json b/package.json index f233a023..78afa8dc 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "Robson Junior (https://github.com/JRobsonJr)" ], "license": "MIT", - "version": "0.1.3", + "version": "0.2.0", "dependencies": { "axios": "^0.18.0" }, diff --git a/src/lib/index.ts b/src/lib/index.ts index 32a86a9c..6d5c9944 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,6 +1,9 @@ +export * from './albums'; +export * from './artists'; export * from './driver'; -export * from './tracks'; export * from './follow'; -export * from './artists'; -export * from './albums'; +export * from './personalization'; export * from './playlists'; +export * from './player'; +export * from './search'; +export * from './tracks'; 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 { + 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(response.data, this.t); + } +} + +export default CursorBasedPage; diff --git a/src/lib/models/paging/page.ts b/src/lib/models/paging/page.ts index 4ce5709c..966e9d2f 100644 --- a/src/lib/models/paging/page.ts +++ b/src/lib/models/paging/page.ts @@ -17,20 +17,42 @@ class Page { 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; + let unwrappedJson = json; + if (wrapper) unwrappedJson = unwrappedJson[wrapper]; this.t = t; - this.href = json.href.split('?')[0]; - 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('&'); + 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; + instance.defaults.baseURL = this.href.split('?')[0]; return instance; } @@ -44,22 +66,25 @@ class Page { 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(response.data, this.t); + return new Page(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 = { limit, offset: 0 }; + if (this.offset < this.limit && !includeRepeated) limit = this.offset; + const offset = Math.max(this.offset - this.limit, 0); + const params = { ...this.queryParams, limit, offset }; const response = await this.getAxiosPageInstance().get('/', { params, }); - return new Page(response.data, this.t); + return new Page(response.data, this.t, this.wrapper); } } 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; 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; 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; diff --git a/src/lib/models/user/user-public.ts b/src/lib/models/user/user-public.ts index a740da34..321bfc25 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,14 @@ 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; } 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(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(response.data, Track); +}; diff --git a/src/lib/player.ts b/src/lib/player.ts new file mode 100644 index 00000000..b4474a34 --- /dev/null +++ b/src/lib/player.ts @@ -0,0 +1,30 @@ +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; + 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(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); +}; 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); }; diff --git a/src/lib/search.ts b/src/lib/search.ts new file mode 100644 index 00000000..15479353 --- /dev/null +++ b/src/lib/search.ts @@ -0,0 +1,61 @@ +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; + type: string; + market?: string; + limit?: number; + offset?: number; +}) => { + 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( + searchResults.data, + AlbumSimplified, + 'albums' + ); +}; + +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(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( + 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(searchResults.data, Track, 'tracks'); +}; diff --git a/src/lib/tracks.ts b/src/lib/tracks.ts index 157fb759..f16681ed 100644 --- a/src/lib/tracks.ts +++ b/src/lib/tracks.ts @@ -1,7 +1,13 @@ 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[]) => { +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/'; @@ -9,12 +15,39 @@ 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 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); }; + +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) + ); +}; diff --git a/test/album.test.ts b/test/albums.test.ts similarity index 93% rename from test/album.test.ts rename to test/albums.test.ts index c724036c..b9755c43 100644 --- a/test/album.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/artist.test.ts b/test/artists.test.ts similarity index 91% rename from test/artist.test.ts rename to test/artists.test.ts index 84ca58ca..dd0351e1 100644 --- a/test/artist.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 3242f2fe..c2bcad46 100644 --- a/test/common/matching-attributes.test.ts +++ b/test/common/matching-attributes.test.ts @@ -4,9 +4,14 @@ 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 { AlbumMock } from '../mocks/album.mock'; -import { ArtistMock } from '../mocks/artist.mock'; -import { TrackMock } from './../mocks/artist-top-tracks.mock'; +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/albums/album.mock'; +import { ArtistMock } from '../mocks/artists/artist.mock'; +import { TrackMock } from '../mocks/artists/artist-top-tracks.mock'; export const checkMatchingAlbumAttributes = ( response: Album, @@ -94,12 +99,49 @@ export const checkMatchingTrackAttributes = ( }; export const checkMatchingPagingObjectAttributes = ( - response: any, + response: Page, 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); 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, + 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/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/personalization/top-artists.mock.ts b/test/mocks/personalization/top-artists.mock.ts new file mode 100644 index 00000000..e1d46fea --- /dev/null +++ b/test/mocks/personalization/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/personalization/top-tracks.mock.ts b/test/mocks/personalization/top-tracks.mock.ts new file mode 100644 index 00000000..9d1f8d24 --- /dev/null +++ b/test/mocks/personalization/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/mocks/player/currently-playing.mock.ts b/test/mocks/player/currently-playing.mock.ts new file mode 100644 index 00000000..eb6c4400 --- /dev/null +++ b/test/mocks/player/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/player/recently-played-tracks.mock.ts b/test/mocks/player/recently-played-tracks.mock.ts new file mode 100644 index 00000000..541f88dd --- /dev/null +++ b/test/mocks/player/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/mocks/search/search-albums.mock.ts b/test/mocks/search/search-albums.mock.ts new file mode 100644 index 00000000..ad13841c --- /dev/null +++ b/test/mocks/search/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/search-artists.mock.ts b/test/mocks/search/search-artists.mock.ts new file mode 100644 index 00000000..c156c75a --- /dev/null +++ b/test/mocks/search/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/search-playlists.mock.ts b/test/mocks/search/search-playlists.mock.ts new file mode 100644 index 00000000..0cbc938d --- /dev/null +++ b/test/mocks/search/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/search-tracks.mock.ts b/test/mocks/search/search-tracks.mock.ts new file mode 100644 index 00000000..56b2f6cb --- /dev/null +++ b/test/mocks/search/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/mocks/tracks/several-tracks.mock.ts b/test/mocks/tracks/several-tracks.mock.ts new file mode 100644 index 00000000..fd622990 --- /dev/null +++ b/test/mocks/tracks/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/tracks/track.mock.ts b/test/mocks/tracks/track.mock.ts new file mode 100644 index 00000000..97ce42fa --- /dev/null +++ b/test/mocks/tracks/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/personalization.test.ts b/test/personalization.test.ts new file mode 100644 index 00000000..9b77d7f1 --- /dev/null +++ b/test/personalization.test.ts @@ -0,0 +1,55 @@ +import nock from 'nock'; + +import { topArtistsMock } from './mocks/personalization/top-artists.mock'; +import { topTracksMock } from './mocks/personalization/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 + ); + }); + }); +}); diff --git a/test/player.test.ts b/test/player.test.ts new file mode 100644 index 00000000..07a532bf --- /dev/null +++ b/test/player.test.ts @@ -0,0 +1,58 @@ +import nock from 'nock'; + +import { currentlyPlayingMock } from './mocks/player/currently-playing.mock'; +import { recentlyPlayedTracksMock } from './mocks/player/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 + ); + }); + }); +}); diff --git a/test/search.test.ts b/test/search.test.ts new file mode 100644 index 00000000..ba8b6198 --- /dev/null +++ b/test/search.test.ts @@ -0,0 +1,101 @@ +import nock from 'nock'; + +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 { + init, + searchAlbums, + searchArtists, + searchPlaylists, + searchTracks, +} from '../src/lib'; + +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 + ); + }); + }); +}); diff --git a/test/tracks.test.ts b/test/tracks.test.ts new file mode 100644 index 00000000..f57a8d37 --- /dev/null +++ b/test/tracks.test.ts @@ -0,0 +1,51 @@ +import nock from 'nock'; + +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'; + +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); + } + }); + }); +});