@qooxdoo/framework
Version:
The JS Framework for Coders
876 lines (756 loc) • 20.6 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2007-2008 1&1 Internet AG, Germany, http://www.1und1.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Fabian Jakobs (fjakobs)
************************************************************************ */
/**
* @ignore(qx.ExtendUseLog1, qx.Mix, qx.MLogger, qx.MMix1, qx.MMix1.foo)
* @ignore(qx.MMix2, qx.MPatch, qx.Patch1, qx.Patch2, qx.UseLog1, qx.UseLog2)
* @ignore(qx.UseLog3)
*/
qx.Class.define("qx.test.Mixin", {
extend: qx.dev.unit.TestCase,
members: {
testMixinBasic() {
qx.Mixin.define("qx.MMix1", {
statics: {
data: null,
foo() {
return "foo";
}
},
members: {
bar() {
return "bar";
}
},
properties: { color: {} }
});
qx.Mixin.define("qx.MMix2", {
members: {
bar() {
return "bar";
}
}
});
qx.Class.define("qx.Mix", {
extend: Object,
include: qx.MMix1,
construct() {}
});
this.assertEquals("foo", qx.MMix1.foo());
this.assertEquals("bar", new qx.Mix().bar());
var mix = new qx.Mix();
mix.setColor("red");
this.assertEquals("red", mix.getColor());
if (this.isDebugOn()) {
this.assertException(
function () {
qx.Class.define("qx.Mix1", {
extend: Object,
include: [qx.MMix1, qx.MMix2],
construct() {}
});
},
Error,
"Overwriting member",
"t1"
);
this.assertException(
function () {
qx.Class.define("qx.Mix2", {
extend: Object,
include: qx.MMix1,
construct() {},
members: {
bar() {
return "bar";
}
}
});
},
Error,
"Overwriting member",
"t2"
);
}
// this is allowed
qx.Class.define("qx.Mix3", {
extend: Object,
include: qx.MMix1,
construct() {},
statics: {
foo() {
return "foo";
}
}
});
if (this.isDebugOn()) {
this.assertException(
function () {
qx.Class.define("qx.Mix4", {
extend: Object,
include: qx.MMix1,
construct() {},
properties: { color: {} }
});
},
Error,
"already has a property",
"t3"
);
}
},
testInclude() {
qx.Mixin.define("qx.MLogger", {
members: {
log(msg) {
return msg;
}
}
});
// normal usage
qx.Class.define("qx.UseLog1", {
extend: Object,
construct() {}
});
qx.Class.include(qx.UseLog1, qx.MLogger);
this.assertEquals("Juhu", new qx.UseLog1().log("Juhu"));
// not allowed to overwrite!
qx.Class.define("qx.UseLog2", {
extend: Object,
construct() {},
members: {
log() {
return "foo";
}
}
});
if (this.isDebugOn()) {
this.assertException(
function () {
qx.Class.include(qx.UseLog2, qx.MLogger);
},
Error,
"Overwriting member"
);
}
// allowed to overwrite!
qx.Class.define("qx.UseLog3", {
extend: Object,
construct() {},
members: {
log() {
return "foo";
}
}
});
this.assertEquals("foo", new qx.UseLog3().log("Juhu"));
qx.Class.patch(qx.UseLog3, qx.MLogger);
this.assertEquals("Juhu", new qx.UseLog3().log("Juhu"));
// extended classes must have included methods as well
qx.Class.define("qx.ExtendUseLog1", { extend: qx.UseLog1 });
this.assertEquals("Juhu", new qx.ExtendUseLog1().log("Juhu"));
},
testPatchOverwritten() {
qx.Class.define("qx.Patch1", {
extend: qx.core.Object,
members: {
sayJuhu() {
return "Juhu";
},
foo() {
return "foo";
}
}
});
qx.Class.define("qx.Patch2", {
extend: qx.core.Object,
members: {
sayJuhu() {
return "Huhu";
},
foo() {
return "bar";
}
}
});
qx.Mixin.define("qx.MPatch", {
members: {
sayJuhu() {
return super.sayJuhu() + " Kinners";
},
/** @lint ignoreUndeclaredPrivates(__b) */
foo(dontRecurs) {
var s = "";
if (!dontRecurs) {
this.__b = new qx.Patch2();
s += "++" + this.__b.foo(true) + "__";
}
s += super.foo();
return s;
}
}
});
if (this.isDebugOn()) {
this.assertException(
function () {
qx.Class.include(qx.Patch1, qx.MPatch);
},
Error,
new RegExp('Overwriting member ".*" of Class ".*" is not allowed!')
);
}
qx.Class.patch(qx.Patch1, qx.MPatch);
qx.Class.patch(qx.Patch2, qx.MPatch);
var o = new qx.Patch1();
this.assertEquals("Juhu Kinners", o.sayJuhu());
o.dispose();
var o = new qx.Patch2();
this.assertEquals("Huhu Kinners", o.sayJuhu());
o.dispose();
// very special case with recursive calls from different classes to
// the mixin member
var o = new qx.Patch1();
this.assertEquals("++bar__foo", o.foo());
o.__b.dispose();
o.dispose();
},
testPatchConstructors() {
this.assertTrue(
qx.test.testclasses.BaseClassIncluded.constructor ===
qx.test.testclasses.BaseClassIncluded
);
this.assertTrue(
qx.test.testclasses.BaseClassPatched.constructor ===
qx.test.testclasses.BaseClassPatched
);
this.assertTrue(
qx.test.testclasses.BaseClassBoth.constructor ===
qx.test.testclasses.BaseClassBoth
);
this.assertTrue(
qx.test.testclasses.DerivedClassIncluded.constructor ===
qx.test.testclasses.DerivedClassIncluded
);
this.assertTrue(
qx.test.testclasses.DerivedClassPatched.constructor ===
qx.test.testclasses.DerivedClassPatched
);
this.assertTrue(
qx.test.testclasses.DerivedClassBoth.constructor ===
qx.test.testclasses.DerivedClassBoth
);
var objBaseIncluded = new qx.test.testclasses.BaseClassIncluded();
this.assertArrayEquals(
["root", "base", "mixin-one", "mixin-two"],
objBaseIncluded.state
);
this.assertEquals("mixin-one", objBaseIncluded.getSomething());
var objIncluded = new qx.test.testclasses.DerivedClassIncluded();
this.assertArrayEquals(
["root", "base", "mixin-one", "mixin-two", "derived"],
objIncluded.state
);
this.assertEquals("mixin-one:derived", objIncluded.getSomething());
var objBasePatched = new qx.test.testclasses.BaseClassPatched();
this.assertArrayEquals(
["root", "base", "mixin-one", "mixin-two"],
objBasePatched.state
);
this.assertEquals("mixin-one", objBasePatched.getSomething());
var objPatched = new qx.test.testclasses.DerivedClassPatched();
this.assertArrayEquals(
["root", "base", "mixin-one", "mixin-two", "derived"],
objPatched.state
);
this.assertEquals("mixin-one:derived", objPatched.getSomething());
var objBaseBoth = new qx.test.testclasses.BaseClassBoth();
this.assertArrayEquals(
["root", "base", "mixin-one", "mixin-two"],
objBaseBoth.state
);
this.assertEquals("mixin-one", objBaseBoth.getSomething());
var objBoth = new qx.test.testclasses.DerivedClassBoth();
this.assertArrayEquals(
["root", "base", "mixin-one", "mixin-two", "derived"],
objBoth.state
);
this.assertEquals("mixin-one:derived", objBoth.getSomething());
this.assertTrue(
objBaseIncluded.constructor === qx.test.testclasses.BaseClassIncluded
);
this.assertTrue(
objIncluded.constructor === qx.test.testclasses.DerivedClassIncluded
);
this.assertTrue(
objBasePatched.constructor === qx.test.testclasses.BaseClassPatched
);
this.assertTrue(
objPatched.constructor === qx.test.testclasses.DerivedClassPatched
);
this.assertTrue(
objBaseBoth.constructor === qx.test.testclasses.BaseClassBoth
);
this.assertTrue(
objBoth.constructor === qx.test.testclasses.DerivedClassBoth
);
this.assertTrue(
objBaseIncluded instanceof qx.test.testclasses.BaseClassIncluded
);
this.assertTrue(
objIncluded instanceof qx.test.testclasses.DerivedClassIncluded
);
this.assertTrue(
objIncluded instanceof qx.test.testclasses.BaseClassIncluded
);
this.assertTrue(
objBasePatched instanceof qx.test.testclasses.BaseClassPatched
);
this.assertTrue(
objPatched instanceof qx.test.testclasses.BaseClassPatched
);
this.assertTrue(
objPatched instanceof qx.test.testclasses.DerivedClassPatched
);
this.assertTrue(objBaseBoth instanceof qx.test.testclasses.BaseClassBoth);
this.assertTrue(objBoth instanceof qx.test.testclasses.BaseClassBoth);
this.assertTrue(objBoth instanceof qx.test.testclasses.DerivedClassBoth);
this.assertTrue(objBaseIncluded instanceof qx.test.testclasses.RootClass);
this.assertTrue(objBaseBoth instanceof qx.test.testclasses.RootClass);
this.assertTrue(objPatched instanceof qx.test.testclasses.RootClass);
},
testPatchDouble() {
qx.Class.define("qx.Patch_1", {
extend: qx.core.Object,
members: {
sayJuhu() {
return "Juhu 1";
}
}
});
qx.Class.define("qx.Patch_2", {
extend: qx.core.Object,
members: {
sayJuhu() {
return "Juhu 2";
}
}
});
qx.Mixin.define("qx.MPatch", {
members: {
sayJuhu() {
return super.sayJuhu() + " Mixin";
}
}
});
qx.Class.patch(qx.Patch_1, qx.MPatch);
qx.Class.patch(qx.Patch_2, qx.MPatch);
var o = new qx.Patch_1();
this.assertEquals("Juhu 1 Mixin", o.sayJuhu());
o.dispose();
var o = new qx.Patch_2();
this.assertEquals("Juhu 2 Mixin", o.sayJuhu());
o.dispose();
},
testPatchOverwrittenDerived() {
qx.Class.define("qx.Patch", {
extend: qx.core.Object,
members: {
sayJuhu() {
return "Juhu";
}
}
});
qx.Class.define("qx.PatchDerived", {
extend: qx.Patch,
members: {
sayJuhu() {
return super.sayJuhu() + " Derived";
}
}
});
qx.Mixin.define("qx.MPatch", {
members: {
sayJuhu() {
return super.sayJuhu() + " Mixin";
}
}
});
qx.Class.patch(qx.PatchDerived, qx.MPatch);
var o = new qx.PatchDerived();
this.assertEquals("Juhu Derived Mixin", o.sayJuhu());
o.dispose();
},
testPatchOverwrittenDerivedInBaseClass() {
qx.Mixin.define("qx.MPatch", {
members: {
sayJuhu() {
return super.sayJuhu() + " Mixin";
}
}
});
qx.Class.define("qx.Patch", {
extend: qx.core.Object,
members: {
sayJuhu() {
return "Juhu";
}
}
});
qx.Class.define("qx.PatchDerived", {
extend: qx.Patch,
members: {
sayJuhu() {
return super.sayJuhu() + " Derived";
}
}
});
qx.Class.patch(qx.Patch, qx.MPatch);
var o = new qx.Patch();
this.assertEquals("Juhu Mixin", o.sayJuhu());
o.dispose();
var o = new qx.PatchDerived();
this.assertEquals("Juhu Mixin Derived", o.sayJuhu());
o.dispose();
},
testPatchMultiOverwrittenDerived() {
qx.Class.define("qx.A", {
extend: qx.core.Object,
members: {
sayJuhu() {
return "A";
}
}
});
qx.Class.define("qx.B", {
extend: qx.A,
members: {
sayJuhu() {
return super() + " B";
}
}
});
qx.Class.define("qx.C", {
extend: qx.B,
members: {
sayJuhu() {
return super() + " C";
}
}
});
qx.Mixin.define("qx.MA", {
members: {
sayJuhu() {
return super.sayJuhu() + " MA";
}
}
});
qx.Mixin.define("qx.MB", {
members: {
sayJuhu() {
return super.sayJuhu() + " MB";
}
}
});
qx.Mixin.define("qx.MC", {
members: {
sayJuhu() {
return super.sayJuhu() + " MC";
}
}
});
qx.Class.patch(qx.A, qx.MA);
qx.Class.patch(qx.B, qx.MB);
qx.Class.patch(qx.C, qx.MC);
var o = new qx.C();
this.assertEquals("A MA B MB C MC", o.sayJuhu());
o.dispose();
},
testDoubleMixin() {
qx.Class.define("qx.D", {
extend: qx.core.Object,
members: {
sayJuhu() {
return "Double";
}
}
});
qx.Mixin.define("qx.MDA", {
members: {
sayJuhu() {
return super.sayJuhu() + " MA";
}
}
});
qx.Mixin.define("qx.MDB", {
members: {
sayJuhu() {
return super.sayJuhu() + " MB";
}
}
});
qx.Class.patch(qx.D, qx.MDA);
qx.Class.patch(qx.D, qx.MDB);
var o = new qx.D();
this.assertEquals("Double MA MB", o.sayJuhu());
o.dispose();
},
testDoubleMixinWithSuperStruct() {
qx.Class.define("qx.E1", {
extend: qx.core.Object,
members: {
sayJuhu() {
return "E1";
}
}
});
qx.Mixin.define("qx.ME1a", {
members: {
sayJuhu() {
return `${super.sayJuhu()} ME1`;
}
}
});
qx.Mixin.define("qx.ME1b", {
members: {
// does not implement `sayJuhu`
}
});
qx.Class.define("qx.E2", {
extend: qx.E1,
members: {
sayJuhu() {
return `${super.sayJuhu()} E2`;
}
}
});
qx.Mixin.define("qx.ME2a", {
members: {
sayJuhu() {
return `${super.sayJuhu()} ME2a`;
}
}
});
qx.Mixin.define("qx.ME2b", {
members: {
sayJuhu() {
return `${super.sayJuhu()} ME2b`;
}
}
});
qx.Class.patch(qx.E1, qx.ME1a);
qx.Class.patch(qx.E1, qx.ME1b);
qx.Class.patch(qx.E2, qx.ME2a);
qx.Class.patch(qx.E2, qx.ME2b);
const e = new qx.E2();
this.assertEquals("E1 ME1 E2 ME2a ME2b", e.sayJuhu());
e.dispose();
},
/**
* tests a large structure to ensure that combinations of patch order,
* presence of given method, nor patched class count have an impact on
* the behavior of `super` in mixin methods.
*/
testLotsMixinLotsSuper() {
qx.Class.define("qx.G1", {
extend: qx.core.Object,
members: {
sayJuhu() {
return "G1";
}
}
});
qx.Mixin.define("qx.M1G1", {
members: {
sayJuhu() {
return `${super.sayJuhu()} M1G1`;
}
}
});
qx.Class.patch(qx.G1, qx.M1G1);
qx.Mixin.define("qx.M2G1", {
members: {
sayJuhu() {
return `${super.sayJuhu()} M2G1`;
}
}
});
qx.Class.patch(qx.G1, qx.M2G1);
qx.Mixin.define("qx.M3G1", {
members: {
sayJuhu() {
return `${super.sayJuhu()} M3G1`;
}
}
});
qx.Class.patch(qx.G1, qx.M3G1);
qx.Mixin.define("qx.M4G1", {
members: {
sayJuhu() {
return `${super.sayJuhu()} M4G1`;
}
}
});
qx.Class.patch(qx.G1, qx.M4G1);
qx.Mixin.define("qx.M5G1", {
members: {
// does not implement `sayJuhu`
}
});
qx.Class.patch(qx.G1, qx.M5G1);
qx.Mixin.define("qx.M6G1", {
members: {
// does not implement `sayJuhu`
}
});
qx.Class.patch(qx.G1, qx.M6G1);
qx.Class.define("qx.G2", {
extend: qx.G1,
members: {
sayJuhu() {
return `${super.sayJuhu()} G2`;
}
}
});
qx.Mixin.define("qx.M1G2", {
members: {
sayJuhu() {
return `${super.sayJuhu()} M1G2`;
}
}
});
qx.Class.patch(qx.G2, qx.M1G2);
qx.Mixin.define("qx.M2G2", {
members: {
sayJuhu() {
return `${super.sayJuhu()} M2G2`;
}
}
});
qx.Class.patch(qx.G2, qx.M2G2);
qx.Mixin.define("qx.M3G2", {
members: {
// does not implement `sayJuhu`
}
});
qx.Class.patch(qx.G2, qx.M3G2);
qx.Mixin.define("qx.M4G2", {
members: {
sayJuhu() {
return `${super.sayJuhu()} M4G2`;
}
}
});
qx.Class.patch(qx.G2, qx.M4G2);
qx.Mixin.define("qx.M5G2", {
members: {
sayJuhu() {
return `${super.sayJuhu()} M5G2`;
}
}
});
qx.Class.patch(qx.G2, qx.M5G2);
qx.Class.define("qx.G3", {
extend: qx.G2,
members: {
sayJuhu() {
return `${super.sayJuhu()} G3`;
}
}
});
qx.Mixin.define("qx.M1G3", {
members: {
// does not implement `sayJuhu`
}
});
qx.Class.patch(qx.G3, qx.M1G3);
qx.Mixin.define("qx.M2G3", {
members: {
sayJuhu() {
return `${super.sayJuhu()} M2G3`;
}
}
});
qx.Class.patch(qx.G3, qx.M2G3);
qx.Mixin.define("qx.M3G3", {
members: {
sayJuhu() {
return `${super.sayJuhu()} M3G3`;
}
}
});
qx.Class.patch(qx.G3, qx.M3G3);
qx.Mixin.define("qx.M4G3", {
members: {
// does not implement `sayJuhu`
}
});
qx.Class.patch(qx.G3, qx.M4G3);
qx.Class.define("qx.G4", {
extend: qx.G3,
members: {
sayJuhu() {
return `${super.sayJuhu()} G4`;
}
}
});
qx.Mixin.define("qx.M1G4", {
members: {
sayJuhu() {
return `${super.sayJuhu()} M1G4`;
}
}
});
qx.Class.patch(qx.G4, qx.M1G4);
qx.Mixin.define("qx.M2G4", {
members: {
// does not implement `sayJuhu`
}
});
qx.Class.patch(qx.G4, qx.M2G4);
qx.Mixin.define("qx.M3G4", {
members: {
sayJuhu() {
return `${super.sayJuhu()} M3G4`;
}
}
});
qx.Class.patch(qx.G4, qx.M3G4);
qx.Class.define("qx.G5", {
extend: qx.G4,
members: {
sayJuhu() {
return `${super.sayJuhu()} G5`;
}
}
});
qx.Mixin.define("qx.M1G5", {
members: {
sayJuhu() {
return `${super.sayJuhu()} M1G5`;
}
}
});
qx.Class.patch(qx.G5, qx.M1G5);
qx.Mixin.define("qx.M2G5", {
members: {
// does not implement `sayJuhu`
}
});
qx.Class.patch(qx.G5, qx.M2G5);
const g5 = new qx.G5();
this.assertEquals(
"G1 M1G1 M2G1 M3G1 M4G1 G2 M1G2 M2G2 M4G2 M5G2 G3 M2G3 M3G3 G4 M1G4 M3G4 G5 M1G5",
g5.sayJuhu()
);
g5.dispose();
}
}
});