|
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 |
|
5 "use strict"; |
|
6 |
|
7 var Trait = require("sdk/deprecated/light-traits").Trait; |
|
8 var utils = require("./utils"); |
|
9 var Data = utils.Data; |
|
10 var Method = utils.Method; |
|
11 var Accessor = utils.Accessor; |
|
12 var Required = utils.Required; |
|
13 var Conflict = utils.Conflict; |
|
14 |
|
15 function method() {} |
|
16 |
|
17 exports.Assert = require("./assert").Assert; |
|
18 |
|
19 exports["test empty trait"] = function (assert) { |
|
20 assert.equalTraits(Trait({}), {}); |
|
21 }; |
|
22 |
|
23 exports["test simple trait"] = function (assert) { |
|
24 var expected = { |
|
25 a: Data(0, true, true, true), |
|
26 b: Method(method, true, true, true) |
|
27 }; |
|
28 |
|
29 assert.equalTraits(Trait({ a: 0, b: method }), expected); |
|
30 }; |
|
31 |
|
32 exports["test simple trait with Trait.required property"] = function (assert) { |
|
33 var actual = Trait({ a: Trait.required, b: 1 }); |
|
34 var expected = { a: Required("a"), b: Data(1) }; |
|
35 |
|
36 assert.equalTraits(actual, expected); |
|
37 }; |
|
38 |
|
39 exports["test ordering of trait properties is irrelevant"] = function (assert) { |
|
40 var actual = Trait({ a: 0, b: 1, c: Trait.required }); |
|
41 var expected = Trait({ b: 1, c: Trait.required, a: 0 }); |
|
42 |
|
43 assert.equalTraits(actual, expected); |
|
44 }; |
|
45 |
|
46 exports["test trait with accessor property"] = function (assert) { |
|
47 var record = { get a() {}, set a(v) {} }; |
|
48 var get = Object.getOwnPropertyDescriptor(record, "a").get; |
|
49 var set = Object.getOwnPropertyDescriptor(record, "a").set; |
|
50 |
|
51 assert.equalTraits(Trait(record), { a: Accessor(get, set) }); |
|
52 }; |
|
53 |
|
54 exports["test simple composition"] = function (assert) { |
|
55 var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ c: 2, d: method })); |
|
56 var expected = { a: Data(0), b: Data(1), c: Data(2), d: Method(method) }; |
|
57 |
|
58 assert.equalTraits(actual, expected); |
|
59 }; |
|
60 |
|
61 exports["test composition with conflict"] = function (assert) { |
|
62 var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ a: 2, c: method })); |
|
63 var expected = { a: Conflict("a"), b: Data(1), c: Method(method) }; |
|
64 |
|
65 assert.equalTraits(actual, expected); |
|
66 }; |
|
67 |
|
68 exports["test composition of identical props does not cause conflict"] = function (assert) { |
|
69 var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ a: 0, c: method })); |
|
70 |
|
71 assert.equalTraits(actual, { a: Data(0), b: Data(1), c: Method(method) }); |
|
72 }; |
|
73 |
|
74 exports["test composition with identical Trait.required props"] = function (assert) { |
|
75 var actual = Trait.compose(Trait({ a: Trait.required, b: 1 }), |
|
76 Trait({ a: Trait.required, c: method })); |
|
77 |
|
78 assert.equalTraits(actual, { a: Required(), b: Data(1), c: Method(method) }); |
|
79 }; |
|
80 |
|
81 exports["test composition satisfying a Trait.required prop"] = function (assert) { |
|
82 var actual = Trait.compose(Trait({ a: Trait.required, b: 1 }), |
|
83 Trait({ a: method })); |
|
84 |
|
85 assert.equalTraits(actual, { a: Method(method), b: Data(1) }); |
|
86 }; |
|
87 |
|
88 exports["test compose is neutral wrt conflicts"] = function (assert) { |
|
89 var actual = Trait.compose(Trait.compose(Trait({ a: 1 }), Trait({ a: 2 })), |
|
90 Trait({ b: 0 })); |
|
91 |
|
92 assert.equalTraits(actual, { a: Conflict("a"), b: Data(0) }); |
|
93 }; |
|
94 |
|
95 exports["test conflicting prop overrides Trait.required prop"] = function (assert) { |
|
96 var actual = Trait.compose(Trait.compose(Trait({ a: 1 }), |
|
97 Trait({ a: 2 })), |
|
98 Trait({ a: Trait.required })); |
|
99 |
|
100 assert.equalTraits(actual, { a: Conflict("a") }); |
|
101 }; |
|
102 |
|
103 exports["test compose is commutative"] = function (assert) { |
|
104 var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ c: 2, d: method })); |
|
105 var expected = Trait.compose(Trait({ c: 2, d: method }), |
|
106 Trait({ a: 0, b: 1 })); |
|
107 |
|
108 assert.equalTraits(actual, expected); |
|
109 }; |
|
110 |
|
111 exports["test compose is commutative, also for Trait.required/conflicting props"] = function (assert) { |
|
112 var actual = Trait.compose(Trait({ a: 0, b: 1, c: 3, e: Trait.required }), |
|
113 Trait({ c: 2, d: method })); |
|
114 |
|
115 var expected = Trait.compose(Trait({ c: 2, d: method }), |
|
116 Trait({ a: 0, b: 1, c: 3, e: Trait.required })); |
|
117 |
|
118 assert.equalTraits(actual, expected); |
|
119 }; |
|
120 |
|
121 exports["test compose is associative"] = function (assert) { |
|
122 var actual = Trait.compose(Trait({ a: 0, b: 1, c: 3, d: Trait.required }), |
|
123 Trait.compose(Trait({ c: 3, d: Trait.required }), |
|
124 Trait({ c: 2, d: method, |
|
125 e: "foo" }))); |
|
126 |
|
127 var expected = Trait.compose( |
|
128 Trait.compose(Trait({ a: 0, b: 1, c: 3, d: Trait.required }), |
|
129 Trait({ c: 3, d: Trait.required })), |
|
130 Trait({ c: 2, d: method, e: "foo" })); |
|
131 |
|
132 assert.equalTraits(actual, expected); |
|
133 }; |
|
134 |
|
135 exports["test diamond import of same prop does not generate conflict"] = function (assert) { |
|
136 var actual = Trait.compose(Trait.compose(Trait({ b: 2 }), Trait({ a: 1 })), |
|
137 Trait.compose(Trait({ c: 3 }), Trait({ a: 1 })), |
|
138 Trait({ d: 4 })); |
|
139 var expected = { a: Data(1), b: Data(2), c: Data(3), d: Data(4) }; |
|
140 |
|
141 assert.equalTraits(actual, expected); |
|
142 }; |
|
143 |
|
144 exports["test resolve with empty resolutions has no effect"] = function (assert) { |
|
145 assert.equalTraits(Trait({ a: 1, b: Trait.required, c: method }).resolve({}), |
|
146 { a: Data(1), b: Required(), c: Method(method) }); |
|
147 }; |
|
148 |
|
149 exports["test resolve: renaming"] = function (assert) { |
|
150 var actual = Trait({ a: 1, b: Trait.required, c: method }); |
|
151 |
|
152 assert.equalTraits(actual.resolve({ a: "A", c: "C" }), |
|
153 { A: Data(1), b: Required(), C: Method(method), |
|
154 a: Required(), c: Required() }); |
|
155 }; |
|
156 |
|
157 exports["test resolve: renaming to conflicting name causes conflict, order 1"] = function (assert) { |
|
158 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "b" }), |
|
159 { b: Conflict("b"), a: Required() }); |
|
160 }; |
|
161 |
|
162 exports["test resolve: renaming to conflicting name causes conflict, order 2"] = function (assert) { |
|
163 assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ a: "b" }), |
|
164 { b: Conflict("b"), a: Required() }); |
|
165 }; |
|
166 |
|
167 exports["test resolve: simple exclusion"] = function (assert) { |
|
168 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined }), |
|
169 { a: Required(), b: Data(2) }); |
|
170 }; |
|
171 |
|
172 exports["test resolve: exclusion to empty trait"] = function (assert) { |
|
173 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: null, b: undefined }), |
|
174 { a: Required(), b: Required() }); |
|
175 }; |
|
176 |
|
177 exports["test resolve: exclusion and renaming of disjoint props"] = function (assert) { |
|
178 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined, b: "c" }), |
|
179 { a: Required(), c: Data(2), b: Required() }); |
|
180 }; |
|
181 |
|
182 exports["test resolve: exclusion and renaming of overlapping props"] = function (assert) { |
|
183 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined, b: "a" }), |
|
184 { a: Data(2), b: Required() }); |
|
185 }; |
|
186 |
|
187 exports["test resolve: renaming to a common alias causes conflict"] = function (assert) { |
|
188 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "c", b: "c" }), |
|
189 { c: Conflict("c"), a: Required(), b: Required() }); |
|
190 }; |
|
191 |
|
192 exports["test resolve: renaming overrides Trait.required target"] = function (assert) { |
|
193 assert.equalTraits(Trait({ a: Trait.required, b: 2 }).resolve({ b: "a" }), |
|
194 { a: Data(2), b: Required() }); |
|
195 }; |
|
196 |
|
197 exports["test resolve: renaming Trait.required properties has no effect"] = function (assert) { |
|
198 assert.equalTraits(Trait({ a: 2, b: Trait.required }).resolve({ b: "a" }), |
|
199 { a: Data(2), b: Required() }); |
|
200 }; |
|
201 |
|
202 exports["test resolve: renaming of non-existent props has no effect"] = function (assert) { |
|
203 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "c", d: "c" }), |
|
204 { c: Data(1), b: Data(2), a: Required() }); |
|
205 }; |
|
206 |
|
207 exports["test resolve: exclusion of non-existent props has no effect"] = function (assert) { |
|
208 assert.equalTraits(Trait({ a: 1 }).resolve({ b: undefined }), { a: Data(1) }); |
|
209 }; |
|
210 |
|
211 exports["test resolve is neutral w.r.t. Trait.required properties"] = function (assert) { |
|
212 var actual = Trait({ a: Trait.required, b: Trait.required, c: "foo", d: 1 }); |
|
213 var expected = { a: Required(), b: Required(), c: Data("foo"), d: Data(1) }; |
|
214 assert.equalTraits(actual.resolve({ a: "c", b: undefined }), expected); |
|
215 }; |
|
216 |
|
217 exports["test resolve supports swapping of property names, ordering 1"] = function (assert) { |
|
218 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "b", b: "a" }), |
|
219 { a: Data(2), b: Data(1) }); |
|
220 }; |
|
221 |
|
222 exports["test resolve supports swapping of property names, ordering 2"] = function (assert) { |
|
223 assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ b: "a", a: "b" }), |
|
224 { a: Data(2), b: Data(1) }); |
|
225 }; |
|
226 |
|
227 exports["test resolve supports swapping of property names, ordering 3"] = function (assert) { |
|
228 assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ b: "a", a: "b" }), |
|
229 { a: Data(2), b: Data(1) }); |
|
230 }; |
|
231 |
|
232 exports["test resolve supports swapping of property names, ordering 4"] = function (assert) { |
|
233 assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ a: "b", b: "a" }), |
|
234 { a: Data(2), b: Data(1) }); |
|
235 }; |
|
236 |
|
237 exports["test create simple"] = function (assert) { |
|
238 var o1 = Trait({ |
|
239 a: 1, |
|
240 b: function () { |
|
241 return this.a; |
|
242 } |
|
243 }).create(Object.prototype); |
|
244 |
|
245 assert.equal(Object.getPrototypeOf(o1), Object.prototype, "o1 prototype"); |
|
246 assert.equal(1, o1.a, "o1.a"); |
|
247 assert.equal(1, o1.b(), "o1.b()"); |
|
248 assert.equal(Object.keys(o1).length, 2, "Object.keys(o1).length === 2"); |
|
249 }; |
|
250 |
|
251 exports["test create with Array.prototype"] = function (assert) { |
|
252 var o2 = Trait({}).create(Array.prototype); |
|
253 assert.equal(Object.getPrototypeOf(o2), Array.prototype, "o2 prototype"); |
|
254 }; |
|
255 |
|
256 exports["test exception for incomplete required properties"] = function (assert) { |
|
257 assert.throws(function () { |
|
258 Trait({ foo: Trait.required }).create(Object.prototype); |
|
259 }, /Missing required property: `foo`/, "required prop error"); |
|
260 }; |
|
261 |
|
262 exports["test exception for unresolved conflicts"] = function (assert) { |
|
263 assert.throws(function () { |
|
264 Trait.compose(Trait({ a: 0 }), Trait({ a: 1 })).create({}); |
|
265 }, /Remaining conflicting property: `a`/, "conflicting prop error"); |
|
266 }; |
|
267 |
|
268 exports["test verify that required properties are present but undefined"] = function (assert) { |
|
269 var o4 = Object.create(Object.prototype, Trait({ foo: Trait.required })); |
|
270 |
|
271 assert.ok("foo" in o4, "required property present"); |
|
272 assert.throws(function () { |
|
273 o4.foo; |
|
274 }, /Missing required property: `foo`/, "required prop error"); |
|
275 }; |
|
276 |
|
277 exports["test verify that conflicting properties are present"] = function (assert) { |
|
278 var o5 = Object.create(Object.prototype, Trait.compose(Trait({ a: 0 }), |
|
279 Trait({ a: 1 }))); |
|
280 |
|
281 assert.ok("a" in o5, "conflicting property present"); |
|
282 assert.throws(function () { |
|
283 o5.a; |
|
284 }, /Remaining conflicting property: `a`/, "conflicting prop access error"); |
|
285 }; |
|
286 |
|
287 exports["test diamond with conflicts"] = function (assert) { |
|
288 function makeT1(x) { |
|
289 return Trait({ |
|
290 m: function () { |
|
291 return x |
|
292 } |
|
293 }) |
|
294 }; |
|
295 |
|
296 function makeT2(x) { |
|
297 return Trait.compose(Trait({ |
|
298 t2: "foo" |
|
299 }), makeT1(x)); |
|
300 }; |
|
301 |
|
302 function makeT3(x) { |
|
303 return Trait.compose(Trait({ |
|
304 t3: "bar" |
|
305 }), makeT1(x)); |
|
306 }; |
|
307 |
|
308 var T4 = Trait.compose(makeT2(5), makeT3(5)); |
|
309 |
|
310 assert.throws(function () { |
|
311 T4.create(Object.prototype); |
|
312 }, /Remaining conflicting property: `m`/, "diamond prop conflict"); |
|
313 }; |
|
314 |
|
315 exports["test providing requirements through proto"] = function (assert) { |
|
316 var t = Trait({ required: Trait.required }).create({ required: "test" }); |
|
317 assert.equal(t.required, "test", "property from proto is inherited"); |
|
318 }; |
|
319 |
|
320 if (module == require.main) |
|
321 require("test").run(exports); |