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

branch
TOR_BUG_9701
changeset 10
ac0c01689b40
equal deleted inserted replaced
-1:000000000000 0:e913a50ab774
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/. */
4
5 "use strict";
6
7 const { Trait } = require('sdk/deprecated/traits');
8
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 });
23
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 );
46
47 assert.ok(
48 !('_list' in List.prototype),
49 'should not expose private API'
50 );
51 }
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 });
69
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 );
76
77 assert.equal(
78 undefined,
79 list._privateMember,
80 'instance should not expose private API'
81 );
82
83 assert.equal(
84 true,
85 list.privateMember,
86 'privates are accessible by public API'
87 );
88
89 list._privateMember = false;
90
91 assert.equal(
92 true,
93 list.privateMember,
94 'property changes on instance must not affect privates'
95 );
96
97 assert.ok(
98 !('_list' in list),
99 'instance should not expose private members'
100 );
101
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 );
117
118 list.add(1);
119 assert.equal(
120 1,
121 list.list[0],
122 'exposed public API should be able of modifying privates'
123 )
124 };
125
126
127 exports['test:instances must not be hackable'] = function(assert) {
128 let SECRET = 'There is no secret!',
129 secret = null;
130
131 let Class = Trait.compose({
132 _secret: null,
133 protect: function(data) this._secret = data
134 });
135
136 let i1 = Class();
137 i1.protect(SECRET);
138
139 assert.equal(
140 undefined,
141 (function() this._secret).call(i1),
142 'call / apply can\'t access private state'
143 );
144
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;
156
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 );
172 }
173
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 });
190
191 assert.ok(List() instanceof List, 'Must be instance of List');
192 assert.ok(new List() instanceof List, 'Must be instance of List');
193 };
194
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 });
211
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 );
218 };
219
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();
237
238 list.add('test');
239
240 assert.equal(
241 1,
242 list.length,
243 'should be able to add element and access it from public getter'
244 );
245 };
246
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 });
254
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 }
265 };
266
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 });
283
284 const Enumerable = Trait.compose({
285 list: Trait.required,
286 forEach: function forEach(consumer) {
287 return this.list.forEach(consumer);
288 }
289 });
290
291 const EnumerableList = Enumerable.compose({
292 get list() this._list.slice(0)
293 }, List);
294
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 );
308 };
309
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 });
326
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 });
344
345 let r = Range(0, 10);
346
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 );
357
358 assert.equal(
359 0,
360 r.length,
361 'should not contain any elements'
362 );
363
364 r.add(5);
365
366 assert.equal(
367 1,
368 r.length,
369 'should add `5` to list'
370 );
371
372 r.add(12);
373
374 assert.equal(
375 1,
376 r.length,
377 'should not add `12` to list'
378 );
379 };
380
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 });
392
393 let (i = 0, sub = Sub()) {
394 for (let item in sub)
395 assert.equal(++i, item, "iterated item has the right value");
396 };
397 };
398
399 require('sdk/test').run(exports);

mercurial