From 2adf93b929e120442c4691b7c5a85ad946b56c62 Mon Sep 17 00:00:00 2001 From: Marcel Robitaille Date: Fri, 20 Sep 2024 22:39:38 +0200 Subject: [PATCH 1/3] Add tests for #96 --- src/lib/CommandHandler.test.ts | 21 +++++++++++ src/lib/CommandHandler.ts | 1 + src/test/testData/errors/.vscode/tasks.json | 28 ++++++++++++++ src/test/testData/errors/mockData.ts | 41 +++++++++++++++++++++ 4 files changed, 91 insertions(+) create mode 100644 src/test/testData/errors/.vscode/tasks.json create mode 100644 src/test/testData/errors/mockData.ts diff --git a/src/lib/CommandHandler.test.ts b/src/lib/CommandHandler.test.ts index 20bfb8e..f2ea117 100644 --- a/src/lib/CommandHandler.test.ts +++ b/src/lib/CommandHandler.test.ts @@ -202,3 +202,24 @@ test("commandArgs", async () => { }, ); }); + +describe("Errors", async () => { + test("It should trigger an error for an empty result", async () => { + const testDataPath = path.join(__dirname, "../test/testData/errors"); + + const tasksJson = await import(path.join(testDataPath, ".vscode/tasks.json")); + const mockData = (await import(path.join(testDataPath, "mockData.ts"))).default; + + mockVscode.setMockData(mockData); + const input = tasksJson.inputs[0].args; + const handler = new CommandHandler( + input, + new UserInputContext(), + mockExtensionContext as unknown as vscode.ExtensionContext, + child_process, + ); + + expect(() => handler.handle()).rejects.toThrowError( + "The command for input 'inputTest' returned empty result."); + }); +}); diff --git a/src/lib/CommandHandler.ts b/src/lib/CommandHandler.ts index b408603..3454e1d 100755 --- a/src/lib/CommandHandler.ts +++ b/src/lib/CommandHandler.ts @@ -162,6 +162,7 @@ export class CommandHandler { if (result.trim().length == 0) { throw new ShellCommandException(`The command for input '${this.input.id}' returned empty result.`); } + return result .split(this.EOL) .map((value: string) => { diff --git a/src/test/testData/errors/.vscode/tasks.json b/src/test/testData/errors/.vscode/tasks.json new file mode 100644 index 0000000..399e4e8 --- /dev/null +++ b/src/test/testData/errors/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "This should trigger an error", + "type": "shell", + "command": "echo ${input:inputTest}", + "problemMatcher": [] + } + ], + "inputs": [ + { + "id": "inputTest", + "type": "command", + "command": "shellCommand.execute", + "args": { + "command": "echo ''", + "cwd": "${workspaceFolder}", + "env": { + "WORKSPACE": "${workspaceFolder[0]}", + "FILE": "${file}", + "PROJECT": "${workspaceFolderBasename}" + } + } + } + ] +} + diff --git a/src/test/testData/errors/mockData.ts b/src/test/testData/errors/mockData.ts new file mode 100644 index 0000000..1dc6092 --- /dev/null +++ b/src/test/testData/errors/mockData.ts @@ -0,0 +1,41 @@ +export default { + "calls": { + "workspace.getConfiguration().inspect()": { + "[\"tasks\",null,\"inputs\"]": { + "key": "tasks.inputs", + "workspaceValue": [ + { + "id": "inputTest", + "type": "command", + "command": "shellCommand.execute", + "args": { + "command": "echo ''", + "cwd": "${workspaceFolder}", + "env": { + "WORKSPACE": "${workspaceFolder[0]}", + "FILE": "${file}", + "PROJECT": "${workspaceFolderBasename}" + } + } + } + ] + } + } + }, + "staticData": { + "window.activeTextEditor.document.fileName": `${__dirname}/.vscode/tasks.json`, + "workspace.workspaceFolders": [ + { + "uri": { + "$mid": 1, + "fsPath": `${__dirname}`, + "external": `file://${__dirname}}`, + "path": `${__dirname}`, + "scheme": "file" + }, + "name": "vscode-shell-command", + "index": 0 + } + ] + } +}; From cf84c8ee266f8e77063021d2715b1c42f222dbf8 Mon Sep 17 00:00:00 2001 From: Marcel Robitaille Date: Fri, 20 Sep 2024 23:04:28 +0200 Subject: [PATCH 2/3] Add tests for #97 --- src/lib/CommandHandler.test.ts | 32 ++++++++++++++++++++++++++++++++ src/lib/CommandHandler.ts | 12 ++++++------ src/mocks/vscode.ts | 11 +++++++++++ 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/src/lib/CommandHandler.test.ts b/src/lib/CommandHandler.test.ts index f2ea117..325bb45 100644 --- a/src/lib/CommandHandler.test.ts +++ b/src/lib/CommandHandler.test.ts @@ -223,3 +223,35 @@ describe("Errors", async () => { "The command for input 'inputTest' returned empty result."); }); }); + +describe("Argument parsing", () => { + test("Test defaults and that all boolean properties use parseBoolean", () => { + expect(CommandHandler.resolveBooleanArgs({ extraTestThing: 42 })) + .toStrictEqual({ + rememberPrevious: false, + useFirstResult: false, + useSingleResult: false, + extraTestThing: 42, + }); + }); + + test("parseBoolean", () => { + expect(CommandHandler.parseBoolean(undefined, true)).toBe(true); + expect(CommandHandler.parseBoolean(undefined, false)).toBe(false); + + expect(CommandHandler.parseBoolean(false, true)).toBe(false); + expect(CommandHandler.parseBoolean(true, false)).toBe(true); + + expect(CommandHandler.parseBoolean("false", true)).toBe(false); + expect(CommandHandler.parseBoolean("fALse", true)).toBe(false); + expect(CommandHandler.parseBoolean("true", false)).toBe(true); + expect(CommandHandler.parseBoolean("tRUe", false)).toBe(true); + + expect(mockVscode.window.getShowWarningMessageCalls().length).toBe(0); + + expect(CommandHandler.parseBoolean(42, true)).toBe(true); + expect(CommandHandler.parseBoolean(42, false)).toBe(false); + + expect(mockVscode.window.getShowWarningMessageCalls().length).toBe(2); + }); +}); diff --git a/src/lib/CommandHandler.ts b/src/lib/CommandHandler.ts index 3454e1d..ce753a0 100755 --- a/src/lib/CommandHandler.ts +++ b/src/lib/CommandHandler.ts @@ -21,7 +21,7 @@ export class CommandHandler { context: vscode.ExtensionContext, subprocess: typeof child_process, ) { - this.args = this.resolveBooleanArgs(args); + this.args = CommandHandler.resolveBooleanArgs(args); if (!Object.prototype.hasOwnProperty.call(this.args, "command")) { throw new ShellCommandException('Please specify the "command" property.'); } @@ -52,17 +52,17 @@ export class CommandHandler { this.subprocess = subprocess; } - protected resolveBooleanArgs(args: object): ShellCommandOptions { + static resolveBooleanArgs(args: object): ShellCommandOptions { const opt = args as ShellCommandOptions; const resolvedBooleans = { - useFirstResult: this.parseBoolean(opt.useFirstResult, false), - useSingleResult: this.parseBoolean(opt.useSingleResult, false), - rememberPrevious: this.parseBoolean(opt.rememberPrevious, false), + useFirstResult: CommandHandler.parseBoolean(opt.useFirstResult, false), + useSingleResult: CommandHandler.parseBoolean(opt.useSingleResult, false), + rememberPrevious: CommandHandler.parseBoolean(opt.rememberPrevious, false), }; return {...args, ...resolvedBooleans} as ShellCommandOptions; } - protected parseBoolean(value: unknown, defaultValue: boolean): boolean { + static parseBoolean(value: unknown, defaultValue: boolean): boolean { if (value === undefined) { return defaultValue; } diff --git a/src/mocks/vscode.ts b/src/mocks/vscode.ts index c997fd7..2815607 100644 --- a/src/mocks/vscode.ts +++ b/src/mocks/vscode.ts @@ -63,6 +63,9 @@ type MockData = { }; } +// This mock does not need to be saved +const showWarningMessageCalls: string[] = []; + export namespace window { export namespace activeTextEditor { export namespace document { @@ -81,6 +84,14 @@ export namespace window { } } } + + export function showWarningMessage(message: string) { + showWarningMessageCalls.push(message); + } + + export function getShowWarningMessageCalls() { + return showWarningMessageCalls; + } } export namespace workspace { From d6a9813be947369d2bb4f4a34a079a974c15f223 Mon Sep 17 00:00:00 2001 From: Marcel Robitaille Date: Fri, 20 Sep 2024 23:06:37 +0200 Subject: [PATCH 3/3] Update changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6141e7a..8a05e6b 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## [Unreleased] + +- Improve error handling +- Improve parsing of boolean arguments + ## [1.10.0] - 2024-04-06 - Support commands with spaces. Add `commandArgs` property, which causes `execFileSync` to be called with `command`.