From 175ed2994d131b6e4e12dab7d54866e7f470db17 Mon Sep 17 00:00:00 2001 From: "Michael (SPG) Weng" Date: Wed, 13 Nov 2024 14:00:04 -0500 Subject: [PATCH 1/5] Add integration tests for Inline/Expand Macro actions - Validate the workflow of user calling Inline/Expand Macro actions on a swift project with macro - Add test fixture for swift macro - Verify inline macro by asserting on inlined value after calling the action - Verify expand macro by asserting expanded macro document contain the right macro Issue: #1205 --- assets/test/swift-macro/Package.swift | 42 +++++ .../Sources/swift-macro/swift_macro.swift | 11 ++ .../Sources/swift-macroClient/main.swift | 8 + .../swift-macroMacros/swift_macroMacro.swift | 33 ++++ test/integration-tests/language/macro.test.ts | 158 ++++++++++++++++++ 5 files changed, 252 insertions(+) create mode 100644 assets/test/swift-macro/Package.swift create mode 100644 assets/test/swift-macro/Sources/swift-macro/swift_macro.swift create mode 100644 assets/test/swift-macro/Sources/swift-macroClient/main.swift create mode 100644 assets/test/swift-macro/Sources/swift-macroMacros/swift_macroMacro.swift create mode 100644 test/integration-tests/language/macro.test.ts diff --git a/assets/test/swift-macro/Package.swift b/assets/test/swift-macro/Package.swift new file mode 100644 index 000000000..24d3dbf6d --- /dev/null +++ b/assets/test/swift-macro/Package.swift @@ -0,0 +1,42 @@ +// swift-tools-version:5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription +import CompilerPluginSupport + +let package = Package( + name: "swift-macro", + platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6), .macCatalyst(.v13)], + products: [ + // Products define the executables and libraries a package produces, making them visible to other packages. + .library( + name: "swift-macro", + targets: ["swift-macro"] + ), + .executable( + name: "swift-macroClient", + targets: ["swift-macroClient"] + ), + ], + dependencies: [ + .package(url: "https://github.com/swiftlang/swift-syntax.git", from: "600.0.0-latest"), + ], + targets: [ + // Targets are the basic building blocks of a package, defining a module or a test suite. + // Targets can depend on other targets in this package and products from dependencies. + // Macro implementation that performs the source transformation of a macro. + .macro( + name: "swift-macroMacros", + dependencies: [ + .product(name: "SwiftSyntaxMacros", package: "swift-syntax"), + .product(name: "SwiftCompilerPlugin", package: "swift-syntax") + ] + ), + + // Library that exposes a macro as part of its API, which is used in client programs. + .target(name: "swift-macro", dependencies: ["swift-macroMacros"]), + + // A client of the library, which is able to use the macro in its own code. + .executableTarget(name: "swift-macroClient", dependencies: ["swift-macro"]), + ] +) diff --git a/assets/test/swift-macro/Sources/swift-macro/swift_macro.swift b/assets/test/swift-macro/Sources/swift-macro/swift_macro.swift new file mode 100644 index 000000000..0e0da12b8 --- /dev/null +++ b/assets/test/swift-macro/Sources/swift-macro/swift_macro.swift @@ -0,0 +1,11 @@ +// The Swift Programming Language +// https://docs.swift.org/swift-book + +/// A macro that produces both a value and a string containing the +/// source code that generated the value. For example, +/// +/// #stringify(x + y) +/// +/// produces a tuple `(x + y, "x + y")`. +@freestanding(expression) +public macro stringify(_ value: T) -> (T, String) = #externalMacro(module: "swift_macroMacros", type: "StringifyMacro") diff --git a/assets/test/swift-macro/Sources/swift-macroClient/main.swift b/assets/test/swift-macro/Sources/swift-macroClient/main.swift new file mode 100644 index 000000000..f80e10e00 --- /dev/null +++ b/assets/test/swift-macro/Sources/swift-macroClient/main.swift @@ -0,0 +1,8 @@ +import swift_macro + +let a = 17 +let b = 25 + +let (result, code) = #stringify(a + b) + +print("The value \(result) was produced by the code \"\(code)\"") diff --git a/assets/test/swift-macro/Sources/swift-macroMacros/swift_macroMacro.swift b/assets/test/swift-macro/Sources/swift-macroMacros/swift_macroMacro.swift new file mode 100644 index 000000000..5f242ce91 --- /dev/null +++ b/assets/test/swift-macro/Sources/swift-macroMacros/swift_macroMacro.swift @@ -0,0 +1,33 @@ +import SwiftCompilerPlugin +import SwiftSyntax +import SwiftSyntaxBuilder +import SwiftSyntaxMacros + +/// Implementation of the `stringify` macro, which takes an expression +/// of any type and produces a tuple containing the value of that expression +/// and the source code that produced the value. For example +/// +/// #stringify(x + y) +/// +/// will expand to +/// +/// (x + y, "x + y") +public struct StringifyMacro: ExpressionMacro { + public static func expansion( + of node: some FreestandingMacroExpansionSyntax, + in context: some MacroExpansionContext + ) -> ExprSyntax { + guard let argument = node.arguments.first?.expression else { + fatalError("compiler bug: the macro does not have any arguments") + } + + return "(\(argument), \(literal: argument.description))" + } +} + +@main +struct swift_macroPlugin: CompilerPlugin { + let providingMacros: [Macro.Type] = [ + StringifyMacro.self, + ] +} diff --git a/test/integration-tests/language/macro.test.ts b/test/integration-tests/language/macro.test.ts new file mode 100644 index 000000000..8808990c1 --- /dev/null +++ b/test/integration-tests/language/macro.test.ts @@ -0,0 +1,158 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the VS Code Swift open source project +// +// Copyright (c) 2024 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 * as langclient from "vscode-languageclient/node"; +import { expect } from "chai"; +import { LanguageClientManager } from "../../../src/sourcekit-lsp/LanguageClientManager"; +import { WorkspaceContext } from "../../../src/WorkspaceContext"; +import { folderContextPromise, globalWorkspaceContextPromise } from "../extension.test"; +import { testAssetUri } from "../../fixtures"; +import { FolderContext } from "../../../src/FolderContext"; +import { waitForEndTaskProcess, waitForNoRunningTasks } from "../../utilities"; +import { getBuildAllTask, SwiftTask } from "../../../src/tasks/SwiftTaskProvider"; +import { Version } from "../../../src/utilities/version"; + +async function waitForClientState( + languageClientManager: LanguageClientManager, + expectedState: langclient.State +): Promise { + let clientState = undefined; + while (clientState !== expectedState) { + clientState = await languageClientManager.useLanguageClient(async client => client.state); + console.warn("Language client is not ready yet. Retrying in 100 ms..."); + await new Promise(resolve => setTimeout(resolve, 100)); + } + return clientState; +} + +suite("Integration, Macros Functionality Support with Sourcekit-lsp", function () { + // Take around 60 seconds if running in isolation, longer than default timeout + this.timeout(2 * 60 * 1000); + + let clientManager: LanguageClientManager; + let workspaceContext: WorkspaceContext; + let folderContext: FolderContext; + + suiteSetup(async function () { + workspaceContext = await globalWorkspaceContextPromise; + + // Wait for a clean starting point, and build all tasks for the fixture + await waitForNoRunningTasks(); + folderContext = await folderContextPromise("swift-macro"); + await workspaceContext.focusFolder(folderContext); + const tasks = (await getBuildAllTask(folderContext)) as SwiftTask; + const exitPromise = waitForEndTaskProcess(tasks); + await vscode.tasks.executeTask(tasks); + const exitCode = await exitPromise; + expect(exitCode).to.equal(0); + + // Ensure lsp client is ready + clientManager = workspaceContext.languageClientManager; + const clientState = await waitForClientState(clientManager, langclient.State.Running); + expect(clientState).to.equals(langclient.State.Running); + }); + + test("Inline/Expand Macro", async function () { + // Focus on the file of interest + const uri = testAssetUri("swift-macro/Sources/swift-macroClient/main.swift"); + const editor = await vscode.window.showTextDocument(uri); + + // Beginning of macro, # + const position = new vscode.Position(5, 21); + + // Create a range starting and ending at the specified position + const range = new vscode.Range(position, position); + + // Execute the code action provider command + const codeActions = await vscode.commands.executeCommand( + "vscode.executeCodeActionProvider", + uri, + range + ); + + // Log and assert the code actions + expect(codeActions).to.be.an("array"); + // Expand Macro action requires Swift 6.10 + if (workspaceContext.swiftVersion.isGreaterThanOrEqual(new Version(6, 1, 0))) { + expect(codeActions.length).to.be.equal(2); + } else { + expect(codeActions.length).to.be.equal(1); + } + + const expectedMacro = '(a + b, "a + b")'; + // Loop through the code actions and execute them based on the command id + for (const action of codeActions) { + expect(action.command).is.not.undefined; + const command = action.command!; + // The id for the action is not clear, the title is "inline macro" + if (command.command === "semantic.refactor.command") { + // Run inline macro action + await vscode.commands.executeCommand(command.command, ...(command.arguments ?? [])); + + // Assert that the macro was inlined correctly + const endPosition = new vscode.Position(5, 37); + const inlineRange = new vscode.Range(position, endPosition); + const updatedText = editor.document.getText(inlineRange); + expect(updatedText).to.equal(expectedMacro); + + // Ensure we are refocusing on the text document for the undo step + await vscode.window.showTextDocument(uri); + + // We need to undo the inline macro or subsequent action will fail + await vscode.commands.executeCommand("undo"); + } else if (command.command === "expand.macro.command") { + // Set up a promise that resolves when the expected document is opened + const expandedMacroUriPromise = new Promise( + (resolve, reject) => { + const disposable = vscode.workspace.onDidOpenTextDocument( + openedDocument => { + if (openedDocument.uri.scheme === "sourcekit-lsp") { + disposable.dispose(); // Stop listening once we find the desired document + resolve(openedDocument); + } + } + ); + + // Set a timeout to reject the promise if the document is not found + setTimeout(() => { + disposable.dispose(); + reject( + new Error( + "Timed out waiting for sourcekit-lsp document to be opened." + ) + ); + }, 10000); // Wait up to 10 seconds for the document + } + ); + + // Run expand macro action + await vscode.commands.executeCommand(command.command, ...(command.arguments ?? [])); + + // Wait for the expanded macro document to be opened + const referenceDocument = await expandedMacroUriPromise; + + // Verify that the reference document was successfully opened + expect(referenceDocument).to.not.be.undefined; + + // Assert that the content contains the expected result + const content = referenceDocument.getText(); + expect(content).to.include(expectedMacro); + } else { + // New action got added, we need to add a new test case if this is hit. + expect.fail(); + } + } + }); +}); From a6dbb5970410f4e948c4df468400727b4ee2adcf Mon Sep 17 00:00:00 2001 From: "Michael (SPG) Weng" Date: Wed, 20 Nov 2024 13:55:15 -0500 Subject: [PATCH 2/5] Version check for the test based on feature support in Sourcekit-lsp --- test/integration-tests/language/macro.test.ts | 45 +++++++++++-------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/test/integration-tests/language/macro.test.ts b/test/integration-tests/language/macro.test.ts index 8808990c1..db3f64464 100644 --- a/test/integration-tests/language/macro.test.ts +++ b/test/integration-tests/language/macro.test.ts @@ -17,12 +17,12 @@ import * as langclient from "vscode-languageclient/node"; import { expect } from "chai"; import { LanguageClientManager } from "../../../src/sourcekit-lsp/LanguageClientManager"; import { WorkspaceContext } from "../../../src/WorkspaceContext"; -import { folderContextPromise, globalWorkspaceContextPromise } from "../extension.test"; import { testAssetUri } from "../../fixtures"; import { FolderContext } from "../../../src/FolderContext"; import { waitForEndTaskProcess, waitForNoRunningTasks } from "../../utilities"; import { getBuildAllTask, SwiftTask } from "../../../src/tasks/SwiftTaskProvider"; import { Version } from "../../../src/utilities/version"; +import { activateExtensionForSuite, folderInRootWorkspace } from "../utilities/testutilities"; async function waitForClientState( languageClientManager: LanguageClientManager, @@ -45,23 +45,29 @@ suite("Integration, Macros Functionality Support with Sourcekit-lsp", function ( let workspaceContext: WorkspaceContext; let folderContext: FolderContext; - suiteSetup(async function () { - workspaceContext = await globalWorkspaceContextPromise; - - // Wait for a clean starting point, and build all tasks for the fixture - await waitForNoRunningTasks(); - folderContext = await folderContextPromise("swift-macro"); - await workspaceContext.focusFolder(folderContext); - const tasks = (await getBuildAllTask(folderContext)) as SwiftTask; - const exitPromise = waitForEndTaskProcess(tasks); - await vscode.tasks.executeTask(tasks); - const exitCode = await exitPromise; - expect(exitCode).to.equal(0); - - // Ensure lsp client is ready - clientManager = workspaceContext.languageClientManager; - const clientState = await waitForClientState(clientManager, langclient.State.Running); - expect(clientState).to.equals(langclient.State.Running); + activateExtensionForSuite({ + async setup(ctx) { + workspaceContext = ctx; + // Macro support in Swift started from 5.9, related support in Sourcekit-lsp started from 5.10 + if (workspaceContext.swiftVersion.isLessThan(new Version(5, 10, 0))) { + this.skip(); + } + + // Wait for a clean starting point, and build all tasks for the fixture + await waitForNoRunningTasks(); + folderContext = await folderInRootWorkspace("defaultPackage", workspaceContext); + await workspaceContext.focusFolder(folderContext); + const tasks = (await getBuildAllTask(folderContext)) as SwiftTask; + const exitPromise = waitForEndTaskProcess(tasks); + await vscode.tasks.executeTask(tasks); + const exitCode = await exitPromise; + expect(exitCode).to.equal(0); + + // Ensure lsp client is ready + clientManager = workspaceContext.languageClientManager; + const clientState = await waitForClientState(clientManager, langclient.State.Running); + expect(clientState).to.equals(langclient.State.Running); + }, }); test("Inline/Expand Macro", async function () { @@ -84,7 +90,8 @@ suite("Integration, Macros Functionality Support with Sourcekit-lsp", function ( // Log and assert the code actions expect(codeActions).to.be.an("array"); - // Expand Macro action requires Swift 6.10 + // Expand Macro action requires Swift 6.1 + // Inline Macro action requires Swift 5.10, anything less than 5.10 is skipped in suite set up if (workspaceContext.swiftVersion.isGreaterThanOrEqual(new Version(6, 1, 0))) { expect(codeActions.length).to.be.equal(2); } else { From 40f16b297089d003dc9b37b881e9f21f9e5bb65b Mon Sep 17 00:00:00 2001 From: "Michael (SPG) Weng" Date: Wed, 20 Nov 2024 16:58:03 -0500 Subject: [PATCH 3/5] Fix wrong test fixture being used - Also add more explanation to the timeout set by the test suite --- test/integration-tests/language/macro.test.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/integration-tests/language/macro.test.ts b/test/integration-tests/language/macro.test.ts index db3f64464..60e7c6a8a 100644 --- a/test/integration-tests/language/macro.test.ts +++ b/test/integration-tests/language/macro.test.ts @@ -38,8 +38,9 @@ async function waitForClientState( } suite("Integration, Macros Functionality Support with Sourcekit-lsp", function () { - // Take around 60 seconds if running in isolation, longer than default timeout - this.timeout(2 * 60 * 1000); + // Take around 45 seconds if running in isolation, longer than default timeout + // But tests run a bit faster in CI when some artifacts are built by earlier test run + this.timeout(2 * 45 * 1000); let clientManager: LanguageClientManager; let workspaceContext: WorkspaceContext; @@ -55,7 +56,7 @@ suite("Integration, Macros Functionality Support with Sourcekit-lsp", function ( // Wait for a clean starting point, and build all tasks for the fixture await waitForNoRunningTasks(); - folderContext = await folderInRootWorkspace("defaultPackage", workspaceContext); + folderContext = await folderInRootWorkspace("swift-macro", workspaceContext); await workspaceContext.focusFolder(folderContext); const tasks = (await getBuildAllTask(folderContext)) as SwiftTask; const exitPromise = waitForEndTaskProcess(tasks); From f56f1660c8e360714de5b485202f0e5db545125b Mon Sep 17 00:00:00 2001 From: "Michael (SPG) Weng" Date: Thu, 21 Nov 2024 11:39:13 -0500 Subject: [PATCH 4/5] Add better assert messages and asserts --- test/integration-tests/language/macro.test.ts | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/test/integration-tests/language/macro.test.ts b/test/integration-tests/language/macro.test.ts index 60e7c6a8a..d0b76b15e 100644 --- a/test/integration-tests/language/macro.test.ts +++ b/test/integration-tests/language/macro.test.ts @@ -38,9 +38,8 @@ async function waitForClientState( } suite("Integration, Macros Functionality Support with Sourcekit-lsp", function () { - // Take around 45 seconds if running in isolation, longer than default timeout - // But tests run a bit faster in CI when some artifacts are built by earlier test run - this.timeout(2 * 45 * 1000); + // Take around 60 seconds if running in isolation, longer than default timeout + this.timeout(2 * 60 * 1000); let clientManager: LanguageClientManager; let workspaceContext: WorkspaceContext; @@ -93,21 +92,28 @@ suite("Integration, Macros Functionality Support with Sourcekit-lsp", function ( expect(codeActions).to.be.an("array"); // Expand Macro action requires Swift 6.1 // Inline Macro action requires Swift 5.10, anything less than 5.10 is skipped in suite set up + let expectedLength; if (workspaceContext.swiftVersion.isGreaterThanOrEqual(new Version(6, 1, 0))) { - expect(codeActions.length).to.be.equal(2); + expectedLength = 2; } else { - expect(codeActions.length).to.be.equal(1); + expectedLength = 1; } + expect( + codeActions.length, + `Unexpected codeActions length. Received actions: ${JSON.stringify(codeActions, null, 2)}` + ).to.be.equal(expectedLength); const expectedMacro = '(a + b, "a + b")'; // Loop through the code actions and execute them based on the command id for (const action of codeActions) { expect(action.command).is.not.undefined; const command = action.command!; + expect(command.arguments).is.not.undefined; + const commandArgs = command.arguments!; // The id for the action is not clear, the title is "inline macro" if (command.command === "semantic.refactor.command") { // Run inline macro action - await vscode.commands.executeCommand(command.command, ...(command.arguments ?? [])); + await vscode.commands.executeCommand(command.command, ...commandArgs); // Assert that the macro was inlined correctly const endPosition = new vscode.Position(5, 37); @@ -146,7 +152,7 @@ suite("Integration, Macros Functionality Support with Sourcekit-lsp", function ( ); // Run expand macro action - await vscode.commands.executeCommand(command.command, ...(command.arguments ?? [])); + await vscode.commands.executeCommand(command.command, ...commandArgs); // Wait for the expanded macro document to be opened const referenceDocument = await expandedMacroUriPromise; From 5072ed54c1221a89eba28783cc24810a835e6694 Mon Sep 17 00:00:00 2001 From: "Michael (SPG) Weng" Date: Tue, 3 Dec 2024 17:12:17 -0500 Subject: [PATCH 5/5] Adjust to only test for expand macro, as this is the only request that needed custom support in the extension. --- test/integration-tests/language/macro.test.ts | 135 ++++++------------ 1 file changed, 47 insertions(+), 88 deletions(-) diff --git a/test/integration-tests/language/macro.test.ts b/test/integration-tests/language/macro.test.ts index d0b76b15e..ae2a47cbe 100644 --- a/test/integration-tests/language/macro.test.ts +++ b/test/integration-tests/language/macro.test.ts @@ -19,7 +19,7 @@ import { LanguageClientManager } from "../../../src/sourcekit-lsp/LanguageClient import { WorkspaceContext } from "../../../src/WorkspaceContext"; import { testAssetUri } from "../../fixtures"; import { FolderContext } from "../../../src/FolderContext"; -import { waitForEndTaskProcess, waitForNoRunningTasks } from "../../utilities"; +import { executeTaskAndWaitForResult, waitForNoRunningTasks } from "../../utilities"; import { getBuildAllTask, SwiftTask } from "../../../src/tasks/SwiftTaskProvider"; import { Version } from "../../../src/utilities/version"; import { activateExtensionForSuite, folderInRootWorkspace } from "../utilities/testutilities"; @@ -48,8 +48,8 @@ suite("Integration, Macros Functionality Support with Sourcekit-lsp", function ( activateExtensionForSuite({ async setup(ctx) { workspaceContext = ctx; - // Macro support in Swift started from 5.9, related support in Sourcekit-lsp started from 5.10 - if (workspaceContext.swiftVersion.isLessThan(new Version(5, 10, 0))) { + // Expand Macro support in Swift started from 6.1 + if (workspaceContext.swiftVersion.isLessThan(new Version(6, 1, 0))) { this.skip(); } @@ -58,10 +58,8 @@ suite("Integration, Macros Functionality Support with Sourcekit-lsp", function ( folderContext = await folderInRootWorkspace("swift-macro", workspaceContext); await workspaceContext.focusFolder(folderContext); const tasks = (await getBuildAllTask(folderContext)) as SwiftTask; - const exitPromise = waitForEndTaskProcess(tasks); - await vscode.tasks.executeTask(tasks); - const exitCode = await exitPromise; - expect(exitCode).to.equal(0); + const { exitCode, output } = await executeTaskAndWaitForResult(tasks); + expect(exitCode, `${output}`).to.equal(0); // Ensure lsp client is ready clientManager = workspaceContext.languageClientManager; @@ -70,10 +68,10 @@ suite("Integration, Macros Functionality Support with Sourcekit-lsp", function ( }, }); - test("Inline/Expand Macro", async function () { + test("Expand Macro", async function () { // Focus on the file of interest const uri = testAssetUri("swift-macro/Sources/swift-macroClient/main.swift"); - const editor = await vscode.window.showTextDocument(uri); + await vscode.window.showTextDocument(uri); // Beginning of macro, # const position = new vscode.Position(5, 21); @@ -88,85 +86,46 @@ suite("Integration, Macros Functionality Support with Sourcekit-lsp", function ( range ); - // Log and assert the code actions - expect(codeActions).to.be.an("array"); - // Expand Macro action requires Swift 6.1 - // Inline Macro action requires Swift 5.10, anything less than 5.10 is skipped in suite set up - let expectedLength; - if (workspaceContext.swiftVersion.isGreaterThanOrEqual(new Version(6, 1, 0))) { - expectedLength = 2; - } else { - expectedLength = 1; - } - expect( - codeActions.length, - `Unexpected codeActions length. Received actions: ${JSON.stringify(codeActions, null, 2)}` - ).to.be.equal(expectedLength); - const expectedMacro = '(a + b, "a + b")'; - // Loop through the code actions and execute them based on the command id - for (const action of codeActions) { - expect(action.command).is.not.undefined; - const command = action.command!; - expect(command.arguments).is.not.undefined; - const commandArgs = command.arguments!; - // The id for the action is not clear, the title is "inline macro" - if (command.command === "semantic.refactor.command") { - // Run inline macro action - await vscode.commands.executeCommand(command.command, ...commandArgs); - - // Assert that the macro was inlined correctly - const endPosition = new vscode.Position(5, 37); - const inlineRange = new vscode.Range(position, endPosition); - const updatedText = editor.document.getText(inlineRange); - expect(updatedText).to.equal(expectedMacro); - - // Ensure we are refocusing on the text document for the undo step - await vscode.window.showTextDocument(uri); - - // We need to undo the inline macro or subsequent action will fail - await vscode.commands.executeCommand("undo"); - } else if (command.command === "expand.macro.command") { - // Set up a promise that resolves when the expected document is opened - const expandedMacroUriPromise = new Promise( - (resolve, reject) => { - const disposable = vscode.workspace.onDidOpenTextDocument( - openedDocument => { - if (openedDocument.uri.scheme === "sourcekit-lsp") { - disposable.dispose(); // Stop listening once we find the desired document - resolve(openedDocument); - } - } - ); - - // Set a timeout to reject the promise if the document is not found - setTimeout(() => { - disposable.dispose(); - reject( - new Error( - "Timed out waiting for sourcekit-lsp document to be opened." - ) - ); - }, 10000); // Wait up to 10 seconds for the document - } - ); - - // Run expand macro action - await vscode.commands.executeCommand(command.command, ...commandArgs); - - // Wait for the expanded macro document to be opened - const referenceDocument = await expandedMacroUriPromise; - - // Verify that the reference document was successfully opened - expect(referenceDocument).to.not.be.undefined; - - // Assert that the content contains the expected result - const content = referenceDocument.getText(); - expect(content).to.include(expectedMacro); - } else { - // New action got added, we need to add a new test case if this is hit. - expect.fail(); - } - } + + // Find the "expand.macro.command" action + const expandMacroAction = codeActions.find( + action => action.command?.command === "expand.macro.command" + ); + + // Assert that the expand macro command is available + expect(expandMacroAction).is.not.undefined; + + // Set up a promise that resolves when the expected document is opened + const expandedMacroUriPromise = new Promise((resolve, reject) => { + const disposable = vscode.workspace.onDidOpenTextDocument(openedDocument => { + if (openedDocument.uri.scheme === "sourcekit-lsp") { + disposable.dispose(); // Stop listening once we find the desired document + resolve(openedDocument); + } + }); + + // Set a timeout to reject the promise if the document is not found + setTimeout(() => { + disposable.dispose(); + reject(new Error("Timed out waiting for sourcekit-lsp document to be opened.")); + }, 10000); // Wait up to 10 seconds for the document + }); + + // Run expand macro action + const command = expandMacroAction!.command!; + expect(command.arguments).is.not.undefined; + const commandArgs = command.arguments!; + await vscode.commands.executeCommand(command.command, ...commandArgs); + + // Wait for the expanded macro document to be opened + const referenceDocument = await expandedMacroUriPromise; + + // Verify that the reference document was successfully opened + expect(referenceDocument).to.not.be.undefined; + + // Assert that the content contains the expected result + const content = referenceDocument.getText(); + expect(content).to.include(expectedMacro); }); });