From ab4d5cf23dc354c244ccb7ef308c3c9a83eef0ce Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sat, 18 Mar 2017 21:42:45 -0400 Subject: [PATCH 1/7] 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 4d6119108892b1eb4e54ed6aeacc190ab6d464f9 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sat, 18 Mar 2017 21:43:09 -0400 Subject: [PATCH 2/7] Add template instrumenter. --- index.js | 12 +++ lib/template-instrumenter.js | 173 +++++++++++++++++++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 lib/template-instrumenter.js diff --git a/index.js b/index.js index 95a02edb..1e20d0d3 100644 --- a/index.js +++ b/index.js @@ -14,6 +14,18 @@ module.exports = { // Ember Methods + setupPreprocessorRegistry: function(type, registry) { + if (!this._isCoverageEnabled()) { return; } + + var TemplateInstrumenter = require('./lib/template-instrumenter'); + + registry.add('htmlbars-ast-plugin', { + name: "template-instrumenter", + plugin: TemplateInstrumenter, + baseDir: __dirname + }); + }, + included: function() { if (this._isCoverageEnabled() && this.parent.isEmberCLIAddon()) { var coveredAddon = this._findCoveredAddon(); 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 49784dcc44286424720e5e0c5b3786ace32a69c8 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sat, 18 Mar 2017 22:01:21 -0400 Subject: [PATCH 3/7] Use shared code to fix module name paths. --- index.js | 8 +- lib/coverage-instrumenter.js | 1 + lib/template-instrumenter.js | 299 ++++++++++++++++++----------------- 3 files changed, 161 insertions(+), 147 deletions(-) diff --git a/index.js b/index.js index 1e20d0d3..7cc404ae 100644 --- a/index.js +++ b/index.js @@ -17,7 +17,13 @@ module.exports = { setupPreprocessorRegistry: function(type, registry) { if (!this._isCoverageEnabled()) { return; } - var TemplateInstrumenter = require('./lib/template-instrumenter'); + 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", diff --git a/lib/coverage-instrumenter.js b/lib/coverage-instrumenter.js index cce2b863..a7fc40f4 100644 --- a/lib/coverage-instrumenter.js +++ b/lib/coverage-instrumenter.js @@ -117,3 +117,4 @@ CoverageInstrumenter.prototype.processString = function(content, relativePath) { }; module.exports = CoverageInstrumenter; +module.exports.fixPath = fixPath; 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 cf793dfd6821269569fe71a99e87933aa958d430 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sat, 18 Mar 2017 23:57:03 -0400 Subject: [PATCH 4/7] 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 81b8191389ade9e4e73673dacdde5854d3e8e82e Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sun, 19 Mar 2017 00:01:48 -0400 Subject: [PATCH 5/7] 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 e9b352e50217e7106843c182295980b30b140748 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sun, 19 Mar 2017 00:02:05 -0400 Subject: [PATCH 6/7] 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 755013a37c6bc71ec78facfd3e33e65a4fdbbbe9 Mon Sep 17 00:00:00 2001 From: Robert Jackson Date: Sun, 19 Mar 2017 12:31:11 -0400 Subject: [PATCH 7/7] 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;