', () => {
const svg = wrap('')
const result = sanitizeSvg(svg)
expect(result).toContain('href="#r"')
})
it('preserves data: font URL in ' +
''
)
const result = sanitizeSvg(svg)
expect(result).toContain('data:font/woff2;base64,d09GMg')
})
it('preserves foreignObject with safe HTML content', () => {
const svg = wrap(
'' +
'Hello world
' +
'
text' +
'
' +
'
codeitalicbold' +
'
'
)
const result = sanitizeSvg(svg)
expect(result).toContain('foreignObject')
expect(result).toContain('', () => {
const svg = wrap('
link')
const result = sanitizeSvg(svg)
expect(result).toContain('https://example.com')
})
it('preserves https link in
inside foreignObject', () => {
const svg = wrap(
'' +
'' +
''
)
const result = sanitizeSvg(svg)
expect(result).toContain('https://example.com')
})
it('preserves safe inline styles', () => {
const svg = wrap('')
const result = sanitizeSvg(svg)
expect(result).toContain('fill: red')
expect(result).toContain('stroke: blue')
})
it('preserves transform, viewBox, preserveAspectRatio', () => {
const svg =
''
const result = sanitizeSvg(svg)
expect(result).toContain('viewBox')
expect(result).toContain('transform')
})
it('preserves data-* and aria-* attributes', () => {
const svg = wrap('')
const result = sanitizeSvg(svg)
expect(result).toContain('data-testid')
expect(result).toContain('aria-label')
})
it('preserves width/height on svg element', () => {
const svg =
''
const result = sanitizeSvg(svg)
expect(result).toContain('width="200"')
expect(result).toContain('height="100"')
})
it('preserves animation elements without event handlers', () => {
const svg = wrap(
'' +
'' +
''
)
const result = sanitizeSvg(svg)
expect(result).toContain('', () => {
const svg = wrap(
'' +
'' +
''
)
const result = sanitizeSvg(svg)
expect(result).toContain('url(#grad)')
})
it('preserves safe targeting non-URI attributes', () => {
const svg = wrap(
'' +
'' +
'' +
''
)
const result = sanitizeSvg(svg)
expect(result).toContain('attributeName="opacity"')
expect(result).toContain('attributeName="fill"')
})
it('preserves data: image in CSS url()', () => {
const svg = wrap(
'' +
''
)
const result = sanitizeSvg(svg)
expect(result).toContain('data:image/png;base64,iVBOR')
})
})
describe('round-trip — tldraw SVG export survives sanitization', () => {
vi.useRealTimers()
it('preserves tldraw-exported SVG with text shapes', async () => {
const editor = new TestEditor()
const geoId = createShapeId('geo')
editor.createShapes([
{
id: geoId,
type: 'geo',
x: 0,
y: 0,
props: {
w: 200,
h: 100,
richText: toRichText('Hello world'),
},
},
])
editor.selectAll()
const exported = await editor.getSvgString(editor.getSelectedShapeIds())
expect(exported).toBeTruthy()
const original = exported!.svg
const sanitized = sanitizeSvg(original)
// Must not be empty
expect(sanitized).not.toBe('')
// Must still contain the SVG root
expect(sanitized).toContain('