@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.
- package/README.md +571 -0
- package/dist/ansi-css-Sk5mWtdK.d.ts +119 -0
- package/dist/ansi-css-V6JIHGsM.d.ts +119 -0
- package/dist/ansi-css-_3eSEU9d.d.ts +119 -0
- package/dist/chunk-3EFDH7PK.js +5235 -0
- package/dist/chunk-3RG5ZIWI.js +10 -0
- package/dist/chunk-3X5IR6WE.js +884 -0
- package/dist/chunk-4FV5ZDCE.js +5236 -0
- package/dist/chunk-4OVMSF2J.js +243 -0
- package/dist/chunk-63FEETIS.js +4048 -0
- package/dist/chunk-B43KP7XJ.js +884 -0
- package/dist/chunk-BMTJXWUV.js +655 -0
- package/dist/chunk-C3SVH4N7.js +882 -0
- package/dist/chunk-EVWR7Y47.js +874 -0
- package/dist/chunk-F6A5VWUC.js +1285 -0
- package/dist/chunk-FD7KW7GE.js +882 -0
- package/dist/chunk-GBQ6UD6I.js +655 -0
- package/dist/chunk-GMDD3M6U.js +5227 -0
- package/dist/chunk-JBHRXOXM.js +1058 -0
- package/dist/chunk-JFOO3EYO.js +1182 -0
- package/dist/chunk-JQ5H3WXL.js +1291 -0
- package/dist/chunk-JQD5NASE.js +234 -0
- package/dist/chunk-KRHJP5R7.js +592 -0
- package/dist/chunk-KWF6WVJE.js +962 -0
- package/dist/chunk-LHYQVN3H.js +1038 -0
- package/dist/chunk-M3TLQLGC.js +1032 -0
- package/dist/chunk-MVW4Q5OP.js +240 -0
- package/dist/chunk-NXCZSWLU.js +1294 -0
- package/dist/chunk-O25TNRO6.js +607 -0
- package/dist/chunk-PNECDA2I.js +884 -0
- package/dist/chunk-QIHWRLJR.js +962 -0
- package/dist/chunk-QW5YMQ7K.js +882 -0
- package/dist/chunk-R5U7XKVJ.js +16 -0
- package/dist/chunk-RP2MVQLR.js +962 -0
- package/dist/chunk-TP6RXGXA.js +1087 -0
- package/dist/chunk-TQQSTITZ.js +655 -0
- package/dist/chunk-X24GWXQV.js +1281 -0
- package/dist/components/index.d.ts +802 -0
- package/dist/components/index.js +149 -0
- package/dist/data/index.d.ts +2554 -0
- package/dist/data/index.js +51 -0
- package/dist/forms/index.d.ts +1596 -0
- package/dist/forms/index.js +464 -0
- package/dist/index-CQRFZntR.d.ts +867 -0
- package/dist/index.d.ts +579 -0
- package/dist/index.js +786 -0
- package/dist/interactive-D0JkWosD.d.ts +217 -0
- package/dist/keyboard/index.d.ts +2 -0
- package/dist/keyboard/index.js +43 -0
- package/dist/renderers/index.d.ts +546 -0
- package/dist/renderers/index.js +2157 -0
- package/dist/storybook/index.d.ts +396 -0
- package/dist/storybook/index.js +641 -0
- package/dist/theme/index.d.ts +1339 -0
- package/dist/theme/index.js +123 -0
- package/dist/types-Bxu5PAgA.d.ts +710 -0
- package/dist/types-CIlop5Ji.d.ts +701 -0
- package/dist/types-Ca8p_p5X.d.ts +710 -0
- package/package.json +90 -0
- package/src/__tests__/components/data/card.test.ts +458 -0
- package/src/__tests__/components/data/list.test.ts +473 -0
- package/src/__tests__/components/data/metrics.test.ts +541 -0
- package/src/__tests__/components/data/table.test.ts +448 -0
- package/src/__tests__/components/input/field.test.ts +555 -0
- package/src/__tests__/components/input/form.test.ts +870 -0
- package/src/__tests__/components/input/search.test.ts +1238 -0
- package/src/__tests__/components/input/select.test.ts +658 -0
- package/src/__tests__/components/navigation/breadcrumb.test.ts +923 -0
- package/src/__tests__/components/navigation/command-palette.test.ts +1095 -0
- package/src/__tests__/components/navigation/sidebar.test.ts +1018 -0
- package/src/__tests__/components/navigation/tabs.test.ts +995 -0
- package/src/__tests__/components.test.tsx +1197 -0
- package/src/__tests__/core/compiler.test.ts +986 -0
- package/src/__tests__/core/parser.test.ts +785 -0
- package/src/__tests__/core/tier-switcher.test.ts +1103 -0
- package/src/__tests__/core/types.test.ts +1398 -0
- package/src/__tests__/data/collections.test.ts +1337 -0
- package/src/__tests__/data/db.test.ts +1265 -0
- package/src/__tests__/data/reactive.test.ts +1010 -0
- package/src/__tests__/data/sync.test.ts +1614 -0
- package/src/__tests__/errors.test.ts +660 -0
- package/src/__tests__/forms/integration.test.ts +444 -0
- package/src/__tests__/integration.test.ts +905 -0
- package/src/__tests__/keyboard.test.ts +1791 -0
- package/src/__tests__/renderer.test.ts +489 -0
- package/src/__tests__/renderers/ansi-css.test.ts +948 -0
- package/src/__tests__/renderers/ansi.test.ts +1366 -0
- package/src/__tests__/renderers/ascii.test.ts +1360 -0
- package/src/__tests__/renderers/interactive.test.ts +2353 -0
- package/src/__tests__/renderers/markdown.test.ts +1483 -0
- package/src/__tests__/renderers/text.test.ts +1369 -0
- package/src/__tests__/renderers/unicode.test.ts +1307 -0
- package/src/__tests__/theme.test.ts +639 -0
- package/src/__tests__/utils/assertions.ts +685 -0
- package/src/__tests__/utils/index.ts +115 -0
- package/src/__tests__/utils/test-renderer.ts +381 -0
- package/src/__tests__/utils/utils.test.ts +560 -0
- package/src/components/containers/card.ts +56 -0
- package/src/components/containers/dialog.ts +53 -0
- package/src/components/containers/index.ts +9 -0
- package/src/components/containers/panel.ts +59 -0
- package/src/components/feedback/badge.ts +40 -0
- package/src/components/feedback/index.ts +8 -0
- package/src/components/feedback/spinner.ts +23 -0
- package/src/components/helpers.ts +81 -0
- package/src/components/index.ts +153 -0
- package/src/components/layout/breadcrumb.ts +31 -0
- package/src/components/layout/index.ts +10 -0
- package/src/components/layout/list.ts +29 -0
- package/src/components/layout/sidebar.ts +79 -0
- package/src/components/layout/table.ts +62 -0
- package/src/components/primitives/box.ts +95 -0
- package/src/components/primitives/button.ts +54 -0
- package/src/components/primitives/index.ts +11 -0
- package/src/components/primitives/input.ts +88 -0
- package/src/components/primitives/select.ts +97 -0
- package/src/components/primitives/text.ts +60 -0
- package/src/components/render.ts +155 -0
- package/src/components/templates/app.ts +43 -0
- package/src/components/templates/index.ts +8 -0
- package/src/components/templates/site.ts +54 -0
- package/src/components/types.ts +777 -0
- package/src/core/compiler.ts +718 -0
- package/src/core/parser.ts +127 -0
- package/src/core/tier-switcher.ts +607 -0
- package/src/core/types.ts +672 -0
- package/src/data/collection.ts +316 -0
- package/src/data/collections.ts +50 -0
- package/src/data/context.tsx +174 -0
- package/src/data/db.ts +127 -0
- package/src/data/hooks.ts +532 -0
- package/src/data/index.ts +138 -0
- package/src/data/reactive.ts +1225 -0
- package/src/data/saas-collections.ts +375 -0
- package/src/data/sync.ts +1213 -0
- package/src/data/types.ts +660 -0
- package/src/forms/converters.ts +512 -0
- package/src/forms/index.ts +133 -0
- package/src/forms/schemas.ts +403 -0
- package/src/forms/types.ts +476 -0
- package/src/index.ts +542 -0
- package/src/keyboard/focus.ts +748 -0
- package/src/keyboard/index.ts +96 -0
- package/src/keyboard/integration.ts +371 -0
- package/src/keyboard/manager.ts +377 -0
- package/src/keyboard/presets.ts +90 -0
- package/src/renderers/ansi-css.ts +576 -0
- package/src/renderers/ansi.ts +802 -0
- package/src/renderers/ascii.ts +680 -0
- package/src/renderers/breadcrumb.ts +480 -0
- package/src/renderers/command-palette.ts +802 -0
- package/src/renderers/components/field.ts +210 -0
- package/src/renderers/components/form.ts +327 -0
- package/src/renderers/components/index.ts +21 -0
- package/src/renderers/components/search.ts +449 -0
- package/src/renderers/components/select.ts +222 -0
- package/src/renderers/index.ts +101 -0
- package/src/renderers/interactive/component-handlers.ts +622 -0
- package/src/renderers/interactive/cursor-manager.ts +147 -0
- package/src/renderers/interactive/focus-manager.ts +279 -0
- package/src/renderers/interactive/index.ts +661 -0
- package/src/renderers/interactive/input-handler.ts +164 -0
- package/src/renderers/interactive/keyboard-handler.ts +212 -0
- package/src/renderers/interactive/mouse-handler.ts +167 -0
- package/src/renderers/interactive/state-manager.ts +109 -0
- package/src/renderers/interactive/types.ts +338 -0
- package/src/renderers/interactive-string.ts +299 -0
- package/src/renderers/interactive.ts +59 -0
- package/src/renderers/markdown.ts +950 -0
- package/src/renderers/sidebar.ts +549 -0
- package/src/renderers/tabs.ts +682 -0
- package/src/renderers/text.ts +791 -0
- package/src/renderers/unicode.ts +917 -0
- package/src/renderers/utils.ts +942 -0
- package/src/router/adapters.ts +383 -0
- package/src/router/types.ts +140 -0
- package/src/router/utils.ts +452 -0
- package/src/schemas.ts +205 -0
- package/src/storybook/index.ts +91 -0
- package/src/storybook/interactive-decorator.tsx +659 -0
- package/src/storybook/keyboard-simulator.ts +501 -0
- package/src/theme/ansi-codes.ts +80 -0
- package/src/theme/box-drawing.ts +132 -0
- package/src/theme/color-convert.ts +254 -0
- package/src/theme/color-support.ts +321 -0
- package/src/theme/index.ts +134 -0
- package/src/theme/strip-ansi.ts +50 -0
- package/src/theme/tailwind-map.ts +469 -0
- package/src/theme/text-styles.ts +206 -0
- package/src/theme/theme-system.ts +568 -0
- package/src/types.ts +103 -0
|
@@ -0,0 +1,685 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mdxui/terminal Test Assertions
|
|
3
|
+
*
|
|
4
|
+
* Custom assertion helpers for testing terminal output. These helpers
|
|
5
|
+
* understand ANSI codes and terminal rendering conventions.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import {
|
|
10
|
+
* expectTerminalContains,
|
|
11
|
+
* expectTerminalMatches,
|
|
12
|
+
* expectAnsiCode,
|
|
13
|
+
* expectBoxDrawing,
|
|
14
|
+
* } from './assertions'
|
|
15
|
+
*
|
|
16
|
+
* const output = renderer.render(<Box border="single"><Text>Hello</Text></Box>)
|
|
17
|
+
*
|
|
18
|
+
* expectTerminalContains(output, 'Hello')
|
|
19
|
+
* expectBoxDrawing(output, 'single')
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { expect } from 'vitest'
|
|
24
|
+
import { ANSI } from '../../theme/ansi-codes'
|
|
25
|
+
import { boxChars, type BoxStyle } from '../../theme/box-drawing'
|
|
26
|
+
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Types
|
|
29
|
+
// ============================================================================
|
|
30
|
+
|
|
31
|
+
/** ANSI style names for assertion helpers */
|
|
32
|
+
export type AnsiStyleName =
|
|
33
|
+
| 'bold'
|
|
34
|
+
| 'dim'
|
|
35
|
+
| 'italic'
|
|
36
|
+
| 'underline'
|
|
37
|
+
| 'inverse'
|
|
38
|
+
| 'strikethrough'
|
|
39
|
+
| 'reset'
|
|
40
|
+
|
|
41
|
+
/** ANSI color names for assertion helpers */
|
|
42
|
+
export type AnsiColorName =
|
|
43
|
+
| 'black'
|
|
44
|
+
| 'red'
|
|
45
|
+
| 'green'
|
|
46
|
+
| 'yellow'
|
|
47
|
+
| 'blue'
|
|
48
|
+
| 'magenta'
|
|
49
|
+
| 'cyan'
|
|
50
|
+
| 'white'
|
|
51
|
+
| 'brightBlack'
|
|
52
|
+
| 'brightRed'
|
|
53
|
+
| 'brightGreen'
|
|
54
|
+
| 'brightYellow'
|
|
55
|
+
| 'brightBlue'
|
|
56
|
+
| 'brightMagenta'
|
|
57
|
+
| 'brightCyan'
|
|
58
|
+
| 'brightWhite'
|
|
59
|
+
|
|
60
|
+
/** Background color names */
|
|
61
|
+
export type AnsiBgColorName =
|
|
62
|
+
| 'bgBlack'
|
|
63
|
+
| 'bgRed'
|
|
64
|
+
| 'bgGreen'
|
|
65
|
+
| 'bgYellow'
|
|
66
|
+
| 'bgBlue'
|
|
67
|
+
| 'bgMagenta'
|
|
68
|
+
| 'bgCyan'
|
|
69
|
+
| 'bgWhite'
|
|
70
|
+
| 'bgBrightBlack'
|
|
71
|
+
| 'bgBrightRed'
|
|
72
|
+
| 'bgBrightGreen'
|
|
73
|
+
| 'bgBrightYellow'
|
|
74
|
+
| 'bgBrightBlue'
|
|
75
|
+
| 'bgBrightMagenta'
|
|
76
|
+
| 'bgBrightCyan'
|
|
77
|
+
| 'bgBrightWhite'
|
|
78
|
+
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// ANSI Code Utilities
|
|
81
|
+
// ============================================================================
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Strip all ANSI escape codes from a string.
|
|
85
|
+
*
|
|
86
|
+
* @param str - The string containing ANSI codes
|
|
87
|
+
* @returns The string with ANSI codes removed
|
|
88
|
+
*/
|
|
89
|
+
export function stripAnsi(str: string): string {
|
|
90
|
+
// eslint-disable-next-line no-control-regex
|
|
91
|
+
return str.replace(/\x1b\[[\d;]*m/g, '')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Check if a string contains any ANSI escape codes.
|
|
96
|
+
*
|
|
97
|
+
* @param str - The string to check
|
|
98
|
+
* @returns True if the string contains ANSI codes
|
|
99
|
+
*/
|
|
100
|
+
export function hasAnsiCodes(str: string): boolean {
|
|
101
|
+
// eslint-disable-next-line no-control-regex
|
|
102
|
+
return /\x1b\[[\d;]*m/.test(str)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Get the visible length of a string (excluding ANSI codes).
|
|
107
|
+
*
|
|
108
|
+
* @param str - The string to measure
|
|
109
|
+
* @returns The visible character count
|
|
110
|
+
*/
|
|
111
|
+
export function visibleLength(str: string): number {
|
|
112
|
+
return stripAnsi(str).length
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Extract all ANSI codes from a string.
|
|
117
|
+
*
|
|
118
|
+
* @param str - The string containing ANSI codes
|
|
119
|
+
* @returns Array of ANSI escape sequences found
|
|
120
|
+
*/
|
|
121
|
+
export function extractAnsiCodes(str: string): string[] {
|
|
122
|
+
// eslint-disable-next-line no-control-regex
|
|
123
|
+
const matches = str.match(/\x1b\[[\d;]*m/g)
|
|
124
|
+
return matches ?? []
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get the plain text content from terminal output.
|
|
129
|
+
*
|
|
130
|
+
* @param output - Array of output lines or single string
|
|
131
|
+
* @returns Plain text with ANSI codes stripped
|
|
132
|
+
*/
|
|
133
|
+
export function getPlainContent(output: string | string[]): string {
|
|
134
|
+
const text = Array.isArray(output) ? output.join('\n') : output
|
|
135
|
+
return stripAnsi(text)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ============================================================================
|
|
139
|
+
// Content Assertions
|
|
140
|
+
// ============================================================================
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Assert that terminal output contains specific text (ignoring ANSI codes).
|
|
144
|
+
*
|
|
145
|
+
* @param output - Terminal output as string array or string
|
|
146
|
+
* @param text - The text to search for
|
|
147
|
+
* @throws AssertionError if text is not found
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```tsx
|
|
151
|
+
* const output = renderer.render(<Text>Hello World</Text>)
|
|
152
|
+
* expectTerminalContains(output, 'Hello')
|
|
153
|
+
* expectTerminalContains(output, 'World')
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
export function expectTerminalContains(
|
|
157
|
+
output: string | string[],
|
|
158
|
+
text: string
|
|
159
|
+
): void {
|
|
160
|
+
const plainContent = getPlainContent(output)
|
|
161
|
+
expect(plainContent).toContain(text)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Assert that terminal output does NOT contain specific text.
|
|
166
|
+
*
|
|
167
|
+
* @param output - Terminal output as string array or string
|
|
168
|
+
* @param text - The text that should not be present
|
|
169
|
+
* @throws AssertionError if text is found
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```tsx
|
|
173
|
+
* const output = renderer.render(<Input type="password" value="secret" />)
|
|
174
|
+
* expectTerminalNotContains(output, 'secret')
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
export function expectTerminalNotContains(
|
|
178
|
+
output: string | string[],
|
|
179
|
+
text: string
|
|
180
|
+
): void {
|
|
181
|
+
const plainContent = getPlainContent(output)
|
|
182
|
+
expect(plainContent).not.toContain(text)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Assert that terminal output matches a regular expression.
|
|
187
|
+
*
|
|
188
|
+
* @param output - Terminal output as string array or string
|
|
189
|
+
* @param pattern - The regex pattern to match
|
|
190
|
+
* @param options - Options for matching
|
|
191
|
+
* @throws AssertionError if pattern doesn't match
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```tsx
|
|
195
|
+
* expectTerminalMatches(output, /Hello \w+/)
|
|
196
|
+
* expectTerminalMatches(output, /error/i, { stripAnsi: true })
|
|
197
|
+
* ```
|
|
198
|
+
*/
|
|
199
|
+
export function expectTerminalMatches(
|
|
200
|
+
output: string | string[],
|
|
201
|
+
pattern: RegExp,
|
|
202
|
+
options: { stripAnsi?: boolean } = {}
|
|
203
|
+
): void {
|
|
204
|
+
let text = Array.isArray(output) ? output.join('\n') : output
|
|
205
|
+
|
|
206
|
+
if (options.stripAnsi !== false) {
|
|
207
|
+
text = stripAnsi(text)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
expect(text).toMatch(pattern)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Assert that a specific line of output contains text.
|
|
215
|
+
*
|
|
216
|
+
* @param output - Array of output lines
|
|
217
|
+
* @param lineIndex - The line index to check (0-based)
|
|
218
|
+
* @param text - The text to search for
|
|
219
|
+
* @throws AssertionError if line doesn't contain text
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```tsx
|
|
223
|
+
* expectLineContains(output, 0, 'Header')
|
|
224
|
+
* expectLineContains(output, 1, 'Content')
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
export function expectLineContains(
|
|
228
|
+
output: string[],
|
|
229
|
+
lineIndex: number,
|
|
230
|
+
text: string
|
|
231
|
+
): void {
|
|
232
|
+
expect(output.length).toBeGreaterThan(lineIndex)
|
|
233
|
+
expect(stripAnsi(output[lineIndex])).toContain(text)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Assert that output has a specific number of lines.
|
|
238
|
+
*
|
|
239
|
+
* @param output - Array of output lines
|
|
240
|
+
* @param count - Expected number of lines
|
|
241
|
+
* @throws AssertionError if line count doesn't match
|
|
242
|
+
*/
|
|
243
|
+
export function expectLineCount(output: string[], count: number): void {
|
|
244
|
+
expect(output).toHaveLength(count)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Assert that output has at least a minimum number of lines.
|
|
249
|
+
*
|
|
250
|
+
* @param output - Array of output lines
|
|
251
|
+
* @param minCount - Minimum expected lines
|
|
252
|
+
* @throws AssertionError if fewer lines than minimum
|
|
253
|
+
*/
|
|
254
|
+
export function expectMinLines(output: string[], minCount: number): void {
|
|
255
|
+
expect(output.length).toBeGreaterThanOrEqual(minCount)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ============================================================================
|
|
259
|
+
// ANSI Style Assertions
|
|
260
|
+
// ============================================================================
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Assert that output contains a specific ANSI code.
|
|
264
|
+
*
|
|
265
|
+
* @param output - Terminal output as string array or string
|
|
266
|
+
* @param code - The ANSI code to check for (e.g., '\x1b[1m' for bold)
|
|
267
|
+
* @throws AssertionError if code is not found
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```tsx
|
|
271
|
+
* expectAnsiCode(output, ANSI.bold)
|
|
272
|
+
* expectAnsiCode(output, '\x1b[31m') // red
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
export function expectAnsiCode(
|
|
276
|
+
output: string | string[],
|
|
277
|
+
code: string
|
|
278
|
+
): void {
|
|
279
|
+
const text = Array.isArray(output) ? output.join('') : output
|
|
280
|
+
expect(text).toContain(code)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Assert that output does NOT contain a specific ANSI code.
|
|
285
|
+
*
|
|
286
|
+
* @param output - Terminal output as string array or string
|
|
287
|
+
* @param code - The ANSI code that should not be present
|
|
288
|
+
* @throws AssertionError if code is found
|
|
289
|
+
*/
|
|
290
|
+
export function expectNoAnsiCode(
|
|
291
|
+
output: string | string[],
|
|
292
|
+
code: string
|
|
293
|
+
): void {
|
|
294
|
+
const text = Array.isArray(output) ? output.join('') : output
|
|
295
|
+
expect(text).not.toContain(code)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Assert that output contains a specific ANSI style by name.
|
|
300
|
+
*
|
|
301
|
+
* @param output - Terminal output
|
|
302
|
+
* @param styleName - The style name (bold, dim, italic, underline, etc.)
|
|
303
|
+
* @throws AssertionError if style is not found
|
|
304
|
+
*
|
|
305
|
+
* @example
|
|
306
|
+
* ```tsx
|
|
307
|
+
* expectStyle(output, 'bold')
|
|
308
|
+
* expectStyle(output, 'underline')
|
|
309
|
+
* ```
|
|
310
|
+
*/
|
|
311
|
+
export function expectStyle(
|
|
312
|
+
output: string | string[],
|
|
313
|
+
styleName: AnsiStyleName
|
|
314
|
+
): void {
|
|
315
|
+
const styleCode = ANSI[styleName]
|
|
316
|
+
expectAnsiCode(output, styleCode)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Assert that output contains a specific foreground color.
|
|
321
|
+
*
|
|
322
|
+
* @param output - Terminal output
|
|
323
|
+
* @param colorName - The color name
|
|
324
|
+
* @throws AssertionError if color is not found
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* ```tsx
|
|
328
|
+
* expectForegroundColor(output, 'red')
|
|
329
|
+
* expectForegroundColor(output, 'cyan')
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
export function expectForegroundColor(
|
|
333
|
+
output: string | string[],
|
|
334
|
+
colorName: AnsiColorName
|
|
335
|
+
): void {
|
|
336
|
+
const colorCode = ANSI[colorName]
|
|
337
|
+
expectAnsiCode(output, colorCode)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Assert that output contains a specific background color.
|
|
342
|
+
*
|
|
343
|
+
* @param output - Terminal output
|
|
344
|
+
* @param colorName - The background color name
|
|
345
|
+
* @throws AssertionError if color is not found
|
|
346
|
+
*
|
|
347
|
+
* @example
|
|
348
|
+
* ```tsx
|
|
349
|
+
* expectBackgroundColor(output, 'bgBlue')
|
|
350
|
+
* expectBackgroundColor(output, 'bgRed')
|
|
351
|
+
* ```
|
|
352
|
+
*/
|
|
353
|
+
export function expectBackgroundColor(
|
|
354
|
+
output: string | string[],
|
|
355
|
+
colorName: AnsiBgColorName
|
|
356
|
+
): void {
|
|
357
|
+
const colorCode = ANSI[colorName]
|
|
358
|
+
expectAnsiCode(output, colorCode)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Assert that output contains an ANSI reset code.
|
|
363
|
+
*
|
|
364
|
+
* This is important to verify that styles are properly cleaned up.
|
|
365
|
+
*
|
|
366
|
+
* @param output - Terminal output
|
|
367
|
+
* @throws AssertionError if reset is not found
|
|
368
|
+
*/
|
|
369
|
+
export function expectReset(output: string | string[]): void {
|
|
370
|
+
expectAnsiCode(output, ANSI.reset)
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Assert that output contains 256-color ANSI code.
|
|
375
|
+
*
|
|
376
|
+
* @param output - Terminal output
|
|
377
|
+
* @param colorNumber - The 256-color code (0-255)
|
|
378
|
+
* @param isForeground - True for foreground, false for background
|
|
379
|
+
* @throws AssertionError if color code is not found
|
|
380
|
+
*
|
|
381
|
+
* @example
|
|
382
|
+
* ```tsx
|
|
383
|
+
* expectAnsi256Color(output, 33) // Blue in 256 palette
|
|
384
|
+
* expectAnsi256Color(output, 196, false) // Red background
|
|
385
|
+
* ```
|
|
386
|
+
*/
|
|
387
|
+
export function expectAnsi256Color(
|
|
388
|
+
output: string | string[],
|
|
389
|
+
colorNumber: number,
|
|
390
|
+
isForeground: boolean = true
|
|
391
|
+
): void {
|
|
392
|
+
const prefix = isForeground ? '38' : '48'
|
|
393
|
+
const code = `\x1b[${prefix};5;${colorNumber}m`
|
|
394
|
+
expectAnsiCode(output, code)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Assert that output contains truecolor RGB ANSI code.
|
|
399
|
+
*
|
|
400
|
+
* @param output - Terminal output
|
|
401
|
+
* @param r - Red component (0-255)
|
|
402
|
+
* @param g - Green component (0-255)
|
|
403
|
+
* @param b - Blue component (0-255)
|
|
404
|
+
* @param isForeground - True for foreground, false for background
|
|
405
|
+
* @throws AssertionError if RGB code is not found
|
|
406
|
+
*
|
|
407
|
+
* @example
|
|
408
|
+
* ```tsx
|
|
409
|
+
* expectTrueColor(output, 255, 0, 0) // Red foreground
|
|
410
|
+
* expectTrueColor(output, 59, 130, 246, false) // Blue-500 background
|
|
411
|
+
* ```
|
|
412
|
+
*/
|
|
413
|
+
export function expectTrueColor(
|
|
414
|
+
output: string | string[],
|
|
415
|
+
r: number,
|
|
416
|
+
g: number,
|
|
417
|
+
b: number,
|
|
418
|
+
isForeground: boolean = true
|
|
419
|
+
): void {
|
|
420
|
+
const prefix = isForeground ? '38' : '48'
|
|
421
|
+
const code = `\x1b[${prefix};2;${r};${g};${b}m`
|
|
422
|
+
expectAnsiCode(output, code)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// ============================================================================
|
|
426
|
+
// Box Drawing Assertions
|
|
427
|
+
// ============================================================================
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Assert that output contains box drawing characters for a specific style.
|
|
431
|
+
*
|
|
432
|
+
* @param output - Terminal output
|
|
433
|
+
* @param style - The box style (single, double, rounded, etc.)
|
|
434
|
+
* @throws AssertionError if box characters are not found
|
|
435
|
+
*
|
|
436
|
+
* @example
|
|
437
|
+
* ```tsx
|
|
438
|
+
* expectBoxDrawing(output, 'single')
|
|
439
|
+
* expectBoxDrawing(output, 'double')
|
|
440
|
+
* expectBoxDrawing(output, 'rounded')
|
|
441
|
+
* ```
|
|
442
|
+
*/
|
|
443
|
+
export function expectBoxDrawing(
|
|
444
|
+
output: string | string[],
|
|
445
|
+
style: BoxStyle
|
|
446
|
+
): void {
|
|
447
|
+
const text = Array.isArray(output) ? output.join('') : output
|
|
448
|
+
const chars = boxChars[style]
|
|
449
|
+
|
|
450
|
+
// Check for corner characters
|
|
451
|
+
expect(text).toContain(chars.topLeft)
|
|
452
|
+
expect(text).toContain(chars.topRight)
|
|
453
|
+
expect(text).toContain(chars.bottomLeft)
|
|
454
|
+
expect(text).toContain(chars.bottomRight)
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Assert that output contains horizontal box lines.
|
|
459
|
+
*
|
|
460
|
+
* @param output - Terminal output
|
|
461
|
+
* @param style - The box style
|
|
462
|
+
* @throws AssertionError if horizontal lines are not found
|
|
463
|
+
*/
|
|
464
|
+
export function expectHorizontalLine(
|
|
465
|
+
output: string | string[],
|
|
466
|
+
style: BoxStyle
|
|
467
|
+
): void {
|
|
468
|
+
const text = Array.isArray(output) ? output.join('') : output
|
|
469
|
+
expect(text).toContain(boxChars[style].horizontal)
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Assert that output contains vertical box lines.
|
|
474
|
+
*
|
|
475
|
+
* @param output - Terminal output
|
|
476
|
+
* @param style - The box style
|
|
477
|
+
* @throws AssertionError if vertical lines are not found
|
|
478
|
+
*/
|
|
479
|
+
export function expectVerticalLine(
|
|
480
|
+
output: string | string[],
|
|
481
|
+
style: BoxStyle
|
|
482
|
+
): void {
|
|
483
|
+
const text = Array.isArray(output) ? output.join('') : output
|
|
484
|
+
expect(text).toContain(boxChars[style].vertical)
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Assert that output does NOT contain box drawing characters.
|
|
489
|
+
*
|
|
490
|
+
* @param output - Terminal output
|
|
491
|
+
* @throws AssertionError if any box characters are found
|
|
492
|
+
*/
|
|
493
|
+
export function expectNoBoxDrawing(output: string | string[]): void {
|
|
494
|
+
const text = stripAnsi(Array.isArray(output) ? output.join('') : output)
|
|
495
|
+
|
|
496
|
+
// Check all box styles
|
|
497
|
+
for (const style of ['single', 'double', 'rounded'] as const) {
|
|
498
|
+
const chars = boxChars[style]
|
|
499
|
+
expect(text).not.toContain(chars.topLeft)
|
|
500
|
+
expect(text).not.toContain(chars.topRight)
|
|
501
|
+
expect(text).not.toContain(chars.bottomLeft)
|
|
502
|
+
expect(text).not.toContain(chars.bottomRight)
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// ============================================================================
|
|
507
|
+
// Dimension Assertions
|
|
508
|
+
// ============================================================================
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* Assert that all lines have a maximum width.
|
|
512
|
+
*
|
|
513
|
+
* @param output - Array of output lines
|
|
514
|
+
* @param maxWidth - Maximum allowed visible width
|
|
515
|
+
* @throws AssertionError if any line exceeds maxWidth
|
|
516
|
+
*
|
|
517
|
+
* @example
|
|
518
|
+
* ```tsx
|
|
519
|
+
* expectMaxWidth(output, 80)
|
|
520
|
+
* ```
|
|
521
|
+
*/
|
|
522
|
+
export function expectMaxWidth(output: string[], maxWidth: number): void {
|
|
523
|
+
for (const line of output) {
|
|
524
|
+
expect(visibleLength(line)).toBeLessThanOrEqual(maxWidth)
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Assert that all lines have an exact width.
|
|
530
|
+
*
|
|
531
|
+
* @param output - Array of output lines
|
|
532
|
+
* @param width - Expected visible width
|
|
533
|
+
* @throws AssertionError if any line doesn't match width
|
|
534
|
+
*/
|
|
535
|
+
export function expectExactWidth(output: string[], width: number): void {
|
|
536
|
+
for (const line of output) {
|
|
537
|
+
expect(visibleLength(line)).toBe(width)
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Assert that output fits within specified dimensions.
|
|
543
|
+
*
|
|
544
|
+
* @param output - Array of output lines
|
|
545
|
+
* @param width - Maximum width
|
|
546
|
+
* @param height - Maximum height (line count)
|
|
547
|
+
* @throws AssertionError if output exceeds dimensions
|
|
548
|
+
*/
|
|
549
|
+
export function expectFitsDimensions(
|
|
550
|
+
output: string[],
|
|
551
|
+
width: number,
|
|
552
|
+
height: number
|
|
553
|
+
): void {
|
|
554
|
+
expect(output.length).toBeLessThanOrEqual(height)
|
|
555
|
+
expectMaxWidth(output, width)
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// ============================================================================
|
|
559
|
+
// Snapshot Utilities
|
|
560
|
+
// ============================================================================
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Create a snapshot-friendly representation of terminal output.
|
|
564
|
+
*
|
|
565
|
+
* This normalizes the output for consistent snapshot comparison:
|
|
566
|
+
* - Optionally strips ANSI codes
|
|
567
|
+
* - Trims trailing whitespace
|
|
568
|
+
* - Normalizes line endings
|
|
569
|
+
*
|
|
570
|
+
* @param output - Terminal output
|
|
571
|
+
* @param options - Snapshot options
|
|
572
|
+
* @returns Normalized string for snapshot comparison
|
|
573
|
+
*
|
|
574
|
+
* @example
|
|
575
|
+
* ```tsx
|
|
576
|
+
* expect(toSnapshot(output)).toMatchSnapshot()
|
|
577
|
+
* expect(toSnapshot(output, { stripAnsi: true })).toMatchSnapshot()
|
|
578
|
+
* ```
|
|
579
|
+
*/
|
|
580
|
+
export function toSnapshot(
|
|
581
|
+
output: string | string[],
|
|
582
|
+
options: {
|
|
583
|
+
stripAnsi?: boolean
|
|
584
|
+
trimLines?: boolean
|
|
585
|
+
} = {}
|
|
586
|
+
): string {
|
|
587
|
+
const { stripAnsi: strip = false, trimLines = true } = options
|
|
588
|
+
|
|
589
|
+
let text = Array.isArray(output) ? output.join('\n') : output
|
|
590
|
+
|
|
591
|
+
if (strip) {
|
|
592
|
+
text = stripAnsi(text)
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (trimLines) {
|
|
596
|
+
text = text
|
|
597
|
+
.split('\n')
|
|
598
|
+
.map(line => line.trimEnd())
|
|
599
|
+
.join('\n')
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
return text
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Compare two terminal outputs for equality (ignoring ANSI codes).
|
|
607
|
+
*
|
|
608
|
+
* @param actual - Actual output
|
|
609
|
+
* @param expected - Expected output
|
|
610
|
+
* @returns True if outputs are equal (after stripping ANSI)
|
|
611
|
+
*/
|
|
612
|
+
export function terminalOutputEquals(
|
|
613
|
+
actual: string | string[],
|
|
614
|
+
expected: string | string[]
|
|
615
|
+
): boolean {
|
|
616
|
+
const actualText = getPlainContent(actual)
|
|
617
|
+
const expectedText = getPlainContent(expected)
|
|
618
|
+
return actualText === expectedText
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Assert that two terminal outputs are equal (ignoring ANSI codes).
|
|
623
|
+
*
|
|
624
|
+
* @param actual - Actual output
|
|
625
|
+
* @param expected - Expected output
|
|
626
|
+
* @throws AssertionError if outputs don't match
|
|
627
|
+
*/
|
|
628
|
+
export function expectTerminalEquals(
|
|
629
|
+
actual: string | string[],
|
|
630
|
+
expected: string | string[]
|
|
631
|
+
): void {
|
|
632
|
+
expect(getPlainContent(actual)).toBe(getPlainContent(expected))
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// ============================================================================
|
|
636
|
+
// Component-Specific Assertions
|
|
637
|
+
// ============================================================================
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Assert that output shows a cursor indicator (inverse video or underline).
|
|
641
|
+
*
|
|
642
|
+
* Used for testing focused input fields.
|
|
643
|
+
*
|
|
644
|
+
* @param output - Terminal output
|
|
645
|
+
* @throws AssertionError if no cursor indicator found
|
|
646
|
+
*/
|
|
647
|
+
export function expectCursor(output: string | string[]): void {
|
|
648
|
+
const text = Array.isArray(output) ? output.join('') : output
|
|
649
|
+
// Cursor is typically shown as inverse video or underline
|
|
650
|
+
expect(text).toMatch(/\x1b\[7m|\x1b\[4m/)
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Assert that output shows focus styling.
|
|
655
|
+
*
|
|
656
|
+
* @param output - Terminal output
|
|
657
|
+
* @throws AssertionError if no focus styling found
|
|
658
|
+
*/
|
|
659
|
+
export function expectFocused(output: string | string[]): void {
|
|
660
|
+
const text = Array.isArray(output) ? output.join('') : output
|
|
661
|
+
// Focus typically includes inverse, underline, or background color
|
|
662
|
+
expect(text).toMatch(/\x1b\[(7|4|4[0-7]|10[0-7])m/)
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Assert that output shows disabled styling (typically dim).
|
|
667
|
+
*
|
|
668
|
+
* @param output - Terminal output
|
|
669
|
+
* @throws AssertionError if no disabled styling found
|
|
670
|
+
*/
|
|
671
|
+
export function expectDisabled(output: string | string[]): void {
|
|
672
|
+
expectAnsiCode(output, ANSI.dim)
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Assert that output shows selection/highlight styling.
|
|
677
|
+
*
|
|
678
|
+
* @param output - Terminal output
|
|
679
|
+
* @throws AssertionError if no selection styling found
|
|
680
|
+
*/
|
|
681
|
+
export function expectSelected(output: string | string[]): void {
|
|
682
|
+
const text = Array.isArray(output) ? output.join('') : output
|
|
683
|
+
// Selection typically includes background color or inverse
|
|
684
|
+
expect(text).toMatch(/\x1b\[(7|4[0-7]|10[0-7])m/)
|
|
685
|
+
}
|