diff --git a/lib/assertion-visitor.js b/lib/assertion-visitor.js index d3b6b86..0c8f4dc 100644 --- a/lib/assertion-visitor.js +++ b/lib/assertion-visitor.js @@ -28,7 +28,7 @@ function astEqual (ast1, ast2) { return deepEqual(espurify(ast1), espurify(ast2)); } -function AssertionVisitor (matcher, assertionPath, options) { +function AssertionVisitor (matcher, assertionPath, enclosingFunc, options) { this.matcher = matcher; this.assertionPath = [].concat(assertionPath); this.options = options || {}; @@ -37,6 +37,8 @@ function AssertionVisitor (matcher, assertionPath, options) { } this.currentArgumentPath = null; this.argumentModified = false; + this.withinGenerator = enclosingFunc && enclosingFunc.generator; + this.withinAsync = enclosingFunc && enclosingFunc.async; } AssertionVisitor.prototype.enter = function (currentNode, parentNode) { @@ -148,8 +150,14 @@ AssertionVisitor.prototype.captureArgument = function (node) { var n = newNodeWithLocationCopyOf(node); var props = []; var newCalleeObject = updateLocRecursively(espurify(this.powerAssertCalleeObject), n, this.options.visitorKeys); + if (this.withinAsync) { + addLiteralTo(props, n, 'async', true); + } addLiteralTo(props, n, 'content', this.canonicalCode); addLiteralTo(props, n, 'filepath', this.filepath); + if (this.withinGenerator) { + addLiteralTo(props, n, 'generator', true); + } addLiteralTo(props, n, 'line', this.lineNum); return n({ type: syntax.CallExpression, diff --git a/lib/instrumentor.js b/lib/instrumentor.js index 7158ab7..c37554c 100644 --- a/lib/instrumentor.js +++ b/lib/instrumentor.js @@ -37,7 +37,8 @@ Instrumentor.prototype.instrument = function (ast) { var candidates = that.matchers.filter(function (matcher) { return matcher.test(currentNode); }); if (candidates.length === 1) { // entering target assertion - assertionVisitor = new AssertionVisitor(candidates[0], path, that.options); + var enclosingFunc = findEnclosingFunction(controller.parents()); + assertionVisitor = new AssertionVisitor(candidates[0], path, enclosingFunc, that.options); assertionVisitor.enter(currentNode, parentNode); return undefined; } @@ -83,6 +84,23 @@ function isCalleeOfParentCallExpression (parentNode, currentKey) { return parentNode.type === syntax.CallExpression && currentKey === 'callee'; } +function isFunction(node) { + return [ + syntax.FunctionDeclaration, + syntax.FunctionExpression, + syntax.ArrowFunctionExpression + ].indexOf(node.type) !== -1; +} + +function findEnclosingFunction(parents) { + for (var i = parents.length - 1; i >= 0; i--) { + if (isFunction(parents[i])) { + return parents[i]; + } + } + return null; +} + function verifyAstPrerequisites (ast, options) { var errorMessage; if (typeof ast.loc === 'undefined') { diff --git a/test/instrumentation_test.js b/test/instrumentation_test.js index 7547e36..f12a55c 100644 --- a/test/instrumentation_test.js +++ b/test/instrumentation_test.js @@ -30,11 +30,11 @@ describe('instrumentation spec', function () { function inst (jsCode, expected) { describe('with loc, range', function () { - var options = {ecmaVersion: 7, locations: true, ranges: true, plugins: {asyncawait: {awaitAnywhere: true}}}; + var options = {ecmaVersion: 7, locations: true, ranges: true, plugins: {asyncawait: true}}; testWithParserOptions(jsCode, expected, options); }); describe('with loc', function () { - var options = {ecmaVersion: 7, locations: true, plugins: {asyncawait: {awaitAnywhere: true}}}; + var options = {ecmaVersion: 7, locations: true, plugins: {asyncawait: true}}; testWithParserOptions(jsCode, expected, options); }); } @@ -400,12 +400,46 @@ describe('instrumentation spec', function () { describe('YieldExpression', function () { inst("function *gen() {assert((yield bigOrSmall(size)) === 'big')}", - "function*gen(){assert(assert._expr(assert._capt(assert._capt(yield bigOrSmall(assert._capt(size,'arguments/0/left/argument/arguments/0')),'arguments/0/left')==='big','arguments/0'),{content:'assert((yield bigOrSmall(size)) === \\'big\\')',filepath:'path/to/some_test.js',line:1}));}"); + "function*gen(){assert(assert._expr(assert._capt(assert._capt(yield bigOrSmall(assert._capt(size,'arguments/0/left/argument/arguments/0')),'arguments/0/left')==='big','arguments/0'),{content:'assert((yield bigOrSmall(size)) === \\'big\\')',filepath:'path/to/some_test.js',generator:true,line:1}));}"); + }); + + describe('YieldExpression vs FunctionCall disambiguation', function () { + inst("function baz() {assert((yield (foo)) === bar)}", + "function baz(){assert(assert._expr(assert._capt(assert._capt(yield(assert._capt(foo,'arguments/0/left/arguments/0')),'arguments/0/left')===assert._capt(bar,'arguments/0/right'),'arguments/0'),{content:'assert(yield(foo) === bar)',filepath:'path/to/some_test.js',line:1}));}"); + + inst("function *baz() {assert((yield (foo)) === bar)}", + "function*baz(){assert(assert._expr(assert._capt(assert._capt(yield foo,'arguments/0/left')===assert._capt(bar,'arguments/0/right'),'arguments/0'),{content:'assert((yield foo) === bar)',filepath:'path/to/some_test.js',generator:true,line:1}));}"); + + inst("var baz = function () {assert((yield (foo)) === bar)}", + "var baz=function(){assert(assert._expr(assert._capt(assert._capt(yield(assert._capt(foo,'arguments/0/left/arguments/0')),'arguments/0/left')===assert._capt(bar,'arguments/0/right'),'arguments/0'),{content:'assert(yield(foo) === bar)',filepath:'path/to/some_test.js',line:1}));};"); + + inst("var baz = function *() {assert((yield (foo)) === bar)}", + "var baz=function*(){assert(assert._expr(assert._capt(assert._capt(yield foo,'arguments/0/left')===assert._capt(bar,'arguments/0/right'),'arguments/0'),{content:'assert((yield foo) === bar)',filepath:'path/to/some_test.js',generator:true,line:1}));};"); }); describe('AwaitExpression', function () { - inst("function *gen() {assert((await bigOrSmall(size)) === 'big')}", - "function*gen(){assert(assert._expr(assert._capt(assert._capt(await bigOrSmall(assert._capt(size,'arguments/0/left/argument/arguments/0')),'arguments/0/left')==='big','arguments/0'),{content:'assert((await bigOrSmall(size)) === \\'big\\')',filepath:'path/to/some_test.js',line:1}));}"); + inst("async function gen() {assert((await bigOrSmall(size)) === 'big')}", + "async function gen(){assert(assert._expr(assert._capt(assert._capt(await bigOrSmall(assert._capt(size,'arguments/0/left/argument/arguments/0')),'arguments/0/left')==='big','arguments/0'),{async:true,content:'assert((await bigOrSmall(size)) === \\'big\\')',filepath:'path/to/some_test.js',line:1}));}"); + }); + + describe('AwaitExpression vs FunctionCall disambiguation', function () { + inst("function baz() {assert((await (foo)) === bar)}", + "function baz(){assert(assert._expr(assert._capt(assert._capt(await(assert._capt(foo,'arguments/0/left/arguments/0')),'arguments/0/left')===assert._capt(bar,'arguments/0/right'),'arguments/0'),{content:'assert(await(foo) === bar)',filepath:'path/to/some_test.js',line:1}));}"); + + inst("async function baz() {assert((await (foo)) === bar)}", + "async function baz(){assert(assert._expr(assert._capt(assert._capt(await foo,'arguments/0/left')===assert._capt(bar,'arguments/0/right'),'arguments/0'),{async:true,content:'assert((await foo) === bar)',filepath:'path/to/some_test.js',line:1}));}"); + + inst("var baz = function () {assert((await (foo)) === bar)}", + "var baz=function(){assert(assert._expr(assert._capt(assert._capt(await(assert._capt(foo,'arguments/0/left/arguments/0')),'arguments/0/left')===assert._capt(bar,'arguments/0/right'),'arguments/0'),{content:'assert(await(foo) === bar)',filepath:'path/to/some_test.js',line:1}));};"); + + inst("var baz = async function () {assert((await (foo)) === bar)}", + "var baz=async function(){assert(assert._expr(assert._capt(assert._capt(await foo,'arguments/0/left')===assert._capt(bar,'arguments/0/right'),'arguments/0'),{async:true,content:'assert((await foo) === bar)',filepath:'path/to/some_test.js',line:1}));};"); + + inst("var baz = () => {assert((await (foo)) === bar)};", + "var baz=()=>{assert(assert._expr(assert._capt(assert._capt(await(assert._capt(foo,'arguments/0/left/arguments/0')),'arguments/0/left')===assert._capt(bar,'arguments/0/right'),'arguments/0'),{content:'assert(await(foo) === bar)',filepath:'path/to/some_test.js',line:1}));};"); + + inst("var baz = async () => {assert((await (foo)) === bar)}", + "var baz=async()=>{assert(assert._expr(assert._capt(assert._capt(await foo,'arguments/0/left')===assert._capt(bar,'arguments/0/right'),'arguments/0'),{async:true,content:'assert((await foo) === bar)',filepath:'path/to/some_test.js',line:1}));};"); }); describe('Enhanced Object Literals', function () {