Repository: unexpectedjs/unexpected
Pull request #414 (2017-08-11 07:39:53)

to have properties: Allow numerical property names passed as either strings or numbers
Disallow everything else, as per the discussion on #413


Patch:
diff --git a/lib/assertions.js b/lib/assertions.js
index 0e6a3d67..7a24ec7f 100644
--- a/lib/assertions.js
+++ b/lib/assertions.js
@@ -217,9 +217,26 @@ module.exports = function (expect) {
         });
     });
 
-    expect.addAssertion('<object|function> [not] to have [own] properties <array>', function (expect, subject, properties) {
-        properties.forEach(function (property) {
-            expect(subject, '[not] to have [own] property', property);
+    expect.addAssertion('<object|function> [not] to have [own] properties <array>', function (expect, subject, propertyNames) {
+        var unsupportedPropertyNames = [];
+        propertyNames.forEach(function (propertyName) {
+            if (typeof propertyName !== 'string' && typeof propertyName !== 'number') {
+                unsupportedPropertyNames.push(propertyName);
+            }
+        });
+        if (unsupportedPropertyNames.length > 0) {
+            expect.errorMode = 'nested';
+            expect.fail(function () {
+                this.error('All expected properties must be passed as strings or numbers, but these are not:')
+                    .indentLines();
+                unsupportedPropertyNames.forEach(function (propertyName) {
+                    this.nl().i().appendInspected(propertyName);
+                }, this);
+                this.outdentLines();
+            });
+        }
+        propertyNames.forEach(function (propertyName) {
+            expect(subject, '[not] to have [own] property', String(propertyName));
         });
     });
 


Test patch:
diff --git a/test/assertions/to-have-properties.spec.js b/test/assertions/to-have-properties.spec.js
index 8462437e..1d2c3428 100644
--- a/test/assertions/to-have-properties.spec.js
+++ b/test/assertions/to-have-properties.spec.js
@@ -161,4 +161,50 @@ describe('to have properties assertion', function () {
             baz: 'baz'
         });
     });
+
+    describe('with expected numerical property names listed as numbers', function () {
+        it('should succeed', function () {
+            expect({ 1: 'foo', 2: 'bar' }, 'to have properties', [ 1, 2 ]);
+        });
+
+        it('should fail with a diff', function () {
+            expect(function () {
+                expect({ 1: 123, 2: 456 }, 'to have properties', [ 1, 3 ]);
+            }, 'to error', 'expected { 1: 123, 2: 456 } to have properties [ 1, 3 ]');
+        });
+    });
+
+    describe('with expected property names listed as neither strings nor numbers', function () {
+        it('should fail when a boolean is passed, even if there is a corresponding string property', function () {
+            expect(function () {
+                expect({ true: 123 }, 'to have properties', [ true ]);
+            }, 'to error',
+                'expected { true: 123 } to have properties [ true ]\n' +
+                '  All expected properties must be passed as strings or numbers, but these are not:\n' +
+                '    true'
+            );
+        });
+
+        it('should fail when an object is passed, even if there is a corresponding string property', function () {
+            expect(function () {
+                expect({ foo: 123 }, 'to have properties', [ { toString: function toString() { return 'foo'; } } ]);
+            }, 'to error',
+                "expected { foo: 123 }\n" +
+                "to have properties [ { toString: function toString() { return 'foo'; } } ]\n" +
+                "  All expected properties must be passed as strings or numbers, but these are not:\n" +
+                "    { toString: function toString() { return 'foo'; } }"
+            );
+        });
+
+        it('should should mention all the non-string, non-number property names in a list after the error message', function () {
+            expect(function () {
+                expect({ foo: 123 }, 'to have properties', [ true, false ]);
+            }, 'to error',
+                'expected { foo: 123 } to have properties [ true, false ]\n' +
+                '  All expected properties must be passed as strings or numbers, but these are not:\n' +
+                '    true\n' +
+                '    false'
+            );
+        });
+    });
 });