hermes-agent/apps/desktop/src/components/assistant-ui/embeds/providers/detect.test.ts
Brooklyn Nicholson 81ac562bf0 feat(desktop): inline embed detection + module primitives
Pure, synchronous URL→descriptor matchers for YouTube, Vimeo, Instagram,
Pinterest, TikTok, X, Spotify, Google Maps and OpenStreetMap, plus the shared
embed primitives (error boundary, fail card, escape-html, dark-mode hook,
sizing token). Declares the mermaid + dompurify deps used by the fenced
renderers.
2026-06-26 03:21:59 -05:00

133 lines
4.9 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import type { FrameEmbed, TweetEmbed } from './types'
import { detectEmbed, isEmbeddableUrl } from './index'
function frame(url: string): FrameEmbed {
const descriptor = detectEmbed(url)
if (!descriptor || descriptor.renderer !== 'frame') {
throw new Error(`expected a frame embed for ${url}`)
}
return descriptor
}
describe('detectEmbed — YouTube', () => {
it.each([
'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
'https://youtu.be/dQw4w9WgXcQ',
'https://www.youtube.com/shorts/dQw4w9WgXcQ',
'https://m.youtube.com/watch?v=dQw4w9WgXcQ',
'https://www.youtube.com/embed/dQw4w9WgXcQ',
'https://www.youtube.com/live/dQw4w9WgXcQ'
])('resolves %s to the privacy-enhanced embed of the same id', url => {
const embed = frame(url)
expect(embed.provider).toBe('youtube')
expect(embed.id).toBe('youtube:dQw4w9WgXcQ')
expect(embed.embedUrl).toContain('youtube-nocookie.com/embed/dQw4w9WgXcQ')
})
it('carries a start time from t/start through to the embed', () => {
expect(frame('https://youtu.be/dQw4w9WgXcQ?t=90').embedUrl).toContain('start=90')
expect(frame('https://youtu.be/dQw4w9WgXcQ?t=1m30s').embedUrl).toContain('start=90')
})
it('rejects ids that are not 11 chars', () => {
expect(detectEmbed('https://www.youtube.com/watch?v=short')).toBeNull()
})
})
describe('detectEmbed — other frame providers', () => {
it('resolves Vimeo numeric ids across path shapes', () => {
expect(frame('https://vimeo.com/76979871').embedUrl).toBe('https://player.vimeo.com/video/76979871')
expect(frame('https://vimeo.com/channels/staffpicks/76979871').id).toBe('vimeo:76979871')
})
it('resolves Instagram posts and reels', () => {
expect(frame('https://www.instagram.com/p/CabcDEF123/').embedUrl).toBe(
'https://www.instagram.com/p/CabcDEF123/embed'
)
expect(frame('https://www.instagram.com/reel/CabcDEF123/').embedUrl).toContain('/reel/CabcDEF123/embed')
expect(frame('https://www.instagram.com/reels/CabcDEF123/').embedUrl).toContain('/reel/CabcDEF123/embed')
})
it('resolves Pinterest pins across locale hosts', () => {
expect(frame('https://www.pinterest.com/pin/1234567890/').embedUrl).toBe(
'https://assets.pinterest.com/ext/embed.html?id=1234567890'
)
expect(frame('https://fr.pinterest.com/pin/1234567890/').provider).toBe('pinterest')
})
it('resolves TikTok videos to the official player', () => {
expect(frame('https://www.tiktok.com/@user/video/7212345678901234567').embedUrl).toBe(
'https://www.tiktok.com/player/v1/7212345678901234567'
)
})
it('resolves Spotify tracks, collections, and locale-prefixed urls', () => {
expect(frame('https://open.spotify.com/track/4cOdK2wGLETKBW3PvgPWqT').embedUrl).toBe(
'https://open.spotify.com/embed/track/4cOdK2wGLETKBW3PvgPWqT'
)
expect(frame('https://open.spotify.com/playlist/37i9dQZF1DXcBWIGoYBM5M').provider).toBe('spotify')
expect(frame('https://open.spotify.com/intl-de/album/1DFixLWuPkv3KT3TnV35m3').id).toBe(
'spotify:album:1DFixLWuPkv3KT3TnV35m3'
)
expect(detectEmbed('https://open.spotify.com/track/')).toBeNull()
})
})
describe('detectEmbed — maps', () => {
it('resolves Google Maps coordinates with zoom', () => {
const embed = frame('https://www.google.com/maps/@40.7128,-74.0060,12z')
expect(embed.provider).toBe('googlemaps')
expect(embed.embedUrl).toContain('output=embed')
expect(embed.embedUrl).toContain('q=40.7128%2C-74.006')
expect(embed.embedUrl).toContain('z=12')
})
it('resolves a Google Maps place name', () => {
expect(frame('https://www.google.com/maps/place/Eiffel+Tower/').embedUrl).toContain('q=Eiffel+Tower')
})
it('resolves OpenStreetMap fragment state to a bbox embed', () => {
const embed = frame('https://www.openstreetmap.org/#map=12/40.7128/-74.0060')
expect(embed.provider).toBe('openstreetmap')
expect(embed.embedUrl).toContain('export/embed.html')
expect(embed.embedUrl).toContain('marker=40.7128%2C-74.006')
expect(embed.embedUrl).toContain('bbox=')
})
})
describe('detectEmbed — Twitter/X', () => {
it('resolves twitter.com and x.com status urls to a tweet descriptor', () => {
for (const url of ['https://twitter.com/jack/status/20', 'https://x.com/jack/status/20']) {
const descriptor = detectEmbed(url)
expect(descriptor?.renderer).toBe('tweet')
expect((descriptor as TweetEmbed).tweetId).toBe('20')
}
})
})
describe('detectEmbed — non-matches', () => {
it.each([
'https://example.com/watch?v=dQw4w9WgXcQ',
'https://github.com/NousResearch/hermes',
'not-a-url',
'ftp://youtube.com/watch?v=dQw4w9WgXcQ',
'mailto:someone@youtube.com'
])('returns null for %s', url => {
expect(detectEmbed(url)).toBeNull()
expect(isEmbeddableUrl(url)).toBe(false)
})
it('handles empty input without throwing', () => {
expect(detectEmbed(undefined)).toBeNull()
expect(detectEmbed('')).toBeNull()
})
})