From c93cfed6ee5481efc5145089050eda55f01c313b Mon Sep 17 00:00:00 2001 From: Robert Wagner Date: Fri, 2 Feb 2018 00:49:04 -0500 Subject: [PATCH 1/8] Start in-repo addon tests --- package.json | 2 + .../my-app-with-in-repo-addon/.eslintrc.js | 42 ++++++++++++ .../app/utils/my-covered-util-app.js | 3 + .../app/utils/my-uncovered-util-app.js | 3 + .../addon/utils/my-covered-util.js | 3 + .../addon/utils/my-uncovered-util.js | 3 + .../app/utils/my-covered-util.js | 1 + .../app/utils/my-uncovered-util.js | 2 + .../my-app-with-in-repo-addon/testem.js | 24 +++++++ .../tests/unit/.gitkeep | 0 .../unit/utils/covered-util-in-addon-test.js | 10 +++ .../tests/unit/utils/my-covered-util-test.js | 10 +++ test/helpers/in-repo-addon.js | 68 +++++++++++++++++++ .../in-repo-addon-coverage-test.js | 56 +++++++++++++++ yarn.lock | 9 ++- 15 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 test/fixtures/my-app-with-in-repo-addon/.eslintrc.js create mode 100644 test/fixtures/my-app-with-in-repo-addon/app/utils/my-covered-util-app.js create mode 100644 test/fixtures/my-app-with-in-repo-addon/app/utils/my-uncovered-util-app.js create mode 100644 test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/addon/utils/my-covered-util.js create mode 100644 test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/addon/utils/my-uncovered-util.js create mode 100644 test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-covered-util.js create mode 100644 test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-uncovered-util.js create mode 100644 test/fixtures/my-app-with-in-repo-addon/testem.js create mode 100644 test/fixtures/my-app-with-in-repo-addon/tests/unit/.gitkeep create mode 100644 test/fixtures/my-app-with-in-repo-addon/tests/unit/utils/covered-util-in-addon-test.js create mode 100644 test/fixtures/my-app-with-in-repo-addon/tests/unit/utils/my-covered-util-test.js create mode 100644 test/helpers/in-repo-addon.js create mode 100644 test/integration/in-repo-addon-coverage-test.js diff --git a/package.json b/package.json index ef2c651a..f613d2bf 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "babel-plugin-istanbul": "^4.1.5", "babel-plugin-transform-async-to-generator": "^6.24.1", "body-parser": "^1.15.0", + "co": "^4.6.0", "ember-cli-babel": "^6.6.0", "ember-cli-htmlbars": "^2.0.1", "ember-cli-version-checker": "^2.0.0", @@ -66,6 +67,7 @@ "ember-try": "^0.2.23", "eslint-plugin-ember": "^5.0.0", "eslint-plugin-node": "^5.2.1", + "fixturify": "^0.3.4", "glob": "^7.1.2", "lerna-changelog": "^0.7.0", "loader.js": "^4.2.3", diff --git a/test/fixtures/my-app-with-in-repo-addon/.eslintrc.js b/test/fixtures/my-app-with-in-repo-addon/.eslintrc.js new file mode 100644 index 00000000..a9bf6a3d --- /dev/null +++ b/test/fixtures/my-app-with-in-repo-addon/.eslintrc.js @@ -0,0 +1,42 @@ +module.exports = { + root: true, + parserOptions: { + ecmaVersion: 2017, + sourceType: 'module' + }, + extends: [ + 'eslint:recommended' + ], + env: { + browser: true + }, + rules: { + }, + overrides: [ + // node files + { + files: [ + 'index.js', + 'testem.js', + 'ember-cli-build.js', + 'config/**/*.js', + 'test/**', + 'tests/dummy/config/**/*.js' + ], + excludedFiles: [ + 'app/**', + 'addon/**', + 'test/fixtures/**', + 'tests/dummy/app/**' + ], + parserOptions: { + sourceType: 'script', + ecmaVersion: 2015 + }, + env: { + browser: false, + node: true + } + } + ] +}; diff --git a/test/fixtures/my-app-with-in-repo-addon/app/utils/my-covered-util-app.js b/test/fixtures/my-app-with-in-repo-addon/app/utils/my-covered-util-app.js new file mode 100644 index 00000000..50867b0d --- /dev/null +++ b/test/fixtures/my-app-with-in-repo-addon/app/utils/my-covered-util-app.js @@ -0,0 +1,3 @@ +export default function myCoveredUtil() { + return true; +} diff --git a/test/fixtures/my-app-with-in-repo-addon/app/utils/my-uncovered-util-app.js b/test/fixtures/my-app-with-in-repo-addon/app/utils/my-uncovered-util-app.js new file mode 100644 index 00000000..8f531cba --- /dev/null +++ b/test/fixtures/my-app-with-in-repo-addon/app/utils/my-uncovered-util-app.js @@ -0,0 +1,3 @@ +export default function myUncoveredUtil() { + return true; +} diff --git a/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/addon/utils/my-covered-util.js b/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/addon/utils/my-covered-util.js new file mode 100644 index 00000000..50867b0d --- /dev/null +++ b/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/addon/utils/my-covered-util.js @@ -0,0 +1,3 @@ +export default function myCoveredUtil() { + return true; +} diff --git a/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/addon/utils/my-uncovered-util.js b/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/addon/utils/my-uncovered-util.js new file mode 100644 index 00000000..8f531cba --- /dev/null +++ b/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/addon/utils/my-uncovered-util.js @@ -0,0 +1,3 @@ +export default function myUncoveredUtil() { + return true; +} diff --git a/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-covered-util.js b/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-covered-util.js new file mode 100644 index 00000000..4c9edb54 --- /dev/null +++ b/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-covered-util.js @@ -0,0 +1 @@ +export { default } from 'my-in-repo-addon/addon/utils/my-covered-util'; diff --git a/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-uncovered-util.js b/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-uncovered-util.js new file mode 100644 index 00000000..08949693 --- /dev/null +++ b/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-uncovered-util.js @@ -0,0 +1,2 @@ +export { default } from 'my-in-repo-addon/addon/utils/my-uncovered-util'; + diff --git a/test/fixtures/my-app-with-in-repo-addon/testem.js b/test/fixtures/my-app-with-in-repo-addon/testem.js new file mode 100644 index 00000000..d1755a4e --- /dev/null +++ b/test/fixtures/my-app-with-in-repo-addon/testem.js @@ -0,0 +1,24 @@ +module.exports = { + test_page: 'tests/index.html?hidepassed', + disable_watching: true, + launch_in_ci: [ + 'Chrome' + ], + launch_in_dev: [ + 'Chrome' + ], + browser_args: { + Chrome: { + mode: 'ci', + args: [ + // --no-sandbox is needed when running Chrome inside a container + process.env.TRAVIS ? '--no-sandbox' : null, + + '--disable-gpu', + '--headless', + '--remote-debugging-port=0', + '--window-size=1440,900' + ].filter(Boolean) + } + } +}; diff --git a/test/fixtures/my-app-with-in-repo-addon/tests/unit/.gitkeep b/test/fixtures/my-app-with-in-repo-addon/tests/unit/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/test/fixtures/my-app-with-in-repo-addon/tests/unit/utils/covered-util-in-addon-test.js b/test/fixtures/my-app-with-in-repo-addon/tests/unit/utils/covered-util-in-addon-test.js new file mode 100644 index 00000000..7b55fd54 --- /dev/null +++ b/test/fixtures/my-app-with-in-repo-addon/tests/unit/utils/covered-util-in-addon-test.js @@ -0,0 +1,10 @@ +import myCoveredUtil from 'my-in-repo-addon/utils/my-covered-util'; +import { module, test } from 'qunit'; + +module('Unit | Utility | my covered util'); + +// Replace this with your real tests. +test('it works', function(assert) { + let result = myCoveredUtil(); + assert.ok(result); +}); diff --git a/test/fixtures/my-app-with-in-repo-addon/tests/unit/utils/my-covered-util-test.js b/test/fixtures/my-app-with-in-repo-addon/tests/unit/utils/my-covered-util-test.js new file mode 100644 index 00000000..abc2e84d --- /dev/null +++ b/test/fixtures/my-app-with-in-repo-addon/tests/unit/utils/my-covered-util-test.js @@ -0,0 +1,10 @@ +import myCoveredUtil from 'my-app-with-in-repo-addon/utils/my-covered-util-app'; +import { module, test } from 'qunit'; + +module('Unit | Utility | my covered util app'); + +// Replace this with your real tests. +test('it works', function(assert) { + let result = myCoveredUtil(); + assert.ok(result); +}); diff --git a/test/helpers/in-repo-addon.js b/test/helpers/in-repo-addon.js new file mode 100644 index 00000000..513ed69c --- /dev/null +++ b/test/helpers/in-repo-addon.js @@ -0,0 +1,68 @@ +'use strict'; + +const path = require('path'); +const fs = require('fs-extra'); +const fixturify = require('fixturify'); + +class InRepoAddon { + static generate(app, name) { + let args = ['generate', 'in-repo-addon', name]; + return app.runEmberCommand.apply(app, args).then(() => { + let addon = new InRepoAddon(app, name); + addon.editPackageJSON( + pkg => (pkg.dependencies = { 'ember-cli-htmlbars': '*' }) + ); + return addon; + }); + } + + constructor(app, name) { + this.name = name; + this.app = app; + this.path = path.join(app.path, 'lib', name); + } + + editPackageJSON(editor) { + let packageJSONPath = path.join(this.path, 'package.json'); + let pkg = fs.readJsonSync(packageJSONPath); + editor(pkg); + fs.writeJsonSync(packageJSONPath, pkg); + } + + writeFixture(fixture) { + fixturify.writeSync(this.path, fixture); + } + + nest(addon) { + this.editPackageJSON(pkg => { + pkg['ember-addon'] = pkg['ember-addon'] || {}; + pkg['ember-addon'].paths = pkg['ember-addon'].paths || []; + pkg['ember-addon'].paths.push(`../${addon.name}`); + }); + } + + generateNestedAddon(name) { + // Generate another in-repo-addon at the app level... + let args = Array.prototype.slice.call(arguments); + args.unshift(this.app); + return InRepoAddon.generate.apply(null, args).then(addon => { + // Remove the in-repo-addon from the app... + this.app.editPackageJSON(pkg => { + pkg['ember-addon'].paths = pkg['ember-addon'].paths.filter( + path => path !== `lib/${name}` + ); + }); + + // Add the in-repo-addon to this engine. + this.editPackageJSON(pkg => { + pkg['ember-addon'] = pkg['ember-addon'] || {}; + pkg['ember-addon'].paths = pkg['ember-addon'].paths || []; + pkg['ember-addon'].paths.push(`../${name}`); + }); + + return addon; + }); + } +} + +module.exports = InRepoAddon; diff --git a/test/integration/in-repo-addon-coverage-test.js b/test/integration/in-repo-addon-coverage-test.js new file mode 100644 index 00000000..8cc08f7f --- /dev/null +++ b/test/integration/in-repo-addon-coverage-test.js @@ -0,0 +1,56 @@ +'use strict'; + +var fs = require('fs-extra'); +var RSVP = require('rsvp'); +var rimraf = RSVP.denodeify(require('rimraf')); +var chai = require('chai'); +const co = require('co'); +var expect = chai.expect; +var chaiFiles = require('chai-files'); +var dir = chaiFiles.dir; +var file = chaiFiles.file; + +const AddonTestApp = require('ember-cli-addon-tests').AddonTestApp; +const InRepoAddon = require('../helpers/in-repo-addon'); + +chai.use(chaiFiles); + +let app; + +describe('in-repo addon coverage generation', function() { + this.timeout(10000000); + beforeEach(function() { + app = new AddonTestApp(); + return app.create('my-app-with-in-repo-addon', { + emberVersion: '2.16.0' + }).then(() => { + app.editPackageJSON(pkg => { + pkg.devDependencies['ember-exam'] = '0.7.0'; + }); + return app.run('npm', 'install').then(() => { + return rimraf(`${app.path}/coverage*`); + }); + }); + }); + + afterEach(function() { + return RSVP.all([ + rimraf(`${app.path}/config/coverage.js`) + ]); + }); + + it.only('runs coverage on in-repo addon', co.wrap(function* () { + let addon = yield InRepoAddon.generate(app, 'my-in-repo-addon'); + addon.editPackageJSON( + pkg => (pkg.dependencies = { 'ember-cli-babel': '*' }) + ); + expect(dir(`${app.path}/coverage`)).to.not.exist; + process.env.COVERAGE = true; + return app.run('ember', 'test').then(function() { + expect(file(`${app.path}/coverage/lcov-report/index.html`)).to.not.be.empty; + expect(file(`${app.path}/coverage/index.html`)).to.not.be.empty; + var summary = fs.readJSONSync(`${app.path}/coverage/coverage-summary.json`); + expect(summary.total.lines.pct).to.equal(83.33); + }); + })); +}); diff --git a/yarn.lock b/yarn.lock index 4eb5952d..2de85208 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3020,6 +3020,13 @@ fireworm@^0.7.0: lodash.flatten "^3.0.2" minimatch "^3.0.2" +fixturify@^0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/fixturify/-/fixturify-0.3.4.tgz#c676de404a7f8ee8e64d0b76118e62ec95ab7b25" + dependencies: + fs-extra "^0.30.0" + matcher-collection "^1.0.4" + flat-cache@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" @@ -4724,7 +4731,7 @@ markdown-it@^8.3.0, markdown-it@^8.3.1: mdurl "^1.0.1" uc.micro "^1.0.3" -matcher-collection@^1.0.0, matcher-collection@^1.0.5: +matcher-collection@^1.0.0, matcher-collection@^1.0.4, matcher-collection@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/matcher-collection/-/matcher-collection-1.0.5.tgz#2ee095438372cb8884f058234138c05c644ec339" dependencies: From 9ad8a6c5b1e3e8b12011b788a9a06c6a5ad3c73d Mon Sep 17 00:00:00 2001 From: Robert Wagner Date: Fri, 2 Feb 2018 09:39:21 -0500 Subject: [PATCH 2/8] Update per Adam's suggestion --- index.js | 15 +++++++++++---- test/integration/in-repo-addon-coverage-test.js | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 1d1c5dc4..a4585193 100644 --- a/index.js +++ b/index.js @@ -144,10 +144,17 @@ module.exports = { let options = appOrAddon.options = appOrAddon.options || {}; options.babel = options.babel || {}; let plugins = options.babel.plugins = options.babel.plugins || []; - plugins.push([this.IstanbulPlugin, { - exclude: this._getExcludes(), - include: this._getIncludes(dir, modulePrefix) - }]); + let include = this._getIncludes(dir, modulePrefix); + let plugin = plugins.find((plugin) => plugin[0] === this.IstanbulPlugin); + if (plugin) { // plugin already exists, so amend it rather than adding another one + plugin[1].include = plugin[1].include.concat(include); + } else { + plugins.push([this.IstanbulPlugin, { + exclude: this._getExcludes(), + include + }]); + } + } }, diff --git a/test/integration/in-repo-addon-coverage-test.js b/test/integration/in-repo-addon-coverage-test.js index 8cc08f7f..f238ef43 100644 --- a/test/integration/in-repo-addon-coverage-test.js +++ b/test/integration/in-repo-addon-coverage-test.js @@ -39,7 +39,7 @@ describe('in-repo addon coverage generation', function() { ]); }); - it.only('runs coverage on in-repo addon', co.wrap(function* () { + it('runs coverage on in-repo addon', co.wrap(function* () { let addon = yield InRepoAddon.generate(app, 'my-in-repo-addon'); addon.editPackageJSON( pkg => (pkg.dependencies = { 'ember-cli-babel': '*' }) From 25abcf565f50ba0a9de6b79d97b0fd564c3b8926 Mon Sep 17 00:00:00 2001 From: Robert Wagner Date: Fri, 2 Feb 2018 13:46:05 -0500 Subject: [PATCH 3/8] Fix import paths --- test/fixtures/my-addon/app/utils/my-covered-util.js | 2 +- test/fixtures/my-addon/app/utils/my-uncovered-util.js | 2 +- .../lib/my-in-repo-addon/app/utils/my-covered-util.js | 2 +- .../lib/my-in-repo-addon/app/utils/my-uncovered-util.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/fixtures/my-addon/app/utils/my-covered-util.js b/test/fixtures/my-addon/app/utils/my-covered-util.js index b32e61dc..a4d15d23 100644 --- a/test/fixtures/my-addon/app/utils/my-covered-util.js +++ b/test/fixtures/my-addon/app/utils/my-covered-util.js @@ -1 +1 @@ -export { default } from '../../addon/utils/my-covered-util'; +export { default } from 'my-addon/utils/my-covered-util'; diff --git a/test/fixtures/my-addon/app/utils/my-uncovered-util.js b/test/fixtures/my-addon/app/utils/my-uncovered-util.js index bef567b1..2f95942a 100644 --- a/test/fixtures/my-addon/app/utils/my-uncovered-util.js +++ b/test/fixtures/my-addon/app/utils/my-uncovered-util.js @@ -1,2 +1,2 @@ -export { default } from '../../addon/utils/my-uncovered-util'; +export { default } from 'my-addon/utils/my-uncovered-util'; diff --git a/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-covered-util.js b/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-covered-util.js index 4c9edb54..ea557609 100644 --- a/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-covered-util.js +++ b/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-covered-util.js @@ -1 +1 @@ -export { default } from 'my-in-repo-addon/addon/utils/my-covered-util'; +export { default } from 'my-in-repo-addon/utils/my-covered-util'; diff --git a/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-uncovered-util.js b/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-uncovered-util.js index 08949693..64e6c01e 100644 --- a/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-uncovered-util.js +++ b/test/fixtures/my-app-with-in-repo-addon/lib/my-in-repo-addon/app/utils/my-uncovered-util.js @@ -1,2 +1,2 @@ -export { default } from 'my-in-repo-addon/addon/utils/my-uncovered-util'; +export { default } from 'my-in-repo-addon/utils/my-uncovered-util'; From 911b3f4507ddfb372b9ca689dd2ba1e03e4e33c1 Mon Sep 17 00:00:00 2001 From: Robert Wagner Date: Mon, 5 Feb 2018 13:42:07 -0500 Subject: [PATCH 4/8] fix babel-plugin-istanbul caching issue (#159) * fix babel-plugin-istanbul caching issue * - Refactor instrumentation logic given `babel-plugin-istanbul` constraints - Fix Unit tests --- index.js | 142 ++++++++------ package.json | 1 + test/integration/app-coverage-test.js | 2 +- .../in-repo-addon-coverage-test.js | 6 +- test/unit/index-test.js | 183 +++++++----------- yarn.lock | 4 + 6 files changed, 156 insertions(+), 182 deletions(-) diff --git a/index.js b/index.js index a4585193..3ffe9f8f 100644 --- a/index.js +++ b/index.js @@ -7,6 +7,7 @@ var attachMiddleware = require('./lib/attach-middleware'); var config = require('./lib/config'); const walkSync = require('walk-sync'); const VersionChecker = require('ember-cli-version-checker'); +const concat = require('lodash.concat'); function requireBabelPlugin(pluginName) { let plugin = require(pluginName); @@ -22,6 +23,12 @@ function requireBabelPlugin(pluginName) { return plugin; } +function getPlugins(appOrAddon) { + let options = appOrAddon.options = appOrAddon.options || {}; + options.babel = options.babel || {}; + return options.babel.plugins = options.babel.plugins || []; +} + // Regular expression to extract the file extension from a path. const EXT_RE = /\.[^\.]+$/; @@ -45,11 +52,19 @@ module.exports = { let checker = new VersionChecker(this.parent).for('ember-cli-babel', 'npm'); if (checker.satisfies('>= 6.0.0')) { - this.IstanbulPlugin = requireBabelPlugin('babel-plugin-istanbul'); + const IstanbulPlugin = requireBabelPlugin('babel-plugin-istanbul'); + const exclude = this._getExcludes(); + const include = this._getIncludes(); + + concat( + this.app, + this._findCoveredAddon(), + this._findInRepoAddons() + ) + .filter(Boolean) + .map(getPlugins) + .forEach((plugins) => plugins.push([IstanbulPlugin, { exclude, include }])); - this._instrumentAppDirectory(); - this._instrumentAddonDirectory(); - this._instrumentInRepoAddonDirectories(); } else { this.project.ui.writeWarnLine( 'ember-cli-code-coverage: You are using an unsupported ember-cli-babel version,' + @@ -96,83 +111,71 @@ module.exports = { // Custom Methods /** - * Instrument the "app" directory. + * Thin wrapper around exists-sync that allows easy stubbing in tests + * @param {String} path - path to check existence of + * @returns {Boolean} whether or not path exists */ - _instrumentAppDirectory() { - const dir = path.join(this.project.root, 'app'); - let prefix = this.parent.isEmberCLIAddon() ? 'dummy' : this.parent.name(); - this._instrumentDirectory(this.app, dir, prefix); + _existsSync: function(path) { + return existsSync(path); }, /** - * Instrument the "addon" directory. + * Get project configuration + * @returns {Configuration} project configuration */ - _instrumentAddonDirectory() { - let addon = this._findCoveredAddon(); - if (addon) { - const dir = path.join(this.project.root, 'addon'); - this._instrumentDirectory(addon, dir, addon.name); - } + _getConfig: function() { + return config(this.project.configPath()); }, /** - * Instrument the in-repo-addon directories in "lib/*". + * Get paths to include for coverage + * @returns {Array} include paths */ - _instrumentInRepoAddonDirectories() { - const pkg = this.project.pkg; - if (pkg['ember-addon'] && pkg['ember-addon'].paths) { - pkg['ember-addon'].paths.forEach((addonPath) => { - let addonName = path.basename(addonPath); - let addonDir = path.join(this.project.root, addonPath); - let addon = this.project.findAddonByName(addonName); - let addonAppDir = path.join(addonDir, 'app'); - let addonAddonDir = path.join(addonDir, 'addon'); - this._instrumentDirectory(this.app, addonAppDir, this.parent.name()); - this._instrumentDirectory(addon, addonAddonDir, addonName); - }); - } + _getIncludes: function() { + return concat( + this._getIncludesForAppDirectory(), + this._getIncludesForAddonDirectory(), + this._getIncludesForInRepoAddonDirectories() + ).filter(Boolean); }, /** - * Instrument directory helper. - * @param {Object} appOrAddon The Ember app or addon config. - * @param {String} dir The path to the Ember app or addon. - * @param {String} modulePrefix The prefix to the ember module ('app', 'dummy' or the name of the addon). + * Get paths to include for covering the "app" directory. + * @returns {Array} include paths */ - _instrumentDirectory(appOrAddon, dir, modulePrefix) { - if (existsSync(dir)) { - let options = appOrAddon.options = appOrAddon.options || {}; - options.babel = options.babel || {}; - let plugins = options.babel.plugins = options.babel.plugins || []; - let include = this._getIncludes(dir, modulePrefix); - let plugin = plugins.find((plugin) => plugin[0] === this.IstanbulPlugin); - if (plugin) { // plugin already exists, so amend it rather than adding another one - plugin[1].include = plugin[1].include.concat(include); - } else { - plugins.push([this.IstanbulPlugin, { - exclude: this._getExcludes(), - include - }]); - } - - } + _getIncludesForAppDirectory: function() { + const dir = path.join(this.project.root, 'app'); + let prefix = this.parent.isEmberCLIAddon() ? 'dummy' : this.parent.name(); + return this._getIncludesForDir(dir, prefix); }, /** - * Thin wrapper around exists-sync that allows easy stubbing in tests - * @param {String} path - path to check existence of - * @returns {Boolean} whether or not path exists + * Get paths to include for covering the "addon" directory. + * @returns {Array} include paths */ - _existsSync: function(path) { - return existsSync(path); + _getIncludesForAddonDirectory: function() { + let addon = this._findCoveredAddon(); + if (addon) { + const dir = path.join(this.project.root, 'addon'); + return this._getIncludesForDir(dir, addon.name); + } }, /** - * Get project configuration - * @returns {Configuration} project configuration + * Get paths to include for covering the in-repo-addon directories in "lib/*". + * @returns {Array} include paths */ - _getConfig: function() { - return config(this.project.configPath()); + _getIncludesForInRepoAddonDirectories: function() { + return this._findInRepoAddons().reduce((acc, addon) => { + let addonDir = path.join(this.project.root, 'lib', addon.name); + let addonAppDir = path.join(addonDir, 'app'); + let addonAddonDir = path.join(addonDir, 'addon'); + return concat( + acc, + this._getIncludesForDir(addonAppDir, this.parent.name()), + this._getIncludesForDir(addonAddonDir, addon.name) + ); + }, []); }, /** @@ -181,7 +184,7 @@ module.exports = { * @param {String} prefix The prefix to the ember module ('app', 'dummy' or the name of the addon). * @returns {Array} include paths */ - _getIncludes: function(dir, prefix) { + _getIncludesForDir: function(dir, prefix) { let dirname = path.relative(this.project.root, dir); let globs = this.registry.extensionsForType('js').map((extension) => `**/*.${extension}`); @@ -236,5 +239,22 @@ module.exports = { } return this._coveredAddon; + }, + + /** + * Find the app's in-repo addons (if any). + * @returns {Array} the in-repo addons + */ + _findInRepoAddons: function() { + if (!this._inRepoAddons) { + const pkg = this.project.pkg; + const inRepoAddonPaths = pkg['ember-addon'] && pkg['ember-addon'].paths; + this._inRepoAddons = (inRepoAddonPaths || []).map((addonPath) => { + let addonName = path.basename(addonPath); + return this.project.findAddonByName(addonName); + }); + } + + return this._inRepoAddons; } }; diff --git a/package.json b/package.json index f613d2bf..f1521a8d 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "extend": "^3.0.0", "fs-extra": "^5.0.0", "istanbul-api": "^1.1.14", + "lodash.concat": "^4.5.0", "node-dir": "^0.1.16", "rsvp": "^4.8.1", "walk-sync": "^0.3.2" diff --git a/test/integration/app-coverage-test.js b/test/integration/app-coverage-test.js index 87a29f9a..31fc20cd 100644 --- a/test/integration/app-coverage-test.js +++ b/test/integration/app-coverage-test.js @@ -18,7 +18,7 @@ let app; describe('app coverage generation', function() { this.timeout(10000000); beforeEach(function() { - app = new AddonTestApp(); + app = new AddonTestApp({ skipNpm: true }); return app.create('my-app', { emberVersion: '2.16.0' }).then(() => { diff --git a/test/integration/in-repo-addon-coverage-test.js b/test/integration/in-repo-addon-coverage-test.js index f238ef43..65724230 100644 --- a/test/integration/in-repo-addon-coverage-test.js +++ b/test/integration/in-repo-addon-coverage-test.js @@ -20,7 +20,7 @@ let app; describe('in-repo addon coverage generation', function() { this.timeout(10000000); beforeEach(function() { - app = new AddonTestApp(); + app = new AddonTestApp({ skipNpm: true }); return app.create('my-app-with-in-repo-addon', { emberVersion: '2.16.0' }).then(() => { @@ -50,7 +50,9 @@ describe('in-repo addon coverage generation', function() { expect(file(`${app.path}/coverage/lcov-report/index.html`)).to.not.be.empty; expect(file(`${app.path}/coverage/index.html`)).to.not.be.empty; var summary = fs.readJSONSync(`${app.path}/coverage/coverage-summary.json`); - expect(summary.total.lines.pct).to.equal(83.33); + expect(summary.total.lines.pct).to.equal(75); + expect(summary['app/utils/my-covered-util-app.js'].lines.total).to.equal(1); + expect(summary['lib/my-in-repo-addon/addon/utils/my-covered-util.js'].lines.total).to.equal(1); }); })); }); diff --git a/test/unit/index-test.js b/test/unit/index-test.js index d4f24c22..e6a3ca43 100644 --- a/test/unit/index-test.js +++ b/test/unit/index-test.js @@ -11,16 +11,17 @@ describe('index.js', function() { beforeEach(function() { sandbox = sinon.sandbox.create(); - Index.registry = { + Index.parent = Index.project = Index.app = Index.IstanbulPlugin = Index.registry = null; + sandbox.stub(Index, 'fileLookup').value({}); + sandbox.stub(Index, 'registry').value({ extensionsForType: function() { return ['js']; } - }; - Index.parent = Index.project = Index.app = Index.IstanbulPlugin = null; - sandbox.stub(Index, 'fileLookup').value({}); + }); }); afterEach(function() { + Index._coveredAddon = Index._inRepoAddons = null; sandbox.restore(); }); @@ -38,15 +39,15 @@ describe('index.js', function() { describe('with coverage enabled', function() { beforeEach(function() { sandbox.stub(Index, '_isCoverageEnabled').returns(true); - Index.fileLookup = { + sandbox.stub(Index, 'fileLookup').value({ 'some/module.js': 'some/file.js', 'some/other/module.js': 'some/other/file.js' - }; - Index.parent = { + }); + sandbox.stub(Index, 'parent').value({ isEmberCLIAddon: function() { return false; } - }; + }); }); it('does nothing if type is not test-body-footer', function() { @@ -84,10 +85,10 @@ describe('index.js', function() { post: sinon.spy() }; - Index.project = { + sandbox.stub(Index, 'project').value({ root: '/path/to/foo-bar', configPath: sinon.stub().returns('tests/dummy/config/environment.js') - }; + }); }); describe('when coverage is enabled', function() { @@ -113,13 +114,13 @@ describe('index.js', function() { }); }); - describe('_getIncludes', function() { + describe('_getIncludesForDir', function() { beforeEach(function() { sandbox.stub(Index, 'project').value({ root: 'test/fixtures/my-addon/' }); }); it('gets files to include from the app directory', function() { - Index._getIncludes('test/fixtures/my-addon/app', 'my-app'); + Index._getIncludesForDir('test/fixtures/my-addon/app', 'my-app'); expect(Index.fileLookup).to.deep.equal({ 'my-app/utils/my-covered-util.js': 'app/utils/my-covered-util.js', 'my-app/utils/my-uncovered-util.js': 'app/utils/my-uncovered-util.js' @@ -127,7 +128,7 @@ describe('index.js', function() { }); it('gets files to include from the addon directory', function() { - Index._getIncludes('test/fixtures/my-addon/addon', 'my-addon'); + Index._getIncludesForDir('test/fixtures/my-addon/addon', 'my-addon'); expect(Index.fileLookup).to.deep.equal({ 'my-addon/utils/my-covered-util.js': 'addon/utils/my-covered-util.js', 'my-addon/utils/my-uncovered-util.js': 'addon/utils/my-uncovered-util.js' @@ -137,14 +138,14 @@ describe('index.js', function() { describe('_getExcludes', function() { beforeEach(function() { - Index.parent = { + sandbox.stub(Index, 'parent').value({ isEmberCLIAddon: function() { return false; }, name: function() { return 'test'; } - }; + }); }); describe('when excludes not defined in config', function() { @@ -275,14 +276,14 @@ describe('index.js', function() { var isAddon; beforeEach(function() { - Index.parent = { + sandbox.stub(Index, 'parent').value({ name: function() { return 'parent-app'; }, isEmberCLIAddon: function() { return isAddon; } - }; + }); }); describe('when parent is an app', function() { @@ -311,13 +312,12 @@ describe('index.js', function() { var result; beforeEach(function() { - Index.project = { + sandbox.stub(Index, 'project').value({ findAddonByName: sinon.stub().returns({ name: 'my-addon' }), pkg: { name: '@scope/ember-cli-my-addon' } - }; - + }); result = Index._findCoveredAddon(); }); @@ -330,7 +330,7 @@ describe('index.js', function() { }); }); - describe('_instrumentDirectory', function() { + describe('_getIncludes', function() { beforeEach(function() { sandbox.stub(Index, 'IstanbulPlugin').value('istanbul'); sandbox.stub(Index, '_getExcludes').returns([]); @@ -341,7 +341,7 @@ describe('index.js', function() { sandbox.stub(Index, 'app').value({}); }); - describe('_instrumentAppDirectory', function() { + describe('_getIncludesForAppDirectory', function() { describe('for an app', function() { beforeEach(function() { @@ -351,30 +351,16 @@ describe('index.js', function() { }); }); - it('instruments the app directory', function() { - Index._instrumentAppDirectory(); + it('gets includes for the app directory', function() { + const includes = Index._getIncludesForAppDirectory(); + expect(includes).to.deep.equal([ + 'my-app/utils/my-covered-util.js', + 'my-app/utils/my-uncovered-util.js' + ]); expect(Index.fileLookup).to.deep.equal({ 'my-app/utils/my-covered-util.js': 'app/utils/my-covered-util.js', 'my-app/utils/my-uncovered-util.js': 'app/utils/my-uncovered-util.js' }); - expect(Index.app).to.deep.equal({ - options: { - babel: { - plugins: [ - [ - 'istanbul', - { - exclude: [], - include: [ - 'my-app/utils/my-covered-util.js', - 'my-app/utils/my-uncovered-util.js' - ] - } - ] - ] - } - } - }); }); }); @@ -386,46 +372,33 @@ describe('index.js', function() { }); }); - it('instruments the app directory', function() { - Index._instrumentAppDirectory(); + it('gets includes for the app directory', function() { + const includes = Index._getIncludesForAppDirectory(); + expect(includes).to.deep.equal([ + 'dummy/utils/my-covered-util.js', + 'dummy/utils/my-uncovered-util.js' + ]); expect(Index.fileLookup).to.deep.equal({ 'dummy/utils/my-covered-util.js': 'app/utils/my-covered-util.js', 'dummy/utils/my-uncovered-util.js': 'app/utils/my-uncovered-util.js' }); - expect(Index.app).to.deep.equal({ - options: { - babel: { - plugins: [ - [ - 'istanbul', - { - exclude: [], - include: [ - 'dummy/utils/my-covered-util.js', - 'dummy/utils/my-uncovered-util.js' - ] - } - ] - ] - } - } - }); }); }); }); - describe('_instrumentAddonDirectory', function() { + describe('_getIncludesForAddonDirectory', function() { describe('for an app', function() { beforeEach(function() { sandbox.stub(Index, '_findCoveredAddon').returns(null); - sandbox.spy(Index, '_instrumentDirectory'); + sandbox.spy(Index, '_getIncludesForDir'); }); - it('does not instrument the addon directory', function() { - Index._instrumentAddonDirectory(); - sinon.assert.notCalled(Index._instrumentDirectory); + it('does not get includes for the addon directory', function() { + const includes = Index._getIncludesForAddonDirectory(); + expect(includes).to.be.undefined; + sinon.assert.notCalled(Index._getIncludesForDir); }); }); @@ -442,55 +415,41 @@ describe('index.js', function() { addon = null; }); - it('instruments the addon directory', function() { - Index._instrumentAddonDirectory(); + it('gets includes for the addon directory', function() { + const includes = Index._getIncludesForAddonDirectory(); + expect(includes).to.deep.equal([ + 'my-addon/utils/my-covered-util.js', + 'my-addon/utils/my-uncovered-util.js' + ]); expect(Index.fileLookup).to.deep.equal({ 'my-addon/utils/my-covered-util.js': 'addon/utils/my-covered-util.js', 'my-addon/utils/my-uncovered-util.js': 'addon/utils/my-uncovered-util.js' }); - expect(addon).to.deep.equal({ - name: 'my-addon', - options: { - babel: { - plugins: [ - [ - 'istanbul', - { - exclude: [], - include: [ - 'my-addon/utils/my-covered-util.js', - 'my-addon/utils/my-uncovered-util.js' - ] - } - ] - ] - } - } - }); }); }); }); - describe('_instrumentInRepoAddonDirectories', function() { + describe('_getIncludesForInRepoAddonDirectories', function() { describe('for an app with no inrepo addons', function() { beforeEach(function() { sandbox.stub(Index, 'project').value({ pkg: { } }); - sandbox.spy(Index, '_instrumentDirectory'); + sandbox.spy(Index, '_getIncludesForDir'); }); it('does not instrument any inrepo addon directories', function() { - Index._instrumentInRepoAddonDirectories(); - sinon.assert.notCalled(Index._instrumentDirectory); + const includes = Index._getIncludesForInRepoAddonDirectories(); + expect(includes).to.deep.equal([]); + sinon.assert.notCalled(Index._getIncludesForDir); }); }); describe('for an app with an inrepo addon', function() { - let addon = {}; + let addon = { name: 'my-in-repo-addon' }; beforeEach(function() { - sandbox.stub(path, 'basename').returns('my-inrepo-addon'); + sandbox.stub(path, 'basename').returns('my-in-repo-addon'); sandbox.stub(Index, 'project').value({ pkg: { 'ember-addon': { @@ -499,7 +458,7 @@ describe('index.js', function() { ] } }, - root: 'test/fixtures/my-addon/', + root: 'test/fixtures/my-app-with-in-repo-addon/', findAddonByName() { return addon; } }); }); @@ -509,30 +468,18 @@ describe('index.js', function() { }); it('instruments the inrepo addon', function() { - Index._instrumentInRepoAddonDirectories(); + const includes = Index._getIncludesForInRepoAddonDirectories(); + expect(includes).to.deep.equal([ + 'my-app/utils/my-covered-util.js', + 'my-app/utils/my-uncovered-util.js', + 'my-in-repo-addon/utils/my-covered-util.js', + 'my-in-repo-addon/utils/my-uncovered-util.js' + ]); expect(Index.fileLookup).to.deep.equal({ - 'my-app/utils/my-covered-util.js': 'app/utils/my-covered-util.js', - 'my-app/utils/my-uncovered-util.js': 'app/utils/my-uncovered-util.js', - 'my-inrepo-addon/utils/my-covered-util.js': 'addon/utils/my-covered-util.js', - 'my-inrepo-addon/utils/my-uncovered-util.js': 'addon/utils/my-uncovered-util.js', - }); - expect(addon).to.deep.equal({ - options: { - babel: { - plugins: [ - [ - 'istanbul', - { - exclude: [], - include: [ - 'my-inrepo-addon/utils/my-covered-util.js', - 'my-inrepo-addon/utils/my-uncovered-util.js' - ] - } - ] - ] - } - } + 'my-app/utils/my-covered-util.js': 'lib/my-in-repo-addon/app/utils/my-covered-util.js', + 'my-app/utils/my-uncovered-util.js': 'lib/my-in-repo-addon/app/utils/my-uncovered-util.js', + 'my-in-repo-addon/utils/my-covered-util.js': 'lib/my-in-repo-addon/addon/utils/my-covered-util.js', + 'my-in-repo-addon/utils/my-uncovered-util.js': 'lib/my-in-repo-addon/addon/utils/my-uncovered-util.js', }); }); }); diff --git a/yarn.lock b/yarn.lock index 2de85208..a9d49b8f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4450,6 +4450,10 @@ lodash.clonedeep@^4.4.1: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" +lodash.concat@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.concat/-/lodash.concat-4.5.0.tgz#b053ae02e4a8008582e7256b9d02bda6d0380395" + lodash.debounce@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-3.1.1.tgz#812211c378a94cc29d5aa4e3346cf0bfce3a7df5" From 6280b48fe4846dc06acf9c1e8e289b9d7b5478cf Mon Sep 17 00:00:00 2001 From: Robert Wagner Date: Mon, 5 Feb 2018 16:21:02 -0500 Subject: [PATCH 5/8] Update app-coverage-test.js --- test/integration/app-coverage-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/app-coverage-test.js b/test/integration/app-coverage-test.js index 31fc20cd..87a29f9a 100644 --- a/test/integration/app-coverage-test.js +++ b/test/integration/app-coverage-test.js @@ -18,7 +18,7 @@ let app; describe('app coverage generation', function() { this.timeout(10000000); beforeEach(function() { - app = new AddonTestApp({ skipNpm: true }); + app = new AddonTestApp(); return app.create('my-app', { emberVersion: '2.16.0' }).then(() => { From 60accecb168d14d2fe756ad31a95a18d5c2460a3 Mon Sep 17 00:00:00 2001 From: Robert Wagner Date: Mon, 5 Feb 2018 16:21:22 -0500 Subject: [PATCH 6/8] Update in-repo-addon-coverage-test.js --- test/integration/in-repo-addon-coverage-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/in-repo-addon-coverage-test.js b/test/integration/in-repo-addon-coverage-test.js index 65724230..0ea6497f 100644 --- a/test/integration/in-repo-addon-coverage-test.js +++ b/test/integration/in-repo-addon-coverage-test.js @@ -20,7 +20,7 @@ let app; describe('in-repo addon coverage generation', function() { this.timeout(10000000); beforeEach(function() { - app = new AddonTestApp({ skipNpm: true }); + app = new AddonTestApp(); return app.create('my-app-with-in-repo-addon', { emberVersion: '2.16.0' }).then(() => { From 546a83d0e79bafc0a0a645fed9e6f2c697a68c4e Mon Sep 17 00:00:00 2001 From: Robert Wagner Date: Mon, 5 Feb 2018 16:42:51 -0500 Subject: [PATCH 7/8] Try setting path to process.cwd --- test/integration/app-coverage-test.js | 1 + test/integration/in-repo-addon-coverage-test.js | 1 + 2 files changed, 2 insertions(+) diff --git a/test/integration/app-coverage-test.js b/test/integration/app-coverage-test.js index 31fc20cd..67ce87d1 100644 --- a/test/integration/app-coverage-test.js +++ b/test/integration/app-coverage-test.js @@ -24,6 +24,7 @@ describe('app coverage generation', function() { }).then(() => { app.editPackageJSON(pkg => { pkg.devDependencies['ember-exam'] = '0.7.0'; + pkg.devDependencies['ember-cli-code-coverage'] = process.cwd(); }); return app.run('npm', 'install').then(() => { return rimraf(`${app.path}/coverage*`); diff --git a/test/integration/in-repo-addon-coverage-test.js b/test/integration/in-repo-addon-coverage-test.js index 65724230..f271ae24 100644 --- a/test/integration/in-repo-addon-coverage-test.js +++ b/test/integration/in-repo-addon-coverage-test.js @@ -26,6 +26,7 @@ describe('in-repo addon coverage generation', function() { }).then(() => { app.editPackageJSON(pkg => { pkg.devDependencies['ember-exam'] = '0.7.0'; + pkg.devDependencies['ember-cli-code-coverage'] = process.cwd(); }); return app.run('npm', 'install').then(() => { return rimraf(`${app.path}/coverage*`); From e4695a6f19987e650e6a1b06b3a08f5ee8eec5fc Mon Sep 17 00:00:00 2001 From: adamjmcgrath Date: Tue, 6 Feb 2018 20:05:35 +0000 Subject: [PATCH 8/8] Fix tests to workaround https://github.com/tomdale/ember-cli-addon-tests/issues/176 --- test/integration/app-coverage-test.js | 10 +++++++++- test/integration/in-repo-addon-coverage-test.js | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/test/integration/app-coverage-test.js b/test/integration/app-coverage-test.js index 05274a3e..57a7fb5f 100644 --- a/test/integration/app-coverage-test.js +++ b/test/integration/app-coverage-test.js @@ -8,6 +8,7 @@ var expect = chai.expect; var chaiFiles = require('chai-files'); var dir = chaiFiles.dir; var file = chaiFiles.file; +var path = require('path'); const AddonTestApp = require('ember-cli-addon-tests').AddonTestApp; @@ -24,9 +25,16 @@ describe('app coverage generation', function() { }).then(() => { app.editPackageJSON(pkg => { pkg.devDependencies['ember-exam'] = '0.7.0'; - pkg.devDependencies['ember-cli-code-coverage'] = process.cwd(); + // Temporarily remove the addon before install to work around https://github.com/tomdale/ember-cli-addon-tests/issues/176 + delete pkg.devDependencies['ember-cli-code-coverage']; }); return app.run('npm', 'install').then(() => { + app.editPackageJSON(pkg => { + pkg.devDependencies['ember-cli-code-coverage'] = '*'; + }); + let addonPath = path.join(app.path, 'node_modules', 'ember-cli-code-coverage'); + fs.removeSync(addonPath); + fs.ensureSymlinkSync(process.cwd(), addonPath); return rimraf(`${app.path}/coverage*`); }); }); diff --git a/test/integration/in-repo-addon-coverage-test.js b/test/integration/in-repo-addon-coverage-test.js index de3a85ed..90e15cd4 100644 --- a/test/integration/in-repo-addon-coverage-test.js +++ b/test/integration/in-repo-addon-coverage-test.js @@ -9,6 +9,7 @@ var expect = chai.expect; var chaiFiles = require('chai-files'); var dir = chaiFiles.dir; var file = chaiFiles.file; +var path = require('path'); const AddonTestApp = require('ember-cli-addon-tests').AddonTestApp; const InRepoAddon = require('../helpers/in-repo-addon'); @@ -26,9 +27,16 @@ describe('in-repo addon coverage generation', function() { }).then(() => { app.editPackageJSON(pkg => { pkg.devDependencies['ember-exam'] = '0.7.0'; - pkg.devDependencies['ember-cli-code-coverage'] = process.cwd(); + // Temporarily remove the addon before install to work around https://github.com/tomdale/ember-cli-addon-tests/issues/176 + delete pkg.devDependencies['ember-cli-code-coverage']; }); return app.run('npm', 'install').then(() => { + app.editPackageJSON(pkg => { + pkg.devDependencies['ember-cli-code-coverage'] = '*'; + }); + let addonPath = path.join(app.path, 'node_modules', 'ember-cli-code-coverage'); + fs.removeSync(addonPath); + fs.ensureSymlinkSync(process.cwd(), addonPath); return rimraf(`${app.path}/coverage*`); }); });