fix(kiwi): import Figma groups as groups
* fix(kiwi): import Figma groups as GROUP nodes instead of FRAME
- Map FRAME nodes with `resizeToFit === true` to `GROUP` type on import
- Write `resizeToFit = true` when exporting `GROUP` nodes back to Kiwi format
- Document findings and diagnosis in packages/docs/development/group-to-frame-import-issue.md
- Extract resolveNodeType helper to satisfy cyclomatic complexity limits
* test(kiwi): harden Figma group import + add coverage
Follow-up hardening on the group import/export fix:
- convert.ts: reorder so the COMPONENT_SET check runs before the FRAME→GROUP
reclassification, and guard the group check on the absence of auto-layout
(`stackMode` unset/NONE). Figma auto-layout "hug" frames use
stackPrimarySizing/stackCounterSizing, not `resizeToFit`, so this prevents a
component-set or auto-layout frame that happens to carry `resizeToFit` from being
misclassified as a group.
- Add tests/engine/io/fig/import/group-reclassify.test.ts: unit coverage for the
reclassification (FRAME+resizeToFit → GROUP, plain frame stays FRAME, auto-layout
hug frame stays FRAME), plus a real-file assertion that gold-preview.fig imports
its groups as GROUP.
- Add tests/engine/io/fig/roundtrip/group.test.ts: a created GROUP survives
export → re-import as a GROUP (validates the export-side resizeToFit write).
- Update exhaustive.test.ts gold-preview golden: 519 nodes that were misimported as
FRAME are now correctly GROUP (FRAME 4525→4006, GROUP 519), and export size shifts
594758→594770 because exported groups now carry resizeToFit.
Note: the pre-existing material3 variables roundtrip failure
("Invalid value EXCLUDE for enum BooleanOperation") is unrelated to groups and
also fails on master — out of scope here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(kiwi): remove temporary group-to-frame analysis doc
It was a scratch diagnostic artifact (and contained a hardcoded local path);
the rationale now lives in the fix's commit messages, code comments, and tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Danila Poyarkov <dev@dannote.net>
2026-06-06 10:49:19 +00:00
|
|
|
import { describe, expect, test } from 'bun:test'
|
|
|
|
|
|
|
|
|
|
import type { NodeChange } from '#core/kiwi/fig/codec'
|
|
|
|
|
import { nodeChangeToProps } from '#core/kiwi/fig/node-change/convert'
|
|
|
|
|
|
|
|
|
|
import { parseFixture } from '#tests/helpers/fig-fixtures'
|
|
|
|
|
import { collectAllNodes } from '#tests/helpers/fig-traversal'
|
2026-06-19 01:05:25 +00:00
|
|
|
import { HEAVY_TEST_TIMEOUT_MS } from '#tests/helpers/test-utils'
|
fix(kiwi): import Figma groups as groups
* fix(kiwi): import Figma groups as GROUP nodes instead of FRAME
- Map FRAME nodes with `resizeToFit === true` to `GROUP` type on import
- Write `resizeToFit = true` when exporting `GROUP` nodes back to Kiwi format
- Document findings and diagnosis in packages/docs/development/group-to-frame-import-issue.md
- Extract resolveNodeType helper to satisfy cyclomatic complexity limits
* test(kiwi): harden Figma group import + add coverage
Follow-up hardening on the group import/export fix:
- convert.ts: reorder so the COMPONENT_SET check runs before the FRAME→GROUP
reclassification, and guard the group check on the absence of auto-layout
(`stackMode` unset/NONE). Figma auto-layout "hug" frames use
stackPrimarySizing/stackCounterSizing, not `resizeToFit`, so this prevents a
component-set or auto-layout frame that happens to carry `resizeToFit` from being
misclassified as a group.
- Add tests/engine/io/fig/import/group-reclassify.test.ts: unit coverage for the
reclassification (FRAME+resizeToFit → GROUP, plain frame stays FRAME, auto-layout
hug frame stays FRAME), plus a real-file assertion that gold-preview.fig imports
its groups as GROUP.
- Add tests/engine/io/fig/roundtrip/group.test.ts: a created GROUP survives
export → re-import as a GROUP (validates the export-side resizeToFit write).
- Update exhaustive.test.ts gold-preview golden: 519 nodes that were misimported as
FRAME are now correctly GROUP (FRAME 4525→4006, GROUP 519), and export size shifts
594758→594770 because exported groups now carry resizeToFit.
Note: the pre-existing material3 variables roundtrip failure
("Invalid value EXCLUDE for enum BooleanOperation") is unrelated to groups and
also fails on master — out of scope here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(kiwi): remove temporary group-to-frame analysis doc
It was a scratch diagnostic artifact (and contained a hardcoded local path);
the rationale now lives in the fix's commit messages, code comments, and tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Danila Poyarkov <dev@dannote.net>
2026-06-06 10:49:19 +00:00
|
|
|
|
|
|
|
|
describe('Figma group reclassification on import', () => {
|
|
|
|
|
test('FRAME with resizeToFit imports as GROUP', () => {
|
|
|
|
|
const props = nodeChangeToProps(
|
|
|
|
|
{ type: 'FRAME', name: 'Group 1', resizeToFit: true } as NodeChange,
|
|
|
|
|
[]
|
|
|
|
|
)
|
|
|
|
|
expect(props.nodeType).toBe('GROUP')
|
|
|
|
|
// groups never clip their children
|
|
|
|
|
expect(props.clipsContent).toBe(false)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('plain FRAME stays FRAME', () => {
|
|
|
|
|
const props = nodeChangeToProps(
|
|
|
|
|
{ type: 'FRAME', name: 'Frame', resizeToFit: false } as NodeChange,
|
|
|
|
|
[]
|
|
|
|
|
)
|
|
|
|
|
expect(props.nodeType).toBe('FRAME')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('FRAME with no resizeToFit flag stays FRAME', () => {
|
|
|
|
|
const props = nodeChangeToProps({ type: 'FRAME', name: 'Frame' } as NodeChange, [])
|
|
|
|
|
expect(props.nodeType).toBe('FRAME')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('auto-layout hug frame stays FRAME (not GROUP)', () => {
|
|
|
|
|
const props = nodeChangeToProps(
|
|
|
|
|
{
|
|
|
|
|
type: 'FRAME',
|
|
|
|
|
name: 'AutoLayout',
|
|
|
|
|
stackMode: 'VERTICAL',
|
|
|
|
|
stackPrimarySizing: 'RESIZE_TO_FIT'
|
|
|
|
|
} as NodeChange,
|
|
|
|
|
[]
|
|
|
|
|
)
|
|
|
|
|
expect(props.nodeType).toBe('FRAME')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('auto-layout frame that also carries resizeToFit stays FRAME', () => {
|
|
|
|
|
const props = nodeChangeToProps(
|
|
|
|
|
{
|
|
|
|
|
type: 'FRAME',
|
|
|
|
|
name: 'AutoLayout2',
|
|
|
|
|
stackMode: 'HORIZONTAL',
|
|
|
|
|
resizeToFit: true
|
|
|
|
|
} as NodeChange,
|
|
|
|
|
[]
|
|
|
|
|
)
|
|
|
|
|
expect(props.nodeType).toBe('FRAME')
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-19 01:05:25 +00:00
|
|
|
test(
|
|
|
|
|
'gold-preview.fig fixture imports its groups as GROUP nodes',
|
|
|
|
|
{ timeout: HEAVY_TEST_TIMEOUT_MS },
|
|
|
|
|
async () => {
|
|
|
|
|
const graph = await parseFixture('gold-preview.fig')
|
|
|
|
|
const groups = collectAllNodes(graph).filter((n) => n.type === 'GROUP')
|
|
|
|
|
// gold-preview.fig contains real Figma groups (FRAME + resizeToFit) that must
|
|
|
|
|
// import as GROUP, not FRAME.
|
|
|
|
|
expect(groups.length).toBeGreaterThan(0)
|
|
|
|
|
}
|
|
|
|
|
)
|
fix(kiwi): import Figma groups as groups
* fix(kiwi): import Figma groups as GROUP nodes instead of FRAME
- Map FRAME nodes with `resizeToFit === true` to `GROUP` type on import
- Write `resizeToFit = true` when exporting `GROUP` nodes back to Kiwi format
- Document findings and diagnosis in packages/docs/development/group-to-frame-import-issue.md
- Extract resolveNodeType helper to satisfy cyclomatic complexity limits
* test(kiwi): harden Figma group import + add coverage
Follow-up hardening on the group import/export fix:
- convert.ts: reorder so the COMPONENT_SET check runs before the FRAME→GROUP
reclassification, and guard the group check on the absence of auto-layout
(`stackMode` unset/NONE). Figma auto-layout "hug" frames use
stackPrimarySizing/stackCounterSizing, not `resizeToFit`, so this prevents a
component-set or auto-layout frame that happens to carry `resizeToFit` from being
misclassified as a group.
- Add tests/engine/io/fig/import/group-reclassify.test.ts: unit coverage for the
reclassification (FRAME+resizeToFit → GROUP, plain frame stays FRAME, auto-layout
hug frame stays FRAME), plus a real-file assertion that gold-preview.fig imports
its groups as GROUP.
- Add tests/engine/io/fig/roundtrip/group.test.ts: a created GROUP survives
export → re-import as a GROUP (validates the export-side resizeToFit write).
- Update exhaustive.test.ts gold-preview golden: 519 nodes that were misimported as
FRAME are now correctly GROUP (FRAME 4525→4006, GROUP 519), and export size shifts
594758→594770 because exported groups now carry resizeToFit.
Note: the pre-existing material3 variables roundtrip failure
("Invalid value EXCLUDE for enum BooleanOperation") is unrelated to groups and
also fails on master — out of scope here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* chore(kiwi): remove temporary group-to-frame analysis doc
It was a scratch diagnostic artifact (and contained a hardcoded local path);
the rationale now lives in the fix's commit messages, code comments, and tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Danila Poyarkov <dev@dannote.net>
2026-06-06 10:49:19 +00:00
|
|
|
})
|