diff --git a/.vscode-test.js b/.vscode-test.js index 168a3f784..62e2e9887 100644 --- a/.vscode-test.js +++ b/.vscode-test.js @@ -74,7 +74,7 @@ module.exports = defineConfig({ ui: "tdd", color: true, timeout, - forbidOnly: isCIBuild, + forbidOnly: false, //isCIBuild, grep: isFastTestRun ? "@slow" : undefined, invert: isFastTestRun, slow: 10000, diff --git a/assets/test/.vscode/settings.json b/assets/test/.vscode/settings.json index db1acbde4..7d4372060 100644 --- a/assets/test/.vscode/settings.json +++ b/assets/test/.vscode/settings.json @@ -8,5 +8,6 @@ "-DTEST_ARGUMENT_SET_VIA_TEST_BUILD_ARGUMENTS_SETTING" ], "lldb.verboseLogging": true, - "swift.backgroundCompilation": false + "swift.backgroundCompilation": false, + "swift.sourcekit-lsp.backgroundIndexing": "off" } \ No newline at end of file diff --git a/test/integration-tests/DiagnosticsManager.test.ts b/test/integration-tests/DiagnosticsManager.test.ts index a0c920f6e..8db4ad42d 100644 --- a/test/integration-tests/DiagnosticsManager.test.ts +++ b/test/integration-tests/DiagnosticsManager.test.ts @@ -13,11 +13,12 @@ //===----------------------------------------------------------------------===// import * as assert from "assert"; +import * as langclient from "vscode-languageclient/node"; import * as vscode from "vscode"; import { SwiftToolchain } from "../../src/toolchain/toolchain"; import { executeTaskAndWaitForResult, waitForNoRunningTasks } from "../utilities/tasks"; import { WorkspaceContext } from "../../src/WorkspaceContext"; -import { testAssetWorkspaceFolder, testSwiftTask } from "../fixtures"; +import { testAssetUri, testAssetWorkspaceFolder, testSwiftTask } from "../fixtures"; import { createBuildAllTask } from "../../src/tasks/SwiftTaskProvider"; import { DiagnosticsManager } from "../../src/DiagnosticsManager"; import { FolderContext } from "../../src/FolderContext"; @@ -25,11 +26,13 @@ import { Version } from "../../src/utilities/version"; import { Workbench } from "../../src/utilities/commands"; import { activateExtensionForSuite, + buildProject, folderInRootWorkspace, updateSettings, } from "./utilities/testutilities"; import { DiagnosticStyle } from "../../src/configuration"; import { expect } from "chai"; +import { touchDocument, waitForClientState, waitForIndex } from "./utilities/lsputilities"; const isEqual = (d1: vscode.Diagnostic, d2: vscode.Diagnostic) => { return ( @@ -63,7 +66,7 @@ function assertWithoutDiagnostic(uri: vscode.Uri, expected: vscode.Diagnostic) { ); } -suite("DiagnosticsManager Test Suite", function () { +suite.only("DiagnosticsManager Test Suite", function () { // Was hitting a timeout in suiteSetup during CI build once in a while this.timeout(15000); @@ -73,8 +76,6 @@ suite("DiagnosticsManager Test Suite", function () { let cppFolderContext: FolderContext; let toolchain: SwiftToolchain; let workspaceFolder: vscode.WorkspaceFolder; - let cWorkspaceFolder: vscode.WorkspaceFolder; - let cppWorkspaceFolder: vscode.WorkspaceFolder; let mainUri: vscode.Uri; let funcUri: vscode.Uri; @@ -135,18 +136,14 @@ suite("DiagnosticsManager Test Suite", function () { workspaceContext = ctx; toolchain = workspaceContext.globalToolchain; workspaceFolder = testAssetWorkspaceFolder("diagnostics"); - cWorkspaceFolder = testAssetWorkspaceFolder("diagnosticsC"); - cppWorkspaceFolder = testAssetWorkspaceFolder("diagnosticsCpp"); folderContext = await folderInRootWorkspace("diagnostics", workspaceContext); cFolderContext = await folderInRootWorkspace("diagnosticsC", workspaceContext); cppFolderContext = await folderInRootWorkspace("diagnosticsCpp", workspaceContext); - mainUri = vscode.Uri.file(`${workspaceFolder.uri.path}/Sources/main.swift`); - funcUri = vscode.Uri.file(`${workspaceFolder.uri.path}/Sources/func.swift`); - cUri = vscode.Uri.file(`${cWorkspaceFolder.uri.path}/Sources/MyPoint/MyPoint.c`); - cppUri = vscode.Uri.file(`${cppWorkspaceFolder.uri.path}/Sources/MyPoint/MyPoint.cpp`); - cppHeaderUri = vscode.Uri.file( - `${cppWorkspaceFolder.uri.path}/Sources/MyPoint/include/MyPoint.h` - ); + mainUri = testAssetUri("diagnostics/Sources/main.swift"); + funcUri = testAssetUri("diagnostics/Sources/func.swift"); + cUri = testAssetUri("diagnosticsC/Sources/MyPoint/MyPoint.c"); + cppUri = testAssetUri("diagnosticsCpp/Sources/MyPoint/MyPoint.cpp"); + cppHeaderUri = testAssetUri("diagnosticsCpp/Sources/MyPoint/include/MyPoint.h"); }, }); @@ -223,6 +220,14 @@ suite("DiagnosticsManager Test Suite", function () { callback?: () => void ) { suite(`${style} diagnosticsStyle`, async function () { + let resetSettings: (() => Promise) | undefined; + suiteTeardown(async () => { + if (resetSettings) { + await resetSettings(); + resetSettings = undefined; + } + }); + // SourceKit-LSP sometimes sends diagnostics // after first build and can cause intermittent // failure if `swiftc` diagnostic is fixed @@ -244,14 +249,13 @@ suite("DiagnosticsManager Test Suite", function () { workspaceContext.diagnostics.clear(); workspaceContext.focusFolder(null); - const teardown = await updateSettings({ "swift.diagnosticsStyle": style }); + resetSettings = await updateSettings({ "swift.diagnosticsStyle": style }); const task = await createBuildAllTask(folderContext); // This return exit code and output for the task but we will omit it here // because the failures are expected and we just want the task to build await executeTaskAndWaitForResult(task).catch(() => { /* Ignore */ }); - return teardown; }); test("succeeds", async function () { @@ -489,9 +493,7 @@ suite("DiagnosticsManager Test Suite", function () { // https://github.com/apple/swift/issues/73973 test("Ignore XCTest failures", async () => { - const testUri = vscode.Uri.file( - `${workspaceFolder.uri.path}/Tests/MyCLITests/MyCLIXCTests.swift` - ); + const testUri = testAssetUri("diagnostics/Tests/MyCLITests/MyCLIXCTests.swift"); const fixture = testSwiftTask("swift", ["test"], workspaceFolder, toolchain); await vscode.tasks.executeTask(fixture.task); // Wait to spawn before writing @@ -667,8 +669,16 @@ suite("DiagnosticsManager Test Suite", function () { }); suite("keepAll", () => { + let resetSettings: (() => Promise) | undefined; + suiteTeardown(async () => { + if (resetSettings) { + await resetSettings(); + resetSettings = undefined; + } + }); + suiteSetup(async function () { - return await updateSettings({ + resetSettings = await updateSettings({ "swift.diagnosticsCollection": "keepAll", }); }); @@ -717,8 +727,16 @@ suite("DiagnosticsManager Test Suite", function () { }); suite("keepSourceKit", () => { + let resetSettings: (() => Promise) | undefined; + suiteTeardown(async () => { + if (resetSettings) { + await resetSettings(); + resetSettings = undefined; + } + }); + suiteSetup(async function () { - return await updateSettings({ + resetSettings = await updateSettings({ "swift.diagnosticsCollection": "keepSourceKit", }); }); @@ -831,8 +849,16 @@ suite("DiagnosticsManager Test Suite", function () { }); suite("keepSwiftc", () => { + let resetSettings: (() => Promise) | undefined; + suiteTeardown(async () => { + if (resetSettings) { + await resetSettings(); + resetSettings = undefined; + } + }); + suiteSetup(async function () { - return await updateSettings({ + resetSettings = await updateSettings({ "swift.diagnosticsCollection": "keepSwiftc", }); }); @@ -921,8 +947,16 @@ suite("DiagnosticsManager Test Suite", function () { }); suite("onlySourceKit", () => { + let resetSettings: (() => Promise) | undefined; + suiteTeardown(async () => { + if (resetSettings) { + await resetSettings(); + resetSettings = undefined; + } + }); + suiteSetup(async function () { - return await updateSettings({ + resetSettings = await updateSettings({ "swift.diagnosticsCollection": "onlySourceKit", }); }); @@ -984,8 +1018,16 @@ suite("DiagnosticsManager Test Suite", function () { }); suite("onlySwiftc", () => { + let resetSettings: (() => Promise) | undefined; + suiteTeardown(async () => { + if (resetSettings) { + await resetSettings(); + resetSettings = undefined; + } + }); + suiteSetup(async function () { - return await updateSettings({ + resetSettings = await updateSettings({ "swift.diagnosticsCollection": "onlySwiftc", }); }); @@ -1048,8 +1090,16 @@ suite("DiagnosticsManager Test Suite", function () { }); suite("cleanup", () => { + let resetSettings: (() => Promise) | undefined; + suiteTeardown(async () => { + if (resetSettings) { + await resetSettings(); + resetSettings = undefined; + } + }); + suiteSetup(async function () { - return await updateSettings({ + resetSettings = await updateSettings({ "swift.diagnosticsCollection": undefined, }); }); @@ -1128,14 +1178,21 @@ suite("DiagnosticsManager Test Suite", function () { // Skipped until we enable it in a nightly build suite("SourceKit-LSP diagnostics @slow", () => { + let resetSettings: (() => Promise) | undefined; + suiteTeardown(async () => { + if (resetSettings) { + await resetSettings(); + resetSettings = undefined; + } + }); + suiteSetup(async function () { - if (workspaceContext.globalToolchainSwiftVersion.isLessThan(new Version(5, 7, 0))) { + if (workspaceContext.globalToolchainSwiftVersion.isLessThan(new Version(6, 0, 0))) { + // need to waitForIndex this.skip(); return; } - workspaceContext.diagnostics.clear(); - workspaceContext.focusFolder(null); - return await updateSettings({ + resetSettings = await updateSettings({ "swift.diagnosticsCollection": "onlySourceKit", // So waitForDiagnostics only resolves from LSP }); }); @@ -1144,82 +1201,81 @@ suite("DiagnosticsManager Test Suite", function () { await vscode.commands.executeCommand(Workbench.ACTION_CLOSEALLEDITORS); }); - test("Provides swift diagnostics", async () => { - // Build for indexing - const task = await createBuildAllTask(folderContext); - await executeTaskAndWaitForResult(task); + async function triggerFileDiagnostics(uri: vscode.Uri, folder: FolderContext) { + const clientManager = workspaceContext.languageClientManager.get(folder); + await waitForClientState(clientManager, langclient.State.Running); + await vscode.window.showTextDocument(uri); + await new Promise(r => setTimeout(r, 2000)); + await touchDocument(clientManager, uri); + await waitForIndex(clientManager, folder.swiftVersion); + } + + suite("swift", () => { + setup(async function () { + this.timeout(3 * 60 * 1000); // Allow 3 minutes to build + await buildProject(workspaceContext, "diagnostics", true); + }); - const lspSource = toolchain.swiftVersion.isGreaterThanOrEqual(new Version(6, 0, 0)) - ? "SourceKit" - : "sourcekitd"; + test("Provides diagnostics", async () => { + const lspSource = toolchain.swiftVersion.isGreaterThanOrEqual(new Version(6, 0, 0)) + ? "SourceKit" + : "sourcekitd"; - // Include warning - const expectedDiagnostic1 = new vscode.Diagnostic( - new vscode.Range(new vscode.Position(1, 8), new vscode.Position(1, 8)), - "Initialization of variable 'unused' was never used; consider replacing with assignment to '_' or removing it", - vscode.DiagnosticSeverity.Warning - ); - expectedDiagnostic1.source = lspSource; // Set by LSP + // Include warning + const expectedDiagnostic1 = new vscode.Diagnostic( + new vscode.Range(new vscode.Position(1, 8), new vscode.Position(1, 8)), + "Initialization of variable 'unused' was never used; consider replacing with assignment to '_' or removing it", + vscode.DiagnosticSeverity.Warning + ); + expectedDiagnostic1.source = lspSource; // Set by LSP - // Include error - const expectedDiagnostic2 = new vscode.Diagnostic( - new vscode.Range(new vscode.Position(7, 0), new vscode.Position(7, 3)), - "Cannot assign to value: 'bar' is a 'let' constant", - vscode.DiagnosticSeverity.Error - ); - expectedDiagnostic2.source = lspSource; // Set by LSP + // Include error + const expectedDiagnostic2 = new vscode.Diagnostic( + new vscode.Range(new vscode.Position(7, 0), new vscode.Position(7, 3)), + "Cannot assign to value: 'bar' is a 'let' constant", + vscode.DiagnosticSeverity.Error + ); + expectedDiagnostic2.source = lspSource; // Set by LSP - // Open file - const promise = waitForDiagnostics({ - [mainUri.fsPath]: [expectedDiagnostic1, expectedDiagnostic2], + const promise = waitForDiagnostics({ + [mainUri.fsPath]: [expectedDiagnostic1, expectedDiagnostic2], + }); + // Retrigger diagnostics + await triggerFileDiagnostics(mainUri, folderContext); + await promise; }); - const document = await vscode.workspace.openTextDocument(mainUri); - await vscode.languages.setTextDocumentLanguage(document, "swift"); - await vscode.window.showTextDocument(document); - - // Retrigger diagnostics - await workspaceContext.focusFolder(folderContext); - await promise; - - assertHasDiagnostic(mainUri, expectedDiagnostic1); - assertHasDiagnostic(mainUri, expectedDiagnostic2); - }).timeout(3 * 60 * 1000); // Allow 3 minutes to build - - test("Provides clang diagnostics", async () => { - // Build for indexing - const task = await createBuildAllTask(cFolderContext); - await executeTaskAndWaitForResult(task); - - // No string manipulation - const expectedDiagnostic1 = new vscode.Diagnostic( - new vscode.Range(new vscode.Position(5, 10), new vscode.Position(5, 13)), - "Use of undeclared identifier 'bar'", - vscode.DiagnosticSeverity.Error - ); - expectedDiagnostic1.source = "clang"; // Set by LSP - - // Remove "(fix available)" from string from SourceKit - const expectedDiagnostic2 = new vscode.Diagnostic( - new vscode.Range(new vscode.Position(7, 4), new vscode.Position(7, 10)), - "Expected ';' after expression", - vscode.DiagnosticSeverity.Error - ); - expectedDiagnostic2.source = "clang"; // Set by LSP + }); - // Open file - const promise = waitForDiagnostics({ - [cUri.fsPath]: [expectedDiagnostic1, expectedDiagnostic2], + suite("clang", async () => { + setup(async function () { + this.timeout(3 * 60 * 1000); // Allow 3 minutes to build + await buildProject(workspaceContext, "diagnosticsC", true); }); - const document = await vscode.workspace.openTextDocument(cUri); - await vscode.languages.setTextDocumentLanguage(document, "c"); - await vscode.window.showTextDocument(document); - // Retrigger diagnostics - await workspaceContext.focusFolder(cFolderContext); - await promise; + test("Provides diagnostics", async () => { + // No string manipulation + const expectedDiagnostic1 = new vscode.Diagnostic( + new vscode.Range(new vscode.Position(5, 10), new vscode.Position(5, 13)), + "Use of undeclared identifier 'bar'", + vscode.DiagnosticSeverity.Error + ); + expectedDiagnostic1.source = "clang"; // Set by LSP + + // Remove "(fix available)" from string from SourceKit + const expectedDiagnostic2 = new vscode.Diagnostic( + new vscode.Range(new vscode.Position(7, 4), new vscode.Position(7, 10)), + "Expected ';' after expression", + vscode.DiagnosticSeverity.Error + ); + expectedDiagnostic2.source = "clang"; // Set by LSP - assertHasDiagnostic(cUri, expectedDiagnostic1); - assertHasDiagnostic(cUri, expectedDiagnostic2); - }).timeout(3 * 60 * 1000); // Allow 3 minutes to build + const promise = waitForDiagnostics({ + [cUri.fsPath]: [expectedDiagnostic1, expectedDiagnostic2], + }); + // Retrigger diagnostics + await triggerFileDiagnostics(cUri, cFolderContext); + await promise; + }); + }); }); }); diff --git a/test/integration-tests/language/LanguageClientIntegration.test.ts b/test/integration-tests/language/LanguageClientIntegration.test.ts index 25c90fb65..1597db469 100644 --- a/test/integration-tests/language/LanguageClientIntegration.test.ts +++ b/test/integration-tests/language/LanguageClientIntegration.test.ts @@ -13,46 +13,33 @@ //===----------------------------------------------------------------------===// 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 { testAssetUri } from "../../fixtures"; -import { executeTaskAndWaitForResult, waitForNoRunningTasks } from "../../utilities/tasks"; -import { getBuildAllTask, SwiftTask } from "../../../src/tasks/SwiftTaskProvider"; -import { activateExtensionForSuite, folderInRootWorkspace } from "../utilities/testutilities"; -import { waitForClientState, waitForIndex } from "../utilities/lsputilities"; +import { activateExtensionForSuite, buildProject } from "../utilities/testutilities"; +import { touchDocument, waitForIndex } from "../utilities/lsputilities"; +import { Version } from "../../../src/utilities/version"; +import { WorkspaceContext } from "../../../src/WorkspaceContext"; import { FolderContext } from "../../../src/FolderContext"; -async function buildProject(ctx: WorkspaceContext, name: string) { - await waitForNoRunningTasks(); - const folderContext = await folderInRootWorkspace(name, ctx); - const task = (await getBuildAllTask(folderContext)) as SwiftTask; - const { exitCode, output } = await executeTaskAndWaitForResult(task); - expect(exitCode, `${output}`).to.equal(0); - return folderContext; -} - -suite("Language Client Integration Suite @slow", function () { +suite.only("Language Client Integration Suite @slow", function () { this.timeout(3 * 60 * 1000); - let clientManager: LanguageClientManager; + let workspaceContext: WorkspaceContext; let folderContext: FolderContext; activateExtensionForSuite({ async setup(ctx) { - folderContext = await buildProject(ctx, "defaultPackage"); - - // Ensure lsp client is ready - clientManager = ctx.languageClientManager.get(folderContext); - await waitForClientState(clientManager, langclient.State.Running); + this.timeout(3 * 60 * 1000); // Allow 3 minutes to build + workspaceContext = ctx; + folderContext = await buildProject(workspaceContext, "defaultPackage"); + if (folderContext.swiftVersion.isLessThan(new Version(6, 0, 0))) { + // need to waitForIndex + this.skip(); + return; + } }, }); - setup(async () => { - await waitForIndex(clientManager, folderContext.swiftVersion); - }); - suite("Symbols", () => { const uri = testAssetUri("defaultPackage/Sources/PackageExe/main.swift"); const expectedDefinitionUri = testAssetUri( @@ -62,15 +49,24 @@ suite("Language Client Integration Suite @slow", function () { // Position of the symbol 'a' in main.swift const position = new vscode.Position(2, 6); - test("Goto Definition", async function () { + suiteSetup(async function () { + await new Promise(r => setTimeout(r, 2000)); // Focus on the file of interest - const editor = await vscode.window.showTextDocument(uri); - const document = editor.document; + const clientManager = workspaceContext.languageClientManager.get(folderContext); + await workspaceContext.focusFolder(null); + await workspaceContext.focusFolder(folderContext); + await vscode.window.showTextDocument(uri); + await new Promise(r => setTimeout(r, 2000)); + await touchDocument(clientManager, uri); + await new Promise(r => setTimeout(r, 2000)); + await waitForIndex(clientManager, folderContext.swiftVersion); + }); + test("Goto Definition", async function () { // Position of the symbol 'a' in main.swift const definitionLocations = await vscode.commands.executeCommand( "vscode.executeDefinitionProvider", - document.uri, + uri, position ); @@ -82,18 +78,14 @@ suite("Language Client Integration Suite @slow", function () { const definition = definitionLocations[0]; // Assert that the definition is in PackageLib.swift at line 0 - expect(definition.uri.toString()).to.equal(expectedDefinitionUri.toString()); + expect(definition.uri.fsPath).to.equal(expectedDefinitionUri.fsPath); expect(definition.range.start.line).to.equal(0); }); test("Find All References", async function () { - // Focus on the file of interest - const editor = await vscode.window.showTextDocument(uri); - const document = editor.document; - const referenceLocations = await vscode.commands.executeCommand( "vscode.executeReferenceProvider", - document.uri, + uri, position ); @@ -104,11 +96,11 @@ suite("Language Client Integration Suite @slow", function () { ); // Extract reference URIs and sort them to have a predictable order - const referenceUris = referenceLocations.map(ref => ref.uri.toString()); + const referenceUris = referenceLocations.map(ref => ref.uri.fsPath); const expectedUris = [ - snippetUri.toString(), - uri.toString(), // Reference in main.swift - expectedDefinitionUri.toString(), // Reference in PackageLib.swift + snippetUri.fsPath, + uri.fsPath, // Reference in main.swift + expectedDefinitionUri.fsPath, // Reference in PackageLib.swift ]; for (const uri of expectedUris) { diff --git a/test/integration-tests/utilities/lsputilities.ts b/test/integration-tests/utilities/lsputilities.ts index 7422d6731..a2431ab87 100644 --- a/test/integration-tests/utilities/lsputilities.ts +++ b/test/integration-tests/utilities/lsputilities.ts @@ -66,6 +66,18 @@ export async function waitForIndex( ); } +export async function touchDocument( + languageClientManager: LanguageClientManager, + uri: vscode.Uri +): Promise { + await languageClientManager.useLanguageClient(async client => + client.sendNotification(langclient.DidChangeTextDocumentNotification.type, { + textDocument: langclient.VersionedTextDocumentIdentifier.create(uri.fsPath, 1), + contentChanges: [], + }) + ); +} + export async function waitForClientState( languageClientManager: LanguageClientManager, expectedState: langclient.State diff --git a/test/integration-tests/utilities/testutilities.ts b/test/integration-tests/utilities/testutilities.ts index 98c60dda0..9a3663784 100644 --- a/test/integration-tests/utilities/testutilities.ts +++ b/test/integration-tests/utilities/testutilities.ts @@ -19,12 +19,14 @@ import { Api } from "../../../src/extension"; import { testAssetUri } from "../../fixtures"; import { WorkspaceContext } from "../../../src/WorkspaceContext"; import { FolderContext } from "../../../src/FolderContext"; -import { waitForNoRunningTasks } from "../../utilities/tasks"; +import { executeTaskAndWaitForResult, waitForNoRunningTasks } from "../../utilities/tasks"; import { closeAllEditors } from "../../utilities/commands"; import { isDeepStrictEqual } from "util"; import { Version } from "../../../src/utilities/version"; import { SwiftOutputChannel } from "../../../src/ui/SwiftOutputChannel"; import configuration from "../../../src/configuration"; +import { getBuildAllTask, SwiftTask } from "../../../src/tasks/SwiftTaskProvider"; +import { expect } from "chai"; function getRootWorkspaceFolder(): vscode.WorkspaceFolder { const result = vscode.workspace.workspaceFolders?.at(0); @@ -340,10 +342,13 @@ export async function updateSettings(settings: SettingsMap): Promise<() => Promi for (const setting of Object.keys(settings)) { const { section, name } = decomposeSettingName(setting); const config = vscode.workspace.getConfiguration(section, { languageId: "swift" }); - savedOriginalSettings[setting] = config.get(name); + const inspectedSetting = vscode.workspace + .getConfiguration(section, { languageId: "swift" }) + .inspect(name); + savedOriginalSettings[setting] = inspectedSetting?.workspaceValue; await config.update( name, - settings[setting] === "" ? undefined : settings[setting], + !settings[setting] ? undefined : settings[setting], vscode.ConfigurationTarget.Workspace ); } @@ -393,3 +398,19 @@ function decomposeSettingName(setting: string): { section: string; name: string } return { section, name }; } + +export async function buildProject( + ctx: WorkspaceContext, + name: string, + ignoreExitCode: boolean = false +) { + await waitForNoRunningTasks(); + const folderContext = await folderInRootWorkspace(name, ctx); + const task = (await getBuildAllTask(folderContext)) as SwiftTask; + const { exitCode, output } = await executeTaskAndWaitForResult(task); + console.log(output); + if (!ignoreExitCode) { + expect(exitCode, `${output}`).to.equal(0); + } + return folderContext; +}