Repository: unexpectedjs/unexpected
Pull request #705 (2020-03-24 14:47:45)

Add support for <object> to have (unconfigurable|unenumerable|unwritable|readonly|read-only) property <string|Symbol>
As discussed in https://gitter.im/unexpectedjs/unexpected?at=5e792f607f1e204174c31dec and onwards.


Patch:
diff --git a/lib/assertions.js b/lib/assertions.js
index 019af699..ce72c461 100644
--- a/lib/assertions.js
+++ b/lib/assertions.js
@@ -277,12 +277,20 @@ module.exports = expect => {
   );
 
   expect.addAssertion(
-    '<object> to have (enumerable|configurable|writable) property <string|Symbol>',
+    '<object> to have (enumerable|unenumerable|configurable|unconfigurable|writable|unwritable|readonly) property <string|Symbol>',
     (expect, subject, key) => {
-      const attribute = expect.alternations[0];
+      let attribute = expect.alternations[0];
+      let negated = false;
+      if (attribute.indexOf('un') === 0) {
+        attribute = attribute.substr(2);
+        negated = true;
+      } else if (attribute === 'readonly') {
+        attribute = 'writable';
+        negated = true;
+      }
       const descriptor = Object.getOwnPropertyDescriptor(subject, key);
       expect(descriptor, 'to be defined');
-      expect(descriptor[attribute], 'to be truthy');
+      expect(descriptor[attribute] !== negated, 'to be true');
       return subject[key];
     }
   );


Test patch:
diff --git a/test/assertions/to-have-property.spec.js b/test/assertions/to-have-property.spec.js
index e8070089..a30720f0 100644
--- a/test/assertions/to-have-property.spec.js
+++ b/test/assertions/to-have-property.spec.js
@@ -26,11 +26,27 @@ describe('to have property assertion', () => {
       writable: false,
       value: 't'
     });
+    Object.defineProperty(subject, 'enumTrue', {
+      enumerable: true,
+      value: 't'
+    });
+    Object.defineProperty(subject, 'configTrue', {
+      configurable: true,
+      value: 't'
+    });
+    Object.defineProperty(subject, 'writableTrue', {
+      writable: true,
+      value: 't'
+    });
 
     it('asserts validity of property descriptor', () => {
       expect(subject, 'to have enumerable property', 'a');
       expect(subject, 'to have configurable property', 'a');
       expect(subject, 'to have writable property', 'a');
+      expect(subject, 'to have unenumerable property', 'enumFalse');
+      expect(subject, 'to have unconfigurable property', 'configFalse');
+      expect(subject, 'to have unwritable property', 'writableFalse');
+      expect(subject, 'to have readonly property', 'writableFalse');
     });
 
     it('throws when assertion fails', () => {
@@ -39,25 +55,85 @@ describe('to have property assertion', () => {
           expect(subject, 'to have enumerable property', 'enumFalse');
         },
         'to throw exception',
-        "expected { a: 'b', enumFalse: 't', configFalse: 't', writableFalse: 't' }\n" +
+        'expected\n' +
+          '{\n' +
+          "  a: 'b', enumFalse: 't', configFalse: 't', writableFalse: 't',\n" +
+          "  enumTrue: 't', configTrue: 't', writableTrue: 't'\n" +
+          '}\n' +
           "to have enumerable property 'enumFalse'"
       );
+      expect(
+        function() {
+          expect(subject, 'to have unenumerable property', 'enumTrue');
+        },
+        'to throw exception',
+        'expected\n' +
+          '{\n' +
+          "  a: 'b', enumFalse: 't', configFalse: 't', writableFalse: 't',\n" +
+          "  enumTrue: 't', configTrue: 't', writableTrue: 't'\n" +
+          '}\n' +
+          "to have unenumerable property 'enumTrue'"
+      );
       expect(
         function() {
           expect(subject, 'to have configurable property', 'configFalse');
         },
         'to throw exception',
-        "expected { a: 'b', enumFalse: 't', configFalse: 't', writableFalse: 't' }\n" +
+        'expected\n' +
+          '{\n' +
+          "  a: 'b', enumFalse: 't', configFalse: 't', writableFalse: 't',\n" +
+          "  enumTrue: 't', configTrue: 't', writableTrue: 't'\n" +
+          '}\n' +
           "to have configurable property 'configFalse'"
       );
+      expect(
+        function() {
+          expect(subject, 'to have unconfigurable property', 'configTrue');
+        },
+        'to throw exception',
+        'expected\n' +
+          '{\n' +
+          "  a: 'b', enumFalse: 't', configFalse: 't', writableFalse: 't',\n" +
+          "  enumTrue: 't', configTrue: 't', writableTrue: 't'\n" +
+          '}\n' +
+          "to have unconfigurable property 'configTrue'"
+      );
       expect(
         function() {
           expect(subject, 'to have writable property', 'writableFalse');
         },
         'to throw exception',
-        "expected { a: 'b', enumFalse: 't', configFalse: 't', writableFalse: 't' }\n" +
+        'expected\n' +
+          '{\n' +
+          "  a: 'b', enumFalse: 't', configFalse: 't', writableFalse: 't',\n" +
+          "  enumTrue: 't', configTrue: 't', writableTrue: 't'\n" +
+          '}\n' +
           "to have writable property 'writableFalse'"
       );
+      expect(
+        function() {
+          expect(subject, 'to have unwritable property', 'writableTrue');
+        },
+        'to throw exception',
+        'expected\n' +
+          '{\n' +
+          "  a: 'b', enumFalse: 't', configFalse: 't', writableFalse: 't',\n" +
+          "  enumTrue: 't', configTrue: 't', writableTrue: 't'\n" +
+          '}\n' +
+          "to have unwritable property 'writableTrue'"
+      );
+      expect(
+        function() {
+          expect(subject, 'to have readonly property', 'writableTrue');
+        },
+        'to throw exception',
+        'expected\n' +
+          '{\n' +
+          "  a: 'b', enumFalse: 't', configFalse: 't', writableFalse: 't',\n" +
+          "  enumTrue: 't', configTrue: 't', writableTrue: 't'\n" +
+          '}\n' +
+          "to have readonly property 'writableTrue'"
+      );
     });
 
     // Regression test