@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,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Types for the segments test CLI command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { PlayFunction, SegmentVariant } from '../core/index.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A test case discovered from segment files
|
|
9
|
+
*/
|
|
10
|
+
export interface TestCase {
|
|
11
|
+
/** Unique identifier for this test */
|
|
12
|
+
id: string;
|
|
13
|
+
/** Component name */
|
|
14
|
+
component: string;
|
|
15
|
+
/** Variant name */
|
|
16
|
+
variant: string;
|
|
17
|
+
/** Tags from the variant */
|
|
18
|
+
tags: string[];
|
|
19
|
+
/** The play function to execute */
|
|
20
|
+
play: PlayFunction;
|
|
21
|
+
/** Source file path */
|
|
22
|
+
sourceFile: string;
|
|
23
|
+
/** Story ID for navigation */
|
|
24
|
+
storyId?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Result of a single step within a test
|
|
29
|
+
*/
|
|
30
|
+
export interface StepResult {
|
|
31
|
+
/** Step name */
|
|
32
|
+
name: string;
|
|
33
|
+
/** Step status */
|
|
34
|
+
status: 'passed' | 'failed' | 'skipped';
|
|
35
|
+
/** Duration in milliseconds */
|
|
36
|
+
duration: number;
|
|
37
|
+
/** Error if step failed */
|
|
38
|
+
error?: TestError;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Error information from a failed test
|
|
43
|
+
*/
|
|
44
|
+
export interface TestError {
|
|
45
|
+
/** Error message */
|
|
46
|
+
message: string;
|
|
47
|
+
/** Stack trace */
|
|
48
|
+
stack?: string;
|
|
49
|
+
/** Assertion details if applicable */
|
|
50
|
+
expected?: unknown;
|
|
51
|
+
/** Actual value if applicable */
|
|
52
|
+
actual?: unknown;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Accessibility violation from axe-core
|
|
57
|
+
*/
|
|
58
|
+
export interface A11yViolation {
|
|
59
|
+
/** Violation ID */
|
|
60
|
+
id: string;
|
|
61
|
+
/** Impact level */
|
|
62
|
+
impact: 'critical' | 'serious' | 'moderate' | 'minor';
|
|
63
|
+
/** Description */
|
|
64
|
+
description: string;
|
|
65
|
+
/** Help text */
|
|
66
|
+
help: string;
|
|
67
|
+
/** Help URL */
|
|
68
|
+
helpUrl: string;
|
|
69
|
+
/** Affected nodes */
|
|
70
|
+
nodes: Array<{
|
|
71
|
+
html: string;
|
|
72
|
+
target: string[];
|
|
73
|
+
}>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Accessibility check result
|
|
78
|
+
*/
|
|
79
|
+
export interface A11yResult {
|
|
80
|
+
/** Number of violations */
|
|
81
|
+
violations: A11yViolation[];
|
|
82
|
+
/** Number of passing checks */
|
|
83
|
+
passes: number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Result of a single test case execution
|
|
88
|
+
*/
|
|
89
|
+
export interface TestResult {
|
|
90
|
+
/** The test case that was run */
|
|
91
|
+
testCase: TestCase;
|
|
92
|
+
/** Overall test status */
|
|
93
|
+
status: 'passed' | 'failed' | 'skipped';
|
|
94
|
+
/** Total duration in milliseconds */
|
|
95
|
+
duration: number;
|
|
96
|
+
/** Results of individual steps */
|
|
97
|
+
steps: StepResult[];
|
|
98
|
+
/** Error if test failed */
|
|
99
|
+
error?: TestError;
|
|
100
|
+
/** Accessibility check result */
|
|
101
|
+
accessibility?: A11yResult;
|
|
102
|
+
/** Screenshot path for visual regression */
|
|
103
|
+
screenshotPath?: string;
|
|
104
|
+
/** Retry attempt number (0 = first attempt) */
|
|
105
|
+
retryAttempt?: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* A test suite groups test results by component
|
|
110
|
+
*/
|
|
111
|
+
export interface TestSuite {
|
|
112
|
+
/** Component name */
|
|
113
|
+
name: string;
|
|
114
|
+
/** Test results in this suite */
|
|
115
|
+
tests: TestResult[];
|
|
116
|
+
/** Total duration for the suite */
|
|
117
|
+
duration: number;
|
|
118
|
+
/** Number of passed tests */
|
|
119
|
+
passed: number;
|
|
120
|
+
/** Number of failed tests */
|
|
121
|
+
failed: number;
|
|
122
|
+
/** Number of skipped tests */
|
|
123
|
+
skipped: number;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Overall result of a test run
|
|
128
|
+
*/
|
|
129
|
+
export interface TestRunResult {
|
|
130
|
+
/** Test suites organized by component */
|
|
131
|
+
suites: TestSuite[];
|
|
132
|
+
/** Total number of tests */
|
|
133
|
+
totalTests: number;
|
|
134
|
+
/** Number of passed tests */
|
|
135
|
+
totalPassed: number;
|
|
136
|
+
/** Number of failed tests */
|
|
137
|
+
totalFailed: number;
|
|
138
|
+
/** Number of skipped tests */
|
|
139
|
+
totalSkipped: number;
|
|
140
|
+
/** Total duration in milliseconds */
|
|
141
|
+
totalDuration: number;
|
|
142
|
+
/** Start timestamp */
|
|
143
|
+
startTime: Date;
|
|
144
|
+
/** End timestamp */
|
|
145
|
+
endTime: Date;
|
|
146
|
+
/** Total a11y violations across all tests */
|
|
147
|
+
totalA11yViolations?: number;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Options for test discovery
|
|
152
|
+
*/
|
|
153
|
+
export interface DiscoveryOptions {
|
|
154
|
+
/** Filter by component name */
|
|
155
|
+
component?: string;
|
|
156
|
+
/** Filter by tags (comma-separated) */
|
|
157
|
+
tags?: string[];
|
|
158
|
+
/** Grep pattern to match variant names */
|
|
159
|
+
grep?: string;
|
|
160
|
+
/** Exclude pattern */
|
|
161
|
+
exclude?: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Options for test execution
|
|
166
|
+
*/
|
|
167
|
+
export interface RunnerOptions {
|
|
168
|
+
/** Number of parallel browser contexts */
|
|
169
|
+
parallel: number;
|
|
170
|
+
/** Timeout per test in milliseconds */
|
|
171
|
+
timeout: number;
|
|
172
|
+
/** Number of retries for failed tests */
|
|
173
|
+
retries: number;
|
|
174
|
+
/** Stop on first failure */
|
|
175
|
+
bail: boolean;
|
|
176
|
+
/** Run accessibility checks */
|
|
177
|
+
a11y: boolean;
|
|
178
|
+
/** Capture screenshots for visual regression */
|
|
179
|
+
visual: boolean;
|
|
180
|
+
/** Update visual snapshots */
|
|
181
|
+
updateSnapshots: boolean;
|
|
182
|
+
/** Output directory for results */
|
|
183
|
+
outputDir: string;
|
|
184
|
+
/** Browser to use */
|
|
185
|
+
browser: 'chromium' | 'firefox' | 'webkit';
|
|
186
|
+
/** Headless mode */
|
|
187
|
+
headless: boolean;
|
|
188
|
+
/** Dev server URL (if already running) */
|
|
189
|
+
serverUrl?: string;
|
|
190
|
+
/** Port for dev server */
|
|
191
|
+
port: number;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Reporter interface for outputting test results
|
|
196
|
+
*/
|
|
197
|
+
export interface TestReporter {
|
|
198
|
+
/** Called when test run starts */
|
|
199
|
+
onRunStart?(testCount: number): void;
|
|
200
|
+
/** Called when a test starts */
|
|
201
|
+
onTestStart?(testCase: TestCase): void;
|
|
202
|
+
/** Called when a test completes */
|
|
203
|
+
onTestComplete?(result: TestResult): void;
|
|
204
|
+
/** Called when test run completes */
|
|
205
|
+
onRunComplete(result: TestRunResult): Promise<void> | void;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Configuration for the test command
|
|
210
|
+
*/
|
|
211
|
+
export interface TestConfig {
|
|
212
|
+
/** Discovery options */
|
|
213
|
+
discovery: DiscoveryOptions;
|
|
214
|
+
/** Runner options */
|
|
215
|
+
runner: RunnerOptions;
|
|
216
|
+
/** Reporter names */
|
|
217
|
+
reporters: string[];
|
|
218
|
+
/** Watch mode */
|
|
219
|
+
watch: boolean;
|
|
220
|
+
/** CI mode (non-interactive, exit codes) */
|
|
221
|
+
ci: boolean;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Event emitted during test execution
|
|
226
|
+
*/
|
|
227
|
+
export type TestEvent =
|
|
228
|
+
| { type: 'run-start'; testCount: number }
|
|
229
|
+
| { type: 'test-start'; testCase: TestCase }
|
|
230
|
+
| { type: 'test-complete'; result: TestResult }
|
|
231
|
+
| { type: 'run-complete'; result: TestRunResult };
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Browser pool context for parallel execution
|
|
235
|
+
*/
|
|
236
|
+
export interface BrowserContext {
|
|
237
|
+
/** Context ID */
|
|
238
|
+
id: number;
|
|
239
|
+
/** Whether context is currently in use */
|
|
240
|
+
busy: boolean;
|
|
241
|
+
/** Playwright browser context */
|
|
242
|
+
context: unknown; // Playwright BrowserContext
|
|
243
|
+
/** Page instance */
|
|
244
|
+
page: unknown; // Playwright Page
|
|
245
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Watch mode - file watcher with selective re-run
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { watch } from 'node:fs';
|
|
6
|
+
import { resolve, relative } from 'node:path';
|
|
7
|
+
import pc from 'picocolors';
|
|
8
|
+
import type { SegmentsConfig } from '../core/index.js';
|
|
9
|
+
import { discoverSegmentFiles } from '../core/node.js';
|
|
10
|
+
import type { TestCase, RunnerOptions, TestReporter } from './types.js';
|
|
11
|
+
import { discoverTests } from './discovery.js';
|
|
12
|
+
import { runTests } from './runner.js';
|
|
13
|
+
|
|
14
|
+
export interface WatchOptions {
|
|
15
|
+
/** Debounce delay in milliseconds */
|
|
16
|
+
debounceMs?: number;
|
|
17
|
+
/** Clear console on each run */
|
|
18
|
+
clearConsole?: boolean;
|
|
19
|
+
/** Run all tests initially */
|
|
20
|
+
runOnStart?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Start watch mode
|
|
25
|
+
*/
|
|
26
|
+
export async function startWatchMode(
|
|
27
|
+
config: SegmentsConfig,
|
|
28
|
+
configDir: string,
|
|
29
|
+
runnerOptions: RunnerOptions,
|
|
30
|
+
reporters: TestReporter[],
|
|
31
|
+
options: WatchOptions = {}
|
|
32
|
+
): Promise<void> {
|
|
33
|
+
const { debounceMs = 300, clearConsole = true, runOnStart = true } = options;
|
|
34
|
+
|
|
35
|
+
let debounceTimer: NodeJS.Timeout | null = null;
|
|
36
|
+
let isRunning = false;
|
|
37
|
+
let pendingFiles = new Set<string>();
|
|
38
|
+
|
|
39
|
+
// Get files to watch
|
|
40
|
+
const segmentFiles = await discoverSegmentFiles(config, configDir);
|
|
41
|
+
const watchPaths = new Set<string>();
|
|
42
|
+
|
|
43
|
+
for (const file of segmentFiles) {
|
|
44
|
+
watchPaths.add(file.absolutePath);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Also watch the directories containing segment files
|
|
48
|
+
const watchDirs = new Set<string>();
|
|
49
|
+
for (const path of watchPaths) {
|
|
50
|
+
const dir = resolve(path, '..');
|
|
51
|
+
watchDirs.add(dir);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
console.log();
|
|
55
|
+
console.log(pc.cyan(pc.bold('Segments Test Runner - Watch Mode')));
|
|
56
|
+
console.log(pc.dim(`Watching ${watchPaths.size} files in ${watchDirs.size} directories`));
|
|
57
|
+
console.log(pc.dim('Press Ctrl+C to stop'));
|
|
58
|
+
console.log();
|
|
59
|
+
|
|
60
|
+
// Function to run tests
|
|
61
|
+
const runTestsForFiles = async (changedFiles: string[]): Promise<void> => {
|
|
62
|
+
if (isRunning) {
|
|
63
|
+
// Queue up changed files for next run
|
|
64
|
+
for (const file of changedFiles) {
|
|
65
|
+
pendingFiles.add(file);
|
|
66
|
+
}
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
isRunning = true;
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
if (clearConsole) {
|
|
74
|
+
console.clear();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log(pc.cyan('Changes detected, running tests...'));
|
|
78
|
+
console.log(pc.dim(`Changed: ${changedFiles.map((f) => relative(configDir, f)).join(', ')}`));
|
|
79
|
+
console.log();
|
|
80
|
+
|
|
81
|
+
// Discover tests (filter to changed files if possible)
|
|
82
|
+
const allTests = await discoverTests(config, configDir, {});
|
|
83
|
+
|
|
84
|
+
// Filter to tests from changed files
|
|
85
|
+
const changedRelativePaths = changedFiles.map((f) => relative(configDir, f));
|
|
86
|
+
const testsToRun = changedRelativePaths.length > 0
|
|
87
|
+
? allTests.filter((t) => changedRelativePaths.some((p) => t.sourceFile.includes(p)))
|
|
88
|
+
: allTests;
|
|
89
|
+
|
|
90
|
+
if (testsToRun.length === 0) {
|
|
91
|
+
console.log(pc.yellow('No tests found in changed files'));
|
|
92
|
+
console.log();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Run the tests
|
|
97
|
+
await runTests(testsToRun, runnerOptions, reporters);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error(pc.red('Error running tests:'), error);
|
|
100
|
+
} finally {
|
|
101
|
+
isRunning = false;
|
|
102
|
+
|
|
103
|
+
// Check if there are pending files to run
|
|
104
|
+
if (pendingFiles.size > 0) {
|
|
105
|
+
const files = Array.from(pendingFiles);
|
|
106
|
+
pendingFiles.clear();
|
|
107
|
+
await runTestsForFiles(files);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Run initial tests if configured
|
|
113
|
+
if (runOnStart) {
|
|
114
|
+
const allTests = await discoverTests(config, configDir, {});
|
|
115
|
+
if (allTests.length > 0) {
|
|
116
|
+
await runTests(allTests, runnerOptions, reporters);
|
|
117
|
+
} else {
|
|
118
|
+
console.log(pc.yellow('No tests with play functions found'));
|
|
119
|
+
}
|
|
120
|
+
console.log();
|
|
121
|
+
console.log(pc.dim('Watching for changes...'));
|
|
122
|
+
console.log();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Set up file watchers
|
|
126
|
+
const watchers: ReturnType<typeof watch>[] = [];
|
|
127
|
+
|
|
128
|
+
for (const dir of watchDirs) {
|
|
129
|
+
try {
|
|
130
|
+
const watcher = watch(dir, { recursive: false }, (eventType, filename) => {
|
|
131
|
+
if (!filename) return;
|
|
132
|
+
|
|
133
|
+
const fullPath = resolve(dir, filename);
|
|
134
|
+
|
|
135
|
+
// Check if this is a segment file we care about
|
|
136
|
+
if (!watchPaths.has(fullPath)) return;
|
|
137
|
+
|
|
138
|
+
// Debounce the run
|
|
139
|
+
if (debounceTimer) {
|
|
140
|
+
clearTimeout(debounceTimer);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
pendingFiles.add(fullPath);
|
|
144
|
+
|
|
145
|
+
debounceTimer = setTimeout(() => {
|
|
146
|
+
const files = Array.from(pendingFiles);
|
|
147
|
+
pendingFiles.clear();
|
|
148
|
+
runTestsForFiles(files);
|
|
149
|
+
}, debounceMs);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
watchers.push(watcher);
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.warn(pc.yellow(`Warning: Could not watch directory ${dir}`), error);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Handle process termination
|
|
159
|
+
const cleanup = () => {
|
|
160
|
+
console.log();
|
|
161
|
+
console.log(pc.dim('Stopping watch mode...'));
|
|
162
|
+
|
|
163
|
+
for (const watcher of watchers) {
|
|
164
|
+
watcher.close();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (debounceTimer) {
|
|
168
|
+
clearTimeout(debounceTimer);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
process.exit(0);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
process.on('SIGINT', cleanup);
|
|
175
|
+
process.on('SIGTERM', cleanup);
|
|
176
|
+
|
|
177
|
+
// Keep process alive
|
|
178
|
+
await new Promise(() => {
|
|
179
|
+
// This promise never resolves, keeping the process alive
|
|
180
|
+
// until terminated
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Interactive watch mode with keyboard controls
|
|
186
|
+
*/
|
|
187
|
+
export async function startInteractiveWatchMode(
|
|
188
|
+
config: SegmentsConfig,
|
|
189
|
+
configDir: string,
|
|
190
|
+
runnerOptions: RunnerOptions,
|
|
191
|
+
reporters: TestReporter[]
|
|
192
|
+
): Promise<void> {
|
|
193
|
+
let lastFailedTests: TestCase[] = [];
|
|
194
|
+
|
|
195
|
+
// Start basic watch mode
|
|
196
|
+
await startWatchMode(config, configDir, runnerOptions, reporters, {
|
|
197
|
+
runOnStart: true,
|
|
198
|
+
clearConsole: true,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { segmentDefinitionSchema, BRAND, type SegmentsConfig } from './core/index.js';
|
|
2
|
+
import {
|
|
3
|
+
discoverSegmentFiles,
|
|
4
|
+
discoverComponentFiles,
|
|
5
|
+
extractComponentName,
|
|
6
|
+
loadSegmentFile,
|
|
7
|
+
type DiscoveredFile,
|
|
8
|
+
} from './core/node.js';
|
|
9
|
+
|
|
10
|
+
export interface ValidationResult {
|
|
11
|
+
valid: boolean;
|
|
12
|
+
errors: ValidationError[];
|
|
13
|
+
warnings: ValidationWarning[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ValidationError {
|
|
17
|
+
file: string;
|
|
18
|
+
message: string;
|
|
19
|
+
details?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ValidationWarning {
|
|
23
|
+
file: string;
|
|
24
|
+
message: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Validate segment file schema
|
|
29
|
+
*/
|
|
30
|
+
export async function validateSchema(
|
|
31
|
+
config: SegmentsConfig,
|
|
32
|
+
configDir: string
|
|
33
|
+
): Promise<ValidationResult> {
|
|
34
|
+
const files = await discoverSegmentFiles(config, configDir);
|
|
35
|
+
const errors: ValidationError[] = [];
|
|
36
|
+
const warnings: ValidationWarning[] = [];
|
|
37
|
+
|
|
38
|
+
for (const file of files) {
|
|
39
|
+
try {
|
|
40
|
+
const segment = await loadSegmentFile(file.absolutePath);
|
|
41
|
+
|
|
42
|
+
if (!segment) {
|
|
43
|
+
errors.push({
|
|
44
|
+
file: file.relativePath,
|
|
45
|
+
message: 'No default export found',
|
|
46
|
+
details: `Segment files must have a default export from defineSegment()`,
|
|
47
|
+
});
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const result = segmentDefinitionSchema.safeParse(segment);
|
|
52
|
+
|
|
53
|
+
if (!result.success) {
|
|
54
|
+
const details = result.error.errors
|
|
55
|
+
.map((e) => `${e.path.join('.')}: ${e.message}`)
|
|
56
|
+
.join('; ');
|
|
57
|
+
|
|
58
|
+
errors.push({
|
|
59
|
+
file: file.relativePath,
|
|
60
|
+
message: 'Invalid segment schema',
|
|
61
|
+
details,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
errors.push({
|
|
66
|
+
file: file.relativePath,
|
|
67
|
+
message: 'Failed to load segment file',
|
|
68
|
+
details: error instanceof Error ? error.message : String(error),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
valid: errors.length === 0,
|
|
75
|
+
errors,
|
|
76
|
+
warnings,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Validate coverage - ensure all components have segments
|
|
82
|
+
*/
|
|
83
|
+
export async function validateCoverage(
|
|
84
|
+
config: SegmentsConfig,
|
|
85
|
+
configDir: string
|
|
86
|
+
): Promise<ValidationResult> {
|
|
87
|
+
const segmentFiles = await discoverSegmentFiles(config, configDir);
|
|
88
|
+
const componentFiles = await discoverComponentFiles(config, configDir);
|
|
89
|
+
const errors: ValidationError[] = [];
|
|
90
|
+
const warnings: ValidationWarning[] = [];
|
|
91
|
+
|
|
92
|
+
// Build set of documented component names
|
|
93
|
+
const documentedComponents = new Set<string>();
|
|
94
|
+
|
|
95
|
+
for (const file of segmentFiles) {
|
|
96
|
+
try {
|
|
97
|
+
const segment = await loadSegmentFile(file.absolutePath);
|
|
98
|
+
|
|
99
|
+
if (segment?.meta?.name) {
|
|
100
|
+
documentedComponents.add(segment.meta.name);
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
// Skip files that fail to load - schema validation will catch these
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Check each component file
|
|
108
|
+
for (const file of componentFiles) {
|
|
109
|
+
const componentName = extractComponentName(file.relativePath);
|
|
110
|
+
|
|
111
|
+
// Check if there's a corresponding segment file
|
|
112
|
+
const segmentPath = file.relativePath.replace(
|
|
113
|
+
/\.(tsx?|jsx?)$/,
|
|
114
|
+
BRAND.fileExtension
|
|
115
|
+
);
|
|
116
|
+
const hasSegmentFile = segmentFiles.some(
|
|
117
|
+
(s) => s.relativePath === segmentPath
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
if (!hasSegmentFile && !documentedComponents.has(componentName)) {
|
|
121
|
+
warnings.push({
|
|
122
|
+
file: file.relativePath,
|
|
123
|
+
message: `Component "${componentName}" has no segment documentation`,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
valid: errors.length === 0,
|
|
130
|
+
errors,
|
|
131
|
+
warnings,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Run all validations
|
|
137
|
+
*/
|
|
138
|
+
export async function validateAll(
|
|
139
|
+
config: SegmentsConfig,
|
|
140
|
+
configDir: string
|
|
141
|
+
): Promise<ValidationResult> {
|
|
142
|
+
const [schemaResult, coverageResult] = await Promise.all([
|
|
143
|
+
validateSchema(config, configDir),
|
|
144
|
+
validateCoverage(config, configDir),
|
|
145
|
+
]);
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
valid: schemaResult.valid && coverageResult.valid,
|
|
149
|
+
errors: [...schemaResult.errors, ...coverageResult.errors],
|
|
150
|
+
warnings: [...schemaResult.warnings, ...coverageResult.warnings],
|
|
151
|
+
};
|
|
152
|
+
}
|