Repository: chaijs/chai (js)

Issue:
chai4 regression: instanceOf validation fails for native HTMLElement instances on IE11
Starting with chai v4, instanceOf now validates the target as a constructor function.
The assertion resides in: https://github.com/chaijs/chai/blob/master/lib/chai/core/assertions.js#L1604
```
    var validInstanceOfTarget = constructor === Object(constructor) && (
        typeof constructor === 'function' ||
        (typeof Symbol !== 'undefined' &&
         typeof Symbol.hasInstance !== 'undefined' &&
         Symbol.hasInstance in constructor)
    );
```
This validation fails on IE11, IE10, and Safari 7 when the constructor is HTMLInputElement.
I'm guessing it's broken for other HTMLElement constructors, and other (old) environments as well.

Sample code which shows the failure:
https://github.com/wix/wix-react-components/blob/master/test/components/number-input.spec.tsx#L17

Failure log:
https://travis-ci.org/wix/wix-react-components/builds/248446200

Notes:
- `typeof HTMLInputElement === "object"` on IE11.
- `<number-input-dom-instance> instanceof HTMLInputElement === true` on IE11
- I'm loading core-js/shim to the client, but I've tested the validation a a clean IE11 (about:blank) and it fails even without the shim.

Same assertion worked with chai 3, as there was no validation.

Pull request description:
fix: `.instanceof` to allow DOM interfaces in IE11
Fixes #1000 

Patch:
diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js
index 09d0a6f..085d209 100644
--- a/lib/chai/core/assertions.js
+++ b/lib/chai/core/assertions.js
@@ -1601,28 +1601,25 @@ module.exports = function (chai, _) {
     var target = flag(this, 'object')
     var ssfi = flag(this, 'ssfi');
     var flagMsg = flag(this, 'message');
-    var validInstanceOfTarget = constructor === Object(constructor) && (
-        typeof constructor === 'function' ||
-        (typeof Symbol !== 'undefined' &&
-         typeof Symbol.hasInstance !== 'undefined' &&
-         Symbol.hasInstance in constructor)
-    );
 
-    if (!validInstanceOfTarget) {
-      flagMsg = flagMsg ? flagMsg + ': ' : '';
-      var constructorType = constructor === null ? 'null' : typeof constructor;
-      throw new AssertionError(
-        flagMsg + 'The instanceof assertion needs a constructor but ' + constructorType + ' was given.',
-        undefined,
-        ssfi
-      );
+    try {
+      var isInstanceOf = target instanceof constructor;
+    } catch (err) {
+      if (err instanceof TypeError) {
+        flagMsg = flagMsg ? flagMsg + ': ' : '';
+        throw new AssertionError(
+          flagMsg + 'The instanceof assertion needs a constructor but '
+            + _.type(constructor) + ' was given.',
+          undefined,
+          ssfi
+        );
+      }
+      throw err;
     }
 
-    var isInstanceOf = target instanceof constructor
-
     var name = _.getName(constructor);
     if (name === null) {
-        name = 'an unnamed constructor';
+      name = 'an unnamed constructor';
     }
 
     this.assert(


Test patch:
diff --git a/test/assert.js b/test/assert.js
index 44bb5ff..5994d88 100644
--- a/test/assert.js
+++ b/test/assert.js
@@ -145,6 +145,17 @@ describe('assert', function () {
     function Foo(){}
     assert.instanceOf(new Foo(), Foo);
 
+    // Normally, `instanceof` requires that the constructor be a function or an
+    // object with a callable `@@hasInstance`. But in some older browsers such
+    // as IE11, `instanceof` also accepts DOM-related interfaces such as
+    // `HTMLElement`, despite being non-callable objects in those browsers.
+    // See: https://github.com/chaijs/chai/issues/1000.
+    if (typeof document !== 'undefined' &&
+        typeof document.createElement !== 'undefined' &&
+        typeof HTMLElement !== 'undefined') {
+      assert.instanceOf(document.createElement('div'), HTMLElement);
+    }
+
     err(function(){
       assert.instanceOf(new Foo(), 1, 'blah');
     }, "blah: The instanceof assertion needs a constructor but number was given.");
@@ -155,7 +166,7 @@ describe('assert', function () {
 
     err(function(){
       assert.instanceOf(new Foo(), {});
-    }, "The instanceof assertion needs a constructor but object was given.");
+    }, "The instanceof assertion needs a constructor but Object was given.");
 
     err(function(){
       assert.instanceOf(new Foo(), true);
@@ -224,7 +235,7 @@ describe('assert', function () {
 
     err(function(){
       assert.notInstanceOf(new Foo(), {});
-    }, "The instanceof assertion needs a constructor but object was given.");
+    }, "The instanceof assertion needs a constructor but Object was given.");
 
     err(function(){
       assert.notInstanceOf(new Foo(), true);
diff --git a/test/expect.js b/test/expect.js
index 484edd6..c14fdfa 100644
--- a/test/expect.js
+++ b/test/expect.js
@@ -374,6 +374,17 @@ describe('expect', function () {
     function Foo(){}
     expect(new Foo()).to.be.an.instanceof(Foo);
 
+    // Normally, `instanceof` requires that the constructor be a function or an
+    // object with a callable `@@hasInstance`. But in some older browsers such
+    // as IE11, `instanceof` also accepts DOM-related interfaces such as
+    // `HTMLElement`, despite being non-callable objects in those browsers.
+    // See: https://github.com/chaijs/chai/issues/1000.
+    if (typeof document !== 'undefined' &&
+        typeof document.createElement !== 'undefined' &&
+        typeof HTMLElement !== 'undefined') {
+      expect(document.createElement('div')).to.be.an.instanceof(HTMLElement);
+    }
+
     err(function(){
       expect(new Foo()).to.an.instanceof(1, 'blah');
     }, "blah: The instanceof assertion needs a constructor but number was given.");
@@ -388,7 +399,7 @@ describe('expect', function () {
 
     err(function(){
       expect(new Foo()).to.an.instanceof({});
-    }, "The instanceof assertion needs a constructor but object was given.");
+    }, "The instanceof assertion needs a constructor but Object was given.");
 
     err(function(){
       expect(new Foo()).to.an.instanceof(true);
diff --git a/test/should.js b/test/should.js
index ebff98c..4ed205a 100644
--- a/test/should.js
+++ b/test/should.js
@@ -420,6 +420,17 @@ describe('should', function() {
     function Foo(){}
     new Foo().should.be.an.instanceof(Foo);
 
+    // Normally, `instanceof` requires that the constructor be a function or an
+    // object with a callable `@@hasInstance`. But in some older browsers such
+    // as IE11, `instanceof` also accepts DOM-related interfaces such as
+    // `HTMLElement`, despite being non-callable objects in those browsers.
+    // See: https://github.com/chaijs/chai/issues/1000.
+    if (typeof document !== 'undefined' &&
+        typeof document.createElement !== 'undefined' &&
+        typeof HTMLElement !== 'undefined') {
+      document.createElement('div').should.be.an.instanceof(HTMLElement);
+    }
+
     err(function(){
       new Foo().should.be.an.instanceof(1, 'blah');
     }, "blah: The instanceof assertion needs a constructor but number was given.");
@@ -430,7 +441,7 @@ describe('should', function() {
 
     err(function(){
       new Foo().should.be.an.instanceof({});
-    }, "The instanceof assertion needs a constructor but object was given.");
+    }, "The instanceof assertion needs a constructor but Object was given.");
 
     err(function(){
       new Foo().should.be.an.instanceof(true);