<NME> mix-test.js
<BEF> 'use strict';

import {assert} from 'chai';

import {
  apply,
  isApplicationOf,
  wrap,
  unwrap,
  hasMixin,
  Mixin,
  BareMixin,
  Cache,
  DeDupe,
  HasInstance,
  mix,
} from '../mixwith';



    test('apply() applies a mixin function', () => {
      const M = (s) => class extends s {
        test() {
          return true;
        }
      };
      class Test extends apply(Object, M) {}
      const i = new Test();
      assert.isTrue(i.test());
    });

    test('isApplication() returns true for a mixin applied by apply()', () => {
      const M = (s) => class extends s {};
      assert.isTrue(isApplicationOf(apply(Object, M).prototype, M));
    });

    test('isApplication() works with wrapped mixins', () => {
      const M = (s) => class extends s {};
      const WrappedM = wrap(M, (superclass) => apply(superclass, M));
      assert.isTrue(isApplicationOf(WrappedM(Object).prototype, WrappedM));
    });

    test('isApplication() returns false when it should', () => {
      const M = (s) => class extends s {};
      const X = (s) => class extends s {};
      assert.isFalse(isApplicationOf(apply(Object, M).prototype, X));
    });

  });

  suite('hasMixin()', () => {
    test('hasMixin() returns true for a mixin applied by apply()', () => {
      const M = (s) => class extends s {};

      assert.isTrue(hasMixin(apply(Object, M).prototype, M));
    });

  });

  suite('wrap() and unwrap()', () => {

    test('wrap() sets the prototype', () => {
      const f = (x) => x*x;
      f.test = true;
      const wrapper = (x) => f(x);
      wrap(f, wrapper);
      assert.isTrue(wrapper.test);
      assert.equal(f, Object.getPrototypeOf(wrapper));
    });

    test('unwrap() returns the wrapped function', () => {
      const f = (x) => x*x;
      const wrapper = (x) => f(x);
      wrap(f, wrapper);
      assert.equal(f, unwrap(wrapper));
    });

  });

  suite('BareMixin', () => {

    test('mixin application is on prototype chain', () => {
      const M = BareMixin((s) => class extends s {});
      class C extends M(Object) {}
      const i = new C();
      assert.isTrue(hasMixin(i, M));
    });

    test('methods on mixin are present', () => {
      const M = BareMixin((s) => class extends s {
        foo() { return 'foo'; }
      });
      class C extends M(Object) {}
      const i = new C();
      assert.deepEqual(i.foo(), 'foo');
    });

    test('methods on superclass are present', () => {
      const M = BareMixin((s) => class extends s {});
      class S {
        foo() { return 'foo'; }
      }
      class C extends M(S) {}
      const i = new C();
      assert.deepEqual(i.foo(), 'foo');
    });

    test('methods on subclass are present', () => {
      const M = BareMixin((s) => class extends s {});
      class C extends M(Object) {
        foo() { return 'foo'; }
      }
      const i = new C();
      assert.deepEqual(i.foo(), 'foo');
    });

    test('methods on mixin override superclass', () => {
      const M = BareMixin((s) => class extends s {
        foo() { return 'bar'; }
      });
      class S {
        foo() { return 'foo'; }
      }
      class C extends M(S) {}
      const i = new C();
      assert.deepEqual(i.foo(), 'bar');
    });

    test('methods on mixin can call super', () => {
      const M = BareMixin((s) => class extends s {
        foo() { return super.foo(); }
      });
      class S {
        foo() { return 'superfoo'; }
      }
      class C extends M(S) {}
      const i = new C();
      assert.deepEqual(i.foo(), 'superfoo');
    });

    test('methods on subclass override superclass', () => {
      const M = BareMixin((s) => class extends s {});
      class S {
        foo() { return 'superfoo'; }
      }
      class C extends M(S) {
        foo() { return 'subfoo'; }
      }
      const i = new C();
      assert.deepEqual(i.foo(), 'subfoo');
    });

    test('methods on subclass override mixin', () => {
      const M = BareMixin((s) => class extends s {
        foo() { return 'mixinfoo'; }
      });
      class S {}
      class C extends M(S) {
        foo() { return 'subfoo'; }
      }
      const i = new C();
      assert.deepEqual(i.foo(), 'subfoo');
    });

    test('methods on subclass can call super to superclass', () => {
      const M = BareMixin((s) => class extends s {});
      class S {
        foo() { return 'superfoo'; }
      }
      class C extends M(S) {
        foo() { return super.foo(); }
      }
      const i = new C();
      assert.deepEqual(i.foo(), 'superfoo');
    });

  });

  suite('DeDupe', () => {

    test('applies the mixin the first time', () => {

  });

  suite('DeDupe', () => {

    test('applies the mixin the first time', () => {

  });

  suite('mix().with()', () => {

    test('subclasses are subclasses', () => {
      let o = new DwithA();
      assert.instanceOf(o, D);
    });

    test('mixin application is on prototype chain', () => {
      let o = new DwithA();
      assert.isTrue(hasMixin(o, A));
    });

    test('subclasses implement mixins', () => {

<MSG> Better tests with localized test cases.
<DFF> @@ -16,109 +16,6 @@ import {
   mix,
 } from '../mixwith';
 
-
-// Enable the @@hasInstance patch in mixwith.HasInstance
-if (!Symbol.hasInstance) {
-  Symbol.hasInstance = Symbol('hasInstance');
-}
-
-let A = Mixin((superclass) => class A extends superclass {
-  foo() {
-    return ['A.foo'];
-  }
-
-  bar() {
-    return ['A.bar'];
-  }
-
-  baz() {
-    return ['A.baz before'].concat(super.baz()).concat(['A.baz after']);
-  }
-
-});
-
-let B = Mixin((superclass) => class extends superclass {
-  bar() {
-    console.log('B.bar');
-  }
-});
-
-let C = Mixin((superclass) => class extends superclass {
-  constructor() {
-    console.log('C.constructor', arguments);
-    super(...arguments);
-  }
-  bar() {
-    console.log('C.bar');
-    super.bar();
-  }
-});
-
-class D {
-
-  bar() {
-    return ['D.bar'];
-  }
-
-  baz() {
-    return ['D.baz'];
-  }
-
-  qux() {
-    return ['D.qux'];
-  }
-
-}
-
-class DwithA extends mix(D).with(A) {
-
-  quux() {
-    return ['DwithA.quux'];
-  }
-
-}
-
-class DwithB extends mix(D).with(B) {
-  bar() {
-    return ['DwithB.bar'];
-  }
-
-  baz() {
-    return ['DwithB.baz'];
-  }
-
-  qux() {
-    return ['DwithB.qux before'].concat(super.qux()).concat(['DwithB.qux after']);
-  }
-
-}
-
-class DwithAwithB extends mix(D).with(B, A) {
-  // constructor() {
-  //   super('OK');
-  // }
-
-  qux() {
-    return ['DwithAwithB.qux'];
-  }
-}
-
-// class DwithAwithC extends mix(D).with(C, A) {
-//   constructor() {
-//     console.log('DwithAwithC.constructor');
-//     super();
-//   }
-//
-//   bar() {
-//     console.log('DwithAwithC.bar');
-//     super.bar();
-//   }
-//
-//   qux() {
-//     console.log('DwithAwithC.qux');
-//   }
-// }
-
 suite('mixwith.js', () => {
 
   suite('apply() and isApplicationOf()', () => {
@@ -182,6 +79,105 @@ suite('mixwith.js', () => {
 
   });
 
+  suite('BareMixin', () => {
+
+    test('mixin application is on prototype chain', () => {
+      const M = BareMixin((s) => class extends s {});
+      class C extends M(Object) {}
+      const i = new C();
+      assert.isTrue(hasMixin(i, M));
+    });
+
+    test('methods on mixin are present', () => {
+      const M = BareMixin((s) => class extends s {
+        foo() { return 'foo'; }
+      });
+      class C extends M(Object) {}
+      const i = new C();
+      assert.deepEqual(i.foo(), 'foo');
+    });
+
+    test('methods on superclass are present', () => {
+      const M = BareMixin((s) => class extends s {});
+      class S {
+        foo() { return 'foo'; }
+      }
+      class C extends M(S) {}
+      const i = new C();
+      assert.deepEqual(i.foo(), 'foo');
+    });
+
+    test('methods on subclass are present', () => {
+      const M = BareMixin((s) => class extends s {});
+      class C extends M(Object) {
+        foo() { return 'foo'; }
+      }
+      const i = new C();
+      assert.deepEqual(i.foo(), 'foo');
+    });
+
+    test('methods on mixin override superclass', () => {
+      const M = BareMixin((s) => class extends s {
+        foo() { return 'bar'; }
+      });
+      class S {
+        foo() { return 'foo'; }
+      }
+      class C extends M(S) {}
+      const i = new C();
+      assert.deepEqual(i.foo(), 'bar');
+    });
+
+    test('methods on mixin can call super', () => {
+      const M = BareMixin((s) => class extends s {
+        foo() { return super.foo(); }
+      });
+      class S {
+        foo() { return 'superfoo'; }
+      }
+      class C extends M(S) {}
+      const i = new C();
+      assert.deepEqual(i.foo(), 'superfoo');
+    });
+
+    test('methods on subclass override superclass', () => {
+      const M = BareMixin((s) => class extends s {});
+      class S {
+        foo() { return 'superfoo'; }
+      }
+      class C extends M(S) {
+        foo() { return 'subfoo'; }
+      }
+      const i = new C();
+      assert.deepEqual(i.foo(), 'subfoo');
+    });
+
+    test('methods on subclass override mixin', () => {
+      const M = BareMixin((s) => class extends s {
+        foo() { return 'mixinfoo'; }
+      });
+      class S {}
+      class C extends M(S) {
+        foo() { return 'subfoo'; }
+      }
+      const i = new C();
+      assert.deepEqual(i.foo(), 'subfoo');
+    });
+
+    test('methods on subclass can call super to superclass', () => {
+      const M = BareMixin((s) => class extends s {});
+      class S {
+        foo() { return 'superfoo'; }
+      }
+      class C extends M(S) {
+        foo() { return super.foo(); }
+      }
+      const i = new C();
+      assert.deepEqual(i.foo(), 'superfoo');
+    });
+
+  });
+
   suite('DeDupe', () => {
 
     test('applies the mixin the first time', () => {
@@ -205,73 +201,51 @@ suite('mixwith.js', () => {
 
   });
 
-  suite('mix().with()', () => {
-
-    test('subclasses are subclasses', () => {
-      let o = new DwithA();
-      assert.instanceOf(o, D);
-    });
+  suite('HasInstance', () => {
 
-    test('mixin application is on prototype chain', () => {
-      let o = new DwithA();
-      assert.isTrue(hasMixin(o, A));
-    });
+    let hasNativeHasInstance = false;
 
-    test('subclasses implement mixins', () => {
+    suiteSetup(() => {
+      // Enable the @@hasInstance patch in mixwith.HasInstance
+      if (!Symbol.hasInstance) {
+        Symbol.hasInstance = Symbol('hasInstance');
+      }
 
       class Check {
         static [Symbol.hasInstance](o) { return true; }
       }
-      let hasNativeHasInstance = 1 instanceof Check;
-
-      if (hasNativeHasInstance) {
-        assert.instanceOf(o, A);
-      }
+      hasNativeHasInstance = 1 instanceof Check;
     });
 
-    test('methods on mixin are present', () => {
-      let o = new DwithA();
-      assert.deepEqual(o.foo(), ['A.foo']);
-    });
-
-    test('methods on superclass are present', () => {
-      let o = new DwithA();
-      assert.deepEqual(o.qux(), ['D.qux']);
-    });
-
-    test('methods on subclass are present', () => {
-      let o = new DwithA();
-      assert.deepEqual(o.quux(), ['DwithA.quux']);
-    });
-
-    test('methods on mixin override superclass', () => {
-      let o = new DwithA();
-      assert.deepEqual(o.bar(), ['A.bar']);
-    });
+    test('subclasses implement mixins', () => {
+      const M = HasInstance((s) => class extends s {});
+      class C extends M(Object) {}
+      const i = new C();
 
-    test('methods on mixin can call super', () => {
-      let o = new DwithA();
-      assert.deepEqual(o.baz(), ['A.baz before', 'D.baz', 'A.baz after']);
+      if (hasNativeHasInstance) {
+        assert.instanceOf(i, C);
+      } else {
+        assert.isTrue(C[Symbol.hasInstance](i));
+      }
     });
 
-    test('methods on subclass override superclass', () => {
-      let o = new DwithB();
-      assert.deepEqual(o.bar(), ['DwithB.bar']);
-    });
+  });
 
-    test('methods on subclass override mixin', () => {
-      let o = new DwithB();
-      assert.deepEqual(o.baz(), ['DwithB.baz']);
-    });
+  suite('mix().with()', () => {
 
-    test('methods on subclass can call super to superclass', () => {
-      let o = new DwithB();
-      assert.deepEqual(o.qux(), ['DwithB.qux before', 'D.qux', 'DwithB.qux after']);
+    test('applies mixins in order', () => {
+      const M1 = BareMixin((s) => class extends s {});
+      const M2 = BareMixin((s) => class extends s {});
+      class S {}
+      class C extends mix(S).with(M1, M2) {}
+      const i = new C();
+      assert.isTrue(hasMixin(i, M1));
+      assert.isTrue(hasMixin(i, M2));
+      assert.isTrue(isApplicationOf(i.__proto__.__proto__, M2));
+      assert.isTrue(isApplicationOf(i.__proto__.__proto__.__proto__, M1));
+      assert.equal(i.__proto__.__proto__.__proto__.__proto__, S.prototype);
     });
 
-
   });
 
-
-
 });

Better tests with localized test cases.