@f0rbit/overview 0.1.0 → 0.2.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/bin/overview +10 -0
- package/package.json +14 -4
- package/packages/core/package.json +2 -2
- package/packages/render/package.json +2 -2
- package/packages/render/src/components/__tests__/widget-container.integration.test.tsx +2 -0
- package/packages/render/src/lib/__tests__/fetch-context.test.ts +2 -2
- package/packages/render/src/lib/__tests__/widget-grid.test.ts +136 -136
package/bin/overview
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Resolve symlink to find the actual package location
|
|
3
|
+
SOURCE="${BASH_SOURCE[0]}"
|
|
4
|
+
while [ -L "$SOURCE" ]; do
|
|
5
|
+
DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
|
|
6
|
+
SOURCE="$(readlink "$SOURCE")"
|
|
7
|
+
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE"
|
|
8
|
+
done
|
|
9
|
+
DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
|
|
10
|
+
exec bun "$DIR/../packages/render/src/overview.tsx" "$@"
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@f0rbit/overview",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Terminal UI dashboard for multi-repo git health",
|
|
5
|
-
"workspaces": [
|
|
5
|
+
"workspaces": [
|
|
6
|
+
"packages/*"
|
|
7
|
+
],
|
|
6
8
|
"bin": {
|
|
7
|
-
"overview": "
|
|
9
|
+
"overview": "bin/overview"
|
|
8
10
|
},
|
|
9
11
|
"files": [
|
|
10
12
|
"packages/",
|
|
13
|
+
"bin/",
|
|
11
14
|
"bunfig.toml",
|
|
12
15
|
"tsconfig.json"
|
|
13
16
|
],
|
|
@@ -23,7 +26,14 @@
|
|
|
23
26
|
"url": "https://github.com/f0rbit/overview.git"
|
|
24
27
|
},
|
|
25
28
|
"license": "MIT",
|
|
26
|
-
"keywords": [
|
|
29
|
+
"keywords": [
|
|
30
|
+
"git",
|
|
31
|
+
"tui",
|
|
32
|
+
"terminal",
|
|
33
|
+
"dashboard",
|
|
34
|
+
"bun",
|
|
35
|
+
"solidjs"
|
|
36
|
+
],
|
|
27
37
|
"engines": {
|
|
28
38
|
"bun": ">=1.0.0"
|
|
29
39
|
},
|
|
@@ -16,7 +16,7 @@ describe("createFetchContext", () => {
|
|
|
16
16
|
// Wait for debounce
|
|
17
17
|
await Bun.sleep(80);
|
|
18
18
|
|
|
19
|
-
expect(result).toBe("hello");
|
|
19
|
+
expect(result!).toBe("hello");
|
|
20
20
|
ctx.dispose();
|
|
21
21
|
});
|
|
22
22
|
|
|
@@ -46,7 +46,7 @@ describe("createFetchContext", () => {
|
|
|
46
46
|
|
|
47
47
|
await Bun.sleep(10);
|
|
48
48
|
|
|
49
|
-
expect(result).toBe("now");
|
|
49
|
+
expect(result!).toBe("now");
|
|
50
50
|
ctx.dispose();
|
|
51
51
|
});
|
|
52
52
|
|
|
@@ -88,10 +88,10 @@ describe("computeRows", () => {
|
|
|
88
88
|
const rows = computeRows(widgets, 80);
|
|
89
89
|
|
|
90
90
|
expect(rows).toHaveLength(1);
|
|
91
|
-
expect(rows[0]
|
|
92
|
-
expect(rows[0]
|
|
93
|
-
expect(rows[0]
|
|
94
|
-
expect(rows[0]
|
|
91
|
+
expect(rows[0]!.columns).toBe(2);
|
|
92
|
+
expect(rows[0]!.widgets).toHaveLength(2);
|
|
93
|
+
expect(rows[0]!.widgets[0]!.id).toBe("git-status");
|
|
94
|
+
expect(rows[0]!.widgets[1]!.id).toBe("repo-meta");
|
|
95
95
|
});
|
|
96
96
|
|
|
97
97
|
test("half + full → sorted by priority: half(p0) before full(p1)", () => {
|
|
@@ -102,10 +102,10 @@ describe("computeRows", () => {
|
|
|
102
102
|
const rows = computeRows(widgets, 80);
|
|
103
103
|
|
|
104
104
|
expect(rows).toHaveLength(2);
|
|
105
|
-
expect(rows[0]
|
|
106
|
-
expect(rows[0]
|
|
107
|
-
expect(rows[1]
|
|
108
|
-
expect(rows[1]
|
|
105
|
+
expect(rows[0]!.columns).toBe(1);
|
|
106
|
+
expect(rows[0]!.widgets[0]!.id).toBe("git-status");
|
|
107
|
+
expect(rows[1]!.columns).toBe(1);
|
|
108
|
+
expect(rows[1]!.widgets[0]!.id).toBe("recent-commits");
|
|
109
109
|
});
|
|
110
110
|
|
|
111
111
|
test("full + half + half → full(p0) first, half pair(p1) second", () => {
|
|
@@ -117,11 +117,11 @@ describe("computeRows", () => {
|
|
|
117
117
|
const rows = computeRows(widgets, 80);
|
|
118
118
|
|
|
119
119
|
expect(rows).toHaveLength(2);
|
|
120
|
-
expect(rows[0]
|
|
121
|
-
expect(rows[0]
|
|
122
|
-
expect(rows[1]
|
|
123
|
-
expect(rows[1]
|
|
124
|
-
expect(rows[1]
|
|
120
|
+
expect(rows[0]!.columns).toBe(1);
|
|
121
|
+
expect(rows[0]!.widgets[0]!.id).toBe("recent-commits");
|
|
122
|
+
expect(rows[1]!.columns).toBe(2);
|
|
123
|
+
expect(rows[1]!.widgets[0]!.id).toBe("git-status");
|
|
124
|
+
expect(rows[1]!.widgets[1]!.id).toBe("repo-meta");
|
|
125
125
|
});
|
|
126
126
|
|
|
127
127
|
test("three half-width → 2-column row + 1-column row", () => {
|
|
@@ -133,11 +133,11 @@ describe("computeRows", () => {
|
|
|
133
133
|
const rows = computeRows(widgets, 80);
|
|
134
134
|
|
|
135
135
|
expect(rows).toHaveLength(2);
|
|
136
|
-
expect(rows[0]
|
|
137
|
-
expect(rows[0]
|
|
138
|
-
expect(rows[1]
|
|
139
|
-
expect(rows[1]
|
|
140
|
-
expect(rows[1]
|
|
136
|
+
expect(rows[0]!.columns).toBe(2);
|
|
137
|
+
expect(rows[0]!.widgets).toHaveLength(2);
|
|
138
|
+
expect(rows[1]!.columns).toBe(1);
|
|
139
|
+
expect(rows[1]!.widgets).toHaveLength(1);
|
|
140
|
+
expect(rows[1]!.widgets[0]!.id).toBe("branch-list");
|
|
141
141
|
});
|
|
142
142
|
|
|
143
143
|
test("single full-width → 1 one-column row", () => {
|
|
@@ -145,8 +145,8 @@ describe("computeRows", () => {
|
|
|
145
145
|
const rows = computeRows(widgets, 80);
|
|
146
146
|
|
|
147
147
|
expect(rows).toHaveLength(1);
|
|
148
|
-
expect(rows[0]
|
|
149
|
-
expect(rows[0]
|
|
148
|
+
expect(rows[0]!.columns).toBe(1);
|
|
149
|
+
expect(rows[0]!.widgets[0]!.id).toBe("recent-commits");
|
|
150
150
|
});
|
|
151
151
|
|
|
152
152
|
test("narrow panel (< 40) → all half become full, each own row", () => {
|
|
@@ -177,9 +177,9 @@ describe("computeRows", () => {
|
|
|
177
177
|
const rows = computeRows(widgets, 80);
|
|
178
178
|
|
|
179
179
|
expect(rows).toHaveLength(1);
|
|
180
|
-
expect(rows[0]
|
|
181
|
-
expect(rows[0]
|
|
182
|
-
expect(rows[0]
|
|
180
|
+
expect(rows[0]!.columns).toBe(2);
|
|
181
|
+
expect(rows[0]!.widgets[0]!.id).toBe("git-status");
|
|
182
|
+
expect(rows[0]!.widgets[1]!.id).toBe("branch-list");
|
|
183
183
|
});
|
|
184
184
|
|
|
185
185
|
test("order is preserved for same-type widgets", () => {
|
|
@@ -192,10 +192,10 @@ describe("computeRows", () => {
|
|
|
192
192
|
const rows = computeRows(widgets, 80);
|
|
193
193
|
|
|
194
194
|
expect(rows).toHaveLength(2);
|
|
195
|
-
expect(rows[0]
|
|
196
|
-
expect(rows[0]
|
|
197
|
-
expect(rows[1]
|
|
198
|
-
expect(rows[1]
|
|
195
|
+
expect(rows[0]!.widgets[0]!.id).toBe("github-prs");
|
|
196
|
+
expect(rows[0]!.widgets[1]!.id).toBe("github-issues");
|
|
197
|
+
expect(rows[1]!.widgets[0]!.id).toBe("github-ci");
|
|
198
|
+
expect(rows[1]!.widgets[1]!.id).toBe("devpad-tasks");
|
|
199
199
|
});
|
|
200
200
|
|
|
201
201
|
test("non-contiguous halfs pair together", () => {
|
|
@@ -208,11 +208,11 @@ describe("computeRows", () => {
|
|
|
208
208
|
|
|
209
209
|
expect(rows).toHaveLength(2);
|
|
210
210
|
// half pair has min priority 0, full has priority 1 → pair first
|
|
211
|
-
expect(rows[0]
|
|
212
|
-
expect(rows[0]
|
|
213
|
-
expect(rows[0]
|
|
214
|
-
expect(rows[1]
|
|
215
|
-
expect(rows[1]
|
|
211
|
+
expect(rows[0]!.columns).toBe(2);
|
|
212
|
+
expect(rows[0]!.widgets[0]!.id).toBe("git-status");
|
|
213
|
+
expect(rows[0]!.widgets[1]!.id).toBe("repo-meta");
|
|
214
|
+
expect(rows[1]!.columns).toBe(1);
|
|
215
|
+
expect(rows[1]!.widgets[0]!.id).toBe("recent-commits");
|
|
216
216
|
});
|
|
217
217
|
|
|
218
218
|
test("full-width at priority 0 comes before halfs at priority 3+4", () => {
|
|
@@ -224,11 +224,11 @@ describe("computeRows", () => {
|
|
|
224
224
|
const rows = computeRows(widgets, 80);
|
|
225
225
|
|
|
226
226
|
expect(rows).toHaveLength(2);
|
|
227
|
-
expect(rows[0]
|
|
228
|
-
expect(rows[0]
|
|
229
|
-
expect(rows[1]
|
|
230
|
-
expect(rows[1]
|
|
231
|
-
expect(rows[1]
|
|
227
|
+
expect(rows[0]!.columns).toBe(1);
|
|
228
|
+
expect(rows[0]!.widgets[0]!.id).toBe("recent-commits");
|
|
229
|
+
expect(rows[1]!.columns).toBe(2);
|
|
230
|
+
expect(rows[1]!.widgets[0]!.id).toBe("git-status");
|
|
231
|
+
expect(rows[1]!.widgets[1]!.id).toBe("repo-meta");
|
|
232
232
|
});
|
|
233
233
|
|
|
234
234
|
test("odd number of halfs → trailing half gets 1-column row", () => {
|
|
@@ -242,14 +242,14 @@ describe("computeRows", () => {
|
|
|
242
242
|
const rows = computeRows(widgets, 80);
|
|
243
243
|
|
|
244
244
|
expect(rows).toHaveLength(3);
|
|
245
|
-
expect(rows[0]
|
|
246
|
-
expect(rows[0]
|
|
247
|
-
expect(rows[0]
|
|
248
|
-
expect(rows[1]
|
|
249
|
-
expect(rows[1]
|
|
250
|
-
expect(rows[1]
|
|
251
|
-
expect(rows[2]
|
|
252
|
-
expect(rows[2]
|
|
245
|
+
expect(rows[0]!.columns).toBe(2);
|
|
246
|
+
expect(rows[0]!.widgets[0]!.id).toBe("git-status");
|
|
247
|
+
expect(rows[0]!.widgets[1]!.id).toBe("repo-meta");
|
|
248
|
+
expect(rows[1]!.columns).toBe(2);
|
|
249
|
+
expect(rows[1]!.widgets[0]!.id).toBe("branch-list");
|
|
250
|
+
expect(rows[1]!.widgets[1]!.id).toBe("recent-commits");
|
|
251
|
+
expect(rows[2]!.columns).toBe(1);
|
|
252
|
+
expect(rows[2]!.widgets[0]!.id).toBe("github-prs");
|
|
253
253
|
});
|
|
254
254
|
|
|
255
255
|
test("three third-width widgets → 1 three-column row", () => {
|
|
@@ -261,11 +261,11 @@ describe("computeRows", () => {
|
|
|
261
261
|
const rows = computeRows(widgets, 80);
|
|
262
262
|
|
|
263
263
|
expect(rows).toHaveLength(1);
|
|
264
|
-
expect(rows[0]
|
|
265
|
-
expect(rows[0]
|
|
266
|
-
expect(rows[0]
|
|
267
|
-
expect(rows[0]
|
|
268
|
-
expect(rows[0]
|
|
264
|
+
expect(rows[0]!.columns).toBe(3);
|
|
265
|
+
expect(rows[0]!.widgets).toHaveLength(3);
|
|
266
|
+
expect(rows[0]!.widgets[0]!.id).toBe("git-status");
|
|
267
|
+
expect(rows[0]!.widgets[1]!.id).toBe("repo-meta");
|
|
268
|
+
expect(rows[0]!.widgets[2]!.id).toBe("github-ci");
|
|
269
269
|
});
|
|
270
270
|
|
|
271
271
|
test("four thirds → 3-col row + 1-col row (auto-expand)", () => {
|
|
@@ -278,11 +278,11 @@ describe("computeRows", () => {
|
|
|
278
278
|
const rows = computeRows(widgets, 80);
|
|
279
279
|
|
|
280
280
|
expect(rows).toHaveLength(2);
|
|
281
|
-
expect(rows[0]
|
|
282
|
-
expect(rows[0]
|
|
283
|
-
expect(rows[1]
|
|
284
|
-
expect(rows[1]
|
|
285
|
-
expect(rows[1]
|
|
281
|
+
expect(rows[0]!.columns).toBe(3);
|
|
282
|
+
expect(rows[0]!.widgets).toHaveLength(3);
|
|
283
|
+
expect(rows[1]!.columns).toBe(1);
|
|
284
|
+
expect(rows[1]!.widgets).toHaveLength(1);
|
|
285
|
+
expect(rows[1]!.widgets[0]!.id).toBe("commit-activity");
|
|
286
286
|
});
|
|
287
287
|
|
|
288
288
|
test("five thirds → 3-col row + 2-col row (auto-expand)", () => {
|
|
@@ -296,12 +296,12 @@ describe("computeRows", () => {
|
|
|
296
296
|
const rows = computeRows(widgets, 80);
|
|
297
297
|
|
|
298
298
|
expect(rows).toHaveLength(2);
|
|
299
|
-
expect(rows[0]
|
|
300
|
-
expect(rows[0]
|
|
301
|
-
expect(rows[1]
|
|
302
|
-
expect(rows[1]
|
|
303
|
-
expect(rows[1]
|
|
304
|
-
expect(rows[1]
|
|
299
|
+
expect(rows[0]!.columns).toBe(3);
|
|
300
|
+
expect(rows[0]!.widgets).toHaveLength(3);
|
|
301
|
+
expect(rows[1]!.columns).toBe(2);
|
|
302
|
+
expect(rows[1]!.widgets).toHaveLength(2);
|
|
303
|
+
expect(rows[1]!.widgets[0]!.id).toBe("commit-activity");
|
|
304
|
+
expect(rows[1]!.widgets[1]!.id).toBe("github-release");
|
|
305
305
|
});
|
|
306
306
|
|
|
307
307
|
test("mixed thirds + halfs + fulls", () => {
|
|
@@ -317,12 +317,12 @@ describe("computeRows", () => {
|
|
|
317
317
|
|
|
318
318
|
expect(rows).toHaveLength(3);
|
|
319
319
|
// Sort by min priority: full(0), thirds(1), halfs(4)
|
|
320
|
-
expect(rows[0]
|
|
321
|
-
expect(rows[0]
|
|
322
|
-
expect(rows[1]
|
|
323
|
-
expect(rows[1]
|
|
324
|
-
expect(rows[2]
|
|
325
|
-
expect(rows[2]
|
|
320
|
+
expect(rows[0]!.columns).toBe(1);
|
|
321
|
+
expect(rows[0]!.widgets[0]!.id).toBe("recent-commits");
|
|
322
|
+
expect(rows[1]!.columns).toBe(3);
|
|
323
|
+
expect(rows[1]!.widgets[0]!.id).toBe("git-status");
|
|
324
|
+
expect(rows[2]!.columns).toBe(2);
|
|
325
|
+
expect(rows[2]!.widgets[0]!.id).toBe("branch-list");
|
|
326
326
|
});
|
|
327
327
|
|
|
328
328
|
test("thirds at narrow panel (<60) fall back to half pairing", () => {
|
|
@@ -335,10 +335,10 @@ describe("computeRows", () => {
|
|
|
335
335
|
|
|
336
336
|
// thirds resolve to half at 50 cols → paired as halfs
|
|
337
337
|
expect(rows).toHaveLength(2);
|
|
338
|
-
expect(rows[0]
|
|
339
|
-
expect(rows[0]
|
|
340
|
-
expect(rows[1]
|
|
341
|
-
expect(rows[1]
|
|
338
|
+
expect(rows[0]!.columns).toBe(2);
|
|
339
|
+
expect(rows[0]!.widgets).toHaveLength(2);
|
|
340
|
+
expect(rows[1]!.columns).toBe(1);
|
|
341
|
+
expect(rows[1]!.widgets).toHaveLength(1);
|
|
342
342
|
});
|
|
343
343
|
|
|
344
344
|
test("thirds at very narrow panel (<40) fall back to full", () => {
|
|
@@ -370,71 +370,71 @@ describe("buildBorderLine", () => {
|
|
|
370
370
|
test("top border, 1-col next row → corners + horizontal fill", () => {
|
|
371
371
|
const line = buildBorderLine("top", W, null, oneCol);
|
|
372
372
|
expect(line.length).toBe(W);
|
|
373
|
-
expect(line[0]).toBe("╭");
|
|
374
|
-
expect(line[W - 1]).toBe("╮");
|
|
373
|
+
expect(line[0]!).toBe("╭");
|
|
374
|
+
expect(line[W - 1]!).toBe("╮");
|
|
375
375
|
// No junction — all interior chars are horizontal
|
|
376
376
|
for (let i = 1; i < W - 1; i++) {
|
|
377
|
-
expect(line[i]).toBe("─");
|
|
377
|
+
expect(line[i]!).toBe("─");
|
|
378
378
|
}
|
|
379
379
|
});
|
|
380
380
|
|
|
381
381
|
test("top border, 2-col next row → has ┬ at midpoint", () => {
|
|
382
382
|
const line = buildBorderLine("top", W, null, twoCol);
|
|
383
383
|
expect(line.length).toBe(W);
|
|
384
|
-
expect(line[0]).toBe("╭");
|
|
385
|
-
expect(line[W - 1]).toBe("╮");
|
|
386
|
-
expect(line[10]).toBe("┬");
|
|
384
|
+
expect(line[0]!).toBe("╭");
|
|
385
|
+
expect(line[W - 1]!).toBe("╮");
|
|
386
|
+
expect(line[10]!).toBe("┬");
|
|
387
387
|
});
|
|
388
388
|
|
|
389
389
|
test("bottom border, 1-col prev row → corners only", () => {
|
|
390
390
|
const line = buildBorderLine("bottom", W, oneCol, null);
|
|
391
391
|
expect(line.length).toBe(W);
|
|
392
|
-
expect(line[0]).toBe("╰");
|
|
393
|
-
expect(line[W - 1]).toBe("╯");
|
|
392
|
+
expect(line[0]!).toBe("╰");
|
|
393
|
+
expect(line[W - 1]!).toBe("╯");
|
|
394
394
|
for (let i = 1; i < W - 1; i++) {
|
|
395
|
-
expect(line[i]).toBe("─");
|
|
395
|
+
expect(line[i]!).toBe("─");
|
|
396
396
|
}
|
|
397
397
|
});
|
|
398
398
|
|
|
399
399
|
test("bottom border, 2-col prev row → has ┴ at midpoint", () => {
|
|
400
400
|
const line = buildBorderLine("bottom", W, twoCol, null);
|
|
401
401
|
expect(line.length).toBe(W);
|
|
402
|
-
expect(line[0]).toBe("╰");
|
|
403
|
-
expect(line[W - 1]).toBe("╯");
|
|
404
|
-
expect(line[10]).toBe("┴");
|
|
402
|
+
expect(line[0]!).toBe("╰");
|
|
403
|
+
expect(line[W - 1]!).toBe("╯");
|
|
404
|
+
expect(line[10]!).toBe("┴");
|
|
405
405
|
});
|
|
406
406
|
|
|
407
407
|
test("mid border, 2-col prev → 1-col next → ┴ at midpoint", () => {
|
|
408
408
|
const line = buildBorderLine("mid", W, twoCol, oneCol);
|
|
409
409
|
expect(line.length).toBe(W);
|
|
410
|
-
expect(line[0]).toBe("├");
|
|
411
|
-
expect(line[W - 1]).toBe("┤");
|
|
412
|
-
expect(line[10]).toBe("┴");
|
|
410
|
+
expect(line[0]!).toBe("├");
|
|
411
|
+
expect(line[W - 1]!).toBe("┤");
|
|
412
|
+
expect(line[10]!).toBe("┴");
|
|
413
413
|
});
|
|
414
414
|
|
|
415
415
|
test("mid border, 1-col prev → 2-col next → ┬ at midpoint", () => {
|
|
416
416
|
const line = buildBorderLine("mid", W, oneCol, twoCol);
|
|
417
417
|
expect(line.length).toBe(W);
|
|
418
|
-
expect(line[0]).toBe("├");
|
|
419
|
-
expect(line[W - 1]).toBe("┤");
|
|
420
|
-
expect(line[10]).toBe("┬");
|
|
418
|
+
expect(line[0]!).toBe("├");
|
|
419
|
+
expect(line[W - 1]!).toBe("┤");
|
|
420
|
+
expect(line[10]!).toBe("┬");
|
|
421
421
|
});
|
|
422
422
|
|
|
423
423
|
test("mid border, 2-col prev → 2-col next → ┼ at midpoint", () => {
|
|
424
424
|
const line = buildBorderLine("mid", W, twoCol, twoCol);
|
|
425
425
|
expect(line.length).toBe(W);
|
|
426
|
-
expect(line[0]).toBe("├");
|
|
427
|
-
expect(line[W - 1]).toBe("┤");
|
|
428
|
-
expect(line[10]).toBe("┼");
|
|
426
|
+
expect(line[0]!).toBe("├");
|
|
427
|
+
expect(line[W - 1]!).toBe("┤");
|
|
428
|
+
expect(line[10]!).toBe("┼");
|
|
429
429
|
});
|
|
430
430
|
|
|
431
431
|
test("mid border, 1-col prev → 1-col next → no junction", () => {
|
|
432
432
|
const line = buildBorderLine("mid", W, oneCol, oneCol);
|
|
433
433
|
expect(line.length).toBe(W);
|
|
434
|
-
expect(line[0]).toBe("├");
|
|
435
|
-
expect(line[W - 1]).toBe("┤");
|
|
434
|
+
expect(line[0]!).toBe("├");
|
|
435
|
+
expect(line[W - 1]!).toBe("┤");
|
|
436
436
|
for (let i = 1; i < W - 1; i++) {
|
|
437
|
-
expect(line[i]).toBe("─");
|
|
437
|
+
expect(line[i]!).toBe("─");
|
|
438
438
|
}
|
|
439
439
|
});
|
|
440
440
|
|
|
@@ -455,8 +455,8 @@ describe("buildBorderLine", () => {
|
|
|
455
455
|
];
|
|
456
456
|
for (const [type, left, right] of cases) {
|
|
457
457
|
const line = buildBorderLine(type, W, oneCol, oneCol);
|
|
458
|
-
expect(line[0]).toBe(left);
|
|
459
|
-
expect(line[W - 1]).toBe(right);
|
|
458
|
+
expect(line[0]!).toBe(left);
|
|
459
|
+
expect(line[W - 1]!).toBe(right);
|
|
460
460
|
}
|
|
461
461
|
});
|
|
462
462
|
|
|
@@ -464,59 +464,59 @@ describe("buildBorderLine", () => {
|
|
|
464
464
|
const odd_w = 21;
|
|
465
465
|
const line = buildBorderLine("top", odd_w, null, twoCol);
|
|
466
466
|
expect(line.length).toBe(odd_w);
|
|
467
|
-
expect(line[Math.floor(odd_w / 2)]).toBe("┬");
|
|
468
|
-
expect(line[0]).toBe("╭");
|
|
469
|
-
expect(line[odd_w - 1]).toBe("╮");
|
|
467
|
+
expect(line[Math.floor(odd_w / 2)]!).toBe("┬");
|
|
468
|
+
expect(line[0]!).toBe("╭");
|
|
469
|
+
expect(line[odd_w - 1]!).toBe("╮");
|
|
470
470
|
});
|
|
471
471
|
|
|
472
472
|
test("top border, 3-col next row → has two junctions", () => {
|
|
473
473
|
const W = 30;
|
|
474
474
|
const line = buildBorderLine("top", W, null, threeCol);
|
|
475
475
|
expect(line.length).toBe(W);
|
|
476
|
-
expect(line[0]).toBe("╭");
|
|
477
|
-
expect(line[W - 1]).toBe("╮");
|
|
478
|
-
expect(line[10]).toBe("┬"); // Math.floor(30/3) = 10
|
|
479
|
-
expect(line[20]).toBe("┬"); // Math.floor(60/3) = 20
|
|
476
|
+
expect(line[0]!).toBe("╭");
|
|
477
|
+
expect(line[W - 1]!).toBe("╮");
|
|
478
|
+
expect(line[10]!).toBe("┬"); // Math.floor(30/3) = 10
|
|
479
|
+
expect(line[20]!).toBe("┬"); // Math.floor(60/3) = 20
|
|
480
480
|
});
|
|
481
481
|
|
|
482
482
|
test("bottom border, 3-col prev row → has two ┴ junctions", () => {
|
|
483
483
|
const W = 30;
|
|
484
484
|
const line = buildBorderLine("bottom", W, threeCol, null);
|
|
485
485
|
expect(line.length).toBe(W);
|
|
486
|
-
expect(line[0]).toBe("╰");
|
|
487
|
-
expect(line[W - 1]).toBe("╯");
|
|
488
|
-
expect(line[10]).toBe("┴");
|
|
489
|
-
expect(line[20]).toBe("┴");
|
|
486
|
+
expect(line[0]!).toBe("╰");
|
|
487
|
+
expect(line[W - 1]!).toBe("╯");
|
|
488
|
+
expect(line[10]!).toBe("┴");
|
|
489
|
+
expect(line[20]!).toBe("┴");
|
|
490
490
|
});
|
|
491
491
|
|
|
492
492
|
test("mid border, 3-col → 3-col → has two ┼ junctions", () => {
|
|
493
493
|
const W = 30;
|
|
494
494
|
const line = buildBorderLine("mid", W, threeCol, threeCol);
|
|
495
495
|
expect(line.length).toBe(W);
|
|
496
|
-
expect(line[0]).toBe("├");
|
|
497
|
-
expect(line[W - 1]).toBe("┤");
|
|
498
|
-
expect(line[10]).toBe("┼");
|
|
499
|
-
expect(line[20]).toBe("┼");
|
|
496
|
+
expect(line[0]!).toBe("├");
|
|
497
|
+
expect(line[W - 1]!).toBe("┤");
|
|
498
|
+
expect(line[10]!).toBe("┼");
|
|
499
|
+
expect(line[20]!).toBe("┼");
|
|
500
500
|
});
|
|
501
501
|
|
|
502
502
|
test("mid border, 3-col → 1-col → two ┴ junctions", () => {
|
|
503
503
|
const W = 30;
|
|
504
504
|
const line = buildBorderLine("mid", W, threeCol, oneCol);
|
|
505
505
|
expect(line.length).toBe(W);
|
|
506
|
-
expect(line[0]).toBe("├");
|
|
507
|
-
expect(line[W - 1]).toBe("┤");
|
|
508
|
-
expect(line[10]).toBe("┴");
|
|
509
|
-
expect(line[20]).toBe("┴");
|
|
506
|
+
expect(line[0]!).toBe("├");
|
|
507
|
+
expect(line[W - 1]!).toBe("┤");
|
|
508
|
+
expect(line[10]!).toBe("┴");
|
|
509
|
+
expect(line[20]!).toBe("┴");
|
|
510
510
|
});
|
|
511
511
|
|
|
512
512
|
test("mid border, 1-col → 3-col → two ┬ junctions", () => {
|
|
513
513
|
const W = 30;
|
|
514
514
|
const line = buildBorderLine("mid", W, oneCol, threeCol);
|
|
515
515
|
expect(line.length).toBe(W);
|
|
516
|
-
expect(line[0]).toBe("├");
|
|
517
|
-
expect(line[W - 1]).toBe("┤");
|
|
518
|
-
expect(line[10]).toBe("┬");
|
|
519
|
-
expect(line[20]).toBe("┬");
|
|
516
|
+
expect(line[0]!).toBe("├");
|
|
517
|
+
expect(line[W - 1]!).toBe("┤");
|
|
518
|
+
expect(line[10]!).toBe("┬");
|
|
519
|
+
expect(line[20]!).toBe("┬");
|
|
520
520
|
});
|
|
521
521
|
|
|
522
522
|
test("mid border, 3-col → 2-col → mixed junctions", () => {
|
|
@@ -528,22 +528,22 @@ describe("buildBorderLine", () => {
|
|
|
528
528
|
const W = 30;
|
|
529
529
|
const line = buildBorderLine("mid", W, threeCol, twoCol);
|
|
530
530
|
expect(line.length).toBe(W);
|
|
531
|
-
expect(line[0]).toBe("├");
|
|
532
|
-
expect(line[W - 1]).toBe("┤");
|
|
533
|
-
expect(line[10]).toBe("┴"); // from 3-col above only
|
|
534
|
-
expect(line[15]).toBe("┬"); // from 2-col below only
|
|
535
|
-
expect(line[20]).toBe("┴"); // from 3-col above only
|
|
531
|
+
expect(line[0]!).toBe("├");
|
|
532
|
+
expect(line[W - 1]!).toBe("┤");
|
|
533
|
+
expect(line[10]!).toBe("┴"); // from 3-col above only
|
|
534
|
+
expect(line[15]!).toBe("┬"); // from 2-col below only
|
|
535
|
+
expect(line[20]!).toBe("┴"); // from 3-col above only
|
|
536
536
|
});
|
|
537
537
|
|
|
538
538
|
test("mid border, 2-col → 3-col → mixed junctions", () => {
|
|
539
539
|
const W = 30;
|
|
540
540
|
const line = buildBorderLine("mid", W, twoCol, threeCol);
|
|
541
541
|
expect(line.length).toBe(W);
|
|
542
|
-
expect(line[0]).toBe("├");
|
|
543
|
-
expect(line[W - 1]).toBe("┤");
|
|
544
|
-
expect(line[10]).toBe("┬"); // from 3-col below only
|
|
545
|
-
expect(line[15]).toBe("┴"); // from 2-col above only
|
|
546
|
-
expect(line[20]).toBe("┬"); // from 3-col below only
|
|
542
|
+
expect(line[0]!).toBe("├");
|
|
543
|
+
expect(line[W - 1]!).toBe("┤");
|
|
544
|
+
expect(line[10]!).toBe("┬"); // from 3-col below only
|
|
545
|
+
expect(line[15]!).toBe("┴"); // from 2-col above only
|
|
546
|
+
expect(line[20]!).toBe("┬"); // from 3-col below only
|
|
547
547
|
});
|
|
548
548
|
});
|
|
549
549
|
|
|
@@ -555,9 +555,9 @@ describe("buildBorderLineWithTitle", () => {
|
|
|
555
555
|
test("inserts title into border line after first char", () => {
|
|
556
556
|
const result = buildBorderLineWithTitle(base, "Hello");
|
|
557
557
|
expect(result.length).toBe(20);
|
|
558
|
-
expect(result[0]).toBe("╭");
|
|
558
|
+
expect(result[0]!).toBe("╭");
|
|
559
559
|
expect(result.slice(1, 8)).toBe(" Hello ");
|
|
560
|
-
expect(result[19]).toBe("╮");
|
|
560
|
+
expect(result[19]!).toBe("╮");
|
|
561
561
|
});
|
|
562
562
|
|
|
563
563
|
test("empty title returns unchanged line", () => {
|
|
@@ -569,8 +569,8 @@ describe("buildBorderLineWithTitle", () => {
|
|
|
569
569
|
const result = buildBorderLineWithTitle(base, long_title);
|
|
570
570
|
expect(result.length).toBe(20);
|
|
571
571
|
expect(result).toContain("…");
|
|
572
|
-
expect(result[0]).toBe("╭");
|
|
573
|
-
expect(result[19]).toBe("╮");
|
|
572
|
+
expect(result[0]!).toBe("╭");
|
|
573
|
+
expect(result[19]!).toBe("╮");
|
|
574
574
|
});
|
|
575
575
|
|
|
576
576
|
test("title fits exactly (boundary)", () => {
|
|
@@ -580,8 +580,8 @@ describe("buildBorderLineWithTitle", () => {
|
|
|
580
580
|
const exact_title = "A".repeat(15);
|
|
581
581
|
const result = buildBorderLineWithTitle(base, exact_title);
|
|
582
582
|
expect(result.length).toBe(20);
|
|
583
|
-
expect(result[0]).toBe("╭");
|
|
584
|
-
expect(result[19]).toBe("╮");
|
|
583
|
+
expect(result[0]!).toBe("╭");
|
|
584
|
+
expect(result[19]!).toBe("╮");
|
|
585
585
|
expect(result).toContain(exact_title);
|
|
586
586
|
expect(result).not.toContain("…");
|
|
587
587
|
});
|