michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: var Trait = require("sdk/deprecated/light-traits").Trait; michael@0: var utils = require("./utils"); michael@0: var Data = utils.Data; michael@0: var Method = utils.Method; michael@0: var Accessor = utils.Accessor; michael@0: var Required = utils.Required; michael@0: var Conflict = utils.Conflict; michael@0: michael@0: function method() {} michael@0: michael@0: exports.Assert = require("./assert").Assert michael@0: exports["test simple composition"] = function(assert) { michael@0: var actual = Trait.compose( michael@0: Trait({ a: 0, b: 1 }), michael@0: { c: { value: 2 }, d: { value: method, enumerable: true } } michael@0: ); michael@0: michael@0: var expected = { michael@0: a: Data(0), michael@0: b: Data(1), michael@0: c: Data(2, false, false, false), michael@0: d: Method(method, true, false, false) michael@0: }; michael@0: michael@0: assert.equalTraits(actual, expected); michael@0: }; michael@0: michael@0: exports["test composition with conflict"] = function(assert) { michael@0: var actual = Trait.compose( michael@0: Trait({ a: 0, b: 1 }), michael@0: { michael@0: a: { michael@0: value: 2, michael@0: writable: true, michael@0: configurable: true, michael@0: enumerable: true michael@0: }, michael@0: c: { michael@0: value: method, michael@0: configurable: true michael@0: } michael@0: } michael@0: ); michael@0: michael@0: var expected = { michael@0: a: Conflict("a"), michael@0: b: Data(1), michael@0: c: Method(method, false, true, false) michael@0: }; michael@0: michael@0: assert.equalTraits(actual, expected); michael@0: }; michael@0: michael@0: exports["test identical props does not cause conflict"] = function(assert) { michael@0: var actual = Trait.compose( michael@0: { michael@0: a: { michael@0: value: 0, michael@0: writable: true, michael@0: configurable: true, michael@0: enumerable: true michael@0: }, michael@0: b: { michael@0: value: 1 michael@0: } michael@0: }, michael@0: Trait({ michael@0: a: 0, michael@0: c: method michael@0: }) michael@0: ); michael@0: michael@0: var expected = { michael@0: a: Data(0), michael@0: b: Data(1, false, false, false), michael@0: c: Method(method) michael@0: } michael@0: michael@0: assert.equalTraits(actual, expected); michael@0: }; michael@0: michael@0: exports["test composition with identical required props"] = function(assert) { michael@0: var actual = Trait.compose( michael@0: Trait({ a: Trait.required, b: 1 }), michael@0: { a: { required: true }, c: { value: method } } michael@0: ); michael@0: michael@0: var expected = { michael@0: a: Required(), michael@0: b: Data(1), michael@0: c: Method(method, false, false, false) michael@0: }; michael@0: michael@0: assert.equalTraits(actual, expected); michael@0: }; michael@0: michael@0: exports["test composition satisfying a required prop"] = function(assert) { michael@0: var actual = Trait.compose( michael@0: Trait({ a: Trait.required, b: 1 }), michael@0: { a: { value: method, enumerable: true } } michael@0: ); michael@0: michael@0: var expected = { michael@0: a: Method(method, true, false, false), michael@0: b: Data(1) michael@0: }; michael@0: michael@0: assert.equalTraits(actual, expected); michael@0: }; michael@0: michael@0: exports["test compose is neutral wrt conflicts"] = function(assert) { michael@0: var actual = Trait.compose( michael@0: Trait({ a: { value: 1 } }, Trait({ a: 2 })), michael@0: { b: { value: 0, writable: true, configurable: true, enumerable: false } } michael@0: ); michael@0: michael@0: var expected = { a: Conflict("a"), b: Data(0, false) }; michael@0: michael@0: assert.equalTraits(actual, expected); michael@0: }; michael@0: michael@0: exports["test conflicting prop overrides Trait.required"] = function(assert) { michael@0: var actual = Trait.compose( michael@0: Trait.compose( michael@0: Trait({ a: 1 }), michael@0: { a: { value: 2 } } michael@0: ), michael@0: { a: { value: Trait.required } } michael@0: ); michael@0: michael@0: var expected = { a: Conflict("a") }; michael@0: michael@0: assert.equalTraits(actual, expected); michael@0: }; michael@0: michael@0: exports["test compose is commutative"] = function(assert) { michael@0: var actual = Trait.compose( michael@0: Trait({ a: 0, b: 1 }), michael@0: { c: { value: 2 }, d: { value: method } } michael@0: ); michael@0: michael@0: var expected = Trait.compose( michael@0: { c: { value: 2 }, d: { value: method } }, michael@0: Trait({ a: 0, b: 1 }) michael@0: ); michael@0: michael@0: assert.equalTraits(actual, expected); michael@0: } michael@0: michael@0: exports["test compose is commutative, also for required/conflicting props"] = function(assert) { michael@0: var actual = Trait.compose( michael@0: { michael@0: a: { value: 0 }, michael@0: b: { value: 1 }, michael@0: c: { value: 3 }, michael@0: e: { value: Trait.required } michael@0: }, michael@0: { michael@0: c: { value: 2 }, michael@0: d: { get: method } michael@0: } michael@0: ); michael@0: michael@0: var expected = Trait.compose( michael@0: Trait({ c: 3 }), michael@0: { michael@0: c: { value: 2 }, michael@0: d: { get: method }, michael@0: a: { value: 0 }, michael@0: b: { value: 1 }, michael@0: e: { value: Trait.required }, michael@0: } michael@0: ); michael@0: michael@0: assert.equalTraits(actual, expected); michael@0: }; michael@0: michael@0: exports["test compose is associative"] = function(assert) { michael@0: var actual = Trait.compose( michael@0: { michael@0: a: { value: 0 }, michael@0: b: { value: 1 }, michael@0: c: { value: 3 }, michael@0: d: { value: Trait.required } michael@0: }, michael@0: Trait.compose( michael@0: { c: { value: 3 }, d: { value: Trait.required } }, michael@0: { c: { value: 2 }, d: { value: method }, e: { value: "foo" } } michael@0: ) michael@0: ); michael@0: michael@0: var expected = Trait.compose( michael@0: Trait.compose( michael@0: { michael@0: a: { value: 0 }, michael@0: b: { value: 1 }, michael@0: c: { value: 3 }, michael@0: d: { value: Trait.required } michael@0: }, michael@0: { michael@0: c: { value: 3 }, michael@0: d: { value: Trait.required } michael@0: } michael@0: ), michael@0: { michael@0: c: { value: 2 }, michael@0: d: { value: method }, michael@0: e: { value: "foo" } michael@0: } michael@0: ); michael@0: michael@0: assert.equalTraits(actual, expected); michael@0: }; michael@0: michael@0: exports["test diamond import of same prop do not conflict"] = function(assert) { michael@0: var actual = Trait.compose( michael@0: Trait.compose( michael@0: { b: { value: 2 } }, michael@0: { a: { value: 1, enumerable: true, configurable: true, writable: true } } michael@0: ), michael@0: Trait.compose( michael@0: { c: { value: 3 } }, michael@0: Trait({ a: 1 }) michael@0: ), michael@0: Trait({ d: 4 }) michael@0: ); michael@0: michael@0: var expected = { michael@0: a: Data(1), michael@0: b: Data(2, false, false, false), michael@0: c: Data(3, false, false, false), michael@0: d: Data(4) michael@0: }; michael@0: michael@0: assert.equalTraits(actual, expected); michael@0: }; michael@0: michael@0: exports["test create simple"] = function(assert) { michael@0: var o1 = Trait.compose( michael@0: Trait({ a: 1 }), michael@0: { michael@0: b: { michael@0: value: function() { michael@0: return this.a; michael@0: } michael@0: } michael@0: } michael@0: ).create(Object.prototype); michael@0: michael@0: assert.equal(Object.getPrototypeOf(o1), Object.prototype, "o1 prototype"); michael@0: assert.equal(1, o1.a, "o1.a"); michael@0: assert.equal(1, o1.b(), "o1.b()"); michael@0: assert.equal(Object.keys(o1).length, 1, "Object.keys(o1).length === 2"); michael@0: }; michael@0: michael@0: exports["test create with Array.prototype"] = function(assert) { michael@0: var o2 = Trait.compose({}, {}).create(Array.prototype); michael@0: assert.equal(Object.getPrototypeOf(o2), Array.prototype, "o2 prototype"); michael@0: }; michael@0: michael@0: exports["test exception for incomplete required properties"] = function(assert) { michael@0: assert.throws(function() { michael@0: Trait({ foo: Trait.required }).create(Object.prototype) michael@0: }, /Missing required property: `foo`/, "required prop error"); michael@0: } michael@0: michael@0: exports["test exception for unresolved conflicts"] = function(assert) { michael@0: assert.throws(function() { michael@0: Trait(Trait({ a: 0 }), Trait({ a: 1 })).create({}) michael@0: }, /Remaining conflicting property: `a`/, "conflicting prop error"); michael@0: } michael@0: michael@0: exports["test conflicting properties are present"] = function(assert) { michael@0: var o5 = Object.create(Object.prototype, Trait.compose( michael@0: { a: { value: 0 } }, michael@0: { a: { value: 1 } } michael@0: )); michael@0: michael@0: assert.ok("a" in o5, "conflicting property present"); michael@0: assert.throws(function() { michael@0: o5.a michael@0: }, /Remaining conflicting property: `a`/, "conflicting prop access error"); michael@0: }; michael@0: michael@0: exports["test diamond with conflicts"] = function(assert) { michael@0: function makeT1(x) { michael@0: return { michael@0: m: { michael@0: value: function() { michael@0: return x michael@0: } michael@0: } michael@0: }; michael@0: }; michael@0: michael@0: function makeT2(x) { michael@0: return Trait.compose( michael@0: Trait({ t2: "foo" }), michael@0: makeT1(x) michael@0: ); michael@0: }; michael@0: michael@0: function makeT3(x) { michael@0: return Trait.compose( michael@0: { michael@0: t3: { value: "bar" } michael@0: }, michael@0: makeT1(x) michael@0: ); michael@0: }; michael@0: michael@0: var T4 = Trait.compose(makeT2(5), makeT3(5)); michael@0: michael@0: assert.throws(function() { michael@0: T4.create(Object.prototype); michael@0: }, /Remaining conflicting property: `m`/, "diamond prop conflict"); michael@0: }; michael@0: michael@0: exports["test providing requirements through proto"] = function(assert) { michael@0: var t = Trait.compose( michael@0: {}, michael@0: { required: { required: true } } michael@0: ).create({ required: "test" }); michael@0: michael@0: assert.equal(t.required, "test", "property from proto is inherited"); michael@0: }; michael@0: michael@0: if (module == require.main) michael@0: require("test").run(exports);