@@ -11,6 +11,11 @@ const renderNodeMock = mock(
1111 `render:${ component } :depth=${ depth } :${ JSON . stringify ( props ) } |${ children . join ( ';' ) } ` ,
1212)
1313
14+ const renderComponentMock = mock (
15+ ( component : string , code : string , variants : Record < string , string > ) =>
16+ `component:${ component } :${ JSON . stringify ( variants ) } |${ code } ` ,
17+ )
18+
1419// Mock Codegen class
1520const mockGetTree = mock (
1621 async ( ) : Promise < NodeTree > => ( {
@@ -35,7 +40,10 @@ describe('ResponsiveCodegen', () => {
3540 let ResponsiveCodegen : typeof import ( '../ResponsiveCodegen' ) . ResponsiveCodegen
3641
3742 beforeEach ( async ( ) => {
38- mock . module ( '../../render' , ( ) => ( { renderNode : renderNodeMock } ) )
43+ mock . module ( '../../render' , ( ) => ( {
44+ renderNode : renderNodeMock ,
45+ renderComponent : renderComponentMock ,
46+ } ) )
3947 mock . module ( '../../Codegen' , ( ) => ( { Codegen : MockCodegen } ) )
4048
4149 ; ( { ResponsiveCodegen } = await import ( '../ResponsiveCodegen' ) )
@@ -160,7 +168,203 @@ describe('ResponsiveCodegen', () => {
160168 it ( 'static helpers detect section and parent section' , ( ) => {
161169 const section = { type : 'SECTION' } as unknown as SectionNode
162170 const frame = { type : 'FRAME' , parent : section } as unknown as SceneNode
171+ const nonSection = { type : 'FRAME' } as unknown as SceneNode
172+ const nodeWithoutSectionParent = {
173+ type : 'FRAME' ,
174+ parent : { type : 'FRAME' } ,
175+ } as unknown as SceneNode
176+
163177 expect ( ResponsiveCodegen . canGenerateResponsive ( section ) ) . toBeTrue ( )
178+ expect ( ResponsiveCodegen . canGenerateResponsive ( nonSection ) ) . toBeFalse ( )
164179 expect ( ResponsiveCodegen . hasParentSection ( frame ) ) . toEqual ( section )
180+ expect (
181+ ResponsiveCodegen . hasParentSection ( nodeWithoutSectionParent ) ,
182+ ) . toBeNull ( )
183+ } )
184+
185+ it ( 'generateViewportResponsiveComponents returns empty when no viewport variant' , async ( ) => {
186+ const componentSet = {
187+ type : 'COMPONENT_SET' ,
188+ name : 'NoViewport' ,
189+ componentPropertyDefinitions : {
190+ size : {
191+ type : 'VARIANT' ,
192+ variantOptions : [ 'sm' , 'md' , 'lg' ] ,
193+ } ,
194+ } ,
195+ children : [ ] ,
196+ } as unknown as ComponentSetNode
197+
198+ const result = await ResponsiveCodegen . generateViewportResponsiveComponents (
199+ componentSet ,
200+ 'NoViewport' ,
201+ )
202+ expect ( result ) . toEqual ( [ ] )
203+ } )
204+
205+ it ( 'generateViewportResponsiveComponents processes non-viewport variants' , async ( ) => {
206+ const componentSet = {
207+ type : 'COMPONENT_SET' ,
208+ name : 'MultiVariant' ,
209+ componentPropertyDefinitions : {
210+ viewport : {
211+ type : 'VARIANT' ,
212+ variantOptions : [ 'mobile' , 'desktop' ] ,
213+ } ,
214+ size : {
215+ type : 'VARIANT' ,
216+ variantOptions : [ 'sm' , 'md' , 'lg' ] ,
217+ } ,
218+ } ,
219+ children : [
220+ {
221+ type : 'COMPONENT' ,
222+ name : 'viewport=mobile, size=md' ,
223+ variantProperties : { viewport : 'mobile' , size : 'md' } ,
224+ children : [ ] ,
225+ layoutMode : 'VERTICAL' ,
226+ width : 320 ,
227+ height : 100 ,
228+ } ,
229+ {
230+ type : 'COMPONENT' ,
231+ name : 'viewport=desktop, size=md' ,
232+ variantProperties : { viewport : 'desktop' , size : 'md' } ,
233+ children : [ ] ,
234+ layoutMode : 'HORIZONTAL' ,
235+ width : 1200 ,
236+ height : 100 ,
237+ } ,
238+ ] ,
239+ } as unknown as ComponentSetNode
240+
241+ const result = await ResponsiveCodegen . generateViewportResponsiveComponents (
242+ componentSet ,
243+ 'MultiVariant' ,
244+ )
245+
246+ expect ( result . length ) . toBeGreaterThan ( 0 )
247+ // Check that the result includes the component name
248+ expect ( result [ 0 ] [ 0 ] ) . toBe ( 'MultiVariant' )
249+ // Check that the generated code includes the size variant type
250+ expect ( result [ 0 ] [ 1 ] ) . toContain ( 'size' )
251+ } )
252+
253+ it ( 'handles component without viewport in variantProperties' , async ( ) => {
254+ const componentSet = {
255+ type : 'COMPONENT_SET' ,
256+ name : 'PartialViewport' ,
257+ componentPropertyDefinitions : {
258+ viewport : {
259+ type : 'VARIANT' ,
260+ variantOptions : [ 'mobile' , 'desktop' ] ,
261+ } ,
262+ } ,
263+ children : [
264+ {
265+ type : 'COMPONENT' ,
266+ name : 'viewport=mobile' ,
267+ variantProperties : { viewport : 'mobile' } ,
268+ children : [ ] ,
269+ layoutMode : 'VERTICAL' ,
270+ width : 320 ,
271+ height : 100 ,
272+ } ,
273+ {
274+ type : 'COMPONENT' ,
275+ name : 'no-viewport' ,
276+ variantProperties : { } , // No viewport property
277+ children : [ ] ,
278+ layoutMode : 'HORIZONTAL' ,
279+ width : 1200 ,
280+ height : 100 ,
281+ } ,
282+ {
283+ type : 'FRAME' , // Not a COMPONENT type
284+ name : 'frame-child' ,
285+ children : [ ] ,
286+ } ,
287+ ] ,
288+ } as unknown as ComponentSetNode
289+
290+ const result = await ResponsiveCodegen . generateViewportResponsiveComponents (
291+ componentSet ,
292+ 'PartialViewport' ,
293+ )
294+
295+ // Should still generate responsive code for the valid component
296+ expect ( result . length ) . toBeGreaterThanOrEqual ( 0 )
297+ } )
298+
299+ it ( 'handles null sectionNode in constructor' , ( ) => {
300+ const generator = new ResponsiveCodegen ( null )
301+ expect ( generator ) . toBeDefined ( )
302+ } )
303+
304+ it ( 'sorts multiple non-viewport variants alphabetically' , async ( ) => {
305+ // Multiple non-viewport variants to trigger the sort callback
306+ const componentSet = {
307+ type : 'COMPONENT_SET' ,
308+ name : 'MultiPropVariant' ,
309+ componentPropertyDefinitions : {
310+ viewport : {
311+ type : 'VARIANT' ,
312+ variantOptions : [ 'mobile' , 'desktop' ] ,
313+ } ,
314+ size : {
315+ type : 'VARIANT' ,
316+ variantOptions : [ 'sm' , 'md' , 'lg' ] ,
317+ } ,
318+ color : {
319+ type : 'VARIANT' ,
320+ variantOptions : [ 'red' , 'blue' , 'green' ] ,
321+ } ,
322+ state : {
323+ type : 'VARIANT' ,
324+ variantOptions : [ 'default' , 'hover' , 'active' ] ,
325+ } ,
326+ } ,
327+ children : [
328+ {
329+ type : 'COMPONENT' ,
330+ name : 'viewport=mobile, size=md, color=red, state=default' ,
331+ variantProperties : {
332+ viewport : 'mobile' ,
333+ size : 'md' ,
334+ color : 'red' ,
335+ state : 'default' ,
336+ } ,
337+ children : [ ] ,
338+ layoutMode : 'VERTICAL' ,
339+ width : 320 ,
340+ height : 100 ,
341+ } ,
342+ {
343+ type : 'COMPONENT' ,
344+ name : 'viewport=desktop, size=md, color=red, state=default' ,
345+ variantProperties : {
346+ viewport : 'desktop' ,
347+ size : 'md' ,
348+ color : 'red' ,
349+ state : 'default' ,
350+ } ,
351+ children : [ ] ,
352+ layoutMode : 'HORIZONTAL' ,
353+ width : 1200 ,
354+ height : 100 ,
355+ } ,
356+ ] ,
357+ } as unknown as ComponentSetNode
358+
359+ const result = await ResponsiveCodegen . generateViewportResponsiveComponents (
360+ componentSet ,
361+ 'MultiPropVariant' ,
362+ )
363+
364+ expect ( result . length ) . toBeGreaterThan ( 0 )
365+ // Check that all non-viewport variants are in the interface
366+ expect ( result [ 0 ] [ 1 ] ) . toContain ( 'size' )
367+ expect ( result [ 0 ] [ 1 ] ) . toContain ( 'color' )
368+ expect ( result [ 0 ] [ 1 ] ) . toContain ( 'state' )
165369 } )
166370} )
0 commit comments