'use strict'; var forEach = require('core-js/library/fn/array/for-each'); var filter = require('core-js/library/fn/array/filter'); var map = require('core-js/library/fn/array/map'); var signature = require('call-signature'); var decorate = require('./decorate'); var keys = require('core-js/library/fn/object/keys'); function Decorator (receiver, config) { this.receiver = receiver; this.config = config; this.onError = config.onError; this.onSuccess = config.onSuccess; this.signatures = map(config.patterns, parse); this.wrapOnlySignatures = map(config.wrapOnlyPatterns, parse); } Decorator.prototype.enhancement = function () { var that = this; var container = this.container(); var wrappedMethods = []; function attach(matcherSpec, enhanced) { var matcher = matcherSpec.parsed; var methodName = detectMethodName(matcher.callee); if (typeof that.receiver[methodName] !== 'function' || wrappedMethods.indexOf(methodName) !== -1) { return; } var callSpec = { thisObj: that.receiver, func: that.receiver[methodName], numArgsToCapture: numberOfArgumentsToCapture(matcherSpec), matcherSpec: matcherSpec, enhanced: enhanced }; container[methodName] = callSpec.enhancedFunc = decorate(callSpec, that); wrappedMethods.push(methodName); } forEach(filter(this.signatures, methodCall), function (matcher) { attach(matcher, true); }); forEach(filter(this.wrapOnlySignatures, methodCall), function (matcher) { attach(matcher, false); }); return container; }; Decorator.prototype.container = function () { var basement = {}; if (typeof this.receiver === 'function') { var candidates = filter(this.signatures, functionCall); var enhanced = true; if (candidates.length === 0) { enhanced = false; candidates = filter(this.wrapOnlySignatures, functionCall); } if (candidates.length === 1) { var callSpec = { thisObj: null, func: this.receiver, numArgsToCapture: numberOfArgumentsToCapture(candidates[0]), matcherSpec: candidates[0], enhanced: enhanced }; basement = callSpec.enhancedFunc = decorate(callSpec, this); } } return basement; }; Decorator.prototype.concreteAssert = function (callSpec, invocation, context) { var func = callSpec.func; var thisObj = this.config.bindReceiver ? callSpec.thisObj : invocation.thisObj; var enhanced = callSpec.enhanced; var args = invocation.values; var message = invocation.message; var matcherSpec = callSpec.matcherSpec; if (context && typeof this.config.modifyMessageBeforeAssert === 'function') { message = this.config.modifyMessageBeforeAssert({originalMessage: message, powerAssertContext: context}); } args = args.concat(message); var data = { thisObj: invocation.thisObj, assertionFunction: callSpec.enhancedFunc, originalMessage: message, defaultMessage: matcherSpec.defaultMessage, matcherSpec: matcherSpec, enhanced: enhanced, args: args }; if (context) { data.powerAssertContext = context; } return this._callFunc(func, thisObj, args, data); }; // see: https://github.com/twada/empower-core/pull/8#issuecomment-173480982 Decorator.prototype._callFunc = function (func, thisObj, args, data) { var ret; try { ret = func.apply(thisObj, args); } catch (e) { data.assertionThrew = true; data.error = e; return this.onError.call(thisObj, data); } data.assertionThrew = false; data.returnValue = ret; return this.onSuccess.call(thisObj, data); }; function numberOfArgumentsToCapture (matcherSpec) { var matcher = matcherSpec.parsed; var len = matcher.args.length; var lastArg; if (0 < len) { lastArg = matcher.args[len - 1]; if (lastArg.name === 'message' && lastArg.optional) { len -= 1; } } return len; } function detectMethodName (callee) { if (callee.type === 'MemberExpression') { return callee.member; } return null; } function functionCall (matcherSpec) { return matcherSpec.parsed.callee.type === 'Identifier'; } function methodCall (matcherSpec) { return matcherSpec.parsed.callee.type === 'MemberExpression'; } function parse(matcherSpec) { if (typeof matcherSpec === 'string') { matcherSpec = {pattern: matcherSpec}; } var ret = {}; forEach(keys(matcherSpec), function (key) { ret[key] = matcherSpec[key]; }); ret.parsed = signature.parse(matcherSpec.pattern); return ret; } module.exports = Decorator;