Index: tests/html/js_typed_interop_test.dart |
diff --git a/tests/html/js_typed_interop_test.dart b/tests/html/js_typed_interop_test.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c8c02600743cdb4c345d992d4f709891ccfc16f7 |
--- /dev/null |
+++ b/tests/html/js_typed_interop_test.dart |
@@ -0,0 +1,139 @@ |
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+library jsArrayTest; |
+ |
+import 'dart:html'; |
+import 'dart:js'; |
+ |
+import 'package:unittest/unittest.dart'; |
+import 'package:unittest/html_config.dart'; |
+ |
+_injectJs() { |
+ document.body.append(new ScriptElement() |
+ ..type = 'text/javascript' |
+ ..innerHtml = r""" |
+ var foo = { |
+ x: 3, |
+ z: 40, // Not specified in typed Dart API so should fail in checked mode. |
+ multiplyByX: function(arg) { return arg * this.x; }, |
+ // This function can be torn off without having to bind this. |
+ multiplyBy2: function(arg) { return arg * 2; } |
+ }; |
+ |
+ var foob = { |
+ x: 8, |
+ y: "why", |
+ multiplyByX: function(arg) { return arg * this.x; } |
+ }; |
+ |
+ var bar = { |
+ x: "foo", |
+ multiplyByX: true |
+ }; |
+ |
+ var selection = ["a", "b", "c", foo, bar]; |
+ selection.doubleLength = function() { return this.length * 2; }; |
+"""); |
+} |
+ |
+abstract class Foo { |
+ int get x; |
+ set x(int v); |
+ num multiplyByX(num y); |
+ num multiplyBy2(num y); |
+} |
+ |
+abstract class Foob extends Foo { |
+ final String y; |
+} |
+ |
+abstract class Bar { |
+ String get x; |
+ bool get multiplyByX; |
+} |
+ |
+class Baz {} |
+ |
+// This class shows the pattern used by APIs such as jQuery that add methods |
+// to Arrays. |
+abstract class Selection implements List { |
+ num doubleLength(); |
+} |
+ |
+Foo get foo => context['foo']; |
+Foob get foob => context['foob']; |
+Bar get bar => context['bar']; |
+Selection get selection => context['selection']; |
+ |
+main() { |
+ // Call experimental API to register Dart interfaces implemented by |
+ // JavaScript classes. |
+ registerJsInterfaces([Foo, Foob, Bar, Selection]); |
+ |
+ _injectJs(); |
+ |
+ useHtmlConfiguration(); |
+ |
+ group('property', () { |
+ test('get', () { |
+ expect(foo.x, equals(3)); |
+ expect(foob.x, equals(8)); |
+ expect(foob.y, equals("why")); |
+ |
+ // Exists in JS but not in API. |
+ expect(() => foo.z, throws); |
+ expect(bar.multiplyByX, isTrue); |
+ }); |
+ test('set', () { |
+ foo.x = 42; |
+ expect(foo.x, equals(42)); |
+ // Property tagged as read only in typed API. |
+ expect(() => foob.y = "bla", throws); |
+ expect(() => foo.unknownName = 42, throws); |
+ }); |
+ }); |
+ |
+ group('method', () { |
+ test('call', () { |
+ foo.x = 100; |
+ expect(foo.multiplyByX(4), equals(400)); |
+ foob.x = 10; |
+ expect(foob.multiplyByX(4), equals(40)); |
+ }); |
+ |
+ test('tearoff', () { |
+ foo.x = 10; |
+ // TODO(jacobr): should we automatically bind "this" for tearoffs of JS |
+ // objects? |
+ JsFunction multiplyBy2 = foo.multiplyBy2; |
+ expect(multiplyBy2(5), equals(10)); |
+ }); |
+ }); |
+ |
+ group('type check', () { |
+ test('js interfaces', () { |
+ expect(foo is JsObject, isTrue); |
+ // Cross-casts are allowed. |
+ expect(foo is Bar, isTrue); |
+ expect(selection is JsArray, isTrue); |
+ |
+ // We do know at runtime whether something is a JsArray or not. |
+ expect(foo is JsArray, isFalse); |
+ }); |
+ |
+ test('dart interfaces', () { |
+ expect(foo is Function, isFalse); |
+ expect(selection is List, isTrue); |
+ }); |
+ }); |
+ |
+ group("registration", () { |
+ test('repeated fails', () { |
+ // The experimental registerJsInterfaces API has already been called so |
+ // it cannot be called a second time. |
+ expect(() => registerJsInterfaces([Baz]), throws); |
+ }); |
+ }); |
+} |