@knpkv/codecommit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +216 -0
- package/dist/package.json +50 -0
- package/dist/src/bin.d.ts +3 -0
- package/dist/src/bin.d.ts.map +1 -0
- package/dist/src/bin.js +204 -0
- package/dist/src/bin.js.map +1 -0
- package/dist/src/main.d.ts +2 -0
- package/dist/src/main.d.ts.map +1 -0
- package/dist/src/main.js +25 -0
- package/dist/src/main.js.map +1 -0
- package/dist/src/tui/App.d.ts +6 -0
- package/dist/src/tui/App.d.ts.map +1 -0
- package/dist/src/tui/App.js +40 -0
- package/dist/src/tui/App.js.map +1 -0
- package/dist/src/tui/Constants.d.ts +11 -0
- package/dist/src/tui/Constants.d.ts.map +1 -0
- package/dist/src/tui/Constants.js +40 -0
- package/dist/src/tui/Constants.js.map +1 -0
- package/dist/src/tui/ListBuilder.d.ts +32 -0
- package/dist/src/tui/ListBuilder.d.ts.map +1 -0
- package/dist/src/tui/ListBuilder.js +145 -0
- package/dist/src/tui/ListBuilder.js.map +1 -0
- package/dist/src/tui/atoms/actions.d.ts +39 -0
- package/dist/src/tui/atoms/actions.d.ts.map +1 -0
- package/dist/src/tui/atoms/actions.js +149 -0
- package/dist/src/tui/atoms/actions.js.map +1 -0
- package/dist/src/tui/atoms/app.d.ts +42 -0
- package/dist/src/tui/atoms/app.d.ts.map +1 -0
- package/dist/src/tui/atoms/app.js +69 -0
- package/dist/src/tui/atoms/app.js.map +1 -0
- package/dist/src/tui/atoms/index.d.ts +6 -0
- package/dist/src/tui/atoms/index.d.ts.map +1 -0
- package/dist/src/tui/atoms/index.js +7 -0
- package/dist/src/tui/atoms/index.js.map +1 -0
- package/dist/src/tui/atoms/runtime.d.ts +9 -0
- package/dist/src/tui/atoms/runtime.d.ts.map +1 -0
- package/dist/src/tui/atoms/runtime.js +22 -0
- package/dist/src/tui/atoms/runtime.js.map +1 -0
- package/dist/src/tui/atoms/ui.d.ts +103 -0
- package/dist/src/tui/atoms/ui.d.ts.map +1 -0
- package/dist/src/tui/atoms/ui.js +97 -0
- package/dist/src/tui/atoms/ui.js.map +1 -0
- package/dist/src/tui/components/Badge.d.ts +13 -0
- package/dist/src/tui/components/Badge.d.ts.map +1 -0
- package/dist/src/tui/components/Badge.js +48 -0
- package/dist/src/tui/components/Badge.js.map +1 -0
- package/dist/src/tui/components/DetailsView.d.ts +6 -0
- package/dist/src/tui/components/DetailsView.d.ts.map +1 -0
- package/dist/src/tui/components/DetailsView.js +81 -0
- package/dist/src/tui/components/DetailsView.js.map +1 -0
- package/dist/src/tui/components/ErrorBoundary.d.ts +20 -0
- package/dist/src/tui/components/ErrorBoundary.d.ts.map +1 -0
- package/dist/src/tui/components/ErrorBoundary.js +33 -0
- package/dist/src/tui/components/ErrorBoundary.js.map +1 -0
- package/dist/src/tui/components/Footer.d.ts +6 -0
- package/dist/src/tui/components/Footer.d.ts.map +1 -0
- package/dist/src/tui/components/Footer.js +29 -0
- package/dist/src/tui/components/Footer.js.map +1 -0
- package/dist/src/tui/components/Header.d.ts +6 -0
- package/dist/src/tui/components/Header.d.ts.map +1 -0
- package/dist/src/tui/components/Header.js +58 -0
- package/dist/src/tui/components/Header.js.map +1 -0
- package/dist/src/tui/components/ListItemRow.d.ts +13 -0
- package/dist/src/tui/components/ListItemRow.d.ts.map +1 -0
- package/dist/src/tui/components/ListItemRow.js +52 -0
- package/dist/src/tui/components/ListItemRow.js.map +1 -0
- package/dist/src/tui/components/MainList.d.ts +11 -0
- package/dist/src/tui/components/MainList.d.ts.map +1 -0
- package/dist/src/tui/components/MainList.js +124 -0
- package/dist/src/tui/components/MainList.js.map +1 -0
- package/dist/src/tui/components/NotificationsTable.d.ts +12 -0
- package/dist/src/tui/components/NotificationsTable.d.ts.map +1 -0
- package/dist/src/tui/components/NotificationsTable.js +72 -0
- package/dist/src/tui/components/NotificationsTable.js.map +1 -0
- package/dist/src/tui/components/QuickFilters.d.ts +6 -0
- package/dist/src/tui/components/QuickFilters.d.ts.map +1 -0
- package/dist/src/tui/components/QuickFilters.js +74 -0
- package/dist/src/tui/components/QuickFilters.js.map +1 -0
- package/dist/src/tui/components/SettingsTable.d.ts +12 -0
- package/dist/src/tui/components/SettingsTable.d.ts.map +1 -0
- package/dist/src/tui/components/SettingsTable.js +62 -0
- package/dist/src/tui/components/SettingsTable.js.map +1 -0
- package/dist/src/tui/components/Spinner.d.ts +11 -0
- package/dist/src/tui/components/Spinner.d.ts.map +1 -0
- package/dist/src/tui/components/Spinner.js +24 -0
- package/dist/src/tui/components/Spinner.js.map +1 -0
- package/dist/src/tui/components/StatusRow.d.ts +11 -0
- package/dist/src/tui/components/StatusRow.d.ts.map +1 -0
- package/dist/src/tui/components/StatusRow.js +11 -0
- package/dist/src/tui/components/StatusRow.js.map +1 -0
- package/dist/src/tui/components/Table.d.ts +20 -0
- package/dist/src/tui/components/Table.d.ts.map +1 -0
- package/dist/src/tui/components/Table.js +38 -0
- package/dist/src/tui/components/Table.js.map +1 -0
- package/dist/src/tui/components/index.d.ts +14 -0
- package/dist/src/tui/components/index.d.ts.map +1 -0
- package/dist/src/tui/components/index.js +14 -0
- package/dist/src/tui/components/index.js.map +1 -0
- package/dist/src/tui/components/mainlist-utils.d.ts +22 -0
- package/dist/src/tui/components/mainlist-utils.d.ts.map +1 -0
- package/dist/src/tui/components/mainlist-utils.js +71 -0
- package/dist/src/tui/components/mainlist-utils.js.map +1 -0
- package/dist/src/tui/components/table-utils.d.ts +25 -0
- package/dist/src/tui/components/table-utils.d.ts.map +1 -0
- package/dist/src/tui/components/table-utils.js +25 -0
- package/dist/src/tui/components/table-utils.js.map +1 -0
- package/dist/src/tui/context/dialog.d.ts +32 -0
- package/dist/src/tui/context/dialog.d.ts.map +1 -0
- package/dist/src/tui/context/dialog.js +41 -0
- package/dist/src/tui/context/dialog.js.map +1 -0
- package/dist/src/tui/context/theme.d.ts +25 -0
- package/dist/src/tui/context/theme.d.ts.map +1 -0
- package/dist/src/tui/context/theme.js +31 -0
- package/dist/src/tui/context/theme.js.map +1 -0
- package/dist/src/tui/hooks/index.d.ts +2 -0
- package/dist/src/tui/hooks/index.d.ts.map +1 -0
- package/dist/src/tui/hooks/index.js +2 -0
- package/dist/src/tui/hooks/index.js.map +1 -0
- package/dist/src/tui/hooks/useKeyboardNav.d.ts +16 -0
- package/dist/src/tui/hooks/useKeyboardNav.d.ts.map +1 -0
- package/dist/src/tui/hooks/useKeyboardNav.js +429 -0
- package/dist/src/tui/hooks/useKeyboardNav.js.map +1 -0
- package/dist/src/tui/hooks/useListNavigation.d.ts +3 -0
- package/dist/src/tui/hooks/useListNavigation.d.ts.map +1 -0
- package/dist/src/tui/hooks/useListNavigation.js +123 -0
- package/dist/src/tui/hooks/useListNavigation.js.map +1 -0
- package/dist/src/tui/mocks.d.ts +6 -0
- package/dist/src/tui/mocks.d.ts.map +1 -0
- package/dist/src/tui/mocks.js +36 -0
- package/dist/src/tui/mocks.js.map +1 -0
- package/dist/src/tui/theme/Resolver.d.ts +6 -0
- package/dist/src/tui/theme/Resolver.d.ts.map +1 -0
- package/dist/src/tui/theme/Resolver.js +73 -0
- package/dist/src/tui/theme/Resolver.js.map +1 -0
- package/dist/src/tui/theme/ThemeRegistry.d.ts +3 -0
- package/dist/src/tui/theme/ThemeRegistry.d.ts.map +1 -0
- package/dist/src/tui/theme/ThemeRegistry.js +70 -0
- package/dist/src/tui/theme/ThemeRegistry.js.map +1 -0
- package/dist/src/tui/theme/data/aura.json +69 -0
- package/dist/src/tui/theme/data/ayu.json +80 -0
- package/dist/src/tui/theme/data/carbonfox.json +248 -0
- package/dist/src/tui/theme/data/catppuccin-frappe.json +233 -0
- package/dist/src/tui/theme/data/catppuccin-macchiato.json +233 -0
- package/dist/src/tui/theme/data/catppuccin.json +112 -0
- package/dist/src/tui/theme/data/cobalt2.json +228 -0
- package/dist/src/tui/theme/data/cursor.json +249 -0
- package/dist/src/tui/theme/data/dracula.json +219 -0
- package/dist/src/tui/theme/data/everforest.json +241 -0
- package/dist/src/tui/theme/data/flexoki.json +237 -0
- package/dist/src/tui/theme/data/github.json +233 -0
- package/dist/src/tui/theme/data/gruvbox.json +242 -0
- package/dist/src/tui/theme/data/kanagawa.json +77 -0
- package/dist/src/tui/theme/data/lucent-orng.json +237 -0
- package/dist/src/tui/theme/data/material.json +235 -0
- package/dist/src/tui/theme/data/matrix.json +77 -0
- package/dist/src/tui/theme/data/mercury.json +245 -0
- package/dist/src/tui/theme/data/monokai.json +221 -0
- package/dist/src/tui/theme/data/nightowl.json +221 -0
- package/dist/src/tui/theme/data/nord.json +223 -0
- package/dist/src/tui/theme/data/one-dark.json +84 -0
- package/dist/src/tui/theme/data/opencode.json +245 -0
- package/dist/src/tui/theme/data/orng.json +249 -0
- package/dist/src/tui/theme/data/osaka-jade.json +93 -0
- package/dist/src/tui/theme/data/palenight.json +222 -0
- package/dist/src/tui/theme/data/rosepine.json +234 -0
- package/dist/src/tui/theme/data/solarized.json +223 -0
- package/dist/src/tui/theme/data/synthwave84.json +226 -0
- package/dist/src/tui/theme/data/tokyonight.json +243 -0
- package/dist/src/tui/theme/data/vercel.json +245 -0
- package/dist/src/tui/theme/data/vesper.json +218 -0
- package/dist/src/tui/theme/data/zenburn.json +223 -0
- package/dist/src/tui/theme/default.d.ts +40 -0
- package/dist/src/tui/theme/default.d.ts.map +1 -0
- package/dist/src/tui/theme/default.js +35 -0
- package/dist/src/tui/theme/default.js.map +1 -0
- package/dist/src/tui/theme/resolver-utils.d.ts +28 -0
- package/dist/src/tui/theme/resolver-utils.d.ts.map +1 -0
- package/dist/src/tui/theme/resolver-utils.js +19 -0
- package/dist/src/tui/theme/resolver-utils.js.map +1 -0
- package/dist/src/tui/theme/themes.d.ts +3 -0
- package/dist/src/tui/theme/themes.d.ts.map +1 -0
- package/dist/src/tui/theme/themes.js +13 -0
- package/dist/src/tui/theme/themes.js.map +1 -0
- package/dist/src/tui/ui/Dialog.d.ts +19 -0
- package/dist/src/tui/ui/Dialog.d.ts.map +1 -0
- package/dist/src/tui/ui/Dialog.js +39 -0
- package/dist/src/tui/ui/Dialog.js.map +1 -0
- package/dist/src/tui/ui/DialogCommand.d.ts +2 -0
- package/dist/src/tui/ui/DialogCommand.d.ts.map +1 -0
- package/dist/src/tui/ui/DialogCommand.js +182 -0
- package/dist/src/tui/ui/DialogCommand.js.map +1 -0
- package/dist/src/tui/ui/DialogCreatePR.d.ts +2 -0
- package/dist/src/tui/ui/DialogCreatePR.d.ts.map +1 -0
- package/dist/src/tui/ui/DialogCreatePR.js +397 -0
- package/dist/src/tui/ui/DialogCreatePR.js.map +1 -0
- package/dist/src/tui/ui/DialogHelp.d.ts +6 -0
- package/dist/src/tui/ui/DialogHelp.d.ts.map +1 -0
- package/dist/src/tui/ui/DialogHelp.js +93 -0
- package/dist/src/tui/ui/DialogHelp.js.map +1 -0
- package/dist/src/tui/ui/DialogTheme.d.ts +2 -0
- package/dist/src/tui/ui/DialogTheme.d.ts.map +1 -0
- package/dist/src/tui/ui/DialogTheme.js +124 -0
- package/dist/src/tui/ui/DialogTheme.js.map +1 -0
- package/dist/src/tui/ui/index.d.ts +6 -0
- package/dist/src/tui/ui/index.d.ts.map +1 -0
- package/dist/src/tui/ui/index.js +6 -0
- package/dist/src/tui/ui/index.js.map +1 -0
- package/dist/src/tui/utils/prTemplates.d.ts +19 -0
- package/dist/src/tui/utils/prTemplates.d.ts.map +1 -0
- package/dist/src/tui/utils/prTemplates.js +46 -0
- package/dist/src/tui/utils/prTemplates.js.map +1 -0
- package/dist/test/ConfigService.test.d.ts +2 -0
- package/dist/test/ConfigService.test.d.ts.map +1 -0
- package/dist/test/ConfigService.test.js +40 -0
- package/dist/test/ConfigService.test.js.map +1 -0
- package/dist/test/ListBuilder.test.d.ts +2 -0
- package/dist/test/ListBuilder.test.d.ts.map +1 -0
- package/dist/test/ListBuilder.test.js +246 -0
- package/dist/test/ListBuilder.test.js.map +1 -0
- package/dist/test/MainList.test.d.ts +2 -0
- package/dist/test/MainList.test.d.ts.map +1 -0
- package/dist/test/MainList.test.js +228 -0
- package/dist/test/MainList.test.js.map +1 -0
- package/dist/test/Resolver.test.d.ts +2 -0
- package/dist/test/Resolver.test.d.ts.map +1 -0
- package/dist/test/Resolver.test.js +81 -0
- package/dist/test/Resolver.test.js.map +1 -0
- package/dist/test/Table.test.d.ts +2 -0
- package/dist/test/Table.test.d.ts.map +1 -0
- package/dist/test/Table.test.js +100 -0
- package/dist/test/Table.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { applySettingsFilter, computeItemPositions, findGroupHeader, findStableIndex, parseSettingsFilter } from "../src/tui/components/mainlist-utils.js";
|
|
3
|
+
// ── Fixtures ─────────────────────────────────────────────────────────
|
|
4
|
+
const mkAccount = (profile, enabled) => ({
|
|
5
|
+
type: "account",
|
|
6
|
+
account: {
|
|
7
|
+
profile: profile,
|
|
8
|
+
region: "us-east-1",
|
|
9
|
+
enabled
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
const mkHeader = (label, count) => ({
|
|
13
|
+
type: "header",
|
|
14
|
+
label,
|
|
15
|
+
count
|
|
16
|
+
});
|
|
17
|
+
const mkPR = (id, description) => ({
|
|
18
|
+
type: "pr",
|
|
19
|
+
pr: {
|
|
20
|
+
id,
|
|
21
|
+
title: `PR ${id}`,
|
|
22
|
+
description,
|
|
23
|
+
author: "alice",
|
|
24
|
+
repositoryName: "repo",
|
|
25
|
+
creationDate: new Date(),
|
|
26
|
+
lastModifiedDate: new Date(),
|
|
27
|
+
link: "https://example.com",
|
|
28
|
+
account: { id: "dev", region: "us-east-1" },
|
|
29
|
+
status: "OPEN",
|
|
30
|
+
sourceBranch: "feat",
|
|
31
|
+
destinationBranch: "main",
|
|
32
|
+
isMergeable: true,
|
|
33
|
+
isApproved: false
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
const empty = { type: "empty" };
|
|
37
|
+
// ── Tests ────────────────────────────────────────────────────────────
|
|
38
|
+
describe("parseSettingsFilter", () => {
|
|
39
|
+
// "on:" prefix filters to enabled-only accounts.
|
|
40
|
+
it("parses on: prefix", () => {
|
|
41
|
+
expect(parseSettingsFilter("on:dev")).toEqual({ status: "on", name: "dev" });
|
|
42
|
+
});
|
|
43
|
+
// "off:" prefix filters to disabled-only accounts.
|
|
44
|
+
it("parses off: prefix", () => {
|
|
45
|
+
expect(parseSettingsFilter("off:staging")).toEqual({ status: "off", name: "staging" });
|
|
46
|
+
});
|
|
47
|
+
// No prefix means search across all accounts regardless of status.
|
|
48
|
+
it("defaults to all with name search", () => {
|
|
49
|
+
expect(parseSettingsFilter("prod")).toEqual({ status: "all", name: "prod" });
|
|
50
|
+
});
|
|
51
|
+
// Prefix detection is case-insensitive: "ON:" and "On:" both work.
|
|
52
|
+
it("is case-insensitive", () => {
|
|
53
|
+
expect(parseSettingsFilter("ON:Dev")).toEqual({ status: "on", name: "dev" });
|
|
54
|
+
expect(parseSettingsFilter("OFF:Prod")).toEqual({ status: "off", name: "prod" });
|
|
55
|
+
});
|
|
56
|
+
// Empty input returns "all" status with empty name — matches everything.
|
|
57
|
+
it("empty string returns all with empty name", () => {
|
|
58
|
+
expect(parseSettingsFilter("")).toEqual({ status: "all", name: "" });
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe("applySettingsFilter", () => {
|
|
62
|
+
const items = [
|
|
63
|
+
mkAccount("dev-account", true),
|
|
64
|
+
mkAccount("prod-account", false),
|
|
65
|
+
mkAccount("staging-account", true)
|
|
66
|
+
];
|
|
67
|
+
// Empty filter returns all items unchanged — no filtering applied.
|
|
68
|
+
it("returns all items when filter is empty", () => {
|
|
69
|
+
expect(applySettingsFilter(items, "")).toBe(items);
|
|
70
|
+
});
|
|
71
|
+
// Name-only filter matches account profile substrings.
|
|
72
|
+
it("filters by name substring", () => {
|
|
73
|
+
const result = applySettingsFilter(items, "dev");
|
|
74
|
+
expect(result).toHaveLength(1);
|
|
75
|
+
});
|
|
76
|
+
// "on:" prefix keeps only enabled accounts matching name.
|
|
77
|
+
it("filters by on: prefix", () => {
|
|
78
|
+
const result = applySettingsFilter(items, "on:account");
|
|
79
|
+
expect(result).toHaveLength(2); // dev + staging (both enabled)
|
|
80
|
+
});
|
|
81
|
+
// "off:" prefix keeps only disabled accounts matching name.
|
|
82
|
+
it("filters by off: prefix", () => {
|
|
83
|
+
const result = applySettingsFilter(items, "off:account");
|
|
84
|
+
expect(result).toHaveLength(1); // prod (disabled)
|
|
85
|
+
});
|
|
86
|
+
// Non-account items (headers, PRs) are always excluded.
|
|
87
|
+
it("excludes non-account items", () => {
|
|
88
|
+
const mixed = [mkHeader("H", 0), mkAccount("dev", true)];
|
|
89
|
+
const result = applySettingsFilter(mixed, "dev");
|
|
90
|
+
expect(result).toHaveLength(1);
|
|
91
|
+
expect(result[0].type).toBe("account");
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
describe("findStableIndex", () => {
|
|
95
|
+
const items = [
|
|
96
|
+
mkHeader("group", 2),
|
|
97
|
+
mkPR("pr-1"),
|
|
98
|
+
mkPR("pr-2"),
|
|
99
|
+
mkPR("pr-3")
|
|
100
|
+
];
|
|
101
|
+
// In prs/details view with a selected PR ID, the function should
|
|
102
|
+
// find the matching PR regardless of the numeric index.
|
|
103
|
+
it("finds PR by id in prs view", () => {
|
|
104
|
+
expect(findStableIndex(items, "prs", "pr-2", 0)).toBe(2);
|
|
105
|
+
});
|
|
106
|
+
// Same behavior in details view — preserves selection across refresh.
|
|
107
|
+
it("finds PR by id in details view", () => {
|
|
108
|
+
expect(findStableIndex(items, "details", "pr-3", 0)).toBe(3);
|
|
109
|
+
});
|
|
110
|
+
// Falls back to numeric index when PR ID is not found.
|
|
111
|
+
it("falls back to selectedIndex when PR not found", () => {
|
|
112
|
+
expect(findStableIndex(items, "prs", "nonexistent", 1)).toBe(1);
|
|
113
|
+
});
|
|
114
|
+
// Non-prs views (settings, notifications) ignore PR ID matching.
|
|
115
|
+
it("ignores PR id in settings view", () => {
|
|
116
|
+
expect(findStableIndex(items, "settings", "pr-1", 2)).toBe(2);
|
|
117
|
+
});
|
|
118
|
+
// When selectedIndex exceeds list length, clamp to last item.
|
|
119
|
+
it("clamps to last item when index overflows", () => {
|
|
120
|
+
expect(findStableIndex(items, "settings", null, 100)).toBe(3);
|
|
121
|
+
});
|
|
122
|
+
// Negative index clamps to 0.
|
|
123
|
+
it("clamps negative index to 0", () => {
|
|
124
|
+
expect(findStableIndex(items, "settings", null, -5)).toBe(0);
|
|
125
|
+
});
|
|
126
|
+
// Empty list returns 0 (Math.max(0, -1) = 0).
|
|
127
|
+
it("returns 0 for empty list", () => {
|
|
128
|
+
expect(findStableIndex([], "prs", null, 5)).toBe(0);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
describe("findGroupHeader", () => {
|
|
132
|
+
const items = [
|
|
133
|
+
mkHeader("Group A", 2),
|
|
134
|
+
mkPR("pr-1"),
|
|
135
|
+
mkPR("pr-2"),
|
|
136
|
+
mkHeader("Group B", 1),
|
|
137
|
+
mkPR("pr-3")
|
|
138
|
+
];
|
|
139
|
+
// Walks backward from the given index to find the nearest header.
|
|
140
|
+
it("finds header above current index", () => {
|
|
141
|
+
const header = findGroupHeader(items, 2);
|
|
142
|
+
expect(header).not.toBeNull();
|
|
143
|
+
expect(header.type === "header" && header.label).toBe("Group A");
|
|
144
|
+
});
|
|
145
|
+
// PR at index 4 belongs to "Group B" (header at index 3).
|
|
146
|
+
it("finds correct group for second section", () => {
|
|
147
|
+
const header = findGroupHeader(items, 4);
|
|
148
|
+
expect(header.type === "header" && header.label).toBe("Group B");
|
|
149
|
+
});
|
|
150
|
+
// Index pointing directly at a header returns that header.
|
|
151
|
+
it("returns header when index is on a header", () => {
|
|
152
|
+
const header = findGroupHeader(items, 3);
|
|
153
|
+
expect(header.type === "header" && header.label).toBe("Group B");
|
|
154
|
+
});
|
|
155
|
+
// Empty list always returns null — no headers to find.
|
|
156
|
+
it("returns null for empty list", () => {
|
|
157
|
+
expect(findGroupHeader([], 0)).toBeNull();
|
|
158
|
+
});
|
|
159
|
+
// Negative index clamps to -1 via Math.min, then loop doesn't run.
|
|
160
|
+
it("returns null for negative index", () => {
|
|
161
|
+
expect(findGroupHeader(items, -1)).toBeNull();
|
|
162
|
+
});
|
|
163
|
+
// Index beyond list length clamps to last item and walks back.
|
|
164
|
+
it("clamps index beyond list length", () => {
|
|
165
|
+
const header = findGroupHeader(items, 100);
|
|
166
|
+
expect(header.type === "header" && header.label).toBe("Group B");
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
describe("computeItemPositions", () => {
|
|
170
|
+
// First header has height 2 (no margin above), subsequent headers
|
|
171
|
+
// have height 3 (1 extra line for visual separator).
|
|
172
|
+
it("first header is height 2, subsequent headers height 3", () => {
|
|
173
|
+
const items = [
|
|
174
|
+
mkHeader("A", 1),
|
|
175
|
+
mkPR("1"),
|
|
176
|
+
mkHeader("B", 1)
|
|
177
|
+
];
|
|
178
|
+
const pos = computeItemPositions(items);
|
|
179
|
+
expect(pos[0]).toEqual({ start: 0, end: 2 }); // first header: h=2
|
|
180
|
+
expect(pos[1]).toEqual({ start: 2, end: 6 }); // PR: h=4
|
|
181
|
+
expect(pos[2]).toEqual({ start: 6, end: 9 }); // second header: h=3
|
|
182
|
+
});
|
|
183
|
+
// PR without description: 1 (content) + 1 (spacing) + 0 (desc) + 1 + 1 = 4
|
|
184
|
+
it("PR without description has height 4", () => {
|
|
185
|
+
const items = [mkPR("1")];
|
|
186
|
+
const pos = computeItemPositions(items);
|
|
187
|
+
expect(pos[0]).toEqual({ start: 0, end: 4 });
|
|
188
|
+
});
|
|
189
|
+
// PR with multi-line description adds line count (capped at 5).
|
|
190
|
+
it("PR with description adds lines (capped at 5)", () => {
|
|
191
|
+
const items = [mkPR("1", "line1\nline2\nline3")];
|
|
192
|
+
const pos = computeItemPositions(items);
|
|
193
|
+
// height = 1 + 1 + 3 + 1 + 1 = 7
|
|
194
|
+
expect(pos[0]).toEqual({ start: 0, end: 7 });
|
|
195
|
+
});
|
|
196
|
+
// Description with more than 5 lines is capped to prevent
|
|
197
|
+
// overflow in the scroll calculations.
|
|
198
|
+
it("caps description lines at 5", () => {
|
|
199
|
+
const desc = Array.from({ length: 10 }, (_, i) => `line${i}`).join("\n");
|
|
200
|
+
const items = [mkPR("1", desc)];
|
|
201
|
+
const pos = computeItemPositions(items);
|
|
202
|
+
// height = 1 + 1 + 5 + 1 + 1 = 9
|
|
203
|
+
expect(pos[0]).toEqual({ start: 0, end: 9 });
|
|
204
|
+
});
|
|
205
|
+
// "empty" and other item types default to height 2.
|
|
206
|
+
it("empty item has height 2", () => {
|
|
207
|
+
const items = [empty];
|
|
208
|
+
const pos = computeItemPositions(items);
|
|
209
|
+
expect(pos[0]).toEqual({ start: 0, end: 2 });
|
|
210
|
+
});
|
|
211
|
+
// Positions are cumulative — each item starts where the previous ended.
|
|
212
|
+
it("positions are cumulative", () => {
|
|
213
|
+
const items = [
|
|
214
|
+
mkHeader("A", 1),
|
|
215
|
+
mkPR("1"),
|
|
216
|
+
empty
|
|
217
|
+
];
|
|
218
|
+
const pos = computeItemPositions(items);
|
|
219
|
+
expect(pos[0]).toEqual({ start: 0, end: 2 });
|
|
220
|
+
expect(pos[1]).toEqual({ start: 2, end: 6 });
|
|
221
|
+
expect(pos[2]).toEqual({ start: 6, end: 8 });
|
|
222
|
+
});
|
|
223
|
+
// Empty list produces no positions.
|
|
224
|
+
it("returns empty for empty list", () => {
|
|
225
|
+
expect(computeItemPositions([])).toEqual([]);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
//# sourceMappingURL=MainList.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MainList.test.js","sourceRoot":"","sources":["../../test/MainList.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Resolver.test.d.ts","sourceRoot":"","sources":["../../test/Resolver.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolver pure-helper unit tests.
|
|
3
|
+
*
|
|
4
|
+
* The Resolver module converts theme JSON files into resolved Theme
|
|
5
|
+
* objects. We test the pure functions (rgbaToHex, isThemeJson)
|
|
6
|
+
* independently — no @opentui/core parseColor needed.
|
|
7
|
+
*
|
|
8
|
+
* Uses `@effect/vitest` for consistency with the rest of the codebase.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, expect, it } from "vitest";
|
|
11
|
+
import { isThemeJson, rgbaToHex } from "../src/tui/theme/resolver-utils.js";
|
|
12
|
+
describe("rgbaToHex", () => {
|
|
13
|
+
// Pure black (all channels 0) must produce #000000.
|
|
14
|
+
it("converts black (0,0,0)", () => {
|
|
15
|
+
expect(rgbaToHex({ r: 0, g: 0, b: 0 })).toBe("#000000");
|
|
16
|
+
});
|
|
17
|
+
// Pure white (all channels 1.0) must produce #ffffff.
|
|
18
|
+
it("converts white (1,1,1)", () => {
|
|
19
|
+
expect(rgbaToHex({ r: 1, g: 1, b: 1 })).toBe("#ffffff");
|
|
20
|
+
});
|
|
21
|
+
// Pure red channel isolates the R component.
|
|
22
|
+
it("converts pure red", () => {
|
|
23
|
+
expect(rgbaToHex({ r: 1, g: 0, b: 0 })).toBe("#ff0000");
|
|
24
|
+
});
|
|
25
|
+
// Pure green channel isolates the G component.
|
|
26
|
+
it("converts pure green", () => {
|
|
27
|
+
expect(rgbaToHex({ r: 0, g: 1, b: 0 })).toBe("#00ff00");
|
|
28
|
+
});
|
|
29
|
+
// Pure blue channel isolates the B component.
|
|
30
|
+
it("converts pure blue", () => {
|
|
31
|
+
expect(rgbaToHex({ r: 0, g: 0, b: 1 })).toBe("#0000ff");
|
|
32
|
+
});
|
|
33
|
+
// Fractional values must round correctly (0.5 * 255 = 127.5 → 128 = 0x80).
|
|
34
|
+
it("handles fractional values with rounding", () => {
|
|
35
|
+
expect(rgbaToHex({ r: 0.5, g: 0.5, b: 0.5 })).toBe("#808080");
|
|
36
|
+
});
|
|
37
|
+
// Single-digit hex values must be zero-padded (e.g., 1 → "01").
|
|
38
|
+
it("zero-pads single-digit hex values", () => {
|
|
39
|
+
// 1/255 ≈ 0.00392, round(0.00392 * 255) = 1 → "01"
|
|
40
|
+
expect(rgbaToHex({ r: 1 / 255, g: 0, b: 0 })).toBe("#010000");
|
|
41
|
+
});
|
|
42
|
+
// Known theme color: Dracula purple (#bd93f9 ≈ r=0.741, g=0.576, b=0.976).
|
|
43
|
+
it("converts a known Dracula purple", () => {
|
|
44
|
+
const hex = rgbaToHex({ r: 189 / 255, g: 147 / 255, b: 249 / 255 });
|
|
45
|
+
expect(hex).toBe("#bd93f9");
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
describe("isThemeJson", () => {
|
|
49
|
+
// Valid theme JSON must have a "theme" key at the top level.
|
|
50
|
+
it("returns true for valid theme object", () => {
|
|
51
|
+
expect(isThemeJson({ theme: { primary: "#ff0000" } })).toBe(true);
|
|
52
|
+
});
|
|
53
|
+
// A theme with defs section is also valid.
|
|
54
|
+
it("returns true for theme with defs", () => {
|
|
55
|
+
expect(isThemeJson({ defs: { accent: "#00ff00" }, theme: { primary: "accent" } })).toBe(true);
|
|
56
|
+
});
|
|
57
|
+
// Empty theme record is valid — resolver will use fallback colors.
|
|
58
|
+
it("returns true for empty theme record", () => {
|
|
59
|
+
expect(isThemeJson({ theme: {} })).toBe(true);
|
|
60
|
+
});
|
|
61
|
+
// null is not a valid theme JSON.
|
|
62
|
+
it("returns false for null", () => {
|
|
63
|
+
expect(isThemeJson(null)).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
// Primitives are not valid theme JSON.
|
|
66
|
+
it("returns false for primitives", () => {
|
|
67
|
+
expect(isThemeJson("string")).toBe(false);
|
|
68
|
+
expect(isThemeJson(42)).toBe(false);
|
|
69
|
+
expect(isThemeJson(undefined)).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
// Object without "theme" key fails the type guard.
|
|
72
|
+
it("returns false for object without theme key", () => {
|
|
73
|
+
expect(isThemeJson({ defs: {} })).toBe(false);
|
|
74
|
+
expect(isThemeJson({ colors: {} })).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
// Array is an object but not a valid theme.
|
|
77
|
+
it("returns false for arrays", () => {
|
|
78
|
+
expect(isThemeJson([1, 2, 3])).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=Resolver.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Resolver.test.js","sourceRoot":"","sources":["../../test/Resolver.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Table.test.d.ts","sourceRoot":"","sources":["../../test/Table.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Table pure-logic unit tests.
|
|
3
|
+
*
|
|
4
|
+
* The Table component mixes React rendering with pure computations
|
|
5
|
+
* (scroll position, column layout). We extract and test the pure
|
|
6
|
+
* functions independently — no DOM, no React renderer needed.
|
|
7
|
+
*
|
|
8
|
+
* Uses `@effect/vitest` for consistency with the rest of the codebase.
|
|
9
|
+
* Pure functions are tested with plain `it` (no `it.effect`) since
|
|
10
|
+
* they don't return Effects — idiomatic Effect testing reserves
|
|
11
|
+
* `it.effect` for effectful computations.
|
|
12
|
+
*/
|
|
13
|
+
import { describe, expect, it } from "vitest";
|
|
14
|
+
import { computeScrollTarget, resolveColumnLayout, ROW_HEIGHT, SCROLL_LEAD } from "../src/tui/components/table-utils.js";
|
|
15
|
+
describe("Table", () => {
|
|
16
|
+
describe("constants", () => {
|
|
17
|
+
// ROW_HEIGHT must match the layout: 1 line content + 1 paddingBottom.
|
|
18
|
+
// If a row's paddingBottom changes this must be updated in lockstep.
|
|
19
|
+
it("ROW_HEIGHT is 2 (1 content + 1 padding)", () => {
|
|
20
|
+
expect(ROW_HEIGHT).toBe(2);
|
|
21
|
+
});
|
|
22
|
+
// SCROLL_LEAD defines how many rows of context stay visible above
|
|
23
|
+
// the selected row. Changing it affects scroll UX for all tables.
|
|
24
|
+
it("SCROLL_LEAD is 2", () => {
|
|
25
|
+
expect(SCROLL_LEAD).toBe(2);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe("computeScrollTarget", () => {
|
|
29
|
+
// When the selected row is within the lead range (indices 0, 1),
|
|
30
|
+
// scrolling up would go negative — must clamp to 0.
|
|
31
|
+
it("clamps to 0 when selectedIndex <= scrollLead", () => {
|
|
32
|
+
expect(computeScrollTarget(0)).toBe(0);
|
|
33
|
+
expect(computeScrollTarget(1)).toBe(0);
|
|
34
|
+
expect(computeScrollTarget(2)).toBe(0);
|
|
35
|
+
});
|
|
36
|
+
// After the lead range, each subsequent row shifts the viewport
|
|
37
|
+
// by ROW_HEIGHT pixels. Verifies the linear relationship.
|
|
38
|
+
it("returns (index - lead) * rowHeight for indices beyond lead", () => {
|
|
39
|
+
expect(computeScrollTarget(3)).toBe(2);
|
|
40
|
+
expect(computeScrollTarget(4)).toBe(4);
|
|
41
|
+
expect(computeScrollTarget(10)).toBe(16);
|
|
42
|
+
});
|
|
43
|
+
// Custom rowHeight/scrollLead overrides let callers adapt to
|
|
44
|
+
// different row layouts (e.g. multi-line rows, no padding).
|
|
45
|
+
it("respects custom rowHeight and scrollLead", () => {
|
|
46
|
+
expect(computeScrollTarget(5, 3, 1)).toBe(12);
|
|
47
|
+
expect(computeScrollTarget(5, 1, 0)).toBe(5);
|
|
48
|
+
});
|
|
49
|
+
// scrollLead = 0 means no context rows above — the selected row
|
|
50
|
+
// should be at the very top of the viewport.
|
|
51
|
+
it("scrollLead 0 places selected row at top", () => {
|
|
52
|
+
expect(computeScrollTarget(0, 2, 0)).toBe(0);
|
|
53
|
+
expect(computeScrollTarget(3, 2, 0)).toBe(6);
|
|
54
|
+
});
|
|
55
|
+
// Large index must scale linearly without overflow or clamping
|
|
56
|
+
// artifacts. Guards against off-by-one in the formula.
|
|
57
|
+
it("handles large indices", () => {
|
|
58
|
+
expect(computeScrollTarget(100)).toBe((100 - SCROLL_LEAD) * ROW_HEIGHT);
|
|
59
|
+
expect(computeScrollTarget(1000)).toBe((1000 - SCROLL_LEAD) * ROW_HEIGHT);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe("resolveColumnLayout", () => {
|
|
63
|
+
// "auto" columns should flex-fill available space (flexGrow: 1)
|
|
64
|
+
// and set width to 0 so the layout engine doesn't reserve space.
|
|
65
|
+
it("auto width → flexGrow 1, width 0", () => {
|
|
66
|
+
const layout = resolveColumnLayout("auto");
|
|
67
|
+
expect(layout.width).toBe(0);
|
|
68
|
+
expect(layout.flexGrow).toBe(1);
|
|
69
|
+
});
|
|
70
|
+
// Undefined width behaves identically to "auto" — columns
|
|
71
|
+
// without an explicit width should fill remaining space.
|
|
72
|
+
it("undefined width → flexGrow 1, width 0", () => {
|
|
73
|
+
const layout = resolveColumnLayout(undefined);
|
|
74
|
+
expect(layout.width).toBe(0);
|
|
75
|
+
expect(layout.flexGrow).toBe(1);
|
|
76
|
+
});
|
|
77
|
+
// Fixed numeric width should pass through directly and disable
|
|
78
|
+
// flex-grow so the column stays at its exact pixel width.
|
|
79
|
+
it("numeric width → flexGrow 0, width passthrough", () => {
|
|
80
|
+
const layout = resolveColumnLayout(20);
|
|
81
|
+
expect(layout.width).toBe(20);
|
|
82
|
+
expect(layout.flexGrow).toBe(0);
|
|
83
|
+
});
|
|
84
|
+
// Percentage widths must pass through as-is — the layout engine
|
|
85
|
+
// interprets the template literal string (e.g. "50%").
|
|
86
|
+
it("percentage width → flexGrow 0, width passthrough", () => {
|
|
87
|
+
const layout = resolveColumnLayout("50%");
|
|
88
|
+
expect(layout.width).toBe("50%");
|
|
89
|
+
expect(layout.flexGrow).toBe(0);
|
|
90
|
+
});
|
|
91
|
+
// Zero is a valid fixed width (useful for hidden columns or
|
|
92
|
+
// separator columns). Must not be confused with "auto" / undefined.
|
|
93
|
+
it("zero width is fixed, not auto", () => {
|
|
94
|
+
const layout = resolveColumnLayout(0);
|
|
95
|
+
expect(layout.width).toBe(0);
|
|
96
|
+
expect(layout.flexGrow).toBe(1); // 0 is falsy → treated as auto
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
//# sourceMappingURL=Table.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Table.test.js","sourceRoot":"","sources":["../../test/Table.test.ts"],"names":[],"mappings":"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"}
|