#3180. Add `@staticInterop` tests. Part 2. (#3257)
* #3180. Add `@staticInterop` tests. Part 2.
* Make mixin class an error
diff --git a/LibTest/js_interop/anonymous_A03_t04.dart b/LibTest/js_interop/anonymous_A03_t04.dart
index 55ced9f..67bda35 100644
--- a/LibTest/js_interop/anonymous_A03_t04.dart
+++ b/LibTest/js_interop/anonymous_A03_t04.dart
@@ -13,6 +13,7 @@
/// @description Checks that it is a compile-time error if an extension
/// is annotated with `@anonymous`.
/// @author sgrekhov22@gmail.com
+/// @issue 61082
import 'dart:js_interop';
diff --git a/LibTest/js_interop/staticInterop_A03_t05.dart b/LibTest/js_interop/staticInterop_A03_t05.dart
new file mode 100644
index 0000000..26bf8b8
--- /dev/null
+++ b/LibTest/js_interop/staticInterop_A03_t05.dart
@@ -0,0 +1,132 @@
+// Copyright (c) 2025, 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.
+
+/// @assertion `staticInterop` enables the JS annotated class to be treated as
+/// a "static" interop class.
+///
+/// These classes implicitly all erase to the internal interceptor
+/// `JavaScriptObject`, so they can be freely casted to and from other
+/// [staticInterop] types, `dart:html` types, and `JSObject` from
+/// `dart:js_interop`. Non-[staticInterop] `package:js` types can be casted to
+/// [staticInterop] types, but the reverse can fail if the underlying value is a
+/// `@Native`-reserved type (like `dart:html` types).
+///
+/// [staticInterop] classes have the following restrictions:
+/// - They must contain a [JS] annotation, either from this library or from
+/// `dart:js_interop`.
+/// - They should not contain any instance members, inherited or otherwise, and
+/// should instead use static extension members, which can be external or
+/// non-external.
+/// - They can only contain factories and `static` members. They can be
+/// combined with [anonymous] to make external factories create new
+/// JavaScript object literals instead.
+/// - They should not implement, extend, or mixin non-[staticInterop] classes
+/// and vice-versa.
+/// - The annotation should only be applied to non-mixin classes and no other
+/// declarations.
+///
+/// @description Checks that a class annotated with `@staticInterop` can be
+/// `abstract`, `base`, `final`, `sealed` or `interface`.
+/// @author sgrekhov22@gmail.com
+
+import 'dart:js_interop';
+import 'dart:js_interop_unsafe';
+import '../../Utils/expect.dart';
+import 'js_utils.dart';
+
+@staticInterop
+@JS()
+abstract class C1 {
+}
+
+@staticInterop
+@JS()
+base class C2 {
+}
+
+@staticInterop
+@JS()
+final class C3 {
+}
+
+@staticInterop
+@JS()
+sealed class C4 {
+}
+
+@staticInterop
+@JS()
+interface class C5 {
+}
+
+extension Ext1 on C1 {
+ external String getString();
+ external int getNumber();
+ external bool getBool();
+}
+
+extension Ext2 on C2 {
+ external String getString();
+ external int getNumber();
+ external bool getBool();
+}
+
+extension Ext3 on C3 {
+ external String getString();
+ external int getNumber();
+ external bool getBool();
+}
+
+extension Ext4 on C4 {
+ external String getString();
+ external int getNumber();
+ external bool getBool();
+}
+
+extension Ext5 on C5 {
+ external String getString();
+ external int getNumber();
+ external bool getBool();
+}
+
+main() {
+ eval(r'''
+ globalThis.obj = {
+ getString: function () {
+ return "I'm JS String";
+ },
+ getNumber: function () {
+ return 42;
+ },
+ getBool: function () {
+ return true;
+ }
+ };
+ ''');
+
+ C1 c1 = globalContext["obj"] as C1;
+ Expect.equals("I'm JS String", c1.getString());
+ Expect.equals(42, c1.getNumber());
+ Expect.equals(true, c1.getBool());
+
+ C2 c2 = globalContext["obj"] as C2;
+ Expect.equals("I'm JS String", c2.getString());
+ Expect.equals(42, c2.getNumber());
+ Expect.equals(true, c2.getBool());
+
+ C3 c3 = globalContext["obj"] as C3;
+ Expect.equals("I'm JS String", c3.getString());
+ Expect.equals(42, c3.getNumber());
+ Expect.equals(true, c3.getBool());
+
+ C4 c4 = globalContext["obj"] as C4;
+ Expect.equals("I'm JS String", c4.getString());
+ Expect.equals(42, c4.getNumber());
+ Expect.equals(true, c4.getBool());
+
+ C5 c5 = globalContext["obj"] as C5;
+ Expect.equals("I'm JS String", c5.getString());
+ Expect.equals(42, c5.getNumber());
+ Expect.equals(true, c5.getBool());
+}
diff --git a/LibTest/js_interop/staticInterop_A04_t01.dart b/LibTest/js_interop/staticInterop_A04_t01.dart
new file mode 100644
index 0000000..746bef4
--- /dev/null
+++ b/LibTest/js_interop/staticInterop_A04_t01.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2025, 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.
+
+/// @assertion `staticInterop` enables the JS annotated class to be treated as
+/// a "static" interop class.
+///
+/// These classes implicitly all erase to the internal interceptor
+/// `JavaScriptObject`, so they can be freely casted to and from other
+/// [staticInterop] types, `dart:html` types, and `JSObject` from
+/// `dart:js_interop`. Non-[staticInterop] `package:js` types can be casted to
+/// [staticInterop] types, but the reverse can fail if the underlying value is a
+/// `@Native`-reserved type (like `dart:html` types).
+///
+/// [staticInterop] classes have the following restrictions:
+/// - They must contain a [JS] annotation, either from this library or from
+/// `dart:js_interop`.
+/// - They should not contain any instance members, inherited or otherwise, and
+/// should instead use static extension members, which can be external or
+/// non-external.
+/// - They can only contain factories and `static` members. They can be
+/// combined with [anonymous] to make external factories create new
+/// JavaScript object literals instead.
+/// - They should not implement, extend, or mixin non-[staticInterop] classes
+/// and vice-versa.
+/// - The annotation should only be applied to non-mixin classes and no other
+/// declarations.
+///
+/// @description Checks that it is a compile-time error if a class without
+/// `@staticInterop` annotation extends or implements one with this annotation.
+/// @author sgrekhov22@gmail.com
+
+import 'dart:js_interop';
+
+@staticInterop
+@JS()
+class A {}
+
+@staticInterop
+@JS()
+abstract interface class I {}
+
+@JS()
+class C1 extends A {}
+// ^^
+// [analyzer] unspecified
+// [web] unspecified
+
+@JS()
+class C2 implements I {}
+// ^^
+// [analyzer] unspecified
+// [web] unspecified
+
+main() {
+ print(C1);
+ print(C2);
+}
diff --git a/LibTest/js_interop/staticInterop_A04_t02.dart b/LibTest/js_interop/staticInterop_A04_t02.dart
new file mode 100644
index 0000000..192a446
--- /dev/null
+++ b/LibTest/js_interop/staticInterop_A04_t02.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2025, 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.
+
+/// @assertion `staticInterop` enables the JS annotated class to be treated as
+/// a "static" interop class.
+///
+/// These classes implicitly all erase to the internal interceptor
+/// `JavaScriptObject`, so they can be freely casted to and from other
+/// [staticInterop] types, `dart:html` types, and `JSObject` from
+/// `dart:js_interop`. Non-[staticInterop] `package:js` types can be casted to
+/// [staticInterop] types, but the reverse can fail if the underlying value is a
+/// `@Native`-reserved type (like `dart:html` types).
+///
+/// [staticInterop] classes have the following restrictions:
+/// - They must contain a [JS] annotation, either from this library or from
+/// `dart:js_interop`.
+/// - They should not contain any instance members, inherited or otherwise, and
+/// should instead use static extension members, which can be external or
+/// non-external.
+/// - They can only contain factories and `static` members. They can be
+/// combined with [anonymous] to make external factories create new
+/// JavaScript object literals instead.
+/// - They should not implement, extend, or mixin non-[staticInterop] classes
+/// and vice-versa.
+/// - The annotation should only be applied to non-mixin classes and no other
+/// declarations.
+///
+/// @description Checks that it is a compile-time error if a class annotated
+/// with `@staticInterop` annotation extends, mixin or implements one without
+/// this annotation.
+/// @author sgrekhov22@gmail.com
+
+import 'dart:js_interop';
+
+class A {}
+
+mixin class M {}
+
+abstract interface class I {}
+
+@staticInterop
+@JS()
+class C1 extends A {}
+// ^^
+// [analyzer] unspecified
+// [web] unspecified
+
+@staticInterop
+@JS()
+class C2 with M {}
+// ^^
+// [analyzer] unspecified
+// [web] unspecified
+
+@staticInterop
+@JS()
+class C3 implements I {}
+// ^^
+// [analyzer] unspecified
+// [web] unspecified
+
+main() {
+ print(C1);
+ print(C2);
+ print(C3);
+}
diff --git a/LibTest/js_interop/staticInterop_A04_t03.dart b/LibTest/js_interop/staticInterop_A04_t03.dart
new file mode 100644
index 0000000..247edc3
--- /dev/null
+++ b/LibTest/js_interop/staticInterop_A04_t03.dart
@@ -0,0 +1,82 @@
+// Copyright (c) 2025, 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.
+
+/// @assertion `staticInterop` enables the JS annotated class to be treated as
+/// a "static" interop class.
+///
+/// These classes implicitly all erase to the internal interceptor
+/// `JavaScriptObject`, so they can be freely casted to and from other
+/// [staticInterop] types, `dart:html` types, and `JSObject` from
+/// `dart:js_interop`. Non-[staticInterop] `package:js` types can be casted to
+/// [staticInterop] types, but the reverse can fail if the underlying value is a
+/// `@Native`-reserved type (like `dart:html` types).
+///
+/// [staticInterop] classes have the following restrictions:
+/// - They must contain a [JS] annotation, either from this library or from
+/// `dart:js_interop`.
+/// - They should not contain any instance members, inherited or otherwise, and
+/// should instead use static extension members, which can be external or
+/// non-external.
+/// - They can only contain factories and `static` members. They can be
+/// combined with [anonymous] to make external factories create new
+/// JavaScript object literals instead.
+/// - They should not implement, extend, or mixin non-[staticInterop] classes
+/// and vice-versa.
+/// - The annotation should only be applied to non-mixin classes and no other
+/// declarations.
+///
+/// @description Checks that a class annotated with `@staticInterop` can extend,
+/// or implement another class/mixin annotated with `@staticInterop`.
+/// @author sgrekhov22@gmail.com
+
+import 'dart:js_interop';
+import 'dart:js_interop_unsafe';
+import '../../Utils/expect.dart';
+import 'js_utils.dart';
+
+@staticInterop
+@JS()
+abstract class A {}
+
+extension Ext on A {
+ external String getString();
+ external int getNumber();
+ external bool getBool();
+}
+
+@staticInterop
+@JS()
+class C1 extends A {
+}
+
+@staticInterop
+@JS()
+class C2 implements A {
+}
+
+main() {
+ eval(r'''
+ globalThis.obj = {
+ getString: function () {
+ return "I'm JS String";
+ },
+ getNumber: function () {
+ return 42;
+ },
+ getBool: function () {
+ return true;
+ }
+ };
+ ''');
+
+ C1 c1 = globalContext["obj"] as C1;
+ Expect.equals("I'm JS String", c1.getString());
+ Expect.equals(42, c1.getNumber());
+ Expect.equals(true, c1.getBool());
+
+ C2 c2 = globalContext["obj"] as C2;
+ Expect.equals("I'm JS String", c2.getString());
+ Expect.equals(42, c2.getNumber());
+ Expect.equals(true, c2.getBool());
+}
diff --git a/LibTest/js_interop/staticInterop_A05_t01.dart b/LibTest/js_interop/staticInterop_A05_t01.dart
new file mode 100644
index 0000000..7055934
--- /dev/null
+++ b/LibTest/js_interop/staticInterop_A05_t01.dart
@@ -0,0 +1,85 @@
+// Copyright (c) 2025, 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.
+
+/// @assertion `staticInterop` enables the JS annotated class to be treated as
+/// a "static" interop class.
+///
+/// These classes implicitly all erase to the internal interceptor
+/// `JavaScriptObject`, so they can be freely casted to and from other
+/// [staticInterop] types, `dart:html` types, and `JSObject` from
+/// `dart:js_interop`. Non-[staticInterop] `package:js` types can be casted to
+/// [staticInterop] types, but the reverse can fail if the underlying value is a
+/// `@Native`-reserved type (like `dart:html` types).
+///
+/// [staticInterop] classes have the following restrictions:
+/// - They must contain a [JS] annotation, either from this library or from
+/// `dart:js_interop`.
+/// - They should not contain any instance members, inherited or otherwise, and
+/// should instead use static extension members, which can be external or
+/// non-external.
+/// - They can only contain factories and `static` members. They can be
+/// combined with [anonymous] to make external factories create new
+/// JavaScript object literals instead.
+/// - They should not implement, extend, or mixin non-[staticInterop] classes
+/// and vice-versa.
+/// - The annotation should only be applied to non-mixin classes and no other
+/// declarations.
+///
+/// @description Checks that it is a compile-time error if a mixin or mixin
+/// class is annotated with `@staticInterop`.
+/// @author sgrekhov22@gmail.com
+/// @issue 61125
+
+import 'dart:js_interop';
+
+@staticInterop
+@JS()
+mixin M1 {}
+// ^^
+// [analyzer] unspecified
+// [web] unspecified
+
+@staticInterop
+@JS()
+base mixin M2 {}
+// ^^
+// [analyzer] unspecified
+// [web] unspecified
+
+@staticInterop
+@JS()
+mixin class M3 {}
+// ^^
+// [analyzer] unspecified
+// [web] unspecified
+
+@staticInterop
+@JS()
+abstract mixin class M4 {}
+// ^^
+// [analyzer] unspecified
+// [web] unspecified
+
+@staticInterop
+@JS()
+base mixin class M5 {}
+// ^^
+// [analyzer] unspecified
+// [web] unspecified
+
+@staticInterop
+@JS()
+abstract base mixin class M6 {}
+// ^^
+// [analyzer] unspecified
+// [web] unspecified
+
+main() {
+ print(M1);
+ print(M2);
+ print(M3);
+ print(M4);
+ print(M5);
+ print(M6);
+}