123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850 |
- /* 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';
- const ERR_CONFLICT = 'Remaining conflicting property: ',
- ERR_REQUIRED = 'Missing required property: ';
- function assertSametrait(assert, trait1, trait2) {
- let names1 = Object.getOwnPropertyNames(trait1),
- names2 = Object.getOwnPropertyNames(trait2);
- assert.equal(
- names1.length,
- names2.length,
- 'equal traits must have same amount of properties'
- );
- for (let i = 0; i < names1.length; i++) {
- let name = names1[i];
- assert.notEqual(
- -1,
- names2.indexOf(name),
- 'equal traits must contain same named properties: ' + name
- );
- assertSameDescriptor(assert, name, trait1[name], trait2[name]);
- }
- }
- function assertSameDescriptor(assert, name, desc1, desc2) {
- if (desc1.conflict || desc2.conflict) {
- assert.equal(
- desc1.conflict,
- desc2.conflict,
- 'if one of same descriptors has `conflict` another must have it: '
- + name
- );
- }
- else if (desc1.required || desc2.required) {
- assert.equal(
- desc1.required,
- desc2.required,
- 'if one of same descriptors is has `required` another must have it: '
- + name
- );
- }
- else {
- assert.equal(
- desc1.get,
- desc2.get,
- 'get must be the same on both descriptors: ' + name
- );
- assert.equal(
- desc1.set,
- desc2.set,
- 'set must be the same on both descriptors: ' + name
- );
- assert.equal(
- desc1.value,
- desc2.value,
- 'value must be the same on both descriptors: ' + name
- );
- assert.equal(
- desc1.enumerable,
- desc2.enumerable,
- 'enumerable must be the same on both descriptors: ' + name
- );
- assert.equal(
- desc1.required,
- desc2.required,
- 'value must be the same on both descriptors: ' + name
- );
- }
- }
- function Data(value, enumerable, confligurable, writable) {
- return {
- value: value,
- enumerable: false !== enumerable,
- confligurable: false !== confligurable,
- writable: false !== writable
- };
- }
- function Method(method, enumerable, confligurable, writable) {
- return {
- value: method,
- enumerable: false !== enumerable,
- confligurable: false !== confligurable,
- writable: false !== writable
- };
- }
- function Accessor(get, set, enumerable, confligurable) {
- return {
- get: get,
- set: set,
- enumerable: false !== enumerable,
- confligurable: false !== confligurable,
- };
- }
- function Required(name) {
- function required() { throw new Error(ERR_REQUIRED + name) }
- return {
- get: required,
- set: required,
- required: true
- };
- }
- function Conflict(name) {
- function conflict() { throw new Error(ERR_CONFLICT + name) }
- return {
- get: conflict,
- set: conflict,
- conflict: true
- };
- }
- function testMethod() {};
- const { trait, compose, resolve, required, override, create } =
- require('sdk/deprecated/traits/core');
- exports['test:empty trait'] = function(assert) {
- assertSametrait(
- assert,
- trait({}),
- {}
- );
- };
- exports['test:simple trait'] = function(assert) {
- assertSametrait(
- assert,
- trait({
- a: 0,
- b: testMethod
- }),
- {
- a: Data(0, true, true, true),
- b: Method(testMethod, true, true, true)
- }
- );
- };
- exports['test:simple trait with required prop'] = function(assert) {
- assertSametrait(
- assert,
- trait({
- a: required,
- b: 1
- }),
- {
- a: Required('a'),
- b: Data(1)
- }
- );
- };
- exports['test:ordering of trait properties is irrelevant'] = function(assert) {
- assertSametrait(
- assert,
- trait({ a: 0, b: 1, c: required }),
- trait({ b: 1, c: required, a: 0 })
- );
- };
- exports['test:trait with accessor property'] = function(assert) {
- let record = { get a() {}, set a(v) {} };
- let get = Object.getOwnPropertyDescriptor(record,'a').get;
- let set = Object.getOwnPropertyDescriptor(record,'a').set;
- assertSametrait(assert,
- trait(record),
- { a: Accessor(get, set ) }
- );
- };
- exports['test:simple composition'] = function(assert) {
- assertSametrait(
- assert,
- compose(
- trait({ a: 0, b: 1 }),
- trait({ c: 2, d: testMethod })
- ),
- {
- a: Data(0),
- b: Data(1),
- c: Data(2),
- d: Method(testMethod)
- }
- );
- };
- exports['test:composition with conflict'] = function(assert) {
- assertSametrait(
- assert,
- compose(
- trait({ a: 0, b: 1 }),
- trait({ a: 2, c: testMethod })
- ),
- {
- a: Conflict('a'),
- b: Data(1),
- c: Method(testMethod)
- }
- );
- };
- exports['test:composition of identical props does not cause conflict'] =
- function(assert) {
- assertSametrait(assert,
- compose(
- trait({ a: 0, b: 1 }),
- trait({ a: 0, c: testMethod })
- ),
- {
- a: Data(0),
- b: Data(1),
- c: Method(testMethod) }
- )
- };
- exports['test:composition with identical required props'] =
- function(assert) {
- assertSametrait(assert,
- compose(
- trait({ a: required, b: 1 }),
- trait({ a: required, c: testMethod })
- ),
- {
- a: Required(),
- b: Data(1),
- c: Method(testMethod)
- }
- );
- };
- exports['test:composition satisfying a required prop'] = function (assert) {
- assertSametrait(assert,
- compose(
- trait({ a: required, b: 1 }),
- trait({ a: testMethod })
- ),
- {
- a: Method(testMethod),
- b: Data(1)
- }
- );
- };
- exports['test:compose is neutral wrt conflicts'] = function (assert) {
- assertSametrait(assert,
- compose(
- compose(
- trait({ a: 1 }),
- trait({ a: 2 })
- ),
- trait({ b: 0 })
- ),
- {
- a: Conflict('a'),
- b: Data(0)
- }
- );
- };
- exports['test:conflicting prop overrides required prop'] = function (assert) {
- assertSametrait(assert,
- compose(
- compose(
- trait({ a: 1 }),
- trait({ a: 2 })
- ),
- trait({ a: required })
- ),
- {
- a: Conflict('a')
- }
- );
- };
- exports['test:compose is commutative'] = function (assert) {
- assertSametrait(assert,
- compose(
- trait({ a: 0, b: 1 }),
- trait({ c: 2, d: testMethod })
- ),
- compose(
- trait({ c: 2, d: testMethod }),
- trait({ a: 0, b: 1 })
- )
- );
- };
- exports['test:compose is commutative, also for required/conflicting props'] =
- function (assert) {
- assertSametrait(assert,
- compose(
- trait({ a: 0, b: 1, c: 3, e: required }),
- trait({ c: 2, d: testMethod })
- ),
- compose(
- trait({ c: 2, d: testMethod }),
- trait({ a: 0, b: 1, c: 3, e: required })
- )
- );
- };
- exports['test:compose is associative'] = function (assert) {
- assertSametrait(assert,
- compose(
- trait({ a: 0, b: 1, c: 3, d: required }),
- compose(
- trait({ c: 3, d: required }),
- trait({ c: 2, d: testMethod, e: 'foo' })
- )
- ),
- compose(
- compose(
- trait({ a: 0, b: 1, c: 3, d: required }),
- trait({ c: 3, d: required })
- ),
- trait({ c: 2, d: testMethod, e: 'foo' })
- )
- );
- };
- exports['test:diamond import of same prop does not generate conflict'] =
- function (assert) {
- assertSametrait(assert,
- compose(
- compose(
- trait({ b: 2 }),
- trait({ a: 1 })
- ),
- compose(
- trait({ c: 3 }),
- trait({ a: 1 })
- ),
- trait({ d: 4 })
- ),
- {
- a: Data(1),
- b: Data(2),
- c: Data(3),
- d: Data(4)
- }
- );
- };
- exports['test:resolve with empty resolutions has no effect'] =
- function (assert) {
- assertSametrait(assert, resolve({}, trait({
- a: 1,
- b: required,
- c: testMethod
- })), {
- a: Data(1),
- b: Required(),
- c: Method(testMethod)
- });
- };
- exports['test:resolve: renaming'] = function (assert) {
- assertSametrait(assert,
- resolve(
- { a: 'A', c: 'C' },
- trait({ a: 1, b: required, c: testMethod })
- ),
- {
- A: Data(1),
- b: Required(),
- C: Method(testMethod),
- a: Required(),
- c: Required()
- }
- );
- };
- exports['test:resolve: renaming to conflicting name causes conflict, order 1']
- = function (assert) {
- assertSametrait(assert,
- resolve(
- { a: 'b'},
- trait({ a: 1, b: 2 })
- ),
- {
- b: Conflict('b'),
- a: Required()
- }
- );
- };
- exports['test:resolve: renaming to conflicting name causes conflict, order 2']
- = function (assert) {
- assertSametrait(assert,
- resolve(
- { a: 'b' },
- trait({ b: 2, a: 1 })
- ),
- {
- b: Conflict('b'),
- a: Required()
- }
- );
- };
- exports['test:resolve: simple exclusion'] = function (assert) {
- assertSametrait(assert,
- resolve(
- { a: undefined },
- trait({ a: 1, b: 2 })
- ),
- {
- a: Required(),
- b: Data(2)
- }
- );
- };
- exports['test:resolve: exclusion to "empty" trait'] = function (assert) {
- assertSametrait(assert,
- resolve(
- { a: undefined, b: undefined },
- trait({ a: 1, b: 2 })
- ),
- {
- a: Required(),
- b: Required()
- }
- );
- };
- exports['test:resolve: exclusion and renaming of disjoint props'] =
- function (assert) {
- assertSametrait(assert,
- resolve(
- { a: undefined, b: 'c' },
- trait({ a: 1, b: 2 })
- ),
- {
- a: Required(),
- c: Data(2),
- b: Required()
- }
- );
- };
- exports['test:resolve: exclusion and renaming of overlapping props'] =
- function (assert) {
- assertSametrait(assert,
- resolve(
- { a: undefined, b: 'a' },
- trait({ a: 1, b: 2 })
- ),
- {
- a: Data(2),
- b: Required()
- }
- );
- };
- exports['test:resolve: renaming to a common alias causes conflict'] =
- function (assert) {
- assertSametrait(assert,
- resolve(
- { a: 'c', b: 'c' },
- trait({ a: 1, b: 2 })
- ),
- {
- c: Conflict('c'),
- a: Required(),
- b: Required()
- }
- );
- };
- exports['test:resolve: renaming overrides required target'] =
- function (assert) {
- assertSametrait(assert,
- resolve(
- { b: 'a' },
- trait({ a: required, b: 2 })
- ),
- {
- a: Data(2),
- b: Required()
- }
- );
- };
- exports['test:resolve: renaming required properties has no effect'] =
- function (assert) {
- assertSametrait(assert,
- resolve(
- { b: 'a' },
- trait({ a: 2, b: required })
- ),
- {
- a: Data(2),
- b: Required()
- }
- );
- };
- exports['test:resolve: renaming of non-existent props has no effect'] =
- function (assert) {
- assertSametrait(assert,
- resolve(
- { a: 'c', d: 'c' },
- trait({ a: 1, b: 2 })
- ),
- {
- c: Data(1),
- b: Data(2),
- a: Required()
- }
- );
- };
- exports['test:resolve: exclusion of non-existent props has no effect'] =
- function (assert) {
- assertSametrait(assert,
- resolve(
- { b: undefined },
- trait({ a: 1 })
- ),
- {
- a: Data(1)
- }
- );
- };
- exports['test:resolve is neutral w.r.t. required properties'] =
- function (assert) {
- assertSametrait(assert,
- resolve(
- { a: 'c', b: undefined },
- trait({ a: required, b: required, c: 'foo', d: 1 })
- ),
- {
- a: Required(),
- b: Required(),
- c: Data('foo'),
- d: Data(1)
- }
- );
- };
- exports['test:resolve supports swapping of property names, ordering 1'] =
- function (assert) {
- assertSametrait(assert,
- resolve(
- { a: 'b', b: 'a' },
- trait({ a: 1, b: 2 })
- ),
- {
- a: Data(2),
- b: Data(1)
- }
- );
- };
- exports['test:resolve supports swapping of property names, ordering 2'] =
- function (assert) {
- assertSametrait(assert,
- resolve(
- { b: 'a', a: 'b' },
- trait({ a: 1, b: 2 })
- ),
- {
- a: Data(2),
- b: Data(1)
- }
- );
- };
- exports['test:resolve supports swapping of property names, ordering 3'] =
- function (assert) {
- assertSametrait(assert,
- resolve(
- { b: 'a', a: 'b' },
- trait({ b: 2, a: 1 })
- ),
- {
- a: Data(2),
- b: Data(1)
- }
- );
- };
- exports['test:resolve supports swapping of property names, ordering 4'] =
- function (assert) {
- assertSametrait(assert,
- resolve(
- { a: 'b', b: 'a' },
- trait({ b: 2, a: 1 })
- ),
- {
- a: Data(2),
- b: Data(1)
- }
- );
- };
- exports['test:override of mutually exclusive traits'] = function (assert) {
- assertSametrait(assert,
- override(
- trait({ a: 1, b: 2 }),
- trait({ c: 3, d: testMethod })
- ),
- {
- a: Data(1),
- b: Data(2),
- c: Data(3),
- d: Method(testMethod)
- }
- );
- };
- exports['test:override of mutually exclusive traits is compose'] =
- function (assert) {
- assertSametrait(assert,
- override(
- trait({ a: 1, b: 2 }),
- trait({ c: 3, d: testMethod })
- ),
- compose(
- trait({ d: testMethod, c: 3 }),
- trait({ b: 2, a: 1 })
- )
- );
- };
- exports['test:override of overlapping traits'] = function (assert) {
- assertSametrait(assert,
- override(
- trait({ a: 1, b: 2 }),
- trait({ a: 3, c: testMethod })
- ),
- {
- a: Data(1),
- b: Data(2),
- c: Method(testMethod)
- }
- );
- };
- exports['test:three-way override of overlapping traits'] = function (assert) {
- assertSametrait(assert,
- override(
- trait({ a: 1, b: 2 }),
- trait({ b: 4, c: 3 }),
- trait({ a: 3, c: testMethod, d: 5 })
- ),
- {
- a: Data(1),
- b: Data(2),
- c: Data(3),
- d: Data(5)
- }
- );
- };
- exports['test:override replaces required properties'] = function (assert) {
- assertSametrait(assert,
- override(
- trait({ a: required, b: 2 }),
- trait({ a: 1, c: testMethod })
- ),
- {
- a: Data(1),
- b: Data(2),
- c: Method(testMethod)
- }
- );
- };
- exports['test:override is not commutative'] = function (assert) {
- assertSametrait(assert,
- override(
- trait({ a: 1, b: 2 }),
- trait({ a: 3, c: 4 })
- ),
- {
- a: Data(1),
- b: Data(2),
- c: Data(4)
- }
- );
- assertSametrait(assert,
- override(
- trait({ a: 3, c: 4 }),
- trait({ a: 1, b: 2 })
- ),
- {
- a: Data(3),
- b: Data(2),
- c: Data(4)
- }
- );
- };
- exports['test:override is associative'] = function (assert) {
- assertSametrait(assert,
- override(
- override(
- trait({ a: 1, b: 2 }),
- trait({ a: 3, c: 4, d: 5 })
- ),
- trait({ a: 6, c: 7, e: 8 })
- ),
- override(
- trait({ a: 1, b: 2 }),
- override(
- trait({ a: 3, c: 4, d: 5 }),
- trait({ a: 6, c: 7, e: 8 })
- )
- )
- );
- };
- exports['test:create simple'] = function(assert) {
- let o1 = create(
- Object.prototype,
- trait({ a: 1, b: function() { return this.a; } })
- );
- assert.equal(
- Object.prototype,
- Object.getPrototypeOf(o1),
- 'o1 prototype'
- );
- assert.equal(1, o1.a, 'o1.a');
- assert.equal(1, o1.b(), 'o1.b()');
- assert.equal(
- 2,
- Object.getOwnPropertyNames(o1).length,
- 'Object.keys(o1).length === 2'
- );
- };
- exports['test:create with Array.prototype'] = function(assert) {
- let o2 = create(Array.prototype, trait({}));
- assert.equal(
- Array.prototype,
- Object.getPrototypeOf(o2),
- "o2 prototype"
- );
- };
- exports['test:exception for incomplete required properties'] =
- function(assert) {
- try {
- create(Object.prototype, trait({ foo: required }));
- assert.fail('expected create to complain about missing required props');
- }
- catch(e) {
- assert.equal(
- 'Error: Missing required property: foo',
- e.toString(),
- 'required prop error'
- );
- }
- };
- exports['test:exception for unresolved conflicts'] = function(assert) {
- try {
- create({}, compose(trait({ a: 0 }), trait({ a: 1 })));
- assert.fail('expected create to complain about unresolved conflicts');
- }
- catch(e) {
- assert.equal(
- 'Error: Remaining conflicting property: a',
- e.toString(),
- 'conflicting prop error'
- );
- }
- };
- exports['test:verify that required properties are present but undefined'] =
- function(assert) {
- try {
- let o4 = Object.create(Object.prototype, trait({ foo: required }));
- assert.equal(true, 'foo' in o4, 'required property present');
- try {
- let foo = o4.foo;
- assert.fail('access to required property must throw');
- }
- catch(e) {
- assert.equal(
- 'Error: Missing required property: foo',
- e.toString(),
- 'required prop error'
- )
- }
- }
- catch(e) {
- assert.fail('did not expect create to complain about required props');
- }
- };
- exports['test:verify that conflicting properties are present'] =
- function(assert) {
- try {
- let o5 = Object.create(
- Object.prototype,
- compose(trait({ a: 0 }), trait({ a: 1 }))
- );
- assert.equal(true, 'a' in o5, 'conflicting property present');
- try {
- let a = o5.a; // accessors or data prop
- assert.fail('expected conflicting prop to cause exception');
- }
- catch (e) {
- assert.equal(
- 'Error: Remaining conflicting property: a',
- e.toString(),
- 'conflicting prop access error'
- );
- }
- }
- catch(e) {
- assert.fail('did not expect create to complain about conflicting props');
- }
- };
- exports['test diamond with conflicts'] = function(assert) {
- function makeT1(x) trait({ m: function() { return x; } })
- function makeT2(x) compose(trait({ t2: 'foo' }), makeT1(x))
- function makeT3(x) compose(trait({ t3: 'bar' }), makeT1(x))
- let T4 = compose(makeT2(5), makeT3(5));
- try {
- let o = create(Object.prototype, T4);
- assert.fail('expected diamond prop to cause exception');
- }
- catch(e) {
- assert.equal(
- 'Error: Remaining conflicting property: m',
- e.toString(),
- 'diamond prop conflict'
- );
- }
- };
- require('sdk/test').run(exports);
|