| // Copyright (c) 2022, 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. |
| |
| // TODO(srujzs): Break this test up into multiple tests, delete redundant tests, |
| // and move them to a shared library. |
| |
| import 'dart:js_util'; |
| import 'package:expect/expect.dart'; |
| import 'package:js/js.dart'; |
| import 'static_interop_library.dart'; |
| |
| @JS() |
| external void eval(String code); |
| |
| @JS('JSClass') |
| @staticInterop |
| class StaticJSClass { |
| external factory StaticJSClass.factory(String foo); |
| |
| external static String externalStaticMethod(); |
| static String staticMethod() => 'bar'; |
| } |
| |
| extension StaticJSClassMethods on StaticJSClass { |
| external String foo; |
| external String sum(String a, String? b, String c); |
| external set nonNullableInt(int d); |
| external int get nonNullableInt; |
| external set nullableInt(int? d); |
| external int? get nullableInt; |
| external int nonNullableIntReturnMethod(); |
| external int? nullableIntReturnMethod(bool returnNull); |
| external String doSum1Or2(String a, [String? b]); |
| external String doSumUpTo2([String? a, String? b]); |
| external String doSum1Or2NonNull(String a, [String b = 'bar']); |
| external String doSumUpTo2NonNull([String a = 'foo', String b = 'bar']); |
| external int doIntSum1Or2(int a, [int? b]); |
| external int doIntSumUpTo2([int? a, int? b]); |
| external int doIntSum1Or2NonNull(int a, [int b = 2]); |
| external int doIntSumUpTo2NonNull([int a = 1, int b = 2]); |
| |
| @JS('nameInJSMethod') |
| external String nameInDartMethod(String a, String b); |
| @JS('nameInJSGetter') |
| external String get nameInDartGetter; |
| @JS('nameInJSSetter') |
| external set nameInDartSetter(String v); |
| external String get nameInJSSetter; |
| } |
| |
| void createClassTest() { |
| eval(r''' |
| globalThis.JSClass = function(foo) { |
| this.foo = foo; |
| this.nonNullableInt = 6; |
| this.nameInJSGetter = 'foo'; |
| this.nonNullableIntReturnMethod = function() { |
| return 7; |
| } |
| this.nullableIntReturnMethod = function(returnNull) { |
| if (returnNull) { |
| return null; |
| } else { |
| return 8; |
| } |
| } |
| this.sum = function(a, b, c) { |
| if (b == null) b = ' '; |
| return a + b + c; |
| } |
| this.doSum1Or2 = function(a, b) { |
| return a + (b ?? 'bar'); |
| } |
| this.doSumUpTo2 = function(a, b) { |
| return (a ?? 'foo') + (b ?? 'bar'); |
| } |
| this.doSum1Or2NonNull = function(a, b) { |
| return (a ?? 'foo') + (b ?? 'bar'); |
| } |
| this.doSumUpTo2NonNull = function(a, b) { |
| return (a ?? 'foo') + (b ?? 'bar'); |
| } |
| this.doIntSum1Or2 = function(a, b) { |
| return a + (b ?? 2); |
| } |
| this.doIntSumUpTo2 = function(a, b) { |
| return (a ?? 1) + (b ?? 2); |
| } |
| this.doIntSum1Or2NonNull = function(a, b) { |
| return a + (b ?? 2); |
| } |
| this.doIntSumUpTo2NonNull = function(a, b) { |
| return (a ?? 1) + (b ?? 2); |
| } |
| this.nameInJSMethod = function(a, b) { |
| return a + b; |
| } |
| } |
| globalThis.JSClass.externalStaticMethod = function() { |
| return 'foo'; |
| } |
| |
| globalThis.library = function() {} |
| globalThis.library.libraryTopLevelGetter = 'foo'; |
| globalThis.library.jsedLibraryTopLevelGetter = 'foo'; |
| globalThis.library.NamespacedClass = function() { |
| this.member = function() { |
| return 'foo'; |
| } |
| } |
| '''); |
| Expect.equals('foo', StaticJSClass.externalStaticMethod()); |
| Expect.equals('bar', StaticJSClass.staticMethod()); |
| |
| final foo = StaticJSClass.factory('foo'); |
| Expect.equals('foo', foo.foo); |
| foo.foo = 'bar'; |
| Expect.equals('bar', foo.foo); |
| Expect.equals('hello world!!', foo.sum('hello', null, 'world!!')); |
| Expect.equals(null, foo.nullableInt); |
| foo.nullableInt = 5; |
| Expect.equals(5, foo.nullableInt); |
| Expect.equals(6, foo.nonNullableInt); |
| foo.nonNullableInt = 16; |
| Expect.equals(16, foo.nonNullableInt); |
| Expect.equals(7, foo.nonNullableIntReturnMethod()); |
| Expect.equals(8, foo.nullableIntReturnMethod(false)); |
| Expect.equals(null, foo.nullableIntReturnMethod(true)); |
| |
| Expect.equals('foobar', foo.doSum1Or2('foo')); |
| Expect.equals('foobar', foo.doSum1Or2('foo', 'bar')); |
| Expect.equals('foobar', foo.doSumUpTo2()); |
| Expect.equals('foobar', foo.doSumUpTo2('foo')); |
| Expect.equals('foobar', foo.doSumUpTo2('foo', 'bar')); |
| |
| Expect.equals('foobar', foo.doSum1Or2NonNull('foo')); |
| Expect.equals('foobar', foo.doSum1Or2NonNull('foo', 'bar')); |
| Expect.equals('foobar', foo.doSumUpTo2NonNull()); |
| Expect.equals('foobar', foo.doSumUpTo2NonNull('foo')); |
| Expect.equals('foobar', foo.doSumUpTo2NonNull('foo', 'bar')); |
| |
| Expect.equals(3, foo.doIntSum1Or2(1)); |
| Expect.equals(3, foo.doIntSum1Or2(1, 2)); |
| Expect.equals(3, foo.doIntSumUpTo2()); |
| Expect.equals(3, foo.doIntSumUpTo2(1)); |
| Expect.equals(3, foo.doIntSumUpTo2(1, 2)); |
| |
| Expect.equals(3, foo.doIntSum1Or2NonNull(1)); |
| Expect.equals(3, foo.doIntSum1Or2NonNull(1, 2)); |
| Expect.equals(3, foo.doIntSumUpTo2NonNull()); |
| Expect.equals(3, foo.doIntSumUpTo2NonNull(1)); |
| Expect.equals(3, foo.doIntSumUpTo2NonNull(1, 2)); |
| |
| Expect.equals('foobar', foo.nameInDartMethod('foo', 'bar')); |
| Expect.equals('foo', foo.nameInDartGetter); |
| foo.nameInDartSetter = 'boo'; |
| Expect.equals('boo', foo.nameInJSSetter); |
| |
| Expect.equals('foo', NamespacedClass().member()); |
| Expect.equals('foo', libraryTopLevelGetter); |
| Expect.equals('foo', libraryOtherTopLevelGetter); |
| } |
| |
| @JS('JSClass.NestedJSClass') |
| @staticInterop |
| class NestedJSClass { |
| external factory NestedJSClass.factory(String foo); |
| } |
| |
| extension NestedJSClassMethods on NestedJSClass { |
| external String foo; |
| } |
| |
| void createClassWithNestedJSNameTest() { |
| eval(r''' |
| globalThis.JSClass = {}; |
| globalThis.JSClass.NestedJSClass = function(foo) { |
| this.foo = foo; |
| }; |
| '''); |
| final foo = NestedJSClass.factory('foo'); |
| Expect.equals(foo.foo, 'foo'); |
| } |
| |
| @JS('JSParent') |
| @staticInterop |
| class StaticJSParent { |
| external factory StaticJSParent.factory(); |
| } |
| |
| extension StaticJSParentMethods on StaticJSParent { |
| external set child(StaticJSChild child); |
| external String childsFoo(); |
| } |
| |
| @JS('JSChild') |
| @staticInterop |
| class StaticJSChild { |
| external factory StaticJSChild.factory(); |
| } |
| |
| extension StaticJSChildMethods on StaticJSChild { |
| external set foo(String s); |
| } |
| |
| void setInteropPropertyTest() { |
| eval(r''' |
| globalThis.JSParent = function() { |
| this.child = null; |
| this.childsFoo = () => { |
| return this.child.foo; |
| } |
| } |
| |
| globalThis.JSChild = function() { |
| this.foo = null; |
| } |
| '''); |
| |
| final parent = StaticJSParent.factory(); |
| final child = StaticJSChild.factory(); |
| parent.child = child; |
| child.foo = 'boo'; |
| Expect.equals('boo', parent.childsFoo()); |
| } |
| |
| class DartObject { |
| final int x; |
| |
| DartObject(this.x); |
| } |
| |
| @JS('JSHolder') |
| @staticInterop |
| class StaticJSHolder { |
| external factory StaticJSHolder.factory(DartObject object); |
| } |
| |
| extension StaticJSHolderMethods on StaticJSHolder { |
| external DartObject foo; |
| } |
| |
| void setDartObjectPropertyTest() { |
| eval(r''' |
| globalThis.JSHolder = function(foo) { |
| this.foo = foo; |
| } |
| '''); |
| final traveler = DartObject(4); |
| final holder = StaticJSHolder.factory(traveler); |
| Expect.equals(traveler, holder.foo); |
| } |
| |
| @JS() |
| external String get foo; |
| |
| @JS() |
| external String? get blu; |
| |
| @JS('') |
| external void set baz(String); |
| |
| @JS('boo.bar') |
| external String get bam; |
| |
| @JS('bar') |
| external String fooBar(String); |
| |
| void topLevelMethodsTest() { |
| eval(r''' |
| globalThis.foo = 'bar'; |
| globalThis.baz = null; |
| globalThis.boo = { |
| 'bar': 'jam' |
| } |
| globalThis.bar = function(string) { |
| return string + ' ' + globalThis.baz; |
| } |
| '''); |
| |
| Expect.equals(foo, 'bar'); |
| Expect.equals(blu, null); |
| Expect.equals(bam, 'jam'); |
| baz = 'world!'; |
| Expect.equals(fooBar('hello'), 'hello world!'); |
| } |
| |
| @JS() |
| @anonymous |
| @staticInterop |
| class AnonymousJSClass { |
| external factory AnonymousJSClass.factory( |
| {String? foo, |
| String bar = 'baz', |
| String? bleep, |
| int? goo, |
| int ooo = 1, |
| List<double>? saz, |
| List<double> zoo = const [1.0, 2.0]}); |
| } |
| |
| extension AnonymousJSClassExtension on AnonymousJSClass { |
| external String? get foo; |
| external String? get bar; |
| external String? get bleep; |
| external int? get goo; |
| external int? get ooo; |
| external List<Object?>? saz; |
| external List<double>? zoo; |
| } |
| |
| void anonymousTest() { |
| final anonymousJSClass = AnonymousJSClass.factory( |
| foo: 'boo', bleep: 'bleep', saz: const [1.0, 2.0], goo: 0); |
| Expect.equals('boo', anonymousJSClass.foo); |
| Expect.equals(null, anonymousJSClass.bar); |
| Expect.equals('bleep', anonymousJSClass.bleep); |
| Expect.equals(0, anonymousJSClass.goo); |
| Expect.equals(null, anonymousJSClass.ooo); |
| Expect.listEquals(const [1.0, 2.0], anonymousJSClass.saz!); |
| Expect.equals(null, anonymousJSClass.zoo); |
| } |
| |
| @JS() |
| @anonymous |
| @staticInterop |
| class AnonymousRedirectJSClass { |
| external factory AnonymousRedirectJSClass._({String Function(String)? foo}); |
| |
| factory AnonymousRedirectJSClass.concrete(String Function(String) foo) => |
| AnonymousRedirectJSClass._(foo: allowInterop(foo)); |
| } |
| |
| extension AnonymousRedirectJSClassExtension on AnonymousRedirectJSClass { |
| external String foo(String bar); |
| } |
| |
| void concreteFactoryConstructorTest() { |
| final anonymousRedirectJSClass = |
| AnonymousRedirectJSClass.concrete((String bar) => bar + bar); |
| Expect.equals('foofoo', anonymousRedirectJSClass.foo('foo')); |
| } |
| |
| @JS() |
| @staticInterop |
| class JSArray {} |
| |
| extension JSArrayExtension on JSArray { |
| external Object? operator [](int index); |
| external int get length; |
| external String get deoptKey; |
| } |
| |
| @JS() |
| external JSArray get arrayObject; |
| |
| void staticInteropBypassConversionTest() { |
| eval(r''' |
| globalThis.arrayObject = ['1', '2']; |
| globalThis.arrayObject.deoptKey = 'foo'; |
| '''); |
| |
| JSArray a = arrayObject; |
| Expect.isTrue(instanceOfString(a, "Array")); |
| Expect.equals(2, a.length); |
| Expect.equals('foo', a.deoptKey); |
| } |
| |
| void main() { |
| createClassTest(); |
| createClassWithNestedJSNameTest(); |
| setInteropPropertyTest(); |
| setDartObjectPropertyTest(); |
| topLevelMethodsTest(); |
| anonymousTest(); |
| concreteFactoryConstructorTest(); |
| staticInteropBypassConversionTest(); |
| } |