You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

124 lines
4.6 KiB

import { describe, expect, it, vi, beforeEach } from 'vitest'
import { finalizeEvent, generateSecretKey, getPublicKey, nip19 } from 'nostr-tools'
import type { Event } from 'nostr-tools'
import client from '@/services/client.service'
import { expandNostrReferencesForReadAloud } from '@/lib/read-aloud'
vi.mock('@/i18n', () => ({
default: {
t: (key: string, opts?: { name?: string }) => {
const table: Record<string, string> = {
'Read aloud unknown author': 'Unknown author',
'Read aloud embedded note unavailable': 'Quoted note not loaded.',
'Read aloud nostr profile unavailable': 'Nostr profile reference.',
'Read aloud relay reference': 'Nostr relay reference.',
'Read aloud nostr reference unavailable': 'Nostr reference.'
}
if (key === 'Read aloud quoted from') {
return `Quoted from ${opts?.name ?? ''}.`
}
return table[key] ?? key
}
},
normalizeToSupportedAppLanguage: (c: string) => c as 'en',
LocalizedLanguageNames: {} as Record<'en', string>
}))
vi.mock('@/services/client.service', () => ({
default: {
peekSessionCachedEvent: vi.fn(),
eventService: {
getSessionMetadataForPubkey: vi.fn()
}
}
}))
const peek = vi.mocked(client.peekSessionCachedEvent)
const getMeta = vi.mocked(client.eventService.getSessionMetadataForPubkey)
describe('expandNostrReferencesForReadAloud', () => {
beforeEach(() => {
peek.mockReset()
getMeta.mockReset()
})
it('leaves plain text unchanged', () => {
expect(expandNostrReferencesForReadAloud('hello world')).toBe('hello world')
})
it('replaces nostr:note with placeholder when event is not cached', () => {
const sk = generateSecretKey()
const ev = finalizeEvent({ kind: 1, content: 'x', tags: [], created_at: 1 }, sk)
const note = nip19.noteEncode(ev.id)
peek.mockReturnValue(undefined)
expect(expandNostrReferencesForReadAloud(`See nostr:${note} please`)).toBe('See Quoted note not loaded. please')
})
it('replaces nostr:note with quoted-from line and body when cached', () => {
const sk = generateSecretKey()
const pk = getPublicKey(sk)
const inner = finalizeEvent({ kind: 1, content: 'Inner **bold**', tags: [], created_at: 2 }, sk)
const note = nip19.noteEncode(inner.id)
peek.mockImplementation((id: string) => {
if (id === note || id === inner.id) return inner as Event
return undefined
})
const profile = finalizeEvent(
{
kind: 0,
content: JSON.stringify({ display_name: 'Pat' }),
tags: [],
created_at: 1
},
sk
) as Event
getMeta.mockImplementation((hex: string) => (hex.toLowerCase() === pk.toLowerCase() ? profile : undefined))
const out = expandNostrReferencesForReadAloud(`Quote: nostr:${note} end`)
expect(out).toContain('Quoted from Pat.')
expect(out).toContain('Inner bold')
expect(out).not.toContain('nostr:')
})
it('uses unknown author when embedded note is cached but profile is not', () => {
const sk = generateSecretKey()
const inner = finalizeEvent({ kind: 1, content: 'Hi', tags: [], created_at: 2 }, sk)
const note = nip19.noteEncode(inner.id)
peek.mockImplementation((id: string) => (id === note || id === inner.id ? (inner as Event) : undefined))
getMeta.mockReturnValue(undefined)
const out = expandNostrReferencesForReadAloud(`nostr:${note}`)
expect(out).toMatch(/^Quoted from Unknown author\. Hi$/)
})
it('replaces nostr:npub with display name when kind 0 is cached', () => {
const sk = generateSecretKey()
const pk = getPublicKey(sk)
const profile = finalizeEvent(
{
kind: 0,
content: JSON.stringify({ name: 'n_name' }),
tags: [],
created_at: 1
},
sk
) as Event
const npub = nip19.npubEncode(pk)
getMeta.mockImplementation((hex: string) => (hex.toLowerCase() === pk.toLowerCase() ? profile : undefined))
expect(expandNostrReferencesForReadAloud(`Hey nostr:${npub}!`)).toBe('Hey n_name!')
})
it('replaces nostr:npub with placeholder when profile is not cached', () => {
const sk = generateSecretKey()
const pk = getPublicKey(sk)
const npub = nip19.npubEncode(pk)
getMeta.mockReturnValue(undefined)
expect(expandNostrReferencesForReadAloud(`x nostr:${npub} y`)).toBe('x Nostr profile reference. y')
})
it('does not match bare npub inside nostr:npub… (no double replacement)', () => {
const sk = generateSecretKey()
const pk = getPublicKey(sk)
const npub = nip19.npubEncode(pk)
getMeta.mockReturnValue(undefined)
expect(expandNostrReferencesForReadAloud(`nostr:${npub}`)).toBe('Nostr profile reference.')
})
})