Repository: eslint/eslint
Pull request #9557 (2017-11-01 07:59:08)

Chore: upgrade ajv@5.3.0
refs #9444
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

**What is the purpose of this pull request? (put an "X" next to item)**

[x] Other, please explain:

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

**What changes did you make? (Give an overview)**
upgrade `ajv@5.3.0`, which replaced `json-stable-stringify` with `json-stable-stringify-without-jsonify`

**Is there anything you'd like reviewers to focus on?**





Patch:
diff --git a/docs/rules/object-property-newline.md b/docs/rules/object-property-newline.md
index 3e0e5a661..8cc73b05e 100644
--- a/docs/rules/object-property-newline.md
+++ b/docs/rules/object-property-newline.md
@@ -92,7 +92,7 @@ This rule applies equally to all property specifications, regardless of notation
 
 - `a: 1` (ES5)
 - `a` (ES2015 shorthand property)
-- ``[`prop${a}`]`` (computed property name)
+- ``[`prop${a}`]`` (ES2015 computed property name)
 
 Thus, the rule (without the object option) prohibits both of these:
 
@@ -200,6 +200,12 @@ const obj3 = {
 const a = "antidisestablishmentarianistically";
 const b = "yugoslavyalılaştırabildiklerimizdenmişsiniz";
 const obj4 = {a, b};
+
+const domain = process.argv[4];
+const obj5 = {
+    foo: "foo", [
+    domain.includes(":") ? "complexdomain" : "simpledomain"
+]: true};
 ```
 
 Examples of **correct** code for this rule, with no object option or with `allowMultiplePropertiesPerLine` set to `false`:
@@ -207,12 +213,18 @@ Examples of **correct** code for this rule, with no object option or with `allow
 ```js
 /*eslint object-property-newline: "error"*/
 
-const obj = {
+const obj1 = {
     foo: "foo",
     bar: "bar",
     baz: "baz"
 };
 
+const obj2 = {
+    foo: "foo"
+    , bar: "bar"
+    , baz: "baz"
+};
+
 const user = process.argv[2];
 const obj3 = {
     user,
@@ -231,9 +243,9 @@ Examples of additional **correct** code for this rule with the `{ "allowMultiple
 ```js
 /*eslint object-property-newline: ["error", { "allowMultiplePropertiesPerLine": true }]*/
 
-var obj = { foo: "foo", bar: "bar", baz: "baz" };
+const obj = { foo: "foo", bar: "bar", baz: "baz" };
 
-var obj2 = {
+const obj2 = {
     foo: "foo", bar: "bar", baz: "baz"
 };
 const user = process.argv[2];
diff --git a/docs/user-guide/command-line-interface.md b/docs/user-guide/command-line-interface.md
index 3e6a5a31b..a8f51cf61 100644
--- a/docs/user-guide/command-line-interface.md
+++ b/docs/user-guide/command-line-interface.md
@@ -333,6 +333,17 @@ Example:
 
     eslint -f ./customformat.js file.js
 
+An npm-installed formatter is resolved with or without `eslint-formatter-` prefix.
+
+Example:
+
+    npm install eslint-formatter-pretty
+
+    eslint -f pretty file.js
+
+    // equivalent:
+    eslint -f eslint-formatter-pretty file.js
+
 When specified, the given format is output to the console. If you'd like to save that output into a file, you can do so on the command line like so:
 
     eslint -f compact file.js > results.txt
diff --git a/lib/cli-engine.js b/lib/cli-engine.js
index 29ee9fef5..39169dc42 100644
--- a/lib/cli-engine.js
+++ b/lib/cli-engine.js
@@ -24,12 +24,16 @@ const fs = require("fs"),
     fileEntryCache = require("file-entry-cache"),
     globUtil = require("./util/glob-util"),
     validator = require("./config/config-validator"),
-    stringify = require("json-stable-stringify"),
+    stringify = require("json-stable-stringify-without-jsonify"),
     hash = require("./util/hash"),
+    ModuleResolver = require("./util/module-resolver"),
+    naming = require("./util/naming"),
     pkg = require("../package.json");
 
 const debug = require("debug")("eslint:cli-engine");
 
+const resolver = new ModuleResolver();
+
 //------------------------------------------------------------------------------
 // Typedefs
 //------------------------------------------------------------------------------
@@ -670,15 +674,22 @@ class CLIEngine {
             // replace \ with / for Windows compatibility
             format = format.replace(/\\/g, "/");
 
+            const cwd = this.options ? this.options.cwd : process.cwd();
+            const namespace = naming.getNamespaceFromTerm(format);
+
             let formatterPath;
 
             // if there's a slash, then it's a file
-            if (format.indexOf("/") > -1) {
-                const cwd = this.options ? this.options.cwd : process.cwd();
-
+            if (!namespace && format.indexOf("/") > -1) {
                 formatterPath = path.resolve(cwd, format);
             } else {
-                formatterPath = `./formatters/${format}`;
+                try {
+                    const npmFormat = naming.normalizePackageName(format, "eslint-formatter");
+
+                    formatterPath = resolver.resolve(npmFormat, `${cwd}/node_modules`);
+                } catch (e) {
+                    formatterPath = `./formatters/${format}`;
+                }
             }
 
             try {
diff --git a/lib/config/config-file.js b/lib/config/config-file.js
index 87412dd2a..8cefd437d 100644
--- a/lib/config/config-file.js
+++ b/lib/config/config-file.js
@@ -13,11 +13,11 @@ const fs = require("fs"),
     path = require("path"),
     ConfigOps = require("./config-ops"),
     validator = require("./config-validator"),
-    pathUtil = require("../util/path-util"),
     ModuleResolver = require("../util/module-resolver"),
+    naming = require("../util/naming"),
     pathIsInside = require("path-is-inside"),
     stripComments = require("strip-json-comments"),
-    stringify = require("json-stable-stringify"),
+    stringify = require("json-stable-stringify-without-jsonify"),
     requireUncached = require("require-uncached");
 
 const debug = require("debug")("eslint:config-file");
@@ -29,7 +29,7 @@ const debug = require("debug")("eslint:config-file");
 /**
  * Determines sort order for object keys for json-stable-stringify
  *
- * see: https://github.com/substack/json-stable-stringify#cmp
+ * see: https://github.com/samn/json-stable-stringify#cmp
  *
  * @param   {Object} a The first comparison object ({key: akey, value: avalue})
  * @param   {Object} b The second comparison object ({key: bkey, value: bvalue})
@@ -436,50 +436,6 @@ function applyExtends(config, configContext, filePath, relativeTo) {
     return config;
 }
 
-/**
- * Brings package name to correct format based on prefix
- * @param {string} name The name of the package.
- * @param {string} prefix Can be either "eslint-plugin" or "eslint-config
- * @returns {string} Normalized name of the package
- * @private
- */
-function normalizePackageName(name, prefix) {
-
-    /*
-     * On Windows, name can come in with Windows slashes instead of Unix slashes.
-     * Normalize to Unix first to avoid errors later on.
-     * https://github.com/eslint/eslint/issues/5644
-     */
-    if (name.indexOf("\\") > -1) {
-        name = pathUtil.convertPathToPosix(name);
-    }
-
-    if (name.charAt(0) === "@") {
-
-        /*
-         * it's a scoped package
-         * package name is "eslint-config", or just a username
-         */
-        const scopedPackageShortcutRegex = new RegExp(`^(@[^/]+)(?:/(?:${prefix})?)?$`),
-            scopedPackageNameRegex = new RegExp(`^${prefix}(-|$)`);
-
-        if (scopedPackageShortcutRegex.test(name)) {
-            name = name.replace(scopedPackageShortcutRegex, `$1/${prefix}`);
-        } else if (!scopedPackageNameRegex.test(name.split("/")[1])) {
-
-            /*
-             * for scoped packages, insert the eslint-config after the first / unless
-             * the path is already @scope/eslint or @scope/eslint-config-xxx
-             */
-            name = name.replace(/^@([^/]+)\/(.*)$/, `@$1/${prefix}-$2`);
-        }
-    } else if (name.indexOf(`${prefix}-`) !== 0) {
-        name = `${prefix}-${name}`;
-    }
-
-    return name;
-}
-
 /**
  * Resolves a configuration file path into the fully-formed path, whether filename
  * or package name.
@@ -505,12 +461,12 @@ function resolve(filePath, relativeTo) {
         const pluginName = filePath.slice(7, filePath.lastIndexOf("/"));
         const configName = filePath.slice(filePath.lastIndexOf("/") + 1);
 
-        normalizedPackageName = normalizePackageName(pluginName, "eslint-plugin");
+        normalizedPackageName = naming.normalizePackageName(pluginName, "eslint-plugin");
         debug(`Attempting to resolve ${normalizedPackageName}`);
         filePath = resolver.resolve(normalizedPackageName, getLookupPath(relativeTo));
         return { filePath, configName, configFullName };
     }
-    normalizedPackageName = normalizePackageName(filePath, "eslint-config");
+    normalizedPackageName = naming.normalizePackageName(filePath, "eslint-config");
     debug(`Attempting to resolve ${normalizedPackageName}`);
     filePath = resolver.resolve(normalizedPackageName, getLookupPath(relativeTo));
     return { filePath, configFullName: filePath };
@@ -614,7 +570,6 @@ module.exports = {
     resolve,
     write,
     applyExtends,
-    normalizePackageName,
     CONFIG_FILES,
 
     /**
diff --git a/lib/config/plugins.js b/lib/config/plugins.js
index 9884f3603..c509c48fe 100644
--- a/lib/config/plugins.js
+++ b/lib/config/plugins.js
@@ -9,13 +9,13 @@
 //------------------------------------------------------------------------------
 
 const debug = require("debug")("eslint:plugins");
+const naming = require("../util/naming");
 
 //------------------------------------------------------------------------------
 // Private
 //------------------------------------------------------------------------------
 
-const PLUGIN_NAME_PREFIX = "eslint-plugin-",
-    NAMESPACE_REGEX = /^@.*\//i;
+const PLUGIN_NAME_PREFIX = "eslint-plugin-";
 
 //------------------------------------------------------------------------------
 // Public Interface
@@ -37,33 +37,6 @@ class Plugins {
         this._rules = rulesContext;
     }
 
-    /**
-     * Removes the prefix `eslint-plugin-` from a plugin name.
-     * @param {string} pluginName The name of the plugin which may have the prefix.
-     * @returns {string} The name of the plugin without prefix.
-     */
-    static removePrefix(pluginName) {
-        return pluginName.startsWith(PLUGIN_NAME_PREFIX) ? pluginName.slice(PLUGIN_NAME_PREFIX.length) : pluginName;
-    }
-
-    /**
-     * Gets the scope (namespace) of a plugin.
-     * @param {string} pluginName The name of the plugin which may have the prefix.
-     * @returns {string} The name of the plugins namepace if it has one.
-     */
-    static getNamespace(pluginName) {
-        return pluginName.match(NAMESPACE_REGEX) ? pluginName.match(NAMESPACE_REGEX)[0] : "";
-    }
-
-    /**
-     * Removes the namespace from a plugin name.
-     * @param {string} pluginName The name of the plugin which may have the prefix.
-     * @returns {string} The name of the plugin without the namespace.
-     */
-    static removeNamespace(pluginName) {
-        return pluginName.replace(NAMESPACE_REGEX, "");
-    }
-
     /**
      * Defines a plugin with a given name rather than loading from disk.
      * @param {string} pluginName The name of the plugin to load.
@@ -71,9 +44,9 @@ class Plugins {
      * @returns {void}
      */
     define(pluginName, plugin) {
-        const pluginNamespace = Plugins.getNamespace(pluginName),
-            pluginNameWithoutNamespace = Plugins.removeNamespace(pluginName),
-            pluginNameWithoutPrefix = Plugins.removePrefix(pluginNameWithoutNamespace),
+        const pluginNamespace = naming.getNamespaceFromTerm(pluginName),
+            pluginNameWithoutNamespace = naming.removeNamespaceFromTerm(pluginName),
+            pluginNameWithoutPrefix = naming.removePrefixFromTerm(PLUGIN_NAME_PREFIX, pluginNameWithoutNamespace),
             shortName = pluginNamespace + pluginNameWithoutPrefix;
 
         // load up environments and rules
@@ -106,9 +79,9 @@ class Plugins {
      * @throws {Error} If the plugin cannot be loaded.
      */
     load(pluginName) {
-        const pluginNamespace = Plugins.getNamespace(pluginName),
-            pluginNameWithoutNamespace = Plugins.removeNamespace(pluginName),
-            pluginNameWithoutPrefix = Plugins.removePrefix(pluginNameWithoutNamespace),
+        const pluginNamespace = naming.getNamespaceFromTerm(pluginName),
+            pluginNameWithoutNamespace = naming.removeNamespaceFromTerm(pluginName),
+            pluginNameWithoutPrefix = naming.removePrefixFromTerm(PLUGIN_NAME_PREFIX, pluginNameWithoutNamespace),
             shortName = pluginNamespace + pluginNameWithoutPrefix,
             longName = pluginNamespace + PLUGIN_NAME_PREFIX + pluginNameWithoutPrefix;
         let plugin = null;
diff --git a/lib/formatters/junit.js b/lib/formatters/junit.js
index 069ed5ff8..77d548f38 100644
--- a/lib/formatters/junit.js
+++ b/lib/formatters/junit.js
@@ -39,22 +39,28 @@ module.exports = function(results) {
 
         const messages = result.messages;
 
-        output += `<testsuite package="org.eslint" time="0" tests="${messages.length}" errors="${messages.length}" name="${result.filePath}">\n`;
-        messages.forEach(message => {
-            const type = message.fatal ? "error" : "failure";
+        if (messages.length > 0) {
+            output += `<testsuite package="org.eslint" time="0" tests="${messages.length}" errors="${messages.length}" name="${result.filePath}">\n`;
+            messages.forEach(message => {
+                const type = message.fatal ? "error" : "failure";
 
-            output += `<testcase time="0" name="org.eslint.${message.ruleId || "unknown"}">`;
-            output += `<${type} message="${xmlEscape(message.message || "")}">`;
-            output += "<![CDATA[";
-            output += `line ${message.line || 0}, col `;
-            output += `${message.column || 0}, ${getMessageType(message)}`;
-            output += ` - ${xmlEscape(message.message || "")}`;
-            output += (message.ruleId ? ` (${message.ruleId})` : "");
-            output += "]]>";
-            output += `</${type}>`;
-            output += "</testcase>\n";
-        });
-        output += "</testsuite>\n";
+                output += `<testcase time="0" name="org.eslint.${message.ruleId || "unknown"}">`;
+                output += `<${type} message="${xmlEscape(message.message || "")}">`;
+                output += "<![CDATA[";
+                output += `line ${message.line || 0}, col `;
+                output += `${message.column || 0}, ${getMessageType(message)}`;
+                output += ` - ${xmlEscape(message.message || "")}`;
+                output += (message.ruleId ? ` (${message.ruleId})` : "");
+                output += "]]>";
+                output += `</${type}>`;
+                output += "</testcase>\n";
+            });
+            output += "</testsuite>\n";
+        } else {
+            output += `<testsuite package="org.eslint" time="0" tests="1" errors="0" name="${result.filePath}">\n`;
+            output += `<testcase time="0" name="${result.filePath}" />\n`;
+            output += "</testsuite>\n";
+        }
 
     });
 
diff --git a/lib/util/naming.js b/lib/util/naming.js
new file mode 100644
index 000000000..dcac81bbd
--- /dev/null
+++ b/lib/util/naming.js
@@ -0,0 +1,112 @@
+/**
+ * @fileoverview Common helpers for naming of plugins, formatters and configs
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const pathUtil = require("../util/path-util");
+
+//------------------------------------------------------------------------------
+// Private
+//------------------------------------------------------------------------------
+
+const NAMESPACE_REGEX = /^@.*\//i;
+
+/**
+ * Brings package name to correct format based on prefix
+ * @param {string} name The name of the package.
+ * @param {string} prefix Can be either "eslint-plugin", "eslint-config" or "eslint-formatter"
+ * @returns {string} Normalized name of the package
+ * @private
+ */
+function normalizePackageName(name, prefix) {
+
+    /**
+     * On Windows, name can come in with Windows slashes instead of Unix slashes.
+     * Normalize to Unix first to avoid errors later on.
+     * https://github.com/eslint/eslint/issues/5644
+     */
+    if (name.indexOf("\\") > -1) {
+        name = pathUtil.convertPathToPosix(name);
+    }
+
+    if (name.charAt(0) === "@") {
+
+        /**
+         * it's a scoped package
+         * package name is the prefix, or just a username
+         */
+        const scopedPackageShortcutRegex = new RegExp(`^(@[^/]+)(?:/(?:${prefix})?)?$`),
+            scopedPackageNameRegex = new RegExp(`^${prefix}(-|$)`);
+
+        if (scopedPackageShortcutRegex.test(name)) {
+            name = name.replace(scopedPackageShortcutRegex, `$1/${prefix}`);
+        } else if (!scopedPackageNameRegex.test(name.split("/")[1])) {
+
+            /**
+             * for scoped packages, insert the prefix after the first / unless
+             * the path is already @scope/eslint or @scope/eslint-xxx-yyy
+             */
+            name = name.replace(/^@([^/]+)\/(.*)$/, `@$1/${prefix}-$2`);
+        }
+    } else if (name.indexOf(`${prefix}-`) !== 0) {
+        name = `${prefix}-${name}`;
+    }
+
+    return name;
+}
+
+/**
+ * Removes the prefix from a term.
+ * @param {string} prefix The prefix to remove.
+ * @param {string} term The term which may have the prefix.
+ * @returns {string} The term without prefix.
+ */
+function removePrefixFromTerm(prefix, term) {
+    return term.startsWith(prefix) ? term.slice(prefix.length) : term;
+}
+
+/**
+ * Adds a prefix to a term.
+ * @param {string} prefix The prefix to add.
+ * @param {string} term The term which may not have the prefix.
+ * @returns {string} The term with prefix.
+ */
+function addPrefixToTerm(prefix, term) {
+    return term.startsWith(prefix) ? term : `${prefix}${term}`;
+}
+
+/**
+ * Gets the scope (namespace) of a term.
+ * @param {string} term The term which may have the namespace.
+ * @returns {string} The namepace of the term if it has one.
+ */
+function getNamespaceFromTerm(term) {
+    const match = term.match(NAMESPACE_REGEX);
+
+    return match ? match[0] : "";
+}
+
+/**
+ * Removes the namespace from a term.
+ * @param {string} term The term which may have the namespace.
+ * @returns {string} The name of the plugin without the namespace.
+ */
+function removeNamespaceFromTerm(term) {
+    return term.replace(NAMESPACE_REGEX, "");
+}
+
+//------------------------------------------------------------------------------
+// Public Interface
+//------------------------------------------------------------------------------
+
+module.exports = {
+    normalizePackageName,
+    removePrefixFromTerm,
+    addPrefixToTerm,
+    getNamespaceFromTerm,
+    removeNamespaceFromTerm
+};
diff --git a/package.json b/package.json
index 46dc70e47..da71cf6d0 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,7 @@
   "homepage": "https://eslint.org",
   "bugs": "https://github.com/eslint/eslint/issues/",
   "dependencies": {
-    "ajv": "^5.2.0",
+    "ajv": "^5.3.0",
     "babel-code-frame": "^6.22.0",
     "chalk": "^2.1.0",
     "concat-stream": "^1.6.0",
@@ -55,7 +55,7 @@
     "inquirer": "^3.0.6",
     "is-resolvable": "^1.0.0",
     "js-yaml": "^3.9.1",
-    "json-stable-stringify": "^1.0.1",
+    "json-stable-stringify-without-jsonify": "^1.0.1",
     "levn": "^0.3.0",
     "lodash": "^4.17.4",
     "minimatch": "^3.0.2",


Test patch:
diff --git a/tests/fixtures/cli-engine/node_modules/@somenamespace/eslint-formatter-foo/index.js b/tests/fixtures/cli-engine/node_modules/@somenamespace/eslint-formatter-foo/index.js
new file mode 100644
index 000000000..0a55f349a
--- /dev/null
+++ b/tests/fixtures/cli-engine/node_modules/@somenamespace/eslint-formatter-foo/index.js
@@ -0,0 +1,1 @@
+module.exports = function() {};
diff --git a/tests/fixtures/cli-engine/node_modules/eslint-formatter-bar/index.js b/tests/fixtures/cli-engine/node_modules/eslint-formatter-bar/index.js
new file mode 100644
index 000000000..0a55f349a
--- /dev/null
+++ b/tests/fixtures/cli-engine/node_modules/eslint-formatter-bar/index.js
@@ -0,0 +1,1 @@
+module.exports = function() {};
diff --git a/tests/lib/cli-engine.js b/tests/lib/cli-engine.js
index 81d00a96e..c924ba297 100644
--- a/tests/lib/cli-engine.js
+++ b/tests/lib/cli-engine.js
@@ -2689,6 +2689,42 @@ describe("CLIEngine", () => {
             assert.isFunction(formatter);
         });
 
+        it("should return a function when a formatter prefixed with eslint-formatter is requested", () => {
+            const engine = new CLIEngine({
+                    cwd: getFixturePath("cli-engine")
+                }),
+                formatter = engine.getFormatter("bar");
+
+            assert.isFunction(formatter);
+        });
+
+        it("should return a function when a formatter is requested, also when the eslint-formatter prefix is included in the format argument", () => {
+            const engine = new CLIEngine({
+                    cwd: getFixturePath("cli-engine")
+                }),
+                formatter = engine.getFormatter("eslint-formatter-bar");
+
+            assert.isFunction(formatter);
+        });
+
+        it("should return a function when a formatter is requested within a scoped npm package", () => {
+            const engine = new CLIEngine({
+                    cwd: getFixturePath("cli-engine")
+                }),
+                formatter = engine.getFormatter("@somenamespace/foo");
+
+            assert.isFunction(formatter);
+        });
+
+        it("should return a function when a formatter is requested within a scoped npm package, also when the eslint-formatter prefix is included in the format argument", () => {
+            const engine = new CLIEngine({
+                    cwd: getFixturePath("cli-engine")
+                }),
+                formatter = engine.getFormatter("@somenamespace/eslint-formatter-foo");
+
+            assert.isFunction(formatter);
+        });
+
         it("should return null when a customer formatter doesn't exist", () => {
             const engine = new CLIEngine(),
                 formatterPath = getFixturePath("formatters", "doesntexist.js");
diff --git a/tests/lib/config/config-file.js b/tests/lib/config/config-file.js
index 4cdf22c9a..91c6508d4 100644
--- a/tests/lib/config/config-file.js
+++ b/tests/lib/config/config-file.js
@@ -1175,26 +1175,6 @@ describe("ConfigFile", () => {
 
     });
 
-    describe("normalizePackageName()", () => {
-
-        leche.withData([
-            ["foo", "eslint-config-foo"],
-            ["eslint-config-foo", "eslint-config-foo"],
-            ["@z/foo", "@z/eslint-config-foo"],
-            ["@z\\foo", "@z/eslint-config-foo"],
-            ["@z\\foo\\bar.js", "@z/eslint-config-foo/bar.js"],
-            ["@z/eslint-config", "@z/eslint-config"],
-            ["@z/eslint-config-foo", "@z/eslint-config-foo"]
-        ], (input, expected) => {
-            it(`should return ${expected} when passed ${input}`, () => {
-                const result = ConfigFile.normalizePackageName(input, "eslint-config");
-
-                assert.strictEqual(result, expected);
-            });
-        });
-
-    });
-
     describe("write()", () => {
 
         let sandbox,
diff --git a/tests/lib/config/plugins.js b/tests/lib/config/plugins.js
index cbc6d9a4c..37b96c048 100644
--- a/tests/lib/config/plugins.js
+++ b/tests/lib/config/plugins.js
@@ -241,35 +241,4 @@ describe("Plugins", () => {
         });
 
     });
-
-    describe("removePrefix()", () => {
-        it("should remove common prefix when passed a plugin name  with a prefix", () => {
-            const pluginName = Plugins.removePrefix("eslint-plugin-test");
-
-            assert.strictEqual(pluginName, "test");
-        });
-
-        it("should not modify plugin name when passed a plugin name without a prefix", () => {
-            const pluginName = Plugins.removePrefix("test");
-
-            assert.strictEqual(pluginName, "test");
-        });
-    });
-
-    describe("getNamespace()", () => {
-        it("should remove namepace when passed with namepace", () => {
-            const namespace = Plugins.getNamespace("@namepace/eslint-plugin-test");
-
-            assert.strictEqual(namespace, "@namepace/");
-        });
-    });
-
-    describe("removeNamespace()", () => {
-        it("should remove namepace when passed with namepace", () => {
-            const namespace = Plugins.removeNamespace("@namepace/eslint-plugin-test");
-
-            assert.strictEqual(namespace, "eslint-plugin-test");
-        });
-    });
-
 });
diff --git a/tests/lib/formatters/junit.js b/tests/lib/formatters/junit.js
index e0e0bfc37..c6995755d 100644
--- a/tests/lib/formatters/junit.js
+++ b/tests/lib/formatters/junit.js
@@ -195,7 +195,20 @@ describe("formatter:junit", () => {
         it("should return 2 <testsuite>", () => {
             const result = formatter(code);
 
-            assert.strictEqual(result.replace(/\n/g, ""), "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites><testsuite package=\"org.eslint\" time=\"0\" tests=\"1\" errors=\"1\" name=\"foo.js\"><testcase time=\"0\" name=\"org.eslint.foo\"><failure message=\"Unexpected foo.\"><![CDATA[line 5, col 10, Warning - Unexpected foo. (foo)]]></failure></testcase></testsuite><testsuite package=\"org.eslint\" time=\"0\" tests=\"0\" errors=\"0\" name=\"bar.js\"></testsuite></testsuites>");
+            assert.strictEqual(result.replace(/\n/g, ""), "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites><testsuite package=\"org.eslint\" time=\"0\" tests=\"1\" errors=\"1\" name=\"foo.js\"><testcase time=\"0\" name=\"org.eslint.foo\"><failure message=\"Unexpected foo.\"><![CDATA[line 5, col 10, Warning - Unexpected foo. (foo)]]></failure></testcase></testsuite><testsuite package=\"org.eslint\" time=\"0\" tests=\"1\" errors=\"0\" name=\"bar.js\"><testcase time=\"0\" name=\"bar.js\" /></testsuite></testsuites>");
+        });
+    });
+
+    describe("when passed a file with no errors", () => {
+        const code = [{
+            filePath: "foo.js",
+            messages: []
+        }];
+
+        it("should print a passing <testcase>", () => {
+            const result = formatter(code);
+
+            assert.strictEqual(result.replace(/\n/g, ""), "<?xml version=\"1.0\" encoding=\"utf-8\"?><testsuites><testsuite package=\"org.eslint\" time=\"0\" tests=\"1\" errors=\"0\" name=\"foo.js\"><testcase time=\"0\" name=\"foo.js\" /></testsuite></testsuites>");
         });
     });
 });
diff --git a/tests/lib/rules/object-property-newline.js b/tests/lib/rules/object-property-newline.js
index 350ecfb17..2d0df640f 100644
--- a/tests/lib/rules/object-property-newline.js
+++ b/tests/lib/rules/object-property-newline.js
@@ -24,7 +24,9 @@ ruleTester.run("object-property-newline", rule, {
 
         // default-case
         "var obj = {\nk1: 'val1',\nk2: 'val2',\nk3: 'val3',\nk4: 'val4'\n};",
+        "var obj = {\nk1: 'val1'\n, k2: 'val2'\n, k3: 'val3'\n, k4: 'val4'\n};",
         "var obj = { k1: 'val1',\nk2: 'val2',\nk3: 'val3',\nk4: 'val4' };",
+        "var obj = { k1: 'val1'\n, k2: 'val2'\n, k3: 'val3'\n, k4: 'val4' };",
         "var obj = { k1: 'val1' };",
         "var obj = {\nk1: 'val1'\n};",
         "var obj = {};",
@@ -173,6 +175,18 @@ ruleTester.run("object-property-newline", rule, {
                 }
             ]
         },
+        {
+            code: "var obj = { k1: 'val1',\nk2: [\n'val2a', 'val2b', 'val2c'\n], k3: 'val3' };",
+            output: "var obj = { k1: 'val1',\nk2: [\n'val2a', 'val2b', 'val2c'\n],\nk3: 'val3' };",
+            errors: [
+                {
+                    message: "Object properties must go on a new line.",
+                    type: "ObjectExpression",
+                    line: 4,
+                    column: 4
+                }
+            ]
+        },
         {
             code: "var obj = { k1: 'val1', [\nk2]: 'val2' };",
             output: "var obj = { k1: 'val1',\n[\nk2]: 'val2' };",
@@ -378,6 +392,25 @@ ruleTester.run("object-property-newline", rule, {
                 }
             ]
         },
+        {
+            code: "var obj = {\nk1:\n'val1', k2: 'val2', k3:\n'val3'\n};",
+            output: "var obj = {\nk1:\n'val1',\nk2: 'val2',\nk3:\n'val3'\n};",
+            options: [{ allowMultiplePropertiesPerLine: true }],
+            errors: [
+                {
+                    message: "Object properties must go on a new line if they aren't all on the same line.",
+                    type: "ObjectExpression",
+                    line: 3,
+                    column: 9
+                },
+                {
+                    message: "Object properties must go on a new line if they aren't all on the same line.",
+                    type: "ObjectExpression",
+                    line: 3,
+                    column: 21
+                }
+            ]
+        },
         {
             code: "var obj = {k1: [\n'foo',\n'bar'\n], k2: 'val1'};",
             output: "var obj = {k1: [\n'foo',\n'bar'\n],\nk2: 'val1'};",
@@ -423,6 +456,19 @@ ruleTester.run("object-property-newline", rule, {
                 }
             ]
         },
+        {
+            code: "var obj = { k1: 'val1',\nk2: [\n'val2a', 'val2b', 'val2c'\n], k3: 'val3' };",
+            output: "var obj = { k1: 'val1',\nk2: [\n'val2a', 'val2b', 'val2c'\n],\nk3: 'val3' };",
+            options: [{ allowMultiplePropertiesPerLine: true }],
+            errors: [
+                {
+                    message: "Object properties must go on a new line if they aren't all on the same line.",
+                    type: "ObjectExpression",
+                    line: 4,
+                    column: 4
+                }
+            ]
+        },
         {
             code: "var obj = { [\nk1]: 'val1', k2: 'val2' };",
             output: "var obj = { [\nk1]: 'val1',\nk2: 'val2' };",
diff --git a/tests/lib/util/naming.js b/tests/lib/util/naming.js
new file mode 100644
index 000000000..e57f1d27a
--- /dev/null
+++ b/tests/lib/util/naming.js
@@ -0,0 +1,82 @@
+/**
+ * @fileoverview Tests for naming util
+ */
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const assert = require("chai").assert,
+    leche = require("leche"),
+    naming = require("../../../lib/util/naming");
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+describe("naming", () => {
+    describe("normalizePackageName()", () => {
+
+        leche.withData([
+            ["foo", "eslint-config-foo"],
+            ["eslint-config-foo", "eslint-config-foo"],
+            ["@z/foo", "@z/eslint-config-foo"],
+            ["@z\\foo", "@z/eslint-config-foo"],
+            ["@z\\foo\\bar.js", "@z/eslint-config-foo/bar.js"],
+            ["@z/eslint-config", "@z/eslint-config"],
+            ["@z/eslint-config-foo", "@z/eslint-config-foo"]
+        ], (input, expected) => {
+            it(`should return ${expected} when passed ${input}`, () => {
+                const result = naming.normalizePackageName(input, "eslint-config");
+
+                assert.strictEqual(result, expected);
+            });
+        });
+
+    });
+
+    describe("removePrefixFromTerm()", () => {
+        it("should remove prefix when passed a term with a prefix", () => {
+            const pluginName = naming.removePrefixFromTerm("eslint-plugin-", "eslint-plugin-test");
+
+            assert.strictEqual(pluginName, "test");
+        });
+
+        it("should not modify term when passed a term without a prefix", () => {
+            const pluginName = naming.removePrefixFromTerm("eslint-plugin-", "test");
+
+            assert.strictEqual(pluginName, "test");
+        });
+    });
+
+    describe("addPrefixToTerm()", () => {
+        it("should add prefix when passed a term without a prefix", () => {
+            const pluginName = naming.addPrefixToTerm("eslint-plugin-", "test");
+
+            assert.strictEqual(pluginName, "eslint-plugin-test");
+        });
+
+        it("should not modify term when passed a term with a prefix", () => {
+            const pluginName = naming.addPrefixToTerm("eslint-plugin-", "eslint-plugin-test");
+
+            assert.strictEqual(pluginName, "eslint-plugin-test");
+        });
+    });
+
+    describe("getNamespaceFromTerm()", () => {
+        it("should remove namepace when passed with namepace", () => {
+            const namespace = naming.getNamespaceFromTerm("@namepace/eslint-plugin-test");
+
+            assert.strictEqual(namespace, "@namepace/");
+        });
+    });
+
+    describe("removeNamespaceFromTerm()", () => {
+        it("should remove namepace when passed with namepace", () => {
+            const namespace = naming.removeNamespaceFromTerm("@namepace/eslint-plugin-test");
+
+            assert.strictEqual(namespace, "eslint-plugin-test");
+        });
+    });
+});