Repository: chaijs/chai (js)

Issue:
Add an assertion for same members where order is important
I've come to discover that it's not straightforward to test that an array has the same members, and that those members are in the expected order. Consider this base example:

```js
var a = {};
var b = {};
var list = [ a, b ];
```

All of the assertions around "members" all explicitly do not consider order, but it would be really convenient to be able to assert that too:

```js
// no idea what to call this assertion
assert.sameExactMembers(list, [ a, b ]);
```

My use-case is that I have an internal list that I can retrieve. In one case I return the list unsorted. (so `sameMembers` works great) In the other case, I am sorting it in a specific way, so I want to assert that my sort worked as expected.

I am using `deepEqual` now, but the member objects can be fairly big/deep and it's very inefficient to traverse these objects, especially since I have the direct object refs in my tests.

```js
// in my case, a & b could be huge, so would prefer not traverse them
assert.deepEqual(list, [ a, b ]);
```

Another option is that I can just use `strictEqual` on each item in the array, but that gets unwieldy quickly imo:

```js
// only 2 in this case... but it's copy-pasta and ugly beyond a few members
assert.strictEqual(list[0], a);
assert.strictEqual(list[1], b);
```

Pull request description:
Add ordered flag for members assertions
- Adds `.ordered` functionality on every interface along with tests
- Adds missing `not` assertions for existing member assertions on `assert` interface
- Makes some existing member assertion tests more consistent across interfaces
- Updates documentation
- Fixes #717 


Patch:
diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js
index 2c25f02..347bf4a 100644
--- a/lib/chai/core/assertions.js
+++ b/lib/chai/core/assertions.js
@@ -92,6 +92,23 @@ module.exports = function (chai, _) {
     flag(this, 'deep', true);
   });
 
+  /**
+   * ### .ordered
+   *
+   * Sets the `ordered` flag, later used by the `members`
+   * assertions.
+   *
+   *     expect(foo).to.have.ordered.members([bar, baz]);
+   *
+   * @name ordered
+   * @namespace BDD
+   * @api public
+   */
+
+  Assertion.addProperty('ordered', function () {
+    flag(this, 'ordered', true);
+  });
+
   /**
    * ### .any
    *
@@ -1547,8 +1564,9 @@ module.exports = function (chai, _) {
   Assertion.addMethod('closeTo', closeTo);
   Assertion.addMethod('approximately', closeTo);
 
-  function isSubsetOf(subset, superset, cmp) {
-    return subset.every(function(elem) {
+  function isSubsetOf(subset, superset, cmp, ordered) {
+    return subset.every(function(elem, idx) {
+      if (ordered) return cmp ? cmp(elem, superset[idx]) : elem === superset[idx];
       if (!cmp) return superset.indexOf(elem) !== -1;
 
       return superset.some(function(elem2) {
@@ -1560,18 +1578,43 @@ module.exports = function (chai, _) {
   /**
    * ### .members(set)
    *
-   * Asserts that the target is a superset of `set`,
-   * or that the target and `set` have the same strictly-equal (===) members.
-   * Alternately, if the `deep` flag is set, set members are compared for deep
-   * equality.
+   * Asserts that the target and `set` have the same members. By default,
+   * members are compared using strict equality (===), and order doesn't
+   * matter.
+   *
+   *     expect([1, 2, 3]).to.have.members([2, 1, 3]);
+   *     expect([1, 2, 3]).to.not.have.members([2, 1]);
+   *     expect([1, 2, 3]).to.not.have.members([5, 1, 3]);
+   *
+   * If the `contains` flag is set via `.include` or `.contain`, instead asserts
+   * that the target is a superset of `set`.
+   *
+   *     expect([1, 2, 3]).to.include.members([2, 1]);
+   *     expect([1, 2, 3]).to.not.include.members([5, 1]);
+   *
+   *     expect([1, 2, 3]).to.contain.members([2, 1]);
+   *     expect([1, 2, 3]).to.not.contain.members([5, 1]);
    *
-   *     expect([1, 2, 3]).to.include.members([3, 2]);
-   *     expect([1, 2, 3]).to.not.include.members([3, 2, 8]);
+   * If the `deep` flag is set, members are instead compared for deep equality.
    *
-   *     expect([4, 2]).to.have.members([2, 4]);
-   *     expect([5, 2]).to.not.have.members([5, 2, 1]);
+   *     expect([{ a: 1 }, { b: 2 }, { c: 3 }]).to.have.deep.members([{ b: 2 }, { a: 1 }, { c: 3 }]);
+   *     expect([{ a: 1 }, { b: 2 }, { c: 3 }]).to.not.have.deep.members([{ b: 2 }, { a: 1 }, { f: 5 }]);
    *
-   *     expect([{ id: 1 }]).to.deep.include.members([{ id: 1 }]);
+   * If the `ordered` flag is set, members must instead appear in the same
+   * order.
+   *
+   *     expect([1, 2, 3]).to.have.ordered.members([1, 2, 3]);
+   *     expect([1, 2, 3]).to.not.have.ordered.members([2, 1, 3]);
+   *
+   * Any of the flags can be combined.
+   *
+   *     expect([{ a: 1 }, { b: 2 }, { c: 3 }]).to.have.deep.ordered.members([{ a: 1 }, { b: 2 }, { c: 3 }]);
+   *
+   * If both the `contains` and `ordered` flag are set, the ordering begins
+   * with the first element in the target.
+   *
+   *     expect([1, 2, 3]).to.include.ordered.members([1, 2]);
+   *     expect([1, 2, 3]).to.not.include.ordered.members([2, 3]);
    *
    * @name members
    * @param {Array} set
@@ -1587,24 +1630,32 @@ module.exports = function (chai, _) {
     new Assertion(obj).to.be.an('array');
     new Assertion(subset).to.be.an('array');
 
-    var cmp = flag(this, 'deep') ? _.eql : undefined;
+    var ordered = flag(this, 'ordered');
+
+    var failMsg, failNegateMsg, lengthCheck;
 
     if (flag(this, 'contains')) {
-      return this.assert(
-          isSubsetOf(subset, obj, cmp)
-        , 'expected #{this} to be a superset of #{exp}'
-        , 'expected #{this} to not be a superset of #{exp}'
-        , subset
-        , obj
-      );
+      lengthCheck = true;
+
+      var subject = ordered ? 'an ordered superset' : 'a superset';
+      failMsg = 'expected #{this} to be ' + subject + ' of #{exp}';
+      failNegateMsg = 'expected #{this} to not be ' + subject + ' of #{exp}';
+    } else {
+      lengthCheck = obj.length === subset.length;
+
+      var subject = ordered ? 'ordered members' : 'members';
+      failMsg = 'expected #{this} to have the same ' + subject + ' as #{exp}';
+      failNegateMsg = 'expected #{this} to not have the same ' + subject + ' as #{exp}';
     }
 
+    var cmp = flag(this, 'deep') ? _.eql : undefined;
+
     this.assert(
-        isSubsetOf(obj, subset, cmp) && isSubsetOf(subset, obj, cmp)
-        , 'expected #{this} to have the same members as #{exp}'
-        , 'expected #{this} to not have the same members as #{exp}'
-        , subset
-        , obj
+        lengthCheck && isSubsetOf(subset, obj, cmp, ordered)
+      , failMsg
+      , failNegateMsg
+      , subset
+      , obj
     );
   });
 
diff --git a/lib/chai/interface/assert.js b/lib/chai/interface/assert.js
index 8160595..1acdb3b 100644
--- a/lib/chai/interface/assert.js
+++ b/lib/chai/interface/assert.js
@@ -1391,8 +1391,8 @@ module.exports = function (chai, util) {
   /**
    * ### .sameMembers(set1, set2, [message])
    *
-   * Asserts that `set1` and `set2` have the same members.
-   * Order is not taken into account.
+   * Asserts that `set1` and `set2` have the same members in any order. Uses a
+   * strict equality check (===).
    *
    *     assert.sameMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'same members');
    *
@@ -1408,13 +1408,33 @@ module.exports = function (chai, util) {
     new Assertion(set1, msg).to.have.same.members(set2);
   }
 
+  /**
+   * ### .notSameMembers(set1, set2, [message])
+   *
+   * Asserts that `set1` and `set2` don't have the same members in any order.
+   * Uses a strict equality check (===).
+   *
+   *     assert.notSameMembers([ 1, 2, 3 ], [ 5, 1, 3 ], 'not same members');
+   *
+   * @name notSameMembers
+   * @param {Array} set1
+   * @param {Array} set2
+   * @param {String} message
+   * @namespace Assert
+   * @api public
+   */
+
+  assert.notSameMembers = function (set1, set2, msg) {
+    new Assertion(set1, msg).to.not.have.same.members(set2);
+  }
+
   /**
    * ### .sameDeepMembers(set1, set2, [message])
    *
-   * Asserts that `set1` and `set2` have the same members - using a deep equality checking.
-   * Order is not taken into account.
+   * Asserts that `set1` and `set2` have the same members in any order. Uses a
+   * deep equality check.
    *
-   *     assert.sameDeepMembers([ {b: 3}, {a: 2}, {c: 5} ], [ {c: 5}, {b: 3}, {a: 2} ], 'same deep members');
+   *     assert.sameDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [{ b: 2 }, { a: 1 }, { c: 3 }], 'same deep members');
    *
    * @name sameDeepMembers
    * @param {Array} set1
@@ -1428,13 +1448,114 @@ module.exports = function (chai, util) {
     new Assertion(set1, msg).to.have.same.deep.members(set2);
   }
 
+  /**
+   * ### .notSameDeepMembers(set1, set2, [message])
+   *
+   * Asserts that `set1` and `set2` don't have the same members in any order.
+   * Uses a deep equality check.
+   *
+   *     assert.notSameDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [{ b: 2 }, { a: 1 }, { f: 5 }], 'not same deep members');
+   *
+   * @name notSameDeepMembers
+   * @param {Array} set1
+   * @param {Array} set2
+   * @param {String} message
+   * @namespace Assert
+   * @api public
+   */
+
+  assert.notSameDeepMembers = function (set1, set2, msg) {
+    new Assertion(set1, msg).to.not.have.same.deep.members(set2);
+  }
+
+  /**
+   * ### .sameOrderedMembers(set1, set2, [message])
+   *
+   * Asserts that `set1` and `set2` have the same members in the same order.
+   * Uses a strict equality check (===).
+   *
+   *     assert.sameOrderedMembers([ 1, 2, 3 ], [ 1, 2, 3 ], 'same ordered members');
+   *
+   * @name sameOrderedMembers
+   * @param {Array} set1
+   * @param {Array} set2
+   * @param {String} message
+   * @namespace Assert
+   * @api public
+   */
+
+  assert.sameOrderedMembers = function (set1, set2, msg) {
+    new Assertion(set1, msg).to.have.same.ordered.members(set2);
+  }
+
+  /**
+   * ### .notSameOrderedMembers(set1, set2, [message])
+   *
+   * Asserts that `set1` and `set2` don't have the same members in the same
+   * order. Uses a strict equality check (===).
+   *
+   *     assert.notSameOrderedMembers([ 1, 2, 3 ], [ 2, 1, 3 ], 'not same ordered members');
+   *
+   * @name notSameOrderedMembers
+   * @param {Array} set1
+   * @param {Array} set2
+   * @param {String} message
+   * @namespace Assert
+   * @api public
+   */
+
+  assert.notSameOrderedMembers = function (set1, set2, msg) {
+    new Assertion(set1, msg).to.not.have.same.ordered.members(set2);
+  }
+
+  /**
+   * ### .sameDeepOrderedMembers(set1, set2, [message])
+   *
+   * Asserts that `set1` and `set2` have the same members in the same order.
+   * Uses a deep equality check.
+   *
+   * assert.sameDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { b: 2 }, { c: 3 } ], 'same deep ordered members');
+   *
+   * @name sameDeepOrderedMembers
+   * @param {Array} set1
+   * @param {Array} set2
+   * @param {String} message
+   * @namespace Assert
+   * @api public
+   */
+
+  assert.sameDeepOrderedMembers = function (set1, set2, msg) {
+    new Assertion(set1, msg).to.have.same.deep.ordered.members(set2);
+  }
+
+  /**
+   * ### .notSameDeepOrderedMembers(set1, set2, [message])
+   *
+   * Asserts that `set1` and `set2` don't have the same members in the same
+   * order. Uses a deep equality check.
+   *
+   * assert.notSameDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { b: 2 }, { z: 5 } ], 'not same deep ordered members');
+   * assert.notSameDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { a: 1 }, { c: 3 } ], 'not same deep ordered members');
+   *
+   * @name notSameDeepOrderedMembers
+   * @param {Array} set1
+   * @param {Array} set2
+   * @param {String} message
+   * @namespace Assert
+   * @api public
+   */
+
+  assert.notSameDeepOrderedMembers = function (set1, set2, msg) {
+    new Assertion(set1, msg).to.not.have.same.deep.ordered.members(set2);
+  }
+
   /**
    * ### .includeMembers(superset, subset, [message])
    *
-   * Asserts that `subset` is included in `superset`.
-   * Order is not taken into account.
+   * Asserts that `subset` is included in `superset` in any order. Uses a
+   * strict equality check (===). Duplicates are ignored.
    *
-   *     assert.includeMembers([ 1, 2, 3 ], [ 2, 1 ], 'include members');
+   *     assert.includeMembers([ 1, 2, 3 ], [ 2, 1, 2 ], 'include members');
    *
    * @name includeMembers
    * @param {Array} superset
@@ -1448,14 +1569,33 @@ module.exports = function (chai, util) {
     new Assertion(superset, msg).to.include.members(subset);
   }
 
+  /**
+   * ### .notIncludeMembers(superset, subset, [message])
+   *
+   * Asserts that `subset` isn't included in `superset` in any order. Uses a
+   * strict equality check (===). Duplicates are ignored.
+   *
+   *     assert.notIncludeMembers([ 1, 2, 3 ], [ 5, 1 ], 'not include members');
+   *
+   * @name notIncludeMembers
+   * @param {Array} superset
+   * @param {Array} subset
+   * @param {String} message
+   * @namespace Assert
+   * @api public
+   */
+
+  assert.notIncludeMembers = function (superset, subset, msg) {
+    new Assertion(superset, msg).to.not.include.members(subset);
+  }
+
   /**
    * ### .includeDeepMembers(superset, subset, [message])
    *
-   * Asserts that `subset` is included in `superset` - using deep equality checking.
-   * Order is not taken into account.
-   * Duplicates are ignored.
+   * Asserts that `subset` is included in `superset` in any order. Uses a deep
+   * equality check. Duplicates are ignored.
    *
-   *     assert.includeDeepMembers([ {a: 1}, {b: 2}, {c: 3} ], [ {b: 2}, {a: 1}, {b: 2} ], 'include deep members');
+   *     assert.includeDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { a: 1 }, { b: 2 } ], 'include deep members');
    *
    * @name includeDeepMembers
    * @param {Array} superset
@@ -1469,6 +1609,113 @@ module.exports = function (chai, util) {
     new Assertion(superset, msg).to.include.deep.members(subset);
   }
 
+  /**
+   * ### .notIncludeDeepMembers(superset, subset, [message])
+   *
+   * Asserts that `subset` isn't included in `superset` in any order. Uses a
+   * deep equality check. Duplicates are ignored.
+   *
+   *     assert.notIncludeDeepMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { f: 5 } ], 'not include deep members');
+   *
+   * @name notIncludeDeepMembers
+   * @param {Array} superset
+   * @param {Array} subset
+   * @param {String} message
+   * @namespace Assert
+   * @api public
+   */
+
+  assert.notIncludeDeepMembers = function (superset, subset, msg) {
+    new Assertion(superset, msg).to.not.include.deep.members(subset);
+  }
+
+  /**
+   * ### .includeOrderedMembers(superset, subset, [message])
+   *
+   * Asserts that `subset` is included in `superset` in the same order
+   * beginning with the first element in `superset`. Uses a strict equality
+   * check (===).
+   *
+   *     assert.includeOrderedMembers([ 1, 2, 3 ], [ 1, 2 ], 'include ordered members');
+   *
+   * @name includeOrderedMembers
+   * @param {Array} superset
+   * @param {Array} subset
+   * @param {String} message
+   * @namespace Assert
+   * @api public
+   */
+
+  assert.includeOrderedMembers = function (superset, subset, msg) {
+    new Assertion(superset, msg).to.include.ordered.members(subset);
+  }
+
+  /**
+   * ### .notIncludeOrderedMembers(superset, subset, [message])
+   *
+   * Asserts that `subset` isn't included in `superset` in the same order
+   * beginning with the first element in `superset`. Uses a strict equality
+   * check (===).
+   *
+   *     assert.notIncludeOrderedMembers([ 1, 2, 3 ], [ 2, 1 ], 'not include ordered members');
+   *     assert.notIncludeOrderedMembers([ 1, 2, 3 ], [ 2, 3 ], 'not include ordered members');
+   *
+   * @name notIncludeOrderedMembers
+   * @param {Array} superset
+   * @param {Array} subset
+   * @param {String} message
+   * @namespace Assert
+   * @api public
+   */
+
+  assert.notIncludeOrderedMembers = function (superset, subset, msg) {
+    new Assertion(superset, msg).to.not.include.ordered.members(subset);
+  }
+
+  /**
+   * ### .includeDeepOrderedMembers(superset, subset, [message])
+   *
+   * Asserts that `subset` is included in `superset` in the same order
+   * beginning with the first element in `superset`. Uses a deep equality
+   * check.
+   *
+   *     assert.includeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { b: 2 } ], 'include deep ordered members');
+   *
+   * @name includeDeepOrderedMembers
+   * @param {Array} superset
+   * @param {Array} subset
+   * @param {String} message
+   * @namespace Assert
+   * @api public
+   */
+
+  assert.includeDeepOrderedMembers = function (superset, subset, msg) {
+    new Assertion(superset, msg).to.include.deep.ordered.members(subset);
+  }
+
+  /**
+   * ### .notIncludeDeepOrderedMembers(superset, subset, [message])
+   *
+   * Asserts that `subset` isn't included in `superset` in the same order
+   * beginning with the first element in `superset`. Uses a deep equality
+   * check.
+   *
+   *     assert.notIncludeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { a: 1 }, { f: 5 } ], 'not include deep ordered members');
+   *     assert.notIncludeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { a: 1 } ], 'not include deep ordered members');
+   *     assert.notIncludeDeepOrderedMembers([ { a: 1 }, { b: 2 }, { c: 3 } ], [ { b: 2 }, { c: 3 } ], 'not include deep ordered members');
+   *
+   * @name notIncludeDeepOrderedMembers
+   * @param {Array} superset
+   * @param {Array} subset
+   * @param {String} message
+   * @namespace Assert
+   * @api public
+   */
+
+  assert.notIncludeDeepOrderedMembers = function (superset, subset, msg) {
+    new Assertion(superset, msg).to.not.include.deep.ordered.members(subset);
+  }
+
   /**
    * ### .oneOf(inList, list, [message])
    *


Test patch:
diff --git a/test/assert.js b/test/assert.js
index bc7438b..bf5d763 100644
--- a/test/assert.js
+++ b/test/assert.js
@@ -1169,8 +1169,86 @@ describe('assert', function () {
     }, "the arguments to closeTo or approximately must be numbers");
   });
 
-  it('members', function() {
-    assert.includeMembers([1, 2, 3], [2, 3]);
+  it('sameMembers', function() {
+    assert.sameMembers([], []);
+    assert.sameMembers([1, 2, 3], [3, 2, 1]);
+    assert.sameMembers([4, 2], [4, 2]);
+
+    err(function() {
+      assert.sameMembers([], [1, 2]);
+    }, 'expected [] to have the same members as [ 1, 2 ]');
+
+    err(function() {
+      assert.sameMembers([1, 54], [6, 1, 54]);
+    }, 'expected [ 1, 54 ] to have the same members as [ 6, 1, 54 ]');
+  });
+
+  it('notSameMembers', function() {
+    assert.notSameMembers([1, 2, 3], [2, 1, 5]);
+    assert.notSameMembers([{a: 1}], [{a: 1}]);
+
+    err(function() {
+      assert.notSameMembers([1, 2, 3], [2, 1, 3]);
+    }, 'expected [ 1, 2, 3 ] to not have the same members as [ 2, 1, 3 ]');
+  });
+
+  it('sameDeepMembers', function() {
+    assert.sameDeepMembers([ {b: 3}, {a: 2}, {c: 5} ], [ {c: 5}, {b: 3}, {a: 2} ], 'same deep members');
+    assert.sameDeepMembers([ {b: 3}, {a: 2}, 5, "hello" ], [ "hello", 5, {b: 3}, {a: 2} ], 'same deep members');
+
+    err(function() {
+      assert.sameDeepMembers([ {b: 3} ], [ {c: 3} ])
+    }, 'expected [ { b: 3 } ] to have the same members as [ { c: 3 } ]');
+
+    err(function() {
+      assert.sameDeepMembers([ {b: 3} ], [ {b: 5} ])
+    }, 'expected [ { b: 3 } ] to have the same members as [ { b: 5 } ]');
+  });
+
+  it('notSameDeepMembers', function() {
+    assert.notSameDeepMembers([{a: 1}, {b: 2}, {c: 3}], [{b: 2}, {a: 1}, {f: 5}]);
+
+    err(function() {
+      assert.notSameDeepMembers([{a: 1}, {b: 2}, {c: 3}], [{b: 2}, {a: 1}, {c: 3}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to not have the same members as [ { b: 2 }, { a: 1 }, { c: 3 } ]');
+  });
+
+  it('sameOrderedMembers', function() {
+    assert.sameOrderedMembers([1, 2, 3], [1, 2, 3]);
+
+    err(function() {
+      assert.sameOrderedMembers([1, 2, 3], [2, 1, 3]);
+    }, 'expected [ 1, 2, 3 ] to have the same ordered members as [ 2, 1, 3 ]');
+  });
+
+  it('notSameOrderedMembers', function() {
+    assert.notSameOrderedMembers([1, 2, 3], [2, 1, 3]);
+    assert.notSameOrderedMembers([1, 2, 3], [1, 2]);
+
+    err(function() {
+      assert.notSameOrderedMembers([1, 2, 3], [1, 2, 3]);
+    }, 'expected [ 1, 2, 3 ] to not have the same ordered members as [ 1, 2, 3 ]');
+  });
+
+  it('sameDeepOrderedMembers', function() {
+    assert.sameDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{a: 1}, {b: 2}, {c: 3}]);
+
+    err(function() {
+      assert.sameDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{b: 2}, {a: 1}, {c: 3}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to have the same ordered members as [ { b: 2 }, { a: 1 }, { c: 3 } ]');
+  });
+
+  it('notSameDeepOrderedMembers', function() {
+    assert.notSameDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{b: 2}, {a: 1}, {c: 3}]);
+    assert.notSameDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{a: 1}, {b: 2}, {f: 5}]);
+
+    err(function() {
+      assert.notSameDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{a: 1}, {b: 2}, {c: 3}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to not have the same ordered members as [ { a: 1 }, { b: 2 }, { c: 3 } ]');
+  });
+
+  it('includeMembers', function() {
+    assert.includeMembers([1, 2, 3], [2, 3, 2]);
     assert.includeMembers([1, 2, 3], []);
     assert.includeMembers([1, 2, 3], [3]);
 
@@ -1183,18 +1261,71 @@ describe('assert', function () {
     }, 'expected [ 5, 6 ] to be a superset of [ 5, 6, 0 ]');
   });
 
-  it('memberEquals', function() {
-    assert.sameMembers([], []);
-    assert.sameMembers([1, 2, 3], [3, 2, 1]);
-    assert.sameMembers([4, 2], [4, 2]);
+  it('notIncludeMembers', function() {
+    assert.notIncludeMembers([1, 2, 3], [5, 1]);
+    assert.notIncludeMembers([{a: 1}], [{a: 1}]);
 
     err(function() {
-      assert.sameMembers([], [1, 2]);
-    }, 'expected [] to have the same members as [ 1, 2 ]');
+      assert.notIncludeMembers([1, 2, 3], [2, 1]);
+    }, 'expected [ 1, 2, 3 ] to not be a superset of [ 2, 1 ]');
+  });
+
+  it('includeDeepMembers', function() {
+    assert.includeDeepMembers([{a:1}, {b:2}, {c:3}], [{c:3}, {b:2}]);
+    assert.includeDeepMembers([{a:1}, {b:2}, {c:3}], []);
+    assert.includeDeepMembers([{a:1}, {b:2}, {c:3}], [{c:3}]);
+    assert.includeDeepMembers([{a:1}, {b:2}, {c:3}, {c:3}], [{c:3}, {c:3}]);
+    assert.includeDeepMembers([{a:1}, {b:2}, {c:3}], [{c:3}, {c:3}]);
 
     err(function() {
-      assert.sameMembers([1, 54], [6, 1, 54]);
-    }, 'expected [ 1, 54 ] to have the same members as [ 6, 1, 54 ]');
+      assert.includeDeepMembers([{e:5}, {f:6}], [{g:7}, {h:8}]);
+    }, 'expected [ { e: 5 }, { f: 6 } ] to be a superset of [ { g: 7 }, { h: 8 } ]');
+
+    err(function() {
+      assert.includeDeepMembers([{e:5}, {f:6}], [{e:5}, {f:6}, {z:0}]);
+    }, 'expected [ { e: 5 }, { f: 6 } ] to be a superset of [ { e: 5 }, { f: 6 }, { z: 0 } ]');
+  });
+
+  it('notIncludeDeepMembers', function() {
+    assert.notIncludeDeepMembers([{a:1}, {b:2}, {c:3}], [{b:2}, {f:5}]);
+
+    err(function() {
+      assert.notIncludeDeepMembers([{a:1}, {b:2}, {c:3}], [{b:2}, {a:1}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to not be a superset of [ { b: 2 }, { a: 1 } ]');
+  });
+
+  it('includeOrderedMembers', function() {
+    assert.includeOrderedMembers([1, 2, 3], [1, 2]);
+
+    err(function() {
+      assert.includeOrderedMembers([1, 2, 3], [2, 1]);
+    }, 'expected [ 1, 2, 3 ] to be an ordered superset of [ 2, 1 ]');
+  });
+
+  it('notIncludeOrderedMembers', function() {
+    assert.notIncludeOrderedMembers([1, 2, 3], [2, 1]);
+    assert.notIncludeOrderedMembers([1, 2, 3], [2, 3]);
+
+    err(function() {
+      assert.notIncludeOrderedMembers([1, 2, 3], [1, 2]);
+    }, 'expected [ 1, 2, 3 ] to not be an ordered superset of [ 1, 2 ]');
+  });
+
+  it('includeDeepOrderedMembers', function() {
+    assert.includeDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{a: 1}, {b: 2}]);
+
+    err(function() {
+      assert.includeDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{b: 2}, {a: 1}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to be an ordered superset of [ { b: 2 }, { a: 1 } ]');
+  });
+
+  it('notIncludeDeepOrderedMembers', function() {
+    assert.notIncludeDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{b: 2}, {a: 1}]);
+    assert.notIncludeDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{a: 1}, {f: 5}]);
+
+    err(function() {
+      assert.notIncludeDeepOrderedMembers([{a: 1}, {b: 2}, {c: 3}], [{a: 1}, {b: 2}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to not be an ordered superset of [ { a: 1 }, { b: 2 } ]');
   });
 
   it('oneOf', function() {
@@ -1271,35 +1402,6 @@ describe('assert', function () {
     }, 'expected 3 to be at most 1');
   });
 
-  it('memberDeepEquals', function() {
-    assert.sameDeepMembers([ {b: 3}, {a: 2}, {c: 5} ], [ {c: 5}, {b: 3}, {a: 2} ], 'same deep members');
-    assert.sameDeepMembers([ {b: 3}, {a: 2}, 5, "hello" ], [ "hello", 5, {b: 3}, {a: 2} ], 'same deep members');
-
-    err(function() {
-      assert.sameDeepMembers([ {b: 3} ], [ {c: 3} ])
-    }, 'expected [ { b: 3 } ] to have the same members as [ { c: 3 } ]');
-
-    err(function() {
-      assert.sameDeepMembers([ {b: 3} ], [ {b: 5} ])
-    }, 'expected [ { b: 3 } ] to have the same members as [ { b: 5 } ]');
-  });
-
-  it('includeDeepMembers', function() {
-    assert.includeDeepMembers([{a:1}, {b:2}, {c:3}], [{c:3}, {b:2}]);
-    assert.includeDeepMembers([{a:1}, {b:2}, {c:3}], []);
-    assert.includeDeepMembers([{a:1}, {b:2}, {c:3}], [{c:3}]);
-    assert.includeDeepMembers([{a:1}, {b:2}, {c:3}, {c:3}], [{c:3}, {c:3}]);
-    assert.includeDeepMembers([{a:1}, {b:2}, {c:3}], [{c:3}, {c:3}]);
-
-    err(function() {
-      assert.includeDeepMembers([{e:5}, {f:6}], [{g:7}, {h:8}]);
-    }, 'expected [ { e: 5 }, { f: 6 } ] to be a superset of [ { g: 7 }, { h: 8 } ]');
-
-    err(function() {
-      assert.includeDeepMembers([{e:5}, {f:6}], [{e:5}, {f:6}, {z:0}]);
-    }, 'expected [ { e: 5 }, { f: 6 } ] to be a superset of [ { e: 5 }, { f: 6 }, { z: 0 } ]');
-  });
-
   it('change', function() {
     var obj = { value: 10, str: 'foo' },
         heroes = ['spiderman', 'superman'],
diff --git a/test/expect.js b/test/expect.js
index dfe954f..173a198 100644
--- a/test/expect.js
+++ b/test/expect.js
@@ -1337,6 +1337,15 @@ describe('expect', function () {
     expect([1, 2, 3]).to.include.members([3, 2]);
     expect([1, 2, 3]).to.not.include.members([8, 4]);
     expect([1, 2, 3]).to.not.include.members([1, 2, 3, 4]);
+    expect([{a: 1}]).to.not.include.members([{a: 1}]);
+
+    err(function() {
+      expect([1, 2, 3]).to.include.members([2, 5]);
+    }, 'expected [ 1, 2, 3 ] to be a superset of [ 2, 5 ]');
+
+    err(function() {
+      expect([1, 2, 3]).to.not.include.members([2, 1]);
+    }, 'expected [ 1, 2, 3 ] to not be a superset of [ 2, 1 ]');
   });
 
   it('same.members', function() {
@@ -1354,6 +1363,14 @@ describe('expect', function () {
     expect([5, 4]).not.members([6, 3]);
     expect([5, 4]).not.members([5, 4, 2]);
     expect([{ id: 1 }]).not.members([{ id: 1 }]);
+
+    err(function() {
+      expect([1, 2, 3]).members([2, 1, 5]);
+    }, 'expected [ 1, 2, 3 ] to have the same members as [ 2, 1, 5 ]');
+
+    err(function() {
+      expect([1, 2, 3]).not.members([2, 1, 3]);
+    }, 'expected [ 1, 2, 3 ] to not have the same members as [ 2, 1, 3 ]');
   });
 
   it('deep.members', function() {
@@ -1361,7 +1378,62 @@ describe('expect', function () {
     expect([{ id: 2 }]).not.deep.members([{ id: 1 }]);
     err(function(){
       expect([{ id: 1 }]).deep.members([{ id: 2 }])
-    }, "expected [ { id: 1 } ] to have the same members as [ { id: 2 } ]");
+    }, 'expected [ { id: 1 } ] to have the same members as [ { id: 2 } ]');
+  });
+
+  it('ordered.members', function() {
+    expect([1, 2, 3]).ordered.members([1, 2, 3]);
+    expect([1, 2, 3]).not.ordered.members([2, 1, 3]);
+    expect([1, 2, 3]).not.ordered.members([1, 2]);
+
+    err(function() {
+      expect([1, 2, 3]).ordered.members([2, 1, 3]);
+    }, 'expected [ 1, 2, 3 ] to have the same ordered members as [ 2, 1, 3 ]');
+
+    err(function() {
+      expect([1, 2, 3]).not.ordered.members([1, 2, 3]);
+    }, 'expected [ 1, 2, 3 ] to not have the same ordered members as [ 1, 2, 3 ]');
+  });
+
+  it('include.ordered.members', function() {
+    expect([1, 2, 3]).include.ordered.members([1, 2]);
+    expect([1, 2, 3]).not.include.ordered.members([2, 1]);
+    expect([1, 2, 3]).not.include.ordered.members([2, 3]);
+
+    err(function() {
+      expect([1, 2, 3]).include.ordered.members([2, 1]);
+    }, 'expected [ 1, 2, 3 ] to be an ordered superset of [ 2, 1 ]');
+
+    err(function() {
+      expect([1, 2, 3]).not.include.ordered.members([1, 2]);
+    }, 'expected [ 1, 2, 3 ] to not be an ordered superset of [ 1, 2 ]');
+  });
+
+  it('deep.ordered.members', function() {
+    expect([{a: 1}, {b: 2}, {c: 3}]).deep.ordered.members([{a: 1}, {b: 2}, {c: 3}]);
+    expect([{a: 1}, {b: 2}, {c: 3}]).not.deep.ordered.members([{b: 2}, {a: 1}, {c: 3}]);
+
+    err(function() {
+      expect([{a: 1}, {b: 2}, {c: 3}]).deep.ordered.members([{b: 2}, {a: 1}, {c: 3}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to have the same ordered members as [ { b: 2 }, { a: 1 }, { c: 3 } ]');
+
+    err(function() {
+      expect([{a: 1}, {b: 2}, {c: 3}]).not.deep.ordered.members([{a: 1}, {b: 2}, {c: 3}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to not have the same ordered members as [ { a: 1 }, { b: 2 }, { c: 3 } ]');
+  });
+
+  it('include.deep.ordered.members', function() {
+    expect([{a: 1}, {b: 2}, {c: 3}]).include.deep.ordered.members([{a: 1}, {b: 2}]);
+    expect([{a: 1}, {b: 2}, {c: 3}]).not.include.deep.ordered.members([{b: 2}, {a: 1}]);
+    expect([{a: 1}, {b: 2}, {c: 3}]).not.include.deep.ordered.members([{b: 2}, {c: 3}]);
+
+    err(function() {
+      expect([{a: 1}, {b: 2}, {c: 3}]).include.deep.ordered.members([{b: 2}, {a: 1}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to be an ordered superset of [ { b: 2 }, { a: 1 } ]');
+
+    err(function() {
+      expect([{a: 1}, {b: 2}, {c: 3}]).not.include.deep.ordered.members([{a: 1}, {b: 2}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to not be an ordered superset of [ { a: 1 }, { b: 2 } ]');
   });
 
   it('change', function() {
diff --git a/test/should.js b/test/should.js
index 0e686a5..1b33690 100644
--- a/test/should.js
+++ b/test/should.js
@@ -1175,6 +1175,7 @@ describe('should', function() {
 
     [1, 2, 3].should.not.include.members([999]);
     [].should.not.include.members([23]);
+    [{a: 1}].should.not.include.members([{a: 1}]);
 
     err(function() {
       [].should.include.members([43]);
@@ -1198,6 +1199,8 @@ describe('should', function() {
     [5, 4].should.have.same.members([5, 4]);
     [].should.have.same.members([]);
 
+    [{a: 1}].should.not.have.same.members([{a: 1}]);
+
     err(function() {
       [1, 2, 3].should.have.same.members([]);
     }, 'expected [ 1, 2, 3 ] to have the same members as []');
@@ -1207,6 +1210,61 @@ describe('should', function() {
     }, 'expected 4 to be an array');
   });
 
+  it('ordered.members', function() {
+    [1, 2, 3].should.ordered.members([1, 2, 3]);
+    [1, 2, 3].should.not.ordered.members([2, 1, 3]);
+    [1, 2, 3].should.not.ordered.members([1, 2]);
+
+    err(function() {
+      [1, 2, 3].should.ordered.members([2, 1, 3]);
+    }, 'expected [ 1, 2, 3 ] to have the same ordered members as [ 2, 1, 3 ]');
+
+    err(function() {
+      [1, 2, 3].should.not.ordered.members([1, 2, 3]);
+    }, 'expected [ 1, 2, 3 ] to not have the same ordered members as [ 1, 2, 3 ]');
+  });
+
+  it('include.ordered.members', function() {
+    [1, 2, 3].should.include.ordered.members([1, 2]);
+    [1, 2, 3].should.not.include.ordered.members([2, 1]);
+    [1, 2, 3].should.not.include.ordered.members([2, 3]);
+
+    err(function() {
+      [1, 2, 3].should.include.ordered.members([2, 1]);
+    }, 'expected [ 1, 2, 3 ] to be an ordered superset of [ 2, 1 ]');
+
+    err(function() {
+      [1, 2, 3].should.not.include.ordered.members([1, 2]);
+    }, 'expected [ 1, 2, 3 ] to not be an ordered superset of [ 1, 2 ]');
+  });
+
+  it('deep.ordered.members', function() {
+    [{a: 1}, {b: 2}, {c: 3}].should.deep.ordered.members([{a: 1}, {b: 2}, {c: 3}]);
+    [{a: 1}, {b: 2}, {c: 3}].should.not.deep.ordered.members([{b: 2}, {a: 1}, {c: 3}]);
+
+    err(function() {
+      [{a: 1}, {b: 2}, {c: 3}].should.deep.ordered.members([{b: 2}, {a: 1}, {c: 3}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to have the same ordered members as [ { b: 2 }, { a: 1 }, { c: 3 } ]');
+
+    err(function() {
+      [{a: 1}, {b: 2}, {c: 3}].should.not.deep.ordered.members([{a: 1}, {b: 2}, {c: 3}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to not have the same ordered members as [ { a: 1 }, { b: 2 }, { c: 3 } ]');
+  });
+
+  it('include.deep.ordered.members', function() {
+    [{a: 1}, {b: 2}, {c: 3}].should.include.deep.ordered.members([{a: 1}, {b: 2}]);
+    [{a: 1}, {b: 2}, {c: 3}].should.not.include.deep.ordered.members([{b: 2}, {a: 1}]);
+    [{a: 1}, {b: 2}, {c: 3}].should.not.include.deep.ordered.members([{b: 2}, {c: 3}]);
+
+    err(function() {
+      [{a: 1}, {b: 2}, {c: 3}].should.include.deep.ordered.members([{b: 2}, {a: 1}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to be an ordered superset of [ { b: 2 }, { a: 1 } ]');
+
+    err(function() {
+      [{a: 1}, {b: 2}, {c: 3}].should.not.include.deep.ordered.members([{a: 1}, {b: 2}]);
+    }, 'expected [ { a: 1 }, { b: 2 }, { c: 3 } ] to not be an ordered superset of [ { a: 1 }, { b: 2 } ]');
+  });
+
   it('change', function() {
     var obj = { value: 10, str: 'foo' },
         heroes = ['spiderman', 'superman'],