mirror of
https://github.com/supabase/supabase.git
synced 2026-06-17 21:23:59 +08:00
Fixes a rendering bug where template URLs like `https://<project-ref>.supabase.co/...` were displayed incorrectly in Assistant responses. The markdown parser was treating `<project-ref>` as a tag and silently stripping it. - Adds `wrapPlaceholderUrls()` preprocessing step that auto-wraps bare URLs containing `<kebab-case>` placeholders in backticks before passing to the markdown renderer - Strengthens the prompt instruction to explicitly say "always format template URLs as inline code using backticks" Sample prompt: ``` What are the OAuth2 endpoints for Supabase projects? List the authorization, token, and JWKS URLs. ``` | Before | After | |--------|--------| | <img width="1314" height="841" alt="CleanShot 2026-03-06 at 15 32 13@2x" src="https://github.com/user-attachments/assets/55eaa0c6-39fb-48e0-91a0-31903021a4c9" /> | <img width="1421" height="827" alt="CleanShot 2026-03-06 at 15 31 37@2x" src="https://github.com/user-attachments/assets/5335c71a-19c7-44a0-b027-6f2efd76eb8c" /> | Closes AI-470
85 lines
3.0 KiB
TypeScript
85 lines
3.0 KiB
TypeScript
import { render } from '@testing-library/react'
|
|
import { describe, expect, test } from 'vitest'
|
|
|
|
import { wrapPlaceholderUrls } from './Message.utils'
|
|
import { OrderedList } from './MessageMarkdown'
|
|
|
|
describe('wrapPlaceholderUrls', () => {
|
|
test('wraps a bare URL containing a placeholder in backticks', () => {
|
|
expect(wrapPlaceholderUrls('https://<project-ref>.supabase.co/auth/v1/oauth/authorize')).toBe(
|
|
'`https://<project-ref>.supabase.co/auth/v1/oauth/authorize`'
|
|
)
|
|
})
|
|
|
|
test('leaves an already-wrapped URL unchanged', () => {
|
|
const input = '`https://<project-ref>.supabase.co`'
|
|
expect(wrapPlaceholderUrls(input)).toBe(input)
|
|
})
|
|
|
|
test('leaves URLs without placeholders unchanged', () => {
|
|
expect(wrapPlaceholderUrls('https://supabase.com/dashboard')).toBe(
|
|
'https://supabase.com/dashboard'
|
|
)
|
|
})
|
|
|
|
test('wraps bare URL but preserves surrounding text', () => {
|
|
expect(
|
|
wrapPlaceholderUrls(
|
|
'Authorization endpoint: https://<project-ref>.supabase.co/auth/v1/oauth/authorize'
|
|
)
|
|
).toBe('Authorization endpoint: `https://<project-ref>.supabase.co/auth/v1/oauth/authorize`')
|
|
})
|
|
|
|
test('skips URLs inside markdown link destinations', () => {
|
|
const input = '[OAuth docs](https://<project-ref>.supabase.co/auth/v1/oauth/authorize)'
|
|
expect(wrapPlaceholderUrls(input)).toBe(input)
|
|
})
|
|
|
|
test('wraps placeholder URL used as markdown link text', () => {
|
|
expect(
|
|
wrapPlaceholderUrls('[https://<project-ref>.supabase.co](https://supabase.com/dashboard)')
|
|
).toBe('[`https://<project-ref>.supabase.co`](https://supabase.com/dashboard)')
|
|
})
|
|
|
|
test('wraps bare URL but skips linked URL when both appear in the same string', () => {
|
|
expect(
|
|
wrapPlaceholderUrls(
|
|
'Use [link](https://<project-ref>.supabase.co) or https://<project-ref>.supabase.co/raw'
|
|
)
|
|
).toBe(
|
|
'Use [link](https://<project-ref>.supabase.co) or `https://<project-ref>.supabase.co/raw`'
|
|
)
|
|
})
|
|
|
|
test('leaves placeholder URLs inside fenced code blocks unchanged', () => {
|
|
const input = '```\nhttps://<project-ref>.supabase.co\n```'
|
|
expect(wrapPlaceholderUrls(input)).toBe(input)
|
|
})
|
|
|
|
test('strips trailing prose punctuation before wrapping', () => {
|
|
expect(wrapPlaceholderUrls('See https://<project-ref>.supabase.co/auth, then proceed.')).toBe(
|
|
'See `https://<project-ref>.supabase.co/auth`, then proceed.'
|
|
)
|
|
})
|
|
|
|
test('wraps URLs whose angle-bracket segment has no hyphen', () => {
|
|
expect(wrapPlaceholderUrls('https://example.com/path?id=<ref>')).toBe(
|
|
'`https://example.com/path?id=<ref>`'
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('OrderedList', () => {
|
|
test('sets counter-reset based on start prop for split lists', () => {
|
|
const { container } = render(
|
|
<OrderedList start={3}>
|
|
<li>Third item</li>
|
|
</OrderedList>
|
|
)
|
|
const ol = container.querySelector('ol')
|
|
expect(ol).toBeInTheDocument()
|
|
expect(ol).toHaveAttribute('start', '3')
|
|
expect(ol).toHaveStyle({ counterReset: 'item 2' })
|
|
})
|
|
})
|