1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/test/test-heritage.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,302 @@ 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 { Class, extend, mix, obscure } = require('sdk/core/heritage'); 1.11 + 1.12 +exports['test extend'] = function(assert) { 1.13 + let ancestor = { a: 1 }; 1.14 + let descendant = extend(ancestor, { 1.15 + b: 2, 1.16 + get c() { return 3 }, 1.17 + d: function() { return 4 } 1.18 + }); 1.19 + 1.20 + assert.ok(ancestor.isPrototypeOf(descendant), 1.21 + 'descendant inherits from ancestor'); 1.22 + assert.ok(descendant.b, 2, 'proprety was implemented'); 1.23 + assert.ok(descendant.c, 3, 'getter was implemented'); 1.24 + assert.ok(descendant.d(), 4, 'method was implemented'); 1.25 + 1.26 + /* Will be fixed once Bug 674195 is shipped. 1.27 + assert.ok(Object.isFrozen(descendant), 1.28 + 'extend returns frozen objects'); 1.29 + */ 1.30 +}; 1.31 + 1.32 +exports['test mix'] = function(assert) { 1.33 + let ancestor = { a: 1 } 1.34 + let mixed = mix(extend(ancestor, { b: 1, c: 1 }), { c: 2 }, { d: 3 }); 1.35 + 1.36 + assert.deepEqual(JSON.parse(JSON.stringify(mixed)), { b: 1, c: 2, d: 3 }, 1.37 + 'properties mixed as expected'); 1.38 + assert.ok(ancestor.isPrototypeOf(mixed), 1.39 + 'first arguments ancestor is ancestor of result'); 1.40 +}; 1.41 + 1.42 +exports['test obscure'] = function(assert) { 1.43 + let fixture = mix({ a: 1 }, obscure({ b: 2 })); 1.44 + 1.45 + assert.equal(fixture.a, 1, 'a property is included'); 1.46 + assert.equal(fixture.b, 2, 'b proprety is included'); 1.47 + assert.ok(!Object.getOwnPropertyDescriptor(fixture, 'b').enumerable, 1.48 + 'obscured properties are non-enumerable'); 1.49 +}; 1.50 + 1.51 +exports['test inheritance'] = function(assert) { 1.52 + let Ancestor = Class({ 1.53 + name: 'ancestor', 1.54 + method: function () { 1.55 + return 'hello ' + this.name; 1.56 + } 1.57 + }); 1.58 + 1.59 + assert.ok(Ancestor() instanceof Ancestor, 1.60 + 'can be instantiated without new'); 1.61 + assert.ok(new Ancestor() instanceof Ancestor, 1.62 + 'can also be instantiated with new'); 1.63 + assert.ok(Ancestor() instanceof Class, 1.64 + 'if ancestor not specified than defaults to Class'); 1.65 + assert.ok(Ancestor.prototype.extends, Class.prototype, 1.66 + 'extends of prototype points to ancestors prototype'); 1.67 + 1.68 + 1.69 + assert.equal(Ancestor().method(), 'hello ancestor', 1.70 + 'instance inherits defined properties'); 1.71 + 1.72 + let Descendant = Class({ 1.73 + extends: Ancestor, 1.74 + name: 'descendant' 1.75 + }); 1.76 + 1.77 + assert.ok(Descendant() instanceof Descendant, 1.78 + 'instantiates correctly'); 1.79 + assert.ok(Descendant() instanceof Ancestor, 1.80 + 'Inherits for passed `extends`'); 1.81 + assert.equal(Descendant().method(), 'hello descendant', 1.82 + 'propreties inherited'); 1.83 +}; 1.84 + 1.85 +exports['test immunity against __proto__'] = function(assert) { 1.86 + let Foo = Class({ name: 'foo', hacked: false }); 1.87 + 1.88 + let Bar = Class({ extends: Foo, name: 'bar' }); 1.89 + 1.90 + assert.throws(function() { 1.91 + Foo.prototype.__proto__ = { hacked: true }; 1.92 + if (Foo() instanceof Base && !Foo().hacked) 1.93 + throw Error('can not change prototype chain'); 1.94 + }, 'prototype chain is immune to __proto__ hacks'); 1.95 + 1.96 + assert.throws(function() { 1.97 + Foo.prototype.__proto__ = { hacked: true }; 1.98 + if (Bar() instanceof Foo && !Bar().hacked) 1.99 + throw Error('can not change prototype chain'); 1.100 + }, 'prototype chain of decedants immune to __proto__ hacks'); 1.101 +}; 1.102 + 1.103 +exports['test super'] = function(assert) { 1.104 + var Foo = Class({ 1.105 + initialize: function initialize(options) { 1.106 + this.name = options.name; 1.107 + } 1.108 + }); 1.109 + 1.110 + var Bar = Class({ 1.111 + extends: Foo, 1.112 + initialize: function Bar(options) { 1.113 + Foo.prototype.initialize.call(this, options); 1.114 + this.type = 'bar'; 1.115 + } 1.116 + }); 1.117 + 1.118 + var bar = Bar({ name: 'test' }); 1.119 + 1.120 + assert.equal(bar.type, 'bar', 'bar initializer was called'); 1.121 + assert.equal(bar.name, 'test', 'bar initializer called Foo initializer'); 1.122 +}; 1.123 + 1.124 +exports['test initialize'] = function(assert) { 1.125 + var Dog = Class({ 1.126 + initialize: function initialize(name) { 1.127 + this.name = name; 1.128 + }, 1.129 + type: 'dog', 1.130 + bark: function bark() { 1.131 + return 'Ruff! Ruff!' 1.132 + } 1.133 + }); 1.134 + 1.135 + var fluffy = Dog('Fluffy'); // instatiation 1.136 + assert.ok(fluffy instanceof Dog, 1.137 + 'instanceof works as expected'); 1.138 + assert.ok(fluffy instanceof Class, 1.139 + 'inherits form Class if not specified otherwise'); 1.140 + assert.ok(fluffy.name, 'fluffy', 1.141 + 'initialize unless specified otherwise'); 1.142 +}; 1.143 + 1.144 +exports['test complements regular inheritace'] = function(assert) { 1.145 + let Base = Class({ name: 'base' }); 1.146 + 1.147 + function Type() { 1.148 + // ... 1.149 + } 1.150 + Type.prototype = Object.create(Base.prototype); 1.151 + Type.prototype.run = function() { 1.152 + // ... 1.153 + }; 1.154 + 1.155 + let value = new Type(); 1.156 + 1.157 + assert.ok(value instanceof Type, 'creates instance of Type'); 1.158 + assert.ok(value instanceof Base, 'inherits from Base'); 1.159 + assert.equal(value.name, 'base', 'inherits properties from Base'); 1.160 + 1.161 + 1.162 + let SubType = Class({ 1.163 + extends: Type, 1.164 + sub: 'type' 1.165 + }); 1.166 + 1.167 + let fixture = SubType(); 1.168 + 1.169 + assert.ok(fixture instanceof Base, 'is instance of Base'); 1.170 + assert.ok(fixture instanceof Type, 'is instance of Type'); 1.171 + assert.ok(fixture instanceof SubType, 'is instance of SubType'); 1.172 + 1.173 + assert.equal(fixture.sub, 'type', 'proprety is defined'); 1.174 + assert.equal(fixture.run, Type.prototype.run, 'proprety is inherited'); 1.175 + assert.equal(fixture.name, 'base', 'inherits base properties'); 1.176 +}; 1.177 + 1.178 +exports['test extends object'] = function(assert) { 1.179 + let prototype = { constructor: function() { return this; }, name: 'me' }; 1.180 + let Foo = Class({ 1.181 + extends: prototype, 1.182 + value: 2 1.183 + }); 1.184 + let foo = new Foo(); 1.185 + 1.186 + assert.ok(foo instanceof Foo, 'instance of Foo'); 1.187 + assert.ok(!(foo instanceof Class), 'is not instance of Class'); 1.188 + assert.ok(prototype.isPrototypeOf(foo), 'inherits from given prototype'); 1.189 + assert.equal(Object.getPrototypeOf(Foo.prototype), prototype, 1.190 + 'contsructor prototype inherits from extends option'); 1.191 + assert.equal(foo.value, 2, 'property is defined'); 1.192 + assert.equal(foo.name, 'me', 'prototype proprety is inherited'); 1.193 +}; 1.194 + 1.195 + 1.196 +var HEX = Class({ 1.197 + hex: function hex() { 1.198 + return '#' + this.color; 1.199 + } 1.200 +}); 1.201 + 1.202 +var RGB = Class({ 1.203 + red: function red() { 1.204 + return parseInt(this.color.substr(0, 2), 16); 1.205 + }, 1.206 + green: function green() { 1.207 + return parseInt(this.color.substr(2, 2), 16); 1.208 + }, 1.209 + blue: function blue() { 1.210 + return parseInt(this.color.substr(4, 2), 16); 1.211 + } 1.212 +}); 1.213 + 1.214 +var CMYK = Class({ 1.215 + black: function black() { 1.216 + var color = Math.max(Math.max(this.red(), this.green()), this.blue()); 1.217 + return (1 - color / 255).toFixed(4); 1.218 + }, 1.219 + magenta: function magenta() { 1.220 + var K = this.black(); 1.221 + return (((1 - this.green() / 255).toFixed(4) - K) / (1 - K)).toFixed(4); 1.222 + }, 1.223 + yellow: function yellow() { 1.224 + var K = this.black(); 1.225 + return (((1 - this.blue() / 255).toFixed(4) - K) / (1 - K)).toFixed(4); 1.226 + }, 1.227 + cyan: function cyan() { 1.228 + var K = this.black(); 1.229 + return (((1 - this.red() / 255).toFixed(4) - K) / (1 - K)).toFixed(4); 1.230 + } 1.231 +}); 1.232 + 1.233 +var Color = Class({ 1.234 + implements: [ HEX, RGB, CMYK ], 1.235 + initialize: function initialize(color) { 1.236 + this.color = color; 1.237 + } 1.238 +}); 1.239 + 1.240 +exports['test composition'] = function(assert) { 1.241 + var pink = Color('FFC0CB'); 1.242 + 1.243 + assert.equal(pink.red(), 255, 'red() works'); 1.244 + assert.equal(pink.green(), 192, 'green() works'); 1.245 + assert.equal(pink.blue(), 203, 'blue() works'); 1.246 + 1.247 + assert.equal(pink.magenta(), 0.2471, 'magenta() works'); 1.248 + assert.equal(pink.yellow(), 0.2039, 'yellow() works'); 1.249 + assert.equal(pink.cyan(), 0.0000, 'cyan() works'); 1.250 + 1.251 + assert.ok(pink instanceof Color, 'is instance of Color'); 1.252 + assert.ok(pink instanceof Class, 'is instance of Class'); 1.253 +}; 1.254 + 1.255 +var Point = Class({ 1.256 + initialize: function initialize(x, y) { 1.257 + this.x = x; 1.258 + this.y = y; 1.259 + }, 1.260 + toString: function toString() { 1.261 + return this.x + ':' + this.y; 1.262 + } 1.263 +}) 1.264 + 1.265 +var Pixel = Class({ 1.266 + extends: Point, 1.267 + implements: [ Color ], 1.268 + initialize: function initialize(x, y, color) { 1.269 + Color.prototype.initialize.call(this, color); 1.270 + Point.prototype.initialize.call(this, x, y); 1.271 + }, 1.272 + toString: function toString() { 1.273 + return this.hex() + '@' + Point.prototype.toString.call(this) 1.274 + } 1.275 +}); 1.276 + 1.277 +exports['test compostion with inheritance'] = function(assert) { 1.278 + var pixel = Pixel(11, 23, 'CC3399'); 1.279 + 1.280 + assert.equal(pixel.toString(), '#CC3399@11:23', 'stringifies correctly'); 1.281 + assert.ok(pixel instanceof Pixel, 'instance of Pixel'); 1.282 + assert.ok(pixel instanceof Point, 'instance of Point'); 1.283 +}; 1.284 + 1.285 +exports['test composition with objects'] = function(assert) { 1.286 + var A = { a: 1, b: 1 }; 1.287 + var B = Class({ b: 2, c: 2 }); 1.288 + var C = { c: 3 }; 1.289 + var D = { d: 4 }; 1.290 + 1.291 + var ABCD = Class({ 1.292 + implements: [ A, B, C, D ], 1.293 + e: 5 1.294 + }); 1.295 + 1.296 + var f = ABCD(); 1.297 + 1.298 + assert.equal(f.a, 1, 'inherits A.a'); 1.299 + assert.equal(f.b, 2, 'inherits B.b overrides A.b'); 1.300 + assert.equal(f.c, 3, 'inherits C.c overrides B.c'); 1.301 + assert.equal(f.d, 4, 'inherits D.d'); 1.302 + assert.equal(f.e, 5, 'implements e'); 1.303 +}; 1.304 + 1.305 +require("test").run(exports);