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

import {mix, _mixinRef, Mixin, _originalMixin} from '../mixwith';
import {assert} from 'chai';

        }
      };
      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'; }
//   }
// }

suite('mix', () => {

  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(o.__proto__.__proto__.hasOwnProperty(_mixinRef));
    assert.equal(o.__proto__.__proto__[_mixinRef], A[_originalMixin]);
  });

  test('subclasses implement mixins', () => {
    let o = new DwithA();
    assert.isTrue(A[Symbol.hasInstance](o));
    if (originalHasInstance) {
      assert.instanceOf(o, A);
    }
  });

  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('methods on mixin can call super', () => {
    let o = new DwithA();
    assert.deepEqual(o.baz(), ['A.baz before', 'D.baz', 'A.baz after']);
  });

  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']);
  });

  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']);
  });


});
          return 7;
        }

        bar() {
          return 'bar';
        }
      }
      let i = new C();
      assert.isTrue(hasMixin(i, M), 'hasMixin');
      assert.isTrue(isApplicationOf(i.__proto__.__proto__, M), 'isApplicationOf');
      assert.equal('foo', i.foo());
      assert.equal('bar', i.bar());
      assert.equal(42, C.staticMixinMethod());
      assert.equal(7, C.staticClassMethod());
    });

  });

});

<MSG> Add DeDupe decorator, apply(), isApplicationOf(), hasMixin(), unwrap()
<DFF> @@ -1,11 +1,26 @@
 'use strict';
 
-import {mix, _mixinRef, Mixin, _originalMixin} from '../mixwith';
 import {assert} from 'chai';
 
-// Enable the @@hasInstance patch in HasInstance
-const originalHasInstance = Symbol.hasInstance;
-Symbol.hasInstance = Symbol.hasInstance || Symbol('hasInstance');
+import {
+  apply,
+  isApplicationOf,
+  wrap,
+  unwrap,
+  hasMixin,
+  Mixin,
+  BareMixin,
+  Cache,
+  DeDupe,
+  HasInstance,
+  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() {
@@ -104,66 +119,136 @@ class DwithAwithB extends mix(D).with(B, A) {
 //   }
 // }
 
-suite('mix', () => {
+suite('mixwith.js', () => {
+
+  suite('apply() and isApplicationOf()', () => {
+
+    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));
+    });
 
-  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(o.__proto__.__proto__.hasOwnProperty(_mixinRef));
-    assert.equal(o.__proto__.__proto__[_mixinRef], A[_originalMixin]);
-  });
+  suite('hasMixin()', () => {
+    test('hasMixin() returns true for a mixin applied by apply()', () => {
+      const M = (s) => class extends s {};
 
-  test('subclasses implement mixins', () => {
-    let o = new DwithA();
-    assert.isTrue(A[Symbol.hasInstance](o));
-    if (originalHasInstance) {
-      assert.instanceOf(o, A);
-    }
-  });
+      assert.isTrue(hasMixin(apply(Object, M).prototype, M));
+    });
 
-  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']);
-  });
+  suite('wrap() and unwrap()', () => {
 
-  test('methods on subclass are present', () => {
-    let o = new DwithA();
-    assert.deepEqual(o.quux(), ['DwithA.quux']);
-  });
+    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('methods on mixin override superclass', () => {
-    let o = new DwithA();
-    assert.deepEqual(o.bar(), ['A.bar']);
-  });
+    test('unwrap() returns the wrapped function', () => {
+      const f = (x) => x*x;
+      const wrapper = (x) => f(x);
+      wrap(f, wrapper);
+      assert.equal(f, unwrap(wrapper));
+    });
 
-  test('methods on mixin can call super', () => {
-    let o = new DwithA();
-    assert.deepEqual(o.baz(), ['A.baz before', 'D.baz', 'A.baz after']);
   });
 
-  test('methods on subclass override superclass', () => {
-    let o = new DwithB();
-    assert.deepEqual(o.bar(), ['DwithB.bar']);
-  });
+  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', () => {
+
+      class Check {
+        static [Symbol.hasInstance](o) { return true; }
+      }
+      let hasNativeHasInstance = 1 instanceof Check;
+
+      if (hasNativeHasInstance) {
+        assert.instanceOf(o, A);
+      }
+    });
+
+    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('methods on mixin can call super', () => {
+      let o = new DwithA();
+      assert.deepEqual(o.baz(), ['A.baz before', 'D.baz', 'A.baz after']);
+    });
+
+    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']);
+    });
+
+    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('methods on subclass override mixin', () => {
-    let o = new DwithB();
-    assert.deepEqual(o.baz(), ['DwithB.baz']);
-  });
 
-  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']);
   });
 
 
+
 });

Add DeDupe decorator, apply(), isApplicationOf(), hasMixin(), unwrap()