diff --git a/.azure-pipelines/job.yml b/.azure-pipelines/job.yml new file mode 100644 index 00000000000..2f8a5537387 --- /dev/null +++ b/.azure-pipelines/job.yml @@ -0,0 +1,37 @@ +parameters: + name: "" + displayName: "" + vmImage: "" + nodeVersion: "" + +jobs: + - job: ${{parameters.name}} + displayName: ${{parameters.displayName}} + pool: + vmImage: ${{parameters.vmImage}} + steps: + - task: NodeTool@0 + displayName: Install Node.js + inputs: + versionSpec: ${{parameters.nodeVersion}} + + - script: npm install + displayName: Install Packages + + - script: npm test + displayName: Test + + - task: PublishTestResults@2 + displayName: Process Test Results + condition: succeededOrFailed() + inputs: + testRunner: JUnit + testResultsFiles: $(System.DefaultWorkingDirectory)/test-results.xml + + - task: PublishCodeCoverageResults@1 + displayName: Process Coverage Results + condition: succeededOrFailed() + inputs: + codeCoverageTool: Cobertura + summaryFileLocation: $(System.DefaultWorkingDirectory)/coverage/cobertura-coverage.xml + reportDirectory: $(System.DefaultWorkingDirectory)/coverage diff --git a/.gitignore b/.gitignore index c65d8d57358..38b7ecd3bf0 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ versions.json .sublimelinterrc .eslint-release-info.json .nyc_output +/test-results.xml diff --git a/.nycrc b/.nycrc index d0f606dc2f6..040f2035d04 100644 --- a/.nycrc +++ b/.nycrc @@ -6,7 +6,8 @@ ], "reporter": [ "lcov", - "text-summary" + "text-summary", + "cobertura" ], "sourceMap": true } diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 40aa2a8a47d..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: node_js -node_js: - - "8" - - "10" - - "11" - - "12" -branches: - only: - - master - -# Run npm test always -script: - - "npm test" -after_success: - - 'if [ "$node_js" = "8" ]; then npm run coveralls; fi' -addons: - code_climate: - repo_token: 1945f7420d920a59f1ff8bf8d1a7b60ccd9e2838a692f73a5a74accd8df30146 diff --git a/Makefile.js b/Makefile.js index e70390ee092..536453bc524 100644 --- a/Makefile.js +++ b/Makefile.js @@ -544,7 +544,10 @@ target.test = function() { echo("Running unit tests"); - lastReturn = exec(`${getBinFile("nyc")} -- ${MOCHA} -R progress -t ${MOCHA_TIMEOUT} -c ${TEST_FILES}`); + // In CI (Azure Pipelines), use JUnit reporter. + const reporter = process.env.TF_BUILD ? "mocha-junit-reporter" : "progress"; + + lastReturn = exec(`${getBinFile("nyc")} -- ${MOCHA} -R ${reporter} -t ${MOCHA_TIMEOUT} -c ${TEST_FILES}`); if (lastReturn.code !== 0) { errors++; } diff --git a/README.md b/README.md index 6e5f3db76a6..efc9d7a48d9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,5 @@ [![NPM version][npm-image]][npm-url] -[![build status][travis-image]][travis-url] -[![Build status][appveyor-image]][appveyor-url] +[![Build Status](https://dev.azure.com/eslint/eslint/_apis/build/status/eslint.eslint?branchName=master)](https://dev.azure.com/eslint/eslint/_build/latest?definitionId=1&branchName=master) [![Downloads][downloads-image]][downloads-url] [![Bountysource](https://www.bountysource.com/badge/tracker?tracker_id=282608)](https://www.bountysource.com/trackers/282608-eslint?utm_source=282608&utm_medium=shield&utm_campaign=TRACKER_BADGE) [![Join the chat at https://gitter.im/eslint/eslint](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/eslint/eslint?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) @@ -271,11 +270,5 @@ The following companies, organizations, and individuals support ESLint's ongoing [npm-image]: https://img.shields.io/npm/v/eslint.svg?style=flat-square [npm-url]: https://www.npmjs.com/package/eslint -[travis-image]: https://img.shields.io/travis/eslint/eslint/master.svg?style=flat-square -[travis-url]: https://travis-ci.org/eslint/eslint -[appveyor-image]: https://ci.appveyor.com/api/projects/status/iwxmiobcvbw3b0av/branch/master?svg=true -[appveyor-url]: https://ci.appveyor.com/project/nzakas/eslint/branch/master -[coveralls-image]: https://img.shields.io/coveralls/eslint/eslint/master.svg?style=flat-square -[coveralls-url]: https://coveralls.io/r/eslint/eslint?branch=master [downloads-image]: https://img.shields.io/npm/dm/eslint.svg?style=flat-square [downloads-url]: https://www.npmjs.com/package/eslint diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 966263e293f..00000000000 --- a/appveyor.yml +++ /dev/null @@ -1,28 +0,0 @@ -# AppVeyor file -# http://www.appveyor.com/docs/appveyor-yml - -# Build version format -version: "{build}" - -# What combinations to test -environment: - matrix: - - nodejs_version: 8 - -branches: - only: - - master - -install: - # Get the latest stable version of Node.js - - ps: Install-Product node $env:nodejs_version - # install modules - - npm install - -build: off - -test_script: - - npm test - -matrix: - fast_finish: true # set this flag to immediately finish build once one of the jobs fails. diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 00000000000..13a2f38c494 --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,38 @@ +trigger: + - master + +jobs: + - template: .azure-pipelines/job.yml + parameters: + name: test_on_linux_node12 + displayName: Test on Node.js 12 (Linux) + vmImage: Ubuntu-16.04 + nodeVersion: 12.x + + - template: .azure-pipelines/job.yml + parameters: + name: test_on_linux_node10 + displayName: Test on Node.js 10 (Linux) + vmImage: Ubuntu-16.04 + nodeVersion: 10.x + + - template: .azure-pipelines/job.yml + parameters: + name: test_on_linux_node8 + displayName: Test on Node.js 8 (Linux) + vmImage: Ubuntu-16.04 + nodeVersion: 8.x + + - template: .azure-pipelines/job.yml + parameters: + name: test_on_windows_node12 + displayName: Test on Node.js 12 (Windows) + vmImage: Windows-2019 + nodeVersion: 12.x + + - template: .azure-pipelines/job.yml + parameters: + name: test_on_macos_node12 + displayName: Test on Node.js 12 (macOS) + vmImage: macOS-10.14 + nodeVersion: 12.x diff --git a/lib/rule-tester/rule-tester.js b/lib/rule-tester/rule-tester.js index 30445076a91..81f3a2fc4e7 100644 --- a/lib/rule-tester/rule-tester.js +++ b/lib/rule-tester/rule-tester.js @@ -123,6 +123,18 @@ function freezeDeeply(x) { } } +/** + * Replace control characters by `\u00xx` form. + * @param {string} text The text to sanitize. + * @returns {string} The sanitized text. + */ +function sanitize(text) { + return text.replace( + /[\u0000-\u001f]/gu, // eslint-disable-line no-control-regex + c => `\\u${c.codePointAt(0).toString(16).padStart(4, "0")}` + ); +} + //------------------------------------------------------------------------------ // Public Interface //------------------------------------------------------------------------------ @@ -613,7 +625,7 @@ class RuleTester { RuleTester.describe(ruleName, () => { RuleTester.describe("valid", () => { test.valid.forEach(valid => { - RuleTester.it(typeof valid === "object" ? valid.code : valid, () => { + RuleTester.it(sanitize(typeof valid === "object" ? valid.code : valid), () => { testValidTemplate(valid); }); }); @@ -621,7 +633,7 @@ class RuleTester { RuleTester.describe("invalid", () => { test.invalid.forEach(invalid => { - RuleTester.it(invalid.code, () => { + RuleTester.it(sanitize(invalid.code), () => { testInvalidTemplate(invalid); }); }); diff --git a/package.json b/package.json index f9a3e95568b..a4bf2eeaaf6 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,7 @@ "gensite": "node Makefile.js gensite", "webpack": "node Makefile.js webpack", "perf": "node Makefile.js perf", - "profile": "beefy tests/bench/bench.js --open -- -t brfs -t ./tests/bench/xform-rules.js -r espree", - "coveralls": "cat ./coverage/lcov.info | coveralls" + "profile": "beefy tests/bench/bench.js --open -- -t brfs -t ./tests/bench/xform-rules.js -r espree" }, "gitHooks": { "pre-commit": "lint-staged" @@ -93,7 +92,6 @@ "cheerio": "^0.22.0", "common-tags": "^1.8.0", "core-js": "^3.1.3", - "coveralls": "^3.0.3", "dateformat": "^3.0.3", "ejs": "^2.6.1", "eslint": "file:.", @@ -118,6 +116,7 @@ "markdownlint-cli": "^0.15.0", "metro-memory-fs": "^0.53.1", "mocha": "^6.1.2", + "mocha-junit-reporter": "^1.23.0", "npm-license": "^0.3.3", "nyc": "^13.3.0", "proxyquire": "^2.0.1", diff --git a/tests/lib/_utils.js b/tests/lib/_utils.js index d8b14b6a97c..3251a602360 100644 --- a/tests/lib/_utils.js +++ b/tests/lib/_utils.js @@ -5,6 +5,9 @@ "use strict"; +const path = require("path"); +const MemoryFs = require("metro-memory-fs"); + /** * Prevents leading spaces in a multiline template literal from appearing in the resulting string * @param {string[]} strings The strings in the template literal @@ -22,7 +25,93 @@ function unIndent(strings, ...values) { return lines.map(line => line.slice(minLineIndent)).join("\n"); } +// eslint-disable-next-line valid-jsdoc +/** + * Add support of `recursive` option. + * @param {import("fs")} fs The in-memory file system. + * @param {() => string} cwd The current working directory. + * @returns {void} + */ +function supportMkdirRecursiveOption(fs, cwd) { + const { mkdirSync } = fs; + + fs.mkdirSync = (filePath, options) => { + if (typeof options === "object" && options !== null) { + if (options.recursive) { + const absolutePath = path.resolve(cwd(), filePath); + const parentPath = path.dirname(absolutePath); + + if ( + parentPath && + parentPath !== absolutePath && + !fs.existsSync(parentPath) + ) { + fs.mkdirSync(parentPath, options); + } + } + mkdirSync(filePath, options.mode); + } else { + mkdirSync(filePath, options); + } + }; +} + +// eslint-disable-next-line valid-jsdoc +/** + * Define in-memory file system. + * @param {Object} options The options. + * @param {() => string} [options.cwd] The current working directory. + * @param {Object} [options.files] The initial files definition in the in-memory file system. + * @returns {import("fs")} The stubbed `ConfigArrayFactory` class. + */ +function defineInMemoryFs({ + cwd = process.cwd, + files = {} +} = {}) { + + /** + * The in-memory file system for this mock. + * @type {import("fs")} + */ + const fs = new MemoryFs({ + cwd, + platform: process.platform === "win32" ? "win32" : "posix" + }); + + // Support D: drive. + if (process.platform === "win32") { + fs._roots.set("D:", fs._makeDir(0o777)); // eslint-disable-line no-underscore-dangle + } + + supportMkdirRecursiveOption(fs, cwd); + fs.mkdirSync(cwd(), { recursive: true }); + + /* + * Write all files to the in-memory file system and compile all JavaScript + * files then set to `stubs`. + */ + (function initFiles(directoryPath, definition) { + for (const [filename, content] of Object.entries(definition)) { + const filePath = path.resolve(directoryPath, filename); + const parentPath = path.dirname(filePath); + + if (typeof content === "object") { + initFiles(filePath, content); + } else if (typeof content === "string") { + if (!fs.existsSync(parentPath)) { + fs.mkdirSync(parentPath, { recursive: true }); + } + fs.writeFileSync(filePath, content); + } else { + throw new Error(`Invalid content: ${typeof content}`); + } + } + }(cwd(), files)); + + return fs; +} module.exports = { + defineInMemoryFs, unIndent }; diff --git a/tests/lib/cli-engine/_utils.js b/tests/lib/cli-engine/_utils.js index 94d6c0158d2..54a49b595b4 100644 --- a/tests/lib/cli-engine/_utils.js +++ b/tests/lib/cli-engine/_utils.js @@ -58,8 +58,8 @@ const path = require("path"); const vm = require("vm"); -const MemoryFs = require("metro-memory-fs"); const Proxyquire = require("proxyquire/lib/proxyquire"); +const { defineInMemoryFs } = require("../_utils"); const CascadingConfigArrayFactoryPath = require.resolve("../../../lib/cli-engine/cascading-config-array-factory"); @@ -242,36 +242,6 @@ function fsImportFresh(fs, stubs, absolutePath) { ); } -/** - * Add support of `recursive` option. - * @param {import("fs")} fs The in-memory file system. - * @param {() => string} cwd The current working directory. - * @returns {void} - */ -function supportMkdirRecursiveOption(fs, cwd) { - const { mkdirSync } = fs; - - fs.mkdirSync = (filePath, options) => { - if (typeof options === "object" && options !== null) { - if (options.recursive) { - const absolutePath = path.resolve(cwd(), filePath); - const parentPath = path.dirname(absolutePath); - - if ( - parentPath && - parentPath !== absolutePath && - !fs.existsSync(parentPath) - ) { - fs.mkdirSync(parentPath, options); - } - } - mkdirSync(filePath, options.mode); - } else { - mkdirSync(filePath, options); - } - }; -} - /** * Define stubbed `ConfigArrayFactory` class what uses the in-memory file system. * @param {Object} options The options. @@ -283,19 +253,7 @@ function defineConfigArrayFactoryWithInMemoryFileSystem({ cwd = process.cwd, files = {} } = {}) { - - /** - * The in-memory file system for this mock. - * @type {import("fs")} - */ - const fs = new MemoryFs({ - cwd, - platform: process.platform === "win32" ? "win32" : "posix" - }); - - supportMkdirRecursiveOption(fs, cwd); - fs.mkdirSync(cwd(), { recursive: true }); - + const fs = defineInMemoryFs({ cwd, files }); const RelativeModuleResolver = { resolve: fsResolve.bind(null, fs) }; /* @@ -315,23 +273,12 @@ function defineConfigArrayFactoryWithInMemoryFileSystem({ (function initFiles(directoryPath, definition) { for (const [filename, content] of Object.entries(definition)) { const filePath = path.resolve(directoryPath, filename); - const parentPath = path.dirname(filePath); if (typeof content === "object") { initFiles(filePath, content); continue; } - /* - * Write this file to the in-memory file system. - * For config files that `fs.readFileSync()` or `importFresh()` will - * import. - */ - if (!fs.existsSync(parentPath)) { - fs.mkdirSync(parentPath, { recursive: true }); - } - fs.writeFileSync(filePath, content); - /* * Compile then stub if this file is a JavaScript file. * For parsers and plugins that `require()` will import. diff --git a/tests/lib/init/npm-utils.js b/tests/lib/init/npm-utils.js index ae14f038349..ed8dbfe6bbb 100644 --- a/tests/lib/init/npm-utils.js +++ b/tests/lib/init/npm-utils.js @@ -9,13 +9,12 @@ //------------------------------------------------------------------------------ const - path = require("path"), assert = require("chai").assert, spawn = require("cross-spawn"), - MemoryFs = require("metro-memory-fs"), sinon = require("sinon"), npmUtils = require("../../../lib/init/npm-utils"), - log = require("../../../lib/shared/logging"); + log = require("../../../lib/shared/logging"), + { defineInMemoryFs } = require("../_utils"); const proxyquire = require("proxyquire").noCallThru().noPreserveCache(); @@ -29,28 +28,8 @@ const proxyquire = require("proxyquire").noCallThru().noPreserveCache(); * @returns {Object} `npm-utils`. */ function requireNpmUtilsWithInMemoryFileSystem(files) { - const fs = new MemoryFs({ - cwd: process.cwd, - platform: process.platform === "win32" ? "win32" : "posix" - }); - - // Make cwd. - (function mkdir(dirPath) { - const parentPath = path.dirname(dirPath); - - if (parentPath && parentPath !== dirPath && !fs.existsSync(parentPath)) { - mkdir(parentPath); - } - fs.mkdirSync(dirPath); - - }(process.cwd())); - - // Write files. - for (const [filename, content] of Object.entries(files)) { - fs.writeFileSync(filename, content); - } + const fs = defineInMemoryFs({ files }); - // Stub. return proxyquire("../../../lib/init/npm-utils", { fs }); }