diff --git a/package.json b/package.json index c856dc7fc..fb8cf50dc 100644 --- a/package.json +++ b/package.json @@ -974,6 +974,16 @@ } ], "commandPalette": [ + { + "command": "swift.runTestsMultipleTimes", + "group": "testExtras", + "when": "false" + }, + { + "command": "swift.runTestsUntilFailure", + "group": "testExtras", + "when": "false" + }, { "command": "swift.generateLaunchConfigurations", "when": "swift.hasExecutableProduct" diff --git a/src/commands.ts b/src/commands.ts index 772725020..385344bab 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -38,7 +38,7 @@ import { resolveDependencies } from "./commands/dependencies/resolve"; import { resetPackage } from "./commands/resetPackage"; import { updateDependencies } from "./commands/dependencies/update"; import { runPluginTask } from "./commands/runPluginTask"; -import { runTestMultipleTimes } from "./commands/testMultipleTimes"; +import { extractTestItemsAndCount, runTestMultipleTimes } from "./commands/testMultipleTimes"; import { newSwiftFile } from "./commands/newFile"; import { runAllTests } from "./commands/runAllTests"; import { updateDependenciesViewList } from "./commands/dependencies/updateDepViewList"; @@ -282,43 +282,6 @@ export function register(ctx: WorkspaceContext): vscode.Disposable[] { ]; } -/** - * Extracts an array of vscode.TestItem and count from the provided varargs. Effectively, this - * converts a varargs function from accepting both numbers and test items to: - * - * function (...testItems: vscode.TestItem[], count?: number): void; - * - * The VS Code testing view sends test items via varargs, but we have a couple testing commands that - * also accept a final count parameter. We have to find the count parameter ourselves since JavaScript - * only supports varargs at the end of an argument list. - */ -function extractTestItemsAndCount(...args: (vscode.TestItem | number)[]): { - testItems: vscode.TestItem[]; - count?: number; -} { - const result = args.reduceRight<{ - testItems: vscode.TestItem[]; - count?: number; - }>( - (result, arg, index) => { - if (typeof arg === "number" && index === args.length - 1) { - result.count = arg; - return result; - } else if (typeof arg === "object") { - result.testItems.push(arg); - return result; - } else { - throw new Error(`Unexpected argument ${arg} at index ${index}`); - } - }, - { testItems: [] } - ); - if (result.testItems.length === 0) { - throw new Error("At least one TestItem must be provided"); - } - return result; -} - /** * Certain commands can be called via a vscode TreeView, which will pass a {@link CommandNode} object. * If the command is called via a command palette or other means, the target will be a string. diff --git a/src/commands/testMultipleTimes.ts b/src/commands/testMultipleTimes.ts index 9151e8559..7cbae3f23 100644 --- a/src/commands/testMultipleTimes.ts +++ b/src/commands/testMultipleTimes.ts @@ -34,7 +34,6 @@ export async function runTestMultipleTimes( let numExecutions = count; if (numExecutions === undefined) { const str = await vscode.window.showInputBox({ - prompt: "Label: ", placeHolder: `${untilFailure ? "Maximum " : ""}# of times to run`, validateInput: value => /^[1-9]\d*$/.test(value) ? undefined : "Enter an integer value", @@ -86,3 +85,41 @@ export async function runTestMultipleTimes( return runStates; } + +/** + * Extracts an array of vscode.TestItem and count from the provided varargs. Effectively, this + * converts a varargs function from accepting both numbers and test items to: + * + * function (...testItems: vscode.TestItem[], count?: number): void; + * + * The VS Code testing view sends test items via varargs, but we have a couple testing commands that + * also accept a final count parameter. We have to find the count parameter ourselves since JavaScript + * only supports varargs at the end of an argument list. + */ +export function extractTestItemsAndCount( + ...args: (vscode.TestItem | number | undefined | null)[] +): { + testItems: vscode.TestItem[]; + count?: number; +} { + const result = args.reduce<{ + testItems: vscode.TestItem[]; + count?: number; + }>( + (result, arg, index) => { + if (arg === undefined || arg === null) { + return result; + } else if (typeof arg === "number" && index === args.length - 1) { + result.count = arg ?? undefined; + return result; + } else if (typeof arg === "object") { + result.testItems.push(arg); + return result; + } else { + throw new Error(`Unexpected argument ${arg} at index ${index}`); + } + }, + { testItems: [] } + ); + return result; +} diff --git a/test/unit-tests/commands/runTestMultipleTimes.test.ts b/test/unit-tests/commands/runTestMultipleTimes.test.ts new file mode 100644 index 000000000..217e96d9b --- /dev/null +++ b/test/unit-tests/commands/runTestMultipleTimes.test.ts @@ -0,0 +1,81 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the VS Code Swift open source project +// +// Copyright (c) 2025 the VS Code Swift project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of VS Code Swift project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +import * as vscode from "vscode"; +import { expect } from "chai"; +import { extractTestItemsAndCount } from "../../../src/commands/testMultipleTimes"; + +suite("Run Tests Multiple Times", () => { + suite("extractTestItemsAndCount()", () => { + function createDummyTestItem(label: string): vscode.TestItem { + return { label } as vscode.TestItem; + } + + test("handles empty arguments", () => { + const { testItems, count } = extractTestItemsAndCount(); + expect(testItems).to.deep.equal([]); + expect(count).to.be.undefined; + }); + + test("handles test items with no count", () => { + const testItem1 = createDummyTestItem("Test Item 1"); + const testItem2 = createDummyTestItem("Test Item 2"); + const testItem3 = createDummyTestItem("Test Item 3"); + + const { testItems, count } = extractTestItemsAndCount(testItem1, testItem2, testItem3); + expect(testItems).to.deep.equal([testItem1, testItem2, testItem3]); + expect(count).to.be.undefined; + }); + + test("handles test items with count", () => { + const testItem1 = createDummyTestItem("Test Item 1"); + const testItem2 = createDummyTestItem("Test Item 2"); + const testItem3 = createDummyTestItem("Test Item 3"); + + const { testItems, count } = extractTestItemsAndCount( + testItem1, + testItem2, + testItem3, + 17 + ); + expect(testItems).to.deep.equal([testItem1, testItem2, testItem3]); + expect(count).to.equal(17); + }); + + test("ignores undefined or null arguments", () => { + const testItem1 = createDummyTestItem("Test Item 1"); + const testItem2 = createDummyTestItem("Test Item 2"); + const testItem3 = createDummyTestItem("Test Item 3"); + + const { testItems, count } = extractTestItemsAndCount( + testItem1, + null, + testItem2, + testItem3, + undefined + ); + expect(testItems).to.deep.equal([testItem1, testItem2, testItem3]); + expect(count).to.be.undefined; + }); + + test("throws an error if the count is not the last argument", () => { + const testItem1 = createDummyTestItem("Test Item 1"); + const testItem2 = createDummyTestItem("Test Item 2"); + + expect(() => extractTestItemsAndCount(testItem1, 17, testItem2)).to.throw( + "Unexpected argument 17 at index 1" + ); + }); + }); +});