From 1da137db1fbf65f0a09ef1c9042fb5e658dbeda3 Mon Sep 17 00:00:00 2001
From: Manish Kumar <manish.kumar@etu.unige.ch>
Date: Tue, 9 Feb 2021 19:22:32 +0100
Subject: [PATCH 1/3] improved dynamicForm and Popup tests + new test for
 createEvent

---
 test/createEvent_test.dart | 47 +++++++++++++++++++++++
 test/dynamicForm_test.dart | 76 +++++++++++++++++++++++++++-----------
 test/popup_test.dart       |  7 +++-
 3 files changed, 108 insertions(+), 22 deletions(-)
 create mode 100644 test/createEvent_test.dart

diff --git a/test/createEvent_test.dart b/test/createEvent_test.dart
new file mode 100644
index 0000000..897f65d
--- /dev/null
+++ b/test/createEvent_test.dart
@@ -0,0 +1,47 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:hive/hive.dart';
+import 'package:hive_flutter/hive_flutter.dart';
+import 'package:naca/createEvent.dart';
+import 'package:naca/dynamicForm.dart';
+import 'package:naca/models/Event.dart';
+
+void main() {
+  Box<Event> eventDB;
+
+  setUpAll(() async {
+    // We init the Hive
+    await Hive.initFlutter();
+    // Since our classes are not native elements, we generate Adapters
+    Hive.registerAdapter<Event>(EventAdapter());
+    // We open the boxes, we can open them anywhere.
+    // It is better to close them after using it.
+
+    eventDB = await Hive.openBox<Event>('event');
+    TestWidgetsFlutterBinding.ensureInitialized();
+  });
+
+  testWidgets('Create event screen is displayed and button works',
+      (WidgetTester tester) async {
+    await tester.pumpWidget(makeTesteableWidget(child: CreateEvent()));
+
+    // Test des éléments de départ
+    expect(find.text("Créer un événement"), findsOneWidget);
+    expect(find.byType(FloatingActionButton), findsOneWidget);
+
+    // Test d'apparition du popup
+    await tester.tap(find.byType(FloatingActionButton));
+    await tester.pumpAndSettle();
+
+    expect(find.byType(DynamicForm), findsOneWidget);
+  });
+}
+
+Widget makeTesteableWidget({Widget child}) {
+  return MaterialApp(
+    home: Scaffold(
+      body: child,
+    ),
+  );
+}
diff --git a/test/dynamicForm_test.dart b/test/dynamicForm_test.dart
index f2a2f3d..e899101 100644
--- a/test/dynamicForm_test.dart
+++ b/test/dynamicForm_test.dart
@@ -2,28 +2,50 @@
 import 'package:flutter/material.dart';
 import 'package:flutter_test/flutter_test.dart';
 import 'package:naca/dynamicForm.dart';
+import 'package:naca/primaryButton.dart';
 
 void main() {
-  testWidgets('DynamicForm creates the TextFormFields',
+  var res;
+  final mandatoryForm = makeTesteableWidget(
+      child: Builder(
+    builder: (BuildContext context) => DynamicForm(
+      fields: [
+        ['Nom', String, true],
+        ['Lieu', String, true],
+        ['Contexte', String, false],
+        ['Date de début', DateTime, true, DateTime.now(), false],
+        ['Date de fin', DateTime, false, null, false],
+      ],
+      buttonText: 'Créer',
+      onSubmit: (formRes) => res = formRes,
+      context: context,
+    ),
+  ));
+
+  final form = makeTesteableWidget(
+      child: Builder(
+    builder: (BuildContext context) => DynamicForm(
+      fields: [
+        ['Nom', String, false],
+        ['Lieu', String, false],
+        ['Contexte', String, false],
+        ['Date de début', DateTime, false, DateTime.now(), false],
+        ['Date de fin', DateTime, false, null, false],
+      ],
+      buttonText: 'Créer',
+      onSubmit: (formRes) {
+        print('bite');
+        res = formRes;
+      },
+      context: context,
+    ),
+  ));
+
+  testWidgets('DynamicForm creates the TextFormFields with mandatory fields',
       (WidgetTester tester) async {
     // Create the widget by telling the tester to build it.
-    final form = makeTesteableWidget(
-        child: Builder(
-      builder: (BuildContext context) => DynamicForm(
-        fields: [
-          ['Nom', String, true],
-          ['Lieu', String, true],
-          ['Contexte', String, false],
-          ['Date de début', DateTime, true, DateTime.now(), false],
-          ['Date de fin', DateTime, false, null, false],
-        ],
-        buttonText: 'Créer',
-        onSubmit: _res,
-        context: context,
-      ),
-    ));
-
-    await tester.pumpWidget(form);
+
+    await tester.pumpWidget(mandatoryForm);
 
     // Test du nombre de champs générés
     // 3 champs String et 2 champs DateTime comprenant chacun 2 champs (pour la date et pour l'heure)
@@ -36,11 +58,23 @@ void main() {
     expect(find.text('Contexte'), findsOneWidget);
     expect(find.text('Date de début*'), findsOneWidget);
     expect(find.text('Date de fin'), findsOneWidget);
+
+    expect(find.text('Créer'), findsOneWidget);
+
+    await tester.tap(find.byType(PrimaryButton));
+    await tester.pumpAndSettle();
+
+    // On s'attend à ne rien recevoir en retour, comme les champs obligatoires sont vides
+    expect(res, isNull);
+
+    // TODO trouver comment tester l'activation des messages d'erreur lorsque l'on valide le formulaire vide
+    /*expect(
+        ((tester.firstWidget(find.text('*Champs obligatoires')) as Text).style)
+            .color,
+        Colors.red);*/
   });
-}
 
-_res() {
-  print('dummy');
+  //TODO appuyer sur le bouton de validation ne semble pas marcher
 }
 
 Widget makeTesteableWidget({Widget child}) {
diff --git a/test/popup_test.dart b/test/popup_test.dart
index 21b1bee..be982c9 100644
--- a/test/popup_test.dart
+++ b/test/popup_test.dart
@@ -10,10 +10,15 @@ void main() {
 
     // Clique sur le bouton pour faire apparaître le popup
     await tester.tap(find.byType(FloatingActionButton));
-
     await tester.pump();
 
     expect(find.text('test'), findsOneWidget);
+
+    // Clique sur le bouton pour faire disparaître le popup
+    await tester.tap(find.byType(FloatingActionButton));
+    await tester.pump();
+
+    expect(find.text('test'), findsNothing);
   });
 }
 
-- 
GitLab


From 121824ba32f032ea3c625331092ff11d935acdf3 Mon Sep 17 00:00:00 2001
From: Manish Kumar <manish.kumar@etu.unige.ch>
Date: Tue, 9 Feb 2021 19:34:25 +0100
Subject: [PATCH 2/3] small fixes

---
 test/createEvent_test.dart |  4 +---
 test/dynamicForm_test.dart | 19 -------------------
 test/popup_test.dart       |  2 +-
 3 files changed, 2 insertions(+), 23 deletions(-)

diff --git a/test/createEvent_test.dart b/test/createEvent_test.dart
index 897f65d..64aa95e 100644
--- a/test/createEvent_test.dart
+++ b/test/createEvent_test.dart
@@ -8,8 +8,6 @@ import 'package:naca/dynamicForm.dart';
 import 'package:naca/models/Event.dart';
 
 void main() {
-  Box<Event> eventDB;
-
   setUpAll(() async {
     // We init the Hive
     await Hive.initFlutter();
@@ -18,7 +16,7 @@ void main() {
     // We open the boxes, we can open them anywhere.
     // It is better to close them after using it.
 
-    eventDB = await Hive.openBox<Event>('event');
+    await Hive.openBox<Event>('event');
     TestWidgetsFlutterBinding.ensureInitialized();
   });
 
diff --git a/test/dynamicForm_test.dart b/test/dynamicForm_test.dart
index e899101..0fec8f0 100644
--- a/test/dynamicForm_test.dart
+++ b/test/dynamicForm_test.dart
@@ -22,25 +22,6 @@ void main() {
     ),
   ));
 
-  final form = makeTesteableWidget(
-      child: Builder(
-    builder: (BuildContext context) => DynamicForm(
-      fields: [
-        ['Nom', String, false],
-        ['Lieu', String, false],
-        ['Contexte', String, false],
-        ['Date de début', DateTime, false, DateTime.now(), false],
-        ['Date de fin', DateTime, false, null, false],
-      ],
-      buttonText: 'Créer',
-      onSubmit: (formRes) {
-        print('bite');
-        res = formRes;
-      },
-      context: context,
-    ),
-  ));
-
   testWidgets('DynamicForm creates the TextFormFields with mandatory fields',
       (WidgetTester tester) async {
     // Create the widget by telling the tester to build it.
diff --git a/test/popup_test.dart b/test/popup_test.dart
index be982c9..9ce25dd 100644
--- a/test/popup_test.dart
+++ b/test/popup_test.dart
@@ -4,7 +4,7 @@ import 'package:flutter_test/flutter_test.dart';
 import 'package:naca/popup.dart';
 
 void main() {
-  testWidgets('The Popup appears and displays the correct widget',
+  testWidgets('The Popup appears, displays the correct widget and disappears',
       (WidgetTester tester) async {
     await tester.pumpWidget(makeTesteableWidget(child: PopupTestWidget()));
 
-- 
GitLab


From 723ea146718ec1069d670051853594735a71dcd3 Mon Sep 17 00:00:00 2001
From: Manish Kumar <manish.kumar@etu.unige.ch>
Date: Wed, 10 Feb 2021 13:24:28 +0100
Subject: [PATCH 3/3] updated some comments and cleaned up the code

---
 lib/dynamicForm.dart | 116 ++++++++++++++++++++++---------------------
 1 file changed, 60 insertions(+), 56 deletions(-)

diff --git a/lib/dynamicForm.dart b/lib/dynamicForm.dart
index 0a23936..30cc967 100644
--- a/lib/dynamicForm.dart
+++ b/lib/dynamicForm.dart
@@ -92,7 +92,7 @@ class DynamicFormState extends State<DynamicForm> {
     TextEditingController dateController;
     TextEditingController timeController;
 
-    // La boucle Future.forEach assure que tout soit exécuté avant de passer au prochain tour de boucle
+    // Itération sur les champs
     widget.fields.forEach(
       (field) {
         if (field[1] == DateTime) {
@@ -102,7 +102,8 @@ class DynamicFormState extends State<DynamicForm> {
           // S'il y a une valeur par défaut et qu'on souhaite qu'elle soit affichée
           if (field.length > 4 && field[4]) {
             dateController.text = dateFormater(field[3]);
-            // On est obligé d'attendre que la variable context soit accessible
+            // On utilise le contexte du parent qui facilite l'appel,
+            // sinon il faudrait faire un appel asynchrone qui ne smblait pas fonctionner dans les tests
             timeController.text =
                 TimeOfDay.fromDateTime(field[3]).format(widget.context);
           }
@@ -143,8 +144,7 @@ class DynamicFormState extends State<DynamicForm> {
     // Variable possédant tous les éléments du formulaire à afficher
     final processedFields = <Widget>[];
     var placeholder;
-    final node = FocusScope.of(context);
-    var editingMode;
+    var textInputAction;
 
     // Construction et ajout des champs selon les paramètres entrés
     for (int i = 0; i < controllers.entries.length; i++) {
@@ -159,19 +159,19 @@ class DynamicFormState extends State<DynamicForm> {
 
       // Champ de type String
       if (field.value['type'] == String) {
-        // Assignation de editingMode qui détermine si l'utilisateur peut accéder au champ suivant
-        // directement depuis le clavier. C'est désactivé  (unfocus) si c'est le dernier champ ou si
+        // Assignation de textInputAction qui détermine si l'utilisateur peut accéder au champ suivant
+        // directement depuis le clavier. C'est désactivé (done) si c'est le dernier champ ou si
         // le prochain champ est de type DateTime et requiert que l'utilisateur tape sur le champ pour faire
         // apparaître le calendrier ou l'horloge
         if (i == controllers.entries.length - 1 ||
             controllers.entries.elementAt(i + 1).value['type'] == DateTime)
-          editingMode = () => node.unfocus();
+          textInputAction = TextInputAction.done;
         else
-          editingMode = () => node.nextFocus();
+          textInputAction = TextInputAction.next;
 
         processedFields.add(
           _buildStringField(placeholder, field.value['required'], context,
-              controllers[field.key]['fieldController'], editingMode),
+              controllers[field.key]['fieldController'], textInputAction),
         );
         // Champ de type DateTime
       } else if (field.value['type'] == DateTime) {
@@ -249,51 +249,7 @@ class DynamicFormState extends State<DynamicForm> {
           body: widget.buttonText,
           fontSize: 30.0,
           padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
-          onPressed: () {
-            // Test de validité du formulaire
-            if (_formKey.currentState.validate()) {
-              // Dictionnaire en retour avec les bons types
-              var resMap = Map();
-              controllers.entries.forEach(
-                (controller) {
-                  if (controller.value['type'] == String) {
-                    controller.value.containsKey('value') &&
-                            controller.value['fieldController'].text.length == 0
-                        ? resMap[controller.key] = controller.value['value']
-                        : resMap[controller.key] =
-                            controller.value['fieldController'].text;
-                  } else if (controller.value['type'] == DateTime) {
-                    // Combinaison des champs date et heure si la date n'est pas null
-                    var selectedDate = controller.value['dateValue'];
-                    var selectedTime = controller.value['timeValue'];
-                    // Test de tout type d'entrée possible, qui peuvent arriver si le champ est optionnel
-                    if (selectedDate == null) {
-                      resMap[controller.key] = null;
-                    } else if (selectedDate != null && selectedTime == null) {
-                      resMap[controller.key] = DateTime(
-                        selectedDate.year,
-                        selectedDate.month,
-                        selectedDate.day,
-                      );
-                    } else {
-                      resMap[controller.key] = DateTime(
-                        selectedDate.year,
-                        selectedDate.month,
-                        selectedDate.day,
-                        selectedTime.hour,
-                        selectedTime.minute,
-                      );
-                    }
-                  }
-                },
-              );
-              widget.onSubmit(resMap);
-            } else {
-              setState(() {
-                validForm = false;
-              });
-            }
-          },
+          onPressed: validateForm,
         ),
       ),
     );
@@ -301,6 +257,54 @@ class DynamicFormState extends State<DynamicForm> {
     return processedFields;
   }
 
+  /// Méthode validant le formulaire
+  void validateForm() {
+    // Test de validité du formulaire
+    if (_formKey.currentState.validate()) {
+      // Dictionnaire en retour avec les bons types
+      var resMap = Map();
+      controllers.entries.forEach(
+        (controller) {
+          if (controller.value['type'] == String) {
+            controller.value.containsKey('value') &&
+                    controller.value['fieldController'].text.length == 0
+                ? resMap[controller.key] = controller.value['value']
+                : resMap[controller.key] =
+                    controller.value['fieldController'].text;
+          } else if (controller.value['type'] == DateTime) {
+            // Combinaison des champs date et heure si la date n'est pas null
+            var selectedDate = controller.value['dateValue'];
+            var selectedTime = controller.value['timeValue'];
+            // Test de tout type d'entrée possible, qui peuvent arriver si le champ est optionnel
+            if (selectedDate == null) {
+              resMap[controller.key] = null;
+            } else if (selectedDate != null && selectedTime == null) {
+              resMap[controller.key] = DateTime(
+                selectedDate.year,
+                selectedDate.month,
+                selectedDate.day,
+              );
+            } else {
+              resMap[controller.key] = DateTime(
+                selectedDate.year,
+                selectedDate.month,
+                selectedDate.day,
+                selectedTime.hour,
+                selectedTime.minute,
+              );
+            }
+          }
+        },
+      );
+      // Appel de la fonction passée en paramètre au widget
+      widget.onSubmit(resMap);
+    } else {
+      setState(() {
+        validForm = false;
+      });
+    }
+  }
+
   @override
   Widget build(BuildContext context) {
     // Construction du formulaire avec la clé créée
@@ -353,7 +357,7 @@ Widget _buildStringField(
   bool requiredField,
   BuildContext context,
   TextEditingController controller,
-  Function onEditingComplete,
+  TextInputAction textInputAction,
 ) {
   return Container(
     margin: EdgeInsets.only(top: 20),
@@ -362,7 +366,7 @@ Widget _buildStringField(
       validator: customValidator(requiredField),
       decoration: _buildInputDecoration(name, context),
       controller: controller,
-      onEditingComplete: onEditingComplete,
+      textInputAction: textInputAction,
       style: TextStyle(color: Theme.of(context).primaryColor),
     ),
   );
-- 
GitLab