import 'package:block_seasons/core/rng.dart'; import 'package:block_seasons/game/engine/piece_generator.dart'; import 'package:block_seasons/game/models/piece_library.dart'; import 'package:block_seasons/game/models/stage.dart'; import 'package:block_seasons/l10n/gen/app_localizations.dart'; import 'package:block_seasons/state/providers.dart'; import 'package:block_seasons/ui/screens/game_screen.dart'; import 'package:block_seasons/ui/widgets/board_widget.dart'; import 'package:block_seasons/ui/widgets/piece_painter.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; final _stage = StageConfig.fromJson({ 'id': 'drag_stage', 'seed': 11, 'moveLimit': 20, 'preset': const >[], 'objectives': [ {'type': 'reachScore', 'target': 999999}, ], 'stars': { 'two': {'movesLeft': 5}, 'three': {'movesLeft': 10}, }, 'generatorProfile': 'mid', }); PieceGenerator _smallPool() => PieceGenerator( SeededRng(3), pool: [ PieceLibrary.byId('mono'), PieceLibrary.byId('domino_h'), PieceLibrary.byId('domino_v'), ], ); void main() { testWidgets('dragging a tray piece onto the board places it', (tester) async { final container = ProviderContainer(); addTearDown(container.dispose); container .read(gameSessionProvider.notifier) .startStage(_stage, generator: _smallPool()); await tester.pumpWidget( UncontrolledProviderScope( container: container, child: const MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: GameScreen(), ), ), ); await tester.pumpAndSettle(); final view0 = container.read(gameSessionProvider)!; final monoIndex = view0.tray.indexWhere((p) => p.id == 'mono'); expect(monoIndex, greaterThanOrEqualTo(0)); final boardRect = tester.getRect(find.byType(BoardWidget)); final cell = boardRect.width / 8; // The dragged piece floats 70px above the finger, so aim the finger // below the intended landing cell (0, 0). final targetCenter = boardRect.topLeft + Offset(cell * 0.5, cell * 0.5); final fingerEnd = targetCenter + Offset(0, 70 + cell / 2); final start = tester.getCenter(find.byType(PieceWidget).at(monoIndex)); final gesture = await tester.startGesture(start); await gesture.moveBy(const Offset(0, -30)); await tester.pump(); await gesture.moveTo(fingerEnd); await tester.pump(); await gesture.up(); await tester.pumpAndSettle(); final view = container.read(gameSessionProvider)!; expect(view.grid.isOccupied(0, 0), isTrue, reason: 'mono should land on (0,0)'); expect(view.tray, hasLength(2)); expect(view.score, 1); }); testWidgets('result overlay appears when out of moves', (tester) async { final container = ProviderContainer(); addTearDown(container.dispose); final oneMove = StageConfig.fromJson({ ..._stage.toJson(), 'moveLimit': 1, }); container .read(gameSessionProvider.notifier) .startStage(oneMove, generator: _smallPool()); await tester.pumpWidget( UncontrolledProviderScope( container: container, child: const MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: GameScreen(), ), ), ); await tester.pumpAndSettle(); container.read(gameSessionProvider.notifier).tryPlace(0, 0, 0); await tester.pumpAndSettle(); expect(find.text('Out of moves'), findsOneWidget); expect(find.text('+5 moves (ad)'), findsOneWidget); }); }