| // Copyright (c) 2016, 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 jsinterop.abstract_test; | |
| import 'package:expect/async_helper.dart'; | |
| import 'package:expect/expect.dart'; | |
| import 'package:compiler/src/common.dart'; | |
| import 'package:compiler/src/commandline_options.dart'; | |
| import 'package:compiler/src/util/memory_compiler.dart'; | |
| const List<Test> TESTS = const <Test>[ | |
| const SingleTest('Empty js-interop class.', ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| class A {} | |
| main() => A(); | |
| ''', warnings: const []), | |
| const SingleTest('Js-interop class with external method.', ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| class A { | |
| external method(); | |
| } | |
| main() => A(); | |
| '''), | |
| const SingleTest( | |
| 'Js-interop class with external method with required parameters.', | |
| ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| class A { | |
| external method(a, b); | |
| } | |
| main() => A(); | |
| ''', | |
| ), | |
| const SingleTest( | |
| 'Js-interop class with external method with optional parameters.', | |
| ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| class A { | |
| external method([a, b]); | |
| } | |
| main() => A(); | |
| ''', | |
| ), | |
| const SingleTest( | |
| 'Js-interop class with external method with optional parameters ' | |
| 'with default values.', | |
| ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| class A { | |
| external method([a = 1, b = 2]); | |
| } | |
| main() => A(); | |
| ''', | |
| ), | |
| const SingleTest('Js-interop class with static method.', ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| class A { | |
| static method() {} | |
| } | |
| main() => A(); | |
| '''), | |
| const SingleTest('Js-interop class that extends a js-interop class.', ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| abstract class A { | |
| method(); | |
| } | |
| @JS() | |
| class B extends A { | |
| external method(); | |
| } | |
| main() => B(); | |
| '''), | |
| const SingleTest( | |
| 'Js-interop class that extends a js-interop class, ' | |
| 'reversed declaration order.', | |
| ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| class B extends A { | |
| external method(); | |
| } | |
| @JS() | |
| abstract class A { | |
| method(); | |
| } | |
| main() => B(); | |
| ''', | |
| ), | |
| const MultiTest( | |
| 'Js-interop class that extends a js-interop class from a different ' | |
| 'library.', | |
| const { | |
| 'main.dart': ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| import 'other.dart'; | |
| @JS() | |
| class B extends A { | |
| external method(); | |
| } | |
| main() => B(); | |
| ''', | |
| 'other.dart': ''' | |
| @JS() | |
| library other; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| abstract class A { | |
| method(); | |
| } | |
| ''', | |
| }, | |
| ), | |
| const SingleTest('Js-interop class that implements a regular class.', ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| abstract class A { | |
| method(); | |
| } | |
| @JS() | |
| class B implements A { | |
| external method(); | |
| } | |
| main() => B(); | |
| '''), | |
| const SingleTest('Js-interop class that implements a js-interop class.', ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| abstract class A { | |
| method(); | |
| } | |
| @JS() | |
| class B implements A { | |
| external method(); | |
| } | |
| main() => B(); | |
| '''), | |
| const SingleTest('Js-interop class with generative constructor.', ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| class A { | |
| external A(); | |
| } | |
| main() => A(); | |
| '''), | |
| const SingleTest('Js-interop class with factory constructor.', ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| class A { | |
| external A._(); | |
| factory A() => A._(); | |
| } | |
| main() => A(); | |
| '''), | |
| const SingleTest('Empty anonymous js-interop class.', ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| @anonymous | |
| class A {} | |
| main() => A(); | |
| '''), | |
| const SingleTest( | |
| 'Anonymous js-interop class with generative constructor.', | |
| ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| @anonymous | |
| class A { | |
| external A(); | |
| } | |
| main() => A(); | |
| ''', | |
| ), | |
| const SingleTest('Anonymous js-interop class with factory constructor.', ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| @anonymous | |
| class A { | |
| external A._(); | |
| factory A() => A._(); | |
| } | |
| main() => A(); | |
| '''), | |
| const SingleTest( | |
| 'Anonymous js-interop class with external factory constructor.', | |
| ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| @anonymous | |
| class A { | |
| external factory A(); | |
| } | |
| main() => A(); | |
| ''', | |
| ), | |
| const SingleTest('External factory constructor with named parameters.', ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| @anonymous | |
| class A { | |
| external factory A({a, b}); | |
| } | |
| main() => A(a: 1); | |
| '''), | |
| const SingleTest( | |
| 'External factory constructor with named parameters ' | |
| 'with default parameters.', | |
| ''' | |
| @JS() | |
| library test; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| @anonymous | |
| class A { | |
| external factory A({a = 1, b = 2}); | |
| } | |
| main() => A(a: 1); | |
| ''', | |
| ), | |
| const SingleTest('Function-typed return type', ''' | |
| @JS() | |
| library lib; | |
| import 'package:js/js.dart'; | |
| @JS('func') | |
| external int Function() func(); | |
| main() { | |
| func(); | |
| } | |
| '''), | |
| const SingleTest( | |
| 'Non-external field.', | |
| ''' | |
| @JS() | |
| library lib; | |
| import 'package:js/js.dart'; | |
| @JS() | |
| @anonymous | |
| class B { | |
| int Function()? callback; | |
| } | |
| @JS('makeB') | |
| external B makeB(); | |
| main() { | |
| makeB().callback!(); | |
| } | |
| ''', | |
| // TODO(34174): Disallow js-interop fields. | |
| /*errors: const [MessageKind.IMPLICIT_JS_INTEROP_FIELD_NOT_SUPPORTED]*/ | |
| ), | |
| ]; | |
| void main(List<String> args) { | |
| asyncTest(() async { | |
| for (Test test in TESTS) { | |
| bool run = true; | |
| if (args.isNotEmpty) { | |
| run = false; | |
| for (String arg in args) { | |
| if (test.name.contains(arg)) { | |
| run = true; | |
| break; | |
| } | |
| } | |
| } | |
| if (run) { | |
| await runTest(test); | |
| } | |
| } | |
| }); | |
| } | |
| abstract class Test { | |
| final String name; | |
| final List<MessageKind> errors; | |
| final List<MessageKind> warnings; | |
| const Test(this.name, {required this.errors, required this.warnings}); | |
| String get source; | |
| Map<String, String> get sources; | |
| } | |
| class SingleTest extends Test { | |
| @override | |
| final String source; | |
| const SingleTest( | |
| super.name, | |
| this.source, { | |
| super.errors = const <MessageKind>[], | |
| super.warnings = const <MessageKind>[], | |
| }); | |
| @override | |
| Map<String, String> get sources => {'main.dart': source}; | |
| } | |
| class MultiTest extends Test { | |
| @override | |
| final Map<String, String> sources; | |
| const MultiTest( | |
| super.name, | |
| this.sources, { | |
| super.errors = const <MessageKind>[], | |
| super.warnings = const <MessageKind>[], | |
| }); | |
| @override | |
| String get source => sources['main.dart']!; | |
| } | |
| runTest(Test test) async { | |
| print('==${test.name}======================================================'); | |
| print(test.source); | |
| await runTestInternal(test); | |
| } | |
| runTestInternal(Test test) async { | |
| DiagnosticCollector collector = DiagnosticCollector(); | |
| List<String> options = <String>[]; | |
| // TODO(redemption): Enable inlining. | |
| options.add(Flags.disableInlining); | |
| await runCompiler( | |
| diagnosticHandler: collector, | |
| options: options, | |
| memorySourceFiles: test.sources, | |
| ); | |
| Expect.equals( | |
| test.errors.length, | |
| collector.errors.length, | |
| 'Unexpected error count.', | |
| ); | |
| Expect.equals( | |
| test.warnings.length, | |
| collector.warnings.length, | |
| 'Unexpected warning count.', | |
| ); | |
| for (int index = 0; index < test.errors.length; index++) { | |
| Expect.equals( | |
| test.errors[index], | |
| collector.errors.elementAt(index).messageKind, | |
| 'Unexpected error.', | |
| ); | |
| } | |
| for (int index = 0; index < test.warnings.length; index++) { | |
| Expect.equals( | |
| test.warnings[index], | |
| collector.warnings.elementAt(index).messageKind, | |
| 'Unexpected warning.', | |
| ); | |
| } | |
| } |