import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:bully/features/tutorial/tutorial_step.dart'; import 'package:bully/features/tutorial/widgets/tutorial_hand.dart'; import 'package:bully/features/tutorial/widgets/tutorial_overlay.dart'; /// Monte l'overlay au-dessus d'un faux écran contenant l'élément visé. Future pumpOverlay( WidgetTester tester, { required List Function(GlobalKey targetKey) steps, required VoidCallback onFinished, }) async { final targetKey = GlobalKey(); await tester.pumpWidget( MaterialApp( home: Scaffold( body: Stack( children: [ Center( child: SizedBox(key: targetKey, width: 160, height: 48), ), TutorialOverlay(steps: steps(targetKey), onFinished: onFinished), ], ), ), ), ); // Laisse le temps à la mesure du spot (défilement + délai de stabilisation). await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); return targetKey; } void main() { testWidgets('la visite avance d\'une étape à l\'autre puis se termine', (WidgetTester tester) async { var finished = false; await pumpOverlay( tester, onFinished: () => finished = true, steps: (targetKey) => [ TutorialStep( targetKey: targetKey, title: 'Démarrez une session', description: 'Tout part d\'ici.', gesture: TutorialGesture.tap, ), TutorialStep( targetKey: targetKey, title: 'Zoomer sur la cible', description: 'Écartez deux doigts.', gesture: TutorialGesture.pinch, ), ], ); expect(find.text('Démarrez une session'), findsOneWidget); expect(find.text('1/2'), findsOneWidget); // La main animée mime le geste attendu, posée sur l'élément mis en avant. expect(find.byType(TutorialHand), findsOneWidget); expect(find.text('Appuyez une fois'), findsOneWidget); await tester.tap(find.text('Suivant')); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); expect(find.text('Zoomer sur la cible'), findsOneWidget); expect(find.text('2/2'), findsOneWidget); expect(find.text('Pincez avec deux doigts pour zoomer'), findsOneWidget); expect(finished, isFalse); await tester.tap(find.text('C\'est parti')); await tester.pump(); expect(finished, isTrue); }); testWidgets('« Passer » interrompt la visite immédiatement', (WidgetTester tester) async { var finished = false; await pumpOverlay( tester, onFinished: () => finished = true, steps: (targetKey) => [ TutorialStep( targetKey: targetKey, title: 'Première étape', description: 'Description.', ), const TutorialStep( title: 'Seconde étape', description: 'Description.', ), ], ); await tester.tap(find.text('Passer')); await tester.pump(); expect(finished, isTrue); expect(find.text('Seconde étape'), findsNothing); }); testWidgets('une étape sans cible s\'affiche comme carte centrée', (WidgetTester tester) async { await pumpOverlay( tester, onFinished: () {}, steps: (_) => [ const TutorialStep( title: 'Bienvenue dans Bully', description: 'Ce guide rapide vous montre l\'essentiel.', ), ], ); expect(find.text('Bienvenue dans Bully'), findsOneWidget); expect(find.text('C\'est parti'), findsOneWidget); // Aucune cible : pas de main animée non plus. expect(find.byType(TutorialHand), findsNothing); }); testWidgets('l\'overlay se rend correctement dans un Navigator push modal', (WidgetTester tester) async { final targetKey = GlobalKey(); await tester.pumpWidget( MaterialApp( home: Builder( builder: (context) => Scaffold( body: Column( children: [ SizedBox(key: targetKey, width: 200, height: 60), ElevatedButton( onPressed: () { Navigator.of(context).push( PageRouteBuilder( opaque: false, pageBuilder: (routeCtx, _, __) => TutorialOverlay( steps: [ const TutorialStep( title: 'Bienvenue', description: 'Texte introductif', ), TutorialStep( targetKey: targetKey, title: 'Bouton', description: 'Texte bouton', gesture: TutorialGesture.tap, ), ], onFinished: () => Navigator.of(routeCtx).pop(), ), ), ); }, child: const Text('Ouvrir'), ), ], ), ), ), ), ); await tester.tap(find.text('Ouvrir')); await tester.pump(); await tester.pump(const Duration(milliseconds: 300)); expect(find.text('Bienvenue'), findsOneWidget); await tester.tap(find.text('Suivant')); await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); expect(find.text('Bouton'), findsOneWidget); expect(find.byType(TutorialHand), findsOneWidget); }); }