discovery-by-llm/frontend/tests/spotifyUrl.test.ts
Justin Visser a3af8f45cc
Some checks are pending
ci / backend (push) Waiting to run
ci / frontend (push) Waiting to run
fix(frontend): harden the streaming ui and extract copy and constants
2026-08-10 15:42:18 +02:00

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()
})
})