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