Repository: typescript-eslint/tslint-to-eslint-config (ts)

Issue:
Don't report an error if .vscode/settings.json doesn't exist
### 🐛 Bug Report

-   `tslint-to-eslint-config` version: 0.4.0
-   ESLint version: N/A
-   Node version: N/A

#### Actual Behavior

```
λ npx tslint-to-eslint-config
❌ 1 error running tslint-to-eslint: ❌
Error: ENOENT: no such file or directory, open 'path\to\dir\.vscode\settings.json'
```

#### Expected Behavior

If the project doesn't have the file, don't complain.

#### Reproduction

In a new empty directory:

```
npx eslint --init
npx tslint-to-eslint-config
```

Pull request:
Stopped erroring when .vscode/settings.json doesn't exist
## PR Checklist

-   [x] Addresses an existing issue: fixes #303
-   [x] That issue was marked as [`status: accepting prs`](https://github.com/typescript-eslint/tslint-to-eslint-config/labels/status%3A%20accepting%20prs)

## Overview

Uses `fs.fileExists` to check if the file exists. If it doesn't but a file was specified, nothing is converted.

Patch:
diff --git a/bin/tslint-to-eslint-config b/bin/tslint-to-eslint-config
old mode 100644
new mode 100755
diff --git a/src/cli/main.ts b/src/cli/main.ts
index b3adcd2..d0747d8 100644
--- a/src/cli/main.ts
+++ b/src/cli/main.ts
@@ -74,6 +74,7 @@ const findConfigurationDependencies = {
 };
 
 const findEditorConfigurationDependencies: FindEditorConfigurationDependencies = {
+    fileSystem: fsFileSystem,
     importer: boundImporter,
 };
 
diff --git a/src/cli/runCli.ts b/src/cli/runCli.ts
index 0a1cba6..2d6a85e 100644
--- a/src/cli/runCli.ts
+++ b/src/cli/runCli.ts
@@ -6,7 +6,6 @@ import { version } from "../../package.json";
 import { Logger } from "../adapters/logger";
 import { SansDependencies } from "../binding";
 import { convertConfig } from "../conversion/convertConfig";
-import { DEFAULT_VSCODE_SETTINGS_PATH } from "../input/vsCodeSettings";
 import { ResultStatus, ResultWithStatus, TSLintToESLintSettings } from "../types";
 
 export type RunCliDependencies = {
@@ -25,11 +24,7 @@ export const runCli = async (
         .option("--package [package]", "package configuration file to convert using")
         .option("--tslint [tslint]", "tslint configuration file to convert using")
         .option("--typescript [typescript]", "typescript configuration file to convert using")
-        .option(
-            "--editor [editor]",
-            "editor configuration file to convert using",
-            DEFAULT_VSCODE_SETTINGS_PATH,
-        )
+        .option("--editor [editor]", "editor configuration file to convert")
         .option("-V --version", "output the package version");
 
     const parsedArgv = {
@@ -79,18 +74,18 @@ const logErrorResult = (result: ResultWithStatus, dependencies: RunCliDependenci
             dependencies.logger.stderr.write(chalk.red("Could not start tslint-to-eslint:"));
             dependencies.logger.stderr.write(chalk.redBright(` ❌${EOL}`));
             for (const complaint of result.complaints) {
-                dependencies.logger.stderr.write(chalk.yellowBright(`${complaint}${EOL}`));
+                dependencies.logger.stderr.write(chalk.yellowBright(`  ${complaint}${EOL}`));
             }
             break;
 
         case ResultStatus.Failed:
             dependencies.logger.stderr.write(chalk.redBright("❌ "));
-            dependencies.logger.stderr.write(chalk.yellow(`${result.errors.length} error`));
-            dependencies.logger.stderr.write(chalk.yellow(result.errors.length === 1 ? "" : "s"));
-            dependencies.logger.stderr.write(chalk.yellow(" running tslint-to-eslint:"));
+            dependencies.logger.stderr.write(chalk.red(`${result.errors.length} error`));
+            dependencies.logger.stderr.write(chalk.red(result.errors.length === 1 ? "" : "s"));
+            dependencies.logger.stderr.write(chalk.red(" running tslint-to-eslint:"));
             dependencies.logger.stderr.write(chalk.redBright(` ❌${EOL}`));
             for (const error of result.errors) {
-                dependencies.logger.stderr.write(chalk.yellowBright(`${error.stack}${EOL}`));
+                dependencies.logger.stderr.write(chalk.gray(`  ${error.stack}${EOL}`));
             }
             break;
     }
diff --git a/src/conversion/convertEditorConfig.ts b/src/conversion/convertEditorConfig.ts
index 2d78ba6..3218ffb 100644
--- a/src/conversion/convertEditorConfig.ts
+++ b/src/conversion/convertEditorConfig.ts
@@ -2,7 +2,6 @@ import { SansDependencies } from "../binding";
 import { writeConversionResults } from "../creation/writeEditorConfigConversionResults";
 import { convertEditorSettings } from "../editorSettings/convertEditorSettings";
 import { findEditorConfiguration } from "../input/findEditorConfiguration";
-import { DEFAULT_VSCODE_SETTINGS_PATH } from "../input/vsCodeSettings";
 import { reportEditorSettingConversionResults } from "../reporting/reportEditorSettingConversionResults";
 import { ResultStatus, ResultWithStatus, TSLintToESLintSettings } from "../types";
 
@@ -20,26 +19,26 @@ export const convertEditorConfig = async (
     dependencies: ConvertEditorConfigDependencies,
     settings: TSLintToESLintSettings,
 ): Promise<ResultWithStatus> => {
-    const editorConfigPath = settings.editor ? settings.editor : DEFAULT_VSCODE_SETTINGS_PATH;
-    const originalEditorConfiguration = await dependencies.findEditorConfiguration(
-        editorConfigPath,
-    );
-    if (originalEditorConfiguration instanceof Error) {
+    const conversion = await dependencies.findEditorConfiguration(settings.editor);
+    if (conversion === undefined) {
+        return {
+            status: ResultStatus.Succeeded,
+        };
+    }
+
+    if (conversion.result instanceof Error) {
         return {
-            errors: [originalEditorConfiguration],
+            errors: [conversion.result],
             status: ResultStatus.Failed,
         };
     }
 
-    const settingConversionResults = dependencies.convertEditorSettings(
-        originalEditorConfiguration,
-    );
+    const settingConversionResults = dependencies.convertEditorSettings(conversion.result);
 
-    const outputPath = editorConfigPath;
     const fileWriteError = await dependencies.writeConversionResults(
-        outputPath,
+        conversion.configPath,
         settingConversionResults,
-        originalEditorConfiguration,
+        conversion.result,
     );
     if (fileWriteError !== undefined) {
         return {
diff --git a/src/input/findEditorConfiguration.ts b/src/input/findEditorConfiguration.ts
index dee2e07..b02c0df 100644
--- a/src/input/findEditorConfiguration.ts
+++ b/src/input/findEditorConfiguration.ts
@@ -1,3 +1,4 @@
+import { FileSystem } from "../adapters/fileSystem";
 import { SansDependencies } from "../binding";
 import { EditorConfiguration } from "./editorConfiguration";
 import { findRawConfiguration } from "./findRawConfiguration";
@@ -6,18 +7,34 @@ import { importer } from "./importer";
 import { DEFAULT_VSCODE_SETTINGS_PATH } from "./vsCodeSettings";
 
 export type FindEditorConfigurationDependencies = {
+    fileSystem: Pick<FileSystem, "fileExists">;
     importer: SansDependencies<typeof importer>;
 };
 
 export const findEditorConfiguration = async (
     dependencies: FindEditorConfigurationDependencies,
-    config: string | undefined,
-): Promise<DeepPartial<EditorConfiguration> | Error> => {
-    const filePath = config ?? DEFAULT_VSCODE_SETTINGS_PATH;
-    const rawConfiguration = await findRawConfiguration<DeepPartial<EditorConfiguration>>(
+    specifiedConfigPath: string | undefined,
+) => {
+    const attemptingConfigPath = specifiedConfigPath ?? DEFAULT_VSCODE_SETTINGS_PATH;
+
+    if (!(await dependencies.fileSystem.fileExists(attemptingConfigPath))) {
+        return specifiedConfigPath === undefined
+            ? undefined
+            : {
+                  configPath: attemptingConfigPath,
+                  result: new Error(
+                      `Could not find editor configuration under '${attemptingConfigPath}'.`,
+                  ),
+              };
+    }
+
+    const result = await findRawConfiguration<DeepPartial<EditorConfiguration>>(
         dependencies.importer,
-        filePath,
+        attemptingConfigPath,
     );
 
-    return rawConfiguration;
+    return {
+        configPath: attemptingConfigPath,
+        result,
+    };
 };
diff --git a/src/reporting/reportConversionResults.ts b/src/reporting/reportConversionResults.ts
index 581aa3a..af5e167 100644
--- a/src/reporting/reportConversionResults.ts
+++ b/src/reporting/reportConversionResults.ts
@@ -26,15 +26,17 @@ export const reportConversionResults = (
     }
 
     if (ruleConversionResults.missing.length !== 0) {
-        const missingSettingOutputMapping = (setting: TSLintRuleOptions) =>
-            `${setting.ruleName} does not yet have an ESLint equivalent.${EOL}`;
-        const additionalWarnings = [`defaulting to eslint-plugin-tslint for these rules`];
         logMissingConversionTarget(
             "rule",
-            missingSettingOutputMapping,
+            (setting: TSLintRuleOptions) =>
+                `${setting.ruleName} does not yet have an ESLint equivalent.${EOL}`,
             ruleConversionResults.missing,
             dependencies.logger,
-            additionalWarnings,
+            [
+                ruleConversionResults.missing.length === 1
+                    ? "defaulting to eslint-plugin-tslint for it."
+                    : "defaulting to eslint-plugin-tslint for these rules.",
+            ],
         );
     }
 
@@ -54,19 +56,27 @@ const logNotices = (converted: Map<string, ESLintRuleOptions>, logger: Logger) =
     ) as RuleWithNotices[];
 
     if (rulesWithNotices.length !== 0) {
-        logger.stdout.write(chalk.yellowBright(`📢 ${rulesWithNotices.length} ESLint`));
-        logger.stdout.write(
-            chalk.yellowBright(rulesWithNotices.length === 1 ? ` rule behaves` : ` rules behave`),
-        );
-        logger.stdout.write(
-            chalk.yellowBright(` differently from their TSLint counterparts: 📢${EOL}`),
-        );
+        logger.stdout.write(chalk.yellowBright(`${EOL}❗ ${rulesWithNotices.length} ESLint`));
+
+        if (rulesWithNotices.length === 1) {
+            logger.stdout.write(
+                chalk.yellowBright(
+                    ` rule behaves differently from its TSLint counterpart ❗${EOL}`,
+                ),
+            );
+        } else {
+            logger.stdout.write(
+                chalk.yellowBright(
+                    ` rules behave differently from their TSLint counterparts ❗${EOL}`,
+                ),
+            );
+        }
 
         for (const rule of rulesWithNotices) {
-            logger.stdout.write(chalk.yellow(`* ${rule.ruleName}:${EOL}`));
+            logger.stdout.write(chalk.yellow(`  * ${rule.ruleName}:${EOL}`));
 
             for (const notice of rule.notices) {
-                logger.stdout.write(chalk.yellow(`  - ${notice}${EOL}`));
+                logger.stdout.write(chalk.yellow(`    - ${notice}${EOL}`));
             }
         }
     }
diff --git a/src/reporting/reportOutputs.ts b/src/reporting/reportOutputs.ts
index 3694b1d..9f084cf 100644
--- a/src/reporting/reportOutputs.ts
+++ b/src/reporting/reportOutputs.ts
@@ -23,14 +23,12 @@ export const logSuccessfulConversions = (
 };
 
 export const logFailedConversions = (failed: ErrorSummary[], logger: Logger) => {
-    logger.stderr.write(`${chalk.redBright(`💀 ${failed.length}`)}`);
+    logger.stderr.write(`${chalk.redBright(`${EOL}❌ ${failed.length}`)}`);
     logger.stderr.write(chalk.red(` error${failed.length === 1 ? "" : "s"}`));
     logger.stderr.write(chalk.red(" thrown."));
-    logger.stderr.write(chalk.redBright(` 💀${EOL}`));
-
+    logger.stderr.write(chalk.redBright(` ❌${EOL}`));
     logger.info.write(failed.map(failed => failed.getSummary()).join("\n\n") + "\n\n");
-
-    logger.stderr.write(chalk.gray(`Check ${logger.debugFileName} for details.${EOL}`));
+    logger.stderr.write(chalk.gray(`  Check ${logger.debugFileName} for details.${EOL}`));
 };
 
 export const logMissingConversionTarget = <T>(
@@ -40,7 +38,7 @@ export const logMissingConversionTarget = <T>(
     logger: Logger,
     additionalWarnings: string[] = [],
 ) => {
-    logger.stdout.write(chalk.yellowBright(`️👀 ${missing.length}`));
+    logger.stdout.write(chalk.yellowBright(`️${EOL}❓ ${missing.length}`));
     logger.stdout.write(
         chalk.yellow(
             missing.length === 1
@@ -48,23 +46,27 @@ export const logMissingConversionTarget = <T>(
                 : ` ${conversionTypeName}s do not yet have ESLint equivalents`,
         ),
     );
-    logger.stdout.write(chalk.yellow(` (see generated log file)`));
+    logger.stdout.write(chalk.yellowBright(` ❓${EOL}`));
+    logger.stdout.write(chalk.yellow(`  See generated log file`));
 
     if (additionalWarnings.length > 0) {
         logger.stdout.write(chalk.yellow("; "));
-    }
-    for (const warning of additionalWarnings) {
-        logger.stdout.write(chalk.yellow(warning));
-    }
-    logger.stdout.write(chalk.yellow("."));
 
-    logger.stdout.write(chalk.yellowBright(` 👀${EOL}`));
+        for (const warning of additionalWarnings) {
+            logger.stdout.write(chalk.yellow(warning));
+        }
+    } else {
+        logger.stdout.write(chalk.yellow("."));
+    }
 
-    logger.info.write(missing.map(conversion => missingOutputMapping(conversion)).join(""));
+    logger.info.write(
+        chalk.yellow(missing.map(conversion => missingOutputMapping(conversion)).join("")),
+    );
+    logger.stdout.write(chalk.yellow(EOL));
 };
 
 export const logMissingPlugins = (plugins: Set<string>, logger: Logger) => {
-    logger.stdout.write(chalk.cyanBright(`⚡ ${plugins.size}`));
+    logger.stdout.write(chalk.cyanBright(`${EOL}⚡ ${plugins.size}`));
     logger.stdout.write(chalk.cyan(" package"));
     logger.stdout.write(chalk.cyan(plugins.size === 1 ? " is" : "s are"));
     logger.stdout.write(chalk.cyan(` required for new ESLint rules.`));
@@ -72,7 +74,8 @@ export const logMissingPlugins = (plugins: Set<string>, logger: Logger) => {
 
     logger.stdout.write(
         Array.from(plugins)
-            .map(pluginName => `\t${chalk.cyanBright(pluginName)}${EOL}`)
+            .map(pluginName => `  ${chalk.cyanBright(pluginName)}${EOL}`)
             .join(""),
     );
+    logger.stdout.write(EOL);
 };
diff --git a/src/rules/converters/space-before-function-paren.ts b/src/rules/converters/space-before-function-paren.ts
index 916a598..a52916c 100644
--- a/src/rules/converters/space-before-function-paren.ts
+++ b/src/rules/converters/space-before-function-paren.ts
@@ -1,29 +1,38 @@
 import { RuleConverter } from "../converter";
 
-const NOT_SUPPORTED_OPTIONS = ["constructor", "method"];
+const SUPPORTED_OPTIONS: string[] = ["anonymous", "asyncArrow", "named"];
 
-function isExistedESLintOption(rule: string) {
-    return !NOT_SUPPORTED_OPTIONS.includes(rule);
-}
+type ObjectArgument = Record<string, "always" | "never">;
 
-export const convertSpaceBeforeFunctionParen: RuleConverter = tslintRule => {
-    const ruleArguments = tslintRule.ruleArguments.filter(isExistedESLintOption);
-    const notices = NOT_SUPPORTED_OPTIONS.reduce<string[]>((acc, option) => {
-        if (tslintRule.ruleArguments.includes(option)) {
-            acc.push(`Option "${option}" is not supported by ESLint.`);
-        }
-        return acc;
-    }, []);
+const isObjectArgument = (argument: unknown): argument is ObjectArgument =>
+    typeof argument === "object" && argument !== null;
 
-    return {
-        rules: [
-            {
-                ...(tslintRule.ruleArguments.length !== 0 && {
-                    ruleArguments,
+export const convertSpaceBeforeFunctionParen: RuleConverter = tslintRule => {
+    const argument: unknown = tslintRule.ruleArguments[0];
+    const ruleName = "space-before-function-paren";
+    if (argument === "always" || argument === "never") {
+        return { rules: [{ ruleArguments: [argument], ruleName }] };
+    }
+    if (isObjectArgument(argument)) {
+        const notices = Object.keys(argument)
+            .filter(option => !SUPPORTED_OPTIONS.includes(option))
+            .map(option => `Option "${option}" is not supported by ESLint.`);
+        const filtered = Object.keys(argument)
+            .filter(option => SUPPORTED_OPTIONS.includes(option))
+            .reduce<ObjectArgument>((obj, option) => {
+                return { ...obj, [option]: argument[option] };
+            }, {});
+        return {
+            rules: [
+                {
                     ...(notices.length !== 0 && { notices }),
-                }),
-                ruleName: "space-before-function-paren",
-            },
-        ],
-    };
+                    ...(Object.keys(filtered).length !== 0 && {
+                        ruleArguments: [filtered],
+                    }),
+                    ruleName,
+                },
+            ],
+        };
+    }
+    return { rules: [{ ruleName }] };
 };


Test patch:
diff --git a/src/cli/runCli.test.ts b/src/cli/runCli.test.ts
index c163ce6..14fa47e 100644
--- a/src/cli/runCli.test.ts
+++ b/src/cli/runCli.test.ts
@@ -68,7 +68,7 @@ describe("runCli", () => {
         expectEqualWrites(
             dependencies.logger.stderr.write,
             "❌ Could not start tslint-to-eslint: ❌",
-            complaint,
+            `  ${complaint}`,
         );
     });
 
@@ -93,7 +93,7 @@ describe("runCli", () => {
         expectEqualWrites(
             dependencies.logger.stderr.write,
             "❌ 1 error running tslint-to-eslint: ❌",
-            `${error.stack}`,
+            `  ${error.stack}`,
         );
     });
 
@@ -121,8 +121,8 @@ describe("runCli", () => {
         expectEqualWrites(
             dependencies.logger.stderr.write,
             "❌ 2 errors running tslint-to-eslint: ❌",
-            `${errors[0].stack}`,
-            `${errors[1].stack}`,
+            `  ${errors[0].stack}`,
+            `  ${errors[1].stack}`,
         );
     });
 
diff --git a/src/conversion/convertEditorConfig.test.ts b/src/conversion/convertEditorConfig.test.ts
index c649325..d734e48 100644
--- a/src/conversion/convertEditorConfig.test.ts
+++ b/src/conversion/convertEditorConfig.test.ts
@@ -19,6 +19,21 @@ const createStubDependencies = (
 });
 
 describe("convertEditorConfig", () => {
+    it("returns a success result when there is no original configuration", async () => {
+        // Arrange
+        const dependencies = createStubDependencies({
+            findEditorConfiguration: async () => undefined,
+        });
+
+        // Act
+        const result = await convertEditorConfig(dependencies, stubSettings);
+
+        // Assert
+        expect(result).toEqual({
+            status: ResultStatus.Succeeded,
+        });
+    });
+
     it("returns the failure result when finding the original configurations fails", async () => {
         // Arrange
         const error = new Error();
@@ -28,7 +43,10 @@ describe("convertEditorConfig", () => {
         };
 
         const dependencies = createStubDependencies({
-            findEditorConfiguration: async () => error,
+            findEditorConfiguration: async () => ({
+                configPath: "",
+                result: error,
+            }),
         });
 
         // Act
@@ -59,14 +77,12 @@ describe("convertEditorConfig", () => {
         // Arrange
         const originalConfig = {
             "typescript.tsdk": "node_modules/typescript/lib",
-            "editor.tabSize": 4,
-            "editor.codeActionsOnSave": {
-                "source.organizeImports": false,
-            },
         };
 
         const dependencies = createStubDependencies({
-            findEditorConfiguration: jest.fn().mockResolvedValue(originalConfig),
+            findEditorConfiguration: jest.fn().mockResolvedValue({
+                result: originalConfig,
+            }),
         });
 
         // Act
@@ -113,20 +129,4 @@ describe("convertEditorConfig", () => {
             status: ResultStatus.Succeeded,
         });
     });
-
-    it("uses VS Code default settings path if editor config parameter is undefined", async () => {
-        // Arrange
-        const expectedEditorPath = ".vscode/settings.json";
-        const settings = {
-            config: "./eslintrc.js",
-        };
-
-        const dependencies = createStubDependencies();
-
-        // Act
-        await convertEditorConfig(dependencies, settings);
-
-        // Assert
-        expect(dependencies.findEditorConfiguration).toHaveBeenCalledWith(expectedEditorPath);
-    });
 });
diff --git a/src/input/findEditorConfiguration.test.ts b/src/input/findEditorConfiguration.test.ts
index 2e6617b..734395c 100644
--- a/src/input/findEditorConfiguration.test.ts
+++ b/src/input/findEditorConfiguration.test.ts
@@ -1,7 +1,9 @@
+import { createStubFileSystem } from "../adapters/fileSystem.stub";
 import {
     findEditorConfiguration,
     FindEditorConfigurationDependencies,
 } from "./findEditorConfiguration";
+import { DEFAULT_VSCODE_SETTINGS_PATH } from "./vsCodeSettings";
 
 const stubConfigPath = "temp/";
 
@@ -10,10 +12,46 @@ export const createStubImporter = (filePath = "") =>
 
 const createStubDependencies = (overrides: Partial<FindEditorConfigurationDependencies> = {}) => ({
     importer: createStubImporter(stubConfigPath),
+    fileSystem: createStubFileSystem(),
     ...overrides,
 });
 
 describe("findEditorConfiguration", () => {
+    it("returns undefined when the file is not specified and does not exist", async () => {
+        // Arrange
+        const dependencies = createStubDependencies({
+            fileSystem: {
+                fileExists: async () => false,
+            },
+        });
+
+        // Act
+        const result = await findEditorConfiguration(dependencies, undefined);
+
+        // Assert
+        expect(result).toEqual(undefined);
+    });
+
+    it("returns an error when the file is specified and does not exist", async () => {
+        // Arrange
+        const dependencies = createStubDependencies({
+            fileSystem: {
+                fileExists: async () => false,
+            },
+        });
+
+        // Act
+        const result = await findEditorConfiguration(dependencies, stubConfigPath);
+
+        // Assert
+        expect(result).toEqual({
+            configPath: stubConfigPath,
+            result: expect.objectContaining({
+                message: `Could not find editor configuration under '${stubConfigPath}'.`,
+            }),
+        });
+    });
+
     it("returns an error when importer returns one", async () => {
         // Arrange
         const message = "error";
@@ -27,11 +65,12 @@ describe("findEditorConfiguration", () => {
         const result = await findEditorConfiguration(dependencies, stubConfigPath);
 
         // Assert
-        expect(result).toEqual(
-            expect.objectContaining({
+        expect(result).toEqual({
+            configPath: stubConfigPath,
+            result: expect.objectContaining({
                 message,
             }),
-        );
+        });
     });
 
     it("reads from the given configuration path when one is provided", async () => {
@@ -46,25 +85,30 @@ describe("findEditorConfiguration", () => {
         expect(dependencies.importer).toHaveBeenLastCalledWith(configPath);
     });
 
-    it("defaults to VS Code editor settings path when config path isn't provided", async () => {
+    it("parses object from the default VS Code configuration path when the file is not specified and read successfully", async () => {
         // Arrange
-        const dependencies = createStubDependencies();
+        const originalConfig = {
+            "typescript.tsdk": "node_modules/typescript/lib",
+        };
+
+        const dependencies = createStubDependencies({
+            importer: async () => originalConfig,
+        });
 
         // Act
-        await findEditorConfiguration(dependencies, undefined);
+        const result = await findEditorConfiguration(dependencies, undefined);
 
         // Assert
-        expect(dependencies.importer).toHaveBeenLastCalledWith(".vscode/settings.json");
+        expect(result).toEqual({
+            configPath: DEFAULT_VSCODE_SETTINGS_PATH,
+            result: originalConfig,
+        });
     });
 
-    it("parses object from configuration path when read successfully", async () => {
+    it("parses object from configuration path when the file is specified and read successfully", async () => {
         // Arrange
         const originalConfig = {
             "typescript.tsdk": "node_modules/typescript/lib",
-            "editor.tabSize": 4,
-            "editor.codeActionsOnSave": {
-                "source.organizeImports": false,
-            },
         };
 
         const dependencies = createStubDependencies({
@@ -75,6 +119,9 @@ describe("findEditorConfiguration", () => {
         const result = await findEditorConfiguration(dependencies, stubConfigPath);
 
         // Assert
-        expect(result).toEqual(originalConfig);
+        expect(result).toEqual({
+            configPath: stubConfigPath,
+            result: originalConfig,
+        });
     });
 });
diff --git a/src/reporting/reportConversionResults.test.ts b/src/reporting/reportConversionResults.test.ts
index 9a239b5..1ef8a27 100644
--- a/src/reporting/reportConversionResults.test.ts
+++ b/src/reporting/reportConversionResults.test.ts
@@ -57,11 +57,11 @@ describe("reportConversionResults", () => {
         // Assert
         expectEqualWrites(
             logger.stdout.write,
-            `✨ 1 rule replaced with its ESLint equivalent. ✨${EOL}` +
-                `📢 1 ESLint rule behaves differently from their TSLint counterparts: 📢${EOL}` +
-                `* tslint-rule-one:${EOL}` +
-                `  - 1${EOL}` +
-                `  - 2${EOL}`,
+            `✨ 1 rule replaced with its ESLint equivalent. ✨${EOL}`,
+            `❗ 1 ESLint rule behaves differently from its TSLint counterpart ❗`,
+            `  * tslint-rule-one:`,
+            `    - 1`,
+            `    - 2`,
         );
     });
 
@@ -98,14 +98,14 @@ describe("reportConversionResults", () => {
         // Assert
         expectEqualWrites(
             logger.stdout.write,
-            `✨ 2 rules replaced with their ESLint equivalents. ✨${EOL}` +
-                `📢 2 ESLint rules behave differently from their TSLint counterparts: 📢${EOL}` +
-                `* tslint-rule-one:${EOL}` +
-                `  - 1${EOL}` +
-                `  - 2${EOL}` +
-                `* tslint-rule-two:${EOL}` +
-                `  - 3${EOL}` +
-                `  - 4${EOL}`,
+            `✨ 2 rules replaced with their ESLint equivalents. ✨${EOL}`,
+            `❗ 2 ESLint rules behave differently from their TSLint counterparts ❗`,
+            `  * tslint-rule-one:`,
+            `    - 1`,
+            `    - 2`,
+            `  * tslint-rule-two:`,
+            `    - 3`,
+            `    - 4`,
         );
     });
 
@@ -123,8 +123,8 @@ describe("reportConversionResults", () => {
         // Assert
         expectEqualWrites(
             logger.stderr.write,
-            "💀 1 error thrown. 💀",
-            `Check ${logger.debugFileName} for details.`,
+            "❌ 1 error thrown. ❌",
+            `  Check ${logger.debugFileName} for details.`,
         );
     });
 
@@ -142,8 +142,8 @@ describe("reportConversionResults", () => {
         // Assert
         expectEqualWrites(
             logger.stderr.write,
-            "💀 2 errors thrown. 💀",
-            `Check ${logger.debugFileName} for details.`,
+            "❌ 2 errors thrown. ❌",
+            `  Check ${logger.debugFileName} for details.`,
         );
     });
 
@@ -167,7 +167,8 @@ describe("reportConversionResults", () => {
         // Assert
         expectEqualWrites(
             logger.stdout.write,
-            "👀 1 rule does not yet have an ESLint equivalent (see generated log file); defaulting to eslint-plugin-tslint for these rules. 👀",
+            "❓ 1 rule does not yet have an ESLint equivalent ❓",
+            `  See generated log file; defaulting to eslint-plugin-tslint for it.`,
         );
         expectEqualWrites(
             logger.info.write,
@@ -200,7 +201,8 @@ describe("reportConversionResults", () => {
         // Assert
         expectEqualWrites(
             logger.stdout.write,
-            "👀 2 rules do not yet have ESLint equivalents (see generated log file); defaulting to eslint-plugin-tslint for these rules. 👀",
+            "❓ 2 rules do not yet have ESLint equivalents ❓",
+            `  See generated log file; defaulting to eslint-plugin-tslint for these rules.`,
         );
         expectEqualWrites(
             logger.info.write,
@@ -224,7 +226,7 @@ describe("reportConversionResults", () => {
         expectEqualWrites(
             logger.stdout.write,
             "⚡ 1 package is required for new ESLint rules. ⚡",
-            "\tplugin-one",
+            "  plugin-one",
         );
     });
 
@@ -243,8 +245,8 @@ describe("reportConversionResults", () => {
         expectEqualWrites(
             logger.stdout.write,
             "⚡ 2 packages are required for new ESLint rules. ⚡",
-            "\tplugin-one",
-            "\tplugin-two",
+            "  plugin-one",
+            "  plugin-two",
         );
     });
 });
diff --git a/src/reporting/reportEditorSettingConversionResults.test.ts b/src/reporting/reportEditorSettingConversionResults.test.ts
index c16c65f..c7df319 100644
--- a/src/reporting/reportEditorSettingConversionResults.test.ts
+++ b/src/reporting/reportEditorSettingConversionResults.test.ts
@@ -28,7 +28,7 @@ describe("reportEditorSettingConversionResults", () => {
         // Assert
         expectEqualWrites(
             logger.stdout.write,
-            `✨ 1 editor setting replaced with its ESLint equivalent. ✨${EOL}`,
+            `${EOL}✨ 1 editor setting replaced with its ESLint equivalent. ✨${EOL}`,
         );
     });
 
@@ -61,7 +61,7 @@ describe("reportEditorSettingConversionResults", () => {
         // Assert
         expectEqualWrites(
             logger.stdout.write,
-            `✨ 2 editor settings replaced with their ESLint equivalents. ✨${EOL}`,
+            `${EOL}✨ 2 editor settings replaced with their ESLint equivalents. ✨${EOL}`,
         );
     });
 
@@ -79,8 +79,8 @@ describe("reportEditorSettingConversionResults", () => {
         // Assert
         expectEqualWrites(
             logger.stderr.write,
-            "💀 1 error thrown. 💀",
-            `Check ${logger.debugFileName} for details.`,
+            `❌ 1 error thrown. ❌`,
+            `  Check ${logger.debugFileName} for details.`,
         );
     });
 
@@ -98,8 +98,8 @@ describe("reportEditorSettingConversionResults", () => {
         // Assert
         expectEqualWrites(
             logger.stderr.write,
-            "💀 2 errors thrown. 💀",
-            `Check ${logger.debugFileName} for details.`,
+            `❌ 2 errors thrown. ❌`,
+            `  Check ${logger.debugFileName} for details.`,
         );
     });
 
@@ -121,7 +121,8 @@ describe("reportEditorSettingConversionResults", () => {
         // Assert
         expectEqualWrites(
             logger.stdout.write,
-            "👀 1 editor setting does not yet have an ESLint equivalent (see generated log file). 👀",
+            `❓ 1 editor setting does not yet have an ESLint equivalent ❓`,
+            `  See generated log file.`,
         );
         expectEqualWrites(
             logger.info.write,
@@ -150,7 +151,8 @@ describe("reportEditorSettingConversionResults", () => {
         // Assert
         expectEqualWrites(
             logger.stdout.write,
-            "👀 2 editor settings do not yet have ESLint equivalents (see generated log file). 👀",
+            `❓ 2 editor settings do not yet have ESLint equivalents ❓`,
+            `  See generated log file.`,
         );
         expectEqualWrites(
             logger.info.write,
diff --git a/src/rules/converters/tests/space-before-function-paren.test.ts b/src/rules/converters/tests/space-before-function-paren.test.ts
index b83285f..77663d4 100644
--- a/src/rules/converters/tests/space-before-function-paren.test.ts
+++ b/src/rules/converters/tests/space-before-function-paren.test.ts
@@ -15,15 +15,45 @@ describe(convertSpaceBeforeFunctionParen, () => {
         });
     });
 
-    test("conversion with an argument", () => {
+    test("conversion with an argument (always)", () => {
         const result = convertSpaceBeforeFunctionParen({
-            ruleArguments: ["anonymous"],
+            ruleArguments: ["always"],
         });
 
         expect(result).toEqual({
             rules: [
                 {
-                    ruleArguments: ["anonymous"],
+                    ruleArguments: ["always"],
+                    ruleName: "space-before-function-paren",
+                },
+            ],
+        });
+    });
+
+    test("conversion with an argument (never)", () => {
+        const result = convertSpaceBeforeFunctionParen({
+            ruleArguments: ["never"],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleArguments: ["never"],
+                    ruleName: "space-before-function-paren",
+                },
+            ],
+        });
+    });
+
+    test("conversion with an argument (object)", () => {
+        const result = convertSpaceBeforeFunctionParen({
+            ruleArguments: [{ anonymous: "never" }],
+        });
+
+        expect(result).toEqual({
+            rules: [
+                {
+                    ruleArguments: [{ anonymous: "never" }],
                     ruleName: "space-before-function-paren",
                 },
             ],
@@ -32,7 +62,15 @@ describe(convertSpaceBeforeFunctionParen, () => {
 
     test("conversion with all existing arguments", () => {
         const result = convertSpaceBeforeFunctionParen({
-            ruleArguments: ["anonymous", "named", "asyncArrow", "method", "constructor"],
+            ruleArguments: [
+                {
+                    anonymous: "never",
+                    asyncArrow: "always",
+                    constructor: "never",
+                    method: "never",
+                    named: "never",
+                },
+            ],
         });
 
         expect(result).toEqual({
@@ -42,7 +80,13 @@ describe(convertSpaceBeforeFunctionParen, () => {
                         'Option "constructor" is not supported by ESLint.',
                         'Option "method" is not supported by ESLint.',
                     ],
-                    ruleArguments: ["anonymous", "named", "asyncArrow"],
+                    ruleArguments: [
+                        {
+                            anonymous: "never",
+                            asyncArrow: "always",
+                            named: "never",
+                        },
+                    ],
                     ruleName: "space-before-function-paren",
                 },
             ],
@@ -51,7 +95,12 @@ describe(convertSpaceBeforeFunctionParen, () => {
 
     test('conversion with not supported options ["method", "constructor"]', () => {
         const result = convertSpaceBeforeFunctionParen({
-            ruleArguments: ["method", "constructor"],
+            ruleArguments: [
+                {
+                    constructor: "never",
+                    method: "never",
+                },
+            ],
         });
 
         expect(result).toEqual({
@@ -61,7 +110,6 @@ describe(convertSpaceBeforeFunctionParen, () => {
                         'Option "constructor" is not supported by ESLint.',
                         'Option "method" is not supported by ESLint.',
                     ],
-                    ruleArguments: [],
                     ruleName: "space-before-function-paren",
                 },
             ],