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

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:90f671811bd6
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 { Class, extend, mix, obscure } = require('sdk/core/heritage');
8
9 exports['test extend'] = function(assert) {
10 let ancestor = { a: 1 };
11 let descendant = extend(ancestor, {
12 b: 2,
13 get c() { return 3 },
14 d: function() { return 4 }
15 });
16
17 assert.ok(ancestor.isPrototypeOf(descendant),
18 'descendant inherits from ancestor');
19 assert.ok(descendant.b, 2, 'proprety was implemented');
20 assert.ok(descendant.c, 3, 'getter was implemented');
21 assert.ok(descendant.d(), 4, 'method was implemented');
22
23 /* Will be fixed once Bug 674195 is shipped.
24 assert.ok(Object.isFrozen(descendant),
25 'extend returns frozen objects');
26 */
27 };
28
29 exports['test mix'] = function(assert) {
30 let ancestor = { a: 1 }
31 let mixed = mix(extend(ancestor, { b: 1, c: 1 }), { c: 2 }, { d: 3 });
32
33 assert.deepEqual(JSON.parse(JSON.stringify(mixed)), { b: 1, c: 2, d: 3 },
34 'properties mixed as expected');
35 assert.ok(ancestor.isPrototypeOf(mixed),
36 'first arguments ancestor is ancestor of result');
37 };
38
39 exports['test obscure'] = function(assert) {
40 let fixture = mix({ a: 1 }, obscure({ b: 2 }));
41
42 assert.equal(fixture.a, 1, 'a property is included');
43 assert.equal(fixture.b, 2, 'b proprety is included');
44 assert.ok(!Object.getOwnPropertyDescriptor(fixture, 'b').enumerable,
45 'obscured properties are non-enumerable');
46 };
47
48 exports['test inheritance'] = function(assert) {
49 let Ancestor = Class({
50 name: 'ancestor',
51 method: function () {
52 return 'hello ' + this.name;
53 }
54 });
55
56 assert.ok(Ancestor() instanceof Ancestor,
57 'can be instantiated without new');
58 assert.ok(new Ancestor() instanceof Ancestor,
59 'can also be instantiated with new');
60 assert.ok(Ancestor() instanceof Class,
61 'if ancestor not specified than defaults to Class');
62 assert.ok(Ancestor.prototype.extends, Class.prototype,
63 'extends of prototype points to ancestors prototype');
64
65
66 assert.equal(Ancestor().method(), 'hello ancestor',
67 'instance inherits defined properties');
68
69 let Descendant = Class({
70 extends: Ancestor,
71 name: 'descendant'
72 });
73
74 assert.ok(Descendant() instanceof Descendant,
75 'instantiates correctly');
76 assert.ok(Descendant() instanceof Ancestor,
77 'Inherits for passed `extends`');
78 assert.equal(Descendant().method(), 'hello descendant',
79 'propreties inherited');
80 };
81
82 exports['test immunity against __proto__'] = function(assert) {
83 let Foo = Class({ name: 'foo', hacked: false });
84
85 let Bar = Class({ extends: Foo, name: 'bar' });
86
87 assert.throws(function() {
88 Foo.prototype.__proto__ = { hacked: true };
89 if (Foo() instanceof Base && !Foo().hacked)
90 throw Error('can not change prototype chain');
91 }, 'prototype chain is immune to __proto__ hacks');
92
93 assert.throws(function() {
94 Foo.prototype.__proto__ = { hacked: true };
95 if (Bar() instanceof Foo && !Bar().hacked)
96 throw Error('can not change prototype chain');
97 }, 'prototype chain of decedants immune to __proto__ hacks');
98 };
99
100 exports['test super'] = function(assert) {
101 var Foo = Class({
102 initialize: function initialize(options) {
103 this.name = options.name;
104 }
105 });
106
107 var Bar = Class({
108 extends: Foo,
109 initialize: function Bar(options) {
110 Foo.prototype.initialize.call(this, options);
111 this.type = 'bar';
112 }
113 });
114
115 var bar = Bar({ name: 'test' });
116
117 assert.equal(bar.type, 'bar', 'bar initializer was called');
118 assert.equal(bar.name, 'test', 'bar initializer called Foo initializer');
119 };
120
121 exports['test initialize'] = function(assert) {
122 var Dog = Class({
123 initialize: function initialize(name) {
124 this.name = name;
125 },
126 type: 'dog',
127 bark: function bark() {
128 return 'Ruff! Ruff!'
129 }
130 });
131
132 var fluffy = Dog('Fluffy'); // instatiation
133 assert.ok(fluffy instanceof Dog,
134 'instanceof works as expected');
135 assert.ok(fluffy instanceof Class,
136 'inherits form Class if not specified otherwise');
137 assert.ok(fluffy.name, 'fluffy',
138 'initialize unless specified otherwise');
139 };
140
141 exports['test complements regular inheritace'] = function(assert) {
142 let Base = Class({ name: 'base' });
143
144 function Type() {
145 // ...
146 }
147 Type.prototype = Object.create(Base.prototype);
148 Type.prototype.run = function() {
149 // ...
150 };
151
152 let value = new Type();
153
154 assert.ok(value instanceof Type, 'creates instance of Type');
155 assert.ok(value instanceof Base, 'inherits from Base');
156 assert.equal(value.name, 'base', 'inherits properties from Base');
157
158
159 let SubType = Class({
160 extends: Type,
161 sub: 'type'
162 });
163
164 let fixture = SubType();
165
166 assert.ok(fixture instanceof Base, 'is instance of Base');
167 assert.ok(fixture instanceof Type, 'is instance of Type');
168 assert.ok(fixture instanceof SubType, 'is instance of SubType');
169
170 assert.equal(fixture.sub, 'type', 'proprety is defined');
171 assert.equal(fixture.run, Type.prototype.run, 'proprety is inherited');
172 assert.equal(fixture.name, 'base', 'inherits base properties');
173 };
174
175 exports['test extends object'] = function(assert) {
176 let prototype = { constructor: function() { return this; }, name: 'me' };
177 let Foo = Class({
178 extends: prototype,
179 value: 2
180 });
181 let foo = new Foo();
182
183 assert.ok(foo instanceof Foo, 'instance of Foo');
184 assert.ok(!(foo instanceof Class), 'is not instance of Class');
185 assert.ok(prototype.isPrototypeOf(foo), 'inherits from given prototype');
186 assert.equal(Object.getPrototypeOf(Foo.prototype), prototype,
187 'contsructor prototype inherits from extends option');
188 assert.equal(foo.value, 2, 'property is defined');
189 assert.equal(foo.name, 'me', 'prototype proprety is inherited');
190 };
191
192
193 var HEX = Class({
194 hex: function hex() {
195 return '#' + this.color;
196 }
197 });
198
199 var RGB = Class({
200 red: function red() {
201 return parseInt(this.color.substr(0, 2), 16);
202 },
203 green: function green() {
204 return parseInt(this.color.substr(2, 2), 16);
205 },
206 blue: function blue() {
207 return parseInt(this.color.substr(4, 2), 16);
208 }
209 });
210
211 var CMYK = Class({
212 black: function black() {
213 var color = Math.max(Math.max(this.red(), this.green()), this.blue());
214 return (1 - color / 255).toFixed(4);
215 },
216 magenta: function magenta() {
217 var K = this.black();
218 return (((1 - this.green() / 255).toFixed(4) - K) / (1 - K)).toFixed(4);
219 },
220 yellow: function yellow() {
221 var K = this.black();
222 return (((1 - this.blue() / 255).toFixed(4) - K) / (1 - K)).toFixed(4);
223 },
224 cyan: function cyan() {
225 var K = this.black();
226 return (((1 - this.red() / 255).toFixed(4) - K) / (1 - K)).toFixed(4);
227 }
228 });
229
230 var Color = Class({
231 implements: [ HEX, RGB, CMYK ],
232 initialize: function initialize(color) {
233 this.color = color;
234 }
235 });
236
237 exports['test composition'] = function(assert) {
238 var pink = Color('FFC0CB');
239
240 assert.equal(pink.red(), 255, 'red() works');
241 assert.equal(pink.green(), 192, 'green() works');
242 assert.equal(pink.blue(), 203, 'blue() works');
243
244 assert.equal(pink.magenta(), 0.2471, 'magenta() works');
245 assert.equal(pink.yellow(), 0.2039, 'yellow() works');
246 assert.equal(pink.cyan(), 0.0000, 'cyan() works');
247
248 assert.ok(pink instanceof Color, 'is instance of Color');
249 assert.ok(pink instanceof Class, 'is instance of Class');
250 };
251
252 var Point = Class({
253 initialize: function initialize(x, y) {
254 this.x = x;
255 this.y = y;
256 },
257 toString: function toString() {
258 return this.x + ':' + this.y;
259 }
260 })
261
262 var Pixel = Class({
263 extends: Point,
264 implements: [ Color ],
265 initialize: function initialize(x, y, color) {
266 Color.prototype.initialize.call(this, color);
267 Point.prototype.initialize.call(this, x, y);
268 },
269 toString: function toString() {
270 return this.hex() + '@' + Point.prototype.toString.call(this)
271 }
272 });
273
274 exports['test compostion with inheritance'] = function(assert) {
275 var pixel = Pixel(11, 23, 'CC3399');
276
277 assert.equal(pixel.toString(), '#CC3399@11:23', 'stringifies correctly');
278 assert.ok(pixel instanceof Pixel, 'instance of Pixel');
279 assert.ok(pixel instanceof Point, 'instance of Point');
280 };
281
282 exports['test composition with objects'] = function(assert) {
283 var A = { a: 1, b: 1 };
284 var B = Class({ b: 2, c: 2 });
285 var C = { c: 3 };
286 var D = { d: 4 };
287
288 var ABCD = Class({
289 implements: [ A, B, C, D ],
290 e: 5
291 });
292
293 var f = ABCD();
294
295 assert.equal(f.a, 1, 'inherits A.a');
296 assert.equal(f.b, 2, 'inherits B.b overrides A.b');
297 assert.equal(f.c, 3, 'inherits C.c overrides B.c');
298 assert.equal(f.d, 4, 'inherits D.d');
299 assert.equal(f.e, 5, 'implements e');
300 };
301
302 require("test").run(exports);

mercurial