import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:provider/provider.dart'; import 'package:melo/player/player_expansion_controller.dart'; // Minimaler Host, der dieselbe Provider/PopScope-Verdrahtung wie HomeShell // nachstellt, ohne die volle App (Audio-Handler etc.) hochfahren zu müssen. // Der eigentliche HomeShell-Umbau wird direkt gegen main.dart getestet, // sobald Task 4/5 die Stack-Struktur eingeführt haben; dieser erste Test // sichert nur die PopScope-Erweiterung ab, isoliert an einem Test-Double. class _TestShell extends StatefulWidget { const _TestShell(); @override State<_TestShell> createState() => _TestShellState(); } class _TestShellState extends State<_TestShell> with TickerProviderStateMixin { late final PlayerExpansionController _controller; @override void initState() { super.initState(); _controller = PlayerExpansionController(vsync: this); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { // Eigener AnimatedBuilder um PopScope, damit canPop bei jedem // _controller-Tick neu ausgewertet wird (Reaktivitäts-Regel aus dem // Plan) — ohne ihn bliebe canPop auf dem Wert des allerersten Builds // eingefroren, weil sonst nichts hier ein Rebuild auslöst. return ChangeNotifierProvider.value( value: _controller, child: AnimatedBuilder( animation: _controller, builder: (context, child) => PopScope( canPop: _controller.progress == 0, onPopInvokedWithResult: (didPop, _) { if (!didPop) _controller.close(context); }, child: child!, ), child: const Scaffold(body: SizedBox()), ), ); } } void main() { testWidgets('Zurück-Taste schließt den Player statt zu poppen, wenn offen', (tester) async { await tester.pumpWidget(const MaterialApp(home: _TestShell())); final controller = tester.state<_TestShellState>(find.byType(_TestShell))._controller; controller.open(tester.element(find.byType(_TestShell))); await tester.pumpAndSettle(); expect(controller.progress, 1.0); // pageBack() sucht einen sichtbaren Zurück-Button, den es hier ohne // zweite Route nicht gibt (siehe Plan-Hinweis für den Umsetzer) — // Navigator.maybePop() löst denselben PopScope-Callback aus. await Navigator.maybePop(tester.element(find.byType(_TestShell))); await tester.pumpAndSettle(); expect(controller.progress, 0.0); }); }