79 lines
2.4 KiB
TypeScript
79 lines
2.4 KiB
TypeScript
import { createApp, h } from 'vue'
|
|
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
import PlaylistSaved from '../src/components/PlaylistSaved.vue'
|
|
import { useApi } from '../src/composables/useApi'
|
|
import { parseSpotifyUrl } from '../src/lib/spotifyUrl'
|
|
import { parseStreamEvent } from '../src/lib/streamParser'
|
|
|
|
afterEach(() => vi.unstubAllGlobals())
|
|
|
|
describe('parseSpotifyUrl', () => {
|
|
it.each([
|
|
['https://open.spotify.com/track/123', 'https://open.spotify.com/track/123'],
|
|
[
|
|
'https://open.spotify.com/playlist/123?si=abc',
|
|
'https://open.spotify.com/playlist/123?si=abc',
|
|
],
|
|
])('accepts %s', (value, expected) => {
|
|
expect(parseSpotifyUrl(value)).toBe(expected)
|
|
})
|
|
|
|
it.each([
|
|
'http://open.spotify.com/track/123',
|
|
'https://embed.spotify.com/track/123',
|
|
'https://open.spotify.com:8443/track/123',
|
|
'https://open.spotify.com.evil.example/track/123',
|
|
'https://user@open.spotify.com/track/123',
|
|
'javascript:alert(1)',
|
|
'/track/123',
|
|
'not a url',
|
|
])('rejects %s', (value) => {
|
|
expect(parseSpotifyUrl(value)).toBeNull()
|
|
})
|
|
|
|
it('removes an unsafe track link at the event parsing boundary', () => {
|
|
const event = parseStreamEvent({
|
|
type: 'track',
|
|
rank: 1,
|
|
track: {
|
|
id: 'track-1',
|
|
uri: 'spotify:track:1',
|
|
title: 'Track',
|
|
artists: ['Artist'],
|
|
album_name: 'Album',
|
|
album_art_url: null,
|
|
external_url: 'https://evil.example/track/1',
|
|
},
|
|
justification: 'It fits.',
|
|
})
|
|
|
|
expect(event).toMatchObject({ type: 'track', track: { external_url: null } })
|
|
})
|
|
|
|
it('keeps playlist confirmation but removes an unsafe API link', async () => {
|
|
vi.stubGlobal(
|
|
'fetch',
|
|
vi.fn(
|
|
async () =>
|
|
new Response(JSON.stringify({ url: 'https://evil.example/playlist/1' }), {
|
|
headers: { 'Content-Type': 'application/json' },
|
|
}),
|
|
),
|
|
)
|
|
const { createPlaylist } = useApi()
|
|
const result = await createPlaylist({
|
|
schema_version: 1,
|
|
name: 'Playlist',
|
|
track_uris: ['spotify:track:1'],
|
|
})
|
|
const root = document.createElement('div')
|
|
const app = createApp({
|
|
render: () => h(PlaylistSaved, { name: 'Playlist', url: result.url, trackCount: 1 }),
|
|
})
|
|
app.mount(root)
|
|
|
|
expect(root.textContent).toContain('Saved Playlist')
|
|
expect(root.querySelector('a')).toBeNull()
|
|
app.unmount()
|
|
})
|
|
})
|