1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/test/test-traits.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,399 @@ 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 +const { Trait } = require('sdk/deprecated/traits'); 1.11 + 1.12 +exports['test:simple compose'] = function(assert) { 1.13 + let List = Trait.compose({ 1.14 + _list: null, 1.15 + constructor: function List() { 1.16 + this._list = []; 1.17 + }, 1.18 + list: function list() this._list.slice(0), 1.19 + add: function add(item) this._list.push(item), 1.20 + remove: function remove(item) { 1.21 + let list = this._list; 1.22 + let index = list.indexOf(item); 1.23 + if (0 <= index) list.slice(index, 1); 1.24 + } 1.25 + }); 1.26 + 1.27 + assert.notEqual(undefined, List, 'should not be undefined'); 1.28 + assert.equal('function', typeof List, 'type should be function'); 1.29 + assert.equal( 1.30 + Trait.compose, 1.31 + List.compose, 1.32 + 'should inherit static compose' 1.33 + ); 1.34 + assert.equal( 1.35 + Trait.override, 1.36 + List.override, 1.37 + 'should inherit static override' 1.38 + ); 1.39 + assert.equal( 1.40 + Trait.required, 1.41 + List.required, 1.42 + 'should inherit static required' 1.43 + ); 1.44 + assert.equal( 1.45 + Trait.resolve, 1.46 + List.resolve, 1.47 + 'should inherit static resolve' 1.48 + ); 1.49 + 1.50 + assert.ok( 1.51 + !('_list' in List.prototype), 1.52 + 'should not expose private API' 1.53 + ); 1.54 +} 1.55 +exports['test: compose trait instance and create instance'] = function(assert) { 1.56 + let List = Trait.compose({ 1.57 + constructor: function List(options) { 1.58 + this._list = []; 1.59 + this._public.publicMember = options.publicMember; 1.60 + }, 1.61 + _privateMember: true, 1.62 + get privateMember() this._privateMember, 1.63 + get list() this._list.slice(0), 1.64 + add: function add(item) this._list.push(item), 1.65 + remove: function remove(item) { 1.66 + let list = this._list 1.67 + let index = list.indexOf(item) 1.68 + if (0 <= index) list.slice(index, 1) 1.69 + } 1.70 + }); 1.71 + let list = List({ publicMember: true }); 1.72 + 1.73 + assert.equal('object', typeof list, 'should return an object') 1.74 + assert.equal( 1.75 + true, 1.76 + list instanceof List, 1.77 + 'should be instance of a List' 1.78 + ); 1.79 + 1.80 + assert.equal( 1.81 + undefined, 1.82 + list._privateMember, 1.83 + 'instance should not expose private API' 1.84 + ); 1.85 + 1.86 + assert.equal( 1.87 + true, 1.88 + list.privateMember, 1.89 + 'privates are accessible by public API' 1.90 + ); 1.91 + 1.92 + list._privateMember = false; 1.93 + 1.94 + assert.equal( 1.95 + true, 1.96 + list.privateMember, 1.97 + 'property changes on instance must not affect privates' 1.98 + ); 1.99 + 1.100 + assert.ok( 1.101 + !('_list' in list), 1.102 + 'instance should not expose private members' 1.103 + ); 1.104 + 1.105 + assert.equal( 1.106 + true, 1.107 + list.publicMember, 1.108 + 'public members are exposed' 1.109 + ) 1.110 + assert.equal( 1.111 + 'function', 1.112 + typeof list.add, 1.113 + 'should be function' 1.114 + ) 1.115 + assert.equal( 1.116 + 'function', 1.117 + typeof list.remove, 1.118 + 'should be function' 1.119 + ); 1.120 + 1.121 + list.add(1); 1.122 + assert.equal( 1.123 + 1, 1.124 + list.list[0], 1.125 + 'exposed public API should be able of modifying privates' 1.126 + ) 1.127 +}; 1.128 + 1.129 + 1.130 +exports['test:instances must not be hackable'] = function(assert) { 1.131 + let SECRET = 'There is no secret!', 1.132 + secret = null; 1.133 + 1.134 + let Class = Trait.compose({ 1.135 + _secret: null, 1.136 + protect: function(data) this._secret = data 1.137 + }); 1.138 + 1.139 + let i1 = Class(); 1.140 + i1.protect(SECRET); 1.141 + 1.142 + assert.equal( 1.143 + undefined, 1.144 + (function() this._secret).call(i1), 1.145 + 'call / apply can\'t access private state' 1.146 + ); 1.147 + 1.148 + let proto = Object.getPrototypeOf(i1); 1.149 + try { 1.150 + proto.reveal = function() this._secret; 1.151 + secret = i1.reveal(); 1.152 + } catch(e) {} 1.153 + assert.notEqual( 1.154 + SECRET, 1.155 + secret, 1.156 + 'public __proto__ changes should not affect privates' 1.157 + ); 1.158 + secret = null; 1.159 + 1.160 + let Class2 = Trait.compose({ 1.161 + _secret: null, 1.162 + protect: function(data) this._secret = data 1.163 + }); 1.164 + let i2 = Class2(); 1.165 + i2.protect(SECRET); 1.166 + try { 1.167 + Object.prototype.reveal = function() this._secret; 1.168 + secret = i2.reveal(); 1.169 + } catch(e) {} 1.170 + assert.notEqual( 1.171 + SECRET, 1.172 + secret, 1.173 + 'Object.prototype changes must not affect instances' 1.174 + ); 1.175 +} 1.176 + 1.177 +exports['test:instanceof'] = function(assert) { 1.178 + const List = Trait.compose({ 1.179 + // private API: 1.180 + _list: null, 1.181 + // public API 1.182 + constructor: function List() { 1.183 + this._list = [] 1.184 + }, 1.185 + get length() this._list.length, 1.186 + add: function add(item) this._list.push(item), 1.187 + remove: function remove(item) { 1.188 + let list = this._list; 1.189 + let index = list.indexOf(item); 1.190 + if (0 <= index) list.slice(index, 1); 1.191 + } 1.192 + }); 1.193 + 1.194 + assert.ok(List() instanceof List, 'Must be instance of List'); 1.195 + assert.ok(new List() instanceof List, 'Must be instance of List'); 1.196 +}; 1.197 + 1.198 +exports['test:privates are unaccessible'] = function(assert) { 1.199 + const List = Trait.compose({ 1.200 + // private API: 1.201 + _list: null, 1.202 + // public API 1.203 + constructor: function List() { 1.204 + this._list = []; 1.205 + }, 1.206 + get length() this._list.length, 1.207 + add: function add(item) this._list.push(item), 1.208 + remove: function remove(item) { 1.209 + let list = this._list; 1.210 + let index = list.indexOf(item); 1.211 + if (0 <= index) list.slice(index, 1); 1.212 + } 1.213 + }); 1.214 + 1.215 + let list = List(); 1.216 + assert.ok(!('_list' in list), 'no privates on instance'); 1.217 + assert.ok( 1.218 + !('_list' in List.prototype), 1.219 + 'no privates on prototype' 1.220 + ); 1.221 +}; 1.222 + 1.223 +exports['test:public API can access private API'] = function(assert) { 1.224 + const List = Trait.compose({ 1.225 + // private API: 1.226 + _list: null, 1.227 + // public API 1.228 + constructor: function List() { 1.229 + this._list = []; 1.230 + }, 1.231 + get length() this._list.length, 1.232 + add: function add(item) this._list.push(item), 1.233 + remove: function remove(item) { 1.234 + let list = this._list; 1.235 + let index = list.indexOf(item); 1.236 + if (0 <= index) list.slice(index, 1); 1.237 + } 1.238 + }); 1.239 + let list = List(); 1.240 + 1.241 + list.add('test'); 1.242 + 1.243 + assert.equal( 1.244 + 1, 1.245 + list.length, 1.246 + 'should be able to add element and access it from public getter' 1.247 + ); 1.248 +}; 1.249 + 1.250 +exports['test:required'] = function(assert) { 1.251 + const Enumerable = Trait.compose({ 1.252 + list: Trait.required, 1.253 + forEach: function forEach(consumer) { 1.254 + return this.list.forEach(consumer); 1.255 + } 1.256 + }); 1.257 + 1.258 + try { 1.259 + let i = Enumerable(); 1.260 + assert.fail('should throw when creating instance with required properties'); 1.261 + } catch(e) { 1.262 + assert.equal( 1.263 + 'Error: Missing required property: list', 1.264 + e.toString(), 1.265 + 'required prop error' 1.266 + ); 1.267 + } 1.268 +}; 1.269 + 1.270 +exports['test:compose with required'] = function(assert) { 1.271 + const List = Trait.compose({ 1.272 + // private API: 1.273 + _list: null, 1.274 + // public API 1.275 + constructor: function List() { 1.276 + this._list = []; 1.277 + }, 1.278 + get length() this._list.length, 1.279 + add: function add(item) this._list.push(item), 1.280 + remove: function remove(item) { 1.281 + let list = this._list; 1.282 + let index = list.indexOf(item); 1.283 + if (0 <= index) list.slice(index, 1); 1.284 + } 1.285 + }); 1.286 + 1.287 + const Enumerable = Trait.compose({ 1.288 + list: Trait.required, 1.289 + forEach: function forEach(consumer) { 1.290 + return this.list.forEach(consumer); 1.291 + } 1.292 + }); 1.293 + 1.294 + const EnumerableList = Enumerable.compose({ 1.295 + get list() this._list.slice(0) 1.296 + }, List); 1.297 + 1.298 + let array = [1,2, 'ab'] 1.299 + let l = EnumerableList(array); 1.300 + array.forEach(function(element) l.add(element)); 1.301 + let number = 0; 1.302 + l.forEach(function(element, index) { 1.303 + number ++; 1.304 + assert.equal(array[index], element, 'should mach array element') 1.305 + }); 1.306 + assert.equal( 1.307 + array.length, 1.308 + number, 1.309 + 'should perform as many asserts as elements in array' 1.310 + ); 1.311 +}; 1.312 + 1.313 +exports['test:resolve'] = function(assert) { 1.314 + const List = Trait.compose({ 1.315 + // private API: 1.316 + _list: null, 1.317 + // public API 1.318 + constructor: function List() { 1.319 + this._list = []; 1.320 + }, 1.321 + get length() this._list.length, 1.322 + add: function add(item) this._list.push(item), 1.323 + remove: function remove(item) { 1.324 + let list = this._list; 1.325 + let index = list.indexOf(item); 1.326 + if (0 <= index) list.slice(index, 1); 1.327 + } 1.328 + }); 1.329 + 1.330 + const Range = List.resolve({ 1.331 + constructor: null, 1.332 + add: '_add', 1.333 + }).compose({ 1.334 + min: null, 1.335 + max: null, 1.336 + get list() this._list.slice(0), 1.337 + constructor: function Range(min, max) { 1.338 + this.min = min; 1.339 + this.max = max; 1.340 + this._list = []; 1.341 + }, 1.342 + add: function(item) { 1.343 + if (item <= this.max && item >= this.min) 1.344 + this._add(item) 1.345 + } 1.346 + }); 1.347 + 1.348 + let r = Range(0, 10); 1.349 + 1.350 + assert.equal( 1.351 + 0, 1.352 + r.min, 1.353 + 'constructor must have set min' 1.354 + ); 1.355 + assert.equal( 1.356 + 10, 1.357 + r.max, 1.358 + 'constructor must have set max' 1.359 + ); 1.360 + 1.361 + assert.equal( 1.362 + 0, 1.363 + r.length, 1.364 + 'should not contain any elements' 1.365 + ); 1.366 + 1.367 + r.add(5); 1.368 + 1.369 + assert.equal( 1.370 + 1, 1.371 + r.length, 1.372 + 'should add `5` to list' 1.373 + ); 1.374 + 1.375 + r.add(12); 1.376 + 1.377 + assert.equal( 1.378 + 1, 1.379 + r.length, 1.380 + 'should not add `12` to list' 1.381 + ); 1.382 +}; 1.383 + 1.384 +exports['test:custom iterator'] = function(assert) { 1.385 + let Sub = Trait.compose({ 1.386 + foo: "foo", 1.387 + bar: "bar", 1.388 + baz: "baz", 1.389 + __iterator__: function() { 1.390 + yield 1; 1.391 + yield 2; 1.392 + yield 3; 1.393 + } 1.394 + }); 1.395 + 1.396 + let (i = 0, sub = Sub()) { 1.397 + for (let item in sub) 1.398 + assert.equal(++i, item, "iterated item has the right value"); 1.399 + }; 1.400 +}; 1.401 + 1.402 +require('sdk/test').run(exports);