diff --git a/src/TestExplorer/TestDiscovery.ts b/src/TestExplorer/TestDiscovery.ts index da202e206..306c18256 100644 --- a/src/TestExplorer/TestDiscovery.ts +++ b/src/TestExplorer/TestDiscovery.ts @@ -158,6 +158,27 @@ function deepMergeTestItemChildren(existingItem: vscode.TestItem, newItem: vscod ); } +/** + * Given a `TestClass` adds the TestClasses tags to each of its children. + * Does not apply recursively. + * @param testClass A test class whose tags should be propagated to its children. + * @returns A `TestClass` whose children include the parent's tags. + */ +function applyTagsToChildren(testClass: TestClass): TestClass { + return { + ...testClass, + children: testClass.children.reduce((children, child) => { + return [ + ...children, + { + ...child, + tags: [...child.tags, ...testClass.tags], + }, + ]; + }, [] as TestClass[]), + }; +} + /** * Updates the existing `vscode.TestItem` if it exists with the same ID as the `TestClass`, * otherwise creates an add a new one. The location on the returned vscode.TestItem is always updated. @@ -203,6 +224,12 @@ export function upsertTestItem( deepMergeTestItemChildren(existingItem, newItem); } + // In VS Code tags are not inherited automatically, so if we're recieving a suite we need + // to set a suites tag on all of its children. Because test items are added top down the children + // aren't updated recursively all at once, but rather one level at a time which then propagages + // parent tags down the tree as children are upserted. + testItem = applyTagsToChildren(testItem); + // Manually add the test style as a tag so we can filter by test type. newItem.tags = [{ id: testItem.style }, ...testItem.tags]; diff --git a/test/suite/testexplorer/TestDiscovery.test.ts b/test/suite/testexplorer/TestDiscovery.test.ts index bf0bbdd3f..3946a9f72 100644 --- a/test/suite/testexplorer/TestDiscovery.test.ts +++ b/test/suite/testexplorer/TestDiscovery.test.ts @@ -31,6 +31,7 @@ suite("TestDiscovery Suite", () => { interface SimplifiedTestItem { id: string; children: SimplifiedTestItem[]; + tags: readonly { id: string }[]; } function testControllerChildren(collection: vscode.TestItemCollection): SimplifiedTestItem[] { @@ -38,7 +39,11 @@ suite("TestDiscovery Suite", () => { collection, (acc, item) => [ ...acc, - { id: item.id, children: testControllerChildren(item.children) }, + { + id: item.id, + tags: [...item.tags.map(tag => ({ id: tag.id }))], + children: testControllerChildren(item.children), + }, ], [] as SimplifiedTestItem[] ); @@ -77,7 +82,7 @@ suite("TestDiscovery Suite", () => { updateTests(testController, [testItem("bar")]); assert.deepStrictEqual(testControllerChildren(testController.items), [ - { id: "bar", children: [] }, + { id: "bar", tags: [{ id: "XCTest" }, { id: "runnable" }], children: [] }, ]); }); @@ -109,9 +114,10 @@ suite("TestDiscovery Suite", () => { assert.deepStrictEqual(testControllerChildren(testController.items), [ { id: "foo", + tags: [{ id: "XCTest" }, { id: "runnable" }], children: [ - { id: "baz", children: [] }, - { id: "bar", children: [] }, + { id: "baz", tags: [{ id: "XCTest" }, { id: "runnable" }], children: [] }, + { id: "bar", tags: [{ id: "XCTest" }, { id: "runnable" }], children: [] }, ], }, ]); @@ -140,7 +146,13 @@ suite("TestDiscovery Suite", () => { updateTests(testController, [newFoo]); assert.deepStrictEqual(testControllerChildren(testController.items), [ - { id: "foo", children: [{ id: "bar", children: [] }] }, + { + id: "foo", + tags: [{ id: "XCTest" }, { id: "runnable" }], + children: [ + { id: "bar", tags: [{ id: "XCTest" }, { id: "runnable" }], children: [] }, + ], + }, ]); assert.deepStrictEqual(testController.items.get("foo")?.uri, newLocation.uri); assert.deepStrictEqual(testController.items.get("foo")?.label, "New Label"); @@ -168,7 +180,57 @@ suite("TestDiscovery Suite", () => { updateTestsFromClasses(testController, swiftPackage, [item]); assert.deepStrictEqual(testControllerChildren(testController.items), [ - { id: "TestTarget", children: [{ id: "bar", children: [] }] }, + { + id: "TestTarget", + tags: [{ id: "test-target" }, { id: "runnable" }], + children: [ + { id: "bar", tags: [{ id: "XCTest" }, { id: "runnable" }], children: [] }, + ], + }, + ]); + }); + + test("Children in suites with tags inherit the suite's tags", async () => { + const testSuite = testItem("suite"); + testSuite.tags = [{ id: "rootTag" }]; + const childSuite = testItem("childSuite"); + childSuite.tags = [{ id: "childSuiteTag" }]; + const childTest = testItem("childTest"); + childTest.tags = [{ id: "childTestTag" }]; + childSuite.children = [childTest]; + testSuite.children = [childSuite]; + + updateTests(testController, [testSuite]); + + assert.deepEqual(testControllerChildren(testController.items), [ + { + id: "suite", + tags: [{ id: "XCTest" }, { id: "rootTag" }, { id: "runnable" }], + children: [ + { + id: "childSuite", + tags: [ + { id: "XCTest" }, + { id: "childSuiteTag" }, + { id: "rootTag" }, + { id: "runnable" }, + ], + children: [ + { + id: "childTest", + children: [], + tags: [ + { id: "XCTest" }, + { id: "childTestTag" }, + { id: "childSuiteTag" }, + { id: "rootTag" }, + { id: "runnable" }, + ], + }, + ], + }, + ], + }, ]); }); });