From d41b5c0c2b45370adaa58db6d9cac018a218acc6 Mon Sep 17 00:00:00 2001 From: kubrickcode Date: Wed, 19 Nov 2025 13:31:56 +0000 Subject: [PATCH] refactor: add unique ID field to ButtonConfig for identity tracking Added mandatory id field to ButtonConfig to enable unique identification of each button and group. - Added id: string field to ButtonConfig type - Implemented ID auto-generation utilities (ensureId, ensureIdsInArray) - Auto-assign IDs when loading existing config data in ConfigReader - Generate UUIDs automatically when creating new buttons/groups in webview components and hooks - Added id field to all test cases to ensure type safety fix #81 --- src/extension/view-dist/index.html | 2 +- src/internal/adapters.ts | 7 +- src/internal/utils/ensure-id.ts | 20 ++ src/shared/types.ts | 1 + src/tests/adapters.spec.ts | 56 ++++- src/tests/command-executor.spec.ts | 105 ++++++--- src/tests/command-tree-provider.spec.ts | 44 ++-- src/tests/config-manager.spec.ts | 4 +- src/tests/show-all-commands.spec.ts | 15 ++ src/tests/status-bar-manager.spec.ts | 20 +- src/tests/ui-items.spec.ts | 41 ++-- src/tests/utils-ensure-id.spec.ts | 217 ++++++++++++++++++ src/view/src/components/command-form.tsx | 2 + src/view/src/components/command-list.tsx | 4 +- .../src/components/group-command-list.tsx | 12 +- src/view/src/hooks/use-command-operations.tsx | 2 + src/view/src/hooks/use-sortable-list.tsx | 9 +- src/view/src/mock/mock-data.tsx | 9 + 18 files changed, 485 insertions(+), 85 deletions(-) create mode 100644 src/internal/utils/ensure-id.ts create mode 100644 src/tests/utils-ensure-id.spec.ts diff --git a/src/extension/view-dist/index.html b/src/extension/view-dist/index.html index 7f0f7adc..23bb8a6a 100644 --- a/src/extension/view-dist/index.html +++ b/src/extension/view-dist/index.html @@ -5,7 +5,7 @@ Vite + React + TS - + diff --git a/src/internal/adapters.ts b/src/internal/adapters.ts index 42f9b51d..b2d37701 100644 --- a/src/internal/adapters.ts +++ b/src/internal/adapters.ts @@ -1,6 +1,8 @@ import * as vscode from "vscode"; import { CONFIG_SECTION } from "../pkg/config-constants"; import { ButtonConfig, RefreshButtonConfig } from "../pkg/types"; +import { ensureIdsInArray, ButtonConfigWithOptionalId } from "./utils/ensure-id"; + const DEFAULT_REFRESH_CONFIG: RefreshButtonConfig = { color: "#00BCD4", enabled: true, @@ -28,7 +30,7 @@ export type StatusBarCreator = ( export type QuickPickCreator = () => vscode.QuickPick; -const getButtonsFromConfig = (config: vscode.WorkspaceConfiguration): ButtonConfig[] => +const getButtonsFromConfig = (config: vscode.WorkspaceConfiguration): ButtonConfigWithOptionalId[] => config.get("buttons") || []; const getRefreshConfigFromConfig = (config: vscode.WorkspaceConfiguration): RefreshButtonConfig => @@ -40,7 +42,8 @@ const isQuickCommandButtonsConfigChange = (event: vscode.ConfigurationChangeEven export const createVSCodeConfigReader = (): ConfigReader => ({ getButtons: () => { const config = vscode.workspace.getConfiguration(CONFIG_SECTION); - return getButtonsFromConfig(config); + const buttons = getButtonsFromConfig(config); + return ensureIdsInArray(buttons); }, getRefreshConfig: () => { const config = vscode.workspace.getConfiguration(CONFIG_SECTION); diff --git a/src/internal/utils/ensure-id.ts b/src/internal/utils/ensure-id.ts new file mode 100644 index 00000000..438a736e --- /dev/null +++ b/src/internal/utils/ensure-id.ts @@ -0,0 +1,20 @@ +import { randomUUID } from "crypto"; +import { ButtonConfig } from "../../pkg/types"; + +export type ButtonConfigWithOptionalId = Omit & { + group?: ButtonConfigWithOptionalId[]; + id?: string; +}; + +export const ensureId = (config: ButtonConfigWithOptionalId): ButtonConfig => { + const { group, id, ...restConfig } = config; + + return { + ...restConfig, + id: id ?? randomUUID(), + ...(group !== undefined && { group: group.map(ensureId) }), + }; +}; + +export const ensureIdsInArray = (configs: ButtonConfigWithOptionalId[]): ButtonConfig[] => + configs.map((config) => ensureId(config)); diff --git a/src/shared/types.ts b/src/shared/types.ts index 0002c655..1fd83b37 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -3,6 +3,7 @@ export type ButtonConfig = { command?: string; executeAll?: boolean; group?: ButtonConfig[]; + id: string; name: string; shortcut?: string; terminalName?: string; diff --git a/src/tests/adapters.spec.ts b/src/tests/adapters.spec.ts index 606a62ec..fe6606c9 100644 --- a/src/tests/adapters.spec.ts +++ b/src/tests/adapters.spec.ts @@ -7,7 +7,7 @@ describe("adapters", () => { }); describe("getButtonsFromConfig", () => { - it("should return buttons from config", () => { + it("should return buttons from config with IDs added", () => { const mockButtons = [ { command: "test command", name: "Test Button" }, { command: "another command", name: "Another Button" }, @@ -22,11 +22,63 @@ describe("adapters", () => { const configReader = createVSCodeConfigReader(); const result = configReader.getButtons(); - expect(result).toEqual(mockButtons); + expect(result).toHaveLength(2); + expect(result[0].name).toBe("Test Button"); + expect(result[1].name).toBe("Another Button"); + expect(result[0].id).toBeDefined(); + expect(result[1].id).toBeDefined(); + expect(result[0].id).not.toBe(result[1].id); expect(vscode.workspace.getConfiguration).toHaveBeenCalledWith("quickCommandButtons"); expect(mockConfig.get).toHaveBeenCalledWith("buttons"); }); + it("should preserve existing IDs in buttons", () => { + const existingId1 = "existing-id-1"; + const existingId2 = "existing-id-2"; + const mockButtons = [ + { command: "test command", name: "Test Button", id: existingId1 }, + { command: "another command", name: "Another Button", id: existingId2 }, + ]; + + const mockConfig = { + get: jest.fn((key: string) => (key === "buttons" ? mockButtons : undefined)), + }; + + (vscode.workspace.getConfiguration as jest.Mock).mockReturnValue(mockConfig); + + const configReader = createVSCodeConfigReader(); + const result = configReader.getButtons(); + + expect(result[0].id).toBe(existingId1); + expect(result[1].id).toBe(existingId2); + }); + + it("should add IDs to nested group items", () => { + const mockButtons = [ + { + name: "Parent Group", + group: [ + { command: "child 1", name: "Child 1" }, + { command: "child 2", name: "Child 2" }, + ], + }, + ]; + + const mockConfig = { + get: jest.fn((key: string) => (key === "buttons" ? mockButtons : undefined)), + }; + + (vscode.workspace.getConfiguration as jest.Mock).mockReturnValue(mockConfig); + + const configReader = createVSCodeConfigReader(); + const result = configReader.getButtons(); + + expect(result[0].id).toBeDefined(); + expect(result[0].group![0].id).toBeDefined(); + expect(result[0].group![1].id).toBeDefined(); + expect(result[0].group![0].id).not.toBe(result[0].group![1].id); + }); + it("should return empty array when no buttons in config", () => { const mockConfig = { get: jest.fn(() => undefined), diff --git a/src/tests/command-executor.spec.ts b/src/tests/command-executor.spec.ts index 2df1cf6b..94d3b0dd 100644 --- a/src/tests/command-executor.spec.ts +++ b/src/tests/command-executor.spec.ts @@ -13,12 +13,12 @@ describe("command-executor", () => { it("should return empty array for unique shortcuts", () => { const items = [ { - command: { name: "test1", shortcut: "a" } as ButtonConfig, + command: { id: "test-1", name: "test1", shortcut: "a" } as ButtonConfig, description: "", label: "Test 1", }, { - command: { name: "test2", shortcut: "b" } as ButtonConfig, + command: { id: "test-2", name: "test2", shortcut: "b" } as ButtonConfig, description: "", label: "Test 2", }, @@ -32,17 +32,17 @@ describe("command-executor", () => { it("should return duplicated shortcuts (case insensitive)", () => { const items = [ { - command: { name: "test1", shortcut: "a" } as ButtonConfig, + command: { id: "test-1", name: "test1", shortcut: "a" } as ButtonConfig, description: "", label: "Test 1", }, { - command: { name: "test2", shortcut: "A" } as ButtonConfig, + command: { id: "test-2", name: "test2", shortcut: "A" } as ButtonConfig, description: "", label: "Test 2", }, { - command: { name: "test3", shortcut: "b" } as ButtonConfig, + command: { id: "test-3", name: "test3", shortcut: "b" } as ButtonConfig, description: "", label: "Test 3", }, @@ -56,22 +56,22 @@ describe("command-executor", () => { it("should return multiple duplicated shortcuts", () => { const items = [ { - command: { name: "test1", shortcut: "a" } as ButtonConfig, + command: { id: "test-1", name: "test1", shortcut: "a" } as ButtonConfig, description: "", label: "Test 1", }, { - command: { name: "test2", shortcut: "A" } as ButtonConfig, + command: { id: "test-2", name: "test2", shortcut: "A" } as ButtonConfig, description: "", label: "Test 2", }, { - command: { name: "test3", shortcut: "b" } as ButtonConfig, + command: { id: "test-3", name: "test3", shortcut: "b" } as ButtonConfig, description: "", label: "Test 3", }, { - command: { name: "test4", shortcut: "B" } as ButtonConfig, + command: { id: "test-4", name: "test4", shortcut: "B" } as ButtonConfig, description: "", label: "Test 4", }, @@ -85,12 +85,12 @@ describe("command-executor", () => { it("should handle items without shortcuts", () => { const items = [ { - command: { name: "test1" } as ButtonConfig, + command: { id: "test-1", name: "test1" } as ButtonConfig, description: "", label: "Test 1", }, { - command: { name: "test2", shortcut: "a" } as ButtonConfig, + command: { id: "test-2", name: "test2", shortcut: "a" } as ButtonConfig, description: "", label: "Test 2", }, @@ -116,12 +116,12 @@ describe("command-executor", () => { it("should handle all items without shortcuts", () => { const items = [ { - command: { name: "test1" } as ButtonConfig, + command: { id: "test-1", name: "test1" } as ButtonConfig, description: "", label: "Test 1", }, { - command: { name: "test2" } as ButtonConfig, + command: { id: "test-2", name: "test2" } as ButtonConfig, description: "", label: "Test 2", }, @@ -136,17 +136,17 @@ describe("command-executor", () => { describe("findShortcutItem", () => { const items = [ { - command: { name: "test1", shortcut: "a" } as ButtonConfig, + command: { id: "test-1", name: "test1", shortcut: "a" } as ButtonConfig, description: "", label: "Test 1", }, { - command: { name: "test2", shortcut: "B" } as ButtonConfig, + command: { id: "test-2", name: "test2", shortcut: "B" } as ButtonConfig, description: "", label: "Test 2", }, { - command: { name: "test3" } as ButtonConfig, + command: { id: "test-3", name: "test3" } as ButtonConfig, description: "", label: "Test 3", }, @@ -191,7 +191,7 @@ describe("command-executor", () => { it("should return undefined when no items have shortcuts", () => { const itemsWithoutShortcuts = [ { - command: { name: "test1" } as ButtonConfig, + command: { id: "test-1", name: "test1" } as ButtonConfig, description: "", label: "Test 1", }, @@ -229,7 +229,7 @@ describe("command-executor", () => { it("should handle Arabic characters", () => { const arabicItems = [ { - command: { name: "test1", shortcut: "z" } as ButtonConfig, + command: { id: "test-arabic", name: "test1", shortcut: "z" } as ButtonConfig, description: "", label: "Test 1", }, @@ -242,7 +242,7 @@ describe("command-executor", () => { it("should handle Hebrew characters", () => { const hebrewItems = [ { - command: { name: "test1", shortcut: "e" } as ButtonConfig, + command: { id: "test-hebrew", name: "test1", shortcut: "e" } as ButtonConfig, description: "", label: "Test 1", }, @@ -257,7 +257,8 @@ describe("command-executor", () => { it("should return 'executeAll' for button with group and executeAll flag", () => { const button: ButtonConfig = { executeAll: true, - group: [{ command: "echo test", name: "child" }], + group: [{ id: "child-1", command: "echo test", name: "child" }], + id: "test-group", name: "test", }; @@ -268,7 +269,8 @@ describe("command-executor", () => { it("should return 'showQuickPick' for button with group but no executeAll flag", () => { const button: ButtonConfig = { - group: [{ command: "echo test", name: "child" }], + group: [{ id: "child-1", command: "echo test", name: "child" }], + id: "test-group", name: "test", }; @@ -280,7 +282,8 @@ describe("command-executor", () => { it("should return 'showQuickPick' for button with group and executeAll set to false", () => { const button: ButtonConfig = { executeAll: false, - group: [{ command: "echo test", name: "child" }], + group: [{ id: "child-1", command: "echo test", name: "child" }], + id: "test-group", name: "test", }; @@ -292,6 +295,7 @@ describe("command-executor", () => { it("should return 'executeCommand' for button with command but no group", () => { const button: ButtonConfig = { command: "echo test", + id: "test-command", name: "test", }; @@ -302,6 +306,7 @@ describe("command-executor", () => { it("should return 'invalid' for button without command and without group", () => { const button: ButtonConfig = { + id: "test-invalid", name: "test", }; @@ -313,6 +318,7 @@ describe("command-executor", () => { it("should return 'invalid' for button with empty command string", () => { const button: ButtonConfig = { command: "", + id: "test-empty", name: "test", }; @@ -325,7 +331,8 @@ describe("command-executor", () => { const button: ButtonConfig = { command: "echo test", executeAll: false, - group: [{ command: "echo child", name: "child" }], + group: [{ id: "child-1", command: "echo child", name: "child" }], + id: "test-mixed", name: "test", }; @@ -338,7 +345,8 @@ describe("command-executor", () => { const button: ButtonConfig = { command: "echo test", executeAll: true, - group: [{ command: "echo child", name: "child" }], + group: [{ id: "child-1", command: "echo child", name: "child" }], + id: "test-executeall", name: "test", }; @@ -353,6 +361,7 @@ describe("command-executor", () => { const commands: ButtonConfig[] = [ { command: "echo test", + id: "test-shortcut", name: "Test Command", shortcut: "t", }, @@ -373,6 +382,7 @@ describe("command-executor", () => { const commands: ButtonConfig[] = [ { command: "echo test", + id: "test-no-shortcut", name: "Test Command", }, ]; @@ -392,6 +402,7 @@ describe("command-executor", () => { const commands: ButtonConfig[] = [ { command: "", + id: "test-empty-cmd", name: "Test Command", shortcut: "t", }, @@ -411,6 +422,7 @@ describe("command-executor", () => { it("should handle command without command property", () => { const commands: ButtonConfig[] = [ { + id: "test-no-cmd-prop", name: "Test Command", shortcut: "t", }, @@ -431,14 +443,17 @@ describe("command-executor", () => { const commands: ButtonConfig[] = [ { command: "echo 1", + id: "cmd-1", name: "Command 1", shortcut: "1", }, { command: "echo 2", + id: "cmd-2", name: "Command 2", }, { + id: "cmd-3", name: "Command 3", shortcut: "3", }, @@ -478,6 +493,7 @@ describe("command-executor", () => { { additionalProperty: "custom", command: "echo test", + id: "test-preserve", name: "Test Command", } as ButtonConfig & { additionalProperty: string }, ]; @@ -493,6 +509,7 @@ describe("command-executor", () => { const mockTerminalExecutor = jest.fn(); const button: ButtonConfig = { command: "echo test", + id: "test-terminal-1", name: "Test Button", }; @@ -503,7 +520,7 @@ describe("command-executor", () => { false, undefined, "Test Button", - expect.objectContaining({ command: "echo test", name: "Test Button" }) + expect.objectContaining({ command: "echo test", id: "test-terminal-1", name: "Test Button" }) ); }); @@ -511,6 +528,7 @@ describe("command-executor", () => { const mockTerminalExecutor = jest.fn(); const button: ButtonConfig = { command: "echo test", + id: "test-vscode-api", name: "Test Button", useVsCodeApi: true, }; @@ -522,7 +540,7 @@ describe("command-executor", () => { true, undefined, "Test Button", - expect.objectContaining({ command: "echo test", name: "Test Button", useVsCodeApi: true }) + expect.objectContaining({ command: "echo test", id: "test-vscode-api", name: "Test Button", useVsCodeApi: true }) ); }); @@ -530,6 +548,7 @@ describe("command-executor", () => { const mockTerminalExecutor = jest.fn(); const button: ButtonConfig = { command: "echo test", + id: "test-terminal-name", name: "Test Button", terminalName: "Custom Terminal", }; @@ -543,6 +562,7 @@ describe("command-executor", () => { "Test Button", expect.objectContaining({ command: "echo test", + id: "test-terminal-name", name: "Test Button", terminalName: "Custom Terminal", }) @@ -553,6 +573,7 @@ describe("command-executor", () => { const mockTerminalExecutor = jest.fn(); const button: ButtonConfig = { command: "echo test", + id: "test-all-params", name: "Test Button", terminalName: "Custom Terminal", useVsCodeApi: true, @@ -567,6 +588,7 @@ describe("command-executor", () => { "Test Button", expect.objectContaining({ command: "echo test", + id: "test-all-params", name: "Test Button", terminalName: "Custom Terminal", useVsCodeApi: true, @@ -577,6 +599,7 @@ describe("command-executor", () => { it("should not call terminalExecutor when command is undefined", () => { const mockTerminalExecutor = jest.fn(); const button: ButtonConfig = { + id: "test-no-command", name: "Test Button", }; @@ -589,6 +612,7 @@ describe("command-executor", () => { const mockTerminalExecutor = jest.fn(); const button: ButtonConfig = { command: "", + id: "test-empty-command", name: "Test Button", }; @@ -604,15 +628,18 @@ describe("command-executor", () => { const commands: ButtonConfig[] = [ { command: "echo test1", + id: "rec-cmd-1", name: "Command 1", }, { command: "echo test2", + id: "rec-cmd-2", name: "Command 2", useVsCodeApi: true, }, { command: "echo test3", + id: "rec-cmd-3", name: "Command 3", terminalName: "Custom Terminal", }, @@ -652,14 +679,17 @@ describe("command-executor", () => { group: [ { command: "echo child1", + id: "child-1", name: "Child 1", }, { command: "echo child2", + id: "child-2", name: "Child 2", useVsCodeApi: true, }, ], + id: "group-cmd", name: "Group Command", }, ]; @@ -691,9 +721,11 @@ describe("command-executor", () => { group: [ { command: "echo child1", + id: "child-1", name: "Child 1", }, ], + id: "group-no-exec", name: "Group Command", }, ]; @@ -714,16 +746,20 @@ describe("command-executor", () => { group: [ { command: "echo level3", + id: "level-3", name: "Level 3 Command", }, ], + id: "level-2-group", name: "Level 2 Group", }, { command: "echo level2", + id: "level-2-cmd", name: "Level 2 Command", }, ], + id: "level-1-group", name: "Level 1 Group", }, ]; @@ -752,9 +788,11 @@ describe("command-executor", () => { const commands: ButtonConfig[] = [ { command: "echo valid", + id: "valid-cmd", name: "Valid Command", }, { + id: "invalid-cmd", name: "Invalid Command", }, ]; @@ -775,10 +813,12 @@ describe("command-executor", () => { const commands: ButtonConfig[] = [ { command: "echo valid", + id: "valid-cmd", name: "Valid Command", }, { command: "", + id: "empty-cmd", name: "Empty Command", }, ]; @@ -808,6 +848,7 @@ describe("command-executor", () => { const commands: ButtonConfig[] = [ { command: "echo regular", + id: "regular-cmd", name: "Regular Command", }, { @@ -815,9 +856,11 @@ describe("command-executor", () => { group: [ { command: "echo child", + id: "child-cmd", name: "Child Command", }, ], + id: "group-with-exec", name: "Group with executeAll", }, { @@ -825,12 +868,15 @@ describe("command-executor", () => { group: [ { command: "echo ignored", + id: "ignored-child", name: "Ignored Child", }, ], + id: "group-without-exec", name: "Group without executeAll", }, { + id: "invalid-cmd", name: "Invalid Command", }, ]; @@ -865,13 +911,16 @@ describe("command-executor", () => { group: [ { command: "echo leaf1", + id: "leaf-1", name: "Leaf 1", }, { command: "echo leaf2", + id: "leaf-2", name: "Leaf 2", }, ], + id: "branch-1", name: "Branch 1", }, { @@ -879,16 +928,20 @@ describe("command-executor", () => { group: [ { command: "echo ignored", + id: "ignored-leaf", name: "Ignored Leaf", }, ], + id: "branch-2", name: "Branch 2", }, { command: "echo direct", + id: "direct-cmd", name: "Direct Command", }, ], + id: "root-group", name: "Root Group", }, ]; diff --git a/src/tests/command-tree-provider.spec.ts b/src/tests/command-tree-provider.spec.ts index bf3c28f3..b0b6da8d 100644 --- a/src/tests/command-tree-provider.spec.ts +++ b/src/tests/command-tree-provider.spec.ts @@ -6,8 +6,8 @@ describe("command-tree-provider", () => { describe("createTreeItems", () => { it("should create command tree items from simple commands", () => { const commands: ButtonConfig[] = [ - { command: "echo hello", name: "Test Command 1" }, - { command: "ls -la", name: "Test Command 2" }, + { id: "test-cmd-1", command: "echo hello", name: "Test Command 1" }, + { id: "test-cmd-2", command: "ls -la", name: "Test Command 2" }, ]; const result = createTreeItems(commands); @@ -29,6 +29,7 @@ describe("command-tree-provider", () => { it("should create command tree items with VS Code API and terminal name", () => { const commands: ButtonConfig[] = [ { + id: "vscode-cmd", command: "workbench.action.openSettings", name: "VS Code Command", terminalName: "settings-terminal", @@ -47,8 +48,8 @@ describe("command-tree-provider", () => { it("should handle commands with empty command string", () => { const commands: ButtonConfig[] = [ - { command: "", name: "Empty Command" }, - { name: "No Command Property" } as ButtonConfig, + { id: "empty-cmd", command: "", name: "Empty Command" }, + { id: "no-cmd-prop", name: "No Command Property" } as ButtonConfig, ]; const result = createTreeItems(commands); @@ -70,6 +71,7 @@ describe("command-tree-provider", () => { it("should preserve original command objects without modification", () => { const commands: ButtonConfig[] = [ { + id: "original-cmd", color: "red", command: "test command", name: "Original Command", @@ -86,14 +88,16 @@ describe("command-tree-provider", () => { it("should handle mixed command configurations", () => { const commands: ButtonConfig[] = [ - { command: "echo simple", name: "Simple" }, + { id: "simple-cmd", command: "echo simple", name: "Simple" }, { + id: "complex-cmd", command: "npm test", name: "Complex", terminalName: "test-terminal", useVsCodeApi: false, }, { + id: "vscode-reload", command: "workbench.action.reload", name: "VS Code", useVsCodeApi: true, @@ -113,11 +117,12 @@ describe("command-tree-provider", () => { it("should handle nested groups", () => { const commands: ButtonConfig[] = [ - { command: "echo simple", name: "Simple Command" }, + { id: "simple-cmd", command: "echo simple", name: "Simple Command" }, { + id: "nested-group", group: [ - { command: "echo sub1", name: "Sub Command 1" }, - { command: "echo sub2", name: "Sub Command 2" }, + { id: "sub-cmd-1", command: "echo sub1", name: "Sub Command 1" }, + { id: "sub-cmd-2", command: "echo sub2", name: "Sub Command 2" }, ], name: "Nested Group", }, @@ -138,15 +143,17 @@ describe("command-tree-provider", () => { describe("createTreeItems (root)", () => { it("should create mixed command and group tree items", () => { const buttons: ButtonConfig[] = [ - { command: "echo hello", name: "Simple Command" }, + { id: "simple-cmd", command: "echo hello", name: "Simple Command" }, { + id: "cmd-group", group: [ - { command: "ls", name: "Group Command 1" }, - { command: "pwd", name: "Group Command 2" }, + { id: "group-cmd-1", command: "ls", name: "Group Command 1" }, + { id: "group-cmd-2", command: "pwd", name: "Group Command 2" }, ], name: "Command Group", }, { + id: "vscode-cmd", command: "workbench.action.reload", name: "VS Code Command", useVsCodeApi: true, @@ -172,12 +179,14 @@ describe("command-tree-provider", () => { it("should verify correct item types and labels", () => { const buttons: ButtonConfig[] = [ { + id: "terminal-cmd", command: "npm run build", name: "Terminal Command", terminalName: "build-terminal", }, { - group: [{ command: "npm start", name: "Start Server" }], + id: "dev-tools", + group: [{ id: "start-server", command: "npm start", name: "Start Server" }], name: "Development Tools", }, ]; @@ -211,8 +220,8 @@ describe("command-tree-provider", () => { it("should handle buttons with no command property", () => { const buttons: ButtonConfig[] = [ - { name: "Invalid Button" } as ButtonConfig, - { command: "", name: "Empty Command" }, + { id: "invalid-btn", name: "Invalid Button" } as ButtonConfig, + { id: "empty-cmd", command: "", name: "Empty Command" }, ]; const result = createTreeItems(buttons); @@ -227,10 +236,11 @@ describe("command-tree-provider", () => { it("should handle nested group structures", () => { const buttons: ButtonConfig[] = [ { + id: "main-group", group: [ - { command: "echo test1", name: "Sub Command 1" }, - { command: "echo test2", name: "Sub Command 2" }, - { command: "echo test3", name: "Sub Command 3" }, + { id: "sub-cmd-1", command: "echo test1", name: "Sub Command 1" }, + { id: "sub-cmd-2", command: "echo test2", name: "Sub Command 2" }, + { id: "sub-cmd-3", command: "echo test3", name: "Sub Command 3" }, ], name: "Main Group", }, diff --git a/src/tests/config-manager.spec.ts b/src/tests/config-manager.spec.ts index 057aaa14..d3472ce0 100644 --- a/src/tests/config-manager.spec.ts +++ b/src/tests/config-manager.spec.ts @@ -111,7 +111,7 @@ describe("ConfigManager", () => { describe("updateButtonConfiguration", () => { it("should update buttons configuration with correct target", async () => { - const mockButtons = [{ name: "Test", command: "echo test" }]; + const mockButtons = [{ id: "test-btn", name: "Test", command: "echo test" }]; const mockConfig = createMockConfig(); mockConfig.get.mockReturnValue(CONFIGURATION_TARGETS.WORKSPACE); mockConfig.update.mockResolvedValue(undefined); @@ -130,7 +130,7 @@ describe("ConfigManager", () => { }); it("should use global target when configured", async () => { - const mockButtons = [{ name: "Test", command: "echo test" }]; + const mockButtons = [{ id: "test-btn", name: "Test", command: "echo test" }]; const mockConfig = createMockConfig(); mockConfig.get.mockReturnValue(CONFIGURATION_TARGETS.GLOBAL); mockConfig.update.mockResolvedValue(undefined); diff --git a/src/tests/show-all-commands.spec.ts b/src/tests/show-all-commands.spec.ts index 443b09fd..6434c090 100644 --- a/src/tests/show-all-commands.spec.ts +++ b/src/tests/show-all-commands.spec.ts @@ -6,11 +6,13 @@ describe("show-all-commands", () => { it("should create QuickPickItems with shortcuts", () => { const buttons: ButtonConfig[] = [ { + id: "test-cmd-1", command: "echo test1", name: "Test Command 1", shortcut: "1", }, { + id: "test-cmd-2", command: "echo test2", name: "Test Command 2", shortcut: "2", @@ -35,10 +37,12 @@ describe("show-all-commands", () => { it("should create QuickPickItems without shortcuts", () => { const buttons: ButtonConfig[] = [ { + id: "test-cmd-1", command: "echo test1", name: "Test Command 1", }, { + id: "test-cmd-2", command: "echo test2", name: "Test Command 2", }, @@ -62,12 +66,15 @@ describe("show-all-commands", () => { it("should handle group buttons", () => { const buttons: ButtonConfig[] = [ { + id: "test-group", group: [ { + id: "nested-cmd-1", command: "echo nested1", name: "Nested Command 1", }, { + id: "nested-cmd-2", command: "echo nested2", name: "Nested Command 2", }, @@ -89,8 +96,10 @@ describe("show-all-commands", () => { it("should handle group buttons with shortcuts", () => { const buttons: ButtonConfig[] = [ { + id: "test-group", group: [ { + id: "nested-cmd", command: "echo nested", name: "Nested Command", }, @@ -113,6 +122,7 @@ describe("show-all-commands", () => { it("should handle buttons with no command", () => { const buttons: ButtonConfig[] = [ { + id: "test-btn", name: "Test Button", }, ]; @@ -139,6 +149,7 @@ describe("show-all-commands", () => { it("should preserve object references", () => { const buttons: ButtonConfig[] = [ { + id: "test-preserve", command: "echo test", name: "Test Command", }, @@ -152,13 +163,16 @@ describe("show-all-commands", () => { it("should handle mixed configurations", () => { const buttons: ButtonConfig[] = [ { + id: "cmd-btn", command: "echo command", name: "Command Button", shortcut: "c", }, { + id: "group-btn", group: [ { + id: "nested-cmd", command: "echo nested", name: "Nested", }, @@ -166,6 +180,7 @@ describe("show-all-commands", () => { name: "Group Button", }, { + id: "empty-btn", name: "Empty Button", }, ]; diff --git a/src/tests/status-bar-manager.spec.ts b/src/tests/status-bar-manager.spec.ts index 295a2a6a..e1b13d02 100644 --- a/src/tests/status-bar-manager.spec.ts +++ b/src/tests/status-bar-manager.spec.ts @@ -41,9 +41,10 @@ describe("status-bar-manager", () => { describe("createTooltipText", () => { it("should return button name with options text for group buttons", () => { const button: ButtonConfig = { + id: "test-group", group: [ - { command: "echo test1", name: "Child 1" }, - { command: "echo test2", name: "Child 2" }, + { id: "child-1", command: "echo test1", name: "Child 1" }, + { id: "child-2", command: "echo test2", name: "Child 2" }, ], name: "Test Group", }; @@ -54,6 +55,7 @@ describe("status-bar-manager", () => { it("should return command when button has command and no group", () => { const button: ButtonConfig = { + id: "test-btn", command: "echo hello", name: "Test Button", }; @@ -64,6 +66,7 @@ describe("status-bar-manager", () => { it("should return button name when button has no command and no group", () => { const button: ButtonConfig = { + id: "test-btn", name: "Test Button", }; @@ -73,6 +76,7 @@ describe("status-bar-manager", () => { it("should return button name with options text for empty group", () => { const button: ButtonConfig = { + id: "empty-group", group: [], name: "Empty Group", }; @@ -83,8 +87,9 @@ describe("status-bar-manager", () => { it("should prioritize group over command when both exist", () => { const button: ButtonConfig = { + id: "mixed-btn", command: "echo test", - group: [{ command: "echo child", name: "Child" }], + group: [{ id: "child-1", command: "echo child", name: "Child" }], name: "Mixed Button", }; @@ -96,6 +101,7 @@ describe("status-bar-manager", () => { describe("createButtonCommand", () => { it("should create command object with correct structure for command button", () => { const button: ButtonConfig = { + id: "test-btn", command: "echo hello", name: "Test Button", }; @@ -111,9 +117,10 @@ describe("status-bar-manager", () => { it("should create command object with correct structure for group button", () => { const button: ButtonConfig = { + id: "test-group", group: [ - { command: "echo test1", name: "Child 1" }, - { command: "echo test2", name: "Child 2" }, + { id: "child-1", command: "echo test1", name: "Child 1" }, + { id: "child-2", command: "echo test2", name: "Child 2" }, ], name: "Test Group", }; @@ -129,6 +136,7 @@ describe("status-bar-manager", () => { it("should preserve button object reference in arguments", () => { const button: ButtonConfig = { + id: "ref-test", command: "echo reference", name: "Reference Test", }; @@ -140,6 +148,7 @@ describe("status-bar-manager", () => { it("should handle button with no command or group", () => { const button: ButtonConfig = { + id: "minimal-btn", name: "Minimal Button", }; @@ -154,6 +163,7 @@ describe("status-bar-manager", () => { it("should handle button with additional properties", () => { const button: ButtonConfig = { + id: "complex-btn", color: "#FF0000", command: "echo test", name: "Complex Button", diff --git a/src/tests/ui-items.spec.ts b/src/tests/ui-items.spec.ts index ff9fe1b2..a1a21014 100644 --- a/src/tests/ui-items.spec.ts +++ b/src/tests/ui-items.spec.ts @@ -40,6 +40,7 @@ describe("ui-items", () => { describe("createQuickPickItem", () => { it("should create QuickPickItem without shortcut", () => { const button: ButtonConfig = { + id: "test-cmd", command: "echo 'test'", name: "Test Command", }; @@ -53,6 +54,7 @@ describe("ui-items", () => { it("should create QuickPickItem with shortcut", () => { const button: ButtonConfig = { + id: "test-cmd", command: "echo 'test'", name: "Test Command", shortcut: "t", @@ -66,9 +68,10 @@ describe("ui-items", () => { it("should create QuickPickItem for group button without command count", () => { const button: ButtonConfig = { + id: "group-btn", group: [ - { command: "cmd1", name: "Command 1" }, - { command: "cmd2", name: "Command 2" }, + { id: "cmd-1", command: "cmd1", name: "Command 1" }, + { id: "cmd-2", command: "cmd2", name: "Command 2" }, ], name: "Group", }; @@ -81,10 +84,11 @@ describe("ui-items", () => { it("should create QuickPickItem for group button with command count", () => { const button: ButtonConfig = { + id: "group-btn", group: [ - { command: "cmd1", name: "Command 1" }, - { command: "cmd2", name: "Command 2" }, - { command: "cmd3", name: "Command 3" }, + { id: "cmd-1", command: "cmd1", name: "Command 1" }, + { id: "cmd-2", command: "cmd2", name: "Command 2" }, + { id: "cmd-3", command: "cmd3", name: "Command 3" }, ], name: "Group", }; @@ -99,8 +103,8 @@ describe("ui-items", () => { describe("createQuickPickItems", () => { it("should create multiple QuickPickItems", () => { const buttons: ButtonConfig[] = [ - { command: "cmd1", name: "Command 1", shortcut: "1" }, - { command: "cmd2", name: "Command 2" }, + { id: "cmd-1", command: "cmd1", name: "Command 1", shortcut: "1" }, + { id: "cmd-2", command: "cmd2", name: "Command 2" }, ]; const items = createQuickPickItems(buttons); @@ -113,9 +117,10 @@ describe("ui-items", () => { it("should include command count when requested", () => { const buttons: ButtonConfig[] = [ { + id: "group-btn", group: [ - { command: "cmd1", name: "Sub 1" }, - { command: "cmd2", name: "Sub 2" }, + { id: "sub-1", command: "cmd1", name: "Sub 1" }, + { id: "sub-2", command: "cmd2", name: "Sub 2" }, ], name: "Group", }, @@ -155,8 +160,8 @@ describe("ui-items", () => { describe("GroupTreeItem", () => { it("should create GroupTreeItem", () => { const commands: ButtonConfig[] = [ - { command: "cmd1", name: "Command 1" }, - { command: "cmd2", name: "Command 2" }, + { id: "cmd-1", command: "cmd1", name: "Command 1" }, + { id: "cmd-2", command: "cmd2", name: "Command 2" }, ]; const item = new GroupTreeItem("Group", commands); @@ -172,6 +177,7 @@ describe("ui-items", () => { describe("createTreeItem", () => { it("should create CommandTreeItem for regular button", () => { const button: ButtonConfig = { + id: "test-btn", command: "echo test", name: "Test", terminalName: "term1", @@ -191,7 +197,8 @@ describe("ui-items", () => { it("should create GroupTreeItem for group button", () => { const button: ButtonConfig = { - group: [{ command: "cmd1", name: "Command 1" }], + id: "group-btn", + group: [{ id: "cmd-1", command: "cmd1", name: "Command 1" }], name: "Group", }; @@ -206,6 +213,7 @@ describe("ui-items", () => { it("should include parent path in buttonId", () => { const button: ButtonConfig = { + id: "test-btn", command: "echo test", name: "Test", }; @@ -220,6 +228,7 @@ describe("ui-items", () => { it("should handle button without command", () => { const button: ButtonConfig = { + id: "empty-btn", name: "Empty", }; @@ -235,9 +244,9 @@ describe("ui-items", () => { describe("createTreeItems", () => { it("should create multiple tree items", () => { const buttons: ButtonConfig[] = [ - { command: "cmd1", name: "Command 1" }, - { group: [{ command: "sub", name: "Sub" }], name: "Group" }, - { command: "cmd2", name: "Command 2" }, + { id: "cmd-1", command: "cmd1", name: "Command 1" }, + { id: "group-1", group: [{ id: "sub-1", command: "sub", name: "Sub" }], name: "Group" }, + { id: "cmd-2", command: "cmd2", name: "Command 2" }, ]; const items = createTreeItems(buttons); @@ -249,7 +258,7 @@ describe("ui-items", () => { }); it("should pass parent path to each item", () => { - const buttons: ButtonConfig[] = [{ command: "cmd", name: "Test" }]; + const buttons: ButtonConfig[] = [{ id: "test-cmd", command: "cmd", name: "Test" }]; const items = createTreeItems(buttons, "Parent"); diff --git a/src/tests/utils-ensure-id.spec.ts b/src/tests/utils-ensure-id.spec.ts new file mode 100644 index 00000000..322a0269 --- /dev/null +++ b/src/tests/utils-ensure-id.spec.ts @@ -0,0 +1,217 @@ +import { ensureId, ensureIdsInArray } from "../internal/utils/ensure-id"; +import { ButtonConfig } from "../pkg/types"; + +describe("ensureId", () => { + describe("ensureId", () => { + it("should add ID to ButtonConfig without ID", () => { + const config = { + name: "Test Button", + command: "echo test", + } as ButtonConfig; + + const result = ensureId(config); + + expect(result.id).toBeDefined(); + expect(typeof result.id).toBe("string"); + expect(result.id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i); + }); + + it("should preserve existing ID", () => { + const existingId = "existing-id-123"; + const config: ButtonConfig = { + name: "Test Button", + command: "echo test", + id: existingId, + }; + + const result = ensureId(config); + + expect(result.id).toBe(existingId); + }); + + it("should preserve all other properties", () => { + const config = { + name: "Test Button", + command: "echo test", + color: "#FF0000", + shortcut: "ctrl+t", + terminalName: "TestTerminal", + useVsCodeApi: true, + executeAll: false, + } as ButtonConfig; + + const result = ensureId(config); + + expect(result.name).toBe(config.name); + expect(result.command).toBe(config.command); + expect(result.color).toBe(config.color); + expect(result.shortcut).toBe(config.shortcut); + expect(result.terminalName).toBe(config.terminalName); + expect(result.useVsCodeApi).toBe(config.useVsCodeApi); + expect(result.executeAll).toBe(config.executeAll); + }); + + it("should recursively add IDs to group items", () => { + const config = { + name: "Parent Group", + group: [ + { name: "Child 1", command: "echo 1" }, + { name: "Child 2", command: "echo 2" }, + ], + } as ButtonConfig; + + const result = ensureId(config); + + expect(result.id).toBeDefined(); + expect(result.group).toBeDefined(); + expect(result.group![0].id).toBeDefined(); + expect(result.group![1].id).toBeDefined(); + expect(result.group![0].id).not.toBe(result.group![1].id); + }); + + it("should handle deeply nested groups", () => { + const config = { + name: "Level 1", + group: [ + { + name: "Level 2", + group: [ + { + name: "Level 3", + command: "echo deep", + }, + ], + }, + ], + } as ButtonConfig; + + const result = ensureId(config); + + expect(result.id).toBeDefined(); + expect(result.group![0].id).toBeDefined(); + expect(result.group![0].group![0].id).toBeDefined(); + }); + + it("should preserve existing IDs in nested groups", () => { + const parentId = "parent-id"; + const childId = "child-id"; + const config: ButtonConfig = { + name: "Parent", + id: parentId, + group: [ + { + name: "Child", + id: childId, + command: "echo test", + }, + ], + }; + + const result = ensureId(config); + + expect(result.id).toBe(parentId); + expect(result.group![0].id).toBe(childId); + }); + + it("should handle empty group array", () => { + const config = { + name: "Empty Group", + group: [] as ButtonConfig[], + } as ButtonConfig; + + const result = ensureId(config); + + expect(result.id).toBeDefined(); + expect(result.group).toEqual([]); + }); + + it("should generate unique IDs for multiple calls", () => { + const config = { + name: "Test Button", + command: "echo test", + } as ButtonConfig; + + const result1 = ensureId(config); + const result2 = ensureId(config); + + expect(result1.id).toBeDefined(); + expect(result2.id).toBeDefined(); + expect(result1.id).not.toBe(result2.id); + }); + }); + + describe("ensureIdsInArray", () => { + it("should add IDs to all configs in array", () => { + const configs = [ + { name: "Button 1", command: "echo 1" }, + { name: "Button 2", command: "echo 2" }, + { name: "Button 3", command: "echo 3" }, + ] as ButtonConfig[]; + + const result = ensureIdsInArray(configs); + + expect(result).toHaveLength(3); + expect(result[0].id).toBeDefined(); + expect(result[1].id).toBeDefined(); + expect(result[2].id).toBeDefined(); + expect(result[0].id).not.toBe(result[1].id); + expect(result[1].id).not.toBe(result[2].id); + }); + + it("should handle empty array", () => { + const configs: ButtonConfig[] = []; + + const result = ensureIdsInArray(configs); + + expect(result).toEqual([]); + }); + + it("should preserve existing IDs in array", () => { + const id1 = "existing-1"; + const id2 = "existing-2"; + const configs: ButtonConfig[] = [ + { name: "Button 1", command: "echo 1", id: id1 }, + { name: "Button 2", command: "echo 2", id: id2 }, + ]; + + const result = ensureIdsInArray(configs); + + expect(result[0].id).toBe(id1); + expect(result[1].id).toBe(id2); + }); + + it("should handle mixed configs with and without IDs", () => { + const existingId = "existing-id"; + const configs = [ + { name: "Button 1", command: "echo 1", id: existingId }, + { name: "Button 2", command: "echo 2" }, + ] as ButtonConfig[]; + + const result = ensureIdsInArray(configs); + + expect(result[0].id).toBe(existingId); + expect(result[1].id).toBeDefined(); + expect(result[1].id).not.toBe(existingId); + }); + + it("should recursively process nested groups in array", () => { + const configs = [ + { + name: "Group 1", + group: [{ name: "Child 1", command: "echo 1" }], + }, + { + name: "Group 2", + group: [{ name: "Child 2", command: "echo 2" }], + }, + ] as ButtonConfig[]; + + const result = ensureIdsInArray(configs); + + expect(result[0].id).toBeDefined(); + expect(result[0].group![0].id).toBeDefined(); + expect(result[1].id).toBeDefined(); + expect(result[1].group![0].id).toBeDefined(); + }); + }); +}); diff --git a/src/view/src/components/command-form.tsx b/src/view/src/components/command-form.tsx index 787a5fb1..4da6cdd0 100644 --- a/src/view/src/components/command-form.tsx +++ b/src/view/src/components/command-form.tsx @@ -19,6 +19,7 @@ export const CommandForm = ({ command, formId, onSave }: CommandFormProps) => { command: "", executeAll: false, group: [], + id: crypto.randomUUID(), name: "", shortcut: "", terminalName: "", @@ -48,6 +49,7 @@ export const CommandForm = ({ command, formId, onSave }: CommandFormProps) => { const saveCommand = () => { const commandConfig: ButtonConfig = { color: formData.color || undefined, + id: formData.id, name: formData.name.trim(), shortcut: formData.shortcut || undefined, }; diff --git a/src/view/src/components/command-list.tsx b/src/view/src/components/command-list.tsx index 70bd30dc..f43749f7 100644 --- a/src/view/src/components/command-list.tsx +++ b/src/view/src/components/command-list.tsx @@ -2,12 +2,12 @@ import { GripVertical } from "lucide-react"; import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from "~/core"; +import { DeleteConfirmationDialog } from "./delete-confirmation-dialog"; import { useCommandForm } from "../context/command-form-context.tsx"; import { useVscodeCommand } from "../context/vscode-command-context.tsx"; import { useSortableItem } from "../hooks/use-sortable-item"; import { useSortableList } from "../hooks/use-sortable-list"; import { type ButtonConfig } from "../types"; -import { DeleteConfirmationDialog } from "./delete-confirmation-dialog"; export const CommandList = () => { const { commands, reorderCommands } = useVscodeCommand(); @@ -31,7 +31,7 @@ export const CommandList = () => {
{commands.map((command, index) => ( - + ))}
diff --git a/src/view/src/components/group-command-list.tsx b/src/view/src/components/group-command-list.tsx index d9479e03..0cd803b0 100644 --- a/src/view/src/components/group-command-list.tsx +++ b/src/view/src/components/group-command-list.tsx @@ -57,17 +57,17 @@ export const GroupCommandList = ({ const sensors = useMemo(() => [pointerSensor, keyboardSensor], [pointerSensor, keyboardSensor]); - const sortableItemIds = useMemo(() => commands.map((_, index) => `${index}`), [commands]); + const sortableItemIds = useMemo(() => commands.map((command) => command.id), [commands]); const handleDragEnd = useCallback( (event: DragEndEvent) => { const { active, over } = event; if (over && active.id !== over.id) { - const oldIndex = Number(active.id); - const newIndex = Number(over.id); + const oldIndex = commands.findIndex((cmd) => cmd.id === active.id); + const newIndex = commands.findIndex((cmd) => cmd.id === over.id); - if (!isNaN(oldIndex) && !isNaN(newIndex)) { + if (oldIndex !== -1 && newIndex !== -1) { const newItems = arrayMove(commands, oldIndex, newIndex); onChange(newItems); } @@ -90,9 +90,9 @@ export const GroupCommandList = ({ {commands.map((command, index) => ( onEditGroup(index) : undefined} onUpdate={updateCommand} diff --git a/src/view/src/hooks/use-command-operations.tsx b/src/view/src/hooks/use-command-operations.tsx index ea7ee37b..de7d6640 100644 --- a/src/view/src/hooks/use-command-operations.tsx +++ b/src/view/src/hooks/use-command-operations.tsx @@ -26,6 +26,7 @@ export const useCommandOperations = ( const addCommand = useCallback(() => { const newCommand: ButtonConfig = { command: "", + id: crypto.randomUUID(), name: "", useVsCodeApi: false, }; @@ -36,6 +37,7 @@ export const useCommandOperations = ( const newGroup: ButtonConfig = { executeAll: false, group: [], + id: crypto.randomUUID(), name: "", }; onChange([...commands, newGroup]); diff --git a/src/view/src/hooks/use-sortable-list.tsx b/src/view/src/hooks/use-sortable-list.tsx index bce4f8f6..05f34f6d 100644 --- a/src/view/src/hooks/use-sortable-list.tsx +++ b/src/view/src/hooks/use-sortable-list.tsx @@ -39,8 +39,8 @@ export const useSortableList = ({ items, onReorder }: UseSortableListProps) => { const { active, over } = event; if (over && active.id !== over.id) { - const oldIndex = Number(active.id); - const newIndex = Number(over.id); + const oldIndex = items.findIndex((item) => item.id === active.id); + const newIndex = items.findIndex((item) => item.id === over.id); if (oldIndex !== -1 && newIndex !== -1) { const newItems = arrayMove(items, oldIndex, newIndex); @@ -53,10 +53,7 @@ export const useSortableList = ({ items, onReorder }: UseSortableListProps) => { const SortableWrapper = ({ children }: { children: React.ReactNode }) => ( - `${index}`)} - strategy={verticalListSortingStrategy} - > + item.id)} strategy={verticalListSortingStrategy}> {children} diff --git a/src/view/src/mock/mock-data.tsx b/src/view/src/mock/mock-data.tsx index 8b12a4e8..ebcbac3a 100644 --- a/src/view/src/mock/mock-data.tsx +++ b/src/view/src/mock/mock-data.tsx @@ -4,6 +4,7 @@ export const mockCommands: ButtonConfig[] = [ { color: "#4CAF50", command: "npm test", + id: "mock-test-command", name: "$(testing-passed-icon) Test", shortcut: "t", terminalName: "Test Runner", @@ -11,6 +12,7 @@ export const mockCommands: ButtonConfig[] = [ { color: "#00BCD4", command: "workbench.action.terminal.new", + id: "mock-terminal-command", name: "$(terminal) Terminal", shortcut: "n", useVsCodeApi: true, @@ -20,11 +22,13 @@ export const mockCommands: ButtonConfig[] = [ group: [ { command: "git pull", + id: "mock-git-pull", name: "$(arrow-down) Pull", shortcut: "l", }, { command: "git push", + id: "mock-git-push", name: "$(arrow-up) Push", shortcut: "p", }, @@ -32,24 +36,29 @@ export const mockCommands: ButtonConfig[] = [ group: [ { command: "git status", + id: "mock-git-status", name: "$(git-commit) Status", shortcut: "s", }, { command: "git diff", + id: "mock-git-diff", name: "$(diff) Diff", shortcut: "d", }, { command: "git log --oneline -5", + id: "mock-git-log", name: "$(history) Log", shortcut: "l", }, ], + id: "mock-git-check-status", name: "$(search) Check Status", shortcut: "c", }, ], + id: "mock-git-group", name: "$(git-branch) Git", shortcut: "g", },