1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/test/traits/descriptor-tests.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,335 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +var Trait = require("sdk/deprecated/light-traits").Trait; 1.11 +var utils = require("./utils"); 1.12 +var Data = utils.Data; 1.13 +var Method = utils.Method; 1.14 +var Accessor = utils.Accessor; 1.15 +var Required = utils.Required; 1.16 +var Conflict = utils.Conflict; 1.17 + 1.18 +function method() {} 1.19 + 1.20 +exports.Assert = require("./assert").Assert 1.21 +exports["test simple composition"] = function(assert) { 1.22 + var actual = Trait.compose( 1.23 + Trait({ a: 0, b: 1 }), 1.24 + { c: { value: 2 }, d: { value: method, enumerable: true } } 1.25 + ); 1.26 + 1.27 + var expected = { 1.28 + a: Data(0), 1.29 + b: Data(1), 1.30 + c: Data(2, false, false, false), 1.31 + d: Method(method, true, false, false) 1.32 + }; 1.33 + 1.34 + assert.equalTraits(actual, expected); 1.35 +}; 1.36 + 1.37 +exports["test composition with conflict"] = function(assert) { 1.38 + var actual = Trait.compose( 1.39 + Trait({ a: 0, b: 1 }), 1.40 + { 1.41 + a: { 1.42 + value: 2, 1.43 + writable: true, 1.44 + configurable: true, 1.45 + enumerable: true 1.46 + }, 1.47 + c: { 1.48 + value: method, 1.49 + configurable: true 1.50 + } 1.51 + } 1.52 + ); 1.53 + 1.54 + var expected = { 1.55 + a: Conflict("a"), 1.56 + b: Data(1), 1.57 + c: Method(method, false, true, false) 1.58 + }; 1.59 + 1.60 + assert.equalTraits(actual, expected); 1.61 +}; 1.62 + 1.63 +exports["test identical props does not cause conflict"] = function(assert) { 1.64 + var actual = Trait.compose( 1.65 + { 1.66 + a: { 1.67 + value: 0, 1.68 + writable: true, 1.69 + configurable: true, 1.70 + enumerable: true 1.71 + }, 1.72 + b: { 1.73 + value: 1 1.74 + } 1.75 + }, 1.76 + Trait({ 1.77 + a: 0, 1.78 + c: method 1.79 + }) 1.80 + ); 1.81 + 1.82 + var expected = { 1.83 + a: Data(0), 1.84 + b: Data(1, false, false, false), 1.85 + c: Method(method) 1.86 + } 1.87 + 1.88 + assert.equalTraits(actual, expected); 1.89 +}; 1.90 + 1.91 +exports["test composition with identical required props"] = function(assert) { 1.92 + var actual = Trait.compose( 1.93 + Trait({ a: Trait.required, b: 1 }), 1.94 + { a: { required: true }, c: { value: method } } 1.95 + ); 1.96 + 1.97 + var expected = { 1.98 + a: Required(), 1.99 + b: Data(1), 1.100 + c: Method(method, false, false, false) 1.101 + }; 1.102 + 1.103 + assert.equalTraits(actual, expected); 1.104 +}; 1.105 + 1.106 +exports["test composition satisfying a required prop"] = function(assert) { 1.107 + var actual = Trait.compose( 1.108 + Trait({ a: Trait.required, b: 1 }), 1.109 + { a: { value: method, enumerable: true } } 1.110 + ); 1.111 + 1.112 + var expected = { 1.113 + a: Method(method, true, false, false), 1.114 + b: Data(1) 1.115 + }; 1.116 + 1.117 + assert.equalTraits(actual, expected); 1.118 +}; 1.119 + 1.120 +exports["test compose is neutral wrt conflicts"] = function(assert) { 1.121 + var actual = Trait.compose( 1.122 + Trait({ a: { value: 1 } }, Trait({ a: 2 })), 1.123 + { b: { value: 0, writable: true, configurable: true, enumerable: false } } 1.124 + ); 1.125 + 1.126 + var expected = { a: Conflict("a"), b: Data(0, false) }; 1.127 + 1.128 + assert.equalTraits(actual, expected); 1.129 +}; 1.130 + 1.131 +exports["test conflicting prop overrides Trait.required"] = function(assert) { 1.132 + var actual = Trait.compose( 1.133 + Trait.compose( 1.134 + Trait({ a: 1 }), 1.135 + { a: { value: 2 } } 1.136 + ), 1.137 + { a: { value: Trait.required } } 1.138 + ); 1.139 + 1.140 + var expected = { a: Conflict("a") }; 1.141 + 1.142 + assert.equalTraits(actual, expected); 1.143 +}; 1.144 + 1.145 +exports["test compose is commutative"] = function(assert) { 1.146 + var actual = Trait.compose( 1.147 + Trait({ a: 0, b: 1 }), 1.148 + { c: { value: 2 }, d: { value: method } } 1.149 + ); 1.150 + 1.151 + var expected = Trait.compose( 1.152 + { c: { value: 2 }, d: { value: method } }, 1.153 + Trait({ a: 0, b: 1 }) 1.154 + ); 1.155 + 1.156 + assert.equalTraits(actual, expected); 1.157 +} 1.158 + 1.159 +exports["test compose is commutative, also for required/conflicting props"] = function(assert) { 1.160 + var actual = Trait.compose( 1.161 + { 1.162 + a: { value: 0 }, 1.163 + b: { value: 1 }, 1.164 + c: { value: 3 }, 1.165 + e: { value: Trait.required } 1.166 + }, 1.167 + { 1.168 + c: { value: 2 }, 1.169 + d: { get: method } 1.170 + } 1.171 + ); 1.172 + 1.173 + var expected = Trait.compose( 1.174 + Trait({ c: 3 }), 1.175 + { 1.176 + c: { value: 2 }, 1.177 + d: { get: method }, 1.178 + a: { value: 0 }, 1.179 + b: { value: 1 }, 1.180 + e: { value: Trait.required }, 1.181 + } 1.182 + ); 1.183 + 1.184 + assert.equalTraits(actual, expected); 1.185 +}; 1.186 + 1.187 +exports["test compose is associative"] = function(assert) { 1.188 + var actual = Trait.compose( 1.189 + { 1.190 + a: { value: 0 }, 1.191 + b: { value: 1 }, 1.192 + c: { value: 3 }, 1.193 + d: { value: Trait.required } 1.194 + }, 1.195 + Trait.compose( 1.196 + { c: { value: 3 }, d: { value: Trait.required } }, 1.197 + { c: { value: 2 }, d: { value: method }, e: { value: "foo" } } 1.198 + ) 1.199 + ); 1.200 + 1.201 + var expected = Trait.compose( 1.202 + Trait.compose( 1.203 + { 1.204 + a: { value: 0 }, 1.205 + b: { value: 1 }, 1.206 + c: { value: 3 }, 1.207 + d: { value: Trait.required } 1.208 + }, 1.209 + { 1.210 + c: { value: 3 }, 1.211 + d: { value: Trait.required } 1.212 + } 1.213 + ), 1.214 + { 1.215 + c: { value: 2 }, 1.216 + d: { value: method }, 1.217 + e: { value: "foo" } 1.218 + } 1.219 + ); 1.220 + 1.221 + assert.equalTraits(actual, expected); 1.222 +}; 1.223 + 1.224 +exports["test diamond import of same prop do not conflict"] = function(assert) { 1.225 + var actual = Trait.compose( 1.226 + Trait.compose( 1.227 + { b: { value: 2 } }, 1.228 + { a: { value: 1, enumerable: true, configurable: true, writable: true } } 1.229 + ), 1.230 + Trait.compose( 1.231 + { c: { value: 3 } }, 1.232 + Trait({ a: 1 }) 1.233 + ), 1.234 + Trait({ d: 4 }) 1.235 + ); 1.236 + 1.237 + var expected = { 1.238 + a: Data(1), 1.239 + b: Data(2, false, false, false), 1.240 + c: Data(3, false, false, false), 1.241 + d: Data(4) 1.242 + }; 1.243 + 1.244 + assert.equalTraits(actual, expected); 1.245 +}; 1.246 + 1.247 +exports["test create simple"] = function(assert) { 1.248 + var o1 = Trait.compose( 1.249 + Trait({ a: 1 }), 1.250 + { 1.251 + b: { 1.252 + value: function() { 1.253 + return this.a; 1.254 + } 1.255 + } 1.256 + } 1.257 + ).create(Object.prototype); 1.258 + 1.259 + assert.equal(Object.getPrototypeOf(o1), Object.prototype, "o1 prototype"); 1.260 + assert.equal(1, o1.a, "o1.a"); 1.261 + assert.equal(1, o1.b(), "o1.b()"); 1.262 + assert.equal(Object.keys(o1).length, 1, "Object.keys(o1).length === 2"); 1.263 +}; 1.264 + 1.265 +exports["test create with Array.prototype"] = function(assert) { 1.266 + var o2 = Trait.compose({}, {}).create(Array.prototype); 1.267 + assert.equal(Object.getPrototypeOf(o2), Array.prototype, "o2 prototype"); 1.268 +}; 1.269 + 1.270 +exports["test exception for incomplete required properties"] = function(assert) { 1.271 + assert.throws(function() { 1.272 + Trait({ foo: Trait.required }).create(Object.prototype) 1.273 + }, /Missing required property: `foo`/, "required prop error"); 1.274 +} 1.275 + 1.276 +exports["test exception for unresolved conflicts"] = function(assert) { 1.277 + assert.throws(function() { 1.278 + Trait(Trait({ a: 0 }), Trait({ a: 1 })).create({}) 1.279 + }, /Remaining conflicting property: `a`/, "conflicting prop error"); 1.280 +} 1.281 + 1.282 +exports["test conflicting properties are present"] = function(assert) { 1.283 + var o5 = Object.create(Object.prototype, Trait.compose( 1.284 + { a: { value: 0 } }, 1.285 + { a: { value: 1 } } 1.286 + )); 1.287 + 1.288 + assert.ok("a" in o5, "conflicting property present"); 1.289 + assert.throws(function() { 1.290 + o5.a 1.291 + }, /Remaining conflicting property: `a`/, "conflicting prop access error"); 1.292 +}; 1.293 + 1.294 +exports["test diamond with conflicts"] = function(assert) { 1.295 + function makeT1(x) { 1.296 + return { 1.297 + m: { 1.298 + value: function() { 1.299 + return x 1.300 + } 1.301 + } 1.302 + }; 1.303 + }; 1.304 + 1.305 + function makeT2(x) { 1.306 + return Trait.compose( 1.307 + Trait({ t2: "foo" }), 1.308 + makeT1(x) 1.309 + ); 1.310 + }; 1.311 + 1.312 + function makeT3(x) { 1.313 + return Trait.compose( 1.314 + { 1.315 + t3: { value: "bar" } 1.316 + }, 1.317 + makeT1(x) 1.318 + ); 1.319 + }; 1.320 + 1.321 + var T4 = Trait.compose(makeT2(5), makeT3(5)); 1.322 + 1.323 + assert.throws(function() { 1.324 + T4.create(Object.prototype); 1.325 + }, /Remaining conflicting property: `m`/, "diamond prop conflict"); 1.326 +}; 1.327 + 1.328 +exports["test providing requirements through proto"] = function(assert) { 1.329 + var t = Trait.compose( 1.330 + {}, 1.331 + { required: { required: true } } 1.332 + ).create({ required: "test" }); 1.333 + 1.334 + assert.equal(t.required, "test", "property from proto is inherited"); 1.335 +}; 1.336 + 1.337 +if (module == require.main) 1.338 + require("test").run(exports);