addon-sdk/source/test/test-traits.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 "use strict";
michael@0 6
michael@0 7 const { Trait } = require('sdk/deprecated/traits');
michael@0 8
michael@0 9 exports['test:simple compose'] = function(assert) {
michael@0 10 let List = Trait.compose({
michael@0 11 _list: null,
michael@0 12 constructor: function List() {
michael@0 13 this._list = [];
michael@0 14 },
michael@0 15 list: function list() this._list.slice(0),
michael@0 16 add: function add(item) this._list.push(item),
michael@0 17 remove: function remove(item) {
michael@0 18 let list = this._list;
michael@0 19 let index = list.indexOf(item);
michael@0 20 if (0 <= index) list.slice(index, 1);
michael@0 21 }
michael@0 22 });
michael@0 23
michael@0 24 assert.notEqual(undefined, List, 'should not be undefined');
michael@0 25 assert.equal('function', typeof List, 'type should be function');
michael@0 26 assert.equal(
michael@0 27 Trait.compose,
michael@0 28 List.compose,
michael@0 29 'should inherit static compose'
michael@0 30 );
michael@0 31 assert.equal(
michael@0 32 Trait.override,
michael@0 33 List.override,
michael@0 34 'should inherit static override'
michael@0 35 );
michael@0 36 assert.equal(
michael@0 37 Trait.required,
michael@0 38 List.required,
michael@0 39 'should inherit static required'
michael@0 40 );
michael@0 41 assert.equal(
michael@0 42 Trait.resolve,
michael@0 43 List.resolve,
michael@0 44 'should inherit static resolve'
michael@0 45 );
michael@0 46
michael@0 47 assert.ok(
michael@0 48 !('_list' in List.prototype),
michael@0 49 'should not expose private API'
michael@0 50 );
michael@0 51 }
michael@0 52 exports['test: compose trait instance and create instance'] = function(assert) {
michael@0 53 let List = Trait.compose({
michael@0 54 constructor: function List(options) {
michael@0 55 this._list = [];
michael@0 56 this._public.publicMember = options.publicMember;
michael@0 57 },
michael@0 58 _privateMember: true,
michael@0 59 get privateMember() this._privateMember,
michael@0 60 get list() this._list.slice(0),
michael@0 61 add: function add(item) this._list.push(item),
michael@0 62 remove: function remove(item) {
michael@0 63 let list = this._list
michael@0 64 let index = list.indexOf(item)
michael@0 65 if (0 <= index) list.slice(index, 1)
michael@0 66 }
michael@0 67 });
michael@0 68 let list = List({ publicMember: true });
michael@0 69
michael@0 70 assert.equal('object', typeof list, 'should return an object')
michael@0 71 assert.equal(
michael@0 72 true,
michael@0 73 list instanceof List,
michael@0 74 'should be instance of a List'
michael@0 75 );
michael@0 76
michael@0 77 assert.equal(
michael@0 78 undefined,
michael@0 79 list._privateMember,
michael@0 80 'instance should not expose private API'
michael@0 81 );
michael@0 82
michael@0 83 assert.equal(
michael@0 84 true,
michael@0 85 list.privateMember,
michael@0 86 'privates are accessible by public API'
michael@0 87 );
michael@0 88
michael@0 89 list._privateMember = false;
michael@0 90
michael@0 91 assert.equal(
michael@0 92 true,
michael@0 93 list.privateMember,
michael@0 94 'property changes on instance must not affect privates'
michael@0 95 );
michael@0 96
michael@0 97 assert.ok(
michael@0 98 !('_list' in list),
michael@0 99 'instance should not expose private members'
michael@0 100 );
michael@0 101
michael@0 102 assert.equal(
michael@0 103 true,
michael@0 104 list.publicMember,
michael@0 105 'public members are exposed'
michael@0 106 )
michael@0 107 assert.equal(
michael@0 108 'function',
michael@0 109 typeof list.add,
michael@0 110 'should be function'
michael@0 111 )
michael@0 112 assert.equal(
michael@0 113 'function',
michael@0 114 typeof list.remove,
michael@0 115 'should be function'
michael@0 116 );
michael@0 117
michael@0 118 list.add(1);
michael@0 119 assert.equal(
michael@0 120 1,
michael@0 121 list.list[0],
michael@0 122 'exposed public API should be able of modifying privates'
michael@0 123 )
michael@0 124 };
michael@0 125
michael@0 126
michael@0 127 exports['test:instances must not be hackable'] = function(assert) {
michael@0 128 let SECRET = 'There is no secret!',
michael@0 129 secret = null;
michael@0 130
michael@0 131 let Class = Trait.compose({
michael@0 132 _secret: null,
michael@0 133 protect: function(data) this._secret = data
michael@0 134 });
michael@0 135
michael@0 136 let i1 = Class();
michael@0 137 i1.protect(SECRET);
michael@0 138
michael@0 139 assert.equal(
michael@0 140 undefined,
michael@0 141 (function() this._secret).call(i1),
michael@0 142 'call / apply can\'t access private state'
michael@0 143 );
michael@0 144
michael@0 145 let proto = Object.getPrototypeOf(i1);
michael@0 146 try {
michael@0 147 proto.reveal = function() this._secret;
michael@0 148 secret = i1.reveal();
michael@0 149 } catch(e) {}
michael@0 150 assert.notEqual(
michael@0 151 SECRET,
michael@0 152 secret,
michael@0 153 'public __proto__ changes should not affect privates'
michael@0 154 );
michael@0 155 secret = null;
michael@0 156
michael@0 157 let Class2 = Trait.compose({
michael@0 158 _secret: null,
michael@0 159 protect: function(data) this._secret = data
michael@0 160 });
michael@0 161 let i2 = Class2();
michael@0 162 i2.protect(SECRET);
michael@0 163 try {
michael@0 164 Object.prototype.reveal = function() this._secret;
michael@0 165 secret = i2.reveal();
michael@0 166 } catch(e) {}
michael@0 167 assert.notEqual(
michael@0 168 SECRET,
michael@0 169 secret,
michael@0 170 'Object.prototype changes must not affect instances'
michael@0 171 );
michael@0 172 }
michael@0 173
michael@0 174 exports['test:instanceof'] = function(assert) {
michael@0 175 const List = Trait.compose({
michael@0 176 // private API:
michael@0 177 _list: null,
michael@0 178 // public API
michael@0 179 constructor: function List() {
michael@0 180 this._list = []
michael@0 181 },
michael@0 182 get length() this._list.length,
michael@0 183 add: function add(item) this._list.push(item),
michael@0 184 remove: function remove(item) {
michael@0 185 let list = this._list;
michael@0 186 let index = list.indexOf(item);
michael@0 187 if (0 <= index) list.slice(index, 1);
michael@0 188 }
michael@0 189 });
michael@0 190
michael@0 191 assert.ok(List() instanceof List, 'Must be instance of List');
michael@0 192 assert.ok(new List() instanceof List, 'Must be instance of List');
michael@0 193 };
michael@0 194
michael@0 195 exports['test:privates are unaccessible'] = function(assert) {
michael@0 196 const List = Trait.compose({
michael@0 197 // private API:
michael@0 198 _list: null,
michael@0 199 // public API
michael@0 200 constructor: function List() {
michael@0 201 this._list = [];
michael@0 202 },
michael@0 203 get length() this._list.length,
michael@0 204 add: function add(item) this._list.push(item),
michael@0 205 remove: function remove(item) {
michael@0 206 let list = this._list;
michael@0 207 let index = list.indexOf(item);
michael@0 208 if (0 <= index) list.slice(index, 1);
michael@0 209 }
michael@0 210 });
michael@0 211
michael@0 212 let list = List();
michael@0 213 assert.ok(!('_list' in list), 'no privates on instance');
michael@0 214 assert.ok(
michael@0 215 !('_list' in List.prototype),
michael@0 216 'no privates on prototype'
michael@0 217 );
michael@0 218 };
michael@0 219
michael@0 220 exports['test:public API can access private API'] = function(assert) {
michael@0 221 const List = Trait.compose({
michael@0 222 // private API:
michael@0 223 _list: null,
michael@0 224 // public API
michael@0 225 constructor: function List() {
michael@0 226 this._list = [];
michael@0 227 },
michael@0 228 get length() this._list.length,
michael@0 229 add: function add(item) this._list.push(item),
michael@0 230 remove: function remove(item) {
michael@0 231 let list = this._list;
michael@0 232 let index = list.indexOf(item);
michael@0 233 if (0 <= index) list.slice(index, 1);
michael@0 234 }
michael@0 235 });
michael@0 236 let list = List();
michael@0 237
michael@0 238 list.add('test');
michael@0 239
michael@0 240 assert.equal(
michael@0 241 1,
michael@0 242 list.length,
michael@0 243 'should be able to add element and access it from public getter'
michael@0 244 );
michael@0 245 };
michael@0 246
michael@0 247 exports['test:required'] = function(assert) {
michael@0 248 const Enumerable = Trait.compose({
michael@0 249 list: Trait.required,
michael@0 250 forEach: function forEach(consumer) {
michael@0 251 return this.list.forEach(consumer);
michael@0 252 }
michael@0 253 });
michael@0 254
michael@0 255 try {
michael@0 256 let i = Enumerable();
michael@0 257 assert.fail('should throw when creating instance with required properties');
michael@0 258 } catch(e) {
michael@0 259 assert.equal(
michael@0 260 'Error: Missing required property: list',
michael@0 261 e.toString(),
michael@0 262 'required prop error'
michael@0 263 );
michael@0 264 }
michael@0 265 };
michael@0 266
michael@0 267 exports['test:compose with required'] = function(assert) {
michael@0 268 const List = Trait.compose({
michael@0 269 // private API:
michael@0 270 _list: null,
michael@0 271 // public API
michael@0 272 constructor: function List() {
michael@0 273 this._list = [];
michael@0 274 },
michael@0 275 get length() this._list.length,
michael@0 276 add: function add(item) this._list.push(item),
michael@0 277 remove: function remove(item) {
michael@0 278 let list = this._list;
michael@0 279 let index = list.indexOf(item);
michael@0 280 if (0 <= index) list.slice(index, 1);
michael@0 281 }
michael@0 282 });
michael@0 283
michael@0 284 const Enumerable = Trait.compose({
michael@0 285 list: Trait.required,
michael@0 286 forEach: function forEach(consumer) {
michael@0 287 return this.list.forEach(consumer);
michael@0 288 }
michael@0 289 });
michael@0 290
michael@0 291 const EnumerableList = Enumerable.compose({
michael@0 292 get list() this._list.slice(0)
michael@0 293 }, List);
michael@0 294
michael@0 295 let array = [1,2, 'ab']
michael@0 296 let l = EnumerableList(array);
michael@0 297 array.forEach(function(element) l.add(element));
michael@0 298 let number = 0;
michael@0 299 l.forEach(function(element, index) {
michael@0 300 number ++;
michael@0 301 assert.equal(array[index], element, 'should mach array element')
michael@0 302 });
michael@0 303 assert.equal(
michael@0 304 array.length,
michael@0 305 number,
michael@0 306 'should perform as many asserts as elements in array'
michael@0 307 );
michael@0 308 };
michael@0 309
michael@0 310 exports['test:resolve'] = function(assert) {
michael@0 311 const List = Trait.compose({
michael@0 312 // private API:
michael@0 313 _list: null,
michael@0 314 // public API
michael@0 315 constructor: function List() {
michael@0 316 this._list = [];
michael@0 317 },
michael@0 318 get length() this._list.length,
michael@0 319 add: function add(item) this._list.push(item),
michael@0 320 remove: function remove(item) {
michael@0 321 let list = this._list;
michael@0 322 let index = list.indexOf(item);
michael@0 323 if (0 <= index) list.slice(index, 1);
michael@0 324 }
michael@0 325 });
michael@0 326
michael@0 327 const Range = List.resolve({
michael@0 328 constructor: null,
michael@0 329 add: '_add',
michael@0 330 }).compose({
michael@0 331 min: null,
michael@0 332 max: null,
michael@0 333 get list() this._list.slice(0),
michael@0 334 constructor: function Range(min, max) {
michael@0 335 this.min = min;
michael@0 336 this.max = max;
michael@0 337 this._list = [];
michael@0 338 },
michael@0 339 add: function(item) {
michael@0 340 if (item <= this.max && item >= this.min)
michael@0 341 this._add(item)
michael@0 342 }
michael@0 343 });
michael@0 344
michael@0 345 let r = Range(0, 10);
michael@0 346
michael@0 347 assert.equal(
michael@0 348 0,
michael@0 349 r.min,
michael@0 350 'constructor must have set min'
michael@0 351 );
michael@0 352 assert.equal(
michael@0 353 10,
michael@0 354 r.max,
michael@0 355 'constructor must have set max'
michael@0 356 );
michael@0 357
michael@0 358 assert.equal(
michael@0 359 0,
michael@0 360 r.length,
michael@0 361 'should not contain any elements'
michael@0 362 );
michael@0 363
michael@0 364 r.add(5);
michael@0 365
michael@0 366 assert.equal(
michael@0 367 1,
michael@0 368 r.length,
michael@0 369 'should add `5` to list'
michael@0 370 );
michael@0 371
michael@0 372 r.add(12);
michael@0 373
michael@0 374 assert.equal(
michael@0 375 1,
michael@0 376 r.length,
michael@0 377 'should not add `12` to list'
michael@0 378 );
michael@0 379 };
michael@0 380
michael@0 381 exports['test:custom iterator'] = function(assert) {
michael@0 382 let Sub = Trait.compose({
michael@0 383 foo: "foo",
michael@0 384 bar: "bar",
michael@0 385 baz: "baz",
michael@0 386 __iterator__: function() {
michael@0 387 yield 1;
michael@0 388 yield 2;
michael@0 389 yield 3;
michael@0 390 }
michael@0 391 });
michael@0 392
michael@0 393 let (i = 0, sub = Sub()) {
michael@0 394 for (let item in sub)
michael@0 395 assert.equal(++i, item, "iterated item has the right value");
michael@0 396 };
michael@0 397 };
michael@0 398
michael@0 399 require('sdk/test').run(exports);

mercurial