Repository: unexpectedjs/unexpected
Pull request #781 (2020-12-12 09:38:24)

to satisfy: Disallow satisfying against a non-plain object
https://gitter.im/unexpectedjs/unexpected?at=5fd3f606bf641c58b41f5ae4

I've marked it as semver-major because it'll probably break someone's crazy test suite, and it's not really super urgent :)


Patch:
diff --git a/lib/assertions.js b/lib/assertions.js
index 47905ce9..6c2c3c76 100644
--- a/lib/assertions.js
+++ b/lib/assertions.js
@@ -1937,6 +1937,16 @@ module.exports = (expect) => {
     }
   );
 
+  const disallowedConstructors = [];
+  ['Set', 'WeakSet', 'Map', 'WeakMap'].forEach((constructorName) => {
+    const Constructor = (typeof window !== 'undefined' ? window : global)[
+      constructorName
+    ];
+    if (typeof Constructor === 'function') {
+      disallowedConstructors.push(Constructor);
+    }
+  });
+
   expect.addAssertion(
     '<object> to [exhaustively] satisfy <object>',
     (expect, subject, value) => {
@@ -1949,6 +1959,21 @@ module.exports = (expect) => {
       if (valueType.is('array-like') && !subjectIsArrayLike) {
         expect.fail();
       }
+      disallowedConstructors.forEach(function (Constructor) {
+        if (subject instanceof Constructor) {
+          expect.fail({
+            message: `A ${Constructor.name} instance is not allowed as a subject of "to satisfy"`,
+            errorMode: 'bubbleThrough',
+          });
+        }
+      });
+
+      if (Object.getPrototypeOf(value) !== Object.prototype) {
+        expect.fail({
+          message: 'Can only satisfy against a plain object',
+          errorMode: 'bubbleThrough',
+        });
+      }
 
       const subjectKeys = subjectType.getKeys(subject);
       const valueKeys = valueType.getKeys(value);


Test patch:
diff --git a/test/assertions/to-satisfy.spec.js b/test/assertions/to-satisfy.spec.js
index b393619e..72b060d7 100644
--- a/test/assertions/to-satisfy.spec.js
+++ b/test/assertions/to-satisfy.spec.js
@@ -10,6 +10,54 @@ describe('to satisfy assertion', () => {
     expect(circular, 'to satisfy', circular);
   });
 
+  it('fails when asserting against a non-plain object', () => {
+    expect(
+      () => expect({}, 'to satisfy', new Set()),
+      'to error',
+      'Can only satisfy against a plain object'
+    );
+  });
+
+  if (typeof Set === 'function') {
+    it('refuses a Set instance as the subject', () => {
+      expect(
+        () => expect(new Set(), 'to satisfy', {}),
+        'to error',
+        'A Set instance is not allowed as a subject of "to satisfy"'
+      );
+    });
+  }
+
+  if (typeof Map === 'function') {
+    it('refuses a Map instance as the subject', () => {
+      expect(
+        () => expect(new Map(), 'to satisfy', {}),
+        'to error',
+        'A Map instance is not allowed as a subject of "to satisfy"'
+      );
+    });
+  }
+
+  if (typeof WeakSet === 'function') {
+    it('refuses a WeakSet instance as the subject', () => {
+      expect(
+        () => expect(new WeakSet(), 'to satisfy', {}),
+        'to error',
+        'A WeakSet instance is not allowed as a subject of "to satisfy"'
+      );
+    });
+  }
+
+  if (typeof WeakMap === 'function') {
+    it('refuses a WeakMap instance as the subject', () => {
+      expect(
+        () => expect(new WeakMap(), 'to satisfy', {}),
+        'to error',
+        'A WeakMap instance is not allowed as a subject of "to satisfy"'
+      );
+    });
+  }
+
   describe('with the not flag', () => {
     it('should succeed when the assertion fails without the not flag', () => {
       expect({ foo: 123 }, 'not to satisfy', { foo: 456 });