blob: 0e8b004c8ff4ff93671c565eb29608643b0450c0 [file] [log] [blame]
// 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.
// @dart = 2.7
library jsinterop.abstract_test;
import 'package:expect/expect.dart';
import 'package:async_helper/async_helper.dart';
import 'package:compiler/src/common.dart';
import 'package:compiler/src/commandline_options.dart';
import '../helpers/memory_compiler.dart';
const List<Test> TESTS = const <Test>[
const Test('Empty js-interop class.', '''
@JS()
library test;
import 'package:js/js.dart';
@JS()
class A {}
main() => new A();
''', warnings: const []),
const Test('Js-interop class with external method.', '''
@JS()
library test;
import 'package:js/js.dart';
@JS()
class A {
external method();
}
main() => new A();
'''),
const Test(
'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() => new A();
'''),
const Test(
'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() => new A();
'''),
const Test(
'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() => new A();
'''),
const Test('Js-interop class with static method.', '''
@JS()
library test;
import 'package:js/js.dart';
@JS()
class A {
static method() {}
}
main() => new A();
'''),
const Test('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() => new B();
'''),
const Test(
'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() => new B();
'''),
const Test.multi(
'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() => new B();
''',
'other.dart': '''
@JS()
library other;
import 'package:js/js.dart';
@JS()
abstract class A {
method();
}
'''
}),
const Test('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() => new B();
'''),
const Test('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() => new B();
'''),
const Test('Js-interop class with generative constructor.', '''
@JS()
library test;
import 'package:js/js.dart';
@JS()
class A {
external A();
}
main() => new A();
'''),
const Test('Js-interop class with factory constructor.', '''
@JS()
library test;
import 'package:js/js.dart';
@JS()
class A {
factory A() => null;
}
main() => new A();
'''),
const Test('Empty anonymous js-interop class.', '''
@JS()
library test;
import 'package:js/js.dart';
@JS()
@anonymous
class A {}
main() => new A();
'''),
const Test('Anonymous js-interop class with generative constructor.', '''
@JS()
library test;
import 'package:js/js.dart';
@JS()
@anonymous
class A {
external A();
}
main() => new A();
'''),
const Test('Anonymous js-interop class with factory constructor.', '''
@JS()
library test;
import 'package:js/js.dart';
@JS()
@anonymous
class A {
factory A() => null;
}
main() => new A();
'''),
const Test(
'Anonymous js-interop class with external factory constructor.', '''
@JS()
library test;
import 'package:js/js.dart';
@JS()
@anonymous
class A {
external factory A();
}
main() => new A();
'''),
const Test('External factory constructor with named parameters.', '''
@JS()
library test;
import 'package:js/js.dart';
@JS()
@anonymous
class A {
external factory A({a, b});
}
main() => new A(a: 1);
'''),
const Test(
'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() => new A(a: 1);
'''),
const Test('Function-typed return type', '''
@JS()
library lib;
import 'package:js/js.dart';
@JS('func')
external int Function() func();
main() {
func();
}
'''),
const Test(
'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);
}
}
});
}
class Test {
final String name;
final String _source;
final Map<String, String> _sources;
final List<MessageKind> errors;
final List<MessageKind> warnings;
const Test(this.name, this._source,
{this.errors: const <MessageKind>[],
this.warnings: const <MessageKind>[]})
: _sources = null;
const Test.multi(this.name, this._sources,
{this.errors: const <MessageKind>[],
this.warnings: const <MessageKind>[]})
: _source = null;
String get source => _source ?? _sources['main.dart'];
Map<String, String> get sources =>
_source != null ? {'main.dart': _source} : _sources;
}
runTest(Test test) async {
print('==${test.name}======================================================');
print(test.source);
await runTestInternal(test);
}
runTestInternal(Test test) async {
DiagnosticCollector collector = new 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.');
}
}