import { describe, it, expect } from 'vitest'
import { createRoot } from '../lib/vdom.ts'
import { invariant } from '../lib/invariant.ts'
import { css } from '../index.ts'
describe('vnode rendering', () => {
describe('special attributes', () => {
it.todo('className')
it.todo('htmlFor')
it.todo('acceptCharset')
it.todo('httpEquiv')
it.todo('xlinkHref')
it.todo('xmlLang')
it.todo('xmlSpace')
it.todo('data-*')
it.todo('aria-*')
})
describe('special props', () => {
it.todo('style')
it.todo('value')
it.todo('defaultValue')
it.todo('checked')
it.todo('defaultChecked')
it.todo('disabled')
})
describe('framework props', () => {
it.todo('does not render key')
it.todo('does not render on')
it.todo('does not render mix')
it.todo('does not render children')
it.todo('does not render tabIndex')
it.todo('does not render acceptCharset')
})
describe('innerHTML prop', () => {
it('sets innerHTML on element', () => {
let container = document.createElement('div')
let root = createRoot(container)
root.render(
)
expect(container.innerHTML).toBe('Hello
')
})
it('ignores children when innerHTML is set', () => {
let container = document.createElement('div')
let root = createRoot(container)
root.render(
,
)
expect(container.innerHTML).toBe('From innerHTML
')
})
it('updates innerHTML on re-render', () => {
let container = document.createElement('div')
let root = createRoot(container)
root.render()
expect(container.innerHTML).toBe('First
')
let div = container.querySelector('div')
invariant(div)
root.render()
expect(container.innerHTML).toBe('Second
')
expect(container.querySelector('div')).toBe(div)
})
it('clears innerHTML when removed', () => {
let container = document.createElement('div')
let root = createRoot(container)
root.render()
expect(container.innerHTML).toBe('Hello
')
root.render()
expect(container.innerHTML).toBe('')
})
it('switches from innerHTML to children', () => {
let container = document.createElement('div')
let root = createRoot(container)
root.render()
expect(container.innerHTML).toBe('From innerHTML
')
root.render(
,
)
expect(container.innerHTML).toBe('')
})
it('switches from children to innerHTML', () => {
let container = document.createElement('div')
let root = createRoot(container)
root.render(
,
)
expect(container.innerHTML).toBe('')
root.render()
expect(container.innerHTML).toBe('From innerHTML
')
})
it('switches from text children to innerHTML without throwing', () => {
let container = document.createElement('div')
let root = createRoot(container)
let renderError: unknown
root.addEventListener('error', (event) => {
renderError = (event as ErrorEvent).error
})
root.render(From text child
)
expect(container.innerHTML).toBe('From text child
')
root.render()
expect(container.innerHTML).toBe('From innerHTML
')
expect(renderError).toBeUndefined()
})
})
describe('css mixin', () => {
it('adds className-based styles', async () => {
let container = document.createElement('div')
let root = createRoot(container)
root.render(Hello
)
let div = container.querySelector('div')
invariant(div instanceof HTMLDivElement)
expect(div.className).toMatch(/rmxc-/)
document.body.appendChild(container)
expect(getComputedStyle(div).color).toBe('rgb(255, 0, 0)')
})
it('composes with className without overriding it', async () => {
let container = document.createElement('div')
let root = createRoot(container)
root.render(
Hello
,
)
let div = container.querySelector('div')
invariant(div instanceof HTMLDivElement)
expect(div.className).toContain('custom-class')
expect(div.className).toMatch(/rmxc-/)
})
it('ignores class when composing css mixin className', async () => {
let container = document.createElement('div')
let root = createRoot(container)
root.render(
Hello
,
)
let div = container.querySelector('div')
invariant(div instanceof HTMLDivElement)
expect(div.className).toMatch(/rmxc-/)
})
it('className updates independently of css mixin output', async () => {
let container = document.createElement('div')
let root = createRoot(container)
root.render(
Hello
,
)
let div = container.querySelector('div')
invariant(div instanceof HTMLDivElement)
expect(div.className).toContain('first')
let generated = div.className.split(/\s+/).find((token) => token.startsWith('rmxc-'))
invariant(generated)
root.render(
Hello
,
)
expect(div.className).toContain('second')
expect(div.className).toContain(generated)
})
it('removes nested selector rules when they become undefined', async () => {
let container = document.createElement('div')
document.body.appendChild(container)
let root = createRoot(container)
root.render(
Test
,
)
let child = container.querySelector('span')
invariant(child)
// More-specific nested selector should win.
expect(getComputedStyle(child).color).toBe('rgb(255, 0, 0)')
root.render(
Test
,
)
// Once the more-specific selector becomes undefined, the child should fall back to the base rule.
expect(getComputedStyle(child).color).toBe('rgb(0, 0, 255)')
})
})
})