1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/test/traits/object-tests.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,321 @@ 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 + 1.22 +exports["test empty trait"] = function (assert) { 1.23 + assert.equalTraits(Trait({}), {}); 1.24 +}; 1.25 + 1.26 +exports["test simple trait"] = function (assert) { 1.27 + var expected = { 1.28 + a: Data(0, true, true, true), 1.29 + b: Method(method, true, true, true) 1.30 + }; 1.31 + 1.32 + assert.equalTraits(Trait({ a: 0, b: method }), expected); 1.33 +}; 1.34 + 1.35 +exports["test simple trait with Trait.required property"] = function (assert) { 1.36 + var actual = Trait({ a: Trait.required, b: 1 }); 1.37 + var expected = { a: Required("a"), b: Data(1) }; 1.38 + 1.39 + assert.equalTraits(actual, expected); 1.40 +}; 1.41 + 1.42 +exports["test ordering of trait properties is irrelevant"] = function (assert) { 1.43 + var actual = Trait({ a: 0, b: 1, c: Trait.required }); 1.44 + var expected = Trait({ b: 1, c: Trait.required, a: 0 }); 1.45 + 1.46 + assert.equalTraits(actual, expected); 1.47 +}; 1.48 + 1.49 +exports["test trait with accessor property"] = function (assert) { 1.50 + var record = { get a() {}, set a(v) {} }; 1.51 + var get = Object.getOwnPropertyDescriptor(record, "a").get; 1.52 + var set = Object.getOwnPropertyDescriptor(record, "a").set; 1.53 + 1.54 + assert.equalTraits(Trait(record), { a: Accessor(get, set) }); 1.55 +}; 1.56 + 1.57 +exports["test simple composition"] = function (assert) { 1.58 + var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ c: 2, d: method })); 1.59 + var expected = { a: Data(0), b: Data(1), c: Data(2), d: Method(method) }; 1.60 + 1.61 + assert.equalTraits(actual, expected); 1.62 +}; 1.63 + 1.64 +exports["test composition with conflict"] = function (assert) { 1.65 + var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ a: 2, c: method })); 1.66 + var expected = { a: Conflict("a"), b: Data(1), c: Method(method) }; 1.67 + 1.68 + assert.equalTraits(actual, expected); 1.69 +}; 1.70 + 1.71 +exports["test composition of identical props does not cause conflict"] = function (assert) { 1.72 + var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ a: 0, c: method })); 1.73 + 1.74 + assert.equalTraits(actual, { a: Data(0), b: Data(1), c: Method(method) }); 1.75 +}; 1.76 + 1.77 +exports["test composition with identical Trait.required props"] = function (assert) { 1.78 + var actual = Trait.compose(Trait({ a: Trait.required, b: 1 }), 1.79 + Trait({ a: Trait.required, c: method })); 1.80 + 1.81 + assert.equalTraits(actual, { a: Required(), b: Data(1), c: Method(method) }); 1.82 +}; 1.83 + 1.84 +exports["test composition satisfying a Trait.required prop"] = function (assert) { 1.85 + var actual = Trait.compose(Trait({ a: Trait.required, b: 1 }), 1.86 + Trait({ a: method })); 1.87 + 1.88 + assert.equalTraits(actual, { a: Method(method), b: Data(1) }); 1.89 +}; 1.90 + 1.91 +exports["test compose is neutral wrt conflicts"] = function (assert) { 1.92 + var actual = Trait.compose(Trait.compose(Trait({ a: 1 }), Trait({ a: 2 })), 1.93 + Trait({ b: 0 })); 1.94 + 1.95 + assert.equalTraits(actual, { a: Conflict("a"), b: Data(0) }); 1.96 +}; 1.97 + 1.98 +exports["test conflicting prop overrides Trait.required prop"] = function (assert) { 1.99 + var actual = Trait.compose(Trait.compose(Trait({ a: 1 }), 1.100 + Trait({ a: 2 })), 1.101 + Trait({ a: Trait.required })); 1.102 + 1.103 + assert.equalTraits(actual, { a: Conflict("a") }); 1.104 +}; 1.105 + 1.106 +exports["test compose is commutative"] = function (assert) { 1.107 + var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ c: 2, d: method })); 1.108 + var expected = Trait.compose(Trait({ c: 2, d: method }), 1.109 + Trait({ a: 0, b: 1 })); 1.110 + 1.111 + assert.equalTraits(actual, expected); 1.112 +}; 1.113 + 1.114 +exports["test compose is commutative, also for Trait.required/conflicting props"] = function (assert) { 1.115 + var actual = Trait.compose(Trait({ a: 0, b: 1, c: 3, e: Trait.required }), 1.116 + Trait({ c: 2, d: method })); 1.117 + 1.118 + var expected = Trait.compose(Trait({ c: 2, d: method }), 1.119 + Trait({ a: 0, b: 1, c: 3, e: Trait.required })); 1.120 + 1.121 + assert.equalTraits(actual, expected); 1.122 +}; 1.123 + 1.124 +exports["test compose is associative"] = function (assert) { 1.125 + var actual = Trait.compose(Trait({ a: 0, b: 1, c: 3, d: Trait.required }), 1.126 + Trait.compose(Trait({ c: 3, d: Trait.required }), 1.127 + Trait({ c: 2, d: method, 1.128 + e: "foo" }))); 1.129 + 1.130 + var expected = Trait.compose( 1.131 + Trait.compose(Trait({ a: 0, b: 1, c: 3, d: Trait.required }), 1.132 + Trait({ c: 3, d: Trait.required })), 1.133 + Trait({ c: 2, d: method, e: "foo" })); 1.134 + 1.135 + assert.equalTraits(actual, expected); 1.136 +}; 1.137 + 1.138 +exports["test diamond import of same prop does not generate conflict"] = function (assert) { 1.139 + var actual = Trait.compose(Trait.compose(Trait({ b: 2 }), Trait({ a: 1 })), 1.140 + Trait.compose(Trait({ c: 3 }), Trait({ a: 1 })), 1.141 + Trait({ d: 4 })); 1.142 + var expected = { a: Data(1), b: Data(2), c: Data(3), d: Data(4) }; 1.143 + 1.144 + assert.equalTraits(actual, expected); 1.145 +}; 1.146 + 1.147 +exports["test resolve with empty resolutions has no effect"] = function (assert) { 1.148 + assert.equalTraits(Trait({ a: 1, b: Trait.required, c: method }).resolve({}), 1.149 + { a: Data(1), b: Required(), c: Method(method) }); 1.150 +}; 1.151 + 1.152 +exports["test resolve: renaming"] = function (assert) { 1.153 + var actual = Trait({ a: 1, b: Trait.required, c: method }); 1.154 + 1.155 + assert.equalTraits(actual.resolve({ a: "A", c: "C" }), 1.156 + { A: Data(1), b: Required(), C: Method(method), 1.157 + a: Required(), c: Required() }); 1.158 +}; 1.159 + 1.160 +exports["test resolve: renaming to conflicting name causes conflict, order 1"] = function (assert) { 1.161 + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "b" }), 1.162 + { b: Conflict("b"), a: Required() }); 1.163 +}; 1.164 + 1.165 +exports["test resolve: renaming to conflicting name causes conflict, order 2"] = function (assert) { 1.166 + assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ a: "b" }), 1.167 + { b: Conflict("b"), a: Required() }); 1.168 +}; 1.169 + 1.170 +exports["test resolve: simple exclusion"] = function (assert) { 1.171 + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined }), 1.172 + { a: Required(), b: Data(2) }); 1.173 +}; 1.174 + 1.175 +exports["test resolve: exclusion to empty trait"] = function (assert) { 1.176 + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: null, b: undefined }), 1.177 + { a: Required(), b: Required() }); 1.178 +}; 1.179 + 1.180 +exports["test resolve: exclusion and renaming of disjoint props"] = function (assert) { 1.181 + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined, b: "c" }), 1.182 + { a: Required(), c: Data(2), b: Required() }); 1.183 +}; 1.184 + 1.185 +exports["test resolve: exclusion and renaming of overlapping props"] = function (assert) { 1.186 + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined, b: "a" }), 1.187 + { a: Data(2), b: Required() }); 1.188 +}; 1.189 + 1.190 +exports["test resolve: renaming to a common alias causes conflict"] = function (assert) { 1.191 + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "c", b: "c" }), 1.192 + { c: Conflict("c"), a: Required(), b: Required() }); 1.193 +}; 1.194 + 1.195 +exports["test resolve: renaming overrides Trait.required target"] = function (assert) { 1.196 + assert.equalTraits(Trait({ a: Trait.required, b: 2 }).resolve({ b: "a" }), 1.197 + { a: Data(2), b: Required() }); 1.198 +}; 1.199 + 1.200 +exports["test resolve: renaming Trait.required properties has no effect"] = function (assert) { 1.201 + assert.equalTraits(Trait({ a: 2, b: Trait.required }).resolve({ b: "a" }), 1.202 + { a: Data(2), b: Required() }); 1.203 +}; 1.204 + 1.205 +exports["test resolve: renaming of non-existent props has no effect"] = function (assert) { 1.206 + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "c", d: "c" }), 1.207 + { c: Data(1), b: Data(2), a: Required() }); 1.208 +}; 1.209 + 1.210 +exports["test resolve: exclusion of non-existent props has no effect"] = function (assert) { 1.211 + assert.equalTraits(Trait({ a: 1 }).resolve({ b: undefined }), { a: Data(1) }); 1.212 +}; 1.213 + 1.214 +exports["test resolve is neutral w.r.t. Trait.required properties"] = function (assert) { 1.215 + var actual = Trait({ a: Trait.required, b: Trait.required, c: "foo", d: 1 }); 1.216 + var expected = { a: Required(), b: Required(), c: Data("foo"), d: Data(1) }; 1.217 + assert.equalTraits(actual.resolve({ a: "c", b: undefined }), expected); 1.218 +}; 1.219 + 1.220 +exports["test resolve supports swapping of property names, ordering 1"] = function (assert) { 1.221 + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "b", b: "a" }), 1.222 + { a: Data(2), b: Data(1) }); 1.223 +}; 1.224 + 1.225 +exports["test resolve supports swapping of property names, ordering 2"] = function (assert) { 1.226 + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ b: "a", a: "b" }), 1.227 + { a: Data(2), b: Data(1) }); 1.228 +}; 1.229 + 1.230 +exports["test resolve supports swapping of property names, ordering 3"] = function (assert) { 1.231 + assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ b: "a", a: "b" }), 1.232 + { a: Data(2), b: Data(1) }); 1.233 +}; 1.234 + 1.235 +exports["test resolve supports swapping of property names, ordering 4"] = function (assert) { 1.236 + assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ a: "b", b: "a" }), 1.237 + { a: Data(2), b: Data(1) }); 1.238 +}; 1.239 + 1.240 +exports["test create simple"] = function (assert) { 1.241 + var o1 = Trait({ 1.242 + a: 1, 1.243 + b: function () { 1.244 + return this.a; 1.245 + } 1.246 + }).create(Object.prototype); 1.247 + 1.248 + assert.equal(Object.getPrototypeOf(o1), Object.prototype, "o1 prototype"); 1.249 + assert.equal(1, o1.a, "o1.a"); 1.250 + assert.equal(1, o1.b(), "o1.b()"); 1.251 + assert.equal(Object.keys(o1).length, 2, "Object.keys(o1).length === 2"); 1.252 +}; 1.253 + 1.254 +exports["test create with Array.prototype"] = function (assert) { 1.255 + var o2 = Trait({}).create(Array.prototype); 1.256 + assert.equal(Object.getPrototypeOf(o2), Array.prototype, "o2 prototype"); 1.257 +}; 1.258 + 1.259 +exports["test exception for incomplete required properties"] = function (assert) { 1.260 + assert.throws(function () { 1.261 + Trait({ foo: Trait.required }).create(Object.prototype); 1.262 + }, /Missing required property: `foo`/, "required prop error"); 1.263 +}; 1.264 + 1.265 +exports["test exception for unresolved conflicts"] = function (assert) { 1.266 + assert.throws(function () { 1.267 + Trait.compose(Trait({ a: 0 }), Trait({ a: 1 })).create({}); 1.268 + }, /Remaining conflicting property: `a`/, "conflicting prop error"); 1.269 +}; 1.270 + 1.271 +exports["test verify that required properties are present but undefined"] = function (assert) { 1.272 + var o4 = Object.create(Object.prototype, Trait({ foo: Trait.required })); 1.273 + 1.274 + assert.ok("foo" in o4, "required property present"); 1.275 + assert.throws(function () { 1.276 + o4.foo; 1.277 + }, /Missing required property: `foo`/, "required prop error"); 1.278 +}; 1.279 + 1.280 +exports["test verify that conflicting properties are present"] = function (assert) { 1.281 + var o5 = Object.create(Object.prototype, Trait.compose(Trait({ a: 0 }), 1.282 + Trait({ a: 1 }))); 1.283 + 1.284 + assert.ok("a" in o5, "conflicting property present"); 1.285 + assert.throws(function () { 1.286 + o5.a; 1.287 + }, /Remaining conflicting property: `a`/, "conflicting prop access error"); 1.288 +}; 1.289 + 1.290 +exports["test diamond with conflicts"] = function (assert) { 1.291 + function makeT1(x) { 1.292 + return Trait({ 1.293 + m: function () { 1.294 + return x 1.295 + } 1.296 + }) 1.297 + }; 1.298 + 1.299 + function makeT2(x) { 1.300 + return Trait.compose(Trait({ 1.301 + t2: "foo" 1.302 + }), makeT1(x)); 1.303 + }; 1.304 + 1.305 + function makeT3(x) { 1.306 + return Trait.compose(Trait({ 1.307 + t3: "bar" 1.308 + }), makeT1(x)); 1.309 + }; 1.310 + 1.311 + var T4 = Trait.compose(makeT2(5), makeT3(5)); 1.312 + 1.313 + assert.throws(function () { 1.314 + T4.create(Object.prototype); 1.315 + }, /Remaining conflicting property: `m`/, "diamond prop conflict"); 1.316 +}; 1.317 + 1.318 +exports["test providing requirements through proto"] = function (assert) { 1.319 + var t = Trait({ required: Trait.required }).create({ required: "test" }); 1.320 + assert.equal(t.required, "test", "property from proto is inherited"); 1.321 +}; 1.322 + 1.323 +if (module == require.main) 1.324 + require("test").run(exports);