@fragments-sdk/cli 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +4783 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-4FDQSGKX.js +786 -0
- package/dist/chunk-4FDQSGKX.js.map +1 -0
- package/dist/chunk-7H2MMGYG.js +369 -0
- package/dist/chunk-7H2MMGYG.js.map +1 -0
- package/dist/chunk-BSCG3IP7.js +619 -0
- package/dist/chunk-BSCG3IP7.js.map +1 -0
- package/dist/chunk-LY2CFFPY.js +898 -0
- package/dist/chunk-LY2CFFPY.js.map +1 -0
- package/dist/chunk-MUZ6CM66.js +6636 -0
- package/dist/chunk-MUZ6CM66.js.map +1 -0
- package/dist/chunk-OAENNG3G.js +1489 -0
- package/dist/chunk-OAENNG3G.js.map +1 -0
- package/dist/chunk-XHNKNI6J.js +235 -0
- package/dist/chunk-XHNKNI6J.js.map +1 -0
- package/dist/core-DWKLGY4N.js +68 -0
- package/dist/core-DWKLGY4N.js.map +1 -0
- package/dist/generate-4LQNJ7SX.js +249 -0
- package/dist/generate-4LQNJ7SX.js.map +1 -0
- package/dist/index.d.ts +775 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/init-EMVI47QG.js +416 -0
- package/dist/init-EMVI47QG.js.map +1 -0
- package/dist/mcp-bin.d.ts +1 -0
- package/dist/mcp-bin.js +1117 -0
- package/dist/mcp-bin.js.map +1 -0
- package/dist/scan-4YPRF7FV.js +12 -0
- package/dist/scan-4YPRF7FV.js.map +1 -0
- package/dist/service-QSZMZJBJ.js +208 -0
- package/dist/service-QSZMZJBJ.js.map +1 -0
- package/dist/static-viewer-MIPGZ4Z7.js +12 -0
- package/dist/static-viewer-MIPGZ4Z7.js.map +1 -0
- package/dist/test-SQ5ZHXWU.js +1067 -0
- package/dist/test-SQ5ZHXWU.js.map +1 -0
- package/dist/tokens-HSGMYK64.js +173 -0
- package/dist/tokens-HSGMYK64.js.map +1 -0
- package/dist/viewer-YRF4SQE4.js +11101 -0
- package/dist/viewer-YRF4SQE4.js.map +1 -0
- package/package.json +107 -0
- package/src/ai.ts +266 -0
- package/src/analyze.ts +265 -0
- package/src/bin.ts +916 -0
- package/src/build.ts +248 -0
- package/src/commands/a11y.ts +302 -0
- package/src/commands/add.ts +313 -0
- package/src/commands/audit.ts +195 -0
- package/src/commands/baseline.ts +221 -0
- package/src/commands/build.ts +144 -0
- package/src/commands/compare.ts +337 -0
- package/src/commands/context.ts +107 -0
- package/src/commands/dev.ts +107 -0
- package/src/commands/enhance.ts +858 -0
- package/src/commands/generate.ts +391 -0
- package/src/commands/init.ts +531 -0
- package/src/commands/link/figma.ts +645 -0
- package/src/commands/link/index.ts +10 -0
- package/src/commands/link/storybook.ts +267 -0
- package/src/commands/list.ts +49 -0
- package/src/commands/metrics.ts +114 -0
- package/src/commands/reset.ts +242 -0
- package/src/commands/scan.ts +537 -0
- package/src/commands/storygen.ts +207 -0
- package/src/commands/tokens.ts +251 -0
- package/src/commands/validate.ts +93 -0
- package/src/commands/verify.ts +215 -0
- package/src/core/composition.test.ts +262 -0
- package/src/core/composition.ts +255 -0
- package/src/core/config.ts +84 -0
- package/src/core/constants.ts +111 -0
- package/src/core/context.ts +380 -0
- package/src/core/defineSegment.ts +137 -0
- package/src/core/discovery.ts +337 -0
- package/src/core/figma.ts +263 -0
- package/src/core/fragment-types.ts +214 -0
- package/src/core/generators/context.ts +389 -0
- package/src/core/generators/index.ts +23 -0
- package/src/core/generators/registry.ts +364 -0
- package/src/core/generators/typescript-extractor.ts +374 -0
- package/src/core/importAnalyzer.ts +217 -0
- package/src/core/index.ts +149 -0
- package/src/core/loader.ts +155 -0
- package/src/core/node.ts +63 -0
- package/src/core/parser.ts +551 -0
- package/src/core/previewLoader.ts +172 -0
- package/src/core/schema/fragment.schema.json +189 -0
- package/src/core/schema/registry.schema.json +137 -0
- package/src/core/schema.ts +182 -0
- package/src/core/storyAdapter.test.ts +571 -0
- package/src/core/storyAdapter.ts +761 -0
- package/src/core/token-types.ts +287 -0
- package/src/core/types.ts +754 -0
- package/src/diff.ts +323 -0
- package/src/index.ts +43 -0
- package/src/mcp/__tests__/projectFields.test.ts +130 -0
- package/src/mcp/bin.ts +36 -0
- package/src/mcp/index.ts +8 -0
- package/src/mcp/server.ts +1310 -0
- package/src/mcp/utils.ts +54 -0
- package/src/mcp-bin.ts +36 -0
- package/src/migrate/__tests__/argTypes/argTypes.test.ts +189 -0
- package/src/migrate/__tests__/args/args.test.ts +452 -0
- package/src/migrate/__tests__/meta/meta.test.ts +198 -0
- package/src/migrate/__tests__/stories/stories.test.ts +278 -0
- package/src/migrate/__tests__/utils/utils.test.ts +371 -0
- package/src/migrate/__tests__/values/values.test.ts +303 -0
- package/src/migrate/bin.ts +108 -0
- package/src/migrate/converter.ts +658 -0
- package/src/migrate/detect.ts +196 -0
- package/src/migrate/index.ts +45 -0
- package/src/migrate/migrate.ts +163 -0
- package/src/migrate/parser.ts +1136 -0
- package/src/migrate/report.ts +624 -0
- package/src/migrate/types.ts +169 -0
- package/src/screenshot.ts +249 -0
- package/src/service/__tests__/ast-utils.test.ts +426 -0
- package/src/service/__tests__/enhance-scanner.test.ts +200 -0
- package/src/service/__tests__/figma/figma.test.ts +652 -0
- package/src/service/__tests__/metrics-store.test.ts +409 -0
- package/src/service/__tests__/patch-generator.test.ts +186 -0
- package/src/service/__tests__/props-extractor.test.ts +365 -0
- package/src/service/__tests__/token-registry.test.ts +267 -0
- package/src/service/analytics.ts +659 -0
- package/src/service/ast-utils.ts +444 -0
- package/src/service/browser-pool.ts +339 -0
- package/src/service/capture.ts +267 -0
- package/src/service/diff.ts +279 -0
- package/src/service/enhance/aggregator.ts +489 -0
- package/src/service/enhance/cache.ts +275 -0
- package/src/service/enhance/codebase-scanner.ts +357 -0
- package/src/service/enhance/context-generator.ts +529 -0
- package/src/service/enhance/doc-extractor.ts +523 -0
- package/src/service/enhance/index.ts +131 -0
- package/src/service/enhance/props-extractor.ts +665 -0
- package/src/service/enhance/scanner.ts +445 -0
- package/src/service/enhance/storybook-parser.ts +552 -0
- package/src/service/enhance/types.ts +346 -0
- package/src/service/enhance/variant-renderer.ts +479 -0
- package/src/service/figma.ts +1008 -0
- package/src/service/index.ts +249 -0
- package/src/service/metrics-store.ts +333 -0
- package/src/service/patch-generator.ts +349 -0
- package/src/service/report.ts +854 -0
- package/src/service/storage.ts +401 -0
- package/src/service/token-fixes.ts +281 -0
- package/src/service/token-parser.ts +504 -0
- package/src/service/token-registry.ts +721 -0
- package/src/service/utils.ts +172 -0
- package/src/setup.ts +241 -0
- package/src/shared/command-wrapper.ts +81 -0
- package/src/shared/dev-server-client.ts +199 -0
- package/src/shared/index.ts +8 -0
- package/src/shared/segment-loader.ts +59 -0
- package/src/shared/types.ts +147 -0
- package/src/static-viewer.ts +715 -0
- package/src/test/discovery.ts +172 -0
- package/src/test/index.ts +281 -0
- package/src/test/reporters/console.ts +194 -0
- package/src/test/reporters/json.ts +190 -0
- package/src/test/reporters/junit.ts +186 -0
- package/src/test/runner.ts +598 -0
- package/src/test/types.ts +245 -0
- package/src/test/watch.ts +200 -0
- package/src/validators.ts +152 -0
- package/src/viewer/__tests__/jsx-parser.test.ts +502 -0
- package/src/viewer/__tests__/render-utils.test.ts +232 -0
- package/src/viewer/__tests__/style-utils.test.ts +404 -0
- package/src/viewer/bin.ts +86 -0
- package/src/viewer/cli/health.ts +256 -0
- package/src/viewer/cli/index.ts +33 -0
- package/src/viewer/cli/scan.ts +124 -0
- package/src/viewer/cli/utils.ts +174 -0
- package/src/viewer/components/AccessibilityPanel.tsx +1404 -0
- package/src/viewer/components/ActionCapture.tsx +172 -0
- package/src/viewer/components/ActionsPanel.tsx +371 -0
- package/src/viewer/components/App.tsx +638 -0
- package/src/viewer/components/BottomPanel.tsx +224 -0
- package/src/viewer/components/CodePanel.tsx +589 -0
- package/src/viewer/components/CommandPalette.tsx +336 -0
- package/src/viewer/components/ComponentGraph.tsx +394 -0
- package/src/viewer/components/ComponentHeader.tsx +85 -0
- package/src/viewer/components/ContractPanel.tsx +234 -0
- package/src/viewer/components/ErrorBoundary.tsx +85 -0
- package/src/viewer/components/FigmaEmbed.tsx +231 -0
- package/src/viewer/components/FragmentEditor.tsx +485 -0
- package/src/viewer/components/HealthDashboard.tsx +452 -0
- package/src/viewer/components/HmrStatusIndicator.tsx +71 -0
- package/src/viewer/components/Icons.tsx +417 -0
- package/src/viewer/components/InteractionsPanel.tsx +720 -0
- package/src/viewer/components/IsolatedPreviewFrame.tsx +321 -0
- package/src/viewer/components/IsolatedRender.tsx +111 -0
- package/src/viewer/components/KeyboardShortcutsHelp.tsx +89 -0
- package/src/viewer/components/LandingPage.tsx +441 -0
- package/src/viewer/components/Layout.tsx +22 -0
- package/src/viewer/components/LeftSidebar.tsx +391 -0
- package/src/viewer/components/MultiViewportPreview.tsx +429 -0
- package/src/viewer/components/PreviewArea.tsx +404 -0
- package/src/viewer/components/PreviewFrameHost.tsx +310 -0
- package/src/viewer/components/PreviewPane.tsx +150 -0
- package/src/viewer/components/PreviewToolbar.tsx +176 -0
- package/src/viewer/components/PropsEditor.tsx +512 -0
- package/src/viewer/components/PropsTable.tsx +98 -0
- package/src/viewer/components/RelationsSection.tsx +57 -0
- package/src/viewer/components/ResizablePanel.tsx +328 -0
- package/src/viewer/components/RightSidebar.tsx +118 -0
- package/src/viewer/components/ScreenshotButton.tsx +90 -0
- package/src/viewer/components/Sidebar.tsx +169 -0
- package/src/viewer/components/SkeletonLoader.tsx +156 -0
- package/src/viewer/components/StoryRenderer.tsx +128 -0
- package/src/viewer/components/ThemeProvider.tsx +96 -0
- package/src/viewer/components/Toast.tsx +67 -0
- package/src/viewer/components/TokenStylePanel.tsx +708 -0
- package/src/viewer/components/UsageSection.tsx +95 -0
- package/src/viewer/components/VariantMatrix.tsx +350 -0
- package/src/viewer/components/VariantRenderer.tsx +131 -0
- package/src/viewer/components/VariantTabs.tsx +84 -0
- package/src/viewer/components/ViewportSelector.tsx +165 -0
- package/src/viewer/components/_future/CreatePage.tsx +836 -0
- package/src/viewer/composition-renderer.ts +381 -0
- package/src/viewer/constants/index.ts +1 -0
- package/src/viewer/constants/ui.ts +185 -0
- package/src/viewer/entry.tsx +299 -0
- package/src/viewer/hooks/index.ts +2 -0
- package/src/viewer/hooks/useA11yCache.ts +383 -0
- package/src/viewer/hooks/useA11yService.ts +498 -0
- package/src/viewer/hooks/useActions.ts +138 -0
- package/src/viewer/hooks/useAppState.ts +124 -0
- package/src/viewer/hooks/useFigmaIntegration.ts +132 -0
- package/src/viewer/hooks/useHmrStatus.ts +109 -0
- package/src/viewer/hooks/useKeyboardShortcuts.ts +222 -0
- package/src/viewer/hooks/usePreviewBridge.ts +347 -0
- package/src/viewer/hooks/useScrollSpy.ts +78 -0
- package/src/viewer/hooks/useUrlState.ts +330 -0
- package/src/viewer/hooks/useViewSettings.ts +125 -0
- package/src/viewer/index.html +28 -0
- package/src/viewer/index.ts +14 -0
- package/src/viewer/intelligence/healthReport.ts +505 -0
- package/src/viewer/intelligence/styleDrift.ts +340 -0
- package/src/viewer/intelligence/usageScanner.ts +309 -0
- package/src/viewer/jsx-parser.ts +485 -0
- package/src/viewer/postcss.config.js +6 -0
- package/src/viewer/preview-frame-entry.tsx +25 -0
- package/src/viewer/preview-frame.html +109 -0
- package/src/viewer/render-template.html +68 -0
- package/src/viewer/render-utils.ts +170 -0
- package/src/viewer/server.ts +276 -0
- package/src/viewer/style-utils.ts +414 -0
- package/src/viewer/styles/globals.css +355 -0
- package/src/viewer/tailwind.config.js +37 -0
- package/src/viewer/types/a11y.ts +197 -0
- package/src/viewer/utils/a11y-fixes.ts +471 -0
- package/src/viewer/utils/actionExport.ts +372 -0
- package/src/viewer/utils/colorSchemes.ts +201 -0
- package/src/viewer/utils/detectRelationships.ts +256 -0
- package/src/viewer/vite-plugin.ts +2143 -0
|
@@ -0,0 +1,452 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { __testing } from "../../parser.js";
|
|
3
|
+
|
|
4
|
+
const { parseArgs, parseArgsSimple, parseArgValue, extractConstDeclarations } = __testing;
|
|
5
|
+
|
|
6
|
+
describe("parseArgs", () => {
|
|
7
|
+
describe("basic key-value parsing", () => {
|
|
8
|
+
it("parses string values", () => {
|
|
9
|
+
const result = parseArgs(`label: 'Hello World'`);
|
|
10
|
+
expect(result.label).toBe("Hello World");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("parses numeric values", () => {
|
|
14
|
+
const result = parseArgs(`count: 42, price: 19.99`);
|
|
15
|
+
expect(result.count).toBe(42);
|
|
16
|
+
expect(result.price).toBe(19.99);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("parses boolean values", () => {
|
|
20
|
+
const result = parseArgs(`disabled: true, visible: false`);
|
|
21
|
+
expect(result.disabled).toBe(true);
|
|
22
|
+
expect(result.visible).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("parses null and undefined", () => {
|
|
26
|
+
const result = parseArgs(`value: null, other: undefined`);
|
|
27
|
+
expect(result.value).toBeNull();
|
|
28
|
+
expect(result.other).toBeUndefined();
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe("nested objects", () => {
|
|
33
|
+
it("parses simple nested object", () => {
|
|
34
|
+
const result = parseArgs(`style: { color: 'red', size: 12 }`);
|
|
35
|
+
expect(result.style).toEqual({ color: "red", size: 12 });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("parses deeply nested objects", () => {
|
|
39
|
+
const result = parseArgs(`config: { theme: { colors: { primary: 'blue' } } }`);
|
|
40
|
+
expect(result.config).toEqual({
|
|
41
|
+
theme: { colors: { primary: "blue" } },
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("handles empty objects", () => {
|
|
46
|
+
const result = parseArgs(`options: {}`);
|
|
47
|
+
expect(result.options).toEqual({});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe("arrays", () => {
|
|
52
|
+
it("parses string arrays", () => {
|
|
53
|
+
const result = parseArgs(`items: ['a', 'b', 'c']`);
|
|
54
|
+
expect(result.items).toEqual(["a", "b", "c"]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("parses numeric arrays", () => {
|
|
58
|
+
const result = parseArgs(`numbers: [1, 2, 3, 4, 5]`);
|
|
59
|
+
expect(result.numbers).toEqual([1, 2, 3, 4, 5]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("parses mixed arrays", () => {
|
|
63
|
+
const result = parseArgs(`mixed: ['text', 42, true]`);
|
|
64
|
+
expect(result.mixed).toEqual(["text", 42, true]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("parses arrays of objects", () => {
|
|
68
|
+
const result = parseArgs(`users: [{ name: 'Alice' }, { name: 'Bob' }]`);
|
|
69
|
+
expect(result.users).toEqual([{ name: "Alice" }, { name: "Bob" }]);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("handles empty arrays", () => {
|
|
73
|
+
const result = parseArgs(`list: []`);
|
|
74
|
+
expect(result.list).toEqual([]);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe("spread syntax", () => {
|
|
79
|
+
it("resolves spread from const declarations", () => {
|
|
80
|
+
const constDeclarations = new Map([
|
|
81
|
+
["defaults", { size: "medium", variant: "primary" }],
|
|
82
|
+
]);
|
|
83
|
+
const result = parseArgs(`...defaults, label: 'Click'`, constDeclarations);
|
|
84
|
+
expect(result.size).toBe("medium");
|
|
85
|
+
expect(result.variant).toBe("primary");
|
|
86
|
+
expect(result.label).toBe("Click");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("marks unresolved spreads", () => {
|
|
90
|
+
const result = parseArgs(`...unknownVar, label: 'Click'`);
|
|
91
|
+
expect(result.__SPREAD__).toBe("__REF__unknownVar");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("explicit values override spread values", () => {
|
|
95
|
+
const constDeclarations = new Map([
|
|
96
|
+
["defaults", { size: "small", label: "Default" }],
|
|
97
|
+
]);
|
|
98
|
+
const result = parseArgs(`...defaults, size: 'large'`, constDeclarations);
|
|
99
|
+
expect(result.size).toBe("large");
|
|
100
|
+
expect(result.label).toBe("Default");
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("shorthand properties", () => {
|
|
105
|
+
it("marks shorthand properties as references", () => {
|
|
106
|
+
const result = parseArgs(`disabled`);
|
|
107
|
+
expect(result.disabled).toBe("__REF__disabled");
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it("handles multiple shorthand properties", () => {
|
|
111
|
+
const result = parseArgs(`disabled, loading, visible`);
|
|
112
|
+
expect(result.disabled).toBe("__REF__disabled");
|
|
113
|
+
expect(result.loading).toBe("__REF__loading");
|
|
114
|
+
expect(result.visible).toBe("__REF__visible");
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
describe("variable references", () => {
|
|
119
|
+
it("marks identifiers as references", () => {
|
|
120
|
+
const result = parseArgs(`onClick: handleClick`);
|
|
121
|
+
expect(result.onClick).toBe("__REF__handleClick");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("marks property access as references", () => {
|
|
125
|
+
const result = parseArgs(`data: props.userData`);
|
|
126
|
+
expect(result.data).toBe("__REF__props.userData");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("marks optional chaining as references", () => {
|
|
130
|
+
const result = parseArgs(`value: config?.settings`);
|
|
131
|
+
expect(result.value).toBe("__REF__config?.settings");
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("marks array access as references", () => {
|
|
135
|
+
const result = parseArgs(`item: items[0]`);
|
|
136
|
+
expect(result.item).toBe("__REF__items[0]");
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe("variable reference resolution", () => {
|
|
141
|
+
it("resolves simple variable reference from constDeclarations", () => {
|
|
142
|
+
const constDeclarations = new Map([
|
|
143
|
+
["mockOptions", [{ label: "Option 1" }, { label: "Option 2" }]],
|
|
144
|
+
]);
|
|
145
|
+
const result = parseArgs(`options: mockOptions`, constDeclarations);
|
|
146
|
+
expect(result.options).toEqual([{ label: "Option 1" }, { label: "Option 2" }]);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("resolves object variable reference from constDeclarations", () => {
|
|
150
|
+
const constDeclarations = new Map([
|
|
151
|
+
["defaultStyle", { color: "blue", fontSize: 14 }],
|
|
152
|
+
]);
|
|
153
|
+
const result = parseArgs(`style: defaultStyle`, constDeclarations);
|
|
154
|
+
expect(result.style).toEqual({ color: "blue", fontSize: 14 });
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("resolves property access from constDeclarations", () => {
|
|
158
|
+
const constDeclarations = new Map([
|
|
159
|
+
["config", { theme: { primary: "blue", secondary: "gray" } }],
|
|
160
|
+
]);
|
|
161
|
+
const result = parseArgs(`color: config.theme.primary`, constDeclarations);
|
|
162
|
+
expect(result.color).toBe("blue");
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("resolves optional chain property access from constDeclarations", () => {
|
|
166
|
+
const constDeclarations = new Map([
|
|
167
|
+
["settings", { display: { mode: "dark" } }],
|
|
168
|
+
]);
|
|
169
|
+
const result = parseArgs(`mode: settings?.display?.mode`, constDeclarations);
|
|
170
|
+
expect(result.mode).toBe("dark");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("falls back to __REF__ when variable not in constDeclarations", () => {
|
|
174
|
+
const constDeclarations = new Map([
|
|
175
|
+
["otherVar", { value: 1 }],
|
|
176
|
+
]);
|
|
177
|
+
const result = parseArgs(`data: unknownVar`, constDeclarations);
|
|
178
|
+
expect(result.data).toBe("__REF__unknownVar");
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("falls back to __REF__ when property path doesn't exist", () => {
|
|
182
|
+
const constDeclarations = new Map([
|
|
183
|
+
["config", { theme: { primary: "blue" } }],
|
|
184
|
+
]);
|
|
185
|
+
const result = parseArgs(`color: config.nonexistent.path`, constDeclarations);
|
|
186
|
+
expect(result.color).toBe("__REF__config.nonexistent.path");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("resolves variables inside nested objects", () => {
|
|
190
|
+
const constDeclarations = new Map([
|
|
191
|
+
["defaultPadding", 16],
|
|
192
|
+
]);
|
|
193
|
+
// Note: defaultPadding is a primitive, not object - this tests value resolution in nested context
|
|
194
|
+
const result = parseArgs(`style: { padding: 8, margin: 4 }`, constDeclarations);
|
|
195
|
+
expect(result.style).toEqual({ padding: 8, margin: 4 });
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("resolves variables inside arrays", () => {
|
|
199
|
+
const constDeclarations = new Map([
|
|
200
|
+
["item1", { id: 1, name: "First" }],
|
|
201
|
+
["item2", { id: 2, name: "Second" }],
|
|
202
|
+
]);
|
|
203
|
+
const result = parseArgs(`items: [item1, item2]`, constDeclarations);
|
|
204
|
+
expect(result.items).toEqual([
|
|
205
|
+
{ id: 1, name: "First" },
|
|
206
|
+
{ id: 2, name: "Second" },
|
|
207
|
+
]);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("handles mixed resolved and unresolved variables", () => {
|
|
211
|
+
const constDeclarations = new Map([
|
|
212
|
+
["knownVar", "resolved value"],
|
|
213
|
+
]);
|
|
214
|
+
const result = parseArgs(`known: knownVar, unknown: unknownVar`, constDeclarations);
|
|
215
|
+
expect(result.known).toBe("resolved value");
|
|
216
|
+
expect(result.unknown).toBe("__REF__unknownVar");
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it("resolves deeply nested property access", () => {
|
|
220
|
+
const constDeclarations = new Map([
|
|
221
|
+
["app", {
|
|
222
|
+
settings: {
|
|
223
|
+
ui: {
|
|
224
|
+
theme: {
|
|
225
|
+
colors: { primary: "#007bff" }
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}],
|
|
230
|
+
]);
|
|
231
|
+
const result = parseArgs(`primaryColor: app.settings.ui.theme.colors.primary`, constDeclarations);
|
|
232
|
+
expect(result.primaryColor).toBe("#007bff");
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
describe("special values", () => {
|
|
237
|
+
it("marks JSX as special value", () => {
|
|
238
|
+
const result = parseArgs(`icon: <Icon />`);
|
|
239
|
+
expect(result.icon).toBe("__JSX__");
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it("marks functions as expressions", () => {
|
|
243
|
+
const result = parseArgs(`onClick: () => console.log('clicked')`);
|
|
244
|
+
expect(result.onClick).toBe("__EXPR__");
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it("marks function calls as expressions", () => {
|
|
248
|
+
const result = parseArgs(`value: getValue()`);
|
|
249
|
+
expect(result.value).toBe("__EXPR__");
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
describe("complex scenarios", () => {
|
|
254
|
+
it("handles commas inside strings", () => {
|
|
255
|
+
const result = parseArgs(`message: 'Hello, World!'`);
|
|
256
|
+
expect(result.message).toBe("Hello, World!");
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it("handles colons inside strings", () => {
|
|
260
|
+
const result = parseArgs(`time: '12:30:00'`);
|
|
261
|
+
expect(result.time).toBe("12:30:00");
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it("handles braces inside strings", () => {
|
|
265
|
+
const result = parseArgs(`template: '{ value }'`);
|
|
266
|
+
expect(result.template).toBe("{ value }");
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it("handles multiple complex properties", () => {
|
|
270
|
+
const result = parseArgs(`
|
|
271
|
+
name: 'Test',
|
|
272
|
+
count: 5,
|
|
273
|
+
enabled: true,
|
|
274
|
+
config: { key: 'value' },
|
|
275
|
+
items: ['a', 'b']
|
|
276
|
+
`);
|
|
277
|
+
expect(result.name).toBe("Test");
|
|
278
|
+
expect(result.count).toBe(5);
|
|
279
|
+
expect(result.enabled).toBe(true);
|
|
280
|
+
expect(result.config).toEqual({ key: "value" });
|
|
281
|
+
expect(result.items).toEqual(["a", "b"]);
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe("parseArgsSimple", () => {
|
|
287
|
+
it("parses basic key-value pairs", () => {
|
|
288
|
+
const result = parseArgsSimple(`label: 'Hello', count: 42`);
|
|
289
|
+
expect(result.label).toBe("Hello");
|
|
290
|
+
expect(result.count).toBe(42);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("skips spread syntax (does not resolve)", () => {
|
|
294
|
+
const result = parseArgsSimple(`...defaults, label: 'Hello'`);
|
|
295
|
+
expect(result.__SPREAD__).toBeUndefined();
|
|
296
|
+
expect(result.label).toBe("Hello");
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("handles shorthand properties", () => {
|
|
300
|
+
const result = parseArgsSimple(`disabled`);
|
|
301
|
+
expect(result.disabled).toBe("__REF__disabled");
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
describe("parseArgValue", () => {
|
|
306
|
+
describe("variable resolution with constDeclarations", () => {
|
|
307
|
+
it("resolves simple identifier from constDeclarations", () => {
|
|
308
|
+
const constDeclarations = new Map<string, unknown>([
|
|
309
|
+
["myValue", "resolved"],
|
|
310
|
+
]);
|
|
311
|
+
const result = parseArgValue("myValue", constDeclarations);
|
|
312
|
+
expect(result).toBe("resolved");
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it("resolves object identifier from constDeclarations", () => {
|
|
316
|
+
const constDeclarations = new Map<string, unknown>([
|
|
317
|
+
["myObject", { a: 1, b: 2 }],
|
|
318
|
+
]);
|
|
319
|
+
const result = parseArgValue("myObject", constDeclarations);
|
|
320
|
+
expect(result).toEqual({ a: 1, b: 2 });
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
it("resolves array identifier from constDeclarations", () => {
|
|
324
|
+
const constDeclarations = new Map<string, unknown>([
|
|
325
|
+
["myArray", [1, 2, 3]],
|
|
326
|
+
]);
|
|
327
|
+
const result = parseArgValue("myArray", constDeclarations);
|
|
328
|
+
expect(result).toEqual([1, 2, 3]);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it("resolves property access from constDeclarations", () => {
|
|
332
|
+
const constDeclarations = new Map<string, unknown>([
|
|
333
|
+
["obj", { nested: { value: 42 } }],
|
|
334
|
+
]);
|
|
335
|
+
const result = parseArgValue("obj.nested.value", constDeclarations);
|
|
336
|
+
expect(result).toBe(42);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it("resolves optional chain from constDeclarations", () => {
|
|
340
|
+
const constDeclarations = new Map<string, unknown>([
|
|
341
|
+
["obj", { nested: { value: "found" } }],
|
|
342
|
+
]);
|
|
343
|
+
const result = parseArgValue("obj?.nested?.value", constDeclarations);
|
|
344
|
+
expect(result).toBe("found");
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it("returns __REF__ for unresolved identifier", () => {
|
|
348
|
+
const constDeclarations = new Map<string, unknown>([
|
|
349
|
+
["other", "value"],
|
|
350
|
+
]);
|
|
351
|
+
const result = parseArgValue("unknownVar", constDeclarations);
|
|
352
|
+
expect(result).toBe("__REF__unknownVar");
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it("returns __REF__ for unresolved property path", () => {
|
|
356
|
+
const constDeclarations = new Map<string, unknown>([
|
|
357
|
+
["obj", { a: 1 }],
|
|
358
|
+
]);
|
|
359
|
+
const result = parseArgValue("obj.b.c", constDeclarations);
|
|
360
|
+
expect(result).toBe("__REF__obj.b.c");
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it("passes constDeclarations to nested object parsing", () => {
|
|
364
|
+
const constDeclarations = new Map<string, unknown>([
|
|
365
|
+
["innerValue", { x: 10 }],
|
|
366
|
+
]);
|
|
367
|
+
const result = parseArgValue("{ prop: innerValue }", constDeclarations);
|
|
368
|
+
expect(result).toEqual({ prop: { x: 10 } });
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
it("passes constDeclarations to array parsing", () => {
|
|
372
|
+
const constDeclarations = new Map<string, unknown>([
|
|
373
|
+
["item", { id: 1 }],
|
|
374
|
+
]);
|
|
375
|
+
const result = parseArgValue("[item, item]", constDeclarations);
|
|
376
|
+
expect(result).toEqual([{ id: 1 }, { id: 1 }]);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
describe("literal values (not affected by constDeclarations)", () => {
|
|
381
|
+
it("parses string literals", () => {
|
|
382
|
+
const result = parseArgValue("'hello'");
|
|
383
|
+
expect(result).toBe("hello");
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
it("parses number literals", () => {
|
|
387
|
+
const result = parseArgValue("42");
|
|
388
|
+
expect(result).toBe(42);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
it("parses boolean literals", () => {
|
|
392
|
+
expect(parseArgValue("true")).toBe(true);
|
|
393
|
+
expect(parseArgValue("false")).toBe(false);
|
|
394
|
+
});
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
describe("extractConstDeclarations", () => {
|
|
399
|
+
it("extracts simple const object", () => {
|
|
400
|
+
const content = `
|
|
401
|
+
const defaultArgs = { size: 'medium', disabled: false };
|
|
402
|
+
`;
|
|
403
|
+
const result = extractConstDeclarations(content);
|
|
404
|
+
expect(result.get("defaultArgs")).toEqual({
|
|
405
|
+
size: "medium",
|
|
406
|
+
disabled: false,
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it("extracts typed const object", () => {
|
|
411
|
+
const content = `
|
|
412
|
+
const baseArgs: ButtonArgs = { variant: 'primary' };
|
|
413
|
+
`;
|
|
414
|
+
const result = extractConstDeclarations(content);
|
|
415
|
+
expect(result.get("baseArgs")).toEqual({ variant: "primary" });
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
it("extracts multiple const declarations", () => {
|
|
419
|
+
const content = `
|
|
420
|
+
const small = { size: 'small' };
|
|
421
|
+
const large = { size: 'large' };
|
|
422
|
+
`;
|
|
423
|
+
const result = extractConstDeclarations(content);
|
|
424
|
+
expect(result.get("small")).toEqual({ size: "small" });
|
|
425
|
+
expect(result.get("large")).toEqual({ size: "large" });
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it("skips meta and default declarations", () => {
|
|
429
|
+
const content = `
|
|
430
|
+
const meta = { title: 'Button' };
|
|
431
|
+
const default = { component: Button };
|
|
432
|
+
const args = { label: 'Click' };
|
|
433
|
+
`;
|
|
434
|
+
const result = extractConstDeclarations(content);
|
|
435
|
+
expect(result.has("meta")).toBe(false);
|
|
436
|
+
expect(result.has("default")).toBe(false);
|
|
437
|
+
expect(result.get("args")).toEqual({ label: "Click" });
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
it("handles nested objects in const declarations", () => {
|
|
441
|
+
const content = `
|
|
442
|
+
const complexArgs = {
|
|
443
|
+
style: { color: 'red' },
|
|
444
|
+
data: { items: ['a', 'b'] }
|
|
445
|
+
};
|
|
446
|
+
`;
|
|
447
|
+
const result = extractConstDeclarations(content);
|
|
448
|
+
const complexArgs = result.get("complexArgs");
|
|
449
|
+
expect(complexArgs).toBeDefined();
|
|
450
|
+
expect(complexArgs?.style).toEqual({ color: "red" });
|
|
451
|
+
});
|
|
452
|
+
});
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { __testing } from "../../parser.js";
|
|
3
|
+
|
|
4
|
+
const { parseMeta } = __testing;
|
|
5
|
+
|
|
6
|
+
describe("parseMeta", () => {
|
|
7
|
+
describe("title extraction", () => {
|
|
8
|
+
it("extracts title from single-quoted string", () => {
|
|
9
|
+
const content = `export default { title: 'Components/Button' }`;
|
|
10
|
+
const result = parseMeta(content, "Button.stories.tsx", []);
|
|
11
|
+
expect(result.title).toBe("Components/Button");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("extracts title from double-quoted string", () => {
|
|
15
|
+
const content = `export default { title: "Components/Forms/Input" }`;
|
|
16
|
+
const result = parseMeta(content, "Input.stories.tsx", []);
|
|
17
|
+
expect(result.title).toBe("Components/Forms/Input");
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("extracts title from backtick string", () => {
|
|
21
|
+
const content = `export default { title: \`Components/Modal\` }`;
|
|
22
|
+
const result = parseMeta(content, "Modal.stories.tsx", []);
|
|
23
|
+
expect(result.title).toBe("Components/Modal");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("prefers titles with slashes (Storybook paths)", () => {
|
|
27
|
+
const content = `
|
|
28
|
+
const translations = { title: 'No recent searches' };
|
|
29
|
+
export default { title: 'Components/Search' }
|
|
30
|
+
`;
|
|
31
|
+
const result = parseMeta(content, "Search.stories.tsx", []);
|
|
32
|
+
expect(result.title).toBe("Components/Search");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("extracts deeply nested title paths", () => {
|
|
36
|
+
const content = `export default { title: 'Design System/Forms/Inputs/TextField' }`;
|
|
37
|
+
const result = parseMeta(content, "TextField.stories.tsx", []);
|
|
38
|
+
expect(result.title).toBe("Design System/Forms/Inputs/TextField");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("generates fallback title from component name", () => {
|
|
42
|
+
const content = `export default { component: Button }`;
|
|
43
|
+
const warnings: string[] = [];
|
|
44
|
+
const result = parseMeta(content, "Button.stories.tsx", warnings);
|
|
45
|
+
expect(result.title).toBe("Components/Button");
|
|
46
|
+
expect(warnings).toContain("No title found, using default: Components/Button");
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe("component name extraction", () => {
|
|
51
|
+
it("extracts component name from title", () => {
|
|
52
|
+
const content = `export default { title: 'Components/Forms/Input' }`;
|
|
53
|
+
const result = parseMeta(content, "Input.stories.tsx", []);
|
|
54
|
+
expect(result.componentName).toBe("Input");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("extracts component name from component field", () => {
|
|
58
|
+
const content = `export default { component: MyButton }`;
|
|
59
|
+
const result = parseMeta(content, "Button.stories.tsx", []);
|
|
60
|
+
expect(result.componentName).toBe("MyButton");
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("prefers component name from title over component field", () => {
|
|
64
|
+
const content = `export default { title: 'Components/Button', component: ButtonComponent }`;
|
|
65
|
+
const result = parseMeta(content, "Button.stories.tsx", []);
|
|
66
|
+
expect(result.componentName).toBe("Button");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("falls back to file name when no title or component", () => {
|
|
70
|
+
const content = `export default {}`;
|
|
71
|
+
const result = parseMeta(content, "CustomButton.stories.tsx", []);
|
|
72
|
+
expect(result.componentName).toBe("CustomButton");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("handles various story file extensions", () => {
|
|
76
|
+
const content = `export default {}`;
|
|
77
|
+
|
|
78
|
+
expect(parseMeta(content, "Button.stories.ts", []).componentName).toBe("Button");
|
|
79
|
+
expect(parseMeta(content, "Button.stories.tsx", []).componentName).toBe("Button");
|
|
80
|
+
expect(parseMeta(content, "Button.stories.jsx", []).componentName).toBe("Button");
|
|
81
|
+
expect(parseMeta(content, "Button.stories.js", []).componentName).toBe("Button");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("returns Unknown and adds warning when cannot determine name", () => {
|
|
85
|
+
const content = `export default {}`;
|
|
86
|
+
const warnings: string[] = [];
|
|
87
|
+
const result = parseMeta(content, "some-random-file.ts", warnings);
|
|
88
|
+
expect(result.componentName).toBe("Unknown");
|
|
89
|
+
expect(warnings).toContain("Could not determine component name");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe("component import extraction", () => {
|
|
94
|
+
it("extracts named import path", () => {
|
|
95
|
+
const content = `
|
|
96
|
+
import { Button } from './Button';
|
|
97
|
+
export default { component: Button }
|
|
98
|
+
`;
|
|
99
|
+
const result = parseMeta(content, "Button.stories.tsx", []);
|
|
100
|
+
expect(result.componentImport).toBe("./Button");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("extracts default import path", () => {
|
|
104
|
+
const content = `
|
|
105
|
+
import Modal from '../components/Modal';
|
|
106
|
+
export default { component: Modal }
|
|
107
|
+
`;
|
|
108
|
+
const result = parseMeta(content, "Modal.stories.tsx", []);
|
|
109
|
+
expect(result.componentImport).toBe("../components/Modal");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("extracts named import from multi-import statement", () => {
|
|
113
|
+
const content = `
|
|
114
|
+
import { Button, Input, Checkbox } from '@ui/components';
|
|
115
|
+
export default { component: Input }
|
|
116
|
+
`;
|
|
117
|
+
const result = parseMeta(content, "Input.stories.tsx", []);
|
|
118
|
+
expect(result.componentImport).toBe("@ui/components");
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("handles import with alias", () => {
|
|
122
|
+
const content = `
|
|
123
|
+
import { Button as MyButton } from './buttons';
|
|
124
|
+
export default { component: MyButton }
|
|
125
|
+
`;
|
|
126
|
+
const result = parseMeta(content, "Button.stories.tsx", []);
|
|
127
|
+
// The regex matches MyButton within the import braces and extracts the import path
|
|
128
|
+
expect(result.componentImport).toBe("./buttons");
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe("tags extraction", () => {
|
|
133
|
+
it("extracts single tag", () => {
|
|
134
|
+
const content = `export default { title: 'Components/Button', tags: ['autodocs'] }`;
|
|
135
|
+
const result = parseMeta(content, "Button.stories.tsx", []);
|
|
136
|
+
expect(result.tags).toEqual(["autodocs"]);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("extracts multiple tags", () => {
|
|
140
|
+
const content = `export default { title: 'Components/Button', tags: ['autodocs', 'stable', 'beta'] }`;
|
|
141
|
+
const result = parseMeta(content, "Button.stories.tsx", []);
|
|
142
|
+
expect(result.tags).toEqual(["autodocs", "stable", "beta"]);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("handles tags with different quote styles", () => {
|
|
146
|
+
const content = `export default { tags: ["autodocs", 'stable'] }`;
|
|
147
|
+
const result = parseMeta(content, "Button.stories.tsx", []);
|
|
148
|
+
expect(result.tags).toEqual(["autodocs", "stable"]);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("returns undefined when no tags", () => {
|
|
152
|
+
const content = `export default { title: 'Components/Button' }`;
|
|
153
|
+
const result = parseMeta(content, "Button.stories.tsx", []);
|
|
154
|
+
expect(result.tags).toBeUndefined();
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe("description extraction", () => {
|
|
159
|
+
it("extracts description from parameters.docs", () => {
|
|
160
|
+
const content = `
|
|
161
|
+
export default {
|
|
162
|
+
title: 'Components/Button',
|
|
163
|
+
parameters: {
|
|
164
|
+
docs: {
|
|
165
|
+
description: {
|
|
166
|
+
component: 'A reusable button component'
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
`;
|
|
172
|
+
const result = parseMeta(content, "Button.stories.tsx", []);
|
|
173
|
+
expect(result.description).toBe("A reusable button component");
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("handles description with single quotes", () => {
|
|
177
|
+
const content = `
|
|
178
|
+
export default {
|
|
179
|
+
parameters: {
|
|
180
|
+
docs: {
|
|
181
|
+
description: {
|
|
182
|
+
component: 'Primary UI button'
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
`;
|
|
188
|
+
const result = parseMeta(content, "Button.stories.tsx", []);
|
|
189
|
+
expect(result.description).toBe("Primary UI button");
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it("returns undefined when no description", () => {
|
|
193
|
+
const content = `export default { title: 'Components/Button' }`;
|
|
194
|
+
const result = parseMeta(content, "Button.stories.tsx", []);
|
|
195
|
+
expect(result.description).toBeUndefined();
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
});
|