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