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