@mdxui/terminal 2.0.0

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.
Files changed (191) hide show
  1. package/README.md +571 -0
  2. package/dist/ansi-css-Sk5mWtdK.d.ts +119 -0
  3. package/dist/ansi-css-V6JIHGsM.d.ts +119 -0
  4. package/dist/ansi-css-_3eSEU9d.d.ts +119 -0
  5. package/dist/chunk-3EFDH7PK.js +5235 -0
  6. package/dist/chunk-3RG5ZIWI.js +10 -0
  7. package/dist/chunk-3X5IR6WE.js +884 -0
  8. package/dist/chunk-4FV5ZDCE.js +5236 -0
  9. package/dist/chunk-4OVMSF2J.js +243 -0
  10. package/dist/chunk-63FEETIS.js +4048 -0
  11. package/dist/chunk-B43KP7XJ.js +884 -0
  12. package/dist/chunk-BMTJXWUV.js +655 -0
  13. package/dist/chunk-C3SVH4N7.js +882 -0
  14. package/dist/chunk-EVWR7Y47.js +874 -0
  15. package/dist/chunk-F6A5VWUC.js +1285 -0
  16. package/dist/chunk-FD7KW7GE.js +882 -0
  17. package/dist/chunk-GBQ6UD6I.js +655 -0
  18. package/dist/chunk-GMDD3M6U.js +5227 -0
  19. package/dist/chunk-JBHRXOXM.js +1058 -0
  20. package/dist/chunk-JFOO3EYO.js +1182 -0
  21. package/dist/chunk-JQ5H3WXL.js +1291 -0
  22. package/dist/chunk-JQD5NASE.js +234 -0
  23. package/dist/chunk-KRHJP5R7.js +592 -0
  24. package/dist/chunk-KWF6WVJE.js +962 -0
  25. package/dist/chunk-LHYQVN3H.js +1038 -0
  26. package/dist/chunk-M3TLQLGC.js +1032 -0
  27. package/dist/chunk-MVW4Q5OP.js +240 -0
  28. package/dist/chunk-NXCZSWLU.js +1294 -0
  29. package/dist/chunk-O25TNRO6.js +607 -0
  30. package/dist/chunk-PNECDA2I.js +884 -0
  31. package/dist/chunk-QIHWRLJR.js +962 -0
  32. package/dist/chunk-QW5YMQ7K.js +882 -0
  33. package/dist/chunk-R5U7XKVJ.js +16 -0
  34. package/dist/chunk-RP2MVQLR.js +962 -0
  35. package/dist/chunk-TP6RXGXA.js +1087 -0
  36. package/dist/chunk-TQQSTITZ.js +655 -0
  37. package/dist/chunk-X24GWXQV.js +1281 -0
  38. package/dist/components/index.d.ts +802 -0
  39. package/dist/components/index.js +149 -0
  40. package/dist/data/index.d.ts +2554 -0
  41. package/dist/data/index.js +51 -0
  42. package/dist/forms/index.d.ts +1596 -0
  43. package/dist/forms/index.js +464 -0
  44. package/dist/index-CQRFZntR.d.ts +867 -0
  45. package/dist/index.d.ts +579 -0
  46. package/dist/index.js +786 -0
  47. package/dist/interactive-D0JkWosD.d.ts +217 -0
  48. package/dist/keyboard/index.d.ts +2 -0
  49. package/dist/keyboard/index.js +43 -0
  50. package/dist/renderers/index.d.ts +546 -0
  51. package/dist/renderers/index.js +2157 -0
  52. package/dist/storybook/index.d.ts +396 -0
  53. package/dist/storybook/index.js +641 -0
  54. package/dist/theme/index.d.ts +1339 -0
  55. package/dist/theme/index.js +123 -0
  56. package/dist/types-Bxu5PAgA.d.ts +710 -0
  57. package/dist/types-CIlop5Ji.d.ts +701 -0
  58. package/dist/types-Ca8p_p5X.d.ts +710 -0
  59. package/package.json +90 -0
  60. package/src/__tests__/components/data/card.test.ts +458 -0
  61. package/src/__tests__/components/data/list.test.ts +473 -0
  62. package/src/__tests__/components/data/metrics.test.ts +541 -0
  63. package/src/__tests__/components/data/table.test.ts +448 -0
  64. package/src/__tests__/components/input/field.test.ts +555 -0
  65. package/src/__tests__/components/input/form.test.ts +870 -0
  66. package/src/__tests__/components/input/search.test.ts +1238 -0
  67. package/src/__tests__/components/input/select.test.ts +658 -0
  68. package/src/__tests__/components/navigation/breadcrumb.test.ts +923 -0
  69. package/src/__tests__/components/navigation/command-palette.test.ts +1095 -0
  70. package/src/__tests__/components/navigation/sidebar.test.ts +1018 -0
  71. package/src/__tests__/components/navigation/tabs.test.ts +995 -0
  72. package/src/__tests__/components.test.tsx +1197 -0
  73. package/src/__tests__/core/compiler.test.ts +986 -0
  74. package/src/__tests__/core/parser.test.ts +785 -0
  75. package/src/__tests__/core/tier-switcher.test.ts +1103 -0
  76. package/src/__tests__/core/types.test.ts +1398 -0
  77. package/src/__tests__/data/collections.test.ts +1337 -0
  78. package/src/__tests__/data/db.test.ts +1265 -0
  79. package/src/__tests__/data/reactive.test.ts +1010 -0
  80. package/src/__tests__/data/sync.test.ts +1614 -0
  81. package/src/__tests__/errors.test.ts +660 -0
  82. package/src/__tests__/forms/integration.test.ts +444 -0
  83. package/src/__tests__/integration.test.ts +905 -0
  84. package/src/__tests__/keyboard.test.ts +1791 -0
  85. package/src/__tests__/renderer.test.ts +489 -0
  86. package/src/__tests__/renderers/ansi-css.test.ts +948 -0
  87. package/src/__tests__/renderers/ansi.test.ts +1366 -0
  88. package/src/__tests__/renderers/ascii.test.ts +1360 -0
  89. package/src/__tests__/renderers/interactive.test.ts +2353 -0
  90. package/src/__tests__/renderers/markdown.test.ts +1483 -0
  91. package/src/__tests__/renderers/text.test.ts +1369 -0
  92. package/src/__tests__/renderers/unicode.test.ts +1307 -0
  93. package/src/__tests__/theme.test.ts +639 -0
  94. package/src/__tests__/utils/assertions.ts +685 -0
  95. package/src/__tests__/utils/index.ts +115 -0
  96. package/src/__tests__/utils/test-renderer.ts +381 -0
  97. package/src/__tests__/utils/utils.test.ts +560 -0
  98. package/src/components/containers/card.ts +56 -0
  99. package/src/components/containers/dialog.ts +53 -0
  100. package/src/components/containers/index.ts +9 -0
  101. package/src/components/containers/panel.ts +59 -0
  102. package/src/components/feedback/badge.ts +40 -0
  103. package/src/components/feedback/index.ts +8 -0
  104. package/src/components/feedback/spinner.ts +23 -0
  105. package/src/components/helpers.ts +81 -0
  106. package/src/components/index.ts +153 -0
  107. package/src/components/layout/breadcrumb.ts +31 -0
  108. package/src/components/layout/index.ts +10 -0
  109. package/src/components/layout/list.ts +29 -0
  110. package/src/components/layout/sidebar.ts +79 -0
  111. package/src/components/layout/table.ts +62 -0
  112. package/src/components/primitives/box.ts +95 -0
  113. package/src/components/primitives/button.ts +54 -0
  114. package/src/components/primitives/index.ts +11 -0
  115. package/src/components/primitives/input.ts +88 -0
  116. package/src/components/primitives/select.ts +97 -0
  117. package/src/components/primitives/text.ts +60 -0
  118. package/src/components/render.ts +155 -0
  119. package/src/components/templates/app.ts +43 -0
  120. package/src/components/templates/index.ts +8 -0
  121. package/src/components/templates/site.ts +54 -0
  122. package/src/components/types.ts +777 -0
  123. package/src/core/compiler.ts +718 -0
  124. package/src/core/parser.ts +127 -0
  125. package/src/core/tier-switcher.ts +607 -0
  126. package/src/core/types.ts +672 -0
  127. package/src/data/collection.ts +316 -0
  128. package/src/data/collections.ts +50 -0
  129. package/src/data/context.tsx +174 -0
  130. package/src/data/db.ts +127 -0
  131. package/src/data/hooks.ts +532 -0
  132. package/src/data/index.ts +138 -0
  133. package/src/data/reactive.ts +1225 -0
  134. package/src/data/saas-collections.ts +375 -0
  135. package/src/data/sync.ts +1213 -0
  136. package/src/data/types.ts +660 -0
  137. package/src/forms/converters.ts +512 -0
  138. package/src/forms/index.ts +133 -0
  139. package/src/forms/schemas.ts +403 -0
  140. package/src/forms/types.ts +476 -0
  141. package/src/index.ts +542 -0
  142. package/src/keyboard/focus.ts +748 -0
  143. package/src/keyboard/index.ts +96 -0
  144. package/src/keyboard/integration.ts +371 -0
  145. package/src/keyboard/manager.ts +377 -0
  146. package/src/keyboard/presets.ts +90 -0
  147. package/src/renderers/ansi-css.ts +576 -0
  148. package/src/renderers/ansi.ts +802 -0
  149. package/src/renderers/ascii.ts +680 -0
  150. package/src/renderers/breadcrumb.ts +480 -0
  151. package/src/renderers/command-palette.ts +802 -0
  152. package/src/renderers/components/field.ts +210 -0
  153. package/src/renderers/components/form.ts +327 -0
  154. package/src/renderers/components/index.ts +21 -0
  155. package/src/renderers/components/search.ts +449 -0
  156. package/src/renderers/components/select.ts +222 -0
  157. package/src/renderers/index.ts +101 -0
  158. package/src/renderers/interactive/component-handlers.ts +622 -0
  159. package/src/renderers/interactive/cursor-manager.ts +147 -0
  160. package/src/renderers/interactive/focus-manager.ts +279 -0
  161. package/src/renderers/interactive/index.ts +661 -0
  162. package/src/renderers/interactive/input-handler.ts +164 -0
  163. package/src/renderers/interactive/keyboard-handler.ts +212 -0
  164. package/src/renderers/interactive/mouse-handler.ts +167 -0
  165. package/src/renderers/interactive/state-manager.ts +109 -0
  166. package/src/renderers/interactive/types.ts +338 -0
  167. package/src/renderers/interactive-string.ts +299 -0
  168. package/src/renderers/interactive.ts +59 -0
  169. package/src/renderers/markdown.ts +950 -0
  170. package/src/renderers/sidebar.ts +549 -0
  171. package/src/renderers/tabs.ts +682 -0
  172. package/src/renderers/text.ts +791 -0
  173. package/src/renderers/unicode.ts +917 -0
  174. package/src/renderers/utils.ts +942 -0
  175. package/src/router/adapters.ts +383 -0
  176. package/src/router/types.ts +140 -0
  177. package/src/router/utils.ts +452 -0
  178. package/src/schemas.ts +205 -0
  179. package/src/storybook/index.ts +91 -0
  180. package/src/storybook/interactive-decorator.tsx +659 -0
  181. package/src/storybook/keyboard-simulator.ts +501 -0
  182. package/src/theme/ansi-codes.ts +80 -0
  183. package/src/theme/box-drawing.ts +132 -0
  184. package/src/theme/color-convert.ts +254 -0
  185. package/src/theme/color-support.ts +321 -0
  186. package/src/theme/index.ts +134 -0
  187. package/src/theme/strip-ansi.ts +50 -0
  188. package/src/theme/tailwind-map.ts +469 -0
  189. package/src/theme/text-styles.ts +206 -0
  190. package/src/theme/theme-system.ts +568 -0
  191. package/src/types.ts +103 -0
@@ -0,0 +1,115 @@
1
+ /**
2
+ * @mdxui/terminal Test Utilities
3
+ *
4
+ * Comprehensive test utilities for testing terminal components.
5
+ * This module provides:
6
+ *
7
+ * - **Mock Renderer**: Test components without a real terminal
8
+ * - **Assertions**: Terminal-aware assertion helpers
9
+ * - **Snapshot Utilities**: Prepare output for snapshot testing
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * import {
14
+ * createTestRenderer,
15
+ * renderToLines,
16
+ * expectTerminalContains,
17
+ * expectBoxDrawing,
18
+ * expectStyle,
19
+ * toSnapshot,
20
+ * } from '../utils'
21
+ *
22
+ * describe('MyComponent', () => {
23
+ * it('renders correctly', () => {
24
+ * const output = renderToLines(<Box border="single"><Text>Hello</Text></Box>)
25
+ *
26
+ * expectTerminalContains(output, 'Hello')
27
+ * expectBoxDrawing(output, 'single')
28
+ * expect(toSnapshot(output)).toMatchSnapshot()
29
+ * })
30
+ *
31
+ * it('applies styling', () => {
32
+ * const output = renderToLines(<Text bold color="red">Error</Text>)
33
+ *
34
+ * expectStyle(output, 'bold')
35
+ * expectForegroundColor(output, 'red')
36
+ * })
37
+ * })
38
+ * ```
39
+ *
40
+ * @packageDocumentation
41
+ */
42
+
43
+ // ============================================================================
44
+ // Mock Renderer
45
+ // ============================================================================
46
+
47
+ export {
48
+ // Core renderer
49
+ createTestRenderer,
50
+ createTestRoot,
51
+ // Convenience functions
52
+ renderToLines,
53
+ renderToString,
54
+ prepareSnapshot,
55
+ // Mock factory functions (for vi.mock)
56
+ mockCreateCliRenderer,
57
+ mockCreateRoot,
58
+ // Types
59
+ type TestRenderer,
60
+ type TestRendererConfig,
61
+ type TestRoot,
62
+ type SnapshotOptions,
63
+ type RenderContext,
64
+ } from './test-renderer'
65
+
66
+ // ============================================================================
67
+ // Assertions
68
+ // ============================================================================
69
+
70
+ export {
71
+ // ANSI utilities
72
+ stripAnsi,
73
+ hasAnsiCodes,
74
+ visibleLength,
75
+ extractAnsiCodes,
76
+ getPlainContent,
77
+ // Content assertions
78
+ expectTerminalContains,
79
+ expectTerminalNotContains,
80
+ expectTerminalMatches,
81
+ expectLineContains,
82
+ expectLineCount,
83
+ expectMinLines,
84
+ // ANSI style assertions
85
+ expectAnsiCode,
86
+ expectNoAnsiCode,
87
+ expectStyle,
88
+ expectForegroundColor,
89
+ expectBackgroundColor,
90
+ expectReset,
91
+ expectAnsi256Color,
92
+ expectTrueColor,
93
+ // Box drawing assertions
94
+ expectBoxDrawing,
95
+ expectHorizontalLine,
96
+ expectVerticalLine,
97
+ expectNoBoxDrawing,
98
+ // Dimension assertions
99
+ expectMaxWidth,
100
+ expectExactWidth,
101
+ expectFitsDimensions,
102
+ // Snapshot utilities
103
+ toSnapshot,
104
+ terminalOutputEquals,
105
+ expectTerminalEquals,
106
+ // Component-specific assertions
107
+ expectCursor,
108
+ expectFocused,
109
+ expectDisabled,
110
+ expectSelected,
111
+ // Types
112
+ type AnsiStyleName,
113
+ type AnsiColorName,
114
+ type AnsiBgColorName,
115
+ } from './assertions'
@@ -0,0 +1,381 @@
1
+ /**
2
+ * @mdxui/terminal Test Renderer
3
+ *
4
+ * Mock terminal renderer for testing terminal components without requiring
5
+ * a real terminal or @opentui/core. Provides predictable output for assertions.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { createTestRenderer, renderToLines } from './test-renderer'
10
+ * import { Box, Text } from '@mdxui/terminal'
11
+ *
12
+ * // Quick rendering
13
+ * const lines = await renderToLines(
14
+ * <Box border="single"><Text>Hello</Text></Box>
15
+ * )
16
+ * expect(lines).toContain('Hello')
17
+ *
18
+ * // Full control
19
+ * const renderer = createTestRenderer({ width: 40, height: 10 })
20
+ * const output = renderer.render(<App />)
21
+ * expect(renderer.getBuffer()).toMatchSnapshot()
22
+ * ```
23
+ */
24
+
25
+ import type { ReactElement, ReactNode } from 'react'
26
+ import type { RenderContext } from '../../components/types'
27
+ import { renderElement } from '../../components/render'
28
+
29
+ // ============================================================================
30
+ // Types
31
+ // ============================================================================
32
+
33
+ /**
34
+ * Configuration options for the test renderer
35
+ */
36
+ export interface TestRendererConfig {
37
+ /** Terminal width in columns (default: 80) */
38
+ width?: number
39
+ /** Terminal height in rows (default: 24) */
40
+ height?: number
41
+ /** Enable color output (default: true) */
42
+ colors?: boolean
43
+ /** Color support level (default: 'truecolor') */
44
+ colorSupport?: 'none' | '16' | '256' | 'truecolor'
45
+ }
46
+
47
+ /**
48
+ * Mock renderer instance for testing
49
+ */
50
+ export interface TestRenderer {
51
+ /** Terminal width in columns */
52
+ readonly width: number
53
+ /** Terminal height in rows */
54
+ readonly height: number
55
+ /** Render a React element and return output lines */
56
+ render(element: ReactElement): string[]
57
+ /** Get the current buffer as a 2D array (rows x columns) */
58
+ getBuffer(): string[][]
59
+ /** Get the current output as a single string with newlines */
60
+ toString(): string
61
+ /** Clear the buffer */
62
+ clear(): void
63
+ /** Get the last rendered output */
64
+ getLastOutput(): string[]
65
+ /** Configure the renderer */
66
+ configure(config: Partial<TestRendererConfig>): void
67
+ /** Mock lifecycle methods (for compatibility with CliRenderer interface) */
68
+ start(): void
69
+ stop(): void
70
+ destroy(): void
71
+ requestRender(): void
72
+ }
73
+
74
+ /**
75
+ * Mock root instance for React rendering
76
+ */
77
+ export interface TestRoot {
78
+ /** Render a React element */
79
+ render(element: ReactNode): void
80
+ /** Unmount and clean up */
81
+ unmount(): void
82
+ /** Get the rendered output */
83
+ getOutput(): string[]
84
+ }
85
+
86
+ /**
87
+ * Snapshot options for terminal output
88
+ */
89
+ export interface SnapshotOptions {
90
+ /** Strip ANSI codes before comparison (default: false) */
91
+ stripAnsi?: boolean
92
+ /** Trim trailing whitespace from lines (default: true) */
93
+ trimTrailingWhitespace?: boolean
94
+ /** Normalize line endings (default: true) */
95
+ normalizeLineEndings?: boolean
96
+ }
97
+
98
+ // ============================================================================
99
+ // Mock Renderer Implementation
100
+ // ============================================================================
101
+
102
+ /**
103
+ * Create a mock terminal renderer for testing.
104
+ *
105
+ * This renderer simulates the @opentui/core renderer behavior
106
+ * but renders to an in-memory buffer instead of the terminal.
107
+ *
108
+ * @param config - Optional configuration
109
+ * @returns A mock renderer instance
110
+ *
111
+ * @example
112
+ * ```tsx
113
+ * const renderer = createTestRenderer({ width: 40, height: 10 })
114
+ * const lines = renderer.render(<Box>Test</Box>)
115
+ * expect(lines[0]).toContain('Test')
116
+ * ```
117
+ */
118
+ export function createTestRenderer(config: TestRendererConfig = {}): TestRenderer {
119
+ let width = config.width ?? 80
120
+ let height = config.height ?? 24
121
+ let colors = config.colors ?? true
122
+ let colorSupport = config.colorSupport ?? 'truecolor'
123
+
124
+ // Internal buffer as 2D array
125
+ let buffer: string[][] = []
126
+ let lastOutput: string[] = []
127
+
128
+ // Initialize empty buffer
129
+ const initBuffer = () => {
130
+ buffer = Array.from({ length: height }, () =>
131
+ Array.from({ length: width }, () => ' ')
132
+ )
133
+ }
134
+
135
+ initBuffer()
136
+
137
+ return {
138
+ get width() {
139
+ return width
140
+ },
141
+ get height() {
142
+ return height
143
+ },
144
+
145
+ render(element: ReactElement): string[] {
146
+ const context: RenderContext = {
147
+ width,
148
+ colors,
149
+ colorSupport,
150
+ }
151
+
152
+ const lines = renderElement(element, context)
153
+ lastOutput = lines
154
+
155
+ // Update buffer with rendered content
156
+ initBuffer()
157
+ lines.forEach((line, row) => {
158
+ if (row < height) {
159
+ const chars = [...line]
160
+ chars.forEach((char, col) => {
161
+ if (col < width) {
162
+ buffer[row][col] = char
163
+ }
164
+ })
165
+ }
166
+ })
167
+
168
+ return lines
169
+ },
170
+
171
+ getBuffer(): string[][] {
172
+ return buffer.map(row => [...row])
173
+ },
174
+
175
+ toString(): string {
176
+ return lastOutput.join('\n')
177
+ },
178
+
179
+ clear(): void {
180
+ initBuffer()
181
+ lastOutput = []
182
+ },
183
+
184
+ getLastOutput(): string[] {
185
+ return [...lastOutput]
186
+ },
187
+
188
+ configure(newConfig: Partial<TestRendererConfig>): void {
189
+ if (newConfig.width !== undefined) width = newConfig.width
190
+ if (newConfig.height !== undefined) height = newConfig.height
191
+ if (newConfig.colors !== undefined) colors = newConfig.colors
192
+ if (newConfig.colorSupport !== undefined) colorSupport = newConfig.colorSupport
193
+ initBuffer()
194
+ },
195
+
196
+ // Mock lifecycle methods (no-op for testing)
197
+ start(): void {},
198
+ stop(): void {},
199
+ destroy(): void {},
200
+ requestRender(): void {},
201
+ }
202
+ }
203
+
204
+ /**
205
+ * Create a mock root for testing React trees.
206
+ *
207
+ * @param renderer - The test renderer to use
208
+ * @returns A mock root instance
209
+ *
210
+ * @example
211
+ * ```tsx
212
+ * const renderer = createTestRenderer()
213
+ * const root = createTestRoot(renderer)
214
+ * root.render(<App />)
215
+ * expect(root.getOutput()).toContain('Welcome')
216
+ * ```
217
+ */
218
+ export function createTestRoot(renderer: TestRenderer): TestRoot {
219
+ let currentOutput: string[] = []
220
+
221
+ return {
222
+ render(element: ReactNode): void {
223
+ if (element === null || element === undefined) {
224
+ currentOutput = []
225
+ renderer.clear()
226
+ return
227
+ }
228
+
229
+ currentOutput = renderer.render(element as ReactElement)
230
+ },
231
+
232
+ unmount(): void {
233
+ currentOutput = []
234
+ renderer.clear()
235
+ },
236
+
237
+ getOutput(): string[] {
238
+ return [...currentOutput]
239
+ },
240
+ }
241
+ }
242
+
243
+ // ============================================================================
244
+ // Helper Functions
245
+ // ============================================================================
246
+
247
+ /**
248
+ * Render a React element directly to lines without creating a renderer.
249
+ *
250
+ * This is a convenience function for quick tests that don't need
251
+ * full renderer control.
252
+ *
253
+ * @param element - The React element to render
254
+ * @param options - Optional configuration
255
+ * @returns Array of rendered output lines
256
+ *
257
+ * @example
258
+ * ```tsx
259
+ * const lines = await renderToLines(<Text color="red">Error</Text>)
260
+ * expect(lines[0]).toContain('Error')
261
+ * ```
262
+ */
263
+ export function renderToLines(
264
+ element: ReactElement,
265
+ options: TestRendererConfig = {}
266
+ ): string[] {
267
+ const renderer = createTestRenderer(options)
268
+ return renderer.render(element)
269
+ }
270
+
271
+ /**
272
+ * Render a React element to a single string with newlines.
273
+ *
274
+ * @param element - The React element to render
275
+ * @param options - Optional configuration
276
+ * @returns The rendered output as a string
277
+ *
278
+ * @example
279
+ * ```tsx
280
+ * const output = renderToString(<Box>Hello</Box>)
281
+ * expect(output).toMatchSnapshot()
282
+ * ```
283
+ */
284
+ export function renderToString(
285
+ element: ReactElement,
286
+ options: TestRendererConfig = {}
287
+ ): string {
288
+ const lines = renderToLines(element, options)
289
+ return lines.join('\n')
290
+ }
291
+
292
+ /**
293
+ * Prepare terminal output for snapshot comparison.
294
+ *
295
+ * Normalizes the output to ensure consistent snapshot matching
296
+ * across different environments.
297
+ *
298
+ * @param output - The raw output (string or array of lines)
299
+ * @param options - Normalization options
300
+ * @returns Normalized output string
301
+ *
302
+ * @example
303
+ * ```tsx
304
+ * const output = renderer.render(<Component />)
305
+ * expect(prepareSnapshot(output)).toMatchSnapshot()
306
+ * ```
307
+ */
308
+ export function prepareSnapshot(
309
+ output: string | string[],
310
+ options: SnapshotOptions = {}
311
+ ): string {
312
+ const {
313
+ stripAnsi = false,
314
+ trimTrailingWhitespace = true,
315
+ normalizeLineEndings = true,
316
+ } = options
317
+
318
+ let text = Array.isArray(output) ? output.join('\n') : output
319
+
320
+ // Strip ANSI codes if requested
321
+ if (stripAnsi) {
322
+ text = text.replace(/\x1b\[[\d;]*m/g, '')
323
+ }
324
+
325
+ // Normalize line endings
326
+ if (normalizeLineEndings) {
327
+ text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n')
328
+ }
329
+
330
+ // Trim trailing whitespace from each line
331
+ if (trimTrailingWhitespace) {
332
+ text = text
333
+ .split('\n')
334
+ .map(line => line.trimEnd())
335
+ .join('\n')
336
+ }
337
+
338
+ return text
339
+ }
340
+
341
+ /**
342
+ * Create a mock @opentui/core createCliRenderer for testing.
343
+ *
344
+ * This returns a mock that can be used with vi.mock() or jest.mock()
345
+ * to replace the real OpenTUI renderer in tests.
346
+ *
347
+ * @param config - Optional configuration
348
+ * @returns A mock createCliRenderer function
349
+ *
350
+ * @example
351
+ * ```tsx
352
+ * vi.mock('@opentui/core', () => ({
353
+ * createCliRenderer: mockCreateCliRenderer({ width: 40 }),
354
+ * }))
355
+ * ```
356
+ */
357
+ export function mockCreateCliRenderer(config: TestRendererConfig = {}) {
358
+ return async () => createTestRenderer(config)
359
+ }
360
+
361
+ /**
362
+ * Create a mock @opentui/react createRoot for testing.
363
+ *
364
+ * @returns A mock createRoot function
365
+ *
366
+ * @example
367
+ * ```tsx
368
+ * vi.mock('@opentui/react', () => ({
369
+ * createRoot: mockCreateRoot(),
370
+ * }))
371
+ * ```
372
+ */
373
+ export function mockCreateRoot() {
374
+ return (renderer: TestRenderer) => createTestRoot(renderer)
375
+ }
376
+
377
+ // ============================================================================
378
+ // Exports
379
+ // ============================================================================
380
+
381
+ export { type RenderContext }