import { describe, expect, it } from 'bun:test'
import { renderJSX, sceneNodeToJSX } from '@open-pencil/core'
import { getNodeOrThrow, childIdAt } from '#tests/helpers/assert'
import { makeSceneGraph } from '#tests/helpers/scene'
describe('text props round-trip', () => {
it('lineHeight renders and exports', async () => {
const g = makeSceneGraph()
const [result] = await renderJSX(g, 'Hello')
const n = getNodeOrThrow(g, result.id)
expect(n.lineHeight).toBe(24)
const jsx = sceneNodeToJSX(n.id, g)
expect(jsx).toContain('lineHeight={24}')
})
it('letterSpacing renders and exports', async () => {
const g = makeSceneGraph()
const [result] = await renderJSX(g, 'Spaced')
const n = getNodeOrThrow(g, result.id)
expect(n.letterSpacing).toBe(2)
const jsx = sceneNodeToJSX(n.id, g)
expect(jsx).toContain('letterSpacing={2}')
})
it('textDecoration renders and exports', async () => {
const g = makeSceneGraph()
const [result] = await renderJSX(g, 'Link')
const n = getNodeOrThrow(g, result.id)
expect(n.textDecoration).toBe('UNDERLINE')
const jsx = sceneNodeToJSX(n.id, g)
expect(jsx).toContain('textDecoration="underline"')
})
it('textCase renders and exports', async () => {
const g = makeSceneGraph()
const [result] = await renderJSX(g, 'label')
const n = getNodeOrThrow(g, result.id)
expect(n.textCase).toBe('UPPER')
const jsx = sceneNodeToJSX(n.id, g)
expect(jsx).toContain('textCase="upper"')
})
it('maxLines renders with truncation', async () => {
const g = makeSceneGraph()
const [result] = await renderJSX(g, 'Long text here')
const n = getNodeOrThrow(g, result.id)
expect(n.maxLines).toBe(2)
expect(n.textTruncation).toBe('ENDING')
const jsx = sceneNodeToJSX(n.id, g)
expect(jsx).toContain('maxLines={2}')
})
it('truncate without maxLines', async () => {
const g = makeSceneGraph()
const [result] = await renderJSX(g, 'Overflow')
const n = getNodeOrThrow(g, result.id)
expect(n.textTruncation).toBe('ENDING')
const jsx = sceneNodeToJSX(n.id, g)
expect(jsx).toContain('truncate')
})
it('accepts Figma-style text aliases', async () => {
const g = makeSceneGraph()
const [result] = await renderJSX(
g,
''
)
const n = getNodeOrThrow(g, result.id)
expect(n.text).toBe('Alias text')
expect(n.fontSize).toBe(24)
expect(n.fontFamily).toBe('Inter')
expect(n.fontWeight).toBe(700)
expect(n.textAlignHorizontal).toBe('CENTER')
expect(n.textAlignVertical).toBe('CENTER')
expect(n.fills[0]?.type).toBe('SOLID')
})
it('accepts characters as a text content alias', async () => {
const g = makeSceneGraph()
const [result] = await renderJSX(g, '')
const n = getNodeOrThrow(g, result.id)
expect(n.text).toBe('Characters text')
})
it('prefers text children over text prop alias', async () => {
const g = makeSceneGraph()
const [result] = await renderJSX(g, 'Child text')
const n = getNodeOrThrow(g, result.id)
expect(n.text).toBe('Child text')
})
it('defaults omit text props', async () => {
const g = makeSceneGraph()
const [result] = await renderJSX(g, 'Plain')
const n = getNodeOrThrow(g, result.id)
expect(n.lineHeight).toBeNull()
expect(n.letterSpacing).toBe(0)
expect(n.textDecoration).toBe('NONE')
expect(n.textCase).toBe('ORIGINAL')
expect(n.maxLines).toBeNull()
const jsx = sceneNodeToJSX(n.id, g)
expect(jsx).not.toContain('lineHeight')
expect(jsx).not.toContain('letterSpacing')
expect(jsx).not.toContain('textDecoration')
expect(jsx).not.toContain('textCase')
expect(jsx).not.toContain('maxLines')
expect(jsx).not.toContain('truncate')
})
it('text w="fill" in flex="col" exports as w="fill" not w={computed}', async () => {
const g = makeSceneGraph()
const [result] = await renderJSX(
g,
`
Hello World
`
)
const card = getNodeOrThrow(g, result.id)
const title = getNodeOrThrow(g, childIdAt(card, 0))
expect(title.layoutAlignSelf).toBe('STRETCH')
expect(title.textAutoResize).toBe('HEIGHT')
const jsx = sceneNodeToJSX(title.id, g)
expect(jsx).toContain('w="fill"')
expect(jsx).not.toMatch(/w=\{/)
})
it('text grow={1} in flex="row" exports as grow not w={computed}', async () => {
const g = makeSceneGraph()
const [result] = await renderJSX(
g,
`
Label
Some value text
`
)
const row = getNodeOrThrow(g, result.id)
const value = getNodeOrThrow(g, childIdAt(row, 1))
expect(value.layoutGrow).toBe(1)
const jsx = sceneNodeToJSX(value.id, g)
expect(jsx).toContain('grow={1}')
expect(jsx).not.toMatch(/\bw=\{\d+\}/)
})
})