| // 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.', |
| ); |
| } |
| } |