openpencil/tests/engine/fonts.test.ts
Danila Poyarkov 9784544f1f Clean up tools/schema, add geometry.ts, isMixed helper, fix imports, add tests
- Strip tools/schema.ts from 1831 to 65 lines (remove duplicate tool defs)
- Fix tools/index.ts to export ALL_TOOLS from registry instead of schema
- Fix renderJsx → renderJSX typo in tools/create.ts and structure.ts
- Add geometry.ts with shared rotation/coordinate math utilities
- Add isArrayMixed() helper to use-multi-props.ts, deduplicate FillSection/StrokeSection/EffectsSection
- Fix broken test imports: use @open-pencil/core instead of relative paths into packages/core/src/
- Merge duplicate import statements across all test files
- Export profiler internals and style-run helpers from core index.ts
- Add 6 new test files: color, undo, snap, vector, style-runs, text-editor (98 new tests)
- Update renderer-effects test for renderer/ directory split
2026-03-06 22:33:10 +03:00

292 lines
7.4 KiB
TypeScript

import { describe, test, expect } from 'bun:test'
import {
collectFontKeys,
isFontLoaded,
markFontLoaded,
styleToWeight,
weightToStyle,
SceneGraph,
} from '@open-pencil/core'
function pageId(graph: SceneGraph) {
return graph.getPages()[0].id
}
describe('styleToWeight', () => {
test('maps common style names', () => {
expect(styleToWeight('Regular')).toBe(400)
expect(styleToWeight('Bold')).toBe(700)
expect(styleToWeight('Light')).toBe(300)
expect(styleToWeight('Thin')).toBe(100)
expect(styleToWeight('Medium')).toBe(500)
expect(styleToWeight('SemiBold')).toBe(600)
expect(styleToWeight('ExtraBold')).toBe(800)
expect(styleToWeight('Black')).toBe(900)
})
test('handles italic variants', () => {
expect(styleToWeight('Bold Italic')).toBe(700)
expect(styleToWeight('Light Italic')).toBe(300)
})
test('case insensitive', () => {
expect(styleToWeight('bold')).toBe(700)
expect(styleToWeight('THIN')).toBe(100)
expect(styleToWeight('semibold')).toBe(600)
})
})
describe('weightToStyle', () => {
test('maps weights to style names', () => {
expect(weightToStyle(100)).toBe('Thin')
expect(weightToStyle(200)).toBe('ExtraLight')
expect(weightToStyle(300)).toBe('Light')
expect(weightToStyle(400)).toBe('Medium')
expect(weightToStyle(500)).toBe('Medium')
expect(weightToStyle(600)).toBe('SemiBold')
expect(weightToStyle(700)).toBe('Bold')
expect(weightToStyle(800)).toBe('ExtraBold')
expect(weightToStyle(900)).toBe('Black')
})
test('appends Italic suffix', () => {
expect(weightToStyle(400, true)).toBe('Medium Italic')
expect(weightToStyle(700, true)).toBe('Bold Italic')
})
})
describe('markFontLoaded / isFontLoaded', () => {
test('marks and checks font', () => {
const family = `TestFont_${Date.now()}`
expect(isFontLoaded(family)).toBe(false)
markFontLoaded(family, 'Regular', new ArrayBuffer(8))
expect(isFontLoaded(family)).toBe(true)
})
test('different styles for same family', () => {
const family = `MultiStyle_${Date.now()}`
expect(isFontLoaded(family)).toBe(false)
markFontLoaded(family, 'Bold', new ArrayBuffer(8))
expect(isFontLoaded(family)).toBe(true)
})
})
describe('collectFontKeys', () => {
test('returns empty for non-text nodes', () => {
const graph = new SceneGraph()
const id = graph.createNode('RECTANGLE', pageId(graph), {
name: 'Rect',
x: 0,
y: 0,
width: 100,
height: 100,
}).id
expect(collectFontKeys(graph, [id])).toEqual([])
})
test('returns empty for text nodes using default font (Inter)', () => {
const graph = new SceneGraph()
const id = graph.createNode('TEXT', pageId(graph), {
name: 'Label',
x: 0,
y: 0,
width: 100,
height: 20,
text: 'Hello',
fontFamily: 'Inter',
fontWeight: 400,
}).id
expect(collectFontKeys(graph, [id])).toEqual([])
})
test('collects non-default font family', () => {
const graph = new SceneGraph()
const id = graph.createNode('TEXT', pageId(graph), {
name: 'Label',
x: 0,
y: 0,
width: 100,
height: 20,
text: 'Hello',
fontFamily: 'Roboto',
fontWeight: 400,
}).id
const keys = collectFontKeys(graph, [id])
expect(keys).toEqual([['Roboto', 'Medium']])
})
test('collects bold weight', () => {
const graph = new SceneGraph()
const id = graph.createNode('TEXT', pageId(graph), {
name: 'Label',
x: 0,
y: 0,
width: 100,
height: 20,
text: 'Hello',
fontFamily: 'Roboto',
fontWeight: 700,
}).id
const keys = collectFontKeys(graph, [id])
expect(keys).toEqual([['Roboto', 'Bold']])
})
test('collects italic variant', () => {
const graph = new SceneGraph()
const id = graph.createNode('TEXT', pageId(graph), {
name: 'Label',
x: 0,
y: 0,
width: 100,
height: 20,
text: 'Hello',
fontFamily: 'Roboto',
fontWeight: 400,
italic: true,
}).id
const keys = collectFontKeys(graph, [id])
expect(keys).toEqual([['Roboto', 'Medium Italic']])
})
test('deduplicates same family+style', () => {
const graph = new SceneGraph()
const page = pageId(graph)
graph.createNode('TEXT', page, {
name: 'A',
x: 0,
y: 0,
width: 100,
height: 20,
text: 'A',
fontFamily: 'Roboto',
fontWeight: 400,
})
graph.createNode('TEXT', page, {
name: 'B',
x: 0,
y: 30,
width: 100,
height: 20,
text: 'B',
fontFamily: 'Roboto',
fontWeight: 400,
})
const ids = graph.getChildren(page).map((n) => n.id)
const keys = collectFontKeys(graph, ids)
expect(keys).toEqual([['Roboto', 'Medium']])
})
test('collects multiple families', () => {
const graph = new SceneGraph()
const page = pageId(graph)
graph.createNode('TEXT', page, {
name: 'A',
x: 0,
y: 0,
width: 100,
height: 20,
text: 'A',
fontFamily: 'Roboto',
fontWeight: 400,
})
graph.createNode('TEXT', page, {
name: 'B',
x: 0,
y: 30,
width: 100,
height: 20,
text: 'B',
fontFamily: 'Open Sans',
fontWeight: 700,
})
const ids = graph.getChildren(page).map((n) => n.id)
const keys = collectFontKeys(graph, ids)
expect(keys).toHaveLength(2)
expect(keys).toContainEqual(['Roboto', 'Medium'])
expect(keys).toContainEqual(['Open Sans', 'Bold'])
})
test('walks into nested children', () => {
const graph = new SceneGraph()
const page = pageId(graph)
const frame = graph.createNode('FRAME', page, {
name: 'Frame',
x: 0,
y: 0,
width: 400,
height: 400,
}).id
graph.createNode('TEXT', frame, {
name: 'Nested',
x: 0,
y: 0,
width: 100,
height: 20,
text: 'Hello',
fontFamily: 'Poppins',
fontWeight: 600,
})
const keys = collectFontKeys(graph, [frame])
expect(keys).toEqual([['Poppins', 'SemiBold']])
})
test('collects fonts from style runs', () => {
const graph = new SceneGraph()
const id = graph.createNode('TEXT', pageId(graph), {
name: 'Mixed',
x: 0,
y: 0,
width: 200,
height: 20,
text: 'Hello World',
fontFamily: 'Roboto',
fontWeight: 400,
styleRuns: [
{
start: 0,
end: 5,
style: { fontFamily: 'Roboto', fontWeight: 400 },
},
{
start: 6,
end: 11,
style: { fontFamily: 'Montserrat', fontWeight: 700 },
},
],
}).id
const keys = collectFontKeys(graph, [id])
expect(keys).toHaveLength(2)
expect(keys).toContainEqual(['Roboto', 'Medium'])
expect(keys).toContainEqual(['Montserrat', 'Bold'])
})
test('style run inherits node font when not specified', () => {
const graph = new SceneGraph()
const id = graph.createNode('TEXT', pageId(graph), {
name: 'Partial',
x: 0,
y: 0,
width: 200,
height: 20,
text: 'Hello',
fontFamily: 'Lato',
fontWeight: 300,
styleRuns: [
{
start: 0,
end: 5,
style: {},
},
],
}).id
const keys = collectFontKeys(graph, [id])
expect(keys).toEqual([['Lato', 'Light']])
})
test('skips invalid node IDs', () => {
const graph = new SceneGraph()
expect(collectFontKeys(graph, ['nonexistent'])).toEqual([])
})
})