import { describe, it, expect } from 'vitest' import { createRoot } from '../lib/vdom.ts' import { invariant } from '../lib/invariant.ts' import { on } from '../index.ts' import type { RemixNode } from '../lib/component.ts' describe('vnode rendering', () => { describe('svg', () => { it('renders SVG root and children with SVG namespace and attributes', () => { let container = document.createElement('div') let { render } = createRoot(container) render( , ) let svg = container.querySelector('svg') let path = container.querySelector('path') invariant(svg instanceof SVGSVGElement) invariant(path instanceof SVGPathElement) expect(svg.namespaceURI).toBe('http://www.w3.org/2000/svg') expect(path.namespaceURI).toBe('http://www.w3.org/2000/svg') // Attribute casing: preserve exceptions and kebab-case general SVG attrs expect(svg.getAttribute('viewBox')).toBe('0 0 24 24') expect(svg.getAttribute('class')).toBe('icon') expect(path.getAttribute('stroke-linecap')).toBe('round') expect(path.getAttribute('stroke-linejoin')).toBe('round') }) it('supports xlinkHref -> xlink:href on SVG elements', () => { let container = document.createElement('div') let { render } = createRoot(container) render( , ) let useEl = container.querySelector('use') invariant(useEl instanceof SVGUseElement) expect(useEl.getAttribute('xlink:href')).toBe('#my-id') }) it('updates and removes namespaced SVG attributes', () => { let container = document.createElement('div') let root = createRoot(container) root.render( Hi , ) let useEl = container.querySelector('#u') invariant(useEl instanceof SVGUseElement) expect(useEl.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe('#one') let textEl = container.querySelector('#t') invariant(textEl instanceof SVGTextElement) expect(textEl.getAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang')).toBe('en') root.render( Hi , ) let updatedUseEl = container.querySelector('#u') invariant(updatedUseEl instanceof SVGUseElement) expect(updatedUseEl.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe('#two') let updatedTextEl = container.querySelector('#t') invariant(updatedTextEl instanceof SVGTextElement) expect(updatedTextEl.getAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang')).toBe( 'fr', ) root.render( Hi , ) let removedUseEl = container.querySelector('#u') invariant(removedUseEl instanceof SVGUseElement) expect(removedUseEl.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe(null) expect(removedUseEl.getAttribute('xlink:href')).toBe(null) let removedTextEl = container.querySelector('#t') invariant(removedTextEl instanceof SVGTextElement) expect(removedTextEl.getAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang')).toBe( null, ) expect(removedTextEl.getAttribute('xml:lang')).toBe(null) }) it('renders HTML subtree inside foreignObject with HTML namespace', () => { let container = document.createElement('div') let { render } = createRoot(container) render(
Hello
, ) let div = container.querySelector('#x') invariant(div) expect(div instanceof HTMLDivElement).toBe(true) expect(div.namespaceURI).toBe('http://www.w3.org/1999/xhtml') }) it('updates and removes SVG attributes', () => { let container = document.createElement('div') let root = createRoot(container) root.render( , ) let path = container.querySelector('#p') invariant(path instanceof SVGPathElement) // Update value root.render( , ) let updated = container.querySelector('#p') invariant(updated instanceof SVGPathElement) expect(updated).toBe(path) expect(updated.getAttribute('stroke-linecap')).toBe('square') // Remove attribute root.render( , ) let removed = container.querySelector('#p') invariant(removed instanceof SVGPathElement) expect(removed.hasAttribute('stroke-linecap')).toBe(false) }) it('uses canonical semantics for critical SVG attributes', () => { let container = document.createElement('div') let root = createRoot(container) root.render( , ) let filter = container.querySelector('#f') invariant(filter instanceof SVGFilterElement) expect(filter.getAttribute('filterUnits')).toBe('userSpaceOnUse') expect(filter.getAttribute('filter-units')).toBe(null) expect(filter.filterUnits.baseVal).toBe(1) let blur = container.querySelector('#blur') invariant(blur instanceof SVGFEGaussianBlurElement) expect(blur.getAttribute('stdDeviation')).toBe('2.5') expect(blur.getAttribute('std-deviation')).toBe(null) let gradient = container.querySelector('#g') invariant(gradient instanceof SVGLinearGradientElement) expect(gradient.getAttribute('gradientUnits')).toBe('userSpaceOnUse') expect(gradient.getAttribute('gradient-units')).toBe(null) expect(gradient.gradientUnits.baseVal).toBe(1) let mask = container.querySelector('#m') invariant(mask instanceof SVGMaskElement) expect(mask.getAttribute('maskUnits')).toBe('userSpaceOnUse') expect(mask.getAttribute('mask-units')).toBe(null) expect(mask.maskUnits.baseVal).toBe(1) let clipPath = container.querySelector('#c') invariant(clipPath instanceof SVGClipPathElement) expect(clipPath.getAttribute('clipPathUnits')).toBe('objectBoundingBox') expect(clipPath.getAttribute('clip-path-units')).toBe(null) expect(clipPath.clipPathUnits.baseVal).toBe(2) }) it('attaches events on SVG elements', () => { let container = document.createElement('div') let root = createRoot(container) let clicked = false root.render( { clicked = true }), ]} /> , ) root.flush() let circle = container.querySelector('#c') invariant(circle instanceof SVGCircleElement) circle.dispatchEvent(new MouseEvent('click', { bubbles: true })) expect(clicked).toBe(true) }) it('propagates SVG namespace through components', () => { let container = document.createElement('div') let root = createRoot(container) function SvgGroup() { return ({ href, children }: { href: string; children?: RemixNode }) => ( {children} ) } root.render( , ) let svg = container.querySelector('svg') let group = container.querySelector('g') let path = container.querySelector('path') invariant(svg instanceof SVGSVGElement) invariant(group instanceof SVGGElement) invariant(path instanceof SVGPathElement) // All elements should have SVG namespace expect(svg.namespaceURI).toBe('http://www.w3.org/2000/svg') expect(group.namespaceURI).toBe('http://www.w3.org/2000/svg') expect(path.namespaceURI).toBe('http://www.w3.org/2000/svg') }) }) })