fix(frontend): harden the streaming ui and extract copy and constants
This commit is contained in:
parent
036ef3cc6f
commit
a3af8f45cc
36 changed files with 1662 additions and 226 deletions
79
frontend/tests/spotifyUrl.test.ts
Normal file
79
frontend/tests/spotifyUrl.test.ts
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
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()
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue