Skip to content

Fix discovery of only the last test in equally named suites #66

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions src/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,20 +200,31 @@ export class Controller {
const add = (
parent: vscode.TestItem,
node: IParsedNode,
id: string,
start: vscode.Location,
end: vscode.Location,
): vscode.TestItem => {
let item = parent.children.get(node.name);
let item = parent.children.get(id);
if (!item) {
item = this.ctrl.createTestItem(node.name, node.name, start.uri);
item = this.ctrl.createTestItem(id, node.name, start.uri);
testMetadata.set(item, { type: ItemType.Test });
parent.children.add(item);
}
item.range = new vscode.Range(start.range.start, end.range.end);

const seen = new Map<string, vscode.TestItem>();
const level2Dupes = new Map<string, vscode.TestItem>();
for (const [, sibling] of parent.children) {
if (sibling.id == item.id || sibling.label !== node.name) {
continue;
}

for (const [, cousin] of sibling.children) {
level2Dupes.set(cousin.label, cousin);
}
}
for (const child of node.children) {
const existing = seen.get(child.name);
const existing = seen.get(child.name) || level2Dupes.get(child.name);
const start = sourceMap.originalPositionFor(
child.location.start.line,
child.location.start.column,
Expand All @@ -227,7 +238,7 @@ export class Controller {
continue;
}

seen.set(child.name, add(item, child, start, end));
seen.set(child.name, add(item, child, child.name, start, end));
}

for (const [id] of item.children) {
Expand All @@ -243,6 +254,7 @@ export class Controller {
// source file. This is probably a good assumption. Likewise we assume that a single
// a single describe/test is not split between different files.
const newTestsInFile = new Map<string, vscode.TestItem>();
let nId: number = 0;
for (const node of tree) {
const start = sourceMap.originalPositionFor(
node.location.start.line,
Expand All @@ -251,7 +263,14 @@ export class Controller {
const end = sourceMap.originalPositionFor(node.location.end.line, node.location.end.column);
const file = last(this.getContainingItemsForFile(start.uri, { compiledFile: uri }))!.item!;
diagnosticCollection.delete(start.uri);
newTestsInFile.set(node.name, add(file, node, start, end));
if (newTestsInFile.has(node.name) && ["describe", "suite"].includes(node.fn)) {
const id = `${node.name}#${nId++}`;
newTestsInFile.set(id, add(file, node, id, start, end));
}
else {
nId = 0;
newTestsInFile.set(node.name, add(file, node, node.name, start, end));
}
}

if (previous) {
Expand Down
44 changes: 39 additions & 5 deletions src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,33 @@ export class TestRunner implements vscode.Disposable {
const concurrency = this.concurrency.value || cpus().length;
const getTestByPath = (path: string[]): vscode.TestItem | undefined => {
const uri = vscode.Uri.parse(path[0]);
let item = last(getContainingItemsForFile(wf, ctrl, uri))!.item;
if (!item) {
let fileItem = last(getContainingItemsForFile(wf, ctrl, uri))!.item;
if (!fileItem) {
return undefined;
}

for (let i = 1; item && i < path.length; i++) {
item = item.children.get(path[i]);
let item: vscode.TestItem;
const searchPath = (test: vscode.TestItem, pathIndex: number) => {
let id = path[pathIndex];
let nId = 0;
let child;
do {
child = test.children.get(id);
if (child && path[pathIndex + 1] === undefined) {
item = child;
}
else {
child && searchPath(child, pathIndex + 1);
}

id = `${path[pathIndex]}#${nId++}`;
}
while (child)
}

return item;
searchPath(fileItem, 1);

return item!;
};

// inline source maps read from the runtime. These will both be definitive
Expand Down Expand Up @@ -422,6 +439,23 @@ export class TestRunner implements vscode.Disposable {
const modernNamePatterns = nodeMajorVersion >= 22;

const addTestsToFileRecord = (record: IIncludeFile, queue: vscode.TestItem[]) => {
//merge queue-items with the same (extended) id
for (let i = 0; i < queue.length; i++) {
let nId = 0;
for (let j = 0; j < queue.length; j++) {
if (i == j) continue;

if (`${queue[i].id}#${nId++}` === queue[j].id) {
for (const [, child] of queue[j].children) {
queue[i].children.add(child);
}
queue.splice(j, 1);
j--;
if (i > j) i--;
}
}
}

record.include ??= new Set();

while (queue.length) {
Expand Down
10 changes: 5 additions & 5 deletions src/test/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ export const getNodeVersion = () => {
type TestTreeExpectation = [string, TestTreeExpectation[]?];

const buildTreeExpectation = (entry: TestTreeExpectation, c: vscode.TestItemCollection) => {
for (const [id, { children }] of c) {
const node: TestTreeExpectation = [id];
for (const [, { children, label }] of c) {
const node: TestTreeExpectation = [label];
buildTreeExpectation(node, children);
if (entry.length === 1) {
entry[1] = [node];
Expand Down Expand Up @@ -112,9 +112,9 @@ export class FakeTestRun implements vscode.TestRun {
public expectStates(expected: { [test: string]: TestState[] }) {
const actual: { [test: string]: TestState[] } = {};
for (const { test, state } of this.states) {
let key = test.id;
let key = test.label;
for (let p = test.parent; p; p = p.parent) {
key = `${p.id}/${key}`;
key = `${p.label}/${key}`;
}
(actual[key] ??= []).push(state);
}
Expand Down Expand Up @@ -171,7 +171,7 @@ export class FakeTestRun implements vscode.TestRun {
end(): void {
this.ended = true;
}
addCoverage(fileCoverage: vscode.FileCoverage): void {}
addCoverage(fileCoverage: vscode.FileCoverage): void { }
onDidDispose = new vscode.EventEmitter<void>().event;
//#endregion
}
Expand Down
14 changes: 7 additions & 7 deletions testCases/simple/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ it("discovers tests", async () => {
const c = await getController();

await expectTestTree(c, [
["hello.test.js", [["math", [["addition"], ["subtraction"]]]]],
["hello.test.js", [["math", [["addition"]]], ["math", [["subtraction"]]]]],
["otherFolder", [["some.test.js", [["addition"]]]]],
["test", [["inAFolder.js", [["addition"]]]]],
["test-WithADash.js", [["addition"]]],
Expand Down Expand Up @@ -52,7 +52,7 @@ it("cleans up folder if all child files are deleted", () =>
await onChange;

await expectTestTree(c, [
["hello.test.js", [["math", [["addition"], ["subtraction"]]]]],
["hello.test.js", [["math", [["addition"]]], ["math", [["subtraction"]]]]],
["otherFolder", [["some.test.js", [["addition"]]]]],
["test-WithADash.js", [["addition"]]],
["test.js", [["addition"]]],
Expand Down Expand Up @@ -98,7 +98,7 @@ it("runs tests", async () => {
"test-WithADash.js/addition": ["started", "passed"],
"test.js/addition": ["started", "passed"],
"withADashTheOtherWay-test.js/addition": ["started", "failed"],
"hello.test.js/math": ["started", "passed"],
"hello.test.js/math": ["started", "passed", "started", "passed"],
"hello.test.js/math/addition": ["started", "passed"],
"hello.test.js/math/subtraction": ["started", "passed"],
"otherFolder/some.test.js/addition": ["started", "passed"],
Expand Down Expand Up @@ -126,7 +126,7 @@ it("runs tests in a file", async () => {
);

run.expectStates({
"hello.test.js/math": ["started", "passed"],
"hello.test.js/math": ["started", "passed", "started", "passed"],
"hello.test.js/math/addition": ["started", "passed"],
"hello.test.js/math/subtraction": ["started", "passed"],
"withADot.test.js/addition": ["started", "passed"],
Expand All @@ -145,7 +145,7 @@ it("runs subsets of tests", async () => {
const nodeVersion = getNodeVersion();
if (nodeVersion < 22) {
run.expectStates({
"hello.test.js/math": ["started", "passed"],
"hello.test.js/math": ["started", "passed", "started", "passed"],
"hello.test.js/math/addition": ["started", "passed"],
// did not work in earlier versions due to nodejs/node#51577
"hello.test.js/math/subtraction": ["started", "passed"],
Expand Down Expand Up @@ -279,7 +279,7 @@ it("handles test excludes", async () => {
c,
new vscode.TestRunRequest(
[c.ctrl.items.get("hello.test.js")!],
[c.ctrl.items.get("hello.test.js")!.children.get("math")!.children.get("subtraction")!],
[c.ctrl.items.get("hello.test.js")!.children.get("math#0")!.children.get("subtraction")!],
),
);

Expand Down Expand Up @@ -343,7 +343,7 @@ it("shows test output", async () => {
},
{
output: "another log",
location: new vscode.Location(uri, new vscode.Position(10, 20)),
location: new vscode.Location(uri, new vscode.Position(12, 20)),
test: undefined,
},
],
Expand Down
2 changes: 2 additions & 0 deletions testCases/simple/workspace/hello.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ describe("math", () => {
console.log("some log");
strictEqual(1 + 1, 2);
});
});

describe("math", () => {
it(`subtraction`, async () => {
process.stdout.write("another log");
strictEqual(1 - 1, 0);
Expand Down