diff --git a/packages/flame/.min_coverage b/packages/flame/.min_coverage index 7e9b9dda4..310562221 100644 --- a/packages/flame/.min_coverage +++ b/packages/flame/.min_coverage @@ -1 +1 @@ -59.4 +61.7 diff --git a/packages/flame/test/game/game_widget/game_widget_drag_test.dart b/packages/flame/test/game/game_widget/game_widget_drag_test.dart new file mode 100644 index 000000000..5697ac3b9 --- /dev/null +++ b/packages/flame/test/game/game_widget/game_widget_drag_test.dart @@ -0,0 +1,97 @@ +import 'package:flame/game.dart'; +import 'package:flame/input.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; + +class HorizontalDragGame extends FlameGame with HorizontalDragDetector { + bool horizontalDragStarted = false; + bool horizontalDragEnded = false; + + @override + void onHorizontalDragStart(_) { + horizontalDragStarted = true; + } + + @override + void onHorizontalDragEnd(_) { + horizontalDragEnded = true; + } +} + +class VerticalDragGame extends FlameGame with VerticalDragDetector { + bool verticalDragStarted = false; + bool verticalDragEnded = false; + + @override + void onVerticalDragStart(_) { + verticalDragStarted = true; + } + + @override + void onVerticalDragEnd(_) { + verticalDragEnded = true; + } +} + +class PanGame extends FlameGame with PanDetector { + bool panStarted = false; + bool panEnded = false; + + @override + void onPanStart(_) { + panStarted = true; + } + + @override + void onPanEnd(_) { + panEnded = true; + } +} + +void main() { + group('GameWidget - HorizontalDragDetector', () { + flameWidgetTest( + 'register drags', + createGame: () => HorizontalDragGame(), + verify: (game, tester) async { + await tester.drag( + find.byGame(), + const Offset(50, 0), + ); + + expect(game.horizontalDragStarted, isTrue); + expect(game.horizontalDragEnded, isTrue); + }, + ); + }); + group('GameWidget - VerticallDragDetector', () { + flameWidgetTest( + 'register drags', + createGame: () => VerticalDragGame(), + verify: (game, tester) async { + await tester.drag( + find.byGame(), + const Offset(50, 0), + ); + + expect(game.verticalDragStarted, isTrue); + expect(game.verticalDragEnded, isTrue); + }, + ); + }); + group('GameWidget - PanDetector', () { + flameWidgetTest( + 'register drags', + createGame: () => PanGame(), + verify: (game, tester) async { + await tester.drag( + find.byGame(), + const Offset(50, 0), + ); + + expect(game.panStarted, isTrue); + expect(game.panEnded, isTrue); + }, + ); + }); +} diff --git a/packages/flame/test/game/game_widget/game_widget_tap_test.dart b/packages/flame/test/game/game_widget/game_widget_tap_test.dart new file mode 100644 index 000000000..542f7c4a0 --- /dev/null +++ b/packages/flame/test/game/game_widget/game_widget_tap_test.dart @@ -0,0 +1,26 @@ +import 'package:flame/game.dart'; +import 'package:flame/input.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; + +class TapGame extends FlameGame with TapDetector { + bool tapRegistered = false; + + @override + void onTap() { + tapRegistered = true; + } +} + +void main() { + group('GameWidget - TapDetectors', () { + flameWidgetTest( + 'can receive taps', + createGame: () => TapGame(), + verify: (game, tester) async { + await tester.tapAt(const Offset(10, 10)); + expect(game.tapRegistered, isTrue); + }, + ); + }); +} diff --git a/packages/flame_test/CHANGELOG.md b/packages/flame_test/CHANGELOG.md index b0b6fd461..432c73856 100644 --- a/packages/flame_test/CHANGELOG.md +++ b/packages/flame_test/CHANGELOG.md @@ -1,5 +1,8 @@ # CHANGELOG +## [next] + - Add `flameTest` and `flameWidgetTest` + ## [0.1.1-releasecandidate.13] - Move dartdoc and dart_code_metrics to dev_dependencies diff --git a/packages/flame_test/example/assets/images/city.png b/packages/flame_test/example/assets/images/city.png new file mode 100644 index 000000000..3cdc6270c Binary files /dev/null and b/packages/flame_test/example/assets/images/city.png differ diff --git a/packages/flame_test/example/lib/game.dart b/packages/flame_test/example/lib/game.dart new file mode 100644 index 000000000..91f546aed --- /dev/null +++ b/packages/flame_test/example/lib/game.dart @@ -0,0 +1,18 @@ +import 'package:flame/components.dart'; +import 'package:flame/game.dart'; +import 'package:flutter/material.dart'; + +class MyGameWidget extends StatelessWidget { + @override + Widget build(BuildContext context) { + return GameWidget(game: MyGame()); + } +} + +class MyGame extends FlameGame { + @override + Future onLoad() async { + final citySprite = await loadSprite('city.png'); + await add(SpriteComponent(sprite: citySprite, size: Vector2.all(200))); + } +} diff --git a/packages/flame_test/example/pubspec.yaml b/packages/flame_test/example/pubspec.yaml index a90a06a37..49f1a65e8 100644 --- a/packages/flame_test/example/pubspec.yaml +++ b/packages/flame_test/example/pubspec.yaml @@ -21,3 +21,7 @@ dev_dependencies: path: ../ test: ^1.17.10 dart_code_metrics: ^4.1.0 + +flutter: + assets: + - assets/images/ diff --git a/packages/flame_test/example/test/flame_test_test.dart b/packages/flame_test/example/test/flame_test_test.dart new file mode 100644 index 000000000..b93346014 --- /dev/null +++ b/packages/flame_test/example/test/flame_test_test.dart @@ -0,0 +1,28 @@ +import 'package:example/game.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('flameTest', () { + TestWidgetsFlutterBinding.ensureInitialized(); + + flameTest( + 'can load the game', + createGame: () => MyGame(), + verify: (game) { + expect(game.children.length, 1); + }, + ); + + flameWidgetTest( + 'render the game widget', + createGame: () => MyGame(), + verify: (game, tester) async { + expect( + find.byGame(), + findsOneWidget, + ); + }, + ); + }); +} diff --git a/packages/flame_test/lib/flame_test.dart b/packages/flame_test/lib/flame_test.dart index 63441fb8b..40c741a46 100644 --- a/packages/flame_test/lib/flame_test.dart +++ b/packages/flame_test/lib/flame_test.dart @@ -1,5 +1,6 @@ export 'src/expect_double.dart'; export 'src/expect_vector2.dart'; +export 'src/flame_test.dart'; export 'src/mock_gesture_events.dart'; export 'src/mock_image.dart'; export 'src/random_test.dart'; diff --git a/packages/flame_test/lib/src/flame_test.dart b/packages/flame_test/lib/src/flame_test.dart new file mode 100644 index 000000000..52e024933 --- /dev/null +++ b/packages/flame_test/lib/src/flame_test.dart @@ -0,0 +1,81 @@ +import 'package:flame/game.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:meta/meta.dart'; + +extension FlameFinds on CommonFinders { + Finder byGame() { + return find.byWidgetPredicate( + (widget) => widget is GameWidget, + ); + } +} + +typedef GameCreateFunction = T Function(); +typedef VerifyFunction = void Function(T); + +/// Creates a [Game] specific test case with given [description]. +/// +/// Use [createGame] to create your game instance. +/// +/// Use [verify] closure to make verifications/assertions. +@isTest +void flameTest( + String description, { + required GameCreateFunction createGame, + VerifyFunction? verify, + Vector2? gameSize, +}) { + test(description, () async { + final game = createGame(); + + final size = gameSize ?? Vector2.all(500); + game.onGameResize(size); + + await game.onLoad(); + game.update(0); + + if (verify != null) { + verify(game); + } + }); +} + +typedef GameWidgetCreateFunction = GameWidget Function( + T game); +typedef WidgetVerifyFunction = Future Function( + T, + WidgetTester, +); + +/// Creates a [Game] specific test case with given [description] +/// which runs inside the Flutter test environment. +/// +/// Use [createGame] to create your game instance. +/// +/// Use [createGameWidget] to create the [GameWidget], if omitted +/// the game instance returned by [createGame] will be wrapped into +/// an empty [GameWidget] instance. +/// +/// Use [verify] closure to make verifications/assertions. +@isTest +void flameWidgetTest( + String description, { + required GameCreateFunction createGame, + GameWidgetCreateFunction? createGameWidget, + WidgetVerifyFunction? verify, +}) { + testWidgets(description, (tester) async { + final game = createGame(); + + await tester.runAsync(() async { + final gameWidget = createGameWidget?.call(game) ?? GameWidget(game: game); + + await tester.pumpWidget(gameWidget); + await tester.pump(); + + if (verify != null) { + await verify(game, tester); + } + }); + }); +}