diff --git a/packages/ansible-language-server/src/interfaces/extensionSettings.ts b/packages/ansible-language-server/src/interfaces/extensionSettings.ts index 996142dd2..c9d3899d8 100644 --- a/packages/ansible-language-server/src/interfaces/extensionSettings.ts +++ b/packages/ansible-language-server/src/interfaces/extensionSettings.ts @@ -10,7 +10,7 @@ export type IContainerEngine = "auto" | "podman" | "docker"; export type IPullPolicy = "always" | "missing" | "never" | "tag"; -interface ExtensionSettingsWithDescriptionBase { +export interface ExtensionSettingsWithDescriptionBase { [key: string]: SettingsEntry | string | boolean; } diff --git a/packages/ansible-language-server/src/services/ansiblePlaybook.ts b/packages/ansible-language-server/src/services/ansiblePlaybook.ts index de560e9d9..0ca81e2e9 100644 --- a/packages/ansible-language-server/src/services/ansiblePlaybook.ts +++ b/packages/ansible-language-server/src/services/ansiblePlaybook.ts @@ -95,14 +95,15 @@ export class AnsiblePlaybook { execError.stderr, ); - diagnostics = filteredErrorMessage - ? this.processReport( - execError.message, - filteredErrorMessage.groups.filename, - parseInt(filteredErrorMessage.groups.line), - parseInt(filteredErrorMessage.groups.column), - ) - : this.processReport(execError.message, docPath, 1, 1); + diagnostics = + filteredErrorMessage && filteredErrorMessage.groups + ? this.processReport( + execError.message, + filteredErrorMessage.groups.filename, + parseInt(filteredErrorMessage.groups.line), + parseInt(filteredErrorMessage.groups.column), + ) + : this.processReport(execError.message, docPath, 1, 1); if (execError.stderr) { this.connection.console.info( diff --git a/packages/ansible-language-server/src/services/executionEnvironment.ts b/packages/ansible-language-server/src/services/executionEnvironment.ts index d7d228fee..4951d5705 100644 --- a/packages/ansible-language-server/src/services/executionEnvironment.ts +++ b/packages/ansible-language-server/src/services/executionEnvironment.ts @@ -16,17 +16,18 @@ import { IVolumeMounts } from "../interfaces/extensionSettings"; export class ExecutionEnvironment { public isServiceInitialized: boolean = false; - private settings: ExtensionSettings; + private settings: ExtensionSettings | undefined = undefined; private connection: Connection; private context: WorkspaceFolderContext; private useProgressTracker = false; private successFileMarker = "SUCCESS"; private settingsVolumeMounts: string[] = []; - private settingsContainerOptions: string; - private _container_engine: IContainerEngine; - private _container_image: string; - private _container_image_id: string; - private _container_volume_mounts: Array; + private settingsContainerOptions: string | undefined = undefined; + private _container_engine: IContainerEngine | undefined = undefined; + private _container_image: string | undefined = undefined; + private _container_image_id: string | undefined = undefined; + private _container_volume_mounts: Array | undefined = + undefined; constructor(connection: Connection, context: WorkspaceFolderContext) { this.connection = connection; @@ -78,7 +79,7 @@ export class ExecutionEnvironment { } public async fetchPluginDocs(ansibleConfig: AnsibleConfig): Promise { - if (!this.isServiceInitialized) { + if (!this.isServiceInitialized || !this._container_image) { this.connection.console.error( `ExecutionEnvironment service not correctly initialized. Failed to fetch plugin docs`, ); @@ -196,7 +197,11 @@ export class ExecutionEnvironment { command: string, mountPaths?: Set, ): string | undefined { - if (!this.isServiceInitialized) { + if ( + !this.isServiceInitialized || + !this._container_engine || + !this._container_image + ) { this.connection.console.error( "ExecutionEnvironment service not correctly initialized.", ); @@ -257,7 +262,9 @@ export class ExecutionEnvironment { // docker does not support this option containerCommand.push("--quiet"); } else { - containerCommand.push(`--user=${process.getuid()}`); + if (process.getuid) { + containerCommand.push(`--user=${process.getuid()}`); + } } // handle container options setting from client @@ -284,6 +291,12 @@ export class ExecutionEnvironment { } private async pullContainerImage(): Promise { + if (!this._container_engine || !this._container_image || !this.settings) { + this.connection.window.showErrorMessage( + "Execution environment not properly initialized.", + ); + return false; + } const imagePuller = new ImagePuller( this.connection, this.context, @@ -304,6 +317,13 @@ export class ExecutionEnvironment { } private setContainerEngine(): boolean { + if (!this._container_engine) { + this.connection.window.showErrorMessage( + "Unable to setContainerEnginer with incompletly initialized settings.", + ); + return false; + } + if (this._container_engine === "auto") { for (const ce of ["podman", "docker"]) { try { @@ -422,7 +442,13 @@ export class ExecutionEnvironment { .trim(); return result.trim() !== ""; } catch (error) { - this.connection.console.error(error); + let message: string; + if (error instanceof Error) { + message = error.message; + } else { + message = JSON.stringify(error); + } + this.connection.console.error(message); return false; } } diff --git a/packages/ansible-language-server/src/services/settingsManager.ts b/packages/ansible-language-server/src/services/settingsManager.ts index fab7e7c28..7fd5cbf4f 100644 --- a/packages/ansible-language-server/src/services/settingsManager.ts +++ b/packages/ansible-language-server/src/services/settingsManager.ts @@ -8,7 +8,7 @@ import { } from "../interfaces/extensionSettings"; export class SettingsManager { - private connection: Connection; + private connection: Connection | null; private clientSupportsConfigRequests; private configurationChangeHandlers: Map = new Map(); @@ -135,7 +135,10 @@ export class SettingsManager { public globalSettings: ExtensionSettings = this.defaultSettings; - constructor(connection: Connection, clientSupportsConfigRequests: boolean) { + constructor( + connection: Connection | null, + clientSupportsConfigRequests: boolean, + ) { this.connection = connection; this.clientSupportsConfigRequests = clientSupportsConfigRequests; } @@ -155,7 +158,7 @@ export class SettingsManager { return Promise.resolve(this.globalSettings); } let result = this.documentSettings.get(uri); - if (!result) { + if (!result && this.connection) { const clientSettings = await this.connection.workspace.getConfiguration({ scopeUri: uri, section: "ansible", @@ -167,6 +170,9 @@ export class SettingsManager { result = Promise.resolve(mergedSettings); this.documentSettings.set(uri, result); } + if (!result) { + return {} as ExtensionSettings; + } return result; } @@ -188,7 +194,7 @@ export class SettingsManager { for (const [uri, handler] of this.configurationChangeHandlers) { const config = await this.documentSettings.get(uri); - if (config) { + if (config && this.connection) { // found cached values, now compare to the new ones const newConfigPromise = this.connection.workspace.getConfiguration({ diff --git a/packages/ansible-language-server/test/consoleOutput.ts b/packages/ansible-language-server/test/consoleOutput.ts index 71634b720..13ad367b3 100644 --- a/packages/ansible-language-server/test/consoleOutput.ts +++ b/packages/ansible-language-server/test/consoleOutput.ts @@ -3,11 +3,11 @@ * by modifying their abilities and redirects them to suppress and release them appropriately */ export class ConsoleOutput { - private logOutput: string; - private debugOutput: string; - private infoOutput: string; - private warnOutput: string; - private errorOutput: string; + private logOutput: string = ""; + private debugOutput: string = ""; + private infoOutput: string = ""; + private warnOutput: string = ""; + private errorOutput: string = ""; private originalConsoleLog = console.log; private originalConsoleDebug = console.debug; diff --git a/packages/ansible-language-server/test/helper.ts b/packages/ansible-language-server/test/helper.ts index 30961b491..ee8163eb7 100644 --- a/packages/ansible-language-server/test/helper.ts +++ b/packages/ansible-language-server/test/helper.ts @@ -106,7 +106,9 @@ export function smartFilter( } // Sort completion list based on `sortText` property of the completion item - completionList.sort((a, b) => a.sortText.localeCompare(b.sortText)); + completionList.sort((a: CompletionItem, b: CompletionItem) => + a.sortText && b.sortText ? a.sortText.localeCompare(b?.sortText) : 0, + ); // Construct a new Fuse object to do fuzzy search with key as `filterText` property of the completion item const searcher = new Fuse(completionList, { diff --git a/packages/ansible-language-server/test/providers/definitionProvider.test.ts b/packages/ansible-language-server/test/providers/definitionProvider.test.ts index 5191c40d5..38bcda8fa 100644 --- a/packages/ansible-language-server/test/providers/definitionProvider.test.ts +++ b/packages/ansible-language-server/test/providers/definitionProvider.test.ts @@ -62,21 +62,21 @@ function testModuleNamesForDefinition( } expect(actualDefinition).to.have.length(1); + if (actualDefinition) { + const definition = actualDefinition[0]; + // file uri check + expect(definition.targetUri.startsWith("file:///")).to.be.true; + expect(definition.targetUri).satisfy((fileUri: string) => + fileExists(URI.parse(fileUri).path), + ); - const definition = actualDefinition[0]; - - // file uri check - expect(definition.targetUri.startsWith("file:///")).to.be.true; - expect(definition.targetUri).satisfy((fileUri: string) => - fileExists(URI.parse(fileUri).path), - ); - - // nodule name range check in the playbook - expect(definition.originSelectionRange).to.deep.equal(selectionRange); + // nodule name range check in the playbook + expect(definition.originSelectionRange).to.deep.equal(selectionRange); - // original document range checks - expect(definition).to.haveOwnProperty("targetRange"); - expect(definition).to.haveOwnProperty("targetSelectionRange"); + // original document range checks + expect(definition).to.haveOwnProperty("targetRange"); + expect(definition).to.haveOwnProperty("targetSelectionRange"); + } }); }); } @@ -88,7 +88,7 @@ describe("getDefinition()", function () { const context = workspaceManager.getContext(fixtureFileUri); const textDoc = getDoc(fixtureFilePath); - const docSettings = context.documentSettings.get(textDoc.uri); + const docSettings = context?.documentSettings.get(textDoc.uri); describe("Module name definitions", function () { describe("With EE enabled @ee", function () { @@ -96,24 +96,33 @@ describe("getDefinition()", function () { setFixtureAnsibleCollectionPathEnv( "/home/runner/.ansible/collections:/usr/share/ansible", ); - await enableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await enableExecutionEnvironmentSettings(docSettings); + } }); - testModuleNamesForDefinition(context, textDoc); + if (context) { + testModuleNamesForDefinition(context, textDoc); + } after(async function () { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); }); describe("With EE disabled", function () { before(async function () { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); - - testModuleNamesForDefinition(context, textDoc); + if (context) { + testModuleNamesForDefinition(context, textDoc); + } }); }); }); diff --git a/packages/ansible-language-server/test/providers/hoverProvider.test.ts b/packages/ansible-language-server/test/providers/hoverProvider.test.ts index a9fdce9a4..2ac997d74 100644 --- a/packages/ansible-language-server/test/providers/hoverProvider.test.ts +++ b/packages/ansible-language-server/test/providers/hoverProvider.test.ts @@ -1,4 +1,5 @@ import { TextDocument } from "vscode-languageserver-textdocument"; +import { Position } from "vscode-languageserver"; import { expect } from "chai"; import { createTestWorkspaceManager, @@ -9,7 +10,6 @@ import { setFixtureAnsibleCollectionPathEnv, } from "../helper"; import { doHover } from "../../src/providers/hoverProvider"; -import { Position } from "vscode-languageserver"; import { WorkspaceFolderContext } from "../../src/services/workspaceManager"; function testPlayKeywords( @@ -254,7 +254,8 @@ describe("doHover()", () => { let context = workspaceManager.getContext(fixtureFileUri); let textDoc = getDoc(fixtureFilePath); - let docSettings = context.documentSettings.get(textDoc.uri); + let docSettings = context?.documentSettings.get(textDoc.uri); + expect(docSettings !== undefined); describe("Play keywords hover", () => { describe("With EE enabled @ee", () => { @@ -262,24 +263,32 @@ describe("doHover()", () => { setFixtureAnsibleCollectionPathEnv( "/home/runner/.ansible/collections:/usr/share/ansible", ); - await enableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await enableExecutionEnvironmentSettings(docSettings); + } }); - - testPlayKeywords(context, textDoc); + if (context) { + testPlayKeywords(context, textDoc); + } after(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); }); describe("With EE disabled", () => { before(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); - - testPlayKeywords(context, textDoc); + if (context) { + testPlayKeywords(context, textDoc); + } }); }); @@ -289,24 +298,32 @@ describe("doHover()", () => { setFixtureAnsibleCollectionPathEnv( "/home/runner/.ansible/collections:/usr/share/ansible", ); - await enableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await enableExecutionEnvironmentSettings(docSettings); + } }); - - testTaskKeywords(context, textDoc); + if (context) { + testTaskKeywords(context, textDoc); + } after(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); }); describe("With EE disabled", () => { before(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); - - testTaskKeywords(context, textDoc); + if (context) { + testTaskKeywords(context, textDoc); + } }); }); @@ -316,33 +333,42 @@ describe("doHover()", () => { setFixtureAnsibleCollectionPathEnv( "/home/runner/.ansible/collections:/usr/share/ansible", ); - await enableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await enableExecutionEnvironmentSettings(docSettings); + } }); - testBlockKeywords(context, textDoc); + if (context) { + testBlockKeywords(context, textDoc); + } after(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); }); describe("With EE disabled", () => { before(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); - testBlockKeywords(context, textDoc); + if (context) { + testBlockKeywords(context, textDoc); + } }); }); - fixtureFilePath = "hover/roles.yml"; fixtureFileUri = resolveDocUri(fixtureFilePath); context = workspaceManager.getContext(fixtureFileUri); textDoc = getDoc(fixtureFilePath); - docSettings = context.documentSettings.get(textDoc.uri); + docSettings = context?.documentSettings.get(textDoc.uri); describe("Role keywords hover", () => { describe("With EE enabled @ee", () => { @@ -350,24 +376,34 @@ describe("doHover()", () => { setFixtureAnsibleCollectionPathEnv( "/home/runner/.ansible/collections:/usr/share/ansible", ); - await enableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await enableExecutionEnvironmentSettings(docSettings); + } }); - testRoleKeywords(context, textDoc); + if (context) { + testRoleKeywords(context, textDoc); + } after(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); }); describe("With EE disabled", () => { before(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); - testRoleKeywords(context, textDoc); + if (context) { + testRoleKeywords(context, textDoc); + } }); }); @@ -376,7 +412,7 @@ describe("doHover()", () => { context = workspaceManager.getContext(fixtureFileUri); textDoc = getDoc(fixtureFilePath); - docSettings = context.documentSettings.get(textDoc.uri); + docSettings = context?.documentSettings.get(textDoc.uri); describe("Module name and options hover", () => { describe("With EE enabled @ee", () => { @@ -384,24 +420,34 @@ describe("doHover()", () => { setFixtureAnsibleCollectionPathEnv( "/home/runner/.ansible/collections:/usr/share/ansible", ); - await enableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await enableExecutionEnvironmentSettings(docSettings); + } }); - testModuleNames(context, textDoc); + if (context) { + testModuleNames(context, textDoc); + } after(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); }); describe("With EE disabled", () => { before(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); - testModuleNames(context, textDoc); + if (context) { + testModuleNames(context, textDoc); + } }); }); @@ -411,24 +457,34 @@ describe("doHover()", () => { setFixtureAnsibleCollectionPathEnv( "/home/runner/.ansible/collections:/usr/share/ansible", ); - await enableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await enableExecutionEnvironmentSettings(docSettings); + } }); - testNoHover(context, textDoc); + if (context) { + testNoHover(context, textDoc); + } after(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); }); describe("With EE disabled", () => { before(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); - testNoHover(context, textDoc); + if (context) { + testNoHover(context, textDoc); + } }); }); @@ -436,7 +492,7 @@ describe("doHover()", () => { fixtureFileUri = resolveDocUri(fixtureFilePath); context = workspaceManager.getContext(fixtureFileUri); textDoc = getDoc(fixtureFilePath); - docSettings = context.documentSettings.get(textDoc.uri); + docSettings = context?.documentSettings.get(textDoc.uri); describe("Hover for playbook adjacent collection", () => { describe("With EE enabled @ee", () => { @@ -444,24 +500,34 @@ describe("doHover()", () => { setFixtureAnsibleCollectionPathEnv( "/home/runner/.ansible/collections:/usr/share/ansible", ); - await enableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await enableExecutionEnvironmentSettings(docSettings); + } }); - testPlaybookAdjacentCollection(context, textDoc); + if (context) { + testPlaybookAdjacentCollection(context, textDoc); + } after(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); }); describe("With EE disabled", () => { before(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); - testPlaybookAdjacentCollection(context, textDoc); + if (context) { + testPlaybookAdjacentCollection(context, textDoc); + } }); }); @@ -470,7 +536,7 @@ describe("doHover()", () => { fixtureFileUri = resolveDocUri(fixtureFilePath); context = workspaceManager.getContext(fixtureFileUri); textDoc = getDoc(fixtureFilePath); - docSettings = context.documentSettings.get(textDoc.uri); + docSettings = context?.documentSettings.get(textDoc.uri); describe("Negate hover for non playbook adjacent collection", () => { describe("With EE enabled @ee", () => { @@ -478,24 +544,34 @@ describe("doHover()", () => { setFixtureAnsibleCollectionPathEnv( "/home/runner/.ansible/collections:/usr/share/ansible", ); - await enableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await enableExecutionEnvironmentSettings(docSettings); + } }); - testNonPlaybookAdjacentCollection(context, textDoc); + if (context) { + testNonPlaybookAdjacentCollection(context, textDoc); + } after(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); }); describe("With EE disabled", () => { before(async () => { setFixtureAnsibleCollectionPathEnv(); - await disableExecutionEnvironmentSettings(docSettings); + if (docSettings) { + await disableExecutionEnvironmentSettings(docSettings); + } }); - testNonPlaybookAdjacentCollection(context, textDoc); + if (context) { + testNonPlaybookAdjacentCollection(context, textDoc); + } }); }); }); diff --git a/packages/ansible-language-server/test/utils/runCommand.test.ts b/packages/ansible-language-server/test/utils/runCommand.test.ts index 4c01f271c..fe2fec49d 100644 --- a/packages/ansible-language-server/test/utils/runCommand.test.ts +++ b/packages/ansible-language-server/test/utils/runCommand.test.ts @@ -5,6 +5,7 @@ import { createConnection } from "vscode-languageserver/node"; import { getDoc } from "../helper"; import * as path from "path"; import { readFileSync } from "fs"; +import { ExecException } from "child_process"; describe("commandRunner", () => { const packageJsonPath = require.resolve("../../package.json"); @@ -78,28 +79,33 @@ describe("commandRunner", () => { process.argv.push("--node-ipc"); const connection = createConnection(); const workspaceManager = new WorkspaceManager(connection); - const textDoc = await getDoc("yaml/ancestryBuilder.yml"); + const textDoc = getDoc("yaml/ancestryBuilder.yml"); const context = workspaceManager.getContext(textDoc.uri); - const settings = await context.documentSettings.get(textDoc.uri); - if (pythonInterpreterPath) { - settings.python.interpreterPath = pythonInterpreterPath; - } + if (context) { + const settings = await context.documentSettings.get(textDoc.uri); + if (pythonInterpreterPath) { + settings.python.interpreterPath = pythonInterpreterPath; + } - const commandRunner = new CommandRunner(connection, context, settings); - try { - const proc = await commandRunner.runCommand( - args[0], - args.slice(1).join(" "), - ); - expect(proc.stdout, proc.stderr).contains(stdout); - expect(proc.stderr, proc.stdout).contains(stderr); - } catch (e) { - if (e instanceof AssertionError) { - throw e; + const commandRunner = new CommandRunner(connection, context, settings); + try { + const proc = await commandRunner.runCommand( + args[0], + args.slice(1).join(" "), + ); + expect(proc.stdout, proc.stderr).contains(stdout); + expect(proc.stderr, proc.stdout).contains(stderr); + } catch (e) { + if (e instanceof AssertionError) { + throw e; + } + if (e instanceof Error) { + const err = e as ExecException; + expect(err.code).equals(rc); + expect(err.stdout).contains(stdout); + expect(err.stderr).contains(stderr); + } } - expect(e.code, e).equals(rc); - expect(e.stdout, e).contains(stdout); - expect(e.stderr, e).contains(stderr); } }); }); diff --git a/packages/ansible-language-server/test/utils/withInterpreter.test.ts b/packages/ansible-language-server/test/utils/withInterpreter.test.ts index 50dfef19c..2bd40253a 100644 --- a/packages/ansible-language-server/test/utils/withInterpreter.test.ts +++ b/packages/ansible-language-server/test/utils/withInterpreter.test.ts @@ -1,8 +1,17 @@ import { expect } from "chai"; import { withInterpreter } from "../../src/utils/misc"; +interface testType { + scenario: string; + executable: string; + args: string; + interpreterPath: string; + activationScript: string; + expectedCommand: string; + expectedEnv: { [name: string]: string }; +} describe("withInterpreter", () => { - const tests = [ + const tests: testType[] = [ { scenario: "when activation script is provided", executable: "ansible-lint", @@ -11,7 +20,7 @@ describe("withInterpreter", () => { activationScript: "/path/to/venv/bin/activate", expectedCommand: "bash -c 'source /path/to/venv/bin/activate && ansible-lint playbook.yml'", - expectedEnv: undefined, + expectedEnv: {}, }, { scenario: "when no activation script is provided", @@ -20,6 +29,7 @@ describe("withInterpreter", () => { interpreterPath: "", activationScript: "", expectedCommand: "ansible-lint playbook.yml", + expectedEnv: {}, }, { scenario: "when absolute path of executable is provided", @@ -28,6 +38,7 @@ describe("withInterpreter", () => { interpreterPath: "", activationScript: "", expectedCommand: "/absolute/path/to/ansible-lint playbook.yml", + expectedEnv: {}, }, { scenario: "when absolute path of interpreter is provided", @@ -68,7 +79,13 @@ describe("withInterpreter", () => { expectedKeys.forEach((key) => { expect(actualCommand[1]).to.haveOwnProperty(key); expect(typeof actualCommand[1] === "object"); - expect(actualCommand[1][key]).to.include(expectedEnv[key]); + if (!actualCommand[1] || typeof expectedEnv === "string") { + expect(false); + } else { + expect(actualCommand[1][key]).to.include( + expectedEnv[key] as string, + ); + } }); } }); diff --git a/packages/ansible-language-server/tools/settings-doc-generator.ts b/packages/ansible-language-server/tools/settings-doc-generator.ts index 5178f1e5d..d5bea6706 100644 --- a/packages/ansible-language-server/tools/settings-doc-generator.ts +++ b/packages/ansible-language-server/tools/settings-doc-generator.ts @@ -1,11 +1,12 @@ -import Handlebars = require("handlebars"); +import * as Handlebars from "handlebars"; import * as fs from "fs"; import { SettingsManager } from "../src/services/settingsManager"; import * as path from "path"; import * as _ from "lodash"; +import { ExtensionSettingsWithDescriptionBase } from "../src/interfaces/extensionSettings"; // Get the default settings values from settingsManager class -const settingsManager = new SettingsManager(null, null); +const settingsManager = new SettingsManager(null, false); const defaultSettings = { ansible: settingsManager.defaultSettingsWithDescription, }; @@ -73,14 +74,22 @@ const settingsReadmeFileUri = path.join( // Register a special function for handlebars to deal with comparison of stringed value of false // Else, normal #if treats it as boolean, even after converting booleans to strings in typescript -Handlebars.registerHelper("ifEqualsFalse", function (arg1, options) { - return arg1.toString() === "false" ? options.fn(this) : options.inverse(this); -}); +Handlebars.registerHelper( + "ifEqualsFalse", + function (this: unknown, arg1: unknown, options: Handlebars.HelperOptions) { + return String(arg1) === "false" ? options.fn(this) : options.inverse(this); + }, +); // Register a special function for handlebars to deal with the checking of "list" as value type of settings -Handlebars.registerHelper("ifValueArray", function (arg1, options) { - return arg1.toString() === "list" ? options.fn(this) : options.inverse(this); -}); +Handlebars.registerHelper( + "ifValueArray", + function (this: unknown, arg1, options: Handlebars.HelperOptions) { + return arg1.toString() === "list" + ? options.fn(this) + : options.inverse(this); + }, +); const template = Handlebars.compile(TEMPLATE); const output = template({ arrayOfDefaultSettings }); @@ -91,7 +100,11 @@ console.log( ); console.log(`File: ${settingsReadmeFileUri}`); -export function toDotNotation(obj, res = {}, current = "") { +export function toDotNotation( + obj: ExtensionSettingsWithDescriptionBase, + res: ExtensionSettingsWithDescriptionBase = {}, + current = "", +): ExtensionSettingsWithDescriptionBase { for (const key in obj) { const value = obj[key]; const newKey = current ? `${current}.${key}` : key; // joined key with dot @@ -99,7 +112,11 @@ export function toDotNotation(obj, res = {}, current = "") { if (_.isArray(value) && value[0]) { toDotNotation(value[0], res, `${newKey}._array`); // it's an array object, so do it again (to identify array '._array' is added) } else { - toDotNotation(value, res, newKey); // it's a nested object, so do it again + toDotNotation( + value as ExtensionSettingsWithDescriptionBase, + res, + newKey, + ); // it's a nested object, so do it again } } else { res[newKey] = value; // it's not an object, so set the property @@ -108,7 +125,17 @@ export function toDotNotation(obj, res = {}, current = "") { return res; } -export function structureSettings(settingsInDotNotation) { +type SettingEntry = { + parent?: SettingEntry; + key?: string; + setting: string; + defaultValue: string | string[]; + valueType: string; + slug: string; +}; +export function structureSettings( + settingsInDotNotation: ExtensionSettingsWithDescriptionBase, +) { // Form an appropriate structure so that it is easier to iterate over it in the template // Structure is as follows: // @@ -141,8 +168,8 @@ export function structureSettings(settingsInDotNotation) { // } // ] - const settingsArray = []; - const objWithArrayValues = []; + const settingsArray: SettingEntry[] = []; + const objWithArrayValues: SettingEntry[] = []; const keysWithArrayValues = []; // keep track of keys whose elements are array for (const k in settingsInDotNotation) { const keyArray = k.split("."); @@ -164,7 +191,7 @@ export function structureSettings(settingsInDotNotation) { ? settingsInDotNotation[`${key}.default`] : "", description: settingsInDotNotation[`${key}.description`], - }; + } as unknown as SettingEntry; objWithArrayValues.push(arrayObj); // break; @@ -177,7 +204,7 @@ export function structureSettings(settingsInDotNotation) { description: settingsInDotNotation[`${key}.description`], valueType: typeof settingsInDotNotation[`${key}.default`], slug: key.replace("ansible.", ""), - }; + } as SettingEntry; settingsArray.push(obj); } @@ -196,7 +223,7 @@ export function structureSettings(settingsInDotNotation) { defaultValue: arrayObjFinal[k], valueType: "list", slug: k.replace("ansible.", ""), - }; + } as unknown as SettingEntry; settingsArray.push(obj); } @@ -204,7 +231,9 @@ export function structureSettings(settingsInDotNotation) { return makeSettingsUnique(settingsArray); } -export function makeSettingsUnique(arrayObject) { +export function makeSettingsUnique( + arrayObject: SettingEntry[], +): SettingEntry[] { const uniqueSettings = arrayObject.filter((value, index) => { const _value = JSON.stringify(value); return ( diff --git a/packages/ansible-language-server/tsconfig.json b/packages/ansible-language-server/tsconfig.json index 75249402a..dd91cdb9c 100644 --- a/packages/ansible-language-server/tsconfig.json +++ b/packages/ansible-language-server/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "alwaysStrict": true, + // "strict": true, "declaration": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true,