1- import { describe , expect , it , test } from 'bun:test'
2- import { getComponentName , space } from '../utils'
1+ import { afterEach , describe , expect , it , test } from 'bun:test'
2+ import {
3+ getComponentName ,
4+ propsToPropsWithTypography ,
5+ resetTextStyleCache ,
6+ space ,
7+ } from '../utils'
8+
9+ // Minimal figma global for propsToPropsWithTypography tests
10+ if ( ! ( globalThis as { figma ?: unknown } ) . figma ) {
11+ ; ( globalThis as { figma ?: unknown } ) . figma = {
12+ getLocalTextStylesAsync : ( ) => Promise . resolve ( [ ] ) ,
13+ getStyleByIdAsync : ( ) => Promise . resolve ( null ) ,
14+ } as unknown as typeof figma
15+ }
316
417describe ( 'space' , ( ) => {
518 it ( 'should create space' , ( ) => {
@@ -9,6 +22,117 @@ describe('space', () => {
922 } )
1023} )
1124
25+ describe ( 'propsToPropsWithTypography' , ( ) => {
26+ afterEach ( ( ) => {
27+ resetTextStyleCache ( )
28+ } )
29+
30+ it ( 'should apply typography from resolved cache (sync fast path)' , async ( ) => {
31+ const origGetLocal = figma . getLocalTextStylesAsync
32+ const origGetStyle = figma . getStyleByIdAsync
33+ figma . getLocalTextStylesAsync = ( ) =>
34+ Promise . resolve ( [ { id : 'ts-1' } as unknown as TextStyle ] ) as ReturnType <
35+ typeof figma . getLocalTextStylesAsync
36+ >
37+ figma . getStyleByIdAsync = ( id : string ) =>
38+ Promise . resolve (
39+ id === 'ts-1'
40+ ? ( { id : 'ts-1' , name : 'Typography/Body' } as unknown as BaseStyle )
41+ : null ,
42+ ) as ReturnType < typeof figma . getStyleByIdAsync >
43+
44+ // First call: populates async caches + resolved caches via .then()
45+ const r1 = await propsToPropsWithTypography (
46+ { fontFamily : 'Arial' , fontSize : 16 , w : 100 , h : 50 } ,
47+ 'ts-1' ,
48+ )
49+ expect ( r1 . typography ) . toBe ( 'body' )
50+ expect ( r1 . fontFamily ) . toBeUndefined ( )
51+ expect ( r1 . w ) . toBeUndefined ( )
52+
53+ // Second call: hits sync resolved-value cache (lines 71-72)
54+ const r2 = await propsToPropsWithTypography (
55+ { fontFamily : 'Inter' , fontSize : 14 , w : 200 , h : 60 } ,
56+ 'ts-1' ,
57+ )
58+ expect ( r2 . typography ) . toBe ( 'body' )
59+ expect ( r2 . fontFamily ) . toBeUndefined ( )
60+ expect ( r2 . w ) . toBeUndefined ( )
61+
62+ figma . getLocalTextStylesAsync = origGetLocal
63+ figma . getStyleByIdAsync = origGetStyle
64+ } )
65+
66+ it ( 'should return early from sync path when textStyleId not in resolved set' , async ( ) => {
67+ const origGetLocal = figma . getLocalTextStylesAsync
68+ const origGetStyle = figma . getStyleByIdAsync
69+ figma . getLocalTextStylesAsync = ( ) =>
70+ Promise . resolve ( [ { id : 'ts-1' } as unknown as TextStyle ] ) as ReturnType <
71+ typeof figma . getLocalTextStylesAsync
72+ >
73+ figma . getStyleByIdAsync = ( ) =>
74+ Promise . resolve ( null ) as ReturnType < typeof figma . getStyleByIdAsync >
75+
76+ // First call: populates resolved cache
77+ await propsToPropsWithTypography (
78+ { fontFamily : 'Arial' , w : 100 , h : 50 } ,
79+ 'ts-1' ,
80+ )
81+
82+ // Second call with unknown textStyleId — hits else branch (lines 75-76)
83+ const r = await propsToPropsWithTypography (
84+ { fontFamily : 'Inter' , w : 200 , h : 60 } ,
85+ 'ts-unknown' ,
86+ )
87+ expect ( r . fontFamily ) . toBe ( 'Inter' )
88+ expect ( r . typography ) . toBeUndefined ( )
89+ expect ( r . w ) . toBeUndefined ( )
90+
91+ // Third call with empty textStyleId — also hits else branch
92+ const r2 = await propsToPropsWithTypography (
93+ { fontFamily : 'Mono' , w : 300 , h : 70 } ,
94+ '' ,
95+ )
96+ expect ( r2 . fontFamily ) . toBe ( 'Mono' )
97+ expect ( r2 . typography ) . toBeUndefined ( )
98+
99+ figma . getLocalTextStylesAsync = origGetLocal
100+ figma . getStyleByIdAsync = origGetStyle
101+ } )
102+
103+ it ( 'should return props without typography when style resolves to null' , async ( ) => {
104+ const origGetLocal = figma . getLocalTextStylesAsync
105+ const origGetStyle = figma . getStyleByIdAsync
106+ figma . getLocalTextStylesAsync = ( ) =>
107+ Promise . resolve ( [
108+ { id : 'ts-null' } as unknown as TextStyle ,
109+ ] ) as ReturnType < typeof figma . getLocalTextStylesAsync >
110+ figma . getStyleByIdAsync = ( ) =>
111+ Promise . resolve ( null ) as ReturnType < typeof figma . getStyleByIdAsync >
112+
113+ // First call: populates caches, style is null
114+ const r1 = await propsToPropsWithTypography (
115+ { fontFamily : 'Arial' , fontSize : 16 , w : 100 , h : 50 } ,
116+ 'ts-null' ,
117+ )
118+ expect ( r1 . typography ) . toBeUndefined ( )
119+ expect ( r1 . fontFamily ) . toBe ( 'Arial' )
120+ expect ( r1 . w ) . toBeUndefined ( )
121+
122+ // Second call: sync path, styleByIdResolved has null → style is falsy, skip applyTypography
123+ const r2 = await propsToPropsWithTypography (
124+ { fontFamily : 'Inter' , fontSize : 14 , w : 200 , h : 60 } ,
125+ 'ts-null' ,
126+ )
127+ expect ( r2 . typography ) . toBeUndefined ( )
128+ expect ( r2 . fontFamily ) . toBe ( 'Inter' )
129+ expect ( r2 . w ) . toBeUndefined ( )
130+
131+ figma . getLocalTextStylesAsync = origGetLocal
132+ figma . getStyleByIdAsync = origGetStyle
133+ } )
134+ } )
135+
12136describe ( 'getComponentName' , ( ) => {
13137 test . each ( [
14138 {
0 commit comments