1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 (function() { 6 7 'use strict'; 8 9 module('base'); 10 11 test('mix(dest, src) should copy properties from |src| to |dest|', 12 function() { 13 var src = { a: 'a', b: 'b'}; 14 var dest = { c: 'c'}; 15 16 base.mix(dest, src); 17 deepEqual(dest, {a: 'a', b: 'b', c: 'c'}); 18 }); 19 20 test('mix(dest, src) should assert if properties are overwritten', 21 function() { 22 var src = { a: 'a', b: 'b'}; 23 var dest = { a: 'a'}; 24 25 sinon.spy(base.debug, 'assert'); 26 27 try { 28 base.mix(dest, src); 29 } catch (e) { 30 } finally { 31 sinon.assert.called(base.debug.assert); 32 base.debug.assert.restore(); 33 } 34 }); 35 36 test('values(obj) should return an array containing the values of |obj|', 37 function() { 38 var output = base.values({ a: 'a', b: 'b'}); 39 40 notEqual(output.indexOf('a'), -1, '"a" should be in the output'); 41 notEqual(output.indexOf('b'), -1, '"b" should be in the output'); 42 }); 43 44 test('dispose(obj) should invoke the dispose method on |obj|', 45 function() { 46 var obj = { 47 dispose: sinon.spy() 48 }; 49 base.dispose(obj); 50 sinon.assert.called(obj.dispose); 51 }); 52 53 test('dispose(obj) should not crash if |obj| is null', 54 function() { 55 expect(0); 56 base.dispose(null); 57 }); 58 59 test('urljoin(url, opt_param) should return url if |opt_param| is missing', 60 function() { 61 QUnit.equal( 62 base.urlJoin('http://www.chromium.org'), 'http://www.chromium.org'); 63 }); 64 65 test('urljoin(url, opt_param) should urlencode |opt_param|', 66 function() { 67 var result = base.urlJoin('http://www.chromium.org', { 68 a: 'a', 69 foo: 'foo', 70 escapist: ':/?#[]@$&+,;=' 71 }); 72 QUnit.equal( 73 result, 74 'http://www.chromium.org?a=a&foo=foo' + 75 '&escapist=%3A%2F%3F%23%5B%5D%40%24%26%2B%2C%3B%3D'); 76 }); 77 78 test('escapeHTML(str) should escape special characters', function() { 79 QUnit.equal( 80 base.escapeHTML('<script>alert("hello")</script>'), 81 '<script>alert("hello")</script>'); 82 }); 83 84 QUnit.asyncTest('Promise.sleep(delay) should fulfill the promise after |delay|', 85 function() { 86 var isCalled = false; 87 var clock = this.clock; 88 89 base.Promise.sleep(100).then(function(){ 90 isCalled = true; 91 ok(true, 'Promise.sleep() is fulfilled after delay.'); 92 QUnit.start(); 93 }); 94 95 // Tick the clock for 2 seconds and check if the promise is fulfilled. 96 clock.tick(2); 97 98 // Promise fulfillment always occur on a new stack. Therefore, we will run 99 // the verification in a requestAnimationFrame. 100 window.requestAnimationFrame(function(){ 101 ok(!isCalled, 'Promise.sleep() should not be fulfilled prematurely.'); 102 clock.tick(101); 103 }.bind(this)); 104 }); 105 106 QUnit.asyncTest('Promise.negate should fulfill iff the promise does not.', 107 function() { 108 109 base.Promise.negate(Promise.reject()).then( 110 ok.bind(null, true), 111 ok.bind(null, false)); 112 base.Promise.negate(Promise.resolve()).then( 113 ok.bind(null, false), 114 ok.bind(null, true)); 115 window.requestAnimationFrame(function(){ 116 QUnit.start(); 117 }); 118 }); 119 120 module('base.Deferred'); 121 122 QUnit.asyncTest('resolve() should fulfill the underlying promise.', function() { 123 function async() { 124 var deferred = new base.Deferred(); 125 deferred.resolve('bar'); 126 return deferred.promise(); 127 } 128 129 async().then(function(value){ 130 QUnit.equal(value, 'bar'); 131 QUnit.start(); 132 }, function() { 133 QUnit.ok(false, 'The reject handler should not be invoked.'); 134 }); 135 }); 136 137 QUnit.asyncTest('reject() should fail the underlying promise.', function() { 138 function async() { 139 var deferred = new base.Deferred(); 140 deferred.reject('bar'); 141 return deferred.promise(); 142 } 143 144 async().then(function(){ 145 QUnit.ok(false, 'The then handler should not be invoked.'); 146 }, function(value) { 147 QUnit.equal(value, 'bar'); 148 QUnit.start(); 149 }); 150 }); 151 152 153 var source = null; 154 var listener = null; 155 156 module('base.EventSource', { 157 setup: function() { 158 source = new base.EventSource(); 159 source.defineEvents(['foo', 'bar']); 160 listener = sinon.spy(); 161 source.addEventListener('foo', listener); 162 }, 163 teardown: function() { 164 source = null; 165 listener = null; 166 } 167 }); 168 169 test('raiseEvent() should invoke the listener', function() { 170 source.raiseEvent('foo'); 171 sinon.assert.called(listener); 172 }); 173 174 test('raiseEvent() should invoke the listener with the correct event data', 175 function() { 176 var data = { 177 field: 'foo' 178 }; 179 source.raiseEvent('foo', data); 180 sinon.assert.calledWith(listener, data); 181 }); 182 183 test( 184 'raiseEvent() should not invoke listeners that are added during raiseEvent', 185 function() { 186 source.addEventListener('foo', function() { 187 source.addEventListener('foo', function() { 188 ok(false); 189 }); 190 ok(true); 191 }); 192 source.raiseEvent('foo'); 193 }); 194 195 test('raiseEvent() should not invoke listeners of a different event', 196 function() { 197 source.raiseEvent('bar'); 198 sinon.assert.notCalled(listener); 199 }); 200 201 test('raiseEvent() should assert when undeclared events are raised', 202 function() { 203 sinon.spy(base.debug, 'assert'); 204 try { 205 source.raiseEvent('undefined'); 206 } catch (e) { 207 } finally { 208 sinon.assert.called(base.debug.assert); 209 base.debug.assert.restore(); 210 } 211 }); 212 213 test( 214 'removeEventListener() should not invoke the listener in subsequent ' + 215 'calls to |raiseEvent|', 216 function() { 217 source.raiseEvent('foo'); 218 sinon.assert.calledOnce(listener); 219 220 source.removeEventListener('foo', listener); 221 source.raiseEvent('foo'); 222 sinon.assert.calledOnce(listener); 223 }); 224 225 test('removeEventListener() should work even if the listener ' + 226 'is removed during |raiseEvent|', 227 function() { 228 var sink = {}; 229 sink.listener = sinon.spy(function() { 230 source.removeEventListener('foo', sink.listener); 231 }); 232 233 source.addEventListener('foo', sink.listener); 234 source.raiseEvent('foo'); 235 sinon.assert.calledOnce(sink.listener); 236 237 source.raiseEvent('foo'); 238 sinon.assert.calledOnce(sink.listener); 239 }); 240 241 test('encodeUtf8() can encode UTF8 strings', function() { 242 function toJsArray(arrayBuffer) { 243 var result = []; 244 var array = new Uint8Array(arrayBuffer); 245 for (var i = 0; i < array.length; ++i) { 246 result.push(array[i]); 247 } 248 return result; 249 } 250 251 // ASCII. 252 QUnit.deepEqual(toJsArray(base.encodeUtf8("ABC")), [0x41, 0x42, 0x43]); 253 254 // Some arbitrary characters from the basic Unicode plane. 255 QUnit.deepEqual( 256 toJsArray(base.encodeUtf8("")), 257 [/* */ 0xE6, 0x8C, 0x82, /* */ 0xD0, 0x83, /* */ 0xD1, 0x84]); 258 259 // Unicode surrogate pair for U+1F603. 260 QUnit.deepEqual(toJsArray(base.encodeUtf8("")), 261 [0xF0, 0x9F, 0x98, 0x83]); 262 }); 263 264 test('decodeUtf8() can decode UTF8 strings', function() { 265 // ASCII. 266 QUnit.equal(base.decodeUtf8(new Uint8Array([0x41, 0x42, 0x43]).buffer), 267 "ABC"); 268 269 // Some arbitrary characters from the basic Unicode plane. 270 QUnit.equal( 271 base.decodeUtf8( 272 new Uint8Array([/* */ 0xE6, 0x8C, 0x82, 273 /* */ 0xD0, 0x83, 274 /* */ 0xD1, 0x84]).buffer), 275 ""); 276 277 // Unicode surrogate pair for U+1F603. 278 QUnit.equal(base.decodeUtf8(new Uint8Array([0xF0, 0x9F, 0x98, 0x83]).buffer), 279 ""); 280 }); 281 282 })(); 283