Skip to content

Commit abd0e6f

Browse files
committed
Merge pull request #6 from jamestalmage/default-message
Allow a default message to be provided for a given pattern.
2 parents fdad3b5 + 8ae9d69 commit abd0e6f

4 files changed

Lines changed: 121 additions & 27 deletions

File tree

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ Both methods are called with a single `event` argument, it will have the followi
8585

8686
- `event.originalMessage` - The actual value the user provided for optional `message` parameter. This will be `undefined` if the user did not provide a value, even if the underlying assertion provides a default message.
8787

88+
- `event.defaultMessage` - If you use objects instead of strings to specify patterns (see below), the `defaultMessage` metadata will be copied directly on the event object.
89+
90+
- `event.matcherSpec` - This contains the complete parsed matcher spec as supplied, as well as any additional metadata you may have supplied (see patterns section below for details on how to supply additional metadata).
91+
8892
- `event.args` - An array of the actual arguments passed to the assertion.
8993

9094
- `event.assertionThrew` - Whether or not the underlying assertion threw an error. This will always be `true` in an `onError` callback, and always `false` in an `onSuccess` callback.
@@ -118,7 +122,7 @@ TBD
118122

119123
| type | default value |
120124
|:--------------------|:--------------------|
121-
| `Array` of `string` | objects shown below |
125+
| `Array` of `string` or `objects`| objects shown below |
122126

123127
```javascript
124128
[
@@ -134,11 +138,23 @@ TBD
134138
'assert.notDeepStrictEqual(actual, expected, [message])'
135139
]
136140
```
137-
138141
Target patterns for power assert feature instrumentation.
139142

140143
Pattern detection is done by [call-signature](https://github.com/jamestalmage/call-signature). Any arguments enclosed in bracket (for example, `[message]`) means optional parameters. Without bracket means mandatory parameters.
141144

145+
Instead of a string, you may alternatively specify an object with a `pattern` property, and any other arbitrary data.
146+
Currently only `defaultMessage` is formally recommended, but you can attach any data here and it will be passed to the `onSuccess` and `onError` handlers.
147+
148+
```javascript
149+
[
150+
{
151+
pattern: 'assert.fail([message])',
152+
defaultMessage:'assert.fail() was called!!'
153+
},
154+
...
155+
]
156+
```
157+
142158
#### options.wrapOnlyPatterns
143159

144160
| type | default value |
@@ -147,6 +163,7 @@ Pattern detection is done by [call-signature](https://github.com/jamestalmage/ca
147163

148164
Methods matching these patterns will not be instrumented by the code transform, but they will be wrapped at runtime and trigger events in the `onSuccess` and `onError` callbacks. Note that "wrap only" events will never have a `powerAssertContext` property.
149165

166+
Similar to the `options.patterns`, you may supply objects with a `pattern` member, and the additional metadata will be passed to the assertion listeners.
150167

151168
### var options = empowerCore.defaultOptions();
152169

lib/decorate.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ var map = require('core-js/library/fn/array/map');
55
var slice = Array.prototype.slice;
66

77
function decorate (callSpec, decorator) {
8-
var func = callSpec.func;
9-
var thisObj = callSpec.thisObj;
108
var numArgsToCapture = callSpec.numArgsToCapture;
11-
var enhanced = callSpec.enhanced;
129

1310
return function decoratedAssert () {
1411
var context, message, hasMessage = false, args = slice.apply(arguments);
@@ -19,12 +16,9 @@ function decorate (callSpec, decorator) {
1916
}
2017

2118
var invocation = {
22-
thisObj: thisObj,
23-
func: func,
2419
values: args,
2520
message: message,
26-
hasMessage: hasMessage,
27-
enhanced: enhanced
21+
hasMessage: hasMessage
2822
};
2923

3024
if (some(args, isCaptured)) {
@@ -45,9 +39,9 @@ function decorate (callSpec, decorator) {
4539
return arg.powerAssertContext.value;
4640
});
4741

48-
return decorator.concreteAssert(invocation, context);
42+
return decorator.concreteAssert(callSpec, invocation, context);
4943
} else {
50-
return decorator.concreteAssert(invocation);
44+
return decorator.concreteAssert(callSpec, invocation);
5145
}
5246
};
5347
}

lib/decorator.js

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,37 @@ var filter = require('core-js/library/fn/array/filter');
55
var map = require('core-js/library/fn/array/map');
66
var signature = require('call-signature');
77
var decorate = require('./decorate');
8+
var keys = require('core-js/library/fn/object/keys');
89

910

1011
function Decorator (receiver, config) {
1112
this.receiver = receiver;
1213
this.config = config;
1314
this.onError = config.onError;
1415
this.onSuccess = config.onSuccess;
15-
this.signatures = map(config.patterns, signature.parse);
16-
this.wrapOnlySignatures = map(config.wrapOnlyPatterns, signature.parse);
16+
this.signatures = map(config.patterns, parse);
17+
this.wrapOnlySignatures = map(config.wrapOnlyPatterns, parse);
1718
}
1819

1920
Decorator.prototype.enhancement = function () {
2021
var that = this;
2122
var container = this.container();
2223
var wrappedMethods = [];
2324

24-
function attach(matcher, enhanced) {
25+
function attach(matcherSpec, enhanced) {
26+
var matcher = matcherSpec.parsed;
2527
var methodName = detectMethodName(matcher.callee);
2628
if (typeof that.receiver[methodName] !== 'function' || wrappedMethods.indexOf(methodName) !== -1) {
2729
return;
2830
}
2931
var callSpec = {
3032
thisObj: that.receiver,
3133
func: that.receiver[methodName],
32-
numArgsToCapture: numberOfArgumentsToCapture(matcher),
34+
numArgsToCapture: numberOfArgumentsToCapture(matcherSpec),
35+
matcherSpec: matcherSpec,
3336
enhanced: enhanced
3437
};
35-
container[methodName] = decorate(callSpec, that);
38+
container[methodName] = callSpec.enhancedFunc = decorate(callSpec, that);
3639
wrappedMethods.push(methodName);
3740
}
3841

@@ -61,27 +64,33 @@ Decorator.prototype.container = function () {
6164
thisObj: null,
6265
func: this.receiver,
6366
numArgsToCapture: numberOfArgumentsToCapture(candidates[0]),
67+
matcherSpec: candidates[0],
6468
enhanced: enhanced
6569
};
66-
basement = decorate(callSpec, this);
70+
basement = callSpec.enhancedFunc = decorate(callSpec, this);
6771
}
6872
}
6973
return basement;
7074
};
7175

72-
Decorator.prototype.concreteAssert = function (invocation, context) {
73-
var func = invocation.func;
74-
var thisObj = invocation.thisObj;
76+
Decorator.prototype.concreteAssert = function (callSpec, invocation, context) {
77+
var func = callSpec.func;
78+
var thisObj = callSpec.thisObj;
79+
var enhanced = callSpec.enhanced;
7580
var args = invocation.values;
7681
var message = invocation.message;
77-
var enhanced = invocation.enhanced;
82+
var matcherSpec = callSpec.matcherSpec;
83+
7884
if (context && typeof this.config.modifyMessageBeforeAssert === 'function') {
7985
message = this.config.modifyMessageBeforeAssert({originalMessage: message, powerAssertContext: context});
8086
}
8187
args = args.concat(message);
8288

8389
var data = {
90+
assertionFunction: callSpec.enhancedFunc,
8491
originalMessage: message,
92+
defaultMessage: matcherSpec.defaultMessage,
93+
matcherSpec: matcherSpec,
8594
enhanced: enhanced,
8695
args: args
8796
};
@@ -103,7 +112,8 @@ Decorator.prototype.concreteAssert = function (invocation, context) {
103112
return this.onSuccess(data);
104113
};
105114

106-
function numberOfArgumentsToCapture (matcher) {
115+
function numberOfArgumentsToCapture (matcherSpec) {
116+
var matcher = matcherSpec.parsed;
107117
var len = matcher.args.length;
108118
var lastArg;
109119
if (0 < len) {
@@ -124,13 +134,25 @@ function detectMethodName (callee) {
124134
}
125135

126136

127-
function functionCall (matcher) {
128-
return matcher.callee.type === 'Identifier';
137+
function functionCall (matcherSpec) {
138+
return matcherSpec.parsed.callee.type === 'Identifier';
129139
}
130140

131141

132-
function methodCall (matcher) {
133-
return matcher.callee.type === 'MemberExpression';
142+
function methodCall (matcherSpec) {
143+
return matcherSpec.parsed.callee.type === 'MemberExpression';
144+
}
145+
146+
function parse(matcherSpec) {
147+
if (typeof matcherSpec === 'string') {
148+
matcherSpec = {pattern: matcherSpec};
149+
}
150+
var ret = {};
151+
keys(matcherSpec).forEach(function (key) {
152+
ret[key] = matcherSpec[key];
153+
});
154+
ret.parsed = signature.parse(matcherSpec.pattern);
155+
return ret;
134156
}
135157

136158

test/empower_test.js

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,58 @@ suite('onSuccess can throw', function () {
518518
});
519519
});
520520

521+
suite('metadata for enhanced methods', function () {
522+
var assert = empower(
523+
{
524+
fail: function (message) {
525+
baseAssert.ok(false, message);
526+
},
527+
pass: function (message) {
528+
// noop
529+
}
530+
},
531+
{
532+
patterns: [
533+
{
534+
pattern: 'assert.fail([message])',
535+
defaultMessage: 'User! You have failed this assertion!'
536+
},
537+
'assert.pass([message])'
538+
],
539+
onError: function (event) {
540+
baseAssert.equal(event.assertionThrew, true);
541+
return event;
542+
},
543+
onSuccess: function (event) {
544+
baseAssert.equal(event.assertionThrew, false);
545+
return event;
546+
}
547+
}
548+
);
549+
550+
test('instrumented', function () {
551+
var event = eval(weave("assert.fail('doh!');"));
552+
baseAssert.equal(event.defaultMessage, 'User! You have failed this assertion!');
553+
baseAssert.strictEqual(event.enhanced, true);
554+
baseAssert.strictEqual(event.assertionFunction, assert.fail);
555+
baseAssert.deepEqual(event.matcherSpec, {
556+
pattern: 'assert.fail([message])',
557+
defaultMessage: 'User! You have failed this assertion!',
558+
parsed: {
559+
args: [{name: 'message', optional: true}],
560+
callee: {object: 'assert', member: 'fail', type: 'MemberExpression'}
561+
}
562+
});
563+
});
564+
565+
test('non-instrumented', function () {
566+
var event = assert.fail('doh!');
567+
baseAssert.equal(event.defaultMessage, 'User! You have failed this assertion!');
568+
baseAssert.strictEqual(event.enhanced, true);
569+
baseAssert.strictEqual(event.assertionFunction, assert.fail);
570+
});
571+
});
572+
521573
suite('wrapOnlyPatterns', function () {
522574
var assert = empower(
523575
{
@@ -530,7 +582,10 @@ suite('wrapOnlyPatterns', function () {
530582
},
531583
{
532584
wrapOnlyPatterns: [
533-
'assert.fail([message])',
585+
{
586+
pattern: 'assert.fail([message])',
587+
defaultMessage: 'User! You have failed this assertion!'
588+
},
534589
'assert.pass([message])'
535590
],
536591
onError: function (event) {
@@ -550,14 +605,17 @@ suite('wrapOnlyPatterns', function () {
550605
baseAssert.strictEqual(event.enhanced, false);
551606
baseAssert.equal(event.originalMessage, 'woot!');
552607
baseAssert.deepEqual(event.args, ['woot!']);
608+
baseAssert.strictEqual(event.assertionFunction, assert.pass);
553609
});
554610

555611
test('instrumented code: error', function () {
556612
var event = eval(weave('assert.fail("Oh no!")'));
557613
baseAssert.equal(event.assertionThrew, true);
558614
baseAssert.strictEqual(event.enhanced, false);
559615
baseAssert.equal(event.originalMessage, 'Oh no!');
616+
baseAssert.equal(event.defaultMessage, 'User! You have failed this assertion!');
560617
baseAssert.deepEqual(event.args, ['Oh no!']);
618+
baseAssert.strictEqual(event.assertionFunction, assert.fail);
561619
});
562620

563621
test('non-instrumented code: success', function () {
@@ -566,14 +624,17 @@ suite('wrapOnlyPatterns', function () {
566624
baseAssert.strictEqual(event.enhanced, false);
567625
baseAssert.equal(event.originalMessage, 'woot!');
568626
baseAssert.deepEqual(event.args, ['woot!']);
627+
baseAssert.strictEqual(event.assertionFunction, assert.pass);
569628
});
570629

571630
test('non-instrumented code: error', function () {
572631
var event = assert.fail('Oh no!');
573632
baseAssert.equal(event.assertionThrew, true);
574633
baseAssert.strictEqual(event.enhanced, false);
575634
baseAssert.equal(event.originalMessage, 'Oh no!');
635+
baseAssert.equal(event.defaultMessage, 'User! You have failed this assertion!');
576636
baseAssert.deepEqual(event.args, ['Oh no!']);
637+
baseAssert.strictEqual(event.assertionFunction, assert.fail);
577638
});
578639
});
579640

0 commit comments

Comments
 (0)