From f74a4febdd9477493d228cab26a2fdd49d735010 Mon Sep 17 00:00:00 2001 From: Shin'ya Ueoka Date: Sat, 20 Jul 2024 10:25:06 +0000 Subject: [PATCH] fix: command completion with a force command. (#485) The completed command line does not include a force suffix `!` even if the original command includes a force suffix `!`. For example, when user input a command as: ``` :bdelege! foo ``` and the Vimmatic completes a command by: ``` :bdelete https://foobar.local/ ``` This PR fix the issue on completion with a force command. --- src/background/usecases/CommandUseCase.ts | 2 +- .../usecases/CommandUseCase.test.ts | 150 ++++++++++++++++++ 2 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 test/background/usecases/CommandUseCase.test.ts diff --git a/src/background/usecases/CommandUseCase.ts b/src/background/usecases/CommandUseCase.ts index 47208299..73735c5c 100644 --- a/src/background/usecases/CommandUseCase.ts +++ b/src/background/usecases/CommandUseCase.ts @@ -70,7 +70,7 @@ export class CommandUseCase { // add original command to completed value completions.forEach((group) => { group.items.forEach((item) => { - item.value = name + " " + item.value; + item.value = `${name}${force ? "!" : ""} ${item.value}`; }); }); return completions; diff --git a/test/background/usecases/CommandUseCase.test.ts b/test/background/usecases/CommandUseCase.test.ts new file mode 100644 index 00000000..570f5974 --- /dev/null +++ b/test/background/usecases/CommandUseCase.test.ts @@ -0,0 +1,150 @@ +import { CommandUseCase } from "../../../src/background/usecases/CommandUseCase"; +import { CommandRegistryImpl } from "../../../src/background/command/CommandRegistry"; +import type { Command } from "../../../src/background/command/types"; +import { describe, beforeAll, test, vi, expect } from "vitest"; + +const commandA: Command = { + names: () => ["a"], + fullname: () => "a", + description: () => "", + getCompletions: () => { + throw new Error("not implemented"); + }, + exec: () => { + throw new Error("not implemented"); + }, +}; + +const commandB: Command = { + names: () => ["b"], + fullname: () => "b", + description: () => "", + getCompletions: () => { + throw new Error("not implemented"); + }, + exec: () => { + throw new Error("not implemented"); + }, +}; + +describe("CommandUseCase", () => { + let reg: CommandRegistryImpl; + + beforeAll(() => { + reg = new CommandRegistryImpl(); + reg.register(commandA); + reg.register(commandB); + }); + + const commandAGetCompletion = vi.spyOn(commandA, "getCompletions"); + const commandAExec = vi.spyOn(commandA, "exec"); + + describe("exec", () => { + test("executes a command", async () => { + const sut = new CommandUseCase(reg); + const ctx = { sender: { tab: { id: 1 }, frameId: 1 } as any }; + + commandAExec.mockResolvedValue(undefined); + + await sut.exec(ctx, "a 123"); + + expect(commandAExec.mock?.lastCall?.[1]).toBe(false); + expect(commandAExec.mock?.lastCall?.[2]).toBe("123"); + }); + + test("execute a command with force", async () => { + const sut = new CommandUseCase(reg); + const ctx = { sender: { tab: { id: 1 }, frameId: 1 } as any }; + + commandAExec.mockResolvedValue(undefined); + + await sut.exec(ctx, "a! 123"); + + expect(commandAExec.mock?.lastCall?.[1]).toBe(true); + expect(commandAExec.mock?.lastCall?.[2]).toBe("123"); + }); + + test("command not found", async () => { + const sut = new CommandUseCase(reg); + const ctx = { sender: { tab: { id: 1 }, frameId: 1 } as any }; + + await expect(() => sut.exec(ctx, "c")).rejects.toThrow( + "c command is not defined", + ); + }); + + test("empty command", async () => { + const sut = new CommandUseCase(reg); + const ctx = { sender: { tab: { id: 1 }, frameId: 1 } as any }; + + await sut.exec(ctx, ""); + // no error + }); + }); + + describe("getCompletions", () => { + test("returns completions of commands", async () => { + const sut = new CommandUseCase(reg); + + expect(await sut.getCompletions("")).toEqual([ + { + name: "Console Command", + items: [ + { primary: "a", secondary: "", value: "a" }, + { primary: "b", secondary: "", value: "b" }, + ], + }, + ]); + expect(await sut.getCompletions("a")).toEqual([ + { + name: "Console Command", + items: [{ primary: "a", secondary: "", value: "a" }], + }, + ]); + }); + + test("returns completions of command arguments", async () => { + commandAGetCompletion.mockResolvedValue([ + { + name: "Group", + items: [{ value: "item1" }, { value: "item2" }], + }, + ]); + + const sut = new CommandUseCase(reg); + + expect(await sut.getCompletions("a ")).toEqual([ + { + name: "Group", + items: [{ value: "a item1" }, { value: "a item2" }], + }, + ]); + }); + + test("returns completions of command arguments with force", async () => { + commandAGetCompletion.mockResolvedValue([ + { + name: "Group", + items: [{ value: "item1" }, { value: "item2" }], + }, + ]); + + const sut = new CommandUseCase(reg); + + expect(await sut.getCompletions("a! ")).toEqual([ + { + name: "Group", + items: [{ value: "a! item1" }, { value: "a! item2" }], + }, + ]); + }); + + test("command not found", async () => { + const sut = new CommandUseCase(reg); + + await expect(() => sut.getCompletions("c ")).rejects.toThrow( + "c command is not defined", + ); + }); + }); +});