@task-mcp/shared 1.0.13 → 1.0.14
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/dist/algorithms/critical-path.d.ts.map +1 -1
- package/dist/algorithms/critical-path.js +2 -14
- package/dist/algorithms/critical-path.js.map +1 -1
- package/dist/algorithms/dependency-integrity.d.ts +8 -0
- package/dist/algorithms/dependency-integrity.d.ts.map +1 -1
- package/dist/algorithms/dependency-integrity.js +42 -24
- package/dist/algorithms/dependency-integrity.js.map +1 -1
- package/dist/algorithms/dependency-integrity.test.d.ts +2 -0
- package/dist/algorithms/dependency-integrity.test.d.ts.map +1 -0
- package/dist/algorithms/dependency-integrity.test.js +309 -0
- package/dist/algorithms/dependency-integrity.test.js.map +1 -0
- package/dist/algorithms/tech-analysis.d.ts +5 -5
- package/dist/algorithms/tech-analysis.d.ts.map +1 -1
- package/dist/algorithms/tech-analysis.js +65 -17
- package/dist/algorithms/tech-analysis.js.map +1 -1
- package/dist/algorithms/topological-sort.d.ts.map +1 -1
- package/dist/algorithms/topological-sort.js +1 -56
- package/dist/algorithms/topological-sort.js.map +1 -1
- package/dist/schemas/index.d.ts +1 -0
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +2 -0
- package/dist/schemas/index.js.map +1 -1
- package/dist/schemas/project.d.ts +6 -6
- package/dist/schemas/state.d.ts +17 -0
- package/dist/schemas/state.d.ts.map +1 -0
- package/dist/schemas/state.js +17 -0
- package/dist/schemas/state.js.map +1 -0
- package/dist/schemas/task.d.ts +13 -4
- package/dist/schemas/task.d.ts.map +1 -1
- package/dist/schemas/task.js +3 -0
- package/dist/schemas/task.js.map +1 -1
- package/dist/schemas/view.d.ts +4 -4
- package/dist/utils/dashboard-renderer.d.ts +3 -0
- package/dist/utils/dashboard-renderer.d.ts.map +1 -1
- package/dist/utils/dashboard-renderer.js +12 -13
- package/dist/utils/dashboard-renderer.js.map +1 -1
- package/dist/utils/dashboard-renderer.test.d.ts +2 -0
- package/dist/utils/dashboard-renderer.test.d.ts.map +1 -0
- package/dist/utils/dashboard-renderer.test.js +777 -0
- package/dist/utils/dashboard-renderer.test.js.map +1 -0
- package/dist/utils/date.d.ts +49 -0
- package/dist/utils/date.d.ts.map +1 -1
- package/dist/utils/date.js +174 -19
- package/dist/utils/date.js.map +1 -1
- package/dist/utils/date.test.js +139 -1
- package/dist/utils/date.test.js.map +1 -1
- package/dist/utils/hierarchy.d.ts +1 -1
- package/dist/utils/hierarchy.d.ts.map +1 -1
- package/dist/utils/hierarchy.js +15 -5
- package/dist/utils/hierarchy.js.map +1 -1
- package/dist/utils/hierarchy.test.d.ts +2 -0
- package/dist/utils/hierarchy.test.d.ts.map +1 -0
- package/dist/utils/hierarchy.test.js +351 -0
- package/dist/utils/hierarchy.test.js.map +1 -0
- package/dist/utils/id.js +1 -1
- package/dist/utils/id.js.map +1 -1
- package/dist/utils/index.d.ts +3 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -2
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/natural-language.d.ts.map +1 -1
- package/dist/utils/natural-language.js +7 -0
- package/dist/utils/natural-language.js.map +1 -1
- package/dist/utils/natural-language.test.js +24 -0
- package/dist/utils/natural-language.test.js.map +1 -1
- package/dist/utils/priority-queue.d.ts +17 -0
- package/dist/utils/priority-queue.d.ts.map +1 -0
- package/dist/utils/priority-queue.js +62 -0
- package/dist/utils/priority-queue.js.map +1 -0
- package/dist/utils/projection.d.ts +9 -0
- package/dist/utils/projection.d.ts.map +1 -1
- package/dist/utils/projection.js +37 -0
- package/dist/utils/projection.js.map +1 -1
- package/dist/utils/terminal-ui.d.ts +5 -0
- package/dist/utils/terminal-ui.d.ts.map +1 -1
- package/dist/utils/terminal-ui.js +88 -11
- package/dist/utils/terminal-ui.js.map +1 -1
- package/dist/utils/terminal-ui.test.d.ts +2 -0
- package/dist/utils/terminal-ui.test.d.ts.map +1 -0
- package/dist/utils/terminal-ui.test.js +683 -0
- package/dist/utils/terminal-ui.test.js.map +1 -0
- package/package.json +1 -1
- package/src/algorithms/critical-path.ts +6 -14
- package/src/algorithms/dependency-integrity.test.ts +348 -0
- package/src/algorithms/dependency-integrity.ts +41 -26
- package/src/algorithms/tech-analysis.ts +86 -18
- package/src/algorithms/topological-sort.ts +1 -62
- package/src/schemas/index.ts +3 -0
- package/src/schemas/state.ts +23 -0
- package/src/schemas/task.ts +3 -0
- package/src/utils/dashboard-renderer.test.ts +981 -0
- package/src/utils/dashboard-renderer.ts +14 -15
- package/src/utils/date.test.ts +170 -1
- package/src/utils/date.ts +214 -19
- package/src/utils/hierarchy.test.ts +411 -0
- package/src/utils/hierarchy.ts +22 -5
- package/src/utils/id.ts +1 -1
- package/src/utils/index.ts +17 -1
- package/src/utils/natural-language.test.ts +28 -0
- package/src/utils/natural-language.ts +8 -0
- package/src/utils/priority-queue.ts +68 -0
- package/src/utils/projection.ts +46 -2
- package/src/utils/terminal-ui.test.ts +831 -0
- package/src/utils/terminal-ui.ts +90 -10
|
@@ -0,0 +1,683 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { color, style, styled, c, stripAnsi, isFullWidth, displayWidth, visibleLength, pad, padEnd, padStart, center, truncateStr, hline, progressBar, box, drawBox, sideBySide, sideBySideArrays, table, renderTable, formatStatus, formatPriority, formatDependencies, banner, BOX, } from "./terminal-ui.js";
|
|
3
|
+
// =============================================================================
|
|
4
|
+
// ANSI Color and Style Tests
|
|
5
|
+
// =============================================================================
|
|
6
|
+
describe("color", () => {
|
|
7
|
+
test("applies red color to text", () => {
|
|
8
|
+
const result = color("test", "red");
|
|
9
|
+
expect(result).toContain("\x1b[31m");
|
|
10
|
+
expect(result).toContain("test");
|
|
11
|
+
expect(result).toContain("\x1b[0m");
|
|
12
|
+
});
|
|
13
|
+
test("applies green color to text", () => {
|
|
14
|
+
const result = color("hello", "green");
|
|
15
|
+
expect(result).toContain("\x1b[32m");
|
|
16
|
+
expect(result).toContain("hello");
|
|
17
|
+
});
|
|
18
|
+
test("applies bright colors", () => {
|
|
19
|
+
const result = color("bright", "brightCyan");
|
|
20
|
+
expect(result).toContain("\x1b[96m");
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe("style", () => {
|
|
24
|
+
test("applies bold style", () => {
|
|
25
|
+
const result = style("bold text", "bold");
|
|
26
|
+
expect(result).toContain("\x1b[1m");
|
|
27
|
+
expect(result).toContain("bold text");
|
|
28
|
+
});
|
|
29
|
+
test("applies dim style", () => {
|
|
30
|
+
const result = style("dim", "dim");
|
|
31
|
+
expect(result).toContain("\x1b[2m");
|
|
32
|
+
});
|
|
33
|
+
test("applies underline style", () => {
|
|
34
|
+
const result = style("underlined", "underline");
|
|
35
|
+
expect(result).toContain("\x1b[4m");
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe("styled", () => {
|
|
39
|
+
test("combines color and style", () => {
|
|
40
|
+
const result = styled("combo", "cyan", "bold");
|
|
41
|
+
expect(result).toContain("\x1b[1;36m");
|
|
42
|
+
expect(result).toContain("combo");
|
|
43
|
+
});
|
|
44
|
+
test("applies only color when no style provided", () => {
|
|
45
|
+
const result = styled("just color", "yellow");
|
|
46
|
+
expect(result).toContain("\x1b[33m");
|
|
47
|
+
expect(result).not.toContain(";");
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
describe("c helper object", () => {
|
|
51
|
+
test("c.bold applies bold style", () => {
|
|
52
|
+
const result = c.bold("text");
|
|
53
|
+
expect(stripAnsi(result)).toBe("text");
|
|
54
|
+
expect(result).toContain("\x1b[1m");
|
|
55
|
+
});
|
|
56
|
+
test("c.red applies red color", () => {
|
|
57
|
+
const result = c.red("error");
|
|
58
|
+
expect(result).toContain("\x1b[31m");
|
|
59
|
+
});
|
|
60
|
+
test("semantic colors work", () => {
|
|
61
|
+
expect(c.success("ok")).toContain("\x1b[32m"); // green
|
|
62
|
+
expect(c.error("fail")).toContain("\x1b[31m"); // red
|
|
63
|
+
expect(c.warning("warn")).toContain("\x1b[33m"); // yellow
|
|
64
|
+
expect(c.info("info")).toContain("\x1b[36m"); // cyan
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
// =============================================================================
|
|
68
|
+
// String Utility Tests
|
|
69
|
+
// =============================================================================
|
|
70
|
+
describe("stripAnsi", () => {
|
|
71
|
+
test("removes ANSI escape codes from string", () => {
|
|
72
|
+
const colored = "\x1b[31mred text\x1b[0m";
|
|
73
|
+
expect(stripAnsi(colored)).toBe("red text");
|
|
74
|
+
});
|
|
75
|
+
test("handles multiple escape codes", () => {
|
|
76
|
+
const multi = "\x1b[1;32mgreen bold\x1b[0m and \x1b[34mblue\x1b[0m";
|
|
77
|
+
expect(stripAnsi(multi)).toBe("green bold and blue");
|
|
78
|
+
});
|
|
79
|
+
test("returns plain string unchanged", () => {
|
|
80
|
+
expect(stripAnsi("plain text")).toBe("plain text");
|
|
81
|
+
});
|
|
82
|
+
test("handles empty string", () => {
|
|
83
|
+
expect(stripAnsi("")).toBe("");
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
// =============================================================================
|
|
87
|
+
// isFullWidth Tests
|
|
88
|
+
// =============================================================================
|
|
89
|
+
describe("isFullWidth", () => {
|
|
90
|
+
// ASCII characters (not full-width)
|
|
91
|
+
test("returns false for ASCII characters", () => {
|
|
92
|
+
expect(isFullWidth("a")).toBe(false);
|
|
93
|
+
expect(isFullWidth("Z")).toBe(false);
|
|
94
|
+
expect(isFullWidth("0")).toBe(false);
|
|
95
|
+
expect(isFullWidth(" ")).toBe(false);
|
|
96
|
+
expect(isFullWidth("!")).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
test("returns false for empty string", () => {
|
|
99
|
+
expect(isFullWidth("")).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
// CJK characters
|
|
102
|
+
test("returns true for Hangul Jamo", () => {
|
|
103
|
+
expect(isFullWidth("\u1100")).toBe(true); // Hangul Choseong Kiyeok
|
|
104
|
+
expect(isFullWidth("\u115F")).toBe(true); // Hangul Choseong Filler
|
|
105
|
+
});
|
|
106
|
+
test("returns true for Hangul Syllables", () => {
|
|
107
|
+
expect(isFullWidth("가")).toBe(true); // U+AC00
|
|
108
|
+
expect(isFullWidth("한")).toBe(true);
|
|
109
|
+
expect(isFullWidth("글")).toBe(true);
|
|
110
|
+
expect(isFullWidth("\uD7AF")).toBe(true); // End of Hangul Syllables
|
|
111
|
+
});
|
|
112
|
+
test("returns true for CJK Ideographs", () => {
|
|
113
|
+
expect(isFullWidth("中")).toBe(true); // Chinese
|
|
114
|
+
expect(isFullWidth("国")).toBe(true);
|
|
115
|
+
expect(isFullWidth("日")).toBe(true); // Japanese Kanji
|
|
116
|
+
expect(isFullWidth("本")).toBe(true);
|
|
117
|
+
});
|
|
118
|
+
test("returns true for CJK Radicals and Kangxi", () => {
|
|
119
|
+
expect(isFullWidth("\u2E80")).toBe(true); // CJK Radical Repeat
|
|
120
|
+
expect(isFullWidth("\u2F00")).toBe(true); // Kangxi Radical One
|
|
121
|
+
});
|
|
122
|
+
test("returns true for Fullwidth ASCII", () => {
|
|
123
|
+
expect(isFullWidth("A")).toBe(true); // Fullwidth A (U+FF21)
|
|
124
|
+
expect(isFullWidth("1")).toBe(true); // Fullwidth 1 (U+FF11)
|
|
125
|
+
});
|
|
126
|
+
test("returns true for CJK Extension B+ characters", () => {
|
|
127
|
+
expect(isFullWidth("\u{20000}")).toBe(true); // CJK Extension B start
|
|
128
|
+
expect(isFullWidth("\u{2A6DF}")).toBe(true); // CJK Extension B
|
|
129
|
+
});
|
|
130
|
+
test("returns true for CJK Extension G+ characters", () => {
|
|
131
|
+
expect(isFullWidth("\u{30000}")).toBe(true); // CJK Extension G start
|
|
132
|
+
});
|
|
133
|
+
// Common emoji (faces)
|
|
134
|
+
test("returns true for face emoji", () => {
|
|
135
|
+
expect(isFullWidth("😀")).toBe(true); // Grinning Face U+1F600
|
|
136
|
+
expect(isFullWidth("😁")).toBe(true); // Beaming Face
|
|
137
|
+
expect(isFullWidth("😂")).toBe(true); // Face with Tears of Joy
|
|
138
|
+
expect(isFullWidth("🙂")).toBe(true); // Slightly Smiling Face
|
|
139
|
+
expect(isFullWidth("😎")).toBe(true); // Smiling Face with Sunglasses
|
|
140
|
+
expect(isFullWidth("😍")).toBe(true); // Smiling Face with Heart-Eyes
|
|
141
|
+
expect(isFullWidth("🥺")).toBe(true); // Pleading Face
|
|
142
|
+
expect(isFullWidth("😭")).toBe(true); // Loudly Crying Face
|
|
143
|
+
});
|
|
144
|
+
// Hand gestures
|
|
145
|
+
test("returns true for hand/gesture emoji", () => {
|
|
146
|
+
expect(isFullWidth("👍")).toBe(true); // Thumbs Up
|
|
147
|
+
expect(isFullWidth("👎")).toBe(true); // Thumbs Down
|
|
148
|
+
expect(isFullWidth("👋")).toBe(true); // Waving Hand
|
|
149
|
+
expect(isFullWidth("🤝")).toBe(true); // Handshake
|
|
150
|
+
expect(isFullWidth("✌️".charAt(0))).toBe(true); // Victory Hand (base char)
|
|
151
|
+
});
|
|
152
|
+
// Animals
|
|
153
|
+
test("returns true for animal emoji", () => {
|
|
154
|
+
expect(isFullWidth("🐶")).toBe(true); // Dog Face
|
|
155
|
+
expect(isFullWidth("🐱")).toBe(true); // Cat Face
|
|
156
|
+
expect(isFullWidth("🦁")).toBe(true); // Lion
|
|
157
|
+
expect(isFullWidth("🐻")).toBe(true); // Bear
|
|
158
|
+
expect(isFullWidth("🦄")).toBe(true); // Unicorn
|
|
159
|
+
});
|
|
160
|
+
// Food
|
|
161
|
+
test("returns true for food emoji", () => {
|
|
162
|
+
expect(isFullWidth("🍎")).toBe(true); // Red Apple
|
|
163
|
+
expect(isFullWidth("🍕")).toBe(true); // Pizza
|
|
164
|
+
expect(isFullWidth("🍔")).toBe(true); // Hamburger
|
|
165
|
+
expect(isFullWidth("☕")).toBe(true); // Hot Beverage (U+2615)
|
|
166
|
+
expect(isFullWidth("🍺")).toBe(true); // Beer Mug
|
|
167
|
+
});
|
|
168
|
+
// Transport
|
|
169
|
+
test("returns true for transport emoji", () => {
|
|
170
|
+
expect(isFullWidth("🚗")).toBe(true); // Automobile
|
|
171
|
+
expect(isFullWidth("✈️".charAt(0))).toBe(true); // Airplane (base char U+2708)
|
|
172
|
+
expect(isFullWidth("🚀")).toBe(true); // Rocket
|
|
173
|
+
expect(isFullWidth("🚂")).toBe(true); // Locomotive
|
|
174
|
+
expect(isFullWidth("⛵")).toBe(true); // Sailboat (U+26F5)
|
|
175
|
+
});
|
|
176
|
+
// Symbols
|
|
177
|
+
test("returns true for symbol emoji", () => {
|
|
178
|
+
expect(isFullWidth("❤️".charAt(0))).toBe(true); // Red Heart (base char U+2764)
|
|
179
|
+
expect(isFullWidth("⭐")).toBe(true); // Star (U+2B50)
|
|
180
|
+
expect(isFullWidth("✨")).toBe(true); // Sparkles (U+2728)
|
|
181
|
+
expect(isFullWidth("🔥")).toBe(true); // Fire
|
|
182
|
+
expect(isFullWidth("💯")).toBe(true); // Hundred Points
|
|
183
|
+
expect(isFullWidth("✅")).toBe(true); // Check Mark Button (U+2705)
|
|
184
|
+
expect(isFullWidth("❌")).toBe(true); // Cross Mark (U+274C)
|
|
185
|
+
});
|
|
186
|
+
// Weather/nature
|
|
187
|
+
test("returns true for weather/nature emoji", () => {
|
|
188
|
+
expect(isFullWidth("☀️".charAt(0))).toBe(true); // Sun (base char)
|
|
189
|
+
expect(isFullWidth("🌙")).toBe(true); // Crescent Moon
|
|
190
|
+
expect(isFullWidth("🌈")).toBe(true); // Rainbow
|
|
191
|
+
expect(isFullWidth("❄️".charAt(0))).toBe(true); // Snowflake (U+2744)
|
|
192
|
+
expect(isFullWidth("⛄")).toBe(true); // Snowman (U+26C4)
|
|
193
|
+
});
|
|
194
|
+
// Misc Symbols (U+2600-U+26FF)
|
|
195
|
+
test("returns true for misc symbols", () => {
|
|
196
|
+
expect(isFullWidth("☀")).toBe(true); // Black Sun (U+2600)
|
|
197
|
+
expect(isFullWidth("☁")).toBe(true); // Cloud (U+2601)
|
|
198
|
+
expect(isFullWidth("☂")).toBe(true); // Umbrella (U+2602)
|
|
199
|
+
expect(isFullWidth("⚡")).toBe(true); // High Voltage (U+26A1)
|
|
200
|
+
expect(isFullWidth("⚽")).toBe(true); // Soccer Ball (U+26BD)
|
|
201
|
+
expect(isFullWidth("⛳")).toBe(true); // Flag in Hole (U+26F3)
|
|
202
|
+
expect(isFullWidth("⛺")).toBe(true); // Tent (U+26FA)
|
|
203
|
+
expect(isFullWidth("⛽")).toBe(true); // Fuel Pump (U+26FD)
|
|
204
|
+
});
|
|
205
|
+
// Dingbats (U+2700-U+27BF)
|
|
206
|
+
test("returns true for dingbats", () => {
|
|
207
|
+
expect(isFullWidth("✂")).toBe(true); // Scissors (U+2702)
|
|
208
|
+
expect(isFullWidth("✈")).toBe(true); // Airplane (U+2708)
|
|
209
|
+
expect(isFullWidth("✉")).toBe(true); // Envelope (U+2709)
|
|
210
|
+
expect(isFullWidth("✏")).toBe(true); // Pencil (U+270F)
|
|
211
|
+
expect(isFullWidth("✔")).toBe(true); // Check Mark (U+2714)
|
|
212
|
+
expect(isFullWidth("✖")).toBe(true); // Heavy Multiplication X (U+2716)
|
|
213
|
+
expect(isFullWidth("➡")).toBe(true); // Right Arrow (U+27A1)
|
|
214
|
+
});
|
|
215
|
+
// Regional Indicator Symbols (for flags)
|
|
216
|
+
test("returns true for regional indicators", () => {
|
|
217
|
+
expect(isFullWidth("🇦")).toBe(true); // Regional Indicator A (U+1F1E6)
|
|
218
|
+
expect(isFullWidth("🇿")).toBe(true); // Regional Indicator Z (U+1F1FF)
|
|
219
|
+
});
|
|
220
|
+
// Extended emoji ranges (newer emoji)
|
|
221
|
+
test("returns true for Symbols Extended-A emoji (U+1FA70-U+1FAFF)", () => {
|
|
222
|
+
expect(isFullWidth("🥽")).toBe(true); // Goggles (U+1F97D - in 1F900-1F9FF)
|
|
223
|
+
expect(isFullWidth("🪐")).toBe(true); // Ringed Planet (U+1FA90)
|
|
224
|
+
expect(isFullWidth("🪴")).toBe(true); // Potted Plant (U+1FAB4)
|
|
225
|
+
expect(isFullWidth("🫀")).toBe(true); // Anatomical Heart (U+1FAC0)
|
|
226
|
+
expect(isFullWidth("🫠")).toBe(true); // Melting Face (U+1FAE0)
|
|
227
|
+
});
|
|
228
|
+
// Chess symbols (U+1FA00-U+1FA6F)
|
|
229
|
+
test("returns true for chess symbols", () => {
|
|
230
|
+
expect(isFullWidth("\u{1FA00}")).toBe(true); // Chess symbols start
|
|
231
|
+
});
|
|
232
|
+
// Media control symbols
|
|
233
|
+
test("returns true for media control symbols", () => {
|
|
234
|
+
expect(isFullWidth("⏩")).toBe(true); // Fast Forward (U+23E9)
|
|
235
|
+
expect(isFullWidth("⏪")).toBe(true); // Rewind (U+23EA)
|
|
236
|
+
expect(isFullWidth("⏯")).toBe(true); // Play/Pause (U+23EF)
|
|
237
|
+
expect(isFullWidth("⏰")).toBe(true); // Alarm Clock (U+23F0)
|
|
238
|
+
expect(isFullWidth("⌚")).toBe(true); // Watch (U+231A)
|
|
239
|
+
expect(isFullWidth("⌛")).toBe(true); // Hourglass (U+231B)
|
|
240
|
+
});
|
|
241
|
+
// Math operators as emoji
|
|
242
|
+
test("returns true for math emoji", () => {
|
|
243
|
+
expect(isFullWidth("➕")).toBe(true); // Plus (U+2795)
|
|
244
|
+
expect(isFullWidth("➖")).toBe(true); // Minus (U+2796)
|
|
245
|
+
expect(isFullWidth("➗")).toBe(true); // Division (U+2797)
|
|
246
|
+
});
|
|
247
|
+
// Question/exclamation marks
|
|
248
|
+
test("returns true for punctuation emoji", () => {
|
|
249
|
+
expect(isFullWidth("❓")).toBe(true); // Question Mark (U+2753)
|
|
250
|
+
expect(isFullWidth("❔")).toBe(true); // White Question Mark (U+2754)
|
|
251
|
+
expect(isFullWidth("❕")).toBe(true); // White Exclamation (U+2755)
|
|
252
|
+
expect(isFullWidth("❗")).toBe(true); // Exclamation Mark (U+2757)
|
|
253
|
+
});
|
|
254
|
+
// Arrows as emoji
|
|
255
|
+
test("returns true for arrow emoji", () => {
|
|
256
|
+
expect(isFullWidth("⬅")).toBe(true); // Left Arrow (U+2B05)
|
|
257
|
+
expect(isFullWidth("⬆")).toBe(true); // Up Arrow (U+2B06)
|
|
258
|
+
expect(isFullWidth("⬇")).toBe(true); // Down Arrow (U+2B07)
|
|
259
|
+
expect(isFullWidth("⤴")).toBe(true); // Right Arrow Curving Up (U+2934)
|
|
260
|
+
expect(isFullWidth("⤵")).toBe(true); // Right Arrow Curving Down (U+2935)
|
|
261
|
+
});
|
|
262
|
+
// Note: Basic arrows (U+2190-U+21FF) are typically narrow in terminals
|
|
263
|
+
test("returns false for basic arrows (narrow)", () => {
|
|
264
|
+
expect(isFullWidth("↩")).toBe(false); // Leftwards Arrow with Hook (U+21A9)
|
|
265
|
+
expect(isFullWidth("←")).toBe(false); // Leftwards Arrow (U+2190)
|
|
266
|
+
expect(isFullWidth("→")).toBe(false); // Rightwards Arrow (U+2192)
|
|
267
|
+
});
|
|
268
|
+
// Zodiac
|
|
269
|
+
test("returns true for zodiac symbols", () => {
|
|
270
|
+
expect(isFullWidth("♈")).toBe(true); // Aries (U+2648)
|
|
271
|
+
expect(isFullWidth("♓")).toBe(true); // Pisces (U+2653)
|
|
272
|
+
});
|
|
273
|
+
// Geometric shapes as emoji
|
|
274
|
+
test("returns true for geometric shape emoji", () => {
|
|
275
|
+
expect(isFullWidth("▶")).toBe(true); // Play Button (U+25B6)
|
|
276
|
+
expect(isFullWidth("◀")).toBe(true); // Reverse Button (U+25C0)
|
|
277
|
+
expect(isFullWidth("⬛")).toBe(true); // Black Large Square (U+2B1B)
|
|
278
|
+
expect(isFullWidth("⬜")).toBe(true); // White Large Square (U+2B1C)
|
|
279
|
+
expect(isFullWidth("⭕")).toBe(true); // Hollow Red Circle (U+2B55)
|
|
280
|
+
});
|
|
281
|
+
// Japanese-specific symbols
|
|
282
|
+
test("returns true for Japanese ideographic symbols", () => {
|
|
283
|
+
expect(isFullWidth("㊗")).toBe(true); // Circled Ideograph Congratulation (U+3297)
|
|
284
|
+
expect(isFullWidth("㊙")).toBe(true); // Circled Ideograph Secret (U+3299)
|
|
285
|
+
expect(isFullWidth("〰")).toBe(true); // Wavy Dash (U+3030)
|
|
286
|
+
expect(isFullWidth("〽")).toBe(true); // Part Alternation Mark (U+303D)
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
describe("displayWidth", () => {
|
|
290
|
+
test("returns length for ASCII string", () => {
|
|
291
|
+
expect(displayWidth("hello")).toBe(5);
|
|
292
|
+
});
|
|
293
|
+
test("excludes ANSI codes from width", () => {
|
|
294
|
+
const colored = c.red("test");
|
|
295
|
+
expect(displayWidth(colored)).toBe(4);
|
|
296
|
+
});
|
|
297
|
+
test("counts CJK characters as width 2", () => {
|
|
298
|
+
expect(displayWidth("한글")).toBe(4); // 2 chars * 2 width
|
|
299
|
+
expect(displayWidth("中文")).toBe(4);
|
|
300
|
+
expect(displayWidth("日本")).toBe(4);
|
|
301
|
+
});
|
|
302
|
+
test("handles mixed ASCII and CJK", () => {
|
|
303
|
+
expect(displayWidth("hello한글")).toBe(9); // 5 + 4
|
|
304
|
+
});
|
|
305
|
+
test("handles empty string", () => {
|
|
306
|
+
expect(displayWidth("")).toBe(0);
|
|
307
|
+
});
|
|
308
|
+
// Emoji width tests
|
|
309
|
+
test("counts common emoji as width 2", () => {
|
|
310
|
+
expect(displayWidth("😀")).toBe(2); // Single emoji
|
|
311
|
+
expect(displayWidth("😀😁")).toBe(4); // Two emoji
|
|
312
|
+
expect(displayWidth("👍")).toBe(2); // Thumbs up
|
|
313
|
+
expect(displayWidth("❤")).toBe(2); // Heart
|
|
314
|
+
});
|
|
315
|
+
test("handles mixed ASCII and emoji", () => {
|
|
316
|
+
expect(displayWidth("hi😀")).toBe(4); // 2 + 2
|
|
317
|
+
expect(displayWidth("test🚀done")).toBe(10); // 4 + 2 + 4
|
|
318
|
+
});
|
|
319
|
+
test("handles mixed CJK and emoji", () => {
|
|
320
|
+
expect(displayWidth("한글😀")).toBe(6); // 4 + 2
|
|
321
|
+
expect(displayWidth("日本🇯🇵")).toBe(8); // 4 + 2 + 2 (flag is 2 regional indicators)
|
|
322
|
+
});
|
|
323
|
+
test("handles emoji with variation selectors correctly", () => {
|
|
324
|
+
// Emoji with variation selector (U+FE0F) - the base char should be counted
|
|
325
|
+
// Note: variation selector is a combining character and adds 0 width
|
|
326
|
+
const heartWithSelector = "❤️"; // U+2764 + U+FE0F
|
|
327
|
+
// This should be 2 (for the heart) + 1 (for variation selector which isn't full-width)
|
|
328
|
+
// But variation selectors are typically invisible, so actual terminal behavior varies
|
|
329
|
+
expect(displayWidth(heartWithSelector)).toBeGreaterThanOrEqual(2);
|
|
330
|
+
});
|
|
331
|
+
test("handles strings with only emoji", () => {
|
|
332
|
+
expect(displayWidth("🎉🎊🎈")).toBe(6); // 3 emoji * 2 width each
|
|
333
|
+
});
|
|
334
|
+
test("handles newer extended emoji", () => {
|
|
335
|
+
expect(displayWidth("🪐")).toBe(2); // Ringed Planet
|
|
336
|
+
expect(displayWidth("🫠")).toBe(2); // Melting Face
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
describe("visibleLength alias", () => {
|
|
340
|
+
test("is the same as displayWidth", () => {
|
|
341
|
+
expect(visibleLength("test")).toBe(displayWidth("test"));
|
|
342
|
+
expect(visibleLength("한글")).toBe(displayWidth("한글"));
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
describe("pad", () => {
|
|
346
|
+
test("pads string to the right (left align) by default", () => {
|
|
347
|
+
expect(pad("hi", 5)).toBe("hi ");
|
|
348
|
+
});
|
|
349
|
+
test("pads string to the left (right align)", () => {
|
|
350
|
+
expect(pad("hi", 5, "right")).toBe(" hi");
|
|
351
|
+
});
|
|
352
|
+
test("centers string", () => {
|
|
353
|
+
expect(pad("hi", 6, "center")).toBe(" hi ");
|
|
354
|
+
});
|
|
355
|
+
test("handles odd padding for center", () => {
|
|
356
|
+
const result = pad("hi", 5, "center");
|
|
357
|
+
expect(result.length).toBe(5);
|
|
358
|
+
expect(result).toBe(" hi ");
|
|
359
|
+
});
|
|
360
|
+
test("returns original string if already at width", () => {
|
|
361
|
+
expect(pad("hello", 5)).toBe("hello");
|
|
362
|
+
});
|
|
363
|
+
test("returns original string if longer than width", () => {
|
|
364
|
+
expect(pad("hello world", 5)).toBe("hello world");
|
|
365
|
+
});
|
|
366
|
+
test("handles CJK characters correctly", () => {
|
|
367
|
+
const result = pad("한", 4); // "한" has display width 2
|
|
368
|
+
expect(displayWidth(result)).toBe(4);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
describe("padEnd alias", () => {
|
|
372
|
+
test("pads to the right", () => {
|
|
373
|
+
expect(padEnd("hi", 5)).toBe("hi ");
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
describe("padStart alias", () => {
|
|
377
|
+
test("pads to the left", () => {
|
|
378
|
+
expect(padStart("hi", 5)).toBe(" hi");
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
describe("center alias", () => {
|
|
382
|
+
test("centers the string", () => {
|
|
383
|
+
expect(center("hi", 6)).toBe(" hi ");
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
describe("truncateStr", () => {
|
|
387
|
+
test("truncates long string with ellipsis", () => {
|
|
388
|
+
const result = truncateStr("hello world", 8);
|
|
389
|
+
expect(result).toBe("hello...");
|
|
390
|
+
});
|
|
391
|
+
test("returns original string if within limit", () => {
|
|
392
|
+
expect(truncateStr("hello", 10)).toBe("hello");
|
|
393
|
+
});
|
|
394
|
+
test("uses custom suffix", () => {
|
|
395
|
+
const result = truncateStr("hello world", 9, "..");
|
|
396
|
+
expect(result).toBe("hello w..");
|
|
397
|
+
});
|
|
398
|
+
test("handles CJK characters", () => {
|
|
399
|
+
const result = truncateStr("한글테스트", 6);
|
|
400
|
+
expect(displayWidth(result)).toBeLessThanOrEqual(6);
|
|
401
|
+
});
|
|
402
|
+
test("handles exact length", () => {
|
|
403
|
+
expect(truncateStr("hello", 5)).toBe("hello");
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
describe("hline", () => {
|
|
407
|
+
test("creates horizontal line with default character", () => {
|
|
408
|
+
const result = hline(5);
|
|
409
|
+
expect(result).toBe("─────");
|
|
410
|
+
expect(result.length).toBe(5);
|
|
411
|
+
});
|
|
412
|
+
test("creates horizontal line with custom character", () => {
|
|
413
|
+
expect(hline(3, "=")).toBe("===");
|
|
414
|
+
});
|
|
415
|
+
});
|
|
416
|
+
// =============================================================================
|
|
417
|
+
// Progress Bar Tests
|
|
418
|
+
// =============================================================================
|
|
419
|
+
describe("progressBar", () => {
|
|
420
|
+
test("creates progress bar with default options", () => {
|
|
421
|
+
const result = progressBar(50, 100);
|
|
422
|
+
expect(stripAnsi(result)).toContain("50%");
|
|
423
|
+
});
|
|
424
|
+
test("shows 0% for empty progress", () => {
|
|
425
|
+
const result = progressBar(0, 100);
|
|
426
|
+
expect(stripAnsi(result)).toContain("0%");
|
|
427
|
+
});
|
|
428
|
+
test("shows 100% for complete progress", () => {
|
|
429
|
+
const result = progressBar(100, 100);
|
|
430
|
+
expect(stripAnsi(result)).toContain("100%");
|
|
431
|
+
});
|
|
432
|
+
test("handles zero total gracefully", () => {
|
|
433
|
+
const result = progressBar(0, 0);
|
|
434
|
+
expect(stripAnsi(result)).toContain("0%");
|
|
435
|
+
});
|
|
436
|
+
test("hides percent when showPercent is false", () => {
|
|
437
|
+
const result = progressBar(50, 100, { showPercent: false });
|
|
438
|
+
expect(stripAnsi(result)).not.toContain("%");
|
|
439
|
+
});
|
|
440
|
+
test("respects custom width", () => {
|
|
441
|
+
const result = progressBar(50, 100, { width: 10, showPercent: false });
|
|
442
|
+
// 10 characters total for the bar
|
|
443
|
+
expect(stripAnsi(result).length).toBe(10);
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
// =============================================================================
|
|
447
|
+
// Box Drawing Tests
|
|
448
|
+
// =============================================================================
|
|
449
|
+
describe("box", () => {
|
|
450
|
+
test("creates box around content", () => {
|
|
451
|
+
const result = box("hello");
|
|
452
|
+
expect(result).toContain("hello");
|
|
453
|
+
expect(result).toContain(BOX.rTopLeft);
|
|
454
|
+
expect(result).toContain(BOX.rBottomRight);
|
|
455
|
+
});
|
|
456
|
+
test("includes title when provided", () => {
|
|
457
|
+
const result = box("content", { title: "Title" });
|
|
458
|
+
expect(result).toContain("Title");
|
|
459
|
+
});
|
|
460
|
+
test("uses square corners when rounded is false", () => {
|
|
461
|
+
const result = box("test", { rounded: false });
|
|
462
|
+
expect(result).toContain(BOX.topLeft);
|
|
463
|
+
expect(result).not.toContain(BOX.rTopLeft);
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
describe("drawBox", () => {
|
|
467
|
+
test("returns array of lines", () => {
|
|
468
|
+
const result = drawBox(["line 1", "line 2"]);
|
|
469
|
+
expect(Array.isArray(result)).toBe(true);
|
|
470
|
+
expect(result.length).toBeGreaterThan(2);
|
|
471
|
+
});
|
|
472
|
+
test("includes title when provided", () => {
|
|
473
|
+
// Use content wider than title to avoid edge case
|
|
474
|
+
const result = drawBox(["longer content here"], { title: "Title" });
|
|
475
|
+
expect(result.join("\n")).toContain("Title");
|
|
476
|
+
});
|
|
477
|
+
test("wraps content with borders", () => {
|
|
478
|
+
const result = drawBox(["test"]);
|
|
479
|
+
const joined = result.join("\n");
|
|
480
|
+
expect(joined).toContain(BOX.topLeft);
|
|
481
|
+
expect(joined).toContain(BOX.bottomRight);
|
|
482
|
+
expect(joined).toContain("test");
|
|
483
|
+
});
|
|
484
|
+
});
|
|
485
|
+
// =============================================================================
|
|
486
|
+
// Side-by-Side Layout Tests
|
|
487
|
+
// =============================================================================
|
|
488
|
+
describe("sideBySide", () => {
|
|
489
|
+
test("places boxes horizontally", () => {
|
|
490
|
+
const box1 = "A\nB";
|
|
491
|
+
const box2 = "X\nY";
|
|
492
|
+
const result = sideBySide([box1, box2]);
|
|
493
|
+
const lines = result.split("\n");
|
|
494
|
+
expect(lines[0]).toContain("A");
|
|
495
|
+
expect(lines[0]).toContain("X");
|
|
496
|
+
});
|
|
497
|
+
test("handles different heights", () => {
|
|
498
|
+
const short = "A";
|
|
499
|
+
const tall = "X\nY\nZ";
|
|
500
|
+
const result = sideBySide([short, tall]);
|
|
501
|
+
const lines = result.split("\n");
|
|
502
|
+
expect(lines.length).toBe(3);
|
|
503
|
+
});
|
|
504
|
+
test("respects gap parameter", () => {
|
|
505
|
+
const result = sideBySide(["A", "B"], 4);
|
|
506
|
+
expect(result).toContain(" "); // 4 space gap
|
|
507
|
+
});
|
|
508
|
+
});
|
|
509
|
+
describe("sideBySideArrays", () => {
|
|
510
|
+
test("merges two arrays side by side", () => {
|
|
511
|
+
const left = ["L1", "L2"];
|
|
512
|
+
const right = ["R1", "R2"];
|
|
513
|
+
const result = sideBySideArrays(left, right);
|
|
514
|
+
expect(result.length).toBe(2);
|
|
515
|
+
expect(result[0]).toContain("L1");
|
|
516
|
+
expect(result[0]).toContain("R1");
|
|
517
|
+
});
|
|
518
|
+
test("handles different length arrays", () => {
|
|
519
|
+
const left = ["L1"];
|
|
520
|
+
const right = ["R1", "R2", "R3"];
|
|
521
|
+
const result = sideBySideArrays(left, right);
|
|
522
|
+
expect(result.length).toBe(3);
|
|
523
|
+
});
|
|
524
|
+
test("respects gap parameter", () => {
|
|
525
|
+
const result = sideBySideArrays(["L"], ["R"], 5);
|
|
526
|
+
expect(result[0]).toContain(" "); // 5 space gap
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
// =============================================================================
|
|
530
|
+
// Table Tests
|
|
531
|
+
// =============================================================================
|
|
532
|
+
describe("table", () => {
|
|
533
|
+
test("renders table with headers and data", () => {
|
|
534
|
+
const data = [
|
|
535
|
+
{ name: "Alice", age: 30 },
|
|
536
|
+
{ name: "Bob", age: 25 },
|
|
537
|
+
];
|
|
538
|
+
const columns = [
|
|
539
|
+
{ key: "name", header: "Name" },
|
|
540
|
+
{ key: "age", header: "Age" },
|
|
541
|
+
];
|
|
542
|
+
const result = table(data, columns);
|
|
543
|
+
expect(result).toContain("Name");
|
|
544
|
+
expect(result).toContain("Age");
|
|
545
|
+
expect(result).toContain("Alice");
|
|
546
|
+
expect(result).toContain("Bob");
|
|
547
|
+
});
|
|
548
|
+
test("uses custom format function", () => {
|
|
549
|
+
const data = [{ value: 100 }];
|
|
550
|
+
const columns = [
|
|
551
|
+
{
|
|
552
|
+
key: "value",
|
|
553
|
+
header: "Value",
|
|
554
|
+
format: (v) => `$${v}`,
|
|
555
|
+
},
|
|
556
|
+
];
|
|
557
|
+
const result = table(data, columns);
|
|
558
|
+
expect(result).toContain("$100");
|
|
559
|
+
});
|
|
560
|
+
test("handles empty data", () => {
|
|
561
|
+
const columns = [{ key: "name", header: "Name" }];
|
|
562
|
+
const result = table([], columns);
|
|
563
|
+
expect(result).toContain("Name");
|
|
564
|
+
});
|
|
565
|
+
});
|
|
566
|
+
describe("renderTable", () => {
|
|
567
|
+
test("returns array of lines with borders", () => {
|
|
568
|
+
const columns = [{ key: "id", header: "ID" }];
|
|
569
|
+
const rows = [{ id: 1 }];
|
|
570
|
+
const result = renderTable(columns, rows);
|
|
571
|
+
expect(Array.isArray(result)).toBe(true);
|
|
572
|
+
expect(result.join("\n")).toContain(BOX.topLeft);
|
|
573
|
+
expect(result.join("\n")).toContain(BOX.bottomRight);
|
|
574
|
+
});
|
|
575
|
+
test("aligns columns correctly", () => {
|
|
576
|
+
const columns = [
|
|
577
|
+
{ key: "name", header: "Name", align: "left" },
|
|
578
|
+
{ key: "count", header: "Count", align: "right" },
|
|
579
|
+
];
|
|
580
|
+
const rows = [{ name: "Test", count: 42 }];
|
|
581
|
+
const result = renderTable(columns, rows);
|
|
582
|
+
expect(result.join("\n")).toContain("Test");
|
|
583
|
+
expect(result.join("\n")).toContain("42");
|
|
584
|
+
});
|
|
585
|
+
});
|
|
586
|
+
// =============================================================================
|
|
587
|
+
// Status & Priority Formatter Tests
|
|
588
|
+
// =============================================================================
|
|
589
|
+
describe("formatStatus", () => {
|
|
590
|
+
test("formats pending status with icon", () => {
|
|
591
|
+
const result = formatStatus("pending");
|
|
592
|
+
expect(stripAnsi(result)).toContain("pending");
|
|
593
|
+
expect(stripAnsi(result)).toContain("○");
|
|
594
|
+
});
|
|
595
|
+
test("formats completed status", () => {
|
|
596
|
+
const result = formatStatus("completed");
|
|
597
|
+
expect(stripAnsi(result)).toContain("completed");
|
|
598
|
+
expect(stripAnsi(result)).toContain("●");
|
|
599
|
+
});
|
|
600
|
+
test("formats blocked status", () => {
|
|
601
|
+
const result = formatStatus("blocked");
|
|
602
|
+
expect(stripAnsi(result)).toContain("blocked");
|
|
603
|
+
});
|
|
604
|
+
test("handles unknown status", () => {
|
|
605
|
+
const result = formatStatus("unknown");
|
|
606
|
+
expect(stripAnsi(result)).toContain("unknown");
|
|
607
|
+
expect(stripAnsi(result)).toContain("?");
|
|
608
|
+
});
|
|
609
|
+
});
|
|
610
|
+
describe("formatPriority", () => {
|
|
611
|
+
test("formats critical priority", () => {
|
|
612
|
+
const result = formatPriority("critical");
|
|
613
|
+
expect(stripAnsi(result)).toBe("critical");
|
|
614
|
+
});
|
|
615
|
+
test("formats high priority", () => {
|
|
616
|
+
const result = formatPriority("high");
|
|
617
|
+
expect(stripAnsi(result)).toBe("high");
|
|
618
|
+
});
|
|
619
|
+
test("handles unknown priority", () => {
|
|
620
|
+
const result = formatPriority("unknown");
|
|
621
|
+
expect(stripAnsi(result)).toBe("unknown");
|
|
622
|
+
});
|
|
623
|
+
});
|
|
624
|
+
describe("formatDependencies", () => {
|
|
625
|
+
test("formats empty dependencies as None", () => {
|
|
626
|
+
const result = formatDependencies([]);
|
|
627
|
+
expect(stripAnsi(result)).toBe("None");
|
|
628
|
+
});
|
|
629
|
+
test("formats single dependency", () => {
|
|
630
|
+
const result = formatDependencies(["task-1"]);
|
|
631
|
+
expect(stripAnsi(result)).toBe("task-1");
|
|
632
|
+
});
|
|
633
|
+
test("formats multiple dependencies with commas", () => {
|
|
634
|
+
const result = formatDependencies(["task-1", "task-2", "task-3"]);
|
|
635
|
+
expect(stripAnsi(result)).toBe("task-1, task-2, task-3");
|
|
636
|
+
});
|
|
637
|
+
});
|
|
638
|
+
// =============================================================================
|
|
639
|
+
// Banner Tests
|
|
640
|
+
// =============================================================================
|
|
641
|
+
describe("banner", () => {
|
|
642
|
+
test("creates ASCII art banner", () => {
|
|
643
|
+
const result = banner("TASK");
|
|
644
|
+
expect(result).toContain("█");
|
|
645
|
+
const lines = result.split("\n");
|
|
646
|
+
expect(lines.length).toBe(5);
|
|
647
|
+
});
|
|
648
|
+
test("handles spaces", () => {
|
|
649
|
+
const result = banner("A B");
|
|
650
|
+
expect(result).toBeTruthy();
|
|
651
|
+
});
|
|
652
|
+
test("handles unknown characters gracefully", () => {
|
|
653
|
+
const result = banner("XYZ");
|
|
654
|
+
// Should not throw, returns spaces for unknown chars
|
|
655
|
+
expect(result).toBeTruthy();
|
|
656
|
+
});
|
|
657
|
+
});
|
|
658
|
+
// =============================================================================
|
|
659
|
+
// BOX Constants Tests
|
|
660
|
+
// =============================================================================
|
|
661
|
+
describe("BOX constants", () => {
|
|
662
|
+
test("has single line box characters", () => {
|
|
663
|
+
expect(BOX.topLeft).toBe("┌");
|
|
664
|
+
expect(BOX.topRight).toBe("┐");
|
|
665
|
+
expect(BOX.bottomLeft).toBe("└");
|
|
666
|
+
expect(BOX.bottomRight).toBe("┘");
|
|
667
|
+
expect(BOX.horizontal).toBe("─");
|
|
668
|
+
expect(BOX.vertical).toBe("│");
|
|
669
|
+
});
|
|
670
|
+
test("has rounded corner characters", () => {
|
|
671
|
+
expect(BOX.rTopLeft).toBe("╭");
|
|
672
|
+
expect(BOX.rTopRight).toBe("╮");
|
|
673
|
+
expect(BOX.rBottomLeft).toBe("╰");
|
|
674
|
+
expect(BOX.rBottomRight).toBe("╯");
|
|
675
|
+
});
|
|
676
|
+
test("has double line characters", () => {
|
|
677
|
+
expect(BOX.dblTopLeft).toBe("╔");
|
|
678
|
+
expect(BOX.dblBottomRight).toBe("╝");
|
|
679
|
+
expect(BOX.dblHorizontal).toBe("═");
|
|
680
|
+
expect(BOX.dblVertical).toBe("║");
|
|
681
|
+
});
|
|
682
|
+
});
|
|
683
|
+
//# sourceMappingURL=terminal-ui.test.js.map
|