Skip to content

Commit 6955cbe

Browse files
Fix error when running tests multiple times from the editor gutter (#1742)
1 parent a8ce0f5 commit 6955cbe

File tree

4 files changed

+130
-39
lines changed

4 files changed

+130
-39
lines changed

package.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -980,6 +980,16 @@
980980
}
981981
],
982982
"commandPalette": [
983+
{
984+
"command": "swift.runTestsMultipleTimes",
985+
"group": "testExtras",
986+
"when": "false"
987+
},
988+
{
989+
"command": "swift.runTestsUntilFailure",
990+
"group": "testExtras",
991+
"when": "false"
992+
},
983993
{
984994
"command": "swift.generateLaunchConfigurations",
985995
"when": "swift.hasExecutableProduct"

src/commands.ts

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import { resolveDependencies } from "./commands/dependencies/resolve";
3838
import { resetPackage } from "./commands/resetPackage";
3939
import { updateDependencies } from "./commands/dependencies/update";
4040
import { runPluginTask } from "./commands/runPluginTask";
41-
import { runTestMultipleTimes } from "./commands/testMultipleTimes";
41+
import { extractTestItemsAndCount, runTestMultipleTimes } from "./commands/testMultipleTimes";
4242
import { newSwiftFile } from "./commands/newFile";
4343
import { runAllTests } from "./commands/runAllTests";
4444
import { updateDependenciesViewList } from "./commands/dependencies/updateDepViewList";
@@ -282,43 +282,6 @@ export function register(ctx: WorkspaceContext): vscode.Disposable[] {
282282
];
283283
}
284284

285-
/**
286-
* Extracts an array of vscode.TestItem and count from the provided varargs. Effectively, this
287-
* converts a varargs function from accepting both numbers and test items to:
288-
*
289-
* function (...testItems: vscode.TestItem[], count?: number): void;
290-
*
291-
* The VS Code testing view sends test items via varargs, but we have a couple testing commands that
292-
* also accept a final count parameter. We have to find the count parameter ourselves since JavaScript
293-
* only supports varargs at the end of an argument list.
294-
*/
295-
function extractTestItemsAndCount(...args: (vscode.TestItem | number)[]): {
296-
testItems: vscode.TestItem[];
297-
count?: number;
298-
} {
299-
const result = args.reduceRight<{
300-
testItems: vscode.TestItem[];
301-
count?: number;
302-
}>(
303-
(result, arg, index) => {
304-
if (typeof arg === "number" && index === args.length - 1) {
305-
result.count = arg;
306-
return result;
307-
} else if (typeof arg === "object") {
308-
result.testItems.push(arg);
309-
return result;
310-
} else {
311-
throw new Error(`Unexpected argument ${arg} at index ${index}`);
312-
}
313-
},
314-
{ testItems: [] }
315-
);
316-
if (result.testItems.length === 0) {
317-
throw new Error("At least one TestItem must be provided");
318-
}
319-
return result;
320-
}
321-
322285
/**
323286
* Certain commands can be called via a vscode TreeView, which will pass a {@link CommandNode} object.
324287
* If the command is called via a command palette or other means, the target will be a string.

src/commands/testMultipleTimes.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ export async function runTestMultipleTimes(
3434
let numExecutions = count;
3535
if (numExecutions === undefined) {
3636
const str = await vscode.window.showInputBox({
37-
prompt: "Label: ",
3837
placeHolder: `${untilFailure ? "Maximum " : ""}# of times to run`,
3938
validateInput: value =>
4039
/^[1-9]\d*$/.test(value) ? undefined : "Enter an integer value",
@@ -86,3 +85,41 @@ export async function runTestMultipleTimes(
8685

8786
return runStates;
8887
}
88+
89+
/**
90+
* Extracts an array of vscode.TestItem and count from the provided varargs. Effectively, this
91+
* converts a varargs function from accepting both numbers and test items to:
92+
*
93+
* function (...testItems: vscode.TestItem[], count?: number): void;
94+
*
95+
* The VS Code testing view sends test items via varargs, but we have a couple testing commands that
96+
* also accept a final count parameter. We have to find the count parameter ourselves since JavaScript
97+
* only supports varargs at the end of an argument list.
98+
*/
99+
export function extractTestItemsAndCount(
100+
...args: (vscode.TestItem | number | undefined | null)[]
101+
): {
102+
testItems: vscode.TestItem[];
103+
count?: number;
104+
} {
105+
const result = args.reduce<{
106+
testItems: vscode.TestItem[];
107+
count?: number;
108+
}>(
109+
(result, arg, index) => {
110+
if (arg === undefined || arg === null) {
111+
return result;
112+
} else if (typeof arg === "number" && index === args.length - 1) {
113+
result.count = arg ?? undefined;
114+
return result;
115+
} else if (typeof arg === "object") {
116+
result.testItems.push(arg);
117+
return result;
118+
} else {
119+
throw new Error(`Unexpected argument ${arg} at index ${index}`);
120+
}
121+
},
122+
{ testItems: [] }
123+
);
124+
return result;
125+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the VS Code Swift open source project
4+
//
5+
// Copyright (c) 2025 the VS Code Swift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import * as vscode from "vscode";
16+
import { expect } from "chai";
17+
import { extractTestItemsAndCount } from "../../../src/commands/testMultipleTimes";
18+
19+
suite("Run Tests Multiple Times", () => {
20+
suite("extractTestItemsAndCount()", () => {
21+
function createDummyTestItem(label: string): vscode.TestItem {
22+
return { label } as vscode.TestItem;
23+
}
24+
25+
test("handles empty arguments", () => {
26+
const { testItems, count } = extractTestItemsAndCount();
27+
expect(testItems).to.deep.equal([]);
28+
expect(count).to.be.undefined;
29+
});
30+
31+
test("handles test items with no count", () => {
32+
const testItem1 = createDummyTestItem("Test Item 1");
33+
const testItem2 = createDummyTestItem("Test Item 2");
34+
const testItem3 = createDummyTestItem("Test Item 3");
35+
36+
const { testItems, count } = extractTestItemsAndCount(testItem1, testItem2, testItem3);
37+
expect(testItems).to.deep.equal([testItem1, testItem2, testItem3]);
38+
expect(count).to.be.undefined;
39+
});
40+
41+
test("handles test items with count", () => {
42+
const testItem1 = createDummyTestItem("Test Item 1");
43+
const testItem2 = createDummyTestItem("Test Item 2");
44+
const testItem3 = createDummyTestItem("Test Item 3");
45+
46+
const { testItems, count } = extractTestItemsAndCount(
47+
testItem1,
48+
testItem2,
49+
testItem3,
50+
17
51+
);
52+
expect(testItems).to.deep.equal([testItem1, testItem2, testItem3]);
53+
expect(count).to.equal(17);
54+
});
55+
56+
test("ignores undefined or null arguments", () => {
57+
const testItem1 = createDummyTestItem("Test Item 1");
58+
const testItem2 = createDummyTestItem("Test Item 2");
59+
const testItem3 = createDummyTestItem("Test Item 3");
60+
61+
const { testItems, count } = extractTestItemsAndCount(
62+
testItem1,
63+
null,
64+
testItem2,
65+
testItem3,
66+
undefined
67+
);
68+
expect(testItems).to.deep.equal([testItem1, testItem2, testItem3]);
69+
expect(count).to.be.undefined;
70+
});
71+
72+
test("throws an error if the count is not the last argument", () => {
73+
const testItem1 = createDummyTestItem("Test Item 1");
74+
const testItem2 = createDummyTestItem("Test Item 2");
75+
76+
expect(() => extractTestItemsAndCount(testItem1, 17, testItem2)).to.throw(
77+
"Unexpected argument 17 at index 1"
78+
);
79+
});
80+
});
81+
});

0 commit comments

Comments
 (0)