diff --git a/package.json b/package.json index 77f44e5e12a0..0d16450bf56f 100644 --- a/package.json +++ b/package.json @@ -94,6 +94,7 @@ "load-perf": "^0.2.0", "markdownlint": "^0.1.0", "mocha": "^2.4.5", + "mock-fs": "^3.9.0", "npm-license": "^0.3.2", "phantomjs-prebuilt": "^2.1.7", "proxyquire": ">=1.0.0 <1.7.5", diff --git a/tests/fixtures/config-hierarchy/file-structure.json b/tests/fixtures/config-hierarchy/file-structure.json new file mode 100644 index 000000000000..9ebe7de30878 --- /dev/null +++ b/tests/fixtures/config-hierarchy/file-structure.json @@ -0,0 +1,110 @@ +{ + "broken": { + ".eslintrc": "env: \r\n node: true\r\nrules:\r\n quotes: [2, \"double\"]\r\n", + "add-conf.yaml": "rules:\r\n semi: [1, \"never\"]\r\n", + "console-wrong-quotes-node.js": "\r\n/*eslint-env node*/\r\n\r\nconsole.log('bar');\r\n", + "console-wrong-quotes.js": "console.log('bar');\r\n", + "override-conf.yaml": "rules:\r\n quotes: 0\r\n", + "override-env-conf.yaml": "extends: \"eslint:recommended\"\nenv:\n node: true\nrules:\n no-mixed-requires: 0\n", + "package.json": "{\r\n \"name\": \"\",\r\n \"version\": \"\",\r\n \"eslintConfig\": {}\r\n}\r\n", + "plugins": { + ".eslintrc": "plugins: \r\n [\"example\"]\r\n ", + "console-wrong-quotes.js": "console.log('bar');\r\n" + }, + "plugins2": { + ".eslintrc": "plugins: \r\n [\"example\", \"eslint-plugin-test\"]\r\n ", + "console-wrong-quotes.js": "console.log('bar');\r\n" + }, + "process-exit.js": "process.exit(0);\r\n", + "subbroken": { + ".eslintrc": "rules:\r\n no-console: 1\r\n quotes: [2, \"single\"]\r\n", + "console-wrong-quotes.js": "console.log('bar');\r\n", + "subsubbroken": { + ".eslintrc": "rules:\r\n no-console: 0\r\n quotes: [1, \"double\"]\r\n", + "console-wrong-quotes.js": "console.log('bar');\r\n" + } + }, + "wrong-quotes.js": "// function is necessary to avoid any other errors\r\nfunction foo(bar) {\r\n \"use strict\";\r\n return bar;\r\n}\r\n\r\nfoo('bar');\r\n" + }, + "envs": { + ".eslintrc.json": "{\n \"root\": true,\n \"env\": { \"node\": true }\n}\n", + "sub": { + ".eslintrc.json": "{\n \"env\": { \"node\": false, \"browser\": true }\n}\n", + "foo.js": "foo;\n" + } + }, + "fileexts": { + ".eslintrc.js": "module.exports = {\n rules: {\n semi: [2, \"always\"]\n },\n root: true\n};\n", + "subdir": { + ".eslintrc.yml": "rules:\n eqeqeq: 2\n", + "subsubdir": { + ".eslintrc.json": "{\n \"env\": {\n \"browser\": true\n }\n}\n" + } + } + }, + "overwrite-ecmaFeatures": { + ".eslintrc": "{\n \"ecmaFeatures\": {\n \"globalReturn\": false\n }\n}\n", + "child": { + ".eslintrc": "{\n \"env\": {\n \"commonjs\": true\n }\n}\n" + } + }, + "packagejson": { + ".eslintrc": "rules:\r\n quotes: [2, \"double\"]\r\n", + "package.json": "{\r\n \"name\": \"\",\r\n \"version\": \"\",\r\n \"eslintConfig\": {\r\n \"rules\": {\r\n \"quotes\": [1, \"single\"]\r\n }\r\n }\r\n}\r\n", + "subdir": { + "package.json": "{\r\n \"name\": \"\",\r\n \"version\": \"\",\r\n \"eslintConfig\": {\r\n \"rules\": {\r\n \"quotes\": [1, \"double\"]\r\n }\r\n }\r\n}\r\n", + "subsubdir": { + "package.json": "{\r\n \"name\": \"\",\r\n \"version\": \"\",\r\n \"eslintConfig\": {\r\n \"rules\": {\r\n \"quotes\": [2, \"single\"]\r\n }\r\n }\r\n}\r\n", + "subsubsubdir": { + "package.json": "{\r\n \"name\": \"\",\r\n \"version\": \"\",\r\n \"eslintConfig\": {\r\n \"rules\": {\r\n \"quotes\": [2, \"double\"]\r\n }\r\n }\r\n}\r\n", + "wrong-quotes.js": "var str = 'quotes';\r\n" + }, + "wrong-quotes.js": "var str = 'quotes';\r\n" + }, + "wrong-quotes.js": "var str = 'quotes';\r\n" + }, + "wrong-quotes.js": "var str = 'quotes';\r\n" + }, + "personal-config": { + "home-folder-with-packagejson": { + "package.json": "{\n \"name\": \"foo\",\n \"version\": \"1.0.0\"\n}" + }, + "home-folder": { + ".eslintrc.json": "{\n \"rules\": {\n \"home-folder-rule\": 2\n }\n}\n", + "project": { + ".eslintrc": "{\n \"rules\": {\n \"project-level-rule\": 2\n }\n}\n", + "package.json": "{}\n" + } + }, + "project-with-config": { + ".eslintrc": "{\n \"rules\": {\n \"project-level-rule\": 2\n }\n}\n", + "package.json": "{}\n", + "subfolder": { + ".eslintrc": "{\n \"rules\": {\n \"subfolder-level-rule\": 2\n }\n}\n" + } + }, + "project-without-config": { + "package.json": "{}\n" + } + }, + "root-true": { + "parent": { + ".eslintrc": "{\n \"rules\": {\n \"semi\": [2, \"always\"],\n \"quotes\": [2, \"single\"]\n },\n \"extends\": [\n \"eslint-config-test\"\n ]\n}\n", + "root": { + ".eslintrc": "{\n \"root\": true,\n \"rules\": {\n \"semi\": [2, \"never\"]\n }\n}\n", + "wrong-semi.js": "var str = 'quotes'\n" + } + } + }, + "shared": { + "a": { + ".eslintrc": "{\r\n \"extends\": \"example\",\r\n \"rules\": {\r\n \"quotes\": [ 2, \"single\" ]\r\n }\r\n}\r\n", + "index.js": "" + }, + "b": { + ".eslintrc": "{\r\n \"extends\": \"example\"\r\n}\r\n", + "index.js": "" + } + }, + "quotes-error.json": "{\r\n \"rules\": {\n\"quotes\": [2, \"double\"]\n }\n}\r\n" +} diff --git a/tests/lib/config.js b/tests/lib/config.js index 51a71ada8abc..3a22e8bda872 100644 --- a/tests/lib/config.js +++ b/tests/lib/config.js @@ -16,7 +16,10 @@ var assert = require("chai").assert, Config = require("../../lib/config"), environments = require("../../conf/environments"), sinon = require("sinon"), - proxyquire = require("proxyquire"); + proxyquire = require("proxyquire"), + mockFs = require("mock-fs"); + +var DIRECTORY_CONFIG_HIERARCHY = require("../fixtures/config-hierarchy/file-structure.json"); require("shelljs/global"); proxyquire = proxyquire.noCallThru().noPreserveCache(); @@ -134,17 +137,46 @@ describe("Config", function() { describe("findLocalConfigFiles()", function() { + /** + * Returns the path inside of the fixture directory. + * @returns {string} The path inside the fixture directory. + * @private + */ + function getFakeFixturePath() { + var args = Array.prototype.slice.call(arguments); + + args.unshift("config-hierarchy"); + args.unshift("fixtures"); + args.unshift("eslint"); + args.unshift(process.cwd()); + return path.join.apply(path, args); + } + + before(function() { + mockFs({ + eslint: { + fixtures: { + "config-hierarchy": DIRECTORY_CONFIG_HIERARCHY + } + } + }); + }); + + after(function() { + mockFs.restore(); + }); + it("should return the path when an .eslintrc file is found", function() { var configHelper = new Config(), - expected = getFixturePath("broken", ".eslintrc"), - actual = configHelper.findLocalConfigFiles(getFixturePath("broken"))[0]; + expected = getFakeFixturePath("broken", ".eslintrc"), + actual = configHelper.findLocalConfigFiles(getFakeFixturePath("broken"))[0]; assert.equal(actual, expected); }); it("should return an empty array when an .eslintrc file is not found", function() { var configHelper = new Config(), - actual = configHelper.findLocalConfigFiles(getFixturePath()); + actual = configHelper.findLocalConfigFiles(getFakeFixturePath()); assert.isArray(actual); assert.lengthOf(actual, 0); @@ -152,9 +184,9 @@ describe("Config", function() { it("should return package.json only when no other config files are found", function() { var configHelper = new Config(), - expected0 = getFixturePath("packagejson", "subdir", "package.json"), - expected1 = getFixturePath("packagejson", ".eslintrc"), - actual = configHelper.findLocalConfigFiles(getFixturePath("packagejson", "subdir")); + expected0 = getFakeFixturePath("packagejson", "subdir", "package.json"), + expected1 = getFakeFixturePath("packagejson", ".eslintrc"), + actual = configHelper.findLocalConfigFiles(getFakeFixturePath("packagejson", "subdir")); assert.isArray(actual); assert.lengthOf(actual, 2); @@ -164,10 +196,10 @@ describe("Config", function() { it("should return the only one config file even if there are multiple found", function() { var configHelper = new Config(), - expected = getFixturePath("broken", ".eslintrc"), + expected = getFakeFixturePath("broken", ".eslintrc"), // The first element of the array is the .eslintrc in the same directory. - actual = configHelper.findLocalConfigFiles(getFixturePath("broken")); + actual = configHelper.findLocalConfigFiles(getFakeFixturePath("broken")); assert.equal(actual.length, 1); assert.equal(actual, expected); @@ -176,19 +208,19 @@ describe("Config", function() { it("should return all possible files when multiple are found", function() { var configHelper = new Config(), expected = [ - getFixturePath("fileexts/subdir/subsubdir/", ".eslintrc.json"), - getFixturePath("fileexts/subdir/", ".eslintrc.yml"), - getFixturePath("fileexts", ".eslintrc.js") + getFakeFixturePath("fileexts/subdir/subsubdir/", ".eslintrc.json"), + getFakeFixturePath("fileexts/subdir/", ".eslintrc.yml"), + getFakeFixturePath("fileexts", ".eslintrc.js") ], - actual = configHelper.findLocalConfigFiles(getFixturePath("fileexts/subdir/subsubdir")); + actual = configHelper.findLocalConfigFiles(getFakeFixturePath("fileexts/subdir/subsubdir")); assert.deepEqual(actual, expected); }); it("should return an empty array when a package.json file is not found", function() { var configHelper = new Config(), - actual = configHelper.findLocalConfigFiles(getFixturePath()); + actual = configHelper.findLocalConfigFiles(getFakeFixturePath()); assert.isArray(actual); assert.lengthOf(actual, 0); @@ -736,23 +768,62 @@ describe("Config", function() { describe("personal config file within home directory", function() { var getCwd; - beforeEach(function() { + /** + * Returns the path inside of the fixture directory. + * @returns {string} The path inside the fixture directory. + * @private + */ + function getFakeFixturePath() { + var args = Array.prototype.slice.call(arguments); + + args.unshift("config-hierarchy"); + args.unshift("fixtures"); + args.unshift("eslint"); + args.unshift(process.cwd()); + return path.join.apply(path, args); + } + + /** + * Mocks the file system for personal-config files + * @returns {undefined} + * @private + */ + function mockPersonalConfigFileSystem() { + mockFs({ + eslint: { + fixtures: { + "config-hierarchy": DIRECTORY_CONFIG_HIERARCHY + } + } + }); + } + + /** + * Mocks the current CWD path + * @param {string} fakeCWDPath - fake CWD path + * @returns {undefined} + * @private + */ + function mockCWDResponse(fakeCWDPath) { getCwd = sinon.stub(process, "cwd"); - }); + getCwd.returns(fakeCWDPath); + } afterEach(function() { getCwd.restore(); + mockFs.restore(); }); it("should load the personal config if no local config was found", function() { - var projectPath = getFixturePath("personal-config", "project-without-config"), - homePath = getFixturePath("personal-config", "home-folder"), - filePath = getFixturePath("personal-config", "project-without-config", "foo.js"); - - getCwd.returns(projectPath); + var projectPath = getFakeFixturePath("personal-config", "project-without-config"), + homePath = getFakeFixturePath("personal-config", "home-folder"), + filePath = getFakeFixturePath("personal-config", "project-without-config", "foo.js"); var StubbedConfig = proxyquire("../../lib/config", { "user-home": homePath }); + mockPersonalConfigFileSystem(); + mockCWDResponse(projectPath); + var config = new StubbedConfig({ cwd: process.cwd() }), actual = config.getConfig(filePath), expected = { @@ -769,14 +840,15 @@ describe("Config", function() { }); it("should ignore the personal config if a local config was found", function() { - var projectPath = getFixturePath("personal-config", "home-folder", "project"), - homePath = getFixturePath("personal-config", "home-folder"), - filePath = getFixturePath("personal-config", "home-folder", "project", "foo.js"); - - getCwd.returns(projectPath); + var projectPath = getFakeFixturePath("personal-config", "home-folder", "project"), + homePath = getFakeFixturePath("personal-config", "home-folder"), + filePath = getFakeFixturePath("personal-config", "home-folder", "project", "foo.js"); var StubbedConfig = proxyquire("../../lib/config", { "user-home": homePath }); + mockPersonalConfigFileSystem(); + mockCWDResponse(projectPath); + var config = new StubbedConfig({ cwd: process.cwd() }), actual = config.getConfig(filePath), expected = { @@ -793,15 +865,16 @@ describe("Config", function() { }); it("should ignore the personal config if config is passed through cli", function() { - var configPath = path.resolve(__dirname, "..", "fixtures", "configurations", "quotes-error.json"); - var projectPath = getFixturePath("personal-config", "project-without-config"), - homePath = getFixturePath("personal-config", "home-folder"), - filePath = getFixturePath("personal-config", "project-without-config", "foo.js"); - - getCwd.returns(projectPath); + var configPath = getFakeFixturePath("quotes-error.json"); + var projectPath = getFakeFixturePath("personal-config", "project-without-config"), + homePath = getFakeFixturePath("personal-config", "home-folder"), + filePath = getFakeFixturePath("personal-config", "project-without-config", "foo.js"); var StubbedConfig = proxyquire("../../lib/config", { "user-home": homePath }); + mockPersonalConfigFileSystem(); + mockCWDResponse(projectPath); + var config = new StubbedConfig({ configFile: configPath, cwd: process.cwd() }), actual = config.getConfig(filePath), expected = { @@ -818,14 +891,15 @@ describe("Config", function() { }); it("should have an empty config if no local config and no personal config was found", function() { - var projectPath = getFixturePath("personal-config", "project-without-config"), - homePath = getFixturePath("personal-config", "folder-does-not-exist"), - filePath = getFixturePath("personal-config", "project-without-config", "foo.js"); - - getCwd.returns(projectPath); + var projectPath = getFakeFixturePath("personal-config", "project-without-config"), + homePath = getFakeFixturePath("personal-config", "folder-does-not-exist"), + filePath = getFakeFixturePath("personal-config", "project-without-config", "foo.js"); var StubbedConfig = proxyquire("../../lib/config", { "user-home": homePath }); + mockPersonalConfigFileSystem(); + mockCWDResponse(projectPath); + var config = new StubbedConfig({ cwd: process.cwd() }), actual = config.getConfig(filePath), expected = { @@ -840,14 +914,15 @@ describe("Config", function() { }); it("should have an empty config if no local config was found and ~/package.json contains no eslintConfig section", function() { - var projectPath = getFixturePath("personal-config", "project-without-config"), - homePath = getFixturePath("personal-config", "home-folder-with-packagejson"), - filePath = getFixturePath("personal-config", "project-without-config", "foo.js"); - - getCwd.returns(projectPath); + var projectPath = getFakeFixturePath("personal-config", "project-without-config"), + homePath = getFakeFixturePath("personal-config", "home-folder-with-packagejson"), + filePath = getFakeFixturePath("personal-config", "project-without-config", "foo.js"); var StubbedConfig = proxyquire("../../lib/config", { "user-home": homePath }); + mockPersonalConfigFileSystem(); + mockCWDResponse(projectPath); + var config = new StubbedConfig({ cwd: process.cwd() }), actual = config.getConfig(filePath), expected = { @@ -862,12 +937,13 @@ describe("Config", function() { }); it("should still load the project config if the current working directory is the same as the home folder", function() { - var projectPath = getFixturePath("personal-config", "project-with-config"), - filePath = getFixturePath("personal-config", "project-with-config", "subfolder", "foo.js"); + var projectPath = getFakeFixturePath("personal-config", "project-with-config"), + filePath = getFakeFixturePath("personal-config", "project-with-config", "subfolder", "foo.js"); var StubbedConfig = proxyquire("../../lib/config", { "user-home": projectPath }); - getCwd.returns(projectPath); + mockPersonalConfigFileSystem(); + mockCWDResponse(projectPath); var config = new StubbedConfig({ cwd: process.cwd() }), actual = config.getConfig(filePath),