| // Copyright (c) 2021, 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. |
| |
| // @dart = 2.9 |
| |
| // Tests the functionality of object properties with the js_util library. For |
| // js_util tests with HTML objects see tests/lib/html/js_util_test.dart. |
| |
| @JS() |
| library js_util_properties_test; |
| |
| import 'package:js/js.dart'; |
| import 'package:js/js_util.dart' as js_util; |
| import 'package:expect/minitest.dart'; |
| |
| @JS() |
| external String jsFunction(); |
| |
| @JS() |
| external void eval(String code); |
| |
| @JS('JSON.stringify') |
| external String stringify(o); |
| |
| @JS('ArrayBuffer') |
| external get JSArrayBufferType; |
| |
| @JS() |
| class Foo { |
| external Foo(num a); |
| |
| external num get a; |
| external num bar(); |
| external Object get objectProperty; |
| external List get list; |
| } |
| |
| @JS('Foo') |
| external get JSFooType; |
| |
| String dartFunction() { |
| return 'Dart Function'; |
| } |
| |
| @JS() |
| @anonymous |
| class ExampleTypedLiteral { |
| external factory ExampleTypedLiteral({a, b}); |
| |
| external get a; |
| external get b; |
| } |
| |
| class ExampleTearoff { |
| int x = 3; |
| foo() => x; |
| } |
| |
| String _getBarWithSideEffect() { |
| var x = 5; |
| expect(x, equals(5)); |
| return 'bar'; |
| } |
| |
| main() { |
| eval(r""" |
| function Foo(a) { |
| this.a = a; |
| } |
| |
| Foo.b = 38; |
| |
| Foo.prototype.list = [2, 4, 6]; |
| |
| Foo.prototype.bar = function() { |
| return this.a; |
| } |
| |
| Foo.prototype.toString = function() { |
| return "I'm a Foo a=" + this.a; |
| } |
| |
| Foo.prototype.fnList = [Foo.prototype.bar, Foo.prototype.toString]; |
| |
| Foo.prototype.objectProperty = { |
| 'c': 1, |
| 'list': [10, 20, 30], |
| 'functionProperty': function() { return 'Function Property'; } |
| } |
| |
| function jsFunction() { |
| return "JS Function"; |
| } |
| |
| Foo.prototype.nestedFunction = function() { |
| return function() { |
| return 'Nested Function'; |
| }; |
| } |
| |
| Foo.prototype.getFirstEl = function(list) { |
| return list[0]; |
| } |
| |
| Foo.prototype.sumFn = function(a, b) { |
| return a + b; |
| } |
| |
| Foo.prototype.getA = function(obj) { |
| return obj.a; |
| } |
| |
| Foo.prototype.callFn = function(fn) { |
| return fn(); |
| } |
| """); |
| |
| group('newObject', () { |
| test('create', () { |
| expect(identical(js_util.newObject(), js_util.newObject()), isFalse); |
| }); |
| |
| test('callMethod', () { |
| var o = js_util.newObject(); |
| expect(js_util.callMethod(o, 'toString', []), equals('[object Object]')); |
| expect(stringify(o), equals('{}')); |
| }); |
| |
| test('properties', () { |
| var o = js_util.newObject(); |
| expect(js_util.hasProperty(o, 'foo bar'), isFalse); |
| expect(js_util.hasProperty(o, 'toString'), isTrue); |
| expect(js_util.callMethod(o, 'hasOwnProperty', ['toString']), isFalse); |
| expect(js_util.callMethod(o, 'hasOwnProperty', ['foo bar']), isFalse); |
| js_util.setProperty(o, 'foo bar', 42); |
| expect(js_util.callMethod(o, 'hasOwnProperty', ['foo bar']), isTrue); |
| expect(js_util.getProperty(o, 'foo bar'), equals(42)); |
| expect(js_util.hasProperty(o, 'foo bar'), isTrue); |
| expect(stringify(o), equals('{"foo bar":42}')); |
| }); |
| |
| test('nested properties calls', () { |
| var o = js_util.newObject(); |
| var f = new Foo(42); |
| js_util.setProperty(o, 'foo', f); |
| var foo = js_util.getProperty(o, 'foo'); |
| expect(foo, equals(f)); |
| expect(js_util.hasProperty(foo, 'a'), isTrue); |
| expect(js_util.getProperty(foo, 'a'), equals(42)); |
| js_util.setProperty(foo, 'a', 24); |
| expect(js_util.getProperty(foo, 'a'), equals(24)); |
| }); |
| }); |
| |
| group('hasProperty', () { |
| test('typed object', () { |
| var f = new Foo(42); |
| expect(js_util.hasProperty(f, 'a'), isTrue); |
| expect(js_util.hasProperty(f, 'bar'), isTrue); |
| expect(js_util.hasProperty(f, 'b'), isFalse); |
| expect(js_util.hasProperty(f, 'toString'), isTrue); |
| js_util.setProperty(f, '__proto__', null); |
| expect(js_util.hasProperty(f, 'toString'), isFalse); |
| }); |
| |
| test('typed literal', () { |
| var literal = new ExampleTypedLiteral(a: 'x', b: 42); |
| expect(js_util.hasProperty(literal, 'a'), isTrue); |
| expect(js_util.hasProperty(literal, 'b'), isTrue); |
| expect(js_util.hasProperty(literal, 'anything'), isFalse); |
| |
| literal = new ExampleTypedLiteral(a: null); |
| expect(js_util.hasProperty(literal, 'a'), isTrue); |
| expect(js_util.hasProperty(literal, 'b'), isFalse); |
| expect(js_util.hasProperty(literal, 'anything'), isFalse); |
| }); |
| |
| test('complex hasProperty calls', () { |
| var f = new Foo(42); |
| expect(js_util.hasProperty(f.objectProperty, 'c'), isTrue); |
| expect(js_util.hasProperty(f.objectProperty, 'nonexistent'), isFalse); |
| |
| // Using a variable for the property name. |
| String propertyName = 'bar'; |
| expect(js_util.hasProperty(f, propertyName), isTrue); |
| }); |
| }); |
| |
| group('getProperty', () { |
| test('typed object', () { |
| var f = new Foo(42); |
| expect(js_util.getProperty(f, 'a'), equals(42)); |
| expect(js_util.getProperty(f, 'b'), isNull); |
| expect(js_util.getProperty(f, 'bar') is Function, isTrue); |
| expect(js_util.getProperty(f, 'list') is List, isTrue); |
| expect(js_util.getProperty(f, 'objectProperty') is Object, isTrue); |
| expect(js_util.getProperty(f, 'toString') is Function, isTrue); |
| js_util.setProperty(f, '__proto__', null); |
| expect(js_util.getProperty(f, 'toString'), isNull); |
| }); |
| |
| test('typed literal', () { |
| var literal = new ExampleTypedLiteral(a: 'x', b: 42); |
| expect(js_util.getProperty(literal, 'a'), equals('x')); |
| expect(js_util.getProperty(literal, 'b'), equals(42)); |
| expect(js_util.getProperty(literal, 'anything'), isNull); |
| }); |
| |
| test('complex getProperty calls', () { |
| var f = new Foo(42); |
| |
| // Accessing a method property. |
| expect(js_util.getProperty(f, 'bar') is Function, isTrue); |
| expect(js_util.callMethod(f, 'bar', []), equals(42)); |
| |
| // Accessing list properties. |
| expect(js_util.getProperty(f, 'list')[0], equals(2)); |
| expect(js_util.getProperty(f, 'fnList')[0] is Function, isTrue); |
| expect( |
| js_util.callMethod( |
| js_util.getProperty(f, 'fnList')[0], 'apply', [f, []]), |
| equals(42)); |
| expect(js_util.getProperty(f.list, "0"), equals(2)); |
| var index = 0; |
| expect(js_util.getProperty(f.list, index++), equals(2)); |
| expect(index, equals(1)); |
| expect(js_util.getProperty(f.list, index), equals(4)); |
| |
| // Accessing nested object properites. |
| var objectProperty = js_util.getProperty(f, 'objectProperty'); |
| expect(js_util.getProperty(objectProperty, 'c'), equals(1)); |
| expect(js_util.getProperty(objectProperty, 'list') is List, isTrue); |
| expect(js_util.getProperty(objectProperty, 'list')[1], equals(20)); |
| expect( |
| js_util.getProperty(objectProperty, 'functionProperty') is Function, |
| isTrue); |
| // Using nested getProperty calls. |
| expect( |
| js_util.getProperty( |
| js_util.getProperty( |
| js_util.getProperty(f, 'objectProperty'), 'list'), |
| 1), |
| equals(20)); |
| |
| // Using a variable for the property name. |
| String propertyName = 'a'; |
| expect(js_util.getProperty(f, propertyName), equals(42)); |
| String bar = _getBarWithSideEffect(); |
| expect(js_util.getProperty(f, bar) is Function, isTrue); |
| expect(js_util.callMethod(f, bar, []), equals(42)); |
| }); |
| }); |
| |
| group('setProperty', () { |
| test('typed object', () { |
| var f = new Foo(42); |
| expect(js_util.getProperty(f, 'a'), equals(42)); |
| js_util.setProperty(f, 'a', 100); |
| expect(f.a, equals(100)); |
| expect(js_util.getProperty(f, 'a'), equals(100)); |
| js_util.setProperty(f, 'a', null); |
| expect(f.a, equals(null)); |
| |
| expect(js_util.getProperty(f, 'list') is List, isTrue); |
| js_util.setProperty(f, 'list', [8]); |
| expect(js_util.getProperty(f, 'list') is List, isTrue); |
| expect(js_util.getProperty(f, 'list')[0], equals(8)); |
| |
| js_util.setProperty(f, 'newProperty', 'new'); |
| expect(js_util.getProperty(f, 'newProperty'), equals('new')); |
| |
| // Using a variable for the property value. |
| var num = 4; |
| js_util.setProperty(f, 'a', num); |
| expect(f.a, equals(num)); |
| var str = 'bar'; |
| js_util.setProperty(f, 'a', str); |
| expect(f.a, equals(str)); |
| var b = false; |
| js_util.setProperty(f, 'a', b); |
| expect(f.a, equals(b)); |
| var list = [2, 4, 6]; |
| js_util.setProperty(f, 'a', list); |
| expect(f.a, equals(list)); |
| var fn = allowInterop(dartFunction); |
| js_util.setProperty(f, 'a', fn); |
| expect(f.a, equals(fn)); |
| }); |
| |
| test('typed literal', () { |
| var literal = new ExampleTypedLiteral(); |
| js_util.setProperty(literal, 'a', 'foo'); |
| expect(js_util.getProperty(literal, 'a'), equals('foo')); |
| expect(literal.a, equals('foo')); |
| js_util.setProperty(literal, 'a', literal); |
| expect(identical(literal.a, literal), isTrue); |
| var list = ['arr']; |
| js_util.setProperty(literal, 'a', list); |
| expect(identical(literal.a, list), isTrue); |
| }); |
| |
| test('complex setProperty calls', () { |
| var f = new Foo(42); |
| |
| // Set function property to a num. |
| expect(js_util.getProperty(f, 'bar') is Function, isTrue); |
| js_util.setProperty(f, 'bar', 5); |
| expect(js_util.getProperty(f, 'bar') is Function, isFalse); |
| expect(js_util.getProperty(f, 'bar'), equals(5)); |
| |
| // Set property to a Dart function. |
| js_util.setProperty(f, 'bar', allowInterop(dartFunction)); |
| expect(js_util.getProperty(f, 'bar') is Function, isTrue); |
| expect(js_util.callMethod(f, 'bar', []), equals('Dart Function')); |
| js_util.setProperty(f, 'bar', allowInterop(() { |
| return 'Inline'; |
| })); |
| expect(js_util.callMethod(f, 'bar', []), equals('Inline')); |
| |
| // Set property to a JS function. |
| js_util.setProperty(f, 'bar', allowInterop(jsFunction)); |
| expect(js_util.getProperty(f, 'bar') is Function, isTrue); |
| expect(js_util.callMethod(f, 'bar', []), equals('JS Function')); |
| |
| // Set property with nested object properties. |
| js_util.setProperty(f.objectProperty, 'c', 'new val'); |
| expect(js_util.getProperty(f.objectProperty, 'c'), equals('new val')); |
| js_util.setProperty(f.objectProperty, 'list', [1, 2, 3]); |
| expect(js_util.getProperty(f.objectProperty, 'list')[1], equals(2)); |
| // Using a nested getProperty call. |
| js_util.setProperty( |
| js_util.getProperty(f, 'objectProperty'), 'c', 'nested val'); |
| expect(js_util.getProperty(f.objectProperty, 'c'), equals('nested val')); |
| |
| // Using a variable for the property name. |
| String propertyName = 'bar'; |
| js_util.setProperty(f, propertyName, 'foo'); |
| expect(js_util.getProperty(f, 'bar'), equals('foo')); |
| String bar = _getBarWithSideEffect(); |
| js_util.setProperty(f, bar, 'baz'); |
| expect(js_util.getProperty(f, bar), equals('baz')); |
| js_util.setProperty(f, _getBarWithSideEffect(), 'mumble'); |
| expect(js_util.getProperty(f, bar), equals('mumble')); |
| |
| // Set property to a function call. |
| js_util.setProperty(f, 'a', dartFunction()); |
| String expected = dartFunction(); |
| expect(f.a, equals(expected)); |
| |
| // Using a tearoff as the property value |
| js_util.setProperty(f, 'tearoff', allowInterop(ExampleTearoff().foo)); |
| expect(js_util.callMethod(f, 'tearoff', []), equals(3)); |
| }); |
| }); |
| |
| group('callMethod', () { |
| test('typed object', () { |
| var f = new Foo(42); |
| expect(js_util.callMethod(f, 'bar', []), equals(42)); |
| }); |
| |
| test('complex callMethod calls', () { |
| var f = new Foo(42); |
| |
| // Call a method that returns an unbound function. |
| expect(js_util.callMethod(f, 'nestedFunction', []) is Function, isTrue); |
| expect(js_util.callMethod(f, 'nestedFunction', [])(), |
| equals('Nested Function')); |
| |
| // Call method on a nested function property. |
| expect(js_util.callMethod(f.objectProperty, 'functionProperty', []), |
| equals('Function Property')); |
| // Using a nested getProperty call. |
| expect( |
| js_util.callMethod( |
| js_util.getProperty(f, 'objectProperty'), 'functionProperty', []), |
| equals('Function Property')); |
| |
| // Call method with different args. |
| expect( |
| js_util.callMethod(f, 'getFirstEl', [ |
| [25, 50] |
| ]), |
| equals(25)); |
| expect(js_util.callMethod(f, 'sumFn', [2, 3]), equals(5)); |
| expect(js_util.callMethod(f, 'getA', [f]), equals(42)); |
| expect(js_util.callMethod(f, 'callFn', [allowInterop(jsFunction)]), |
| equals("JS Function")); |
| expect(js_util.callMethod(f, 'callFn', [allowInterop(dartFunction)]), |
| equals("Dart Function")); |
| expect( |
| js_util.callMethod(f, 'callFn', [ |
| allowInterop(() { |
| return "inline"; |
| }) |
| ]), |
| equals("inline")); |
| |
| // Using a variable for the method name. |
| String methodName = 'bar'; |
| expect(js_util.callMethod(f, methodName, []), equals(42)); |
| String bar = _getBarWithSideEffect(); |
| expect(js_util.callMethod(f, bar, []), equals(42)); |
| }); |
| }); |
| |
| group('instanceof', () { |
| test('typed object', () { |
| var f = new Foo(42); |
| expect(js_util.instanceof(f, JSFooType), isTrue); |
| expect(js_util.instanceof(f, JSArrayBufferType), isFalse); |
| }); |
| |
| test('typed literal', () { |
| var literal = new ExampleTypedLiteral(); |
| expect(js_util.instanceof(literal, JSFooType), isFalse); |
| }); |
| }); |
| |
| group('callConstructor', () { |
| test('typed object', () { |
| Foo f = js_util.callConstructor(JSFooType, [42]); |
| expect(f.a, equals(42)); |
| }); |
| }); |
| } |