object-tests.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. var Trait = require("sdk/deprecated/light-traits").Trait;
  6. var utils = require("./utils");
  7. var Data = utils.Data;
  8. var Method = utils.Method;
  9. var Accessor = utils.Accessor;
  10. var Required = utils.Required;
  11. var Conflict = utils.Conflict;
  12. function method() {}
  13. exports.Assert = require("./assert").Assert;
  14. exports["test empty trait"] = function (assert) {
  15. assert.equalTraits(Trait({}), {});
  16. };
  17. exports["test simple trait"] = function (assert) {
  18. var expected = {
  19. a: Data(0, true, true, true),
  20. b: Method(method, true, true, true)
  21. };
  22. assert.equalTraits(Trait({ a: 0, b: method }), expected);
  23. };
  24. exports["test simple trait with Trait.required property"] = function (assert) {
  25. var actual = Trait({ a: Trait.required, b: 1 });
  26. var expected = { a: Required("a"), b: Data(1) };
  27. assert.equalTraits(actual, expected);
  28. };
  29. exports["test ordering of trait properties is irrelevant"] = function (assert) {
  30. var actual = Trait({ a: 0, b: 1, c: Trait.required });
  31. var expected = Trait({ b: 1, c: Trait.required, a: 0 });
  32. assert.equalTraits(actual, expected);
  33. };
  34. exports["test trait with accessor property"] = function (assert) {
  35. var record = { get a() {}, set a(v) {} };
  36. var get = Object.getOwnPropertyDescriptor(record, "a").get;
  37. var set = Object.getOwnPropertyDescriptor(record, "a").set;
  38. assert.equalTraits(Trait(record), { a: Accessor(get, set) });
  39. };
  40. exports["test simple composition"] = function (assert) {
  41. var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ c: 2, d: method }));
  42. var expected = { a: Data(0), b: Data(1), c: Data(2), d: Method(method) };
  43. assert.equalTraits(actual, expected);
  44. };
  45. exports["test composition with conflict"] = function (assert) {
  46. var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ a: 2, c: method }));
  47. var expected = { a: Conflict("a"), b: Data(1), c: Method(method) };
  48. assert.equalTraits(actual, expected);
  49. };
  50. exports["test composition of identical props does not cause conflict"] = function (assert) {
  51. var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ a: 0, c: method }));
  52. assert.equalTraits(actual, { a: Data(0), b: Data(1), c: Method(method) });
  53. };
  54. exports["test composition with identical Trait.required props"] = function (assert) {
  55. var actual = Trait.compose(Trait({ a: Trait.required, b: 1 }),
  56. Trait({ a: Trait.required, c: method }));
  57. assert.equalTraits(actual, { a: Required(), b: Data(1), c: Method(method) });
  58. };
  59. exports["test composition satisfying a Trait.required prop"] = function (assert) {
  60. var actual = Trait.compose(Trait({ a: Trait.required, b: 1 }),
  61. Trait({ a: method }));
  62. assert.equalTraits(actual, { a: Method(method), b: Data(1) });
  63. };
  64. exports["test compose is neutral wrt conflicts"] = function (assert) {
  65. var actual = Trait.compose(Trait.compose(Trait({ a: 1 }), Trait({ a: 2 })),
  66. Trait({ b: 0 }));
  67. assert.equalTraits(actual, { a: Conflict("a"), b: Data(0) });
  68. };
  69. exports["test conflicting prop overrides Trait.required prop"] = function (assert) {
  70. var actual = Trait.compose(Trait.compose(Trait({ a: 1 }),
  71. Trait({ a: 2 })),
  72. Trait({ a: Trait.required }));
  73. assert.equalTraits(actual, { a: Conflict("a") });
  74. };
  75. exports["test compose is commutative"] = function (assert) {
  76. var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ c: 2, d: method }));
  77. var expected = Trait.compose(Trait({ c: 2, d: method }),
  78. Trait({ a: 0, b: 1 }));
  79. assert.equalTraits(actual, expected);
  80. };
  81. exports["test compose is commutative, also for Trait.required/conflicting props"] = function (assert) {
  82. var actual = Trait.compose(Trait({ a: 0, b: 1, c: 3, e: Trait.required }),
  83. Trait({ c: 2, d: method }));
  84. var expected = Trait.compose(Trait({ c: 2, d: method }),
  85. Trait({ a: 0, b: 1, c: 3, e: Trait.required }));
  86. assert.equalTraits(actual, expected);
  87. };
  88. exports["test compose is associative"] = function (assert) {
  89. var actual = Trait.compose(Trait({ a: 0, b: 1, c: 3, d: Trait.required }),
  90. Trait.compose(Trait({ c: 3, d: Trait.required }),
  91. Trait({ c: 2, d: method,
  92. e: "foo" })));
  93. var expected = Trait.compose(
  94. Trait.compose(Trait({ a: 0, b: 1, c: 3, d: Trait.required }),
  95. Trait({ c: 3, d: Trait.required })),
  96. Trait({ c: 2, d: method, e: "foo" }));
  97. assert.equalTraits(actual, expected);
  98. };
  99. exports["test diamond import of same prop does not generate conflict"] = function (assert) {
  100. var actual = Trait.compose(Trait.compose(Trait({ b: 2 }), Trait({ a: 1 })),
  101. Trait.compose(Trait({ c: 3 }), Trait({ a: 1 })),
  102. Trait({ d: 4 }));
  103. var expected = { a: Data(1), b: Data(2), c: Data(3), d: Data(4) };
  104. assert.equalTraits(actual, expected);
  105. };
  106. exports["test resolve with empty resolutions has no effect"] = function (assert) {
  107. assert.equalTraits(Trait({ a: 1, b: Trait.required, c: method }).resolve({}),
  108. { a: Data(1), b: Required(), c: Method(method) });
  109. };
  110. exports["test resolve: renaming"] = function (assert) {
  111. var actual = Trait({ a: 1, b: Trait.required, c: method });
  112. assert.equalTraits(actual.resolve({ a: "A", c: "C" }),
  113. { A: Data(1), b: Required(), C: Method(method),
  114. a: Required(), c: Required() });
  115. };
  116. exports["test resolve: renaming to conflicting name causes conflict, order 1"] = function (assert) {
  117. assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "b" }),
  118. { b: Conflict("b"), a: Required() });
  119. };
  120. exports["test resolve: renaming to conflicting name causes conflict, order 2"] = function (assert) {
  121. assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ a: "b" }),
  122. { b: Conflict("b"), a: Required() });
  123. };
  124. exports["test resolve: simple exclusion"] = function (assert) {
  125. assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined }),
  126. { a: Required(), b: Data(2) });
  127. };
  128. exports["test resolve: exclusion to empty trait"] = function (assert) {
  129. assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: null, b: undefined }),
  130. { a: Required(), b: Required() });
  131. };
  132. exports["test resolve: exclusion and renaming of disjoint props"] = function (assert) {
  133. assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined, b: "c" }),
  134. { a: Required(), c: Data(2), b: Required() });
  135. };
  136. exports["test resolve: exclusion and renaming of overlapping props"] = function (assert) {
  137. assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined, b: "a" }),
  138. { a: Data(2), b: Required() });
  139. };
  140. exports["test resolve: renaming to a common alias causes conflict"] = function (assert) {
  141. assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "c", b: "c" }),
  142. { c: Conflict("c"), a: Required(), b: Required() });
  143. };
  144. exports["test resolve: renaming overrides Trait.required target"] = function (assert) {
  145. assert.equalTraits(Trait({ a: Trait.required, b: 2 }).resolve({ b: "a" }),
  146. { a: Data(2), b: Required() });
  147. };
  148. exports["test resolve: renaming Trait.required properties has no effect"] = function (assert) {
  149. assert.equalTraits(Trait({ a: 2, b: Trait.required }).resolve({ b: "a" }),
  150. { a: Data(2), b: Required() });
  151. };
  152. exports["test resolve: renaming of non-existent props has no effect"] = function (assert) {
  153. assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "c", d: "c" }),
  154. { c: Data(1), b: Data(2), a: Required() });
  155. };
  156. exports["test resolve: exclusion of non-existent props has no effect"] = function (assert) {
  157. assert.equalTraits(Trait({ a: 1 }).resolve({ b: undefined }), { a: Data(1) });
  158. };
  159. exports["test resolve is neutral w.r.t. Trait.required properties"] = function (assert) {
  160. var actual = Trait({ a: Trait.required, b: Trait.required, c: "foo", d: 1 });
  161. var expected = { a: Required(), b: Required(), c: Data("foo"), d: Data(1) };
  162. assert.equalTraits(actual.resolve({ a: "c", b: undefined }), expected);
  163. };
  164. exports["test resolve supports swapping of property names, ordering 1"] = function (assert) {
  165. assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "b", b: "a" }),
  166. { a: Data(2), b: Data(1) });
  167. };
  168. exports["test resolve supports swapping of property names, ordering 2"] = function (assert) {
  169. assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ b: "a", a: "b" }),
  170. { a: Data(2), b: Data(1) });
  171. };
  172. exports["test resolve supports swapping of property names, ordering 3"] = function (assert) {
  173. assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ b: "a", a: "b" }),
  174. { a: Data(2), b: Data(1) });
  175. };
  176. exports["test resolve supports swapping of property names, ordering 4"] = function (assert) {
  177. assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ a: "b", b: "a" }),
  178. { a: Data(2), b: Data(1) });
  179. };
  180. exports["test create simple"] = function (assert) {
  181. var o1 = Trait({
  182. a: 1,
  183. b: function () {
  184. return this.a;
  185. }
  186. }).create(Object.prototype);
  187. assert.equal(Object.getPrototypeOf(o1), Object.prototype, "o1 prototype");
  188. assert.equal(1, o1.a, "o1.a");
  189. assert.equal(1, o1.b(), "o1.b()");
  190. assert.equal(Object.keys(o1).length, 2, "Object.keys(o1).length === 2");
  191. };
  192. exports["test create with Array.prototype"] = function (assert) {
  193. var o2 = Trait({}).create(Array.prototype);
  194. assert.equal(Object.getPrototypeOf(o2), Array.prototype, "o2 prototype");
  195. };
  196. exports["test exception for incomplete required properties"] = function (assert) {
  197. assert.throws(function () {
  198. Trait({ foo: Trait.required }).create(Object.prototype);
  199. }, /Missing required property: `foo`/, "required prop error");
  200. };
  201. exports["test exception for unresolved conflicts"] = function (assert) {
  202. assert.throws(function () {
  203. Trait.compose(Trait({ a: 0 }), Trait({ a: 1 })).create({});
  204. }, /Remaining conflicting property: `a`/, "conflicting prop error");
  205. };
  206. exports["test verify that required properties are present but undefined"] = function (assert) {
  207. var o4 = Object.create(Object.prototype, Trait({ foo: Trait.required }));
  208. assert.ok("foo" in o4, "required property present");
  209. assert.throws(function () {
  210. o4.foo;
  211. }, /Missing required property: `foo`/, "required prop error");
  212. };
  213. exports["test verify that conflicting properties are present"] = function (assert) {
  214. var o5 = Object.create(Object.prototype, Trait.compose(Trait({ a: 0 }),
  215. Trait({ a: 1 })));
  216. assert.ok("a" in o5, "conflicting property present");
  217. assert.throws(function () {
  218. o5.a;
  219. }, /Remaining conflicting property: `a`/, "conflicting prop access error");
  220. };
  221. exports["test diamond with conflicts"] = function (assert) {
  222. function makeT1(x) {
  223. return Trait({
  224. m: function () {
  225. return x
  226. }
  227. })
  228. };
  229. function makeT2(x) {
  230. return Trait.compose(Trait({
  231. t2: "foo"
  232. }), makeT1(x));
  233. };
  234. function makeT3(x) {
  235. return Trait.compose(Trait({
  236. t3: "bar"
  237. }), makeT1(x));
  238. };
  239. var T4 = Trait.compose(makeT2(5), makeT3(5));
  240. assert.throws(function () {
  241. T4.create(Object.prototype);
  242. }, /Remaining conflicting property: `m`/, "diamond prop conflict");
  243. };
  244. exports["test providing requirements through proto"] = function (assert) {
  245. var t = Trait({ required: Trait.required }).create({ required: "test" });
  246. assert.equal(t.required, "test", "property from proto is inherited");
  247. };
  248. if (module == require.main)
  249. require("test").run(exports);