From 700aaa499689e29c4b89b809d899bb178966ee82 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sat, 18 Mar 2017 21:42:45 -0400 Subject: [PATCH 1/8] Add basic helpers for coverage. --- addon/.eslintrc.js | 13 ++++ .../ember-cli-code-coverage-increment.js | 15 +++++ .../ember-cli-code-coverage-register.js | 8 +++ .../ember-cli-code-coverage-increment.js | 1 + .../ember-cli-code-coverage-register.js | 1 + .../ember-cli-code-coverage-increment-test.js | 61 +++++++++++++++++++ .../ember-cli-code-coverage-register-test.js | 41 +++++++++++++ 7 files changed, 140 insertions(+) create mode 100644 addon/.eslintrc.js create mode 100644 addon/helpers/ember-cli-code-coverage-increment.js create mode 100644 addon/helpers/ember-cli-code-coverage-register.js create mode 100644 app/helpers/ember-cli-code-coverage-increment.js create mode 100644 app/helpers/ember-cli-code-coverage-register.js create mode 100644 tests/unit/helpers/ember-cli-code-coverage-increment-test.js create mode 100644 tests/unit/helpers/ember-cli-code-coverage-register-test.js diff --git a/addon/.eslintrc.js b/addon/.eslintrc.js new file mode 100644 index 00000000..fbfc3640 --- /dev/null +++ b/addon/.eslintrc.js @@ -0,0 +1,13 @@ +module.exports = { + root: true, + parserOptions: { + ecmaVersion: 6, + sourceType: 'module' + }, + extends: 'eslint:recommended', + env: { + browser: true + }, + rules: { + } +}; diff --git a/addon/helpers/ember-cli-code-coverage-increment.js b/addon/helpers/ember-cli-code-coverage-increment.js new file mode 100644 index 00000000..b2ccec27 --- /dev/null +++ b/addon/helpers/ember-cli-code-coverage-increment.js @@ -0,0 +1,15 @@ +import Ember from 'ember'; + +export function emberCliCodeCoverageIncrement(params, hash) { + let { path, statement, branch, condition } = hash; + + if (statement) { + window.__coverage__[path].s[statement]++; + } + + if (branch && condition) { + window.__coverage__[path].b[branch][condition]++; + } +} + +export default Ember.Helper.helper(emberCliCodeCoverageIncrement); diff --git a/addon/helpers/ember-cli-code-coverage-register.js b/addon/helpers/ember-cli-code-coverage-register.js new file mode 100644 index 00000000..2e5ee2b1 --- /dev/null +++ b/addon/helpers/ember-cli-code-coverage-register.js @@ -0,0 +1,8 @@ +import Ember from 'ember'; + +export function emberCliCodeCoverageRegister([rawData]) { + let coverageData = JSON.parse(rawData); + window.__coverage__[coverageData.path] = coverageData; +} + +export default Ember.Helper.helper(emberCliCodeCoverageRegister); diff --git a/app/helpers/ember-cli-code-coverage-increment.js b/app/helpers/ember-cli-code-coverage-increment.js new file mode 100644 index 00000000..49fea3b2 --- /dev/null +++ b/app/helpers/ember-cli-code-coverage-increment.js @@ -0,0 +1 @@ +export { default, emberCliCodeCoverageIncrement } from 'ember-cli-code-coverage/helpers/ember-cli-code-coverage-increment'; diff --git a/app/helpers/ember-cli-code-coverage-register.js b/app/helpers/ember-cli-code-coverage-register.js new file mode 100644 index 00000000..f92ea09c --- /dev/null +++ b/app/helpers/ember-cli-code-coverage-register.js @@ -0,0 +1 @@ +export { default, emberCliCodeCoverageRegister } from 'ember-cli-code-coverage/helpers/ember-cli-code-coverage-register'; diff --git a/tests/unit/helpers/ember-cli-code-coverage-increment-test.js b/tests/unit/helpers/ember-cli-code-coverage-increment-test.js new file mode 100644 index 00000000..09a2506f --- /dev/null +++ b/tests/unit/helpers/ember-cli-code-coverage-increment-test.js @@ -0,0 +1,61 @@ +import { emberCliCodeCoverageIncrement } from 'dummy/helpers/ember-cli-code-coverage-increment'; +import { module, test } from 'qunit'; + +const ORIGINAL_COVERAGE = window.__coverage__; + +function registerFile(path) { + window.__coverage__[path] = { + "path": path, + "s": { + "1": 0, + "2": 0, + "3": 0, + "4": 0, + "5": 0, + "6": 0 + }, + "b": { + "1": [0, 0], + "2": [0, 0] + }, + "f": { }, + "fnMap": { }, + "statementMap": { + // not needed for testing + }, + "branchMap": { + // not needed for testing + }, + "code": [ + // not needed for testing + ] + }; +} + +module('Unit | Helper | ember cli code coverage increment', { + beforeEach() { + window.__coverage__ = {}; + }, + + afterEach() { + window.__coverage__ = ORIGINAL_COVERAGE; + } +}); + +test('it increments the given statement', function(assert) { + let path = 'app/templates/foo'; + registerFile(path); + + emberCliCodeCoverageIncrement([], { path, statement: "1" }); + + assert.equal(window.__coverage__[path].s["1"], 1, 'statement was incremented'); +}); + +test('it increments the given branch', function(assert) { + let path = 'app/templates/foo'; + registerFile(path); + + emberCliCodeCoverageIncrement([], { path, branch: '1', condition: '0' }); + + assert.equal(window.__coverage__[path].b[1][0], 1, 'branch was incremented'); +}); diff --git a/tests/unit/helpers/ember-cli-code-coverage-register-test.js b/tests/unit/helpers/ember-cli-code-coverage-register-test.js new file mode 100644 index 00000000..dfe2deca --- /dev/null +++ b/tests/unit/helpers/ember-cli-code-coverage-register-test.js @@ -0,0 +1,41 @@ +import { emberCliCodeCoverageRegister } from 'dummy/helpers/ember-cli-code-coverage-register'; +import { module, test } from 'qunit'; + +const ORIGINAL_COVERAGE = window.__coverage__; + +module('Unit | Helper | ember cli code coverage register', { + beforeEach() { + window.__coverage__ = {}; + this.fileData = { + "path": 'app/templates/foo', + "s": { + "1": 0, + "2": 0 + }, + "b": { + "1": [0, 0], + "2": [0, 0] + }, + "f": { }, + "fnMap": { }, + "statementMap": {}, + "branchMap": {}, + "code": [] + }; + }, + + afterEach() { + window.__coverage__ = ORIGINAL_COVERAGE; + } +}); + +// Replace this with your real tests. +test('registers the given JSON data for the path', function(assert) { + emberCliCodeCoverageRegister([JSON.stringify(this.fileData)]); + + assert.deepEqual( + window.__coverage__[this.fileData.path], + this.fileData, + 'registered matches' + ); +}); From 1c79eb46484b3f2a7d4d2225395567b42c725fc3 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sat, 18 Mar 2017 21:43:09 -0400 Subject: [PATCH 2/8] Add template instrumenter. --- index.js | 18 ++++ lib/template-instrumenter.js | 173 +++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+) create mode 100644 lib/template-instrumenter.js diff --git a/index.js b/index.js index 6c8fc0c9..4e687587 100644 --- a/index.js +++ b/index.js @@ -43,6 +43,24 @@ module.exports = { fileLookup: null, // Ember Methods + setupPreprocessorRegistry: function(type, registry) { + if (!this._isCoverageEnabled()) { return; } + + const buildTemplateInstrumenter = require('./lib/template-instrumenter'); + let TemplateInstrumenter = buildTemplateInstrumenter( + this._parentName(), + this.parent.root, + this.registry.extensionsForType('template'), + this.project.isEmberCLIAddon() + ); + + registry.add('htmlbars-ast-plugin', { + name: "template-instrumenter", + plugin: TemplateInstrumenter, + baseDir: __dirname + }); + }, + included: function(appOrAddon) { this._super.included.apply(this, arguments); diff --git a/lib/template-instrumenter.js b/lib/template-instrumenter.js new file mode 100644 index 00000000..d51776e5 --- /dev/null +++ b/lib/template-instrumenter.js @@ -0,0 +1,173 @@ +'use strict'; + +const LINE_ENDINGS = /(?:\r\n?|\n)/; + +module.exports = class IstanbulInstrumenter { + constructor(options) { + + this.options = options; + this.moduleName = options.meta && options.meta.moduleName; + + this.coverageData = { + path: this.moduleName, + s: { }, + b: { }, + f: { }, + fnMap: { }, + statementMap: { }, + branchMap: { }, + code: [ ] + }; + + this._currentStatement = 0; + this._currentBranch = 0; + + if (options.contents) { + this.coverageData.code = options.contents.split(LINE_ENDINGS); + } + } + + currentContainer() { + return this._containerStack[this._containerStack.length - 1]; + } + + insertHelper(container, node, hash) { + let children = container.body || container.children; + let index = children.indexOf(node); + let b = this.syntax.builders; + + hash.pairs.push( + b.pair('path', b.string(this.moduleName)) + ); + + let helper = b.mustache( + b.path('ember-cli-code-coverage-increment'), + null, + hash + ); + helper.isCoverageHelper = true; + + container._statementsToInsert = container._statementsToInsert || []; + container._statementsToInsert.unshift({ + helper, + index + }); + } + + insertStatementHelper(node) { + let b = this.syntax.builders; + + let hash = b.hash([ + b.pair('statement', b.string(this._currentStatement)) + ]); + this.insertHelper(this.currentContainer(), node, hash); + } + + insertBranchHelper(container, node, condition) { + let b = this.syntax.builders; + + let hash = b.hash([ + b.pair('branch', b.string(this._currentBranch)), + b.pair('condition', b.string(condition)) + ]); + + this.insertHelper(container, node, hash); + } + + processStatementsToInsert(node) { + if (node._statementsToInsert) { + node._statementsToInsert.forEach((statement) => { + let { helper, index } = statement; + + let children = node.children || node.body; + children.splice(index, 0, helper); + }); + } + } + + handleBlock(node) { + // blocks are statements + // blocks have contents + this.handleStatement(node); + this._currentBranch++; + this.coverageData.b[this._currentBranch] = 0; + this.coverageData.branchMap[this._currentStatement] = { + start: { line: node.loc.start.line, column: node.loc.start.column }, + end: { line: node.loc.end.line, column: node.loc.end.column }, + }; + + if (node.type === 'BlockStatement') { + this.insertBranchHelper(node.program, node); + } + } + + handleStatement(node) { + if (node.type === 'TextNode' && node.chars.trim() === '') { + return; + } + + if (node.isCoverageHelper) { return; } + + this._currentStatement++; + this.coverageData.s[this._currentStatement] = 0; + this.coverageData.statementMap[this._currentStatement] = { + start: { line: node.loc.start.line, column: node.loc.start.column }, + end: { line: node.loc.end.line, column: node.loc.end.column }, + }; + + this.insertStatementHelper(node); + } + + transform(ast) { + let handleBlock = { + enter: (node) => { + this.handleBlock(node); + this._containerStack.push(node); + }, + exit: (node) => { + this._containerStack.pop(); + this.processStatementsToInsert(node); + } + }; + + let handleStatement = (node) => this.handleStatement(node); + + let b = this.syntax.builders; + + this.syntax.traverse(ast, { + Program: { + enter: (node) => { + if (!this._topLevelProgram) { + this._topLevelProgram = node; + this._containerStack = [node]; + } else { + this._containerStack.push(node); + } + }, + exit: (node) => { + this.processStatementsToInsert(node); + if (node === this._topLevelProgram) { + let helper = b.mustache( + b.path('ember-cli-code-coverage-register'), + [ + b.string(JSON.stringify(this.coverageData)) + ] + ); + helper.isCoverageHelper = true; + + node.body.unshift(helper); + } else { + this._containerStack.pop(); + } + }, + }, + + ElementNode: handleBlock, + BlockStatement: handleBlock, + MustacheStatement: handleStatement, + TextNode: handleStatement, + }); + + return ast; + } +}; From 6f090712dd8ee456ea69c8964f2ebc97bb32c976 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sat, 18 Mar 2017 22:01:21 -0400 Subject: [PATCH 3/8] Use shared code to fix module name paths. --- lib/template-instrumenter.js | 299 ++++++++++++++++++----------------- 1 file changed, 153 insertions(+), 146 deletions(-) diff --git a/lib/template-instrumenter.js b/lib/template-instrumenter.js index d51776e5..6d1525e7 100644 --- a/lib/template-instrumenter.js +++ b/lib/template-instrumenter.js @@ -1,173 +1,180 @@ 'use strict'; const LINE_ENDINGS = /(?:\r\n?|\n)/; +const fixPath = require('./coverage-instrumenter').fixPath; -module.exports = class IstanbulInstrumenter { - constructor(options) { +module.exports = function(appName, appRoot, templateExtensions, isAddon) { + return class IstanbulInstrumenter { + constructor(options) { - this.options = options; - this.moduleName = options.meta && options.meta.moduleName; + this.options = options; - this.coverageData = { - path: this.moduleName, - s: { }, - b: { }, - f: { }, - fnMap: { }, - statementMap: { }, - branchMap: { }, - code: [ ] - }; + let moduleName = options.meta.moduleName; + let relativePath = fixPath(moduleName, appName, appRoot, templateExtensions, isAddon); - this._currentStatement = 0; - this._currentBranch = 0; + this.relativePath = relativePath; - if (options.contents) { - this.coverageData.code = options.contents.split(LINE_ENDINGS); + this.coverageData = { + path: this.relativePath, + s: { }, + b: { }, + f: { }, + fnMap: { }, + statementMap: { }, + branchMap: { }, + code: [ ] + }; + + this._currentStatement = 0; + this._currentBranch = 0; + + if (options.contents) { + this.coverageData.code = options.contents.split(LINE_ENDINGS); + } } - } - - currentContainer() { - return this._containerStack[this._containerStack.length - 1]; - } - - insertHelper(container, node, hash) { - let children = container.body || container.children; - let index = children.indexOf(node); - let b = this.syntax.builders; - - hash.pairs.push( - b.pair('path', b.string(this.moduleName)) - ); - - let helper = b.mustache( - b.path('ember-cli-code-coverage-increment'), - null, - hash - ); - helper.isCoverageHelper = true; - - container._statementsToInsert = container._statementsToInsert || []; - container._statementsToInsert.unshift({ - helper, - index - }); - } - - insertStatementHelper(node) { - let b = this.syntax.builders; - - let hash = b.hash([ - b.pair('statement', b.string(this._currentStatement)) - ]); - this.insertHelper(this.currentContainer(), node, hash); - } - - insertBranchHelper(container, node, condition) { - let b = this.syntax.builders; - - let hash = b.hash([ - b.pair('branch', b.string(this._currentBranch)), - b.pair('condition', b.string(condition)) - ]); - - this.insertHelper(container, node, hash); - } - - processStatementsToInsert(node) { - if (node._statementsToInsert) { - node._statementsToInsert.forEach((statement) => { - let { helper, index } = statement; - - let children = node.children || node.body; - children.splice(index, 0, helper); + + currentContainer() { + return this._containerStack[this._containerStack.length - 1]; + } + + insertHelper(container, node, hash) { + let children = container.body || container.children; + let index = children.indexOf(node); + let b = this.syntax.builders; + + hash.pairs.push( + b.pair('path', b.string(this.relativePath)) + ); + + let helper = b.mustache( + b.path('ember-cli-code-coverage-increment'), + null, + hash + ); + helper.isCoverageHelper = true; + + container._statementsToInsert = container._statementsToInsert || []; + container._statementsToInsert.unshift({ + helper, + index }); } - } - - handleBlock(node) { - // blocks are statements - // blocks have contents - this.handleStatement(node); - this._currentBranch++; - this.coverageData.b[this._currentBranch] = 0; - this.coverageData.branchMap[this._currentStatement] = { - start: { line: node.loc.start.line, column: node.loc.start.column }, - end: { line: node.loc.end.line, column: node.loc.end.column }, - }; - - if (node.type === 'BlockStatement') { - this.insertBranchHelper(node.program, node); + + insertStatementHelper(node) { + let b = this.syntax.builders; + + let hash = b.hash([ + b.pair('statement', b.string(this._currentStatement)) + ]); + this.insertHelper(this.currentContainer(), node, hash); } - } - handleStatement(node) { - if (node.type === 'TextNode' && node.chars.trim() === '') { - return; + insertBranchHelper(container, node, condition) { + let b = this.syntax.builders; + + let hash = b.hash([ + b.pair('branch', b.string(this._currentBranch)), + b.pair('condition', b.string(condition)) + ]); + + this.insertHelper(container, node, hash); } - if (node.isCoverageHelper) { return; } - - this._currentStatement++; - this.coverageData.s[this._currentStatement] = 0; - this.coverageData.statementMap[this._currentStatement] = { - start: { line: node.loc.start.line, column: node.loc.start.column }, - end: { line: node.loc.end.line, column: node.loc.end.column }, - }; - - this.insertStatementHelper(node); - } - - transform(ast) { - let handleBlock = { - enter: (node) => { - this.handleBlock(node); - this._containerStack.push(node); - }, - exit: (node) => { - this._containerStack.pop(); - this.processStatementsToInsert(node); + processStatementsToInsert(node) { + if (node._statementsToInsert) { + node._statementsToInsert.forEach((statement) => { + let { helper, index } = statement; + + let children = node.children || node.body; + children.splice(index, 0, helper); + }); } - }; + } + + handleBlock(node) { + // blocks are statements + // blocks have contents + this.handleStatement(node); + this._currentBranch++; + this.coverageData.b[this._currentBranch] = 0; + this.coverageData.branchMap[this._currentStatement] = { + start: { line: node.loc.start.line, column: node.loc.start.column }, + end: { line: node.loc.end.line, column: node.loc.end.column }, + }; + + if (node.type === 'BlockStatement') { + this.insertBranchHelper(node.program, node); + } + } + + handleStatement(node) { + if (node.type === 'TextNode' && node.chars.trim() === '') { + return; + } + + if (node.isCoverageHelper) { return; } - let handleStatement = (node) => this.handleStatement(node); + this._currentStatement++; + this.coverageData.s[this._currentStatement] = 0; + this.coverageData.statementMap[this._currentStatement] = { + start: { line: node.loc.start.line, column: node.loc.start.column }, + end: { line: node.loc.end.line, column: node.loc.end.column }, + }; - let b = this.syntax.builders; + this.insertStatementHelper(node); + } - this.syntax.traverse(ast, { - Program: { + transform(ast) { + let handleBlock = { enter: (node) => { - if (!this._topLevelProgram) { - this._topLevelProgram = node; - this._containerStack = [node]; - } else { - this._containerStack.push(node); - } + this.handleBlock(node); + this._containerStack.push(node); }, exit: (node) => { + this._containerStack.pop(); this.processStatementsToInsert(node); - if (node === this._topLevelProgram) { - let helper = b.mustache( - b.path('ember-cli-code-coverage-register'), - [ - b.string(JSON.stringify(this.coverageData)) - ] - ); - helper.isCoverageHelper = true; - - node.body.unshift(helper); - } else { - this._containerStack.pop(); - } + } + }; + + let handleStatement = (node) => this.handleStatement(node); + + let b = this.syntax.builders; + + this.syntax.traverse(ast, { + Program: { + enter: (node) => { + if (!this._topLevelProgram) { + this._topLevelProgram = node; + this._containerStack = [node]; + } else { + this._containerStack.push(node); + } + }, + exit: (node) => { + this.processStatementsToInsert(node); + if (node === this._topLevelProgram) { + let helper = b.mustache( + b.path('ember-cli-code-coverage-register'), + [ + b.string(JSON.stringify(this.coverageData)) + ] + ); + helper.isCoverageHelper = true; + + node.body.unshift(helper); + } else { + this._containerStack.pop(); + } + }, }, - }, - ElementNode: handleBlock, - BlockStatement: handleBlock, - MustacheStatement: handleStatement, - TextNode: handleStatement, - }); + ElementNode: handleBlock, + BlockStatement: handleBlock, + MustacheStatement: handleStatement, + TextNode: handleStatement, + }); - return ast; - } + return ast; + } + }; }; From 193120374459f5e616791d6785a8674ebcd7a211 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sat, 18 Mar 2017 23:57:03 -0400 Subject: [PATCH 4/8] Only instrument files in `app` and `addon`. Likely need to expose better hooks for this, but this seems good enough for now. --- lib/template-instrumenter.js | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/lib/template-instrumenter.js b/lib/template-instrumenter.js index 6d1525e7..d290bf5d 100644 --- a/lib/template-instrumenter.js +++ b/lib/template-instrumenter.js @@ -1,6 +1,7 @@ 'use strict'; const LINE_ENDINGS = /(?:\r\n?|\n)/; +require('string.prototype.startswith'); const fixPath = require('./coverage-instrumenter').fixPath; module.exports = function(appName, appRoot, templateExtensions, isAddon) { @@ -33,6 +34,12 @@ module.exports = function(appName, appRoot, templateExtensions, isAddon) { } } + shouldInstrument() { + let relativePath = this.relativePath; + + return !relativePath || relativePath.startsWith('app') || relativePath.startsWith('addon'); + } + currentContainer() { return this._containerStack[this._containerStack.length - 1]; } @@ -117,14 +124,24 @@ module.exports = function(appName, appRoot, templateExtensions, isAddon) { this._currentStatement++; this.coverageData.s[this._currentStatement] = 0; this.coverageData.statementMap[this._currentStatement] = { - start: { line: node.loc.start.line, column: node.loc.start.column }, - end: { line: node.loc.end.line, column: node.loc.end.column }, + start: { + line: node.loc && node.loc.start.line, + column: node.loc && node.loc.start.column + }, + end: { + line: node.loc && node.loc.end.line, + column: node.loc && node.loc.end.column + }, }; this.insertStatementHelper(node); } transform(ast) { + if (!this.shouldInstrument()) { + return; + } + let handleBlock = { enter: (node) => { this.handleBlock(node); From 571bec7b83476b725df99beb09fc5bde113bb98a Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sun, 19 Mar 2017 00:01:48 -0400 Subject: [PATCH 5/8] Avoid errors when moduleName is not provided. --- lib/template-instrumenter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/template-instrumenter.js b/lib/template-instrumenter.js index d290bf5d..7e88bc85 100644 --- a/lib/template-instrumenter.js +++ b/lib/template-instrumenter.js @@ -11,7 +11,7 @@ module.exports = function(appName, appRoot, templateExtensions, isAddon) { this.options = options; let moduleName = options.meta.moduleName; - let relativePath = fixPath(moduleName, appName, appRoot, templateExtensions, isAddon); + let relativePath = moduleName && fixPath(moduleName, appName, appRoot, templateExtensions, isAddon); this.relativePath = relativePath; From 00ee3e7a321618cf35160fb7f51e1cdc27d49ad7 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sun, 19 Mar 2017 00:02:05 -0400 Subject: [PATCH 6/8] Fix generation of branch data. --- lib/template-instrumenter.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/template-instrumenter.js b/lib/template-instrumenter.js index 7e88bc85..57ab99b3 100644 --- a/lib/template-instrumenter.js +++ b/lib/template-instrumenter.js @@ -103,8 +103,8 @@ module.exports = function(appName, appRoot, templateExtensions, isAddon) { // blocks have contents this.handleStatement(node); this._currentBranch++; - this.coverageData.b[this._currentBranch] = 0; - this.coverageData.branchMap[this._currentStatement] = { + this.coverageData.b[this._currentBranch] = [0,0]; + this.coverageData.branchMap[this._currentBranch] = { start: { line: node.loc.start.line, column: node.loc.start.column }, end: { line: node.loc.end.line, column: node.loc.end.column }, }; From 2ddd6855db031322b8353c0e0a5dbafacc6ee195 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sun, 19 Mar 2017 12:31:11 -0400 Subject: [PATCH 7/8] Prevent inserting helpers into AttrNode's. --- lib/template-instrumenter.js | 37 ++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/lib/template-instrumenter.js b/lib/template-instrumenter.js index 57ab99b3..dacc84cd 100644 --- a/lib/template-instrumenter.js +++ b/lib/template-instrumenter.js @@ -99,9 +99,16 @@ module.exports = function(appName, appRoot, templateExtensions, isAddon) { } handleBlock(node) { - // blocks are statements - // blocks have contents + // cannot process blocks without a loc + if (!node.loc) { + return; + } + + if (node.isCoverageHelper) { return; } + if (this.currentContainer()._ignoreCoverage) { return; } + this.handleStatement(node); + this._currentBranch++; this.coverageData.b[this._currentBranch] = [0,0]; this.coverageData.branchMap[this._currentBranch] = { @@ -120,13 +127,23 @@ module.exports = function(appName, appRoot, templateExtensions, isAddon) { } if (node.isCoverageHelper) { return; } + if (this.currentContainer()._ignoreCoverage) { return; } + + // cannot process statements without a loc + if (!node.loc) { + return; + } + + if (node.loc.start.line == null) { + return; + } this._currentStatement++; this.coverageData.s[this._currentStatement] = 0; this.coverageData.statementMap[this._currentStatement] = { start: { - line: node.loc && node.loc.start.line, - column: node.loc && node.loc.start.column + line: node.loc.start.line, + column: node.loc.start.column }, end: { line: node.loc && node.loc.end.line, @@ -189,6 +206,18 @@ module.exports = function(appName, appRoot, templateExtensions, isAddon) { BlockStatement: handleBlock, MustacheStatement: handleStatement, TextNode: handleStatement, + + AttrNode: { + enter: (node) => { + this._containerStack.push(node); + // cannot properly inject helpers into AttrNode positions + node._ignoreCoverage = true; + }, + + exit: () => { + this._containerStack.pop(); + } + } }); return ast; From 8369091b1d989f8bbb9d388a519b72a2d91b0129 Mon Sep 17 00:00:00 2001 From: "L. Preston Sego III" Date: Mon, 15 Oct 2018 07:56:41 -0400 Subject: [PATCH 8/8] template instrumentation (merged master) (#200) * Update CHANGELOG for 0.3.12. * 0.3.12 * Improve ES7 error message This messaging was confusing to me so I took a stab at clarifying the intent. * add mocha support and fix pretender bug * [Fixes #111] Update dependencies, get babel instrumenter working (#115) babel 6 fix * test * test * add mocha support and fix pretender bug [Fixes #111] Update dependencies, get babel instrumenter working (#115) babel 6 fix * 0.4.0 * update changelog v0.4.0 * Fixes the hanging issue of #88 (#90) * Refactor onload to onreadystatechange onreadystatechange is more reliable, chromium doesnt trigger onload * Fix PhantomJS honoring responseType issue * 0.4.1 * update changelog v0.4.1 * Suggest using `posttest` hook for `ember coverage-merge` It might be helpful to suggest how/where to run `coverage-merge` for people looking to set this up in a CI environment. The only other thing I might add is that you need to run `npm test` and have the appropriate configuration there for `posttest` to trigger, but I'm not sure if that's overkill since that should be common knowledge. * Resolve addon file paths correctly in CLI >= 2.12 * Spelling Fix * support nested coverageFolder * fix test * Update minimum version of ember-cli-babel. The previously locked version was not compatible with `node@8` (due to `engines` shenanigans). * Add babel-plugin-istanbul dep. * Use babel-plugin-istanbul instead of custom instrumenter. This has some negative effects still: * Does not re-write the paths to match "real" on disk paths * Does not instrument dummy app files (I think) Even with these negative side-effects, it has massive upside: * Massively less overall code to maintain * Does not require us to parse babel config (and therefore avoids issues around parallelism in broccoli-babel-transpiler) * Significantly faster when used (e.g. no longer has to double parse and process files) * Refactor middleware to use new istanbul API. * Refactor coverage-merge command to work with new istanbul-api. * Add node badge Figured it would be nice to have a badge for the node version, so people can quickly see what the latest release is. * 0.4.2 * Fix paths for istanbul report and remove parallel logic for impicit parallel support * Delete uneeded files * Remove component fixtures and add tests for 'excludes' config * Add support for in-repo-addons #120 * Revert parallel changes (TODO: move to another PR) * Only include test fixtures when testing the addon. * Add index.js unit tests * Update some docs * Add comment about .istanbul.yml to README * Fix typo * Bump ember, fix lint * Adjust some deps * Bump ember-cli-release * Add ember-cli-changelog * Released v1.0.0-beta.0 * Add back "Avoid throwing errors while requiring files for coverage" #64 See #63 Fixes #150 * Upgrade out-of-date deps * Update sinon * Setup travis ci to release on pushed tag, add lerna-changelog - Documented in RELEASE.md * Add v1.0.0-beta.1 to CHANGELOG [ci skip] * 1.0.0-beta.1 * Don't restrict travis to particular branches * Try using travis stages * Reformat .travis.yml and do not require sudo * Pin auto-dist-tag and add --no-sandbox to chrome args if on travis * Split out script for use with matrix build * Adjust deploy config * Make fixtures external (#156) * Start moving test files to separate addon * First pass at ember-cli-addon-tests * Fix test * Remove addon test, fix lint * Remove treeFor * Try sudo required * Remove no sandbox * Drop node 4 from travis, use npm instead of yarn * Add 8 * Add filter * Add testem.js to fixtures * Add eslint * Remove eslint plugins * Start in-repo addon tests (#158) * Start in-repo addon tests * Update per Adam's suggestion * Fix import paths * fix babel-plugin-istanbul caching issue (#159) * fix babel-plugin-istanbul caching issue * - Refactor instrumentation logic given `babel-plugin-istanbul` constraints - Fix Unit tests * Update app-coverage-test.js * Update in-repo-addon-coverage-test.js * Try setting path to process.cwd * Fix tests to workaround https://github.com/tomdale/ember-cli-addon-tests/issues/176 * Update CHANGELOG for v1.0.0-beta.2. * 1.0.0-beta.2 * Support for addon-test-support (#160) * Start on support for addon-test-support * Remove only so all tests run * Try adding test-support prefix * Ensure addon-test-support coverage * Add tests for in-repo engines (#162) * First attempt at in-repo-engine * Fix engine coverage test * Fix lint * Update CHANGELOG for v1.0.0-beta.3. * 1.0.0-beta.3 * Remove merge-coverage and explicit parallel option (#163) * Remove merge-coverage and explicit parallel option * fix lint * Update README.md Describe how parallel works * Revert "Remove merge-coverage and explicit parallel option (#163)" This reverts commit 0592f5f9cc861c18b889c1d11622dbb7678f61b3. * Keep implicit and explicit parallel logic * Update babel-plugin-istanbul (#169) * Use the parent registry for determining JS extensions (#164) * Removing unused dependency exists-sync which fixes the deprecation warning from ember-cli (#179) * Update CHANGELOG for v1.0.0-beta.4. * 1.0.0-beta.4 * Fix fileLookup is null in testemMiddleware (#182) * upgrade istanbul-api to 2.0.1 (#186) istanbul-api@2.0.1 was released on June 6, 2018 and tagged as "next" on npm. The breaking change is pretty straightforward: https://github.com/istanbuljs/istanbuljs/blob/master/packages/istanbul-api/CHANGELOG.md#breaking-changes * Filter out in-repo addons that could not be found (#188) * Ember 3.4 (#190) * Bump deps (#191) * Bump deps * Reset engine test versions * Update CHANGELOG for v1.0.0-beta.5 * 1.0.0-beta.5 * Do not publish coverage, tests, or .idea to npm (#192) * Update babel-plugin-istanbul (#194) * Update CHANGELOG for v1.0.0-beta.6 * 1.0.0-beta.6 * TypeScript integration (howto) (#196) * TypeScript integration (howto) * removed some abstraction * 1.0.0-beta.6 * fix: handle babel 7 absolute paths (#199) --- index.js | 2 +- test/unit/index-test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 4e687587..52ef5feb 100644 --- a/index.js +++ b/index.js @@ -61,7 +61,6 @@ module.exports = { }); }, - included: function(appOrAddon) { this._super.included.apply(this, arguments); @@ -222,6 +221,7 @@ module.exports = { return walkSync(dir, { directories: false, globs }).map(file => { const postfix = hasEmberCliTypescript ? file : file.replace(EXT_RE, '.js'); const module = prefix + '/' + postfix; + this.fileLookup[module] = path.join(dirname, file); return module; }); diff --git a/test/unit/index-test.js b/test/unit/index-test.js index 9fa2956b..50f9f333 100644 --- a/test/unit/index-test.js +++ b/test/unit/index-test.js @@ -516,4 +516,4 @@ describe('index.js', function() { }); -}); \ No newline at end of file +});