Chromium Code Reviews| 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 |
| index c8c02600743cdb4c345d992d4f709891ccfc16f7..0e795d46ae81546505b3ef7244d02327ada34937 100644 |
| --- a/tests/html/js_typed_interop_test.dart |
| +++ b/tests/html/js_typed_interop_test.dart |
| @@ -2,11 +2,13 @@ |
| // 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; |
| +@Js() |
| +library js_typed_interop_test; |
| import 'dart:html'; |
| import 'dart:js'; |
| +import 'package:js/js.dart'; |
| import 'package:unittest/unittest.dart'; |
| import 'package:unittest/html_config.dart'; |
| @@ -14,12 +16,31 @@ _injectJs() { |
| document.body.append(new ScriptElement() |
| ..type = 'text/javascript' |
| ..innerHtml = r""" |
| + var Foo = { |
| + multiplyDefault2: function(a, b) { |
| + if (arguments.length >= 2) return a *b; |
| + return a * 2; |
| + } |
| + }; |
| + |
| 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; } |
| + multiplyBy2: function(arg) { return arg * 2; }, |
| + callClosureWithArg1: function(closure, arg) { |
| + return closure(arg); |
| + }, |
| + callClosureWithArg2: function(closure, arg1, arg2) { |
| + return closure(arg1, arg2); |
| + }, |
| + callClosureWithArgAndThis: function(closure, arg) { |
| + return closure.apply(this, [arg]); |
| + }, |
| + getBar() { |
| + return bar; |
| + } |
| }; |
| var foob = { |
| @@ -30,7 +51,19 @@ _injectJs() { |
| var bar = { |
| x: "foo", |
| - multiplyByX: true |
| + multiplyByX: true, |
| + getFoo() { |
| + return foo; |
| + } |
| + }; |
| + |
| + function ClassWithConstructor(a, b) { |
| + this.a = a; |
| + this.b = b; |
| + }; |
| + |
| + ClassWithConstructor.prototype = { |
| + getA: function() { return this.a;} |
| }; |
| var selection = ["a", "b", "c", foo, bar]; |
| @@ -38,44 +71,111 @@ _injectJs() { |
| """); |
| } |
| -abstract class Foo { |
| - int get x; |
| - set x(int v); |
| - num multiplyByX(num y); |
| - num multiplyBy2(num y); |
| +class RegularClass { |
| + factory RegularClass(a) { |
| + return new RegularClass.fooConstructor(a); |
| + } |
| + RegularClass.fooConstructor(this.a); |
| + var a; |
| +} |
| + |
| +@Js() |
| +class ClassWithConstructor { |
| + external ClassWithConstructor(aParam, bParam); |
| + external getA(); |
| + external get a; |
| + external get b; |
| +} |
| + |
| +@Js() |
| +class Foo extends JavaScriptObject { |
|
alexandre.ardhuin
2015/10/01 08:51:05
I didn't understand quickly why some objects were
|
| + external int get x; |
| + external set x(int v); |
| + external num multiplyByX(num y); |
| + external num multiplyBy2(num y); |
| + external callClosureWithArgAndThis(Function closure, arg); |
| + external callClosureWithArg1(Function closure, arg1); |
| + external callClosureWithArg2(Function closure, arg1, arg2); |
| + external Bar getBar(); |
| + external static int multiplyDefault2(int a, [int b]); |
| + |
| +} |
| + |
| +@Js() |
| +class ExampleLiteral |
| +{ |
| + external factory ExampleLiteral({int x, String y, num z}); |
| + |
| + external int get x; |
| + external String get y; |
| + external num get z; |
| } |
| -abstract class Foob extends Foo { |
| - final String y; |
| +@Js('Foob') |
| +class Foob extends Foo { |
| + external String get y; |
| } |
| -abstract class Bar { |
| - String get x; |
| - bool get multiplyByX; |
| +@Js('Bar') |
| +class Bar extends JavaScriptObject { |
| + external String get x; |
| + external bool get multiplyByX; |
| + external Foo getFoo(); |
| } |
| 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(); |
| +// This class shows the pattern used by APIs like jQuery that add methods |
| +// to array like objects. |
| +abstract class Selection implements JsArray { |
| + external num doubleLength(); |
| } |
| -Foo get foo => context['foo']; |
| -Foob get foob => context['foob']; |
| -Bar get bar => context['bar']; |
| -Selection get selection => context['selection']; |
| +// No @Js is required for these external methods as the library is |
| +// annotated with Js. |
| +external Foo get foo; |
| +external Foob get foob; |
| +external Bar get bar; |
| +external Selection get selection; |
| + |
| +addWithDefault(a, [b = 100]) => a + b; |
| + |
| +@Js("JSON.stringify") |
| +external String stringify(obj); |
| main() { |
| + registerJsInterfaces(); |
| // Call experimental API to register Dart interfaces implemented by |
| // JavaScript classes. |
| - registerJsInterfaces([Foo, Foob, Bar, Selection]); |
| _injectJs(); |
| useHtmlConfiguration(); |
| + group('object literal', () { |
| + test('simple', () { |
| + var l = new ExampleLiteral(x: 3, y: "foo"); |
| + expect(l.x, equals(3)); |
| + expect(l.y, equals("foo")); |
| + expect(l.z, isNull); |
| + expect(stringify(l), equals('{"x":3,"y":"foo"}')); |
| + l = new ExampleLiteral(z: 100); |
| + expect(l.x, isNull); |
| + expect(l.y, isNull); |
| + expect(l.z, equals(100)); |
| + expect(stringify(l), equals('{"z":100}')); |
| + }); |
| + }); |
| + |
| + group('constructor', () { |
| + test('simple', () { |
| + var o = new ClassWithConstructor("foo", "bar"); |
| + expect(o.a, equals("foo")); |
| + expect(o.b, equals("bar")); |
| + expect(o.getA(), equals("foo")); |
| + }); |
| + }); |
| + |
| group('property', () { |
| test('get', () { |
| expect(foo.x, equals(3)); |
| @@ -83,15 +183,15 @@ main() { |
| expect(foob.y, equals("why")); |
| // Exists in JS but not in API. |
| - expect(() => foo.z, throws); |
| + expect(() => (foo as dynamic).zSomeInvalidName, 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); |
| + expect(() => (foob as dynamic).y = "bla", throws); |
| + expect(() => (foo as dynamic).unknownName = 42, throws); |
| }); |
| }); |
| @@ -105,22 +205,74 @@ main() { |
| test('tearoff', () { |
| foo.x = 10; |
| - // TODO(jacobr): should we automatically bind "this" for tearoffs of JS |
| - // objects? |
| - JsFunction multiplyBy2 = foo.multiplyBy2; |
| + Function multiplyBy2 = foo.multiplyBy2; |
| expect(multiplyBy2(5), equals(10)); |
| + Function multiplyByX = foo.multiplyByX; |
| + // Tearing off a JS closure doesn't bind this. |
| + // You will need to use the new method tearoff syntax to bind this. |
| + expect(multiplyByX(4), isNaN); |
| + }); |
| + }); |
| + |
| + group('static method', () { |
| + test('call from dart', () { |
| +/* expect(Foo.multiplyDefault2(6, 7), equals(42)); |
| + expect(Foo.multiplyDefault2(6), equals(12));*/ |
| + Function tearOffMethod = Foo.multiplyDefault2; |
| + expect(tearOffMethod(6, 6), equals(36)); |
| + expect(tearOffMethod(6), equals(12)); |
| + }); |
| + }); |
| + |
| + group('closure', () { |
| + test('call from js', () { |
| + localClosure(x) => x * 10; |
| + var wrappedLocalClosure = allowInterop(localClosure); |
| + expect( |
| + identical(allowInterop(localClosure), wrappedLocalClosure), isTrue); |
| + expect(foo.callClosureWithArg1(wrappedLocalClosure, 10), equals(100)); |
| + expect(foo.callClosureWithArg1(wrappedLocalClosure, "a"), |
| + equals("aaaaaaaaaa")); |
| + expect(foo.callClosureWithArg1(allowInterop(addWithDefault), 10), |
| + equals(110)); |
| + expect(foo.callClosureWithArg2(allowInterop(addWithDefault), 10, 20), |
| + equals(30)); |
| + addThisXAndArg(Foo that, int arg) { |
| + return foo.x + arg; |
| + } |
| + var wrappedCaptureThisClosure = allowInteropCaptureThis(addThisXAndArg); |
| + foo.x = 20; |
| + expect(foo.callClosureWithArgAndThis(wrappedCaptureThisClosure, 10), |
| + equals(30)); |
| + foo.x = 50; |
| + expect(foo.callClosureWithArgAndThis(wrappedCaptureThisClosure, 10), |
| + equals(60)); |
| + expect( |
| + identical(allowInteropCaptureThis(addThisXAndArg), |
| + wrappedCaptureThisClosure), |
| + isTrue); |
| + }); |
| + }); |
| + |
| + group('chain calls', () { |
| + test("method calls", () { |
| + // In dart2js make sure we still use interceptors when making nested |
| + // calls to objects. |
| + var bar = foo.getBar().getFoo().getBar().getFoo().getBar(); |
| + expect(bar.x, equals("foo")); |
| }); |
| }); |
| group('type check', () { |
| test('js interfaces', () { |
| - expect(foo is JsObject, isTrue); |
| - // Cross-casts are allowed. |
| + // Is checks return true for all JavaScript interfaces. |
| expect(foo is Bar, isTrue); |
| - expect(selection is JsArray, isTrue); |
| + expect(foo is Foob, isTrue); |
| + |
| + expect(selection is List, isTrue); |
| // We do know at runtime whether something is a JsArray or not. |
| - expect(foo is JsArray, isFalse); |
| + expect(foo is List, isFalse); |
| }); |
| test('dart interfaces', () { |
| @@ -128,12 +280,4 @@ main() { |
| 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); |
| - }); |
| - }); |
| } |