| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library js_tests; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 import 'dart:html'; | |
| 9 | |
| 10 import 'package:js/js.dart' as js; | |
| 11 import 'package:unittest/unittest.dart'; | |
| 12 import 'package:unittest/html_config.dart'; | |
| 13 | |
| 14 class Foo implements js.Serializable<js.Proxy> { | |
| 15 final js.Proxy _proxy; | |
| 16 | |
| 17 Foo(num a) : this._proxy = new js.Proxy(js.context.Foo, a); | |
| 18 | |
| 19 js.Proxy toJs() => _proxy; | |
| 20 | |
| 21 num get a => _proxy.a; | |
| 22 num bar() => _proxy.bar(); | |
| 23 } | |
| 24 | |
| 25 class Color implements js.Serializable<String> { | |
| 26 static final RED = new Color._("red"); | |
| 27 static final BLUE = new Color._("blue"); | |
| 28 String _value; | |
| 29 Color._(this._value); | |
| 30 String toJs() => this._value; | |
| 31 } | |
| 32 | |
| 33 main() { | |
| 34 useHtmlConfiguration(); | |
| 35 | |
| 36 test('test scope', () { | |
| 37 var ctx; | |
| 38 js.scoped(() { | |
| 39 ctx = js.context; | |
| 40 }); | |
| 41 js.scoped(() { | |
| 42 expect(() => ctx.x, throws); | |
| 43 }); | |
| 44 }); | |
| 45 | |
| 46 test('read global field', () { | |
| 47 expect(js.context.x, equals(42)); | |
| 48 expect(js.context['x'], equals(42)); | |
| 49 expect(() => js.context.y, throwsA(isNoSuchMethodError)); | |
| 50 }); | |
| 51 | |
| 52 test('read global field with underscore', () { | |
| 53 expect(js.context._x, equals(123)); | |
| 54 expect(js.context['_x'], equals(123)); | |
| 55 expect(() => js.context._y, throwsA(isNoSuchMethodError)); | |
| 56 }); | |
| 57 | |
| 58 test('js instantiation : new Foo()', () { | |
| 59 final Foo2 = js.context.container.Foo; | |
| 60 final foo = new js.Proxy(Foo2, 42); | |
| 61 expect(foo.a, 42); | |
| 62 expect(Foo2.b, 38); | |
| 63 }); | |
| 64 | |
| 65 test('js instantiation : new Array()', () { | |
| 66 final a = new js.Proxy(js.context.Array); | |
| 67 expect(a, isNotNull); | |
| 68 expect(a.length, equals(0)); | |
| 69 | |
| 70 a.push("value 1"); | |
| 71 expect(a.length, equals(1)); | |
| 72 expect(a[0], equals("value 1")); | |
| 73 | |
| 74 a.pop(); | |
| 75 expect(a.length, equals(0)); | |
| 76 }); | |
| 77 | |
| 78 test('js instantiation : new Date()', () { | |
| 79 final a = new js.Proxy(js.context.Date); | |
| 80 expect(a.getTime(), isNotNull); | |
| 81 }); | |
| 82 | |
| 83 test('js instantiation : new Date(12345678)', () { | |
| 84 final a = new js.Proxy(js.context.Date, 12345678); | |
| 85 expect(a.getTime(), equals(12345678)); | |
| 86 }); | |
| 87 | |
| 88 test('js instantiation : new Date("December 17, 1995 03:24:00 GMT+01:00")', | |
| 89 () { | |
| 90 final a = new js.Proxy(js.context.Date, | |
| 91 "December 17, 1995 03:24:00 GMT+01:00"); | |
| 92 expect(a.getTime(), equals(819167040000)); | |
| 93 }); | |
| 94 | |
| 95 test('js instantiation : new Date(1995,11,17)', () { | |
| 96 // Note: JS Date counts months from 0 while Dart counts from 1. | |
| 97 final a = new js.Proxy(js.context.Date, 1995, 11, 17); | |
| 98 final b = new DateTime(1995, 12, 17); | |
| 99 expect(a.getTime(), equals(b.millisecondsSinceEpoch)); | |
| 100 }); | |
| 101 | |
| 102 test('js instantiation : new Date(1995,11,17,3,24,0)', () { | |
| 103 // Note: JS Date counts months from 0 while Dart counts from 1. | |
| 104 final a = new js.Proxy.withArgList(js.context.Date, | |
| 105 [1995, 11, 17, 3, 24, 0]); | |
| 106 final b = new DateTime(1995, 12, 17, 3, 24, 0); | |
| 107 expect(a.getTime(), equals(b.millisecondsSinceEpoch)); | |
| 108 }); | |
| 109 | |
| 110 test('js instantiation : new Object()', () { | |
| 111 final a = new js.Proxy(js.context.Object); | |
| 112 expect(a, isNotNull); | |
| 113 | |
| 114 a.attr = "value"; | |
| 115 expect(a.attr, equals("value")); | |
| 116 }); | |
| 117 | |
| 118 test(r'js instantiation : new RegExp("^\w+$")', () { | |
| 119 final a = new js.Proxy(js.context.RegExp, r'^\w+$'); | |
| 120 expect(a, isNotNull); | |
| 121 expect(a.test('true'), isTrue); | |
| 122 expect(a.test(' false'), isFalse); | |
| 123 }); | |
| 124 | |
| 125 test('js instantiation via map notation : new Array()', () { | |
| 126 final a = new js.Proxy(js.context['Array']); | |
| 127 expect(a, isNotNull); | |
| 128 expect(a['length'], equals(0)); | |
| 129 | |
| 130 a['push']("value 1"); | |
| 131 expect(a['length'], equals(1)); | |
| 132 expect(a[0], equals("value 1")); | |
| 133 | |
| 134 a['pop'](); | |
| 135 expect(a['length'], equals(0)); | |
| 136 }); | |
| 137 | |
| 138 test('js instantiation via map notation : new Date()', () { | |
| 139 final a = new js.Proxy(js.context['Date']); | |
| 140 expect(a['getTime'](), isNotNull); | |
| 141 }); | |
| 142 | |
| 143 test('js instantiation : typed array', () { | |
| 144 final codeUnits = "test".codeUnits; | |
| 145 final buf = new js.Proxy(js.context.ArrayBuffer, codeUnits.length); | |
| 146 final bufView = new js.Proxy(js.context.Uint8Array, buf); | |
| 147 for (var i = 0; i < codeUnits.length; i++) { | |
| 148 bufView[i] = codeUnits[i]; | |
| 149 } | |
| 150 }); | |
| 151 | |
| 152 test('write global field', () { | |
| 153 js.context.y = 42; | |
| 154 expect(js.context.y, equals(42)); | |
| 155 expect(js.context['y'], equals(42)); | |
| 156 }); | |
| 157 | |
| 158 test('get JS FunctionProxy', () { | |
| 159 var razzle = js.context.razzle; | |
| 160 expect(razzle(), equals(42)); | |
| 161 }); | |
| 162 | |
| 163 test('call JS function', () { | |
| 164 expect(js.context.razzle(), equals(42)); | |
| 165 expect(() => js.context.dazzle(), throwsA(isNoSuchMethodError)); | |
| 166 }); | |
| 167 | |
| 168 test('call JS function via map notation', () { | |
| 169 expect(js.context['razzle'](), equals(42)); | |
| 170 expect(() => js.context['dazzle'](), throwsA(isNoSuchMethodError)); | |
| 171 }); | |
| 172 | |
| 173 test('call JS function with varargs', () { | |
| 174 expect(js.context.varArgs(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), | |
| 175 equals(55)); | |
| 176 }); | |
| 177 | |
| 178 test('allocate JS object', () { | |
| 179 var foo = new js.Proxy(js.context.Foo, 42); | |
| 180 expect(foo.a, equals(42)); | |
| 181 expect(foo.bar(), equals(42)); | |
| 182 expect(() => foo.baz(), throwsA(isNoSuchMethodError)); | |
| 183 }); | |
| 184 | |
| 185 test('call toString()', () { | |
| 186 var foo = new js.Proxy(js.context.Foo, 42); | |
| 187 expect(foo.toString(), equals("I'm a Foo a=42")); | |
| 188 var container = js.context.container; | |
| 189 expect(container.toString(), equals("[object Object]")); | |
| 190 }); | |
| 191 | |
| 192 test('allocate simple JS array', () { | |
| 193 final list = [1, 2, 3, 4, 5, 6, 7, 8]; | |
| 194 var array = js.array(list); | |
| 195 expect(js.context.isArray(array), isTrue); | |
| 196 expect(array.length, equals(list.length)); | |
| 197 for (var i = 0; i < list.length ; i++) { | |
| 198 expect(array[i], equals(list[i])); | |
| 199 } | |
| 200 }); | |
| 201 | |
| 202 test('allocate JS array with iterable', () { | |
| 203 final set = new Set.from([1, 2, 3, 4, 5, 6, 7, 8]); | |
| 204 var array = js.array(set); | |
| 205 expect(js.context.isArray(array), isTrue); | |
| 206 expect(array.length, equals(set.length)); | |
| 207 for (var i = 0; i < array.length ; i++) { | |
| 208 expect(set.contains(array[i]), isTrue); | |
| 209 } | |
| 210 }); | |
| 211 | |
| 212 test('allocate simple JS map', () { | |
| 213 var map = {'a': 1, 'b': 2, 'c': 3}; | |
| 214 var jsMap = js.map(map); | |
| 215 expect(!js.context.isArray(jsMap), isTrue); | |
| 216 for (final key in map.keys) { | |
| 217 expect(js.context.checkMap(jsMap, key, map[key]), isTrue); | |
| 218 } | |
| 219 }); | |
| 220 | |
| 221 test('allocate complex JS object', () { | |
| 222 final object = | |
| 223 { | |
| 224 'a': [1, [2, 3]], | |
| 225 'b': { | |
| 226 'c': 3, | |
| 227 'd': new js.Proxy(js.context.Foo, 42) | |
| 228 }, | |
| 229 'e': null | |
| 230 }; | |
| 231 var jsObject = js.map(object); | |
| 232 expect(jsObject['a'][0], equals(object['a'][0])); | |
| 233 expect(jsObject['a'][1][0], equals(object['a'][1][0])); | |
| 234 expect(jsObject['a'][1][1], equals(object['a'][1][1])); | |
| 235 expect(jsObject['b']['c'], equals(object['b']['c'])); | |
| 236 expect(jsObject['b']['d'], equals(object['b']['d'])); | |
| 237 expect(jsObject['b']['d'].bar(), equals(42)); | |
| 238 expect(jsObject['e'], isNull); | |
| 239 }); | |
| 240 | |
| 241 test('invoke Dart callback from JS', () { | |
| 242 expect(() => js.context.invokeCallback(), throws); | |
| 243 | |
| 244 js.context.callback = new js.Callback.once(() => 42); | |
| 245 expect(js.context.invokeCallback(), equals(42)); | |
| 246 expect(() => js.context.invokeCallback(), throws); | |
| 247 }); | |
| 248 | |
| 249 test('callback as parameter', () { | |
| 250 expect(js.context.getTypeOf(js.context.razzle), equals("function")); | |
| 251 }); | |
| 252 | |
| 253 test('invoke Dart callback from JS with this', () { | |
| 254 final constructor = new js.Callback.once(($this, arg1) { | |
| 255 $this.a = 42; | |
| 256 $this.b = js.array(["a", arg1]); | |
| 257 }, withThis: true); | |
| 258 var o = new js.Proxy(constructor, "b"); | |
| 259 expect(o.a, equals(42)); | |
| 260 expect(o.b[0], equals("a")); | |
| 261 expect(o.b[1], equals("b")); | |
| 262 }); | |
| 263 | |
| 264 test('invoke Dart callback from JS with 11 parameters', () { | |
| 265 js.context.callbackWith11params = new js.Callback.once((p1, p2, p3, p4, | |
| 266 p5, p6, p7, p8, p9, p10, p11) => '$p1$p2$p3$p4$p5$p6$p7$p8$p9$p10' | |
| 267 '$p11'); | |
| 268 expect(js.context.invokeCallbackWith11params(), equals('1234567891011')); | |
| 269 }); | |
| 270 | |
| 271 test('create a Dart callback outside a scope', () { | |
| 272 // Note, the test framework does not guarantee that each test runs as a | |
| 273 // separate event. This test creates a new asynchronous event and | |
| 274 // ensures that a callback can be created without a scope (i.e., that the | |
| 275 // scope is created on demand). | |
| 276 final subtest = () { | |
| 277 var callback = new js.Callback.once(() => 42); | |
| 278 js.context.callback = callback; | |
| 279 expect(js.context.invokeCallback(), equals(42)); | |
| 280 }; | |
| 281 | |
| 282 scheduleMicrotask(expectAsync0(subtest)); | |
| 283 }); | |
| 284 | |
| 285 test('global scope', () { | |
| 286 var x; | |
| 287 var y; | |
| 288 js.scoped(() { | |
| 289 x = new js.Proxy(js.context.Foo, 42); | |
| 290 y = new js.Proxy(js.context.Foo, 38); | |
| 291 expect(x.a, equals(42)); | |
| 292 expect(y.a, equals(38)); | |
| 293 js.retain(y); | |
| 294 }); | |
| 295 js.scoped(() { | |
| 296 expect(() => x.a, throws); | |
| 297 expect(y.a, equals(38)); | |
| 298 js.release(y); | |
| 299 expect(() => y.a, throws); | |
| 300 }); | |
| 301 }); | |
| 302 | |
| 303 test('global scope for Serializable', () { | |
| 304 Foo x; | |
| 305 Foo y; | |
| 306 js.scoped(() { | |
| 307 x = new Foo(42); | |
| 308 y = new Foo(38); | |
| 309 expect(x.a, equals(42)); | |
| 310 expect(y.a, equals(38)); | |
| 311 js.retain(y); | |
| 312 }); | |
| 313 js.scoped(() { | |
| 314 expect(() => x.a, throws); | |
| 315 expect(y.a, equals(38)); | |
| 316 js.release(y); | |
| 317 expect(() => y.a, throws); | |
| 318 }); | |
| 319 }); | |
| 320 | |
| 321 test('retain and release in the same scope', () { | |
| 322 var x; | |
| 323 js.scoped(() { | |
| 324 x = new js.Proxy(js.context.Foo, 42); | |
| 325 expect(x.a, equals(42)); | |
| 326 js.retain(x); | |
| 327 expect(x.a, equals(42)); | |
| 328 js.release(x); | |
| 329 expect(() => x.a, throws); | |
| 330 }); | |
| 331 js.scoped(() { | |
| 332 expect(() => x.a, throws); | |
| 333 }); | |
| 334 }); | |
| 335 | |
| 336 test('retain and release a function', () { | |
| 337 var razzle; | |
| 338 js.scoped(() { | |
| 339 razzle = js.retain(js.context.razzle); | |
| 340 }); | |
| 341 js.scoped(() { | |
| 342 expect(razzle(), 42); | |
| 343 js.release(razzle); | |
| 344 expect(() => razzle(), throws); | |
| 345 }); | |
| 346 }); | |
| 347 | |
| 348 test('pass unattached Dom Element', () { | |
| 349 final div = new DivElement(); | |
| 350 div.classes.add('a'); | |
| 351 expect(js.context.getElementAttribute(div, 'class'), equals('a')); | |
| 352 }); | |
| 353 | |
| 354 test('pass unattached Dom Element two times on same call', () { | |
| 355 final div = new DivElement(); | |
| 356 div.classes.add('a'); | |
| 357 expect(js.context.addClassAttributes(js.array([div, div])), equals('aa')); | |
| 358 }); | |
| 359 | |
| 360 test('pass Dom Element attached to an unattached element', () { | |
| 361 final div = new DivElement(); | |
| 362 div.classes.add('a'); | |
| 363 final container = new DivElement(); | |
| 364 container.children.add(div); | |
| 365 expect(js.context.getElementAttribute(div, 'class'), equals('a')); | |
| 366 }); | |
| 367 | |
| 368 test('pass 2 Dom Elements attached to an unattached element', () { | |
| 369 final div1 = new DivElement(); | |
| 370 div1.classes.add('a'); | |
| 371 final div2 = new DivElement(); | |
| 372 div2.classes.add('b'); | |
| 373 final container = new DivElement(); | |
| 374 container.children.add(div1); | |
| 375 container.children.add(div2); | |
| 376 final f = js.context.addClassAttributes; | |
| 377 expect(f(js.array([div1, div2])), equals('ab')); | |
| 378 }); | |
| 379 | |
| 380 test('pass multiple Dom Elements unattached to document', () { | |
| 381 // A is alone | |
| 382 // 1 and 3 are brother | |
| 383 // 2 is child of 3 | |
| 384 final divA = new DivElement()..classes.add('A'); | |
| 385 final div1 = new DivElement()..classes.add('1'); | |
| 386 final div2 = new DivElement()..classes.add('2'); | |
| 387 final div3 = new DivElement()..classes.add('3')..children.add(div2); | |
| 388 final container = new DivElement()..children.addAll([div1, div3]); | |
| 389 final f = js.context.addClassAttributes; | |
| 390 expect(f(js.array([divA, div1, div2, div3])), equals('A123')); | |
| 391 expect(f(js.array([divA, div1, div3, div2])), equals('A132')); | |
| 392 expect(f(js.array([divA, div1, div1, div3, divA, div2, div3])), | |
| 393 equals('A113A23')); | |
| 394 expect(!document.documentElement.contains(divA), isTrue); | |
| 395 expect(!document.documentElement.contains(div1), isTrue); | |
| 396 expect(!document.documentElement.contains(div2), isTrue); | |
| 397 expect(!document.documentElement.contains(div3), isTrue); | |
| 398 expect(!document.documentElement.contains(container), isTrue); | |
| 399 }); | |
| 400 | |
| 401 test('pass one Dom Elements unattached and another attached', () { | |
| 402 final div1 = new DivElement()..classes.add('1'); | |
| 403 final div2 = new DivElement()..classes.add('2'); | |
| 404 document.documentElement.children.add(div2); | |
| 405 final f = js.context.addClassAttributes; | |
| 406 expect(f(js.array([div1, div2])), equals('12')); | |
| 407 expect(!document.documentElement.contains(div1), isTrue); | |
| 408 expect(document.documentElement.contains(div2), isTrue); | |
| 409 }); | |
| 410 | |
| 411 test('pass documentElement', () { | |
| 412 expect(js.context.returnElement(document.documentElement), | |
| 413 equals(document.documentElement)); | |
| 414 }); | |
| 415 | |
| 416 test('retrieve unattached Dom Element', () { | |
| 417 var result = js.context.getNewDivElement(); | |
| 418 expect(result is DivElement, isTrue); | |
| 419 expect(!document.documentElement.contains(result), isTrue); | |
| 420 }); | |
| 421 | |
| 422 test('element of foreign document should not be serialized', () { | |
| 423 final foreignDoc = js.context.foreignDoc; | |
| 424 final root = foreignDoc.documentElement; | |
| 425 expect(root is js.Proxy, isTrue); | |
| 426 final element = root.firstChild; | |
| 427 expect(element is js.Proxy, isTrue); | |
| 428 expect(element.getAttribute('id'), equals('abc')); | |
| 429 }); | |
| 430 | |
| 431 test('return a JS proxy to JavaScript', () { | |
| 432 var result = js.context.testJsMap( | |
| 433 new js.Callback.once(() => js.map({ 'value': 42 }))); | |
| 434 expect(result, 42); | |
| 435 }); | |
| 436 | |
| 437 test('dispose a callback', () { | |
| 438 var x = 0; | |
| 439 final callback = new js.Callback.many(() => x++); | |
| 440 js.context.callback = callback; | |
| 441 expect(js.context.invokeCallback(), equals(0)); | |
| 442 expect(js.context.invokeCallback(), equals(1)); | |
| 443 callback.dispose(); | |
| 444 expect(() => js.context.invokeCallback(), throws); | |
| 445 }); | |
| 446 | |
| 447 test('test proxy equality', () { | |
| 448 var foo1 = new js.Proxy(js.context.Foo, 1); | |
| 449 var foo2 = new js.Proxy(js.context.Foo, 2); | |
| 450 js.context.foo = foo1; | |
| 451 js.context.foo = foo2; | |
| 452 expect(foo1, isNot(equals(js.context.foo))); | |
| 453 expect(foo2, equals(js.context.foo)); | |
| 454 }); | |
| 455 | |
| 456 test('test instanceof', () { | |
| 457 var foo = new js.Proxy(js.context.Foo, 1); | |
| 458 expect(js.instanceof(foo, js.context.Foo), isTrue); | |
| 459 expect(js.instanceof(foo, js.context.Object), isTrue); | |
| 460 expect(js.instanceof(foo, js.context.String), isFalse); | |
| 461 }); | |
| 462 | |
| 463 test('test hasProperty', () { | |
| 464 var object = js.map({}); | |
| 465 object.a = 1; | |
| 466 expect(js.hasProperty(object, "a"), isTrue); | |
| 467 expect(js.hasProperty(object, "b"), isFalse); | |
| 468 }); | |
| 469 | |
| 470 test('test deleteProperty', () { | |
| 471 var object = js.map({}); | |
| 472 object.a = 1; | |
| 473 expect(js.context.Object.keys(object).length, 1); | |
| 474 expect(js.context.Object.keys(object)[0], "a"); | |
| 475 js.deleteProperty(object, "a"); | |
| 476 expect(js.context.Object.keys(object).length, 0); | |
| 477 }); | |
| 478 | |
| 479 test('test index get and set', () { | |
| 480 final myArray = js.context.myArray; | |
| 481 expect(myArray.length, equals(1)); | |
| 482 expect(myArray[0], equals("value1")); | |
| 483 myArray[0] = "value2"; | |
| 484 expect(myArray.length, equals(1)); | |
| 485 expect(myArray[0], equals("value2")); | |
| 486 | |
| 487 final foo = new js.Proxy(js.context.Foo, 1); | |
| 488 foo["getAge"] = new js.Callback.once(() => 10); | |
| 489 expect(foo.getAge(), equals(10)); | |
| 490 }); | |
| 491 | |
| 492 test('test experimental apis', () { | |
| 493 var depth = js.$experimentalEnterScope(); | |
| 494 expect(js.context.x, equals(42)); | |
| 495 js.$experimentalExitScope(depth); | |
| 496 }); | |
| 497 | |
| 498 test('access a property of a function', () { | |
| 499 expect(js.context.Bar(), "ret_value"); | |
| 500 expect(js.context.Bar.foo, "property_value"); | |
| 501 }); | |
| 502 | |
| 503 test('retrieve same dart Object', () { | |
| 504 final date = new DateTime.now(); | |
| 505 js.context.dartDate = date; | |
| 506 expect(js.context.dartDate, equals(date)); | |
| 507 }); | |
| 508 | |
| 509 test('usage of Serializable', () { | |
| 510 final red = Color.RED; | |
| 511 js.context.color = red; | |
| 512 expect(js.context.color, equals(red._value)); | |
| 513 }); | |
| 514 | |
| 515 test('check for leaks', () { | |
| 516 // Verify that the number of live objects is zero. | |
| 517 final verifyNoLeaks = expectAsync0(() => expect(0, js.proxyCount())); | |
| 518 // Run this check asychnronously to ensure that any current scope is | |
| 519 // cleared first. | |
| 520 scheduleMicrotask(verifyNoLeaks); | |
| 521 }); | |
| 522 } | |
| OLD | NEW |