import { describe, it, expect } from "vitest"; import { parseCommandInput, executeCommandParser } from "./command-parser"; /** * Regression tests for parseCommandInput * * These tests document the current behavior to ensure we don't break * existing command parsing when we add global flag support. */ describe("parseCommandInput - regression tests", () => { describe("basic commands", () => { it("should parse simple command with no args", () => { const result = parseCommandInput("help"); expect(result.commandName).toBe("help"); expect(result.args).toEqual([]); expect(result.command).toBeDefined(); }); it("should parse command with single arg", () => { const result = parseCommandInput("nip 01"); expect(result.commandName).toBe("nip"); expect(result.args).toEqual(["01"]); }); it("should parse command with multiple args", () => { const result = parseCommandInput("profile alice@domain.com"); expect(result.commandName).toBe("profile"); expect(result.args).toEqual(["alice@domain.com"]); }); }); describe("commands with flags", () => { it("should preserve req command with flags", () => { const result = parseCommandInput("req -k 1 -a alice"); expect(result.commandName).toBe("req"); expect(result.args).toEqual(["-k", "1", "-a", "alice"]); }); it("should preserve comma-separated values", () => { const result = parseCommandInput("req -k 1,3,7 -l 50"); expect(result.commandName).toBe("req"); expect(result.args).toEqual(["-k", "1,3,7", "-l", "50"]); }); it("should handle long flag names", () => { const result = parseCommandInput("req --kind 1 --limit 20"); expect(result.commandName).toBe("req"); expect(result.args).toEqual(["--kind", "1", "--limit", "20"]); }); it("should handle mixed short and long flags", () => { const result = parseCommandInput("req -k 1 --author alice -l 50"); expect(result.commandName).toBe("req"); expect(result.args).toEqual(["-k", "1", "--author", "alice", "-l", "50"]); }); }); describe("commands with complex identifiers", () => { it("should handle hex pubkey", () => { const hexKey = "82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2"; const result = parseCommandInput(`profile ${hexKey}`); expect(result.commandName).toBe("profile"); expect(result.args).toEqual([hexKey]); }); it("should handle npub", () => { const npub = "npub1abc123def456"; const result = parseCommandInput(`profile ${npub}`); expect(result.commandName).toBe("profile"); expect(result.args).toEqual([npub]); }); it("should handle nip05 identifier", () => { const result = parseCommandInput("profile alice@nostr.com"); expect(result.commandName).toBe("profile"); expect(result.args).toEqual(["alice@nostr.com"]); }); it("should handle relay URL", () => { const result = parseCommandInput("relay wss://relay.damus.io"); expect(result.commandName).toBe("relay"); expect(result.args).toEqual(["wss://relay.damus.io"]); }); }); describe("special arguments", () => { it("should handle $me alias", () => { const result = parseCommandInput("req -k 1 -a $me"); expect(result.commandName).toBe("req"); expect(result.args).toEqual(["-k", "1", "-a", "$me"]); }); it("should handle $contacts alias", () => { const result = parseCommandInput("req -k 1 -a $contacts"); expect(result.commandName).toBe("req"); expect(result.args).toEqual(["-k", "1", "-a", "$contacts"]); }); }); describe("whitespace handling", () => { it("should trim leading whitespace", () => { const result = parseCommandInput(" profile alice"); expect(result.commandName).toBe("profile"); expect(result.args).toEqual(["alice"]); }); it("should trim trailing whitespace", () => { const result = parseCommandInput("profile alice "); expect(result.commandName).toBe("profile"); expect(result.args).toEqual(["alice"]); }); it("should collapse multiple spaces", () => { const result = parseCommandInput("req -k 1 -a alice"); expect(result.commandName).toBe("req"); expect(result.args).toEqual(["-k", "1", "-a", "alice"]); }); }); describe("error cases", () => { it("should handle empty input", () => { const result = parseCommandInput(""); expect(result.commandName).toBe(""); expect(result.error).toBe("No command provided"); }); it("should handle unknown command", () => { const result = parseCommandInput("unknowncommand"); expect(result.commandName).toBe("unknowncommand"); expect(result.error).toContain("Unknown command"); }); }); describe("case sensitivity", () => { it("should handle lowercase command", () => { const result = parseCommandInput("profile alice"); expect(result.commandName).toBe("profile"); }); it("should handle uppercase command (converted to lowercase)", () => { const result = parseCommandInput("PROFILE alice"); expect(result.commandName).toBe("profile"); }); it("should handle mixed case command", () => { const result = parseCommandInput("Profile alice"); expect(result.commandName).toBe("profile"); }); }); describe("real-world command examples", () => { it("req: get recent notes", () => { const result = parseCommandInput("req -k 1 -l 20"); expect(result.commandName).toBe("req"); expect(result.args).toEqual(["-k", "1", "-l", "20"]); }); it("req: get notes from specific author", () => { const result = parseCommandInput("req -k 1 -a npub1abc... -l 50"); expect(result.commandName).toBe("req"); expect(result.args).toEqual(["-k", "1", "-a", "npub1abc...", "-l", "50"]); }); it("req: complex filter", () => { const result = parseCommandInput( "req -k 1,3,7 -a alice@nostr.com -l 100 --since 24h", ); expect(result.commandName).toBe("req"); expect(result.args).toEqual([ "-k", "1,3,7", "-a", "alice@nostr.com", "-l", "100", "--since", "24h", ]); }); it("profile: by npub", () => { const result = parseCommandInput("profile npub1abc..."); expect(result.commandName).toBe("profile"); expect(result.args).toEqual(["npub1abc..."]); }); it("profile: by nip05", () => { const result = parseCommandInput("profile jack@cash.app"); expect(result.commandName).toBe("profile"); expect(result.args).toEqual(["jack@cash.app"]); }); it("nip: view specification", () => { const result = parseCommandInput("nip 19"); expect(result.commandName).toBe("nip"); expect(result.args).toEqual(["19"]); }); it("relay: view relay info", () => { const result = parseCommandInput("relay nos.lol"); expect(result.commandName).toBe("relay"); expect(result.args).toEqual(["nos.lol"]); }); }); describe("global flags - new functionality", () => { it("should extract --title flag", () => { const result = parseCommandInput('profile alice --title "My Window"'); expect(result.commandName).toBe("profile"); expect(result.args).toEqual(["alice"]); expect(result.globalFlags?.windowProps?.title).toBe("My Window"); }); it("should handle --title at start", () => { const result = parseCommandInput('--title "My Window" profile alice'); expect(result.commandName).toBe("profile"); expect(result.args).toEqual(["alice"]); expect(result.globalFlags?.windowProps?.title).toBe("My Window"); }); it("should handle --title in middle", () => { const result = parseCommandInput('req -k 1 --title "My Feed" -a alice'); expect(result.commandName).toBe("req"); expect(result.args).toEqual(["-k", "1", "-a", "alice"]); expect(result.globalFlags?.windowProps?.title).toBe("My Feed"); }); it("should handle --title with single quotes", () => { const result = parseCommandInput("profile alice --title 'My Window'"); expect(result.globalFlags?.windowProps?.title).toBe("My Window"); }); it("should handle --title without quotes (single word)", () => { const result = parseCommandInput("profile alice --title MyWindow"); expect(result.globalFlags?.windowProps?.title).toBe("MyWindow"); }); it("should preserve command behavior when no --title", () => { const result = parseCommandInput("req -k 1 -a alice"); expect(result.commandName).toBe("req"); expect(result.args).toEqual(["-k", "1", "-a", "alice"]); expect(result.globalFlags).toEqual({}); }); it("should error when --title has no value", () => { const result = parseCommandInput("profile alice --title"); expect(result.error).toContain("--title requires a value"); }); it("should handle emoji in --title", () => { const result = parseCommandInput('profile alice --title "👤 Alice"'); expect(result.globalFlags?.windowProps?.title).toBe("👤 Alice"); }); }); }); describe("executeCommandParser - alias resolution", () => { it("should resolve $me in profile command when activeAccountPubkey is provided", async () => { const input = "profile $me"; const parsed = parseCommandInput(input); const activeAccountPubkey = "82341f882b6eabcd2ba7f1ef90aad961cf074af15b9ef44a09f9d2a8fbfbe6a2"; const result = await executeCommandParser(parsed, activeAccountPubkey); expect(result.error).toBeUndefined(); expect(result.props.pubkey).toBe(activeAccountPubkey); }); it("should return $me literal in profile command when activeAccountPubkey is NOT provided", async () => { const input = "profile $me"; const parsed = parseCommandInput(input); const result = await executeCommandParser(parsed); expect(result.error).toBeUndefined(); expect(result.props.pubkey).toBe("$me"); }); });