blob: f99da54eb51f7e1e3302a72c0287e8ca67a8b781 [file] [log] [blame]
// 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.
import 'package:analyzer/src/error/codes.g.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../dart/resolution/context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(InvalidWidgetPreviewApplicationTest);
});
}
@reflectiveTest
class InvalidWidgetPreviewApplicationTest extends PubPackageResolutionTest {
@override
void setUp() {
super.setUp();
writeTestPackageConfig(PackageConfigFileBuilder(), flutter: true);
}
// @Preview cannot be applied to constructors of abstract classes.
test_invalidAbstractClassConstructors() async {
await assertErrorsInCode(
'''
import 'package:flutter/widgets.dart';
import 'package:flutter/widget_previews.dart';
abstract class B extends StatelessWidget {
@Preview()
B();
@override
Widget build(BuildContext context) {
return Container();
}
}
''',
[error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 133, 7)],
);
}
// @Preview application must invoke the `Preview` constructor.
test_invalidAnnotationApplication() async {
await assertErrorsInCode(
'''
import 'package:flutter/widgets.dart';
import 'package:flutter/widget_previews.dart';
class B extends StatelessWidget {
@Preview
B();
}
''',
[error(CompileTimeErrorCode.NO_ANNOTATION_CONSTRUCTOR_ARGUMENTS, 123, 8)],
);
}
// @Preview cannot be applied to external functions.
test_invalidExternalFunction() async {
await assertErrorsInCode(
'''
import 'package:flutter/widgets.dart';
import 'package:flutter/widget_previews.dart';
@Preview()
external Widget foo();
class B extends StatelessWidget {
@Preview()
external B();
@Preview()
external static Widget foo();
}
''',
[
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 88, 7),
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 159, 7),
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 189, 7),
],
);
}
// @Preview cannot be applied to instance members of classes.
test_invalidInstanceMethod() async {
await assertErrorsInCode(
'''
import 'package:flutter/widgets.dart';
import 'package:flutter/widget_previews.dart';
class B {
@Preview()
Widget foo() {
return Text('Foo');
}
}
''',
[error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 100, 7)],
);
}
// @Preview cannot be applied to nested functions.
test_invalidNestedFunction() async {
await assertErrorsInCode(
'''
import 'package:flutter/widgets.dart';
import 'package:flutter/widget_previews.dart';
Widget foo() {
@Preview()
Widget nested() {
return Text('Foo');
}
return nested();
}
class B {
static Widget foo() {
@Preview()
Widget nested() {
return Text('Foo');
}
return nested();
}
}
''',
[
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 105, 7),
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 223, 7),
],
);
}
// @Preview cannot be applied to:
//
// - Enums members
// - Extension methods
// - Extension type members
// - Mixin members
test_invalidParentContext() async {
await assertErrorsInCode(
'''
import 'package:flutter/widgets.dart';
import 'package:flutter/widget_previews.dart';
class Foo extends StatelessWidget {
@override
Widget build(BuildContext context) => Text('Foo');
}
enum B {
a,
b,
c;
@Preview()
const B();
}
extension on Foo {
@Preview()
Widget invalidExtensionPreview() => Text('Invalid');
}
mixin PreviewMixin {
@Preview()
Widget invalidMixinPreview() => Text('Invalid');
}
extension type FooExtensionType(Foo foo) {
@Preview()
Widget invalidExtensionTypePreview() => Text('Invalid');
}
''',
[
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 219, 7),
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 267, 7),
error(WarningCode.UNUSED_ELEMENT, 286, 23),
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 359, 7),
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 469, 7),
],
);
}
// @Preview cannot be applied to members of private classes.
test_invalidPrivateClass() async {
await assertErrorsInCode(
'''
import 'package:flutter/widgets.dart';
import 'package:flutter/widget_previews.dart';
class _B extends StatelessWidget {
@Preview()
_B();
@Preview()
factory _B.foo() => _B();
@Preview()
static Widget bar() => Text('Bar');
@override
Widget build(BuildContext context) {
return Container();
}
}
''',
[
error(WarningCode.UNUSED_ELEMENT, 93, 2),
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 125, 7),
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 147, 7),
error(WarningCode.UNUSED_ELEMENT, 170, 3),
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 189, 7),
error(WarningCode.UNUSED_ELEMENT, 215, 3),
],
);
}
// @Preview cannot be applied to private generative or factory constructors.
test_invalidPrivateClassConstructors() async {
await assertErrorsInCode(
'''
import 'package:flutter/widgets.dart';
import 'package:flutter/widget_previews.dart';
class B extends StatelessWidget {
@Preview()
B._();
@Preview()
factory B._foo() => B();
B();
@override
Widget build(BuildContext context) {
return Container();
}
}
''',
[
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 124, 7),
error(WarningCode.UNUSED_ELEMENT, 138, 1),
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 147, 7),
error(WarningCode.UNUSED_ELEMENT, 169, 4),
],
);
}
// @Preview cannot be applied to private static functions.
test_invalidPrivateClassStatic() async {
await assertErrorsInCode(
'''
import 'package:flutter/widgets.dart';
import 'package:flutter/widget_previews.dart';
class B extends StatelessWidget {
@Preview()
static Widget _foo() {
return Text('Foo');
}
@override
Widget build(BuildContext context) {
return Container();
}
}
''',
[
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 124, 7),
error(WarningCode.UNUSED_ELEMENT, 150, 4),
],
);
}
// @Preview cannot be applied to private top-level functions.
test_invalidPrivateTopLevel() async {
await assertErrorsInCode(
'''
import 'package:flutter/widgets.dart';
import 'package:flutter/widget_previews.dart';
@Preview()
Widget _foo() {
return Text('Foo');
}
''',
[
error(WarningCode.INVALID_WIDGET_PREVIEW_APPLICATION, 88, 7),
error(WarningCode.UNUSED_ELEMENT, 105, 4),
],
);
}
// Ensure that @Preview can be applied to public factory constructors of
// abstract Widget subtypes.
test_validAbstractClassFactoryConstructor() async {
await assertNoErrorsInCode('''
import 'package:flutter/widgets.dart';
import 'package:flutter/widget_previews.dart';
abstract class B extends StatelessWidget {
@Preview()
factory B() => C();
@Preview()
factory B.named() => C.named();
B._();
}
class C extends B {
C() : super._();
factory C.named() => C();
@override
Widget build(BuildContext context) {
return Container();
}
}
''');
}
// Ensure that constant instances of @Preview(...) can be applied.
test_validAnnotationConstant() async {
await assertNoErrorsInCode('''
import 'package:flutter/widget_previews.dart';
import 'package:flutter/widgets.dart';
const myPreview = Preview(name: 'Testing');
@myPreview
Widget bar() => Text('Bar');
''');
}
// Ensure that @Preview can be applied to public factory constructors of
// Widget subtypes, including those with optional parameters.
test_validClassFactoryConstructor() async {
await assertNoErrorsInCode('''
import 'package:flutter/widgets.dart';
import 'package:flutter/widget_previews.dart';
class B extends StatelessWidget {
@Preview()
factory B.foo({Key? key}) => B._(key: key);
@Preview()
factory B.bar() => B._();
B._({super.key});
@override
Widget build(BuildContext context) {
return Container();
}
}
''');
}
// Ensure that @Preview can be applied to public constructors of Widget
// subtypes, including those with optional parameters.
test_validClassGenerativeConstructor() async {
await assertNoErrorsInCode('''
import 'package:flutter/widgets.dart';
import 'package:flutter/widget_previews.dart';
class B extends StatelessWidget {
@Preview()
const B({super.key});
@Preview()
B.foo([String? _]);
@override
Widget build(BuildContext context) {
return Container();
}
}
''');
}
// Ensure that @Preview can be applied to public static functions that are
// defined in public classes and that return Widget or WidgetBuilder.
test_validClassStatic() async {
await assertNoErrorsInCode('''
import 'package:flutter/widgets.dart';
import 'package:flutter/widget_previews.dart';
class B extends StatelessWidget {
@Preview()
static Widget foo() {
return Text('Foo');
}
@Preview()
static WidgetBuilder bar() {
return (BuildContext context) {
return Text('Bar');
};
}
@override
Widget build(BuildContext context) {
return Container();
}
}
''');
}
// Ensure that @Preview can be applied to public top-level functions that
// return a Widget or WidgetBuilder.
test_validTopLevel() async {
await assertNoErrorsInCode('''
import 'package:flutter/widget_previews.dart';
import 'package:flutter/widgets.dart';
@Preview(name: 'Widget')
Widget foo() => Text('Foo');
@Preview(name: 'WidgetBuilder')
WidgetBuilder bar() {
return (BuildContext context) {
return Text('Bar');
};
}
''');
}
// Ensure that @Preview can be applied to functions that explicitly return a
// subtype of Widget.
test_validTopLevel_widgetSubtype() async {
await assertNoErrorsInCode('''
import 'package:flutter/widget_previews.dart';
import 'package:flutter/widgets.dart';
typedef MyWidget = Widget;
@Preview(name: 'Testing')
Text foo() => Text('Foo');
@Preview(name: 'Testing')
MyWidget bar() => Text('Bar');
''');
}
}