@task-mcp/shared 1.0.20 → 1.0.21

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 (167) hide show
  1. package/package.json +1 -6
  2. package/dist/algorithms/critical-path.d.ts +0 -46
  3. package/dist/algorithms/critical-path.d.ts.map +0 -1
  4. package/dist/algorithms/critical-path.js +0 -320
  5. package/dist/algorithms/critical-path.js.map +0 -1
  6. package/dist/algorithms/critical-path.test.d.ts +0 -2
  7. package/dist/algorithms/critical-path.test.d.ts.map +0 -1
  8. package/dist/algorithms/critical-path.test.js +0 -194
  9. package/dist/algorithms/critical-path.test.js.map +0 -1
  10. package/dist/algorithms/dependency-integrity.d.ts +0 -81
  11. package/dist/algorithms/dependency-integrity.d.ts.map +0 -1
  12. package/dist/algorithms/dependency-integrity.js +0 -207
  13. package/dist/algorithms/dependency-integrity.js.map +0 -1
  14. package/dist/algorithms/dependency-integrity.test.d.ts +0 -2
  15. package/dist/algorithms/dependency-integrity.test.d.ts.map +0 -1
  16. package/dist/algorithms/dependency-integrity.test.js +0 -309
  17. package/dist/algorithms/dependency-integrity.test.js.map +0 -1
  18. package/dist/algorithms/index.d.ts +0 -5
  19. package/dist/algorithms/index.d.ts.map +0 -1
  20. package/dist/algorithms/index.js +0 -5
  21. package/dist/algorithms/index.js.map +0 -1
  22. package/dist/algorithms/tech-analysis.d.ts +0 -106
  23. package/dist/algorithms/tech-analysis.d.ts.map +0 -1
  24. package/dist/algorithms/tech-analysis.js +0 -344
  25. package/dist/algorithms/tech-analysis.js.map +0 -1
  26. package/dist/algorithms/tech-analysis.test.d.ts +0 -2
  27. package/dist/algorithms/tech-analysis.test.d.ts.map +0 -1
  28. package/dist/algorithms/tech-analysis.test.js +0 -338
  29. package/dist/algorithms/tech-analysis.test.js.map +0 -1
  30. package/dist/algorithms/topological-sort.d.ts +0 -41
  31. package/dist/algorithms/topological-sort.d.ts.map +0 -1
  32. package/dist/algorithms/topological-sort.js +0 -165
  33. package/dist/algorithms/topological-sort.js.map +0 -1
  34. package/dist/algorithms/topological-sort.test.d.ts +0 -2
  35. package/dist/algorithms/topological-sort.test.d.ts.map +0 -1
  36. package/dist/algorithms/topological-sort.test.js +0 -162
  37. package/dist/algorithms/topological-sort.test.js.map +0 -1
  38. package/dist/index.d.ts +0 -4
  39. package/dist/index.d.ts.map +0 -1
  40. package/dist/index.js +0 -7
  41. package/dist/index.js.map +0 -1
  42. package/dist/schemas/inbox.d.ts +0 -55
  43. package/dist/schemas/inbox.d.ts.map +0 -1
  44. package/dist/schemas/inbox.js +0 -25
  45. package/dist/schemas/inbox.js.map +0 -1
  46. package/dist/schemas/index.d.ts +0 -7
  47. package/dist/schemas/index.d.ts.map +0 -1
  48. package/dist/schemas/index.js +0 -17
  49. package/dist/schemas/index.js.map +0 -1
  50. package/dist/schemas/project.d.ts +0 -177
  51. package/dist/schemas/project.d.ts.map +0 -1
  52. package/dist/schemas/project.js +0 -56
  53. package/dist/schemas/project.js.map +0 -1
  54. package/dist/schemas/response-format.d.ts +0 -148
  55. package/dist/schemas/response-format.d.ts.map +0 -1
  56. package/dist/schemas/response-format.js +0 -18
  57. package/dist/schemas/response-format.js.map +0 -1
  58. package/dist/schemas/response-schema.d.ts +0 -307
  59. package/dist/schemas/response-schema.d.ts.map +0 -1
  60. package/dist/schemas/response-schema.js +0 -75
  61. package/dist/schemas/response-schema.js.map +0 -1
  62. package/dist/schemas/response-schema.test.d.ts +0 -2
  63. package/dist/schemas/response-schema.test.d.ts.map +0 -1
  64. package/dist/schemas/response-schema.test.js +0 -256
  65. package/dist/schemas/response-schema.test.js.map +0 -1
  66. package/dist/schemas/state.d.ts +0 -17
  67. package/dist/schemas/state.d.ts.map +0 -1
  68. package/dist/schemas/state.js +0 -17
  69. package/dist/schemas/state.js.map +0 -1
  70. package/dist/schemas/task.d.ts +0 -881
  71. package/dist/schemas/task.d.ts.map +0 -1
  72. package/dist/schemas/task.js +0 -189
  73. package/dist/schemas/task.js.map +0 -1
  74. package/dist/schemas/view.d.ts +0 -143
  75. package/dist/schemas/view.d.ts.map +0 -1
  76. package/dist/schemas/view.js +0 -48
  77. package/dist/schemas/view.js.map +0 -1
  78. package/dist/utils/dashboard-renderer.d.ts +0 -93
  79. package/dist/utils/dashboard-renderer.d.ts.map +0 -1
  80. package/dist/utils/dashboard-renderer.js +0 -424
  81. package/dist/utils/dashboard-renderer.js.map +0 -1
  82. package/dist/utils/dashboard-renderer.test.d.ts +0 -2
  83. package/dist/utils/dashboard-renderer.test.d.ts.map +0 -1
  84. package/dist/utils/dashboard-renderer.test.js +0 -774
  85. package/dist/utils/dashboard-renderer.test.js.map +0 -1
  86. package/dist/utils/date.d.ts +0 -94
  87. package/dist/utils/date.d.ts.map +0 -1
  88. package/dist/utils/date.js +0 -323
  89. package/dist/utils/date.js.map +0 -1
  90. package/dist/utils/date.test.d.ts +0 -2
  91. package/dist/utils/date.test.d.ts.map +0 -1
  92. package/dist/utils/date.test.js +0 -276
  93. package/dist/utils/date.test.js.map +0 -1
  94. package/dist/utils/hierarchy.d.ts +0 -102
  95. package/dist/utils/hierarchy.d.ts.map +0 -1
  96. package/dist/utils/hierarchy.js +0 -236
  97. package/dist/utils/hierarchy.js.map +0 -1
  98. package/dist/utils/hierarchy.test.d.ts +0 -2
  99. package/dist/utils/hierarchy.test.d.ts.map +0 -1
  100. package/dist/utils/hierarchy.test.js +0 -436
  101. package/dist/utils/hierarchy.test.js.map +0 -1
  102. package/dist/utils/id.d.ts +0 -60
  103. package/dist/utils/id.d.ts.map +0 -1
  104. package/dist/utils/id.js +0 -118
  105. package/dist/utils/id.js.map +0 -1
  106. package/dist/utils/id.test.d.ts +0 -2
  107. package/dist/utils/id.test.d.ts.map +0 -1
  108. package/dist/utils/id.test.js +0 -193
  109. package/dist/utils/id.test.js.map +0 -1
  110. package/dist/utils/index.d.ts +0 -12
  111. package/dist/utils/index.d.ts.map +0 -1
  112. package/dist/utils/index.js +0 -34
  113. package/dist/utils/index.js.map +0 -1
  114. package/dist/utils/natural-language.d.ts +0 -57
  115. package/dist/utils/natural-language.d.ts.map +0 -1
  116. package/dist/utils/natural-language.js +0 -211
  117. package/dist/utils/natural-language.js.map +0 -1
  118. package/dist/utils/natural-language.test.d.ts +0 -2
  119. package/dist/utils/natural-language.test.d.ts.map +0 -1
  120. package/dist/utils/natural-language.test.js +0 -197
  121. package/dist/utils/natural-language.test.js.map +0 -1
  122. package/dist/utils/priority-queue.d.ts +0 -17
  123. package/dist/utils/priority-queue.d.ts.map +0 -1
  124. package/dist/utils/priority-queue.js +0 -62
  125. package/dist/utils/priority-queue.js.map +0 -1
  126. package/dist/utils/priority-queue.test.d.ts +0 -2
  127. package/dist/utils/priority-queue.test.d.ts.map +0 -1
  128. package/dist/utils/priority-queue.test.js +0 -82
  129. package/dist/utils/priority-queue.test.js.map +0 -1
  130. package/dist/utils/projection.d.ts +0 -65
  131. package/dist/utils/projection.d.ts.map +0 -1
  132. package/dist/utils/projection.js +0 -180
  133. package/dist/utils/projection.js.map +0 -1
  134. package/dist/utils/projection.test.d.ts +0 -2
  135. package/dist/utils/projection.test.d.ts.map +0 -1
  136. package/dist/utils/projection.test.js +0 -336
  137. package/dist/utils/projection.test.js.map +0 -1
  138. package/dist/utils/terminal-ui.d.ts +0 -208
  139. package/dist/utils/terminal-ui.d.ts.map +0 -1
  140. package/dist/utils/terminal-ui.js +0 -611
  141. package/dist/utils/terminal-ui.js.map +0 -1
  142. package/dist/utils/terminal-ui.test.d.ts +0 -2
  143. package/dist/utils/terminal-ui.test.d.ts.map +0 -1
  144. package/dist/utils/terminal-ui.test.js +0 -683
  145. package/dist/utils/terminal-ui.test.js.map +0 -1
  146. package/dist/utils/workspace.d.ts +0 -100
  147. package/dist/utils/workspace.d.ts.map +0 -1
  148. package/dist/utils/workspace.js +0 -173
  149. package/dist/utils/workspace.js.map +0 -1
  150. package/dist/utils/workspace.test.d.ts +0 -2
  151. package/dist/utils/workspace.test.d.ts.map +0 -1
  152. package/dist/utils/workspace.test.js +0 -97
  153. package/dist/utils/workspace.test.js.map +0 -1
  154. package/src/algorithms/critical-path.test.ts +0 -241
  155. package/src/algorithms/dependency-integrity.test.ts +0 -348
  156. package/src/algorithms/tech-analysis.test.ts +0 -413
  157. package/src/algorithms/topological-sort.test.ts +0 -190
  158. package/src/schemas/response-schema.test.ts +0 -314
  159. package/src/utils/dashboard-renderer.test.ts +0 -983
  160. package/src/utils/date.test.ts +0 -329
  161. package/src/utils/hierarchy.test.ts +0 -505
  162. package/src/utils/id.test.ts +0 -235
  163. package/src/utils/natural-language.test.ts +0 -242
  164. package/src/utils/priority-queue.test.ts +0 -103
  165. package/src/utils/projection.test.ts +0 -425
  166. package/src/utils/terminal-ui.test.ts +0 -831
  167. package/src/utils/workspace.test.ts +0 -125
@@ -1,683 +0,0 @@
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