Repository: unexpectedjs/unexpected
Pull request #364 (2017-01-22 23:21:04)

Show the type signature of the subject and arguments when failing with "assertion not found"
As suggested by @sunesimonsen on #339


Patch:
diff --git a/lib/Unexpected.js b/lib/Unexpected.js
index c5a24c0c..b25ae036 100644
--- a/lib/Unexpected.js
+++ b/lib/Unexpected.js
@@ -847,6 +847,7 @@ function calculateLimits(items) {
 
 Unexpected.prototype.throwAssertionNotFoundError = function (subject, testDescriptionString, args) {
     var candidateHandlers = this.assertions[testDescriptionString];
+    var that = this;
     if (candidateHandlers) {
         this.fail({
             message: function (output) {
@@ -858,13 +859,23 @@ Unexpected.prototype.throwAssertionNotFoundError = function (subject, testDescri
                 };
                 output.append(createStandardErrorMessage(output.clone(), subjectOutput, testDescriptionString, argsOutput)).nl()
                     .indentLines();
-                output.i().error('No matching assertion, did you mean:').nl();
+                output.i().error('The assertion does not have a matching signature for:').nl()
+                    .indentLines().i().text('<').text(that.findTypeOf(subject).name).text('>').sp().text(testDescriptionString);
+
+                args.forEach(function (arg, i) {
+                    output.sp().text('<').text(that.findTypeOf(arg).name).text('>');
+                });
+
+                output
+                    .outdentLines()
+                    .nl()
+                    .i().text('did you mean:').nl();
                 var assertionDeclarations = Object.keys(candidateHandlers.reduce(function (result, handler) {
                     result[handler.declaration] = true;
                     return result;
                 }, {})).sort();
                 assertionDeclarations.forEach(function (declaration, i) {
-                    output.nl(i > 0 ? 1 : 0).i().text(declaration);
+                    output.nl(i > 0 ? 1 : 0).i().i().text(declaration);
                 });
             }
         });
@@ -872,7 +883,6 @@ Unexpected.prototype.throwAssertionNotFoundError = function (subject, testDescri
 
     var assertionsWithScore = [];
     var assertionStrings = Object.keys(this.assertions);
-    var that = this;
 
     function compareAssertions(a, b) {
         var aAssertion = that.lookupAssertionRule(subject, a, args);


Test patch:
diff --git a/test/api/clone.spec.js b/test/api/clone.spec.js
index d9e93579..f11dd29b 100644
--- a/test/api/clone.spec.js
+++ b/test/api/clone.spec.js
@@ -43,9 +43,12 @@ describe('clone', function () {
                 expect(function () {
                     clonedExpect('foobarquux', 'to foobarquux');
                 }, 'to throw',
-                       "expected 'foobarquux' to foobarquux\n" +
-                       "  No matching assertion, did you mean:\n" +
-                       "  <array> to foobarquux");
+                    "expected 'foobarquux' to foobarquux\n" +
+                    "  The assertion does not have a matching signature for:\n" +
+                    "    <string> to foobarquux\n" +
+                    "  did you mean:\n" +
+                    "    <array> to foobarquux"
+                );
             });
 
             it('prefers to suggest a similarly named assertion defined for the correct type over an exact match defined for other types', function () {
@@ -57,9 +60,12 @@ describe('clone', function () {
                 expect(function () {
                     clonedExpect(['fooo'], 'to fooo');
                 }, 'to throw',
-                       "expected [ 'fooo' ] to fooo\n" +
-                       "  No matching assertion, did you mean:\n" +
-                       "  <string> to fooo");
+                    "expected [ 'fooo' ] to fooo\n" +
+                    "  The assertion does not have a matching signature for:\n" +
+                    "    <array> to fooo\n" +
+                    "  did you mean:\n" +
+                    "    <string> to fooo"
+                );
 
                 clonedExpect.addAssertion('<null> to fooo', function (expect, subject) {
                     expect(subject.message, 'to equal', 'fooo');
@@ -67,10 +73,13 @@ describe('clone', function () {
                 expect(function () {
                     clonedExpect(['fooo'], 'to fooo');
                 }, 'to throw',
-                       "expected [ 'fooo' ] to fooo\n" +
-                       "  No matching assertion, did you mean:\n" +
-                       "  <null> to fooo\n" +
-                       "  <string> to fooo");
+                    "expected [ 'fooo' ] to fooo\n" +
+                    "  The assertion does not have a matching signature for:\n" +
+                    "    <array> to fooo\n" +
+                    "  did you mean:\n" +
+                    "    <null> to fooo\n" +
+                    "    <string> to fooo"
+                );
             });
 
             it('prefers to suggest a similarly named assertion for a more specific type', function () {
diff --git a/test/api/shift.spec.js b/test/api/shift.spec.js
index f3e29292..03e9bb87 100644
--- a/test/api/shift.spec.js
+++ b/test/api/shift.spec.js
@@ -119,10 +119,12 @@ describe('expect.shift', function () {
         expect(function () {
             clonedExpect('foo', 'when prepended with foo', function () {});
         }, 'to throw',
-               "expected 'foo' when prepended with foo function () {}\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <string> when prepended with foo <assertion>"
-              );
+            "expected 'foo' when prepended with foo function () {}\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <string> when prepended with foo <function>\n" +
+            "  did you mean:\n" +
+            "    <string> when prepended with foo <assertion>"
+        );
     });
 
     describe('with an async assertion', function () {
diff --git a/test/assertions/to-be-a.spec.js b/test/assertions/to-be-a.spec.js
index c3370d5d..5145b997 100644
--- a/test/assertions/to-be-a.spec.js
+++ b/test/assertions/to-be-a.spec.js
@@ -75,44 +75,56 @@ describe('to be a/an assertion', function () {
         expect(function () {
             expect('foo', 'to be an', undefined);
         }, 'to throw',
-               "expected 'foo' to be an undefined\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <any> [not] to be (a|an) <function>\n" +
-               "  <any> [not] to be (a|an) <string>\n" +
-               "  <any> [not] to be (a|an) <type>");
+            "expected 'foo' to be an undefined\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <string> to be an <undefined>\n" +
+            "  did you mean:\n" +
+            "    <any> [not] to be (a|an) <function>\n" +
+            "    <any> [not] to be (a|an) <string>\n" +
+            "    <any> [not] to be (a|an) <type>"
+        );
     });
 
     it('should throw when the type is specified as null', function () {
         expect(function () {
             expect('foo', 'to be a', null);
         }, 'to throw',
-               "expected 'foo' to be a null\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <any> [not] to be (a|an) <function>\n" +
-               "  <any> [not] to be (a|an) <string>\n" +
-               "  <any> [not] to be (a|an) <type>");
+            "expected 'foo' to be a null\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <string> to be a <null>\n" +
+            "  did you mean:\n" +
+            "    <any> [not] to be (a|an) <function>\n" +
+            "    <any> [not] to be (a|an) <string>\n" +
+            "    <any> [not] to be (a|an) <type>"
+        );
     });
 
     it('should not consider a string a to be an instance of an object without a name property', function () {
         expect(function () {
             expect('foo', 'to be a', {});
         }, 'to throw',
-               "expected 'foo' to be a {}\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <any> [not] to be (a|an) <function>\n" +
-               "  <any> [not] to be (a|an) <string>\n" +
-               "  <any> [not] to be (a|an) <type>");
+            "expected 'foo' to be a {}\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <string> to be a <object>\n" +
+            "  did you mean:\n" +
+            "    <any> [not] to be (a|an) <function>\n" +
+            "    <any> [not] to be (a|an) <string>\n" +
+            "    <any> [not] to be (a|an) <type>"
+        );
     });
 
     it('should throw when the type is specified as an object without an identify function', function () {
         expect(function () {
             expect('foo', 'to be a', { name: 'bar' });
         }, 'to throw',
-               "expected 'foo' to be a { name: 'bar' }\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <any> [not] to be (a|an) <function>\n" +
-               "  <any> [not] to be (a|an) <string>\n" +
-               "  <any> [not] to be (a|an) <type>");
+            "expected 'foo' to be a { name: 'bar' }\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <string> to be a <object>\n" +
+            "  did you mean:\n" +
+            "    <any> [not] to be (a|an) <function>\n" +
+            "    <any> [not] to be (a|an) <string>\n" +
+            "    <any> [not] to be (a|an) <type>"
+        );
     });
 
     it('should throw when the type is specified as an object with an identify function, but without a name property', function () {
@@ -123,10 +135,12 @@ describe('to be a/an assertion', function () {
             // http://v8project.blogspot.dk/2016/04/v8-release-51.html
             expect(err.getErrorMessage('text').toString().replace('function identify', 'function '), 'to satisfy',
                 "expected 'foo' to be a { identify: function () { return true; } }\n" +
-                "  No matching assertion, did you mean:\n" +
-                "  <any> [not] to be (a|an) <function>\n" +
-                "  <any> [not] to be (a|an) <string>\n" +
-                "  <any> [not] to be (a|an) <type>"
+                "  The assertion does not have a matching signature for:\n" +
+                "    <string> to be a <object>\n" +
+                "  did you mean:\n" +
+                "    <any> [not] to be (a|an) <function>\n" +
+                "    <any> [not] to be (a|an) <string>\n" +
+                "    <any> [not] to be (a|an) <type>"
             );
         });
     });
diff --git a/test/assertions/to-be-empty.spec.js b/test/assertions/to-be-empty.spec.js
index 254c7bdf..0fab67c6 100644
--- a/test/assertions/to-be-empty.spec.js
+++ b/test/assertions/to-be-empty.spec.js
@@ -25,10 +25,13 @@ describe('empty assertion', function () {
         expect(function () {
             expect(null, 'to be empty');
         }, 'to throw exception',
-               "expected null to be empty\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <object> [not] to be empty\n" +
-               "  <string|array-like> [not] to be empty");
+            "expected null to be empty\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <null> to be empty\n" +
+            "  did you mean:\n" +
+            "    <object> [not] to be empty\n" +
+            "    <string|array-like> [not] to be empty"
+        );
 
         expect(function () {
             expect({ a: 'b' }, 'to be empty');
diff --git a/test/assertions/to-be-finite.spec.js b/test/assertions/to-be-finite.spec.js
index 5e7cd273..3d09c1e9 100644
--- a/test/assertions/to-be-finite.spec.js
+++ b/test/assertions/to-be-finite.spec.js
@@ -11,9 +11,12 @@ describe('to be finite assertion', function () {
         expect(function () {
             expect(NaN, 'not to be finite');
         }, 'to throw',
-               "expected NaN not to be finite\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <number> [not] to be finite");
+            "expected NaN not to be finite\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <NaN> not to be finite\n" +
+            "  did you mean:\n" +
+            "    <number> [not] to be finite"
+        );
     });
 
     it('throws when the assertion fails', function () {
diff --git a/test/assertions/to-be-greater-than.spec.js b/test/assertions/to-be-greater-than.spec.js
index 95b68096..88d318a8 100644
--- a/test/assertions/to-be-greater-than.spec.js
+++ b/test/assertions/to-be-greater-than.spec.js
@@ -16,9 +16,12 @@ describe('greater than assertion', function () {
         expect(function () {
             expect(NaN, 'not to be greater than', 1);
         }, 'to throw',
-               "expected NaN not to be greater than 1\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <number> [not] to be (greater than|above) <number>\n" +
-               "  <string> [not] to be (greater than|above) <string>");
+            "expected NaN not to be greater than 1\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <NaN> not to be greater than <number>\n" +
+            "  did you mean:\n" +
+            "    <number> [not] to be (greater than|above) <number>\n" +
+            "    <string> [not] to be (greater than|above) <string>"
+        );
     });
 });
diff --git a/test/assertions/to-be-infinite.spec.js b/test/assertions/to-be-infinite.spec.js
index a213f007..0f633368 100644
--- a/test/assertions/to-be-infinite.spec.js
+++ b/test/assertions/to-be-infinite.spec.js
@@ -11,9 +11,12 @@ describe('to be infinite assertion', function () {
         expect(function () {
             expect(NaN, 'not to be infinite');
         }, 'to throw',
-               "expected NaN not to be infinite\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <number> [not] to be infinite");
+            "expected NaN not to be infinite\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <NaN> not to be infinite\n" +
+            "  did you mean:\n" +
+            "    <number> [not] to be infinite"
+        );
     });
 
     it('throws when the assertion fails', function () {
diff --git a/test/assertions/to-be-within.spec.js b/test/assertions/to-be-within.spec.js
index 86a13a9b..493ec65d 100644
--- a/test/assertions/to-be-within.spec.js
+++ b/test/assertions/to-be-within.spec.js
@@ -17,10 +17,13 @@ describe('within assertion', function () {
         expect(function () {
             expect(null, 'not to be within', 0, 4);
         }, 'to throw exception',
-               "expected null not to be within 0, 4\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <number> [not] to be within <number> <number>\n" +
-               "  <string> [not] to be within <string> <string>");
+            "expected null not to be within 0, 4\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <null> not to be within <number> <number>\n" +
+            "  did you mean:\n" +
+            "    <number> [not] to be within <number> <number>\n" +
+            "    <string> [not] to be within <string> <string>"
+        );
     });
 
     it('throws with the correct error message when the end points are strings', function () {
diff --git a/test/assertions/to-call-the-callback.spec.js b/test/assertions/to-call-the-callback.spec.js
index 3021a13f..67e444cb 100644
--- a/test/assertions/to-call-the-callback.spec.js
+++ b/test/assertions/to-call-the-callback.spec.js
@@ -241,9 +241,9 @@ describe('to call the callback assertion', function () {
                         setTimeout(cb, 0);
                     }, 'to call the callback with error', new Error('bla'));
                 }, 'to error',
-                              "expected function (cb) { setTimeout(cb, 0); }\n" +
-                              "to call the callback with error Error('bla')"
-                             );
+                    "expected function (cb) { setTimeout(cb, 0); }\n" +
+                    "to call the callback with error Error('bla')"
+                );
             });
         });
 
@@ -262,8 +262,8 @@ describe('to call the callback assertion', function () {
                         setTimeout(cb, 0);
                     }, 'to call the callback with error');
                 }, 'to error',
-                              "expected function (cb) { setTimeout(cb, 0); } to call the callback with error"
-                             );
+                    "expected function (cb) { setTimeout(cb, 0); } to call the callback with error"
+                );
             });
         });
     });
@@ -277,15 +277,18 @@ describe('to call the callback assertion', function () {
                     }, 0);
                 }, 'to call the callback without error', new Error('bla'));
             }, 'to throw',
-                   "expected\n" +
-                   "function (cb) {\n" +
-                   "  setTimeout(function () {\n" +
-                   "    cb(new Error('bla'));\n" +
-                   "  }, 0);\n" +
-                   "}\n" +
-                   "to call the callback without error Error('bla')\n" +
-                   "  No matching assertion, did you mean:\n" +
-                   "  <function> to call the callback without error");
+                "expected\n" +
+                "function (cb) {\n" +
+                "  setTimeout(function () {\n" +
+                "    cb(new Error('bla'));\n" +
+                "  }, 0);\n" +
+                "}\n" +
+                "to call the callback without error Error('bla')\n" +
+                "  The assertion does not have a matching signature for:\n" +
+                "    <function> to call the callback without error <Error>\n" +
+                "  did you mean:\n" +
+                "    <function> to call the callback without error"
+            );
         });
 
         it('should succeed', function () {
diff --git a/test/assertions/to-contain.spec.js b/test/assertions/to-contain.spec.js
index 9a92cc3a..502f6b45 100644
--- a/test/assertions/to-contain.spec.js
+++ b/test/assertions/to-contain.spec.js
@@ -22,10 +22,13 @@ describe('to contain assertion', function () {
         expect(function () {
             expect(null, 'not to contain', 'world');
         }, 'to throw',
-               "expected null not to contain 'world'\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <array-like> [not] to contain <any+>\n" +
-               "  <string> [not] to contain <string+>");
+            "expected null not to contain 'world'\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <null> not to contain <string>\n" +
+            "  did you mean:\n" +
+            "    <array-like> [not] to contain <any+>\n" +
+            "    <string> [not] to contain <string+>"
+        );
 
         expect(function () {
             expect('hello world', 'to contain', 'foo');
@@ -51,10 +54,13 @@ describe('to contain assertion', function () {
         expect(function () {
             expect(1, 'to contain', 1);
         }, 'to throw exception',
-               "expected 1 to contain 1\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <array-like> [not] to contain <any+>\n" +
-               "  <string> [not] to contain <string+>");
+            "expected 1 to contain 1\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <number> to contain <number>\n" +
+            "  did you mean:\n" +
+            "    <array-like> [not] to contain <any+>\n" +
+            "    <string> [not] to contain <string+>"
+        );
     });
 
     it('produces a diff showing full and partial matches for each needle when the assertion fails', function () {
diff --git a/test/assertions/to-have-a-value-satisfying.spec.js b/test/assertions/to-have-a-value-satisfying.spec.js
index 7566236b..d907dc26 100644
--- a/test/assertions/to-have-a-value-satisfying.spec.js
+++ b/test/assertions/to-have-a-value-satisfying.spec.js
@@ -7,9 +7,11 @@ describe('to have a value satisfying assertion', function () {
             },
             'to throw',
             "expected [ 1, 2, 3 ] to have a value satisfying\n" +
-            "  No matching assertion, did you mean:\n" +
-            "  <object> to have a value [exhaustively] satisfying <any>\n" +
-            "  <object> to have a value [exhaustively] satisfying <assertion>"
+            "  The assertion does not have a matching signature for:\n" +
+            "    <array> to have a value satisfying\n" +
+            "  did you mean:\n" +
+            "    <object> to have a value [exhaustively] satisfying <any>\n" +
+            "    <object> to have a value [exhaustively] satisfying <assertion>"
         );
     });
 
@@ -38,9 +40,11 @@ describe('to have a value satisfying assertion', function () {
             },
             'to throw',
             "expected 42 to have a value satisfying function (value) {}\n" +
-            "  No matching assertion, did you mean:\n" +
-            "  <object> to have a value [exhaustively] satisfying <any>\n" +
-            "  <object> to have a value [exhaustively] satisfying <assertion>"
+            "  The assertion does not have a matching signature for:\n" +
+            "    <number> to have a value satisfying <function>\n" +
+            "  did you mean:\n" +
+            "    <object> to have a value [exhaustively] satisfying <any>\n" +
+            "    <object> to have a value [exhaustively] satisfying <assertion>"
         );
     });
 
diff --git a/test/assertions/to-have-an-item-satisfying.spec.js b/test/assertions/to-have-an-item-satisfying.spec.js
index 6259d31c..563b2c4c 100644
--- a/test/assertions/to-have-an-item-satisfying.spec.js
+++ b/test/assertions/to-have-an-item-satisfying.spec.js
@@ -7,9 +7,12 @@ describe('to have an item satisfying assertion', function () {
             },
             'to throw',
             "expected [ 1, 2, 3 ] to have an item satisfying\n" +
-            "  No matching assertion, did you mean:\n" +
-            "  <array-like> to have an item [exhaustively] satisfying <any>\n" +
-            "  <array-like> to have an item [exhaustively] satisfying <assertion>");
+            "  The assertion does not have a matching signature for:\n" +
+            "    <array> to have an item satisfying\n" +
+            "  did you mean:\n" +
+            "    <array-like> to have an item [exhaustively] satisfying <any>\n" +
+            "    <array-like> to have an item [exhaustively] satisfying <assertion>"
+        );
     });
 
     it('only accepts arrays as the subject', function () {
@@ -19,9 +22,12 @@ describe('to have an item satisfying assertion', function () {
             },
             'to throw',
             "expected 42 to have an item satisfying 'to be a number'\n" +
-            "  No matching assertion, did you mean:\n" +
-            "  <array-like> to have an item [exhaustively] satisfying <any>\n" +
-            "  <array-like> to have an item [exhaustively] satisfying <assertion>");
+            "  The assertion does not have a matching signature for:\n" +
+            "    <number> to have an item satisfying <string>\n" +
+            "  did you mean:\n" +
+            "    <array-like> to have an item [exhaustively] satisfying <any>\n" +
+            "    <array-like> to have an item [exhaustively] satisfying <assertion>"
+        );
     });
 
     it('fails if the given array is empty', function () {
diff --git a/test/assertions/to-have-items-satisfying.spec.js b/test/assertions/to-have-items-satisfying.spec.js
index 53df3d73..56d205af 100644
--- a/test/assertions/to-have-items-satisfying.spec.js
+++ b/test/assertions/to-have-items-satisfying.spec.js
@@ -4,30 +4,39 @@ describe('to have items satisfying assertion', function () {
         expect(function () {
             expect([1, 2, 3], 'to have items satisfying');
         }, 'to throw',
-               "expected [ 1, 2, 3 ] to have items satisfying\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <array-like> to have items [exhaustively] satisfying <any>\n" +
-               "  <array-like> to have items [exhaustively] satisfying <assertion>");
+            "expected [ 1, 2, 3 ] to have items satisfying\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <array> to have items satisfying\n" +
+            "  did you mean:\n" +
+            "    <array-like> to have items [exhaustively] satisfying <any>\n" +
+            "    <array-like> to have items [exhaustively] satisfying <assertion>"
+        );
     });
 
     it('does not accept a fourth argument', function () {
         expect(function () {
             expect([1], 'to have items satisfying', 1, 2);
         }, 'to throw',
-               "expected [ 1 ] to have items satisfying 1, 2\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <array-like> to have items [exhaustively] satisfying <any>\n" +
-               "  <array-like> to have items [exhaustively] satisfying <assertion>");
+            "expected [ 1 ] to have items satisfying 1, 2\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <array> to have items satisfying <number> <number>\n" +
+            "  did you mean:\n" +
+            "    <array-like> to have items [exhaustively] satisfying <any>\n" +
+            "    <array-like> to have items [exhaustively] satisfying <assertion>"
+        );
     });
 
     it('only accepts arrays as the target object', function () {
         expect(function () {
             expect(42, 'to have items satisfying', function (item) {});
         }, 'to throw',
-               "expected 42 to have items satisfying function (item) {}\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <array-like> to have items [exhaustively] satisfying <any>\n" +
-               "  <array-like> to have items [exhaustively] satisfying <assertion>");
+            "expected 42 to have items satisfying function (item) {}\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <number> to have items satisfying <function>\n" +
+            "  did you mean:\n" +
+            "    <array-like> to have items [exhaustively] satisfying <any>\n" +
+            "    <array-like> to have items [exhaustively] satisfying <assertion>"
+        );
     });
 
     it('fails if the given array is empty', function () {
diff --git a/test/assertions/to-have-keys-satisfying.spec.js b/test/assertions/to-have-keys-satisfying.spec.js
index fdac1305..9a056f83 100644
--- a/test/assertions/to-have-keys-satisfying.spec.js
+++ b/test/assertions/to-have-keys-satisfying.spec.js
@@ -4,30 +4,39 @@ describe('to have keys satisfying assertion', function () {
         expect(function () {
             expect([1, 2, 3], 'to have keys satisfying');
         }, 'to throw',
-               "expected [ 1, 2, 3 ] to have keys satisfying\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <object> to have keys satisfying <any>\n" +
-               "  <object> to have keys satisfying <assertion>");
+            "expected [ 1, 2, 3 ] to have keys satisfying\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <array> to have keys satisfying\n" +
+            "  did you mean:\n" +
+            "    <object> to have keys satisfying <any>\n" +
+            "    <object> to have keys satisfying <assertion>"
+        );
     });
 
     it('does not accept a fourth argument', function () {
         expect(function () {
             expect([1], 'to have keys satisfying', 0, 1);
         }, 'to throw',
-               "expected [ 1 ] to have keys satisfying 0, 1\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <object> to have keys satisfying <any>\n" +
-               "  <object> to have keys satisfying <assertion>");
+            "expected [ 1 ] to have keys satisfying 0, 1\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <array> to have keys satisfying <number> <number>\n" +
+            "  did you mean:\n" +
+            "    <object> to have keys satisfying <any>\n" +
+            "    <object> to have keys satisfying <assertion>"
+        );
     });
 
     it('only accepts objects as the target', function () {
         expect(function () {
             expect(42, 'to have keys satisfying', function (key) {});
         }, 'to throw',
-               "expected 42 to have keys satisfying function (key) {}\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <object> to have keys satisfying <any>\n" +
-               "  <object> to have keys satisfying <assertion>");
+            "expected 42 to have keys satisfying function (key) {}\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <number> to have keys satisfying <function>\n" +
+            "  did you mean:\n" +
+            "    <object> to have keys satisfying <any>\n" +
+            "    <object> to have keys satisfying <assertion>"
+        );
     });
 
     it('asserts that the given callback does not throw for any keys in the map', function () {
diff --git a/test/assertions/to-have-length.spec.js b/test/assertions/to-have-length.spec.js
index 16f637e3..be2aed6b 100644
--- a/test/assertions/to-have-length.spec.js
+++ b/test/assertions/to-have-length.spec.js
@@ -34,16 +34,22 @@ describe('to have length assertion', function () {
         expect(function () {
             expect(null, 'to have length', 4);
         }, 'to throw exception',
-               "expected null to have length 4\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <string|array-like> [not] to have length <number>");
+            "expected null to have length 4\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <null> to have length <number>\n" +
+            "  did you mean:\n" +
+            "    <string|array-like> [not] to have length <number>"
+        );
 
         expect(function () {
             expect({ length: 4 }, 'to have length', 4);
         }, 'to throw exception',
-               "expected { length: 4 } to have length 4\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <string|array-like> [not] to have length <number>");
+            "expected { length: 4 } to have length 4\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <object> to have length <number>\n" +
+            "  did you mean:\n" +
+            "    <string|array-like> [not] to have length <number>"
+        );
     });
 
     if (typeof Buffer !== 'undefined') {
diff --git a/test/assertions/to-have-properties.spec.js b/test/assertions/to-have-properties.spec.js
index 21ac7aef..8462437e 100644
--- a/test/assertions/to-have-properties.spec.js
+++ b/test/assertions/to-have-properties.spec.js
@@ -130,17 +130,23 @@ describe('to have properties assertion', function () {
         expect(function () {
             expect({a: 'foo', b: 'bar'}, 'to have properties', 'a', 'b');
         }, 'to throw',
-               "expected { a: 'foo', b: 'bar' } to have properties 'a', 'b'\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <object|function> [not] to have [own] properties <array>\n" +
-               "  <object|function> to have [own] properties <object>");
+            "expected { a: 'foo', b: 'bar' } to have properties 'a', 'b'\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <object> to have properties <string> <string>\n" +
+            "  did you mean:\n" +
+            "    <object|function> [not] to have [own] properties <array>\n" +
+            "    <object|function> to have [own] properties <object>"
+        );
 
         expect(function () {
             expect({a: 'foo', b: 'bar'}, 'not to have properties', {a: 'foo', b: 'bar'});
         }, 'to throw',
-               "expected { a: 'foo', b: 'bar' } not to have properties { a: 'foo', b: 'bar' }\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <object|function> [not] to have [own] properties <array>");
+            "expected { a: 'foo', b: 'bar' } not to have properties { a: 'foo', b: 'bar' }\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <object> not to have properties <object>\n" +
+            "  did you mean:\n" +
+            "    <object|function> [not] to have [own] properties <array>"
+        );
     });
 
     it('works with function objects as well', function () {
diff --git a/test/assertions/to-have-property.spec.js b/test/assertions/to-have-property.spec.js
index 22196e1b..f3842651 100644
--- a/test/assertions/to-have-property.spec.js
+++ b/test/assertions/to-have-property.spec.js
@@ -20,10 +20,13 @@ describe('to have property assertion', function () {
         expect(function () {
             expect(null, 'to have property', 'b');
         }, 'to throw exception',
-               "expected null to have property 'b'\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <object|function> [not] to have property <string>\n" +
-               "  <object|function> to have [own] property <string> <any>");
+            "expected null to have property 'b'\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <null> to have property <string>\n" +
+            "  did you mean:\n" +
+            "    <object|function> [not] to have property <string>\n" +
+            "    <object|function> to have [own] property <string> <any>"
+        );
 
         expect(function () {
             expect({a: 'b'}, 'to have property', 'a', 'c');
@@ -45,32 +48,44 @@ describe('to have property assertion', function () {
             // property expectations ignores value if property
             expect(null, 'not to have property', 'a', 'b');
         }, 'to throw exception',
-               "expected null not to have property 'a', 'b'\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <object|function> [not] to have property <string>");
+            "expected null not to have property 'a', 'b'\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <null> not to have property <string> <string>\n" +
+            "  did you mean:\n" +
+            "    <object|function> [not] to have property <string>"
+        );
 
         expect(function () {
             // property expectations on value expects the property to be present
             expect(null, 'not to have own property', 'a', 'b');
         }, 'to throw exception',
-               "expected null not to have own property 'a', 'b'\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <object|function> [not] to have own property <string>");
+            "expected null not to have own property 'a', 'b'\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <null> not to have own property <string> <string>\n" +
+            "  did you mean:\n" +
+            "    <object|function> [not] to have own property <string>"
+        );
     });
 
     it('does not support the not-flag in combination with a value argument', function () {
         expect(function () {
             expect({ a: 'a' }, 'not to have property', 'a', 'a');
         }, "to throw",
-               "expected { a: 'a' } not to have property 'a', 'a'\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <object|function> [not] to have property <string>");
+            "expected { a: 'a' } not to have property 'a', 'a'\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <object> not to have property <string> <string>\n" +
+            "  did you mean:\n" +
+            "    <object|function> [not] to have property <string>"
+        );
 
         expect(function () {
             expect({ a: 'a' }, 'not to have own property', 'a', 'a');
         }, "to throw",
-               "expected { a: 'a' } not to have own property 'a', 'a'\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <object|function> [not] to have own property <string>");
+            "expected { a: 'a' } not to have own property 'a', 'a'\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <object> not to have own property <string> <string>\n" +
+            "  did you mean:\n" +
+            "    <object|function> [not] to have own property <string>"
+        );
     });
 });
diff --git a/test/assertions/to-have-values-satisfying.spec.js b/test/assertions/to-have-values-satisfying.spec.js
index f7196216..f720d682 100644
--- a/test/assertions/to-have-values-satisfying.spec.js
+++ b/test/assertions/to-have-values-satisfying.spec.js
@@ -4,30 +4,39 @@ describe('to have values satisfying assertion', function () {
         expect(function () {
             expect([1, 2, 3], 'to have values satisfying');
         }, 'to throw',
-               "expected [ 1, 2, 3 ] to have values satisfying\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <object> to have values [exhaustively] satisfying <any>\n" +
-               "  <object> to have values [exhaustively] satisfying <assertion>");
+            "expected [ 1, 2, 3 ] to have values satisfying\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <array> to have values satisfying\n" +
+            "  did you mean:\n" +
+            "    <object> to have values [exhaustively] satisfying <any>\n" +
+            "    <object> to have values [exhaustively] satisfying <assertion>"
+        );
     });
 
     it('does not accept a fourth argument', function () {
         expect(function () {
             expect([1], 'to have values satisfying', 1, 2);
         }, 'to throw',
-               "expected [ 1 ] to have values satisfying 1, 2\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <object> to have values [exhaustively] satisfying <any>\n" +
-               "  <object> to have values [exhaustively] satisfying <assertion>");
+            "expected [ 1 ] to have values satisfying 1, 2\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <array> to have values satisfying <number> <number>\n" +
+            "  did you mean:\n" +
+            "    <object> to have values [exhaustively] satisfying <any>\n" +
+            "    <object> to have values [exhaustively] satisfying <assertion>"
+        );
     });
 
     it('only accepts objects and arrays as the target', function () {
         expect(function () {
             expect(42, 'to have values satisfying', function (value) {});
         }, 'to throw',
-               "expected 42 to have values satisfying function (value) {}\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <object> to have values [exhaustively] satisfying <any>\n" +
-               "  <object> to have values [exhaustively] satisfying <assertion>");
+            "expected 42 to have values satisfying function (value) {}\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <number> to have values satisfying <function>\n" +
+            "  did you mean:\n" +
+            "    <object> to have values [exhaustively] satisfying <any>\n" +
+            "    <object> to have values [exhaustively] satisfying <assertion>"
+        );
     });
 
     it('asserts that the given callback does not throw for any values in the map', function () {
diff --git a/test/assertions/to-satisfy.spec.js b/test/assertions/to-satisfy.spec.js
index ee892c25..6f208642 100644
--- a/test/assertions/to-satisfy.spec.js
+++ b/test/assertions/to-satisfy.spec.js
@@ -1084,8 +1084,10 @@ describe('to satisfy assertion', function () {
                "\n" +
                "{\n" +
                "  bool: 'true' // expected 'true' to be true\n" +
-               "               //   No matching assertion, did you mean:\n" +
-               "               //   <boolean> [not] to be true\n" +
+               "               //   The assertion does not have a matching signature for:\n" +
+               "               //     <string> to be true\n" +
+               "               //   did you mean:\n" +
+               "               //     <boolean> [not] to be true\n" +
                "}");
     });
 
diff --git a/test/assertions/to-throw.spec.js b/test/assertions/to-throw.spec.js
index fe55582a..3081fa9e 100644
--- a/test/assertions/to-throw.spec.js
+++ b/test/assertions/to-throw.spec.js
@@ -55,10 +55,13 @@ describe('to throw assertion', function () {
         expect(function () {
             expect(1, 'to throw exception');
         }, 'to throw exception',
-               "expected 1 to throw exception\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <function> to (throw|throw error|throw exception)\n" +
-               "  <function> to (throw|throw error|throw exception) <any>");
+            "expected 1 to throw exception\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <number> to throw exception\n" +
+            "  did you mean:\n" +
+            "    <function> to (throw|throw error|throw exception)\n" +
+            "    <function> to (throw|throw error|throw exception) <any>"
+        );
     });
 
     it('given a function the function is called with the exception', function () {
@@ -81,13 +84,16 @@ describe('to throw assertion', function () {
                 throw new Error('matches the exception message');
             }, 'not to throw', /matches the exception message/);
         }, 'to throw',
-               "expected\n" +
-               "function () {\n" +
-               "  throw new Error('matches the exception message');\n" +
-               "}\n" +
-               "not to throw /matches the exception message/\n" +
-               "  No matching assertion, did you mean:\n" +
-               "  <function> not to throw");
+            "expected\n" +
+            "function () {\n" +
+            "  throw new Error('matches the exception message');\n" +
+            "}\n" +
+            "not to throw /matches the exception message/\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <function> not to throw <regexp>\n" +
+            "  did you mean:\n" +
+            "    <function> not to throw"
+        );
     });
 
     it('provides a diff when the exception message does not match the given string', function () {
diff --git a/test/unexpected.spec.js b/test/unexpected.spec.js
index 83073839..bc97d1dd 100644
--- a/test/unexpected.spec.js
+++ b/test/unexpected.spec.js
@@ -53,6 +53,21 @@ describe('unexpected', function () {
         }, 'to throw exception', "Unknown assertion 'to bee', did you mean: 'to be'");
     });
 
+    it('shows a specific error message when the assertion exists, but not for the given type signature', function () {
+        var clonedExpect = expect.clone().addAssertion('<string> [not] to foo <array>', function (expect) {
+            expect();
+        });
+        expect(function () {
+            clonedExpect(123, 'to foo', 456);
+        }, 'to throw',
+            "expected 123 to foo 456\n" +
+            "  The assertion does not have a matching signature for:\n" +
+            "    <number> to foo <number>\n" +
+            "  did you mean:\n" +
+            "    <string> [not] to foo <array>"
+        );
+    });
+
     describe('expect', function () {
         it('should catch non-Unexpected error caught from a nested assertion', function () {
             var clonedExpect = expect.clone().addAssertion('to foo', function (expect, subject) {
@@ -887,8 +902,10 @@ describe('unexpected', function () {
         }, 'to throw',
             "expected function () { return 123; } when called when called with 'abc', /123/\n" +
             "  expected 123 when called with 'abc', /123/\n" +
-            "    No matching assertion, did you mean:\n" +
-            "    <function> [when] called with <array-like> <assertion?>"
+            "    The assertion does not have a matching signature for:\n" +
+            "      <number> when called with <string> <regexp>\n" +
+            "    did you mean:\n" +
+            "      <function> [when] called with <array-like> <assertion?>"
         );
     });