Repository: chaijs/chai (js)

Issue:
`.not.have.keys` incorrectly failing
Because of [this line](https://github.com/chaijs/chai/blob/a64eb623449f058143930bd3a9d943985afa13b6/lib/chai/core/assertions.js#L1464) (I presume), the following tests both fail:

```js
expect({a: 1, b: 2, c: 3}).to.have.keys('a', 'b');  // Correctly fails
expect({a: 1, b: 2, c: 3}).to.not.have.keys('a', 'b'); // Should pass but it fails too
```

The second test is incorrectly failing; it should pass right away on the basis that the target and given values don't have the same number of keys, which is a requirement when the `contains` flag isn't set.

Pull request description:
fix: make negated `.keys` consider size of sets
When neither the `contains` nor `any` flags are set, the `.keys`
assertion implicitly means `.all.keys` and thus requires the target
and given sets to be the same size. Therefore, `not.keys` implicitly
means `.not.all.keys`, and should thus pass when the target and given
sets aren't the same size, even if the target set is a superset of the
given set. This commit enables this behavior.

Fixes #919

Patch:
diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js
index 2daf10b..41b647c 100644
--- a/lib/chai/core/assertions.js
+++ b/lib/chai/core/assertions.js
@@ -1461,7 +1461,7 @@ module.exports = function (chai, _) {
         });
       });
 
-      if (!flag(this, 'negate') && !flag(this, 'contains')) {
+      if (!flag(this, 'contains')) {
         ok = ok && keys.length == actual.length;
       }
     }


Test patch:
diff --git a/test/assert.js b/test/assert.js
index d8d3db0..f2fbd3d 100644
--- a/test/assert.js
+++ b/test/assert.js
@@ -666,6 +666,7 @@ describe('assert', function () {
     assert.containsAllKeys({ foo: 1, bar: 2 }, { 'bar': 7, 'foo': 6 });
 
     assert.doesNotHaveAllKeys({ foo: 1, bar: 2 }, [ 'baz' ]);
+    assert.doesNotHaveAllKeys({ foo: 1, bar: 2 }, [ 'foo' ]);
     assert.doesNotHaveAllKeys({ foo: 1, bar: 2 }, [ 'foo', 'baz' ]);
     assert.doesNotHaveAllKeys({ foo: 1, bar: 2, baz: 3 }, [ 'foo', 'bar', 'baz', 'fake' ]);
     assert.doesNotHaveAllKeys({ foo: 1, bar: 2 }, [ 'baz', 'foo' ]);
diff --git a/test/expect.js b/test/expect.js
index 1fecf02..13cc9a4 100644
--- a/test/expect.js
+++ b/test/expect.js
@@ -1568,6 +1568,7 @@ describe('expect', function () {
     expect({ foo: 1, bar: 2, baz: 3 }).to.contain.all.keys(['bar', 'foo']);
 
     expect({ foo: 1, bar: 2 }).to.not.have.keys('baz');
+    expect({ foo: 1, bar: 2 }).to.not.have.keys('foo');
     expect({ foo: 1, bar: 2 }).to.not.have.keys('foo', 'baz');
     expect({ foo: 1, bar: 2 }).to.not.contain.keys('baz');
     expect({ foo: 1, bar: 2 }).to.not.contain.keys('foo', 'baz');
diff --git a/test/should.js b/test/should.js
index 612f74e..cbb3902 100644
--- a/test/should.js
+++ b/test/should.js
@@ -1369,6 +1369,7 @@ describe('should', function() {
     ({ foo: 1, bar: 2 }).should.contain.keys({ 'foo': 6 });
 
     ({ foo: 1, bar: 2 }).should.not.have.keys('baz');
+    ({ foo: 1, bar: 2 }).should.not.have.keys('foo');
     ({ foo: 1, bar: 2 }).should.not.have.keys('foo', 'baz');
     ({ foo: 1, bar: 2 }).should.not.contain.keys('baz');
     ({ foo: 1, bar: 2 }).should.not.contain.keys('foo', 'baz');