diff options
author | 2012-03-30 17:07:02 -0700 | |
---|---|---|
committer | 2012-03-30 17:07:02 -0700 | |
commit | f6ab6622aab00fe7c2f4c3dc41f786ebbe0f0d73 (patch) | |
tree | 870111038542cd27153e1396ebdc063573249689 /tools/addon-sdk-1.5/packages/api-utils/tests/test-traits.js |
initial revision
Diffstat (limited to 'tools/addon-sdk-1.5/packages/api-utils/tests/test-traits.js')
-rw-r--r-- | tools/addon-sdk-1.5/packages/api-utils/tests/test-traits.js | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/tools/addon-sdk-1.5/packages/api-utils/tests/test-traits.js b/tools/addon-sdk-1.5/packages/api-utils/tests/test-traits.js new file mode 100644 index 0000000..8b88f94 --- /dev/null +++ b/tools/addon-sdk-1.5/packages/api-utils/tests/test-traits.js @@ -0,0 +1,398 @@ +/* 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 { Trait } = require('traits'); + +exports['test:simple compose'] = function(test) { + let List = Trait.compose({ + _list: null, + constructor: function List() { + this._list = []; + }, + list: function list() this._list.slice(0), + add: function add(item) this._list.push(item), + remove: function remove(item) { + let list = this._list; + let index = list.indexOf(item); + if (0 <= index) list.slice(index, 1); + } + }); + + test.assertNotEqual(undefined, List, 'should not be undefined'); + test.assertEqual('function', typeof List, 'type should be function'); + test.assertEqual( + Trait.compose, + List.compose, + 'should inherit static compose' + ); + test.assertEqual( + Trait.override, + List.override, + 'should inherit static override' + ); + test.assertEqual( + Trait.required, + List.required, + 'should inherit static required' + ); + test.assertEqual( + Trait.resolve, + List.resolve, + 'should inherit static resolve' + ); + + test.assert( + !('_list' in List.prototype), + 'should not expose private API' + ); +} +exports['test: compose trait instance and create instance'] = function(test) { + let List = Trait.compose({ + constructor: function List(options) { + this._list = []; + this._public.publicMember = options.publicMember; + }, + _privateMember: true, + get privateMember() this._privateMember, + get list() this._list.slice(0), + add: function add(item) this._list.push(item), + remove: function remove(item) { + let list = this._list + let index = list.indexOf(item) + if (0 <= index) list.slice(index, 1) + } + }); + let list = List({ publicMember: true }); + + test.assertEqual('object', typeof list, 'should return an object') + test.assertEqual( + true, + list instanceof List, + 'should be instance of a List' + ); + + test.assertEqual( + undefined, + list._privateMember, + 'instance should not expose private API' + ); + + test.assertEqual( + true, + list.privateMember, + 'privates are accessible by public API' + ); + + list._privateMember = false; + + test.assertEqual( + true, + list.privateMember, + 'property changes on instance must not affect privates' + ); + + test.assert( + !('_list' in list), + 'instance should not expose private members' + ); + + test.assertEqual( + true, + list.publicMember, + 'public members are exposed' + ) + test.assertEqual( + 'function', + typeof list.add, + 'should be function' + ) + test.assertEqual( + 'function', + typeof list.remove, + 'should be function' + ); + + list.add(1); + test.assertEqual( + 1, + list.list[0], + 'exposed public API should be able of modifying privates' + ) +}; + + +exports['test:instances must not be hackable'] = function(test) { + let SECRET = 'There is no secret!', + secret = null; + + let Class = Trait.compose({ + _secret: null, + protect: function(data) this._secret = data + }); + + let i1 = Class(); + i1.protect(SECRET); + + test.assertEqual( + undefined, + (function() this._secret).call(i1), + 'call / apply can\'t access private state' + ); + + let proto = Object.getPrototypeOf(i1); + try { + proto.reveal = function() this._secret; + secret = i1.reveal(); + } catch(e) {} + test.assertNotEqual( + SECRET, + secret, + 'public __proto__ changes should not affect privates' + ); + secret = null; + + let Class2 = Trait.compose({ + _secret: null, + protect: function(data) this._secret = data + }); + let i2 = Class2(); + i2.protect(SECRET); + try { + Object.prototype.reveal = function() this._secret; + secret = i2.reveal(); + } catch(e) {} + test.assertNotEqual( + SECRET, + secret, + 'Object.prototype changes must not affect instances' + ); +} + +exports['test:instanceof'] = function(test) { + const List = Trait.compose({ + // private API: + _list: null, + // public API + constructor: function List() { + this._list = [] + }, + get length() this._list.length, + add: function add(item) this._list.push(item), + remove: function remove(item) { + let list = this._list; + let index = list.indexOf(item); + if (0 <= index) list.slice(index, 1); + } + }); + + test.assert(List() instanceof List, 'Must be instance of List'); + test.assert(new List() instanceof List, 'Must be instance of List'); +}; + +exports['test:privates are unaccessible'] = function(test) { + const List = Trait.compose({ + // private API: + _list: null, + // public API + constructor: function List() { + this._list = []; + }, + get length() this._list.length, + add: function add(item) this._list.push(item), + remove: function remove(item) { + let list = this._list; + let index = list.indexOf(item); + if (0 <= index) list.slice(index, 1); + } + }); + + let list = List(); + test.assert(!('_list' in list), 'no privates on instance'); + test.assert( + !('_list' in List.prototype), + 'no privates on prototype' + ); +}; + +exports['test:public API can access private API'] = function(test) { + const List = Trait.compose({ + // private API: + _list: null, + // public API + constructor: function List() { + this._list = []; + }, + get length() this._list.length, + add: function add(item) this._list.push(item), + remove: function remove(item) { + let list = this._list; + let index = list.indexOf(item); + if (0 <= index) list.slice(index, 1); + } + }); + let list = List(); + + list.add('test'); + + test.assertEqual( + 1, + list.length, + 'should be able to add element and access it from public getter' + ); +}; + +exports['test:required'] = function(test) { + const Enumerable = Trait.compose({ + list: Trait.required, + forEach: function forEach(consumer) { + return this.list.forEach(consumer); + } + }); + + try { + let i = Enumerable(); + test.fail('should throw when creating instance with required properties'); + } catch(e) { + test.assertEqual( + 'Error: Missing required property: list', + e.toString(), + 'required prop error' + ); + } +}; + +exports['test:compose with required'] = function(test) { + const List = Trait.compose({ + // private API: + _list: null, + // public API + constructor: function List() { + this._list = []; + }, + get length() this._list.length, + add: function add(item) this._list.push(item), + remove: function remove(item) { + let list = this._list; + let index = list.indexOf(item); + if (0 <= index) list.slice(index, 1); + } + }); + + const Enumerable = Trait.compose({ + list: Trait.required, + forEach: function forEach(consumer) { + return this.list.forEach(consumer); + } + }); + + const EnumerableList = Enumerable.compose({ + get list() this._list.slice(0) + }, List); + + let array = [1,2, 'ab'] + let l = EnumerableList(array); + array.forEach(function(element) l.add(element)); + let number = 0; + l.forEach(function(element, index) { + number ++; + test.assertEqual(array[index], element, 'should mach array element') + }); + test.assertEqual( + array.length, + number, + 'should perform as many asserts as elements in array' + ); +}; + +exports['test:resolve'] = function(test) { + const List = Trait.compose({ + // private API: + _list: null, + // public API + constructor: function List() { + this._list = []; + }, + get length() this._list.length, + add: function add(item) this._list.push(item), + remove: function remove(item) { + let list = this._list; + let index = list.indexOf(item); + if (0 <= index) list.slice(index, 1); + } + }); + + const Range = List.resolve({ + constructor: null, + add: '_add', + }).compose({ + min: null, + max: null, + get list() this._list.slice(0), + constructor: function Range(min, max) { + this.min = min; + this.max = max; + this._list = []; + }, + add: function(item) { + if (item <= this.max && item >= this.min) + this._add(item) + } + }); + + let r = Range(0, 10); + + test.assertEqual( + 0, + r.min, + 'constructor must have set min' + ); + test.assertEqual( + 10, + r.max, + 'constructor must have set max' + ); + + test.assertEqual( + 0, + r.length, + 'should not contain any elements' + ); + + r.add(5); + + test.assertEqual( + 1, + r.length, + 'should add `5` to list' + ); + + r.add(12); + + test.assertEqual( + 1, + r.length, + 'should not add `12` to list' + ); +}; + +exports['test:custom iterator'] = function(test) { + let Sub = Trait.compose({ + foo: "foo", + bar: "bar", + baz: "baz", + __iterator__: function() { + yield 1; + yield 2; + yield 3; + } + }); + + let (i = 0, sub = Sub()) { + for (let item in sub) + test.assertEqual(++i, item, "iterated item has the right value"); + }; +}; + |