diff --git a/.env b/.env new file mode 100644 index 0000000..797c39f --- /dev/null +++ b/.env @@ -0,0 +1 @@ +ip_address=127.0.0.1 diff --git a/.gitignore b/.gitignore index e5924f5..e903697 100644 --- a/.gitignore +++ b/.gitignore @@ -55,4 +55,5 @@ scripts/private/ AppDir/ appimage-builder-cache/ AppImageBuilder.yml -android/app/build.gradle \ No newline at end of file +android/app/build.gradle +integration_test/duniter/data/chains/ \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 20e7767..865439e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,7 +5,7 @@ stages: - package .env: - image: axiomteam/gecko-ci:v0.0.9 + image: axiomteam/gecko-ci:v0.0.11 tags: - redshift diff --git a/assets/translations/fr.json b/assets/translations/fr.json index 57056c4..bfe26e6 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -148,7 +148,7 @@ "seeAWallet": "Voir un portefeuille", "mustWaitXBeforeCertify": "Vous devez attendre\n{} avant\nde pouvoir certifier", "mustConfirmHisIdentity": "Cette personne doit confirmer\nson identité avant de pouvoir\nêtre certifié", - "canRenewCertInX": "Vous pourrez renouveller\ncette certification\ndans {}", + "canRenewCertInX": "Vous pourrez renouveler\ncette certification\ndans {}", "executeATransfer": "Effectuer un virement", "executeTheTransfer": "Effectuer le virement", "doATransfer": "Faire un\nvirement", diff --git a/integration_test/README.md b/integration_test/README.md new file mode 100644 index 0000000..9583901 --- /dev/null +++ b/integration_test/README.md @@ -0,0 +1,128 @@ +# Context des tests + +Chaque test est précédé par le lancement d'un noeud Duniter v2s en docker [dont voici le compose](https://git.duniter.org/clients/gecko/-/blob/end2EndTests/integration_test/duniter/docker-compose.yml). + +Voici le yaml de configuration de la monnaie de test éphémère: https://git.duniter.org/clients/gecko/-/blob/end2EndTests/integration_test/duniter/data/gecko_tests.json + + +Voici le mnemonic de test utilisé: +`pipe paddle ketchup filter life ice feel embody glide quantum ride usage` + +Et les 5 premiers portefeuilles Gecko associés: +``` +test1: 5FeggKqw2AbnGZF9Y9WPM2QTgzENS3Hit94Ewgmzdg5a3LNa +test2: 5E4i8vcNjnrDp21Sbnp32WHm2gz8YP3GGFwmdpfg5bHd8Whb +test3: 5FhTLzXLNBPmtXtDBFECmD7fvKmTtTQDtvBTfVr97tachA1p +test4: 5DXJ4CusmCg8S1yF6JGVn4fxgk5oFx42WctXqHZ17mykgje5 +test5: 5Dq3giahrBfykJogPetZJ2jjSmhw49Fa7i6qKkseUvRJ2T3R +``` + +Seul les 4 premiers sont membres au démarrage. + +Voici le scénario de test principal que j'ai réalisé pour le moment + +Scénario 1 +- Changer le noeud Duniter pour se connecter au nœud local (l'ip local est récupéré automatiquement, car le nœud est sur le host (votre pc), alors que l'app est dans son émulateur) +- Importer le coffre de test +- Effectuer une transaction du Portefeuille 1 (test1) vers le portefeuille 5 (test5) +- Vérifier que les frais de créations de compte ont bien été prélevés +- Certifier test5 avec test1, test2 et test3 et vérifier qu'il deviens bien membre +- Créer 10 blocs, puis encore 10, puis 30 de plus et vérifier à chaque fois si le compte génère bien ses DU à la bonne valeur, réévaluation comprise au bloc 50. + +Des vérifications sur l'état du texte affiché à l'écran ou des widgets affichés ou non sont fait entre chaque étapes pour vérifier que tout ce passe toujours bien. +Si la moindre erreur intervient, le test s'arrête et vous informe de l'erreur en question. + +Voici le code du test contenant ce scénario: https://git.duniter.org/clients/gecko/-/blob/end2EndTests/integration_test/gecko_complete.dart + +Ce test dur environ 1 minutes et 15 seconds, compilation et lancement de nœud au démarrage inclus. + +Voici le rendu (attention ça va assez vite ^^) : + + +https://tube.p2p.legal/w/kMc5c8KnLi9BpwJrM4EnKX + +On remarque notamment que des blocs sont créés uniquement et directement après un extrinsic lancé depuis l'app + +--- + +# Tuto contributeurs + +**Il n'est nécessaire ni de connaître le code de Ğecko, ni de connaître Dart/flutter pour écrire un nouveau scénario de test !** + +Il vous suffit de comprendre par exemple cet extrait de code: + +``` +// Copy test mnemonic in clipboard +await clipCopy(testMnemonic); + +// Open screen import chest +await goKey(keyRestoreChest, duration: 0); + +// Tap on button to paste mnemonic +await goKey(keyPastMnemonic); + +// Tap on next button 4 times to skip 3 screen +await goKey(keyGoNext); +await goKey(keyGoNext); +await goKey(keyGoNext); +await goKey(keyGoNext); + +// Check if cached password checkbox is checked +final isCached = await isIconPresent(Icons.check_box); + +// If not, tap on to cache password +if (!isCached) await goKey(keyCachePassword, duration: 0); + +// Enter password +await enterText(keyPinForm, 'AAAAA', 0); + +// Check if string "Accéder à mon coffre" is present in screen +await waitFor('Accéder à mon coffre'); + +// Go to wallets home +await goKey(keyGoWalletsHome, duration: 0); + +// Check if string "ĞD" is present in screen +await waitFor('ĞD'); + +// Tap on add a new derivation button +await addDerivation(); + +// Tap on Wallet 5 +await goKey(keyOpenWallet(test5.address)); + +// Copy address of Wallet 5 +await goKey(keyCopyAddress); + +// Check if string "Cette adresse a été copié" is present in screen +await waitFor('Cette adresse a été copié'); + +// Pop screen 2 time to go back home +await goBack(); +await goBack(); + +// Create a new bloc (useless here, just to show you the method) +await spawnBlock(); + +// Check if string "y'a pas de lézard" is present in screen +await waitFor("y'a pas de lézard"); +``` + +Vous avez dans ce bout de code commenté tous ce dont vous avez besoin pour effectuer un test d'intégration dans Ğecko :slight_smile: + +Vous trouverez toutes les clés de widgets disponibles dans l'app dans ce fichier: https://git.duniter.org/clients/gecko/-/blob/end2EndTests/lib/models/widgets_keys.dart + +Ce sont ces clés qui vous permette d’interagir avec les widgets de l'app depuis votre test. + +Pour créer un nouveau test **à partir de zero**, voici la marche à suivre: +- Suivez [le readme](https://git.duniter.org/clients/gecko/-/blob/master/README.md) pour configurer votre environnement de développement et ainsi pouvoir lancer Ğecko en mode debug dans un émulateur. +- Créer un nouveau fichier pour votre test dans le dossier `integration_test` (ici nous l’appellerons `mon_test.dart`) +- Prenez exemple sur le fichier `gecko_complete.dart` pour écrire votre test +- Lancer un émulateur android (1 seul) +- Exécutez votre test ainsi: `./integration_test/launch_test.sh mon_test` + +Créer toute sorte de tests imaginable dans Ğecko est très utile pour éviter un maximum les régressions de bugs entre les différentes versions. + +Si vous avez envie de nous aider, que vous ne savez presque pas coder mais que vous êtes prêt à mettre un peu les mains dans la sauce, et que vous avez une idée de scénario à tester, alors n'hésitez pas, je répondrais à toutes vos questions :slight_smile: + +A noter que ces tests permettent de tester Gecko mais aussi partiellement Duniter et l'indexer d'une même pierre. diff --git a/integration_test/app_test_integration.dart.disable b/integration_test/app_test_integration.dart.disable deleted file mode 100644 index eeec460..0000000 --- a/integration_test/app_test_integration.dart.disable +++ /dev/null @@ -1,447 +0,0 @@ -import 'package:flutter/material.dart'; -import 'dart:io'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; - -import 'package:gecko/main.dart' as app; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - int globalTimeout = 2; - group( - 'Gecko end-to-end tests', - () { - // First, define the Finders and use them to locate widgets from the - // test suite. Note: the Strings provided to the `byValueKey` method must - // be the same as the Strings we used for the Keys in step 1. - // final manageWalletsFinder = find.byKey(Key('manageWallets')); - // final buttonFinder = find.byValueKey('increment'); - - // FlutterDriver driver; - WidgetTester tester; - String pinCode; - - // *** Global functions *** // - - // Easy get text - Future getText(String text) async { - Text resultText = tester.firstWidget(find.byKey(Key(text))); - // Text pinCodeText = generatedPinFinder.evaluate().single.widget as Text; - - return resultText.data; - } - - // Function to tap the widget by key - Future tapOn(String key) async { - await tester.tap(find.byKey(Key(key))); - } - - // Function to go back to previous screen - Future goBack() async { - await Process.run( - 'adb', - ['shell', 'input', 'keyevent', 'KEYCODE_BACK'], - runInShell: true, - ); - } - - // Easy sleep - Future sleep(int _time) async { - await Future.delayed(Duration(milliseconds: _time)); - } - - // Test if widget exist on screen, return a boolean - Future isPresent(String text, - {Duration timeout = const Duration(seconds: 1)}) async { - try { - expect(text, findsOneWidget); - return true; - } catch (exception) { - return false; - } - } - - // Create a derivation - Future createDerivation(String _name) async { - await tapOn('addDerivation'); - await sleep(100); - - await tester.enterText(find.byKey(Key('DerivationNameKey')), _name); - - await tapOn('validDerivation'); - await sleep(300); - } - - // Delete a derivation - Future deleteWallet(bool _confirm) async { - await tapOn('deleteWallet'); - await sleep(100); - _confirm - ? await tapOn('confirmDeleting') - : await tapOn('cancelDeleting'); - await sleep(300); - } - - // Delete all wallets - Future deleteAllWallets() async { - await tester.tap(find.byKey(Key('drawerMenu'))); - await sleep(300); - await tester.tap(find.byKey(Key('parameters'))); - await sleep(300); - await tester.tap(find.byKey(Key('deleteAllWallets'))); - await sleep(300); - await tester.tap(find.byKey(Key('confirmDeletingAllWallets'))); - await sleep(300); - } - - // Fast creation of new Keychain - Future createNewKeychain(String name) async { - await tapOn('drawerMenu'); - await sleep(300); - await tapOn('parameters'); - await sleep(300); - await tapOn('generateKeychain'); - expect(find.text(''), findsOneWidget); - - pinCode = await getText('generatedPin'); - - await tapOn('storeKeychain'); - await sleep(100); - await tester.enterText(find.byKey(Key('askedWord')), 'triche'); - await tapOn('walletName'); - await tester.enterText(find.byKey(Key('walletName')), 'name'); - await tapOn('confirmStorage'); - await sleep(300); - return pinCode; - } - - // *** Begin of tests *** // - - testWidgets('OnBoarding - Open wallets management', ( - WidgetTester tester, { - timeout: Timeout.none, - }) async { - app.main(); - await tester.pumpAndSettle(); - - // expect("y'a pas de lézard !", findsOneWidget); - await tester.tap(find.byKey(Key('manageWallets'))); - - print( - '####################################################################'); - - // If a wallet exist, go to delete theme all - await tester.pumpAndSettle(); - if (!await isPresent( - "Je ne connais pour l’instant aucun de vos portefeuilles.\n\nVous pouvez en créer un nouveau, ou bien importer un portefeuille Cesium existant.")) { - await tester.pumpAndSettle(); - // await tester.pageBack(); - await goBack(); - - await sleep(500); - await deleteAllWallets(); - - await sleep(300); - await tester.tap(find.byKey(Key('manageWallets'))); - } - - await tester.pumpAndSettle(); - - // Verify onboarding is starting, with text - expect( - "Je ne connais pour l’instant aucun de vos portefeuilles.\n\nVous pouvez en créer un nouveau, ou bien importer un portefeuille Cesium existant.", - findsOneWidget); - }); - - // test('OnBoarding - Go to create restore sentance', ( - // {timeout: Timeout.none}) async { - // await tapOn('goStep1'); - // await tapOn('goStep2'); - // await tapOn('goStep3'); - // await tapOn('goStep4'); - // await tapOn('goStep5'); - // await tapOn('goStep6'); - - // expect( - // "J’ai généré votre phrase de restauration !\nTâchez de la garder bien secrète, car elle permet à quiconque la connaît d’accéder à tous vos portefeuilles.", - // findsOneWidget); - // }); - - // test('OnBoarding - Generate sentance and confirme it', ( - // {timeout: Timeout.none}) async { - // await tapOn('goStep7'); - - // await tester.pumpAndSettle(); - - // Future selectWord() async { - // List words = [for (var i = 1; i <= 13; i += 1) i]; - - // for (var j = 1; j < 13; j++) { - // words[j] = await getText('word$j'); - // } - // expect(await getText('step7'), - // "C'est le moment de noter votre phrase !"); - - // await tapOn('goStep8'); - // await sleep(200); - - // String goodWord = words[int.parse( - // await getText('askedWord'), - // )]; - - // // Enter the expected word - // await tester.enterText(find.byKey(Key('inputWord')), goodWord); - - // // Check if word is valid - // expect(find.text("C'est le bon mot !"), findsOneWidget); - - // // Continue onboarding workflow - // await tapOn('goStep9'); - // } - - // await selectWord(); - - // //Go back 2 times to mnemonic generation screen - // await goBack(); - // await goBack(); - // await sleep(100); - - // // Generate 3 times mnemonic - // await tapOn('generateMnemonic'); - // await tapOn('generateMnemonic'); - // await tapOn('generateMnemonic'); - // await sleep(500); - - // await selectWord(); - // }); - // test('OnBoarding - Generate secret code and confirm it', ( - // {timeout: Timeout.none}) async { - // expect(await getText('step9'), - // "Super !\n\nJe vais maintenant créer votre code secret. \n\nVotre code secret chiffre votre trousseau de clefs, ce qui le rend inutilisable par d’autres, par exemple si vous perdez votre téléphone ou si on vous le vole."); - - // await tapOn('goStep10'); - // await tapOn('goStep11'); - - // while (await getText('generatedPin') == '') { - // print('Waiting for pin code generation...'); - // await sleep(100); - // } - - // // Change secret code 4 times - // for (int i = 0; i < 4; i++) await tapOn('changeSecretCode'); - - // await sleep(500); - // pinCode = await getText('generatedPin'); - - // await tapOn('goStep12'); - // await sleep(300); - - // // //Enter bad secret code - // // await tester.enterText('abcde'); - // // await tapOn('formKey'); - // // await sleep(1500); - // // await tapOn('formKey2'); - - // //Enter good secret code - // await tester.enterText(find.byKey(Key('formKey2')), pinCode); - - // expect(await getText('step13'), - // "Top !\n\nVotre trousseau de clef et votre portefeuille ont été créés avec un immense succès.\n\nFélicitations !"); - // }); - - // test('My wallets - Rename first derivation', ( - // {timeout: const Duration(seconds: 2)}) async { - // await tapOn('goWalletHome'); - - // expect(await getText('myWallets'), "Mes portefeuilles"); - // await sleep(300); - - // // Go to first derivation and rename it - // await tester.tap(find.text('Mon portefeuille courant')); - // await sleep(300); - // await tapOn('renameWallet'); - // await sleep(100); - // await tapOn('walletName'); - // await sleep(100); - // await tester.enterText( - // find.byKey(Key('walletName')), 'Renommage wallet 1'); - // await sleep(300); - // await tapOn('renameWallet'); - // await sleep(400); - // expect('Renommage wallet 1', findsOneWidget); - // await goBack(); - // }); - - // test('My wallets - Create a derivations, open thems, tap all buttons', ( - // {timeout: const Duration(seconds: 2)}) async { - // expect('Renommage wallet 1', findsOneWidget); - - // // Add a second derivation - // await createDerivation('Derivation 2'); - - // // Go to second derivation options - // await tester.tap(find.text('Derivation 2')); - // await sleep(100); - - // // Test options - // await tapOn('displayBalance'); - // await tapOn('displayHistory'); - // await sleep(300); - // await goBack(); - // await tapOn('displayBalance'); - // await sleep(100); - // await tapOn('displayBalance'); - // await sleep(100); - // await tapOn('displayBalance'); - // await tapOn('setDefaultWallet'); - // await sleep(50); - // await tapOn('copyPubkey'); - // expect('Cette clé publique a été copié dans votre presse-papier.', - // findsOneWidget); - // await goBack(); - - // // Add a third derivation - // await createDerivation('Derivation 3'); - - // // Add a fourth derivation - // await createDerivation('Derivation 4'); - // await sleep(50); - - // // Go to third derivation options - // await tester.tap(find.text('Derivation 3')); - // await sleep(100); - // await tapOn('displayBalance'); - - // // Delete third derivation - // await deleteWallet(true); - // }); - - // test('My wallets - Extra tests', ( - // {timeout: const Duration(seconds: 2)}) async { - // // Add derivation 5,6 and 7 - // expect('Derivation 4', findsOneWidget); - // await createDerivation('Derivation 5'); - // await createDerivation('Derivation 6'); - // await createDerivation('Derivation 7'); - - // // Go home and come back to my wallets view - // await goBack(); - // await sleep(100); - // await tapOn('manageWallets'); - // await sleep(200); - // //Enter secret code - // await tester.enterText(find.byKey(Key('formKey')), pinCode); - // await sleep(200); - - // // Go to derivation 6 and delete it - // await tester.tap(find.text('Derivation 6')); - // await sleep(100); - // await deleteWallet(true); - - // // Go to 2nd derivation and check if it's de default - // await tester.tap(find.text('Derivation 2')); - // expect('Ce portefeuille est celui par defaut', findsOneWidget); - // await tapOn('setDefaultWallet'); - // await sleep(100); - // expect('Ce portefeuille est celui par defaut', findsOneWidget); - // await sleep(300); - - // // Display history, copy pubkey, go back and rename wallet name - // await tapOn('displayHistory'); - // await sleep(400); - // await tapOn('copyPubkey'); - // expect('Cette clé publique a été copié dans votre presse-papier.', - // findsOneWidget); - // await sleep(800); - // await goBack(); - // await sleep(300); - // await tapOn('renameWallet'); - // await sleep(100); - // await tapOn('walletName'); - // await sleep(100); - // await tester.enterText( - // find.byKey(Key('walletName')), 'Renommage wallet 2'); - // await sleep(300); - // await tapOn('renameWallet'); - // await sleep(400); - // await goBack(); - // expect('Renommage wallet 2', findsOneWidget); - // await createDerivation('Derivation 8'); - // await createDerivation('Derivation 9'); - // await createDerivation('Derivation 10'); - // await createDerivation('Derivation 11'); - // await createDerivation('Derivation 12'); - // await createDerivation('Derivation 13'); - // await createDerivation('Derivation 14'); - // await createDerivation('Derivation 15'); - // await createDerivation('Derivation 16'); - // await createDerivation('Derivation 17'); - // await createDerivation('Derivation 18'); - // await createDerivation('Derivation 19'); - // await createDerivation('Derivation 20'); - // await sleep(400); - - // // Scroll the wallet screen until Derivation 20 and open it - // await tester.scrollUntilVisible(find.byKey(Key('listWallets')), -300.0); - - // expect('Derivation 20', findsOneWidget); - // await sleep(400); - // await tester.tap(find.text('Derivation 20')); - // await tapOn('copyPubkey'); - // }); - - // test('Search - Search Pi profile, navigate in history transactions', ( - // {timeout: const Duration(seconds: 2)}) async { - // expect('Derivation 20', findsOneWidget); - // await goBack(); - // await goBack(); - // await sleep(200); - // await tapOn('searchIcon'); - // await sleep(400); - // await tester.enterText(find.byKey(Key('searchInput')), - // 'D2meevcAHFTS2gQMvmRW5Hzi25jDdikk4nC4u1FkwRaU'); - // await sleep(100); - // await tapOn('copyPubkey'); - // await sleep(500); - // await tapOn('switchPayHistory'); - // await sleep(1200); - // // await tester.scrollIntoView(find.byValueKey('listTransactions')); - // await tester.scrollUntilVisible( - // find.byKey(Key('listTransactions')), - // -600.0, - // ); - // await sleep(100); - // await tapOn('transaction33'); - // expect('Commentaire:', findsOneWidget); - - // // Want to paste pubkey copied, but doesn't work actualy with flutter driver: https://github.com/flutter/flutter/issues/47448 - // // final ClipboardData pubkeyCopied = - // // await Clipboard.getData(Clipboard.kTextPlain); - // // await tester.enterText(pubkeyCopied.text); - - // await sleep(300); - // }, timeout: Timeout(Duration(minutes: globalTimeout))); - - // test('Wallet generation - Fast wallets generations', ( - // {timeout: const Duration(seconds: 2)}) async { - // expect('Commentaire:', findsOneWidget); - // await goBack(); - // await goBack(); - // await deleteAllWallets(); - // await sleep(100); - // final String pincode = await createNewKeychain('Fast wallet'); - // await sleep(100); - // await tapOn('manageWallets'); - // await sleep(200); - // await tester.enterText(find.byKey(Key('formKey')), pinCode); - // await sleep(100); - // await createDerivation('Derivation 2'); - // await sleep(100); - // await tester.tap(find.text('Fast wallet')); - // expect('Fast wallet', findsOneWidget); - // // Wait 3 seconds at the end - // await sleep(3000); - // }); - }, - ); -} diff --git a/integration_test/cert_state.dart b/integration_test/cert_state.dart new file mode 100644 index 0000000..f36dc5c --- /dev/null +++ b/integration_test/cert_state.dart @@ -0,0 +1,55 @@ +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:gecko/globals.dart'; +import 'package:gecko/models/widgets_keys.dart'; +import 'package:integration_test/integration_test.dart'; +import 'general_actions.dart'; +import 'tests_utility.dart'; + +void main() async { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + await dotenv.load(); + + testWidgets('Certifications state', (testerLoc) async { + tester = testerLoc; + // Connect local node and import test chest in background + await bkFastStart(); + + // Open chest + await firstOpenChest(); + await goBack(); + + // Go wallet 5 view + await tapKey(keyOpenSearch); + await enterText(keySearchField, test5.address); + await tapKey(keyConfirmSearch); + await waitFor(test5.shortAddress()); + await tapKey(keySearchResult(test5.address)); + await waitFor('Certifier'); + await waitFor('Vous devez ', reverse: true); + await waitFor('Vous pourrez renouveler ', reverse: true); + + // Background pay 25 + await bkPay( + fromAddress: test1.address, destAddress: test5.address, amount: 25); + await waitFor('25.0 $currencyName'); + await spawnBlock(); + await waitFor('22.0 $currencyName'); + await bkCertify(fromAddress: test1.address, destAddress: test5.address); + await waitFor('1', exactMatch: true); + await bkConfirmIdentity(fromAddress: test5.address, name: test5.name); + await bkCertify(fromAddress: test2.address, destAddress: test5.address); + await waitFor('2', exactMatch: true); + await bkCertify(fromAddress: test3.address, destAddress: test5.address); + await waitFor('3', exactMatch: true); + await bkCertify(fromAddress: test4.address, destAddress: test5.address); + await waitFor('4', exactMatch: true); + await bkPay( + fromAddress: test2.address, destAddress: test5.address, amount: 40); + await waitFor('61.99 $currencyName'); + await spawnBlock(until: 10); + await waitFor('161.99 $currencyName'); + await spawnBlock(until: 20); + await waitFor('261.99 $currencyName'); + }, timeout: testTimeout()); +} diff --git a/integration_test/duniter/data/gecko_tests.json b/integration_test/duniter/data/gecko_tests.json new file mode 100755 index 0000000..804b3de --- /dev/null +++ b/integration_test/duniter/data/gecko_tests.json @@ -0,0 +1,79 @@ +{ + "first_ud": 10000, + "first_ud_reeval": 50, + "genesis_parameters": { + "genesis_certs_expire_on": 500, + "genesis_certs_min_received": 3, + "genesis_memberships_expire_on": 1051200, + "genesis_smith_certs_expire_on": 2102400, + "genesis_smith_certs_min_received": 3, + "genesis_smith_memberships_expire_on": 1051200 + }, + "identities": { + "test1": { + "balance": 10000, + "certs": ["test2", "test3", "test4"], + "pubkey": "5FeggKqw2AbnGZF9Y9WPM2QTgzENS3Hit94Ewgmzdg5a3LNa" + }, + "test2": { + "balance": 10000, + "certs": ["test1", "test3", "test4"], + "pubkey": "5E4i8vcNjnrDp21Sbnp32WHm2gz8YP3GGFwmdpfg5bHd8Whb" + }, + "test3": { + "balance": 10000, + "certs": ["test1", "test2", "test4"], + "pubkey": "5FhTLzXLNBPmtXtDBFECmD7fvKmTtTQDtvBTfVr97tachA1p" + }, + "test4": { + "balance": 10000, + "certs": ["test1", "test2", "test3"], + "pubkey": "5DXJ4CusmCg8S1yF6JGVn4fxgk5oFx42WctXqHZ17mykgje5" + }, + "testCesium1": { + "balance": 10000, + "certs": ["test1", "test2", "test3"], + "pubkey": "5GAT6CJW8yVKwUuQc7sM5Kk9GZVTpbZYk9PfjNXtvnNgAJZ1" + } + }, + "parameters": { + "babe_epoch_duration": 30, + "cert_period": 15, + "cert_max_by_issuer": 10, + "cert_min_received_cert_to_issue_cert": 2, + "cert_validity_period": 1000, + "idty_confirm_period": 40, + "idty_creation_period": 50, + "membership_period": 1000, + "pending_membership_period": 500, + "ud_creation_period": 10, + "ud_reeval_period": 50, + "smith_cert_period": 15, + "smith_cert_max_by_issuer": 8, + "smith_cert_min_received_cert_to_issue_cert": 2, + "smith_cert_validity_period": 1000, + "smith_membership_period": 1000, + "smith_pending_membership_period": 500, + "smiths_wot_first_cert_issuable_on": 20, + "smiths_wot_min_cert_for_membership": 3, + "wot_first_cert_issuable_on": 0, + "wot_min_cert_for_create_idty_right": 3, + "wot_min_cert_for_membership": 3 + }, + "smiths": { + "test1": { + "certs": ["test2", "test3", "test4"] + }, + "test2": { + "certs": ["test1", "test3", "test4"] + }, + "test3": { + "certs": ["test1", "test2", "test4"] + }, + "test4": { + "certs": ["test1", "test2", "test3"] + } + }, + "sudo_key": "5FeggKqw2AbnGZF9Y9WPM2QTgzENS3Hit94Ewgmzdg5a3LNa", + "technical_committee": ["test1", "test2", "test3"] +} diff --git a/integration_test/duniter/docker-compose.yml b/integration_test/duniter/docker-compose.yml new file mode 100644 index 0000000..5180193 --- /dev/null +++ b/integration_test/duniter/docker-compose.yml @@ -0,0 +1,18 @@ +version: "3.5" + +services: + duniter-v2s-gecko-tests: + container_name: duniter-v2s-gecko-tests + image: duniter/duniter-v2s:debug-latest + command: --sealing=manual + ports: + - "127.0.0.1:9615:9615" + - "127.0.0.1:9933:9933" + - "0.0.0.0:9944:9944" + - "30333:30333" + environment: + DUNITER_INSTANCE_NAME: "gecko_tests" + DUNITER_CHAIN_NAME: "dev" + DUNITER_GENESIS_CONFIG: "/var/lib/duniter/gecko_tests.json" + volumes: + - ./data:/var/lib/duniter diff --git a/integration_test/gecko_complete.dart b/integration_test/gecko_complete.dart new file mode 100644 index 0000000..bcf1c23 --- /dev/null +++ b/integration_test/gecko_complete.dart @@ -0,0 +1,139 @@ +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:gecko/models/widgets_keys.dart'; +import 'package:integration_test/integration_test.dart'; +import 'general_actions.dart'; +import 'tests_utility.dart'; + +void main() async { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + await dotenv.load(); + + testWidgets('Gecko complete', (testerLoc) async { + // Share WidgetTester to test provider + tester = testerLoc; + + // Start app and wait finish starting + await startWait(); + + // Change Duniter endpoint to local + await changeNode(); + + // Delete all existing chests is exists + await deleteAllWallets(); + + // Restore the test chest + await restoreChest(); + + // Execute a transaction to test5 + await payTest2(); + + // Certify test5 account with 3 accounts to become member + await certifyTest5(); + }, timeout: testTimeout()); +} + +Future payTest2() async { + await waitFor('Rechercher'); + await tapKey(keyOpenSearch); + final addressToSearch = await clipPaste(); + final endAddress = addressToSearch.substring(addressToSearch.length - 6); + expect(addressToSearch, test5.address); + await enterText(keySearchField, addressToSearch); + await tapKey(keyConfirmSearch); + await waitFor(endAddress); + await tapKey(keySearchResult(addressToSearch)); + await waitFor(endAddress); + await waitFor('0.0 ĞD'); + await tapKey(keyPay); + await enterText(keyAmountField, '12.14'); + await tapKey(keyConfirmPayment); + spawnBlock(duration: 500); + + await waitFor('validé !', timeout: const Duration(seconds: 1)); + await tapKey(keyCloseTransactionScreen, duration: 0); + await waitFor('12.14'); + spawnBlock(duration: 500); + await waitFor('9.14'); + humanRead(2); +} + +Future certifyTest5() async { + // Create identity with Test1 account + await tapKey(keyCertify); + await tapKey(keyConfirm); + spawnBlock(duration: 500); + await waitFor('validé !', timeout: const Duration(seconds: 1)); + await tapKey(keyCloseTransactionScreen); + await waitFor('Identité créée'); + + // Confirm Identity Test5 + await tapKey(keyAppBarChest, duration: 300); + await tapKey(keyOpenWallet(test5.address)); + await tapKey(keyCopyAddress); + humanRead(3); + await tapKey(keyConfirmIdentity); + await enterText(keyEnterIdentityUsername, test5.name); + await tapKey(keyConfirm); + spawnBlock(duration: 500); + await waitFor('validé !', timeout: const Duration(seconds: 1)); + await tapKey(keyCloseTransactionScreen); + await waitFor('Identité confirmée'); + humanRead(2); + // Set wallet 2 as default wallet + await goBack(); + await tapKey(keyOpenWallet(test2.address)); + await tapKey(keySetDefaultWallet); + await waitFor('Ce portefeuille est celui par defaut'); + + // Search Wallet 5 again + await tapKey(keyAppBarSearch); + final addressToSearch = await clipPaste(); + final endAddress = addressToSearch.substring(addressToSearch.length - 6); + expect(addressToSearch, test5.address); + await enterText(keySearchField, addressToSearch); + await tapKey(keyConfirmSearch); + await waitFor(endAddress); + await tapKey(keySearchResult(addressToSearch)); + await waitFor(endAddress); + await waitFor('1'); + + // Certify with test2 account + await tapKey(keyCertify); + await tapKey(keyConfirm); + spawnBlock(duration: 500); + await waitFor('validé !', timeout: const Duration(seconds: 1)); + await tapKey(keyCloseTransactionScreen); + await waitFor('2'); + + // Change default wallet to test3 + await tapKey(keyPay); + await tapKey(keyChangeChest); + await tapKey(keySelectThisWallet(test3.address)); + await tapKey(keyConfirm); + await sleep(); + + // Certify with test3 account + await tapKey(keyCertify); + await tapKey(keyConfirm); + spawnBlock(duration: 500); + await waitFor('validé !', timeout: const Duration(seconds: 1)); + await tapKey(keyCloseTransactionScreen); + await waitFor('Vous devez attendre'); + + // Check if test5 is member + await tapKey(keyAppBarChest, duration: 300); + await tapKey(keyOpenWallet(test5.address)); + await waitFor('Membre validé !'); + + // spawn 20 blocs and check if ud is creating + await spawnBlock(until: 10); + await waitFor('109.13'); + await spawnBlock(until: 20); + await waitFor('209.13'); + + // Check UD reval + await spawnBlock(until: 50); + await waitFor('509.36'); + humanRead(5); +} diff --git a/integration_test/general_actions.dart b/integration_test/general_actions.dart new file mode 100644 index 0000000..612675a --- /dev/null +++ b/integration_test/general_actions.dart @@ -0,0 +1,163 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:gecko/globals.dart'; +import 'package:gecko/models/widgets_keys.dart'; +import 'package:gecko/providers/generate_wallets.dart'; +import 'package:provider/provider.dart'; +import 'tests_utility.dart'; + +// GENERAL ACTIONS + +Future changeNode() async { + final ipAddress = dotenv.env['ip_address'] ?? '127.0.0.1'; + log.d('ip address: $ipAddress'); + + await tapKey(keyDrawerMenu); + await tapKey(keyParameters); + await tapKey(keySelectDuniterNodeDropDown, duration: 5); + await tapKey(keySelectDuniterNode('Personnalisé'), selectLast: true); + await enterText(keyCustomDuniterEndpoint, 'ws://$ipAddress:9944'); + await tapKey(keyConnectToEndpoint); + await isIconPresent(Icons.add_card_sharp, + timeout: const Duration(seconds: 8)); + await goBack(); +} + +Future deleteAllWallets() async { + if (await isPresent('Rechercher')) { + await tapKey(keyDrawerMenu); + await tapKey(keyParameters); + await tapKey(keyDeleteAllWallets); + await tapKey(keyConfirm); + await tester.pumpAndSettle(); + } +} + +Future restoreChest() async { + // Copy test mnemonic in clipboard + await clipCopy(testMnemonic); + + // Open screen import chest + await tapKey(keyRestoreChest, duration: 0); + + // Tap on button to paste mnemonic + await tapKey(keyPastMnemonic); + + // Tap on next button 4 times to skip 3 screen + await tapKey(keyGoNext); + await tapKey(keyGoNext); + await tapKey(keyGoNext); + await tapKey(keyGoNext); + + // Check if cached password checkbox is checked + final isCached = await isIconPresent(Icons.check_box); + + // If not, tap on to cache password + if (!isCached) await tapKey(keyCachePassword, duration: 0); + + // Enter password + await enterText(keyPinForm, 'AAAAA', 0); + + // Check if string "Accéder à mon coffre" is present in screen + await waitFor('Accéder à mon coffre'); + + // Go to wallets home + await tapKey(keyGoWalletsHome, duration: 0); + + // Check if string "ĞD" is present in screen + await waitFor('ĞD'); + + // Tap on add a new derivation button + await addDerivation(); + + // Tap on Wallet 5 + await tapKey(keyOpenWallet(test5.address)); + + // Copy address of Wallet 5 + await tapKey(keyCopyAddress); + + // Check if string "Cette adresse a été copié" is present in screen + await waitFor('Cette adresse a été copié'); + + // Pop screen 2 time to go back home + await goBack(); + await goBack(); +} + +Future onboardingNewChest() async { + final generateWalletProvider = + Provider.of(homeContext, listen: false); + // Open screen create new wallet + await tapKey(keyOnboardingNewChest); + + // Tap on next button 4 times to skip 3 screen + await tapKey(keyGoNext); + await tapKey(keyGoNext); + await tapKey(keyGoNext); + await tapKey(keyGoNext); + await waitFor('7', exactMatch: true); + + final word41 = getWidgetText(keyMnemonicWord('4')); + + // Change 2 times mnemonic + await tapKey(keyGenerateMnemonic); + await tester.pumpAndSettle(); + final word42 = getWidgetText(keyMnemonicWord('4')); + expect(word41, isNot(word42)); + await tapKey(keyGenerateMnemonic, duration: 500); + await tester.pumpAndSettle(); + final word43 = getWidgetText(keyMnemonicWord('4')); + expect(word42, isNot(word43)); + + // Go next screen + await tapKey(keyGoNext); + await tester.pumpAndSettle(); + + // Enter asked word + final askedWordNumber = int.parse(getWidgetText(keyAskedWord)); + List mnemonic = generateWalletProvider.generatedMnemonic!.split(' '); + + final askedWord = mnemonic[askedWordNumber - 1]; + await enterText(keyInputWord, askedWord); + await waitFor('Continuer', exactMatch: true); + await tapKey(keyGoNext); + await tapKey(keyGoNext); + await tapKey(keyGoNext); + await waitFor('AAAAA', exactMatch: true); + await tapKey(keyGoNext); + + // Check if cached password checkbox is checked + final isCached = await isIconPresent(Icons.check_box); + + // If not, tap on to cache password + if (!isCached) await tapKey(keyCachePassword, duration: 0); + + // Enter password + await enterText(keyPinForm, 'AAAAA', 0); + + // Check if string "Accéder à mon coffre" is present in screen + await waitFor('Accéder à mon coffre'); + + // Go to wallets home + await tapKey(keyGoWalletsHome, duration: 0); + + // Check if string "ĞD" is present in screen + await waitFor('Mon portefeuille co'); + await waitFor('0.0 $currencyName'); + // await waitFor('Scanner un'); +} + +Future addDerivation() async { + await tapKey(keyAddDerivation); + await waitFor('Portefeuille 5'); +} + +Future firstOpenChest() async { + await tapKey(keyOpenWalletsHomme); + sleep(300); + final isCached = await isIconPresent(Icons.check_box); + if (!isCached) await tapKey(keyCachePassword, duration: 0); + await enterText(keyPinForm, 'AAAAA', 0); + await waitFor('100.0 $currencyName'); +} diff --git a/integration_test/launch_test.sh b/integration_test/launch_test.sh new file mode 100755 index 0000000..7f130c0 --- /dev/null +++ b/integration_test/launch_test.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +testName=$1 +option=$2 +[[ ! $testName ]] && testName='gecko_complete' + +# Get local IP and set .env +ip_address=$(hostname -I | awk '{print $1}') +echo "ip_address=$ip_address" > .env +[[ $option == 'human' ]] && echo "isHumanReading=true" >> .env + +## Start local Duniter node +cd integration_test/duniter +docker compose down +rm -rf data/chains +docker compose up -d +cd ../.. + +# Start integration test +flutter test integration_test/$testName.dart && echo '0' > /tmp/geckoTestResult || echo '1' > /tmp/geckoTestResult + +# Reset .env +echo "ip_address=127.0.0.1" > .env + +# Stop Duniter +cd integration_test/duniter +docker compose down + + diff --git a/integration_test/migrate_cesium_identity.dart b/integration_test/migrate_cesium_identity.dart new file mode 100644 index 0000000..e57720f --- /dev/null +++ b/integration_test/migrate_cesium_identity.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:gecko/globals.dart'; +import 'package:gecko/models/widgets_keys.dart'; +import 'package:integration_test/integration_test.dart'; +import 'general_actions.dart'; +import 'tests_utility.dart'; + +void main() async { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + await dotenv.load(); + + testWidgets('Migrate Cesium identity and balance', (testerLoc) async { + tester = testerLoc; + // Connect local node and import test chest in background + await bkFastStart(); + + // Open chest + await firstOpenChest(); + + // Go to test1 options and check if balance growup with UDs creations + await tapKey(keyAddDerivation); + await waitFor('Portefeuille 6'); + + await scrollUntil(keyImportG1v1); + await tapKey(keyImportG1v1); + await enterText(keyCesiumId, 'test'); + await enterText(keyCesiumPassword, 'test'); + await waitFor(cesiumTest1.shortAddress()); + await waitFor('100.0 $currencyName'); + await waitFor('3', exactMatch: true); + + isObscureText(); + await tapKey(keyCesiumIdVisible); + await tester.pumpAndSettle(); + isObscureText(false); + await tapKey(keyCesiumIdVisible); + await tester.pumpAndSettle(); + isObscureText(); + + await tapKey(keySelectWallet); + await tapKey(keySelectThisWallet(test6.address), selectLast: true); + await waitForButtonEnabled(keyConfirm); + await tapKey(keyConfirm); + spawnBlock(duration: 2000); + await waitFor('validé !'); + await tapKey(keyCloseTransactionScreen, duration: 0); + + await tapKey(keyOpenWallet(test6.address), duration: 300); + await waitFor('3', exactMatch: true); + await waitFor('Membre validé !'); + + await waitFor('99.98 $currencyName'); + }, timeout: testTimeout()); +} + +isObscureText([bool isObscure = true]) { + final passwordTextFormField = find.descendant( + of: find.byKey(keyCesiumId), + matching: find.byType(EditableText), + ); + final input = tester.widget(passwordTextFormField); + expect(input.obscureText, isObscure ? isTrue : isFalse); +} diff --git a/integration_test/multi_chests.dart b/integration_test/multi_chests.dart new file mode 100644 index 0000000..a81742e --- /dev/null +++ b/integration_test/multi_chests.dart @@ -0,0 +1,16 @@ +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'general_actions.dart'; +import 'tests_utility.dart'; + +void main() async { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + await dotenv.load(); + + testWidgets('Onboarding and multi chest', (testerLoc) async { + tester = testerLoc; + await bkFastStart(false); + await onboardingNewChest(); + }, timeout: testTimeout()); +} diff --git a/integration_test/tests_utility.dart b/integration_test/tests_utility.dart new file mode 100644 index 0000000..e8c60b7 --- /dev/null +++ b/integration_test/tests_utility.dart @@ -0,0 +1,318 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:gecko/globals.dart'; +import 'package:gecko/models/wallet_data.dart'; +import 'package:gecko/providers/generate_wallets.dart'; +import 'package:gecko/providers/my_wallets.dart'; +import 'package:gecko/providers/substrate_sdk.dart'; +import 'package:provider/provider.dart'; +import 'dart:io' as io; +import 'package:gecko/main.dart' as app; + +final bool isHumanReading = + dotenv.env['isHumanReading'] == 'true' ? true : false; +Timeout testTimeout([int seconds = 120]) => + Timeout(Duration(seconds: isHumanReading ? 600 : seconds)); +final sub = Provider.of(homeContext, listen: false); +late WidgetTester tester; + +// TEST WALLETS CONSTS +const testMnemonic = + 'pipe paddle ketchup filter life ice feel embody glide quantum ride usage'; +final test1 = + TestWallet('5FeggKqw2AbnGZF9Y9WPM2QTgzENS3Hit94Ewgmzdg5a3LNa', 'test1'); +final test2 = + TestWallet('5E4i8vcNjnrDp21Sbnp32WHm2gz8YP3GGFwmdpfg5bHd8Whb', 'test2'); +final test3 = + TestWallet('5FhTLzXLNBPmtXtDBFECmD7fvKmTtTQDtvBTfVr97tachA1p', 'test3'); +final test4 = + TestWallet('5DXJ4CusmCg8S1yF6JGVn4fxgk5oFx42WctXqHZ17mykgje5', 'test4'); +final test5 = + TestWallet('5Dq3giahrBfykJogPetZJ2jjSmhw49Fa7i6qKkseUvRJ2T3R', 'test5'); +final test6 = + TestWallet('5GxEp3do81j97kNaH4JyZgDXuPoKWoTuxXXWGyyNXeKeVLHb', 'test6'); +final test7 = + TestWallet('5FZ1sSvREbQLCtSSCvMUx7KCAnpJkB7q5mfz2oixiZq2ChET', 'test7'); +final test8 = + TestWallet('5CoKV9EEgwb2NmWamTXUAa6ycfNb2k1iNfVGvJAkg7dLq9RH', 'test8'); +final cesiumTest1 = TestWallet( + '5GAT6CJW8yVKwUuQc7sM5Kk9GZVTpbZYk9PfjNXtvnNgAJZ1', 'cesiumTest1'); +final cesiumTest2 = TestWallet( + '5DTnny1tTkUs1SXHZTx98RUAj76Z88FfFhsQjd48dXnk8gHR', 'cesiumTest2'); +final cesiumTest3 = TestWallet( + '5EJct9jTDNKco4YiYfETAseq1gaduBtsJUcNnFicfvh3bTV6', 'cesiumTest3'); +final cesiumTest4 = TestWallet( + '5HD1oSv6A7VNxPYos6F86JFZ3bhz5LnEaWC4hkwLMj84v4ww', 'cesiumTest4'); + +// CUSTOM FUNCTIONS + +Future sleep([int time = 1000]) async { + await Future.delayed(Duration(milliseconds: time)); +} + +Future clipPaste() async => + (await Clipboard.getData('text/plain'))?.text ?? ''; + +clipCopy(String text) async => + await Clipboard.setData(ClipboardData(text: text)); + +Future humanRead([int time = 1, bool force = false]) async { + if (isHumanReading || force) io.sleep(Duration(seconds: time)); +} + +Future tapKey(Key buttonKey, + {Finder? customFinder, int duration = 100, bool selectLast = false}) async { + if (duration != 0) { + await tester.pumpAndSettle(Duration(milliseconds: duration)); + } + final Finder finder = customFinder ?? find.byKey(buttonKey); + log.d('INTEGRATION TEST: Tap on ${finder.description}}'); + await tester.tap(selectLast ? finder.last : finder); + humanRead(); +} + +Finder findByKey(Key key) { + return find.byKey(key); +} + +bool isButtonEnabled(Key key) { + return tester.widget(findByKey(key)).enabled; +} + +Future scrollUntil(Key element) async { + final findList = find.byType(Scrollable); + final findElement = findByKey(element); + await tester.scrollUntilVisible( + findElement, + 500.0, + scrollable: findList, + ); +} + +Future waitForButtonEnabled(Key key, + {Duration timeout = const Duration(seconds: 5), + bool reverse = false}) async { + final end = DateTime.now().add(timeout); + + log.d('INTEGRATION TEST: Wait for $key to be enabled'); + + do { + if (DateTime.now().isAfter(end)) { + throw Exception('Timed out waiting for button enabled: $key'); + } + + await tester.pumpAndSettle(); + await Future.delayed(const Duration(milliseconds: 100)); + } while (reverse ? isButtonEnabled(key) : !isButtonEnabled(key)); + humanRead(); +} + +Future goBack() async { + final NavigatorState navigator = tester.state(find.byType(Navigator)); + log.d('INTEGRATION TEST: Go back'); + navigator.pop(); + await tester.pump(); + humanRead(); +} + +Future enterText(Key fieldKey, String textIn, [int duration = 200]) async { + if (duration != 0) { + await tester.pumpAndSettle(Duration(milliseconds: duration)); + } + log.d('INTEGRATION TEST: Enter text: $textIn'); + await tester.enterText(find.byKey(fieldKey), textIn); + humanRead(); +} + +Future waitFor(String text, + {Duration timeout = const Duration(seconds: 5), + bool reverse = false, + bool exactMatch = false}) async { + final end = DateTime.now().add(timeout); + + Finder finder = exactMatch ? find.text(text) : find.textContaining(text); + log.d('INTEGRATION TEST: Wait for: $text'); + + do { + if (DateTime.now().isAfter(end)) { + throw Exception('Timed out waiting for text $text'); + } + + await tester.pumpAndSettle(); + await Future.delayed(const Duration(milliseconds: 100)); + } while (reverse ? finder.evaluate().isNotEmpty : finder.evaluate().isEmpty); + humanRead(); +} + +// Test if text is visible on screen, return a boolean +Future isPresent(String text, + {Duration timeout = const Duration(seconds: 1)}) async { + try { + await waitFor(text, timeout: timeout); + humanRead(); + return true; + } catch (exception) { + humanRead(); + return false; + } +} + +// Test if widget exist on screen, return a boolean +Future isIconPresent(IconData icon, + {Duration timeout = const Duration(seconds: 1)}) async { + await tester.pumpAndSettle(); + final finder = find.byIcon(icon); + log.d('tatatatatatata: ${finder.evaluate()}'); + humanRead(); + return finder.evaluate().isEmpty ? false : true; +} + +Future spawnBlock({int number = 1, int duration = 200, int? until}) async { + if (duration != 0) { + await sleep(duration); + } + if (until != null) { + number = until - sub.blocNumber; + } + await sub.spawnBlock(number); + await sleep(200); +} + +// Pay in background +Future bkPay( + {required String fromAddress, + required String destAddress, + required double amount}) async { + sub.pay( + fromAddress: fromAddress, + destAddress: destAddress, + amount: amount, + password: 'AAAAA'); + await spawnBlock(); + await sleep(500); +} + +// Certify in background +Future bkCertify( + {required String fromAddress, required String destAddress}) async { + sub.certify(fromAddress, destAddress, 'AAAAA'); + await spawnBlock(); + await sleep(500); +} + +// Confirm my identity in background +Future bkConfirmIdentity( + {required String fromAddress, required String name}) async { + sub.confirmIdentity(fromAddress, name, 'AAAAA'); + await spawnBlock(); + await sleep(500); +} + +// Change node in background +Future bkSetNode([String? endpoint]) async { + if (endpoint == null) { + final ipAddress = dotenv.env['ip_address'] ?? '127.0.0.1'; + endpoint = 'ws://$ipAddress:9944'; + } + configBox.put('customEndpoint', endpoint); + sub.connectNode(homeContext); +} + +// Restore chest in background +Future bkRestoreChest([String mnemonic = testMnemonic]) async { + final myWalletProvider = + Provider.of(homeContext, listen: false); + final generateWalletProvider = + Provider.of(homeContext, listen: false); + + await generateWalletProvider.storeHDWChest(homeContext); + + for (int number = 0; number <= 4; number++) { + await _addImportAccount( + mnemonic: mnemonic, + chest: 0, + number: number, + name: 'test${number + 1}', + derivation: (number + 1) * 2); + } + myWalletProvider.rebuildWidget(); +} + +Future _addImportAccount( + {required String mnemonic, + required int chest, + required int number, + required String name, + required int derivation}) async { + final address = await sub.importAccount( + mnemonic: mnemonic, derivePath: '//$derivation', password: 'AAAAA'); + final myWallet = WalletData( + version: dataVersion, + chest: chest, + address: address, + number: number, + name: name, + derivation: derivation, + imageDefaultPath: '${number % 4}.png'); + await walletBox.add(myWallet); + + return myWallet; +} + +// Delete all wallets in background +Future bkDeleteAllWallets() async { + final myWalletProvider = + Provider.of(homeContext, listen: false); + final isWalletsPresents = + await isPresent('Scanner un', timeout: const Duration(milliseconds: 300)); + if (isWalletsPresents) { + await walletBox.clear(); + await chestBox.clear(); + await configBox.delete('defaultWallet'); + await sub.deleteAllAccounts(); + myWalletProvider.pinCode = ''; + myWalletProvider.rebuildWidget(); + } +} + +Future bkFastStart([bool restoreChest = true]) async { + // Start app and wait finish starting + await startWait(); + + // Connect to local endpoint + await bkSetNode(); + await sleep(); + + // Delete all existing chests is exists + await bkDeleteAllWallets(); + + if (restoreChest) { + // Restore the test chest + await bkRestoreChest(); + await waitFor("y'a pas de lézard"); + } +} + +Future startWait() async { + app.main(); + await waitFor('Test starting...', reverse: true); + await tester.pumpAndSettle(const Duration(milliseconds: 300)); + await sleep(3000); +} + +String getWidgetText(Key key) { + final word4Finder = find.byKey(key); + return (word4Finder.evaluate().single.widget as Text).data!; +} + +class TestWallet { + String address; + String name; + + TestWallet(this.address, this.name); + + endAddress() => address.substring(address.length - 6); + shortAddress() => getShortPubkey(address); +} diff --git a/integration_test/ud_creation_state.dart b/integration_test/ud_creation_state.dart new file mode 100644 index 0000000..88e9abd --- /dev/null +++ b/integration_test/ud_creation_state.dart @@ -0,0 +1,29 @@ +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:gecko/globals.dart'; +import 'package:gecko/models/widgets_keys.dart'; +import 'package:integration_test/integration_test.dart'; +import 'general_actions.dart'; +import 'tests_utility.dart'; + +void main() async { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + await dotenv.load(); + + testWidgets('UDs creation state', (testerLoc) async { + tester = testerLoc; + // Connect local node and import test chest in background + await bkFastStart(); + + // Open chest + await firstOpenChest(); + + // Go to test1 options and check if balance growup with UDs creations + await tapKey(keyOpenWallet(test1.address)); + await waitFor('100.0 $currencyName'); + await spawnBlock(until: 10); + await waitFor('200.0 $currencyName'); + await spawnBlock(until: 20); + await waitFor('300.0 $currencyName'); + }, timeout: testTimeout()); +} diff --git a/lib/globals.dart b/lib/globals.dart index dff45e6..eb199e7 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:gecko/models/chest_data.dart'; import 'package:gecko/models/g1_wallets_list.dart'; import 'package:gecko/models/wallet_data.dart'; -import 'package:hive/hive.dart'; +import 'package:hive_flutter/hive_flutter.dart'; import 'package:logger/logger.dart'; import 'package:shared_preferences/shared_preferences.dart'; diff --git a/lib/main.dart b/lib/main.dart index c0fd1f1..a7262eb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,6 +16,7 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/services.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/providers/cesium_plus.dart'; import 'package:gecko/models/chest_data.dart'; @@ -51,6 +52,10 @@ Future main() async { WidgetsFlutterBinding.ensureInitialized(); await EasyLocalization.ensureInitialized(); + if (kDebugMode) { + await dotenv.load(); + } + HomeProvider homeProvider = HomeProvider(); // DuniterIndexer _duniterIndexer = DuniterIndexer(); await initHiveForFlutter(); @@ -105,7 +110,7 @@ Future main() async { supportedLocales: const [Locale('en'), Locale('fr'), Locale('es')], path: 'assets/translations', fallbackLocale: const Locale('en'), - child: Gecko(indexerEndpoint), + child: const Gecko(), ), ), ); @@ -117,15 +122,14 @@ Future main() async { supportedLocales: const [Locale('en'), Locale('fr'), Locale('es')], path: 'assets/translations', fallbackLocale: const Locale('en'), - child: Gecko(indexerEndpoint), + child: const Gecko(), ), ); } } class Gecko extends StatelessWidget { - const Gecko(this.indexerEndpoint, {Key? key}) : super(key: key); - final String? indexerEndpoint; + const Gecko({Key? key}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/lib/models/widgets_keys.dart b/lib/models/widgets_keys.dart new file mode 100644 index 0000000..85bb691 --- /dev/null +++ b/lib/models/widgets_keys.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; + +// General +const keyInfoPopup = Key('keyInfoPopup'); +const keyGoNext = Key('keyGoNext'); +const keyCancel = Key('keyCancel'); +const keyConfirm = Key('keyConfirm'); +const keyAppBarSearch = Key('keyAppBarSearch'); +const keyAppBarQrcode = Key('keyAppBarQrcode'); +const keyAppBarChest = Key('keyAppBarChest'); + +// Home +const keyParameters = Key('keyParameters'); +const keyContacts = Key('keyContacts'); +const keyDrawerMenu = Key('keyDrawerMenu'); +const keyOpenWalletsHomme = Key('keyOpenWalletsHomme'); +const keyOpenSearch = Key('keyOpenSearch'); +const keyRestoreChest = Key('keyRestoreChest'); +const keyOnboardingNewChest = Key('keyOnboardingNewChest'); + +// Wallets home +const keyImportG1v1 = Key('keyImportG1v1'); +const keyChangeChest = Key('keyChangeChest'); +const keyListWallets = Key('keyListWallets'); +const keyAddDerivation = Key('keyAddDerivation'); + +// Wallet options +const keyCopyAddress = Key('keyCopyAddress'); +const keyOpenActivity = Key('keyOpenActivity'); +const keyManageMembership = Key('keyManageMembership'); +const keySetDefaultWallet = Key('keySetDefaultWallet'); +const keyDeleteWallet = Key('keyDeleteWallet'); +const keyWalletName = Key('keyWalletName'); +const keyRenameWallet = Key('keyRenameWallet'); +const keyConfirmIdentity = Key('keyConfirmIdentity'); +const keyEnterIdentityUsername = Key('keyEnterIdentityUsername'); + +// Chest options +const keyShowSeed = Key('keyShowSeed'); +const keyChangePin = Key('keyChangePin'); +const keycreateRootDerivation = Key('keycreateRootDerivation'); +const keyDeleteChest = Key('keyDeleteChest'); + +// Manage membership +const keyMigrateIdentity = Key('keyMigrateIdentity'); +const keyRevokeIdty = Key('keyRevokeIdty'); + +// Choose chest +const keyCreateNewChest = Key('keyCreateNewChest'); +const keyImportChest = Key('keyImportChest'); + +// Profile view +const keyViewActivity = Key('keyViewActivity'); +const keyCertify = Key('keyCertify'); +const keyPay = Key('keyPay'); +const keyAmountField = Key('keyAmountField'); +const keyConfirmPayment = Key('keyConfirmPayment'); +const keyCloseTransactionScreen = Key('keyCloseTransactionScreen'); + +// Activity view +const keyListTransactions = Key('keyListTransactions'); + +// Unlock wallet +const keyPinForm = Key('keyPinForm'); +const keyPopButton = Key('keyPopButton'); +const keyCachePassword = Key('keyCachePassword'); + +// Settings +const keyDeleteAllWallets = Key('keyDeleteAllWallets'); +const keySelectDuniterNodeDropDown = Key('keySelectDuniterNodeDropDown'); +const keyCustomDuniterEndpoint = Key('keyCustomDuniterEndpoint'); +const keyConnectToEndpoint = Key('keyConnectToEndpoint'); + +// Onboarding +const keyPastMnemonic = Key('keyPastMnemonic'); +const keyBubbleSpeak = Key('keyBubbleSpeak'); +const keyGenerateMnemonic = Key('keyGenerateMnemonic'); +const keyAskedWord = Key('keyAskedWord'); +const keyInputWord = Key('keyInputWord'); +const keyGeneratedPin = Key('keyGeneratedPin'); +const keyGoWalletsHome = Key('keyGoWalletsHome'); + +// Search +const keySearchField = Key('keySearchField'); +const keyConfirmSearch = Key('keyConfirmSearch'); + +// Import Cesium wallet +const keyCesiumId = Key('keyCesiumId'); +const keyCesiumPassword = Key('keyCesiumPassword'); +const keySelectWallet = Key('keySelectWallet'); +const keyCesiumIdVisible = Key('keyCesiumIdVisible'); + +// Items keys +Key keyTransaction(int keyId) => Key('keyTransaction$keyId'); +Key keyMnemonicWord(String word) => Key('keyMnemonicWord$word'); +Key keySearchResult(String address) => Key('keySearchResult$address'); +Key keySelectDuniterNode(String endpoint) => + Key('keySelectDuniterNode$endpoint'); +Key keyOpenWallet(String address) => Key('keyOpenWallet$address'); +Key keySelectThisWallet(String address) => Key('keySelectThisWallet$address'); diff --git a/lib/providers/chest_provider.dart b/lib/providers/chest_provider.dart index 0134e26..ce788b9 100644 --- a/lib/providers/chest_provider.dart +++ b/lib/providers/chest_provider.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/chest_data.dart'; import 'package:gecko/models/wallet_data.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/providers/substrate_sdk.dart'; import 'package:provider/provider.dart'; @@ -59,13 +60,13 @@ class ChestProvider with ChangeNotifier { title: Text('areYouSureToDeleteWallet'.tr(args: [walletName!])), actions: [ TextButton( - child: Text("no".tr(), key: const Key('cancelDeleting')), + child: Text("no".tr(), key: keyCancel), onPressed: () { Navigator.pop(context, false); }, ), TextButton( - child: Text("yes".tr(), key: const Key('confirmDeleting')), + child: Text("yes".tr(), key: keyConfirm), onPressed: () { Navigator.pop(context, true); }, diff --git a/lib/providers/duniter_indexer.dart b/lib/providers/duniter_indexer.dart index 65a96c3..8d6123b 100644 --- a/lib/providers/duniter_indexer.dart +++ b/lib/providers/duniter_indexer.dart @@ -8,6 +8,7 @@ import 'package:gecko/globals.dart'; import 'package:gecko/models/g1_wallets_list.dart'; import 'package:gecko/models/queries_indexer.dart'; import 'package:gecko/models/wallet_data.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/cesium_plus.dart'; import 'package:gecko/providers/substrate_sdk.dart'; import 'package:gecko/providers/wallet_options.dart'; @@ -293,7 +294,6 @@ class DuniterIndexer with ChangeNotifier { return Text('noResult'.tr()); } - int keyID = 0; double avatarSize = 55; return Expanded( child: ListView(children: [ @@ -301,7 +301,7 @@ class DuniterIndexer with ChangeNotifier { Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: ListTile( - key: Key('searchResult${keyID++}'), + key: keySearchResult(profile['id']), horizontalTitleGap: 40, contentPadding: const EdgeInsets.all(5), leading: cesiumPlusProvider.defaultAvatar(avatarSize), diff --git a/lib/providers/generate_wallets.dart b/lib/providers/generate_wallets.dart index 24aef78..0d81211 100644 --- a/lib/providers/generate_wallets.dart +++ b/lib/providers/generate_wallets.dart @@ -1,5 +1,4 @@ import 'dart:math'; -import 'dart:typed_data'; import 'package:durt/durt.dart' as durt; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -354,6 +353,7 @@ class GenerateWalletsProvider with ChangeNotifier { cellController10, cellController11 ]; + if (sentence?.text == null) return; for (var word in sentence!.text!.split(' ')) { bool isValid = isBipWord(word, false); diff --git a/lib/providers/home.dart b/lib/providers/home.dart index d825863..fe5102f 100644 --- a/lib/providers/home.dart +++ b/lib/providers/home.dart @@ -11,6 +11,7 @@ import 'package:flutter/services.dart'; import 'dart:async'; import 'package:gecko/globals.dart'; import 'package:gecko/models/wallet_data.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/providers/wallets_profiles.dart'; import 'package:gecko/screens/myWallets/unlocking_wallet.dart'; @@ -148,6 +149,7 @@ class HomeProvider with ChangeNotifier { const Spacer(), const SizedBox(width: 11), IconButton( + key: keyAppBarSearch, iconSize: 40, icon: const Image(image: AssetImage('assets/loupe-noire.png')), onPressed: () { @@ -166,6 +168,7 @@ class HomeProvider with ChangeNotifier { const SizedBox(width: 22), const Spacer(), IconButton( + key: keyAppBarQrcode, iconSize: 70, icon: const Image(image: AssetImage('assets/qrcode-scan.png')), onPressed: () async { @@ -179,6 +182,7 @@ class HomeProvider with ChangeNotifier { const Spacer(), const SizedBox(width: 15), IconButton( + key: keyAppBarChest, iconSize: 60, icon: const Image(image: AssetImage('assets/wallet.png')), onPressed: () async { diff --git a/lib/providers/substrate_sdk.dart b/lib/providers/substrate_sdk.dart index 13a85cf..3955cf4 100644 --- a/lib/providers/substrate_sdk.dart +++ b/lib/providers/substrate_sdk.dart @@ -1,7 +1,7 @@ // ignore_for_file: use_build_context_synchronously -import 'dart:typed_data'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/chest_data.dart'; @@ -37,7 +37,8 @@ class SubstrateSdk with ChangeNotifier { TextEditingController csSalt = TextEditingController(); TextEditingController csPassword = TextEditingController(); String g1V1NewAddress = ''; - bool isCesiumIDVisible = true; + bool isCesiumIDVisible = false; + bool isCesiumAddresLoading = false; ///////////////////////////////////// ////////// 1: API METHODS /////////// @@ -80,10 +81,11 @@ class SubstrateSdk with ChangeNotifier { [null])[0]; } - TxSenderData _setSender() { + Future _setSender(String address) async { + final fromPubkey = await sdk.api.account.decodeAddress([address]); return TxSenderData( - keyring.current.address, - keyring.current.pubKey, + address, + fromPubkey!.keys.first, ); } @@ -685,7 +687,7 @@ class SubstrateSdk with ChangeNotifier { ); const tx1 = 'api.tx.universalDividend.claimUds()'; final tx2 = amount == -1 - ? 'api.tx.balances.transferAll(false)' + ? 'api.tx.balances.transferAll("$destAddress", false)' : 'api.tx.balances.transferKeepAlive("$destAddress", $amountUnit)'; rawParams = '[[$tx1, $tx2]]'; @@ -696,14 +698,14 @@ class SubstrateSdk with ChangeNotifier { } Future certify( - String fromAddress, String password, String toAddress) async { + String fromAddress, String destAddress, String password) async { transactionStatus = ''; final myIdtyStatus = await idtyStatus(fromAddress); - final toIdtyStatus = await idtyStatus(toAddress); + final toIdtyStatus = await idtyStatus(destAddress); final fromIndex = await _getIdentityIndexOf(fromAddress); - final toIndex = await _getIdentityIndexOf(toAddress); + final toIndex = await _getIdentityIndexOf(destAddress); if (myIdtyStatus != 'Validated') { transactionStatus = 'notMember'; @@ -711,12 +713,12 @@ class SubstrateSdk with ChangeNotifier { return 'notMember'; } - final sender = _setSender(); + final sender = await _setSender(fromAddress); TxInfoData txInfo; List txOptions = []; String? rawParams; - final toCerts = await getCerts(toAddress); + final toCerts = await getCerts(destAddress); // log.d('debug: ${currencyParameters['minCertForMembership']}'); @@ -726,7 +728,7 @@ class SubstrateSdk with ChangeNotifier { 'createIdentity', sender, ); - txOptions = [toAddress]; + txOptions = [destAddress]; } else if (toIdtyStatus == 'Validated' || toIdtyStatus == 'ConfirmedByOwner') { if (toCerts[0] >= currencyParameters['minCertForMembership']! - 1 && @@ -776,11 +778,11 @@ class SubstrateSdk with ChangeNotifier { Future confirmIdentity( String fromAddress, String name, String password) async { - log.d('me: ${keyring.current.address!}'); + final fromPubkey = await sdk.api.account.decodeAddress([fromAddress]); final sender = TxSenderData( - keyring.current.address, - keyring.current.pubKey, + fromAddress, + fromPubkey!.keys.first, ); final txInfo = TxInfoData( @@ -848,10 +850,11 @@ newKeySig: $newKeySig"""); const tx1 = 'api.tx.universalDividend.claimUds()'; final tx2 = 'api.tx.identity.changeOwnerKey("$destAddress", "$newKeySig")'; - // const tx3 = 'api.tx.balances.transferAll(false)'; + final tx3 = 'api.tx.balances.transferAll("$destAddress", false)'; - rawParams = - fromBalance['unclaimedUds'] == 0 ? '[[$tx2]]' : '[[$tx1, $tx2]]'; + rawParams = fromBalance['unclaimedUds'] == 0 + ? '[[$tx2, $tx3]]' + : '[[$tx1, $tx2, $tx3]]'; } else { txInfo = TxInfoData( 'identity', @@ -873,7 +876,6 @@ newKeySig: $newKeySig"""); keyring.current.pubKey, ); - log.d(sender.address); TxInfoData txInfo; txInfo = TxInfoData( @@ -940,6 +942,17 @@ newKeySig: $newKeySig"""); await sdk.api.keyring.deleteAccount(keyring, keypair); } + Future spawnBlock([int number = 1, int until = 0]) async { + if (!kDebugMode) return; + if (blocNumber < until) { + number = until - blocNumber; + } + for (var i = 1; i <= number; i++) { + await sdk.webView! + .evalJavascript('api.rpc.engine.createBlock(true, true)'); + } + } + void reload() { notifyListeners(); } diff --git a/lib/providers/wallet_options.dart b/lib/providers/wallet_options.dart index c1a2821..3438bec 100644 --- a/lib/providers/wallet_options.dart +++ b/lib/providers/wallet_options.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'dart:async'; import 'package:gecko/globals.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/duniter_indexer.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/models/wallet_data.dart'; @@ -136,7 +137,7 @@ class WalletOptionsProvider with ChangeNotifier { DuniterIndexer duniterIndexer = Provider.of(context, listen: false); - _showText(String text, + showText(String text, [double size = 18, bool bold = false, bool smooth = true]) { log.d('$address $text'); return AnimatedFadeOutIn( @@ -162,16 +163,16 @@ class WalletOptionsProvider with ChangeNotifier { switch (snapshot.data.toString()) { case 'noid': { - return _showText('noIdentity'.tr()); + return showText('noIdentity'.tr()); } case 'Created': { - return _showText('identityCreated'.tr()); + return showText('identityCreated'.tr()); } case 'ConfirmedByOwner': { return isOwner - ? _showText('identityConfirmed'.tr()) + ? showText('identityConfirmed'.tr()) : duniterIndexer.getNameByAddress( context, address, @@ -186,7 +187,7 @@ class WalletOptionsProvider with ChangeNotifier { case 'Validated': { return isOwner - ? _showText('memberValidated'.tr(), 18, true) + ? showText('memberValidated'.tr(), 18, true) : duniterIndexer.getNameByAddress( context, address, @@ -200,11 +201,11 @@ class WalletOptionsProvider with ChangeNotifier { case 'expired': { - return _showText('identityExpired'.tr()); + return showText('identityExpired'.tr()); } } return SizedBox( - child: _showText('', 18, false, false), + child: showText('', 18, false, false), ); }); }); @@ -238,6 +239,7 @@ class WalletOptionsProvider with ChangeNotifier { child: Column(children: [ const SizedBox(height: 20), TextField( + key: keyEnterIdentityUsername, onChanged: (_) => notifyListeners(), inputFormatters: [ // FilteringTextInputFormatter.allow(RegExp("[0-9a-zA-Z]")), @@ -258,7 +260,7 @@ class WalletOptionsProvider with ChangeNotifier { Consumer( builder: (context, wOptions, _) { return TextButton( - key: const Key('infoPopup'), + key: keyConfirm, child: Text( "validate".tr(), style: TextStyle( @@ -354,7 +356,7 @@ class WalletOptionsProvider with ChangeNotifier { Consumer( builder: (context, wOptions, _) { return TextButton( - key: const Key('infoPopup'), + key: keyInfoPopup, child: Text( "validate".tr(), style: TextStyle( @@ -381,7 +383,7 @@ class WalletOptionsProvider with ChangeNotifier { mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( - key: const Key('cancel'), + key: keyCancel, child: Text( "cancel".tr(), style: TextStyle( @@ -457,7 +459,7 @@ class WalletOptionsProvider with ChangeNotifier { width: 260, child: Stack(children: [ TextField( - key: const Key('walletName'), + key: keyWalletName, autofocus: false, focusNode: walletNameFocus, enabled: isEditing, @@ -481,7 +483,7 @@ class WalletOptionsProvider with ChangeNotifier { Positioned( right: 0, child: InkWell( - key: const Key('renameWallet'), + key: keyRenameWallet, onTap: () async { // _isNewNameValid = // walletProvider.editWalletName(wallet.id(), isCesium: false); diff --git a/lib/providers/wallets_profiles.dart b/lib/providers/wallets_profiles.dart index ce181c5..3dba88a 100644 --- a/lib/providers/wallets_profiles.dart +++ b/lib/providers/wallets_profiles.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/g1_wallets_list.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/cesium_plus.dart'; import 'package:gecko/providers/substrate_sdk.dart'; import 'package:gecko/providers/wallet_options.dart'; @@ -166,7 +167,7 @@ class WalletsProfilesProvider with ChangeNotifier { ), Row(children: [ GestureDetector( - key: const Key('copyPubkey'), + key: keyCopyAddress, onTap: () { Clipboard.setData(ClipboardData(text: address)); snackCopyKey(context); diff --git a/lib/screens/activity.dart b/lib/screens/activity.dart index ebb0aed..2be757d 100644 --- a/lib/screens/activity.dart +++ b/lib/screens/activity.dart @@ -1,7 +1,10 @@ +// ignore_for_file: must_be_immutable + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/queries_indexer.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/cesium_plus.dart'; import 'package:gecko/providers/duniter_indexer.dart'; import 'package:gecko/providers/home.dart'; @@ -12,7 +15,6 @@ import 'package:gecko/screens/wallet_view.dart'; import 'package:graphql_flutter/graphql_flutter.dart'; import 'package:provider/provider.dart'; -// ignore: must_be_immutable class ActivityScreen extends StatelessWidget with ChangeNotifier { ActivityScreen({required this.address, this.avatar, this.username, Key? key}) : super(key: key); @@ -132,7 +134,7 @@ class ActivityScreen extends StatelessWidget with ChangeNotifier { child: Builder( builder: (context) => Expanded( child: ListView( - key: const Key('listTransactions'), + key: keyListTransactions, controller: scrollController, children: [historyView(context, result)], ), @@ -286,7 +288,7 @@ class ActivityScreen extends StatelessWidget with ChangeNotifier { child: // Row(children: [Column(children: [],)],) ListTile( - key: Key('transaction${keyID++}'), + key: keyTransaction(keyID++), contentPadding: const EdgeInsets.only( left: 20, right: 30, top: 15, bottom: 15), leading: ClipOval( diff --git a/lib/screens/avatar_fullscreen.dart b/lib/screens/avatar_fullscreen.dart index 682e290..ef87be1 100644 --- a/lib/screens/avatar_fullscreen.dart +++ b/lib/screens/avatar_fullscreen.dart @@ -1,14 +1,9 @@ import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:flutter/material.dart'; -// import 'package:gecko/models/home.dart'; -// import 'package:provider/provider.dart'; -// ignore: must_be_immutable class AvatarFullscreen extends StatelessWidget { - TextEditingController tplController = TextEditingController(); - - AvatarFullscreen(this.avatar, {this.title, this.color, Key? key}) + const AvatarFullscreen(this.avatar, {this.title, this.color, Key? key}) : super(key: key); final Image? avatar; final String? title; diff --git a/lib/screens/common_elements.dart b/lib/screens/common_elements.dart index 59149f7..71a8278 100644 --- a/lib/screens/common_elements.dart +++ b/lib/screens/common_elements.dart @@ -3,6 +3,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:gecko/globals.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/substrate_sdk.dart'; import 'package:provider/provider.dart'; @@ -62,10 +63,10 @@ class CommonElements { width: 380 * ratio, height: 60 * ratio, child: ElevatedButton( + key: keyGoNext, style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, // background - onPrimary: Colors.white, // foreground + foregroundColor: Colors.white, backgroundColor: orangeC, + elevation: 4, // foreground ), onPressed: () { Navigator.push( @@ -247,7 +248,7 @@ Future confirmPopup(BuildContext context, String title) async { mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( - key: const Key('confirmPopop'), + key: keyConfirm, child: Text( "yes".tr(), style: const TextStyle( @@ -295,7 +296,7 @@ Future infoPopup(BuildContext context, String title) async { mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( - key: const Key('infoPopup'), + key: keyInfoPopup, child: const Text( "D'accord", style: TextStyle( diff --git a/lib/screens/home.dart b/lib/screens/home.dart index 4157045..221e19e 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -5,6 +5,7 @@ import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/stateful_wrapper.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/chest_provider.dart'; import 'package:gecko/providers/duniter_indexer.dart'; import 'package:gecko/providers/home.dart'; @@ -64,7 +65,7 @@ class HomeScreen extends StatelessWidget { ]), ), ListTile( - key: const Key('parameters'), + key: keyParameters, title: Text('parameters'.tr()), onTap: () { Navigator.pop(context); @@ -77,7 +78,7 @@ class HomeScreen extends StatelessWidget { }, ), ListTile( - key: const Key('contacts'), + key: keyContacts, title: Text('contactsManagement'.tr()), onTap: () { Navigator.pop(context); @@ -89,26 +90,6 @@ class HomeScreen extends StatelessWidget { ); }, ), - - // ListTile( - // key: const Key('substrateSandbox'), - // title: const Text('Substrate debug'), - // onTap: () { - // Navigator.pop(context); - // Navigator.push( - // context, - // MaterialPageRoute(builder: (context) { - // return const SubstrateSandBox(); - // }), - // ); - // }, - // ), - - // ListTile( - // title: const Text('A propos'), - // onTap: () { - // }, - // ), ])), Align( alignment: FractionalOffset.bottomCenter, @@ -141,17 +122,19 @@ class HomeScreen extends StatelessWidget { myWalletProvider.rebuildWidget(); } - var connectivityResult = - await (Connectivity().checkConnectivity()); + // var connectivityResult = + // await (Connectivity().checkConnectivity()); + + // if (connectivityResult != ConnectivityResult.mobile && + // connectivityResult != ConnectivityResult.wifi) { + // homeProvider.changeMessage( + // "notConnectedToInternet".tr(), 0); + // sub.nodeConnected = false; + // } + + // TODO: fix random bad network status on startup HomeProvider homeProvider = Provider.of(ctx, listen: false); - if (connectivityResult != ConnectivityResult.mobile && - connectivityResult != ConnectivityResult.wifi) { - homeProvider.changeMessage( - "notConnectedToInternet".tr(), 0); - sub.nodeConnected = false; - } - Connectivity() .onConnectivityChanged .listen((ConnectivityResult result) async { @@ -166,6 +149,7 @@ class HomeScreen extends StatelessWidget { await sub.connectNode(ctx); } }); + // await sub.connectNode(ctx); } // _duniterIndexer.checkIndexerEndpointBackground(); }); @@ -220,7 +204,7 @@ Widget geckHome(context) { left: 15, child: Builder( builder: (context) => IconButton( - key: const Key('drawerMenu'), + key: keyDrawerMenu, icon: const Icon( Icons.menu, color: Colors.white, @@ -301,6 +285,7 @@ Widget geckHome(context) { child: Material( color: orangeC, // button color child: InkWell( + key: keyOpenSearch, child: Padding( padding: const EdgeInsets.all(18), child: Image( @@ -343,7 +328,7 @@ Widget geckHome(context) { ], ), child: ClipOval( - key: const Key('manageWallets'), + key: keyOpenWalletsHomme, child: Material( color: orangeC, // button color child: InkWell( @@ -466,7 +451,7 @@ Widget welcomeHome(context) { left: 15, child: Builder( builder: (context) => IconButton( - key: const Key('drawerMenu'), + key: keyDrawerMenu, icon: const Icon( Icons.menu, color: Colors.white, @@ -554,10 +539,10 @@ Widget welcomeHome(context) { width: 410, height: 70, child: ElevatedButton( + key: keyOnboardingNewChest, style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, // background - onPrimary: Colors.white, // foreground + foregroundColor: Colors.white, elevation: 4, + backgroundColor: orangeC, // foreground ), onPressed: () { Navigator.push( @@ -581,6 +566,7 @@ Widget welcomeHome(context) { width: 410, height: 70, child: OutlinedButton( + key: keyRestoreChest, style: OutlinedButton.styleFrom( side: BorderSide(width: 4, color: orangeC)), onPressed: () { diff --git a/lib/screens/myWallets/change_pin.dart b/lib/screens/myWallets/change_pin.dart index 26b3f4c..be5a3bc 100644 --- a/lib/screens/myWallets/change_pin.dart +++ b/lib/screens/myWallets/change_pin.dart @@ -1,4 +1,4 @@ -// ignore_for_file: use_build_context_synchronously +// ignore_for_file: use_build_context_synchronously, must_be_immutable import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; @@ -10,11 +10,9 @@ import 'package:gecko/models/wallet_data.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/providers/substrate_sdk.dart'; import 'package:gecko/screens/myWallets/unlocking_wallet.dart'; -import 'dart:io'; import 'package:provider/provider.dart'; -// ignore: must_be_immutable class ChangePinScreen extends StatelessWidget with ChangeNotifier { ChangePinScreen( {Key? keyMyWallets, @@ -23,9 +21,8 @@ class ChangePinScreen extends StatelessWidget with ChangeNotifier { : super(key: keyMyWallets); final String? walletName; final MyWalletsProvider walletProvider; - Directory? appPath; - TextEditingController newPin = TextEditingController(); + final TextEditingController newPin = TextEditingController(); @override Widget build(BuildContext context) { @@ -102,9 +99,8 @@ class ChangePinScreen extends StatelessWidget with ChangeNotifier { height: 50, child: ElevatedButton( style: ElevatedButton.styleFrom( - elevation: 12, - primary: Colors.green[400], //smoothYellow, // background - onPrimary: Colors.black, // foreground + foregroundColor: Colors.black, elevation: 12, + backgroundColor: Colors.green[400], // foreground ), onPressed: () async { WalletData defaultWallet = diff --git a/lib/screens/myWallets/chest_options.dart b/lib/screens/myWallets/chest_options.dart index 221aeda..7b5e59c 100644 --- a/lib/screens/myWallets/chest_options.dart +++ b/lib/screens/myWallets/chest_options.dart @@ -6,6 +6,7 @@ import 'package:gecko/globals.dart'; import 'package:gecko/models/chest_data.dart'; import 'package:flutter/services.dart'; import 'package:gecko/models/wallet_data.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/chest_provider.dart'; import 'package:gecko/providers/home.dart'; import 'package:gecko/providers/my_wallets.dart'; @@ -58,7 +59,7 @@ class ChestOptions extends StatelessWidget { child: Column(children: [ SizedBox(height: 30 * ratio), InkWell( - key: const Key('showSeed'), + key: keyShowSeed, onTap: () async { MyWalletsProvider myWalletProvider = Provider.of(context, listen: false); @@ -108,7 +109,7 @@ class ChestOptions extends StatelessWidget { SizedBox(height: 10 * ratio), Consumer(builder: (context, sub, _) { return InkWell( - key: const Key('changePin'), + key: keyChangePin, onTap: sub.nodeConnected ? () async { // await _chestProvider.changePin(context, cesiumWallet); @@ -152,7 +153,7 @@ class ChestOptions extends StatelessWidget { SizedBox(height: 10 * ratio), Consumer(builder: (context, sub, _) { return InkWell( - key: const Key('createRootDerivation'), + key: keycreateRootDerivation, onTap: sub.nodeConnected ? () async { await Navigator.push( @@ -188,7 +189,7 @@ class ChestOptions extends StatelessWidget { }), SizedBox(height: 10 * ratio), InkWell( - key: const Key('deleteChest'), + key: keyDeleteChest, onTap: () async { await chestProvider.deleteChest(context, currentChest); }, diff --git a/lib/screens/myWallets/choose_chest.dart b/lib/screens/myWallets/choose_chest.dart index a3d4bf9..e91676d 100644 --- a/lib/screens/myWallets/choose_chest.dart +++ b/lib/screens/myWallets/choose_chest.dart @@ -3,6 +3,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/models/wallet_data.dart'; import 'package:flutter/material.dart'; @@ -22,7 +23,6 @@ class ChooseChest extends StatefulWidget { } } -// ignore: must_be_immutable class _ChooseChestState extends State { TextEditingController tplController = TextEditingController(); CarouselController buttonCarouselController = CarouselController(); @@ -111,8 +111,8 @@ class _ChooseChestState extends State { height: 70, child: ElevatedButton( style: ElevatedButton.styleFrom( - primary: orangeC, // background - onPrimary: Colors.black, // foreground + foregroundColor: Colors.black, + backgroundColor: orangeC, // foreground ), onPressed: () async { await configBox.put('currentChest', currentChest); @@ -156,7 +156,7 @@ class _ChooseChestState extends State { child: Align( alignment: Alignment.bottomCenter, child: InkWell( - key: const Key('createNewChest'), + key: keyCreateNewChest, onTap: () { Navigator.push( context, @@ -179,7 +179,7 @@ class _ChooseChestState extends State { ), ), InkWell( - key: const Key('importChest'), + key: keyImportChest, onTap: () { Navigator.push( context, diff --git a/lib/screens/myWallets/choose_wallet.dart b/lib/screens/myWallets/choose_wallet.dart index e4823a8..50c4eab 100644 --- a/lib/screens/myWallets/choose_wallet.dart +++ b/lib/screens/myWallets/choose_wallet.dart @@ -1,4 +1,4 @@ -// ignore_for_file: use_build_context_synchronously +// ignore_for_file: use_build_context_synchronously, must_be_immutable import 'dart:io'; @@ -7,6 +7,7 @@ import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:flutter/material.dart'; import 'package:gecko/models/wallet_data.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/providers/substrate_sdk.dart'; import 'package:gecko/providers/wallet_options.dart'; @@ -15,7 +16,6 @@ import 'package:provider/provider.dart'; // import 'package:gecko/models/home.dart'; // import 'package:provider/provider.dart'; -// ignore: must_be_immutable class ChooseWalletScreen extends StatelessWidget { ChooseWalletScreen({Key? key, required this.pin}) : super(key: key); final String pin; @@ -46,10 +46,10 @@ class ChooseWalletScreen extends StatelessWidget { width: 470, height: 70, child: ElevatedButton( + key: keyConfirm, style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, // background - onPrimary: Colors.white, // foreground + foregroundColor: Colors.white, elevation: 4, + backgroundColor: orangeC, // foreground ), onPressed: () async { await sub.setCurrentWallet(selectedWallet!); @@ -112,7 +112,7 @@ class ChooseWalletScreen extends StatelessWidget { return CustomScrollView(slivers: [ const SliverToBoxAdapter(child: SizedBox(height: 20)), SliverGrid.count( - key: const Key('listWallets'), + key: keyListWallets, crossAxisCount: nTule, childAspectRatio: 1, crossAxisSpacing: 0, @@ -122,6 +122,7 @@ class ChooseWalletScreen extends StatelessWidget { Padding( padding: const EdgeInsets.all(16), child: GestureDetector( + key: keySelectThisWallet(repository.address!), onTap: () { selectedWallet = repository; myWalletProvider.rebuildWidget(); diff --git a/lib/screens/myWallets/custom_derivations.dart b/lib/screens/myWallets/custom_derivations.dart index a5d264d..be97443 100644 --- a/lib/screens/myWallets/custom_derivations.dart +++ b/lib/screens/myWallets/custom_derivations.dart @@ -107,9 +107,8 @@ class _CustomDerivationState extends State { height: 70, child: ElevatedButton( style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, // background - onPrimary: Colors.white, // foreground + foregroundColor: Colors.white, elevation: 4, + backgroundColor: orangeC, // foreground ), onPressed: () async { WalletData? defaultWallet = diff --git a/lib/screens/myWallets/import_g1_v1.dart b/lib/screens/myWallets/import_g1_v1.dart index 6448c8b..052754c 100644 --- a/lib/screens/myWallets/import_g1_v1.dart +++ b/lib/screens/myWallets/import_g1_v1.dart @@ -7,6 +7,7 @@ import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:flutter/material.dart'; import 'package:gecko/models/wallet_data.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/providers/substrate_sdk.dart'; import 'package:gecko/providers/wallet_options.dart'; @@ -21,7 +22,6 @@ class ImportG1v1 extends StatelessWidget { @override Widget build(BuildContext context) { SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); - // HomeProvider _homeProvider = Provider.of(context); WalletOptionsProvider walletOptions = Provider.of(context, listen: false); MyWalletsProvider myWalletProvider = @@ -109,6 +109,7 @@ class ImportG1v1 extends StatelessWidget { return Column(children: [ const SizedBox(height: 20), TextFormField( + key: keyCesiumId, autofocus: true, onChanged: (text) { if (debounce?.isActive ?? false) { @@ -116,21 +117,23 @@ class ImportG1v1 extends StatelessWidget { } debounce = Timer( const Duration(milliseconds: debouneTime), () { + sub.reload(); sub.csToV2Address( sub.csSalt.text, sub.csPassword.text); }); }, keyboardType: TextInputType.text, controller: sub.csSalt, - obscureText: sub + obscureText: !sub .isCesiumIDVisible, //This will obscure text dynamically decoration: InputDecoration( hintText: 'enterCesiumId'.tr(), suffixIcon: IconButton( + key: keyCesiumIdVisible, icon: Icon( sub.isCesiumIDVisible - ? Icons.visibility - : Icons.visibility_off, + ? Icons.visibility_off + : Icons.visibility, color: Colors.black, ), onPressed: () { @@ -141,6 +144,7 @@ class ImportG1v1 extends StatelessWidget { ), const SizedBox(height: 20), TextFormField( + key: keyCesiumPassword, autofocus: true, onChanged: (text) { if (debounce?.isActive ?? false) { @@ -148,21 +152,23 @@ class ImportG1v1 extends StatelessWidget { } debounce = Timer( const Duration(milliseconds: debouneTime), () { + sub.g1V1NewAddress = ''; + sub.reload(); sub.csToV2Address( sub.csSalt.text, sub.csPassword.text); }); }, keyboardType: TextInputType.text, controller: sub.csPassword, - obscureText: sub + obscureText: !sub .isCesiumIDVisible, //This will obscure text dynamically decoration: InputDecoration( hintText: 'enterCesiumPassword'.tr(), suffixIcon: IconButton( icon: Icon( sub.isCesiumIDVisible - ? Icons.visibility - : Icons.visibility_off, + ? Icons.visibility_off + : Icons.visibility, color: Colors.black, ), onPressed: () { @@ -173,7 +179,7 @@ class ImportG1v1 extends StatelessWidget { ), const SizedBox(height: 20), GestureDetector( - key: const Key('copyPubkey'), + key: keyCopyAddress, onTap: () { Clipboard.setData( ClipboardData(text: sub.g1V1NewAddress)); @@ -187,14 +193,6 @@ class ImportG1v1 extends StatelessWidget { ), ), ), - // Text( - // getShortPubkey(sub.g1V1NewAddress), - // style: const TextStyle( - // fontSize: 18, - // color: Colors.black, - // fontWeight: FontWeight.bold, - // fontFamily: 'Monospace'), - // ), const SizedBox(height: 20), Text( '${balance['transferableBalance']} $currencyName', @@ -213,12 +211,14 @@ class ImportG1v1 extends StatelessWidget { Text('selectDestWallet'.tr()), const SizedBox(height: 5), DropdownButtonHideUnderline( + key: keySelectWallet, child: DropdownButton( // alignment: AlignmentDirectional.topStart, value: selectedWallet, icon: const Icon(Icons.keyboard_arrow_down), items: myWalletProvider.listWallets.map((wallet) { return DropdownMenuItem( + key: keySelectThisWallet(wallet.address!), value: wallet, child: Text( wallet.name!, @@ -237,14 +237,13 @@ class ImportG1v1 extends StatelessWidget { width: 380 * ratio, height: 60 * ratio, child: ElevatedButton( + key: keyConfirm, style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, // background - onPrimary: Colors.white, // foreground + foregroundColor: Colors.white, elevation: 4, + backgroundColor: orangeC, // foreground ), onPressed: canValidate ? () async { - log.d('GOOO'); WalletData? defaultWallet = myWalletProvider.getDefaultWallet(); diff --git a/lib/screens/myWallets/manage_membership.dart b/lib/screens/myWallets/manage_membership.dart index 4a05ec0..99feb0b 100644 --- a/lib/screens/myWallets/manage_membership.dart +++ b/lib/screens/myWallets/manage_membership.dart @@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:flutter/material.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/screens/myWallets/migrate_identity.dart'; // import 'package:gecko/models/wallet_data.dart'; // import 'package:gecko/providers/my_wallets.dart'; @@ -41,7 +42,7 @@ class ManageMembership extends StatelessWidget { Widget migrateIdentity(BuildContext context) { return InkWell( - key: const Key('migrateIdentity'), + key: keyMigrateIdentity, onTap: () async { Navigator.push( context, @@ -64,7 +65,7 @@ class ManageMembership extends StatelessWidget { Widget revokeMyIdentity(BuildContext context) { return InkWell( - key: const Key('revokeIdty'), + key: keyRevokeIdty, onTap: () async { // TODOO: Generate revoke document, and understand extrinsic identity.revokeIdentity options // final _answer = await confirmPopup(context, diff --git a/lib/screens/myWallets/migrate_identity.dart b/lib/screens/myWallets/migrate_identity.dart index b8fa00d..535f6d3 100644 --- a/lib/screens/myWallets/migrate_identity.dart +++ b/lib/screens/myWallets/migrate_identity.dart @@ -188,9 +188,8 @@ class MigrateIdentityScreen extends StatelessWidget { height: 60 * ratio, child: ElevatedButton( style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, // background - onPrimary: Colors.white, // foreground + foregroundColor: Colors.white, elevation: 4, + backgroundColor: orangeC, // foreground ), onPressed: canValidate ? () async { diff --git a/lib/screens/myWallets/restore_chest.dart b/lib/screens/myWallets/restore_chest.dart index 06540a5..92e5440 100644 --- a/lib/screens/myWallets/restore_chest.dart +++ b/lib/screens/myWallets/restore_chest.dart @@ -3,6 +3,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:flutter/material.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/generate_wallets.dart'; import 'package:gecko/providers/substrate_sdk.dart'; import 'package:gecko/screens/common_elements.dart'; @@ -91,10 +92,10 @@ class RestoreChest extends StatelessWidget { width: 410, height: 70, child: ElevatedButton( + key: keyGoNext, style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, // background - onPrimary: Colors.white, // foreground + foregroundColor: Colors.white, elevation: 4, + backgroundColor: orangeC, // foreground ), onPressed: () async { if (await sub @@ -130,10 +131,10 @@ class RestoreChest extends StatelessWidget { width: 190, height: 60, child: ElevatedButton( + key: keyPastMnemonic, style: ElevatedButton.styleFrom( - elevation: 4, - primary: yellowC, // background - onPrimary: Colors.black, // foreground + foregroundColor: Colors.black, elevation: 4, + backgroundColor: yellowC, // foreground ), onPressed: () { genW.pasteMnemonic(context); @@ -173,7 +174,7 @@ class RestoreChest extends StatelessWidget { color: Colors.white, child: Text( text, - key: const Key('importText'), + key: keyBubbleSpeak, textAlign: TextAlign.justify, style: const TextStyle( color: Colors.black, fontSize: 19, fontWeight: FontWeight.w400), diff --git a/lib/screens/myWallets/show_seed.dart b/lib/screens/myWallets/show_seed.dart index b30b463..1bd1433 100644 --- a/lib/screens/myWallets/show_seed.dart +++ b/lib/screens/myWallets/show_seed.dart @@ -1,9 +1,9 @@ -import 'dart:typed_data'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:flutter/material.dart'; import 'package:gecko/models/wallet_data.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/providers/substrate_sdk.dart'; import 'package:gecko/screens/common_elements.dart'; @@ -73,12 +73,12 @@ class ShowSeed extends StatelessWidget { height: 40, child: ElevatedButton( style: ElevatedButton.styleFrom( + foregroundColor: Colors.black, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), - elevation: 1, - primary: orangeC, // background - onPrimary: Colors.black, // foreground + backgroundColor: orangeC, + elevation: 1, // foreground ), onPressed: () { Clipboard.setData( @@ -123,9 +123,8 @@ class ShowSeed extends StatelessWidget { height: 60 * ratio, child: ElevatedButton( style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, // background - onPrimary: Colors.white, // foreground + foregroundColor: Colors.white, elevation: 4, + backgroundColor: orangeC, // foreground ), onPressed: () { Navigator.pop(context); @@ -206,7 +205,7 @@ class ShowSeed extends StatelessWidget { ), Text( dataWord, - key: Key('word$dataWord'), + key: keyMnemonicWord(dataWord), style: TextStyle(fontSize: 17 * ratio, color: Colors.black), ), ]), diff --git a/lib/screens/myWallets/transaction_comment.dart b/lib/screens/myWallets/transaction_comment.dart index 3632f65..7312324 100644 --- a/lib/screens/myWallets/transaction_comment.dart +++ b/lib/screens/myWallets/transaction_comment.dart @@ -1,3 +1,5 @@ +// ignore_for_file: must_be_immutable + import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; @@ -5,7 +7,6 @@ import 'package:flutter/material.dart'; // import 'package:gecko/models/home.dart'; // import 'package:provider/provider.dart'; -// ignore: must_be_immutable class TransactionCommentScreen extends StatelessWidget { TextEditingController tplController = TextEditingController(); diff --git a/lib/screens/myWallets/unlocking_wallet.dart b/lib/screens/myWallets/unlocking_wallet.dart index 7b84eff..04c23ab 100644 --- a/lib/screens/myWallets/unlocking_wallet.dart +++ b/lib/screens/myWallets/unlocking_wallet.dart @@ -1,7 +1,11 @@ +// ignore_for_file: must_be_immutable + import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:gecko/models/chest_data.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/substrate_sdk.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/models/wallet_data.dart'; @@ -12,7 +16,6 @@ import 'package:pin_code_fields/pin_code_fields.dart'; import 'package:provider/provider.dart'; import 'package:gecko/globals.dart'; -// ignore: must_be_immutable class UnlockingWallet extends StatelessWidget { UnlockingWallet({Key? keyUnlockWallet, required this.wallet}) : super(key: keyUnlockWallet); @@ -23,7 +26,6 @@ class UnlockingWallet extends StatelessWidget { // ignore: close_sinks StreamController? errorController; - final formKey = GlobalKey(); Color? pinColor = const Color(0xffF9F9F1); var walletPin = ''; @@ -52,7 +54,7 @@ class UnlockingWallet extends StatelessWidget { left: 15, child: Builder( builder: (context) => IconButton( - key: const Key('popButton'), + key: keyPopButton, icon: const Icon( Icons.arrow_back, color: Colors.black, @@ -103,6 +105,7 @@ class UnlockingWallet extends StatelessWidget { SizedBox(height: 3 * ratio), if (canUnlock) InkWell( + key: keyCachePassword, onTap: () { walletOptions.changePinCacheChoice(); }, @@ -127,7 +130,7 @@ class UnlockingWallet extends StatelessWidget { const SizedBox(height: 10), // if (canUnlock) InkWell( - key: const Key('chooseChest'), + key: keyChangeChest, onTap: () { Navigator.push( context, @@ -182,10 +185,11 @@ class UnlockingWallet extends StatelessWidget { } return Form( - key: formKey, + // key: keyPinForm, child: Padding( padding: EdgeInsets.symmetric(vertical: 5 * ratio, horizontal: 30), child: PinCodeTextField( + key: keyPinForm, focusNode: pinFocus, autoFocus: true, appContext: context, @@ -213,6 +217,7 @@ class UnlockingWallet extends StatelessWidget { fieldWidth: 50, activeFillColor: Colors.black, ), + showCursor: kDebugMode ? false : true, cursorColor: Colors.black, animationDuration: const Duration(milliseconds: 300), textStyle: const TextStyle(fontSize: 20, height: 1.6), diff --git a/lib/screens/myWallets/wallet_options.dart b/lib/screens/myWallets/wallet_options.dart index 58f58ee..bcbd873 100644 --- a/lib/screens/myWallets/wallet_options.dart +++ b/lib/screens/myWallets/wallet_options.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:gecko/globals.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/duniter_indexer.dart'; import 'package:gecko/providers/home.dart'; import 'package:gecko/providers/my_wallets.dart'; @@ -37,6 +38,10 @@ class WalletOptions extends StatelessWidget { DuniterIndexer duniterIndexer = Provider.of(context, listen: false); + // SubstrateSdk sub = Provider.of(context, listen: false); + // sub.spawnBlock(); + // sub.spawnBlock(0, 20); + log.d(walletOptions.address.text); final int currentChest = myWalletProvider.getCurrentChest(); @@ -213,7 +218,7 @@ class WalletOptions extends StatelessWidget { else const SizedBox(), if (isMember.data!) - manageMemberStatus(context) + manageMembership(context) ]); }), ]); @@ -292,10 +297,10 @@ class WalletOptions extends StatelessWidget { width: 320, height: 60, child: ElevatedButton( + key: keyConfirmIdentity, style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, // background - onPrimary: Colors.white, // foreground + foregroundColor: Colors.white, elevation: 4, + backgroundColor: orangeC, // foreground ), onPressed: () { walletProvider.confirmIdentityPopup(context); @@ -334,7 +339,7 @@ class WalletOptions extends StatelessWidget { Widget pubkeyWidget(WalletOptionsProvider walletProvider, BuildContext ctx) { final String shortPubkey = getShortPubkey(walletProvider.address.text); return GestureDetector( - key: const Key('copyPubkey'), + key: keyCopyAddress, onTap: () { Clipboard.setData(ClipboardData(text: walletProvider.address.text)); snackCopyKey(ctx); @@ -359,12 +364,12 @@ class WalletOptions extends StatelessWidget { height: 40, child: ElevatedButton( style: ElevatedButton.styleFrom( + foregroundColor: Colors.black, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), - elevation: 1, - primary: orangeC, // background - onPrimary: Colors.black, // foreground + backgroundColor: orangeC, + elevation: 1, // foreground ), onPressed: () { Clipboard.setData( @@ -394,7 +399,7 @@ class WalletOptions extends StatelessWidget { WalletsProfilesProvider historyProvider, WalletOptionsProvider walletProvider) { return InkWell( - key: const Key('displayActivity'), + key: keyOpenActivity, onTap: () { // _historyProvider.nPage = 1; Navigator.push( @@ -431,11 +436,11 @@ class WalletOptions extends StatelessWidget { ); } - Widget manageMemberStatus(BuildContext context) { + Widget manageMembership(BuildContext context) { WalletOptionsProvider walletOptions = Provider.of(context, listen: false); return InkWell( - key: const Key('manageStatus'), + key: keyManageMembership, onTap: () { Navigator.push( context, @@ -471,7 +476,7 @@ class WalletOptions extends StatelessWidget { WalletData defaultWallet = myWalletProvider.getDefaultWallet(); walletOptions.isDefaultWallet = (defaultWallet.number == wallet.id()[1]); return InkWell( - key: const Key('setDefaultWallet'), + key: keySetDefaultWallet, onTap: !walletProvider.isDefaultWallet ? () async { await setDefaultWallet(context, currentChest); @@ -544,7 +549,7 @@ class WalletOptions extends StatelessWidget { !hasConsumers.data! && (balance > 2 || balance == 0); return InkWell( - key: const Key('deleteWallet'), + key: keyDeleteWallet, onTap: canDelete ? () async { await walletProvider.deleteWallet(context, wallet); diff --git a/lib/screens/myWallets/wallets_home.dart b/lib/screens/myWallets/wallets_home.dart index 19a324c..2cb0d0a 100644 --- a/lib/screens/myWallets/wallets_home.dart +++ b/lib/screens/myWallets/wallets_home.dart @@ -4,6 +4,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/chest_data.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/duniter_indexer.dart'; import 'package:gecko/providers/home.dart'; import 'package:gecko/providers/my_wallets.dart'; @@ -59,7 +60,6 @@ class WalletsHome extends StatelessWidget { ); }), title: Text(currentChest.name!, - key: const Key('myWallets'), style: TextStyle(color: Colors.grey[850])), backgroundColor: const Color(0xffFFD58D), ), @@ -87,9 +87,8 @@ class WalletsHome extends StatelessWidget { height: 60, ), style: ElevatedButton.styleFrom( - elevation: 2, - primary: floattingYellow, // background - onPrimary: Colors.black, // foreground + foregroundColor: Colors.black, elevation: 2, + backgroundColor: floattingYellow, // foreground ), onPressed: () => Navigator.push( context, @@ -108,7 +107,7 @@ class WalletsHome extends StatelessWidget { )), const SizedBox(height: 30), InkWell( - key: const Key('importG1v1'), + key: keyImportG1v1, onTap: () { Navigator.push( context, @@ -130,7 +129,7 @@ class WalletsHome extends StatelessWidget { ), const SizedBox(height: 5), InkWell( - key: const Key('changeChest'), + key: keyChangeChest, onTap: () { Navigator.push( context, @@ -190,7 +189,7 @@ class WalletsHome extends StatelessWidget { return CustomScrollView(slivers: [ const SliverToBoxAdapter(child: SizedBox(height: 20)), SliverGrid.count( - key: const Key('listWallets'), + key: keyListWallets, crossAxisCount: nTule, childAspectRatio: 1, crossAxisSpacing: 0, @@ -200,6 +199,7 @@ class WalletsHome extends StatelessWidget { Padding( padding: const EdgeInsets.all(16), child: GestureDetector( + key: keyOpenWallet(repository.address!), onTap: () { walletOptions.getAddress( currentChestNumber, repository.derivation!); @@ -366,7 +366,7 @@ class WalletsHome extends StatelessWidget { child: Column(children: [ Expanded( child: InkWell( - key: const Key('addDerivation'), + key: keyAddDerivation, onTap: () async { if (!myWalletProvider.isNewDerivationLoading) { WalletData? defaultWallet = diff --git a/lib/screens/my_contacts.dart b/lib/screens/my_contacts.dart index 31ab42c..3307e35 100644 --- a/lib/screens/my_contacts.dart +++ b/lib/screens/my_contacts.dart @@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:flutter/material.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/cesium_plus.dart'; import 'package:gecko/models/g1_wallets_list.dart'; import 'package:gecko/providers/duniter_indexer.dart'; @@ -28,7 +29,6 @@ class ContactsScreen extends StatelessWidget { DuniterIndexer duniterIndexer = Provider.of(context, listen: false); - int keyID = 0; double avatarSize = 55; final myContacts = contactsBox.toMap().values.toList(); @@ -71,7 +71,7 @@ class ContactsScreen extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(horizontal: 5), child: ListTile( - key: Key('searchResult${keyID++}'), + key: keySearchResult('keyID++'), horizontalTitleGap: 40, contentPadding: const EdgeInsets.all(5), leading: cesiumPlusProvider diff --git a/lib/screens/onBoarding/10.dart b/lib/screens/onBoarding/10.dart index 2680d33..4b7b144 100644 --- a/lib/screens/onBoarding/10.dart +++ b/lib/screens/onBoarding/10.dart @@ -1,11 +1,14 @@ // ignore_for_file: file_names +// ignore_for_file: must_be_immutable import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; import 'package:gecko/globals.dart'; import 'package:gecko/models/wallet_data.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/generate_wallets.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/providers/substrate_sdk.dart'; @@ -15,7 +18,6 @@ import 'package:gecko/screens/onBoarding/11_congratulations.dart'; import 'package:pin_code_fields/pin_code_fields.dart'; import 'package:provider/provider.dart'; -// ignore: must_be_immutable class OnboardingStepTen extends StatelessWidget { OnboardingStepTen({Key? validationKey, this.scanDerivation = false}) : super(key: validationKey); @@ -100,6 +102,7 @@ class OnboardingStepTen extends StatelessWidget { Consumer(builder: (context, sub, _) { return sub.nodeConnected ? InkWell( + key: keyCachePassword, onTap: () { walletOptions.changePinCacheChoice(); }, @@ -149,7 +152,7 @@ class OnboardingStepTen extends StatelessWidget { child: Padding( padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 30), child: PinCodeTextField( - key: const Key('formKey2'), + key: keyPinForm, autoFocus: true, appContext: context, pastedTextStyle: TextStyle( @@ -176,6 +179,7 @@ class OnboardingStepTen extends StatelessWidget { fieldWidth: 50, activeFillColor: hasError ? Colors.blueAccent : Colors.black, ), + showCursor: kDebugMode ? false : true, cursorColor: Colors.black, animationDuration: const Duration(milliseconds: 300), textStyle: const TextStyle(fontSize: 20, height: 1.6), @@ -224,6 +228,7 @@ class OnboardingStepTen extends StatelessWidget { generateWalletProvider.generatedMnemonic = ''; myWalletProvider.resetPinCode(); + // sleep(const Duration(milliseconds: 500)); Navigator.push( context, FaderTransition( diff --git a/lib/screens/onBoarding/11_congratulations.dart b/lib/screens/onBoarding/11_congratulations.dart index c461a3b..d372db2 100644 --- a/lib/screens/onBoarding/11_congratulations.dart +++ b/lib/screens/onBoarding/11_congratulations.dart @@ -4,10 +4,9 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; import 'package:gecko/globals.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/screens/common_elements.dart'; -import 'package:gecko/screens/myWallets/wallets_home.dart'; -// ignore: must_be_immutable class OnboardingStepEleven extends StatelessWidget { const OnboardingStepEleven({Key? key}) : super(key: key); @@ -54,20 +53,28 @@ Widget finishButton(BuildContext context) { width: 380 * ratio, height: 60 * ratio, child: ElevatedButton( - key: const Key('goWalletHome'), + key: keyGoWalletsHome, style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, - onPrimary: Colors.white, // foreground + foregroundColor: Colors.white, elevation: 4, + backgroundColor: orangeC, // foreground ), onPressed: () { - Navigator.pushAndRemoveUntil( - context, - MaterialPageRoute(builder: (context) { - return const WalletsHome(); - }), - ModalRoute.withName('/'), - ); + //TODO: fix bad widget ancestor when pupUntil (multi_chest test failed) + + // Navigator.popUntil(homeContext, ModalRoute.withName('/')); + // Navigator.of(homeContext, rootNavigator: true) + // .popUntil(ModalRoute.withName('/')); + // while (Navigator.of(homeContext).canPop()) { + // Navigator.of(homeContext).pop(); + // } + + // Navigator.pushNamed(homeContext, '/mywallets'); + + Navigator.pushNamedAndRemoveUntil( + context, '/mywallets', (route) => route.isFirst); + + // Navigator.pushNamedAndRemoveUntil( + // homeContext, '/mywallets', ModalRoute.withName('/')); }, child: Text("accessMyChest".tr(), style: diff --git a/lib/screens/onBoarding/5.dart b/lib/screens/onBoarding/5.dart index 6b9426f..5609e67 100644 --- a/lib/screens/onBoarding/5.dart +++ b/lib/screens/onBoarding/5.dart @@ -4,6 +4,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; import 'package:gecko/globals.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/generate_wallets.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/screens/common_elements.dart'; @@ -80,11 +81,10 @@ class _ChooseChestState extends State { width: 380 * ratio, height: 60 * ratio, child: ElevatedButton( - key: const Key('generateMnemonic'), + key: keyGenerateMnemonic, style: ElevatedButton.styleFrom( - elevation: 4, - primary: const Color(0xffFFD58D), - onPrimary: Colors.black, // foreground + foregroundColor: Colors.black, elevation: 4, + backgroundColor: const Color(0xffFFD58D), // foreground ), onPressed: () { // _generateWalletProvider.reloadBuild(); @@ -101,7 +101,8 @@ class _ChooseChestState extends State { SizedBox(height: 22 * ratio), nextButton( context, "iNotedMyMnemonic".tr(), false, widget.skipIntro), - SizedBox(height: 35 * ratio), + const Spacer(), + // SizedBox(height: 35 * ratio), ]), CommonElements().offlineInfo(context), ]), @@ -174,14 +175,13 @@ Widget arrayCell(dataWord) { ), Text( dataWord.split(':')[1], - key: Key('word${dataWord.split(':')[0]}'), + key: keyMnemonicWord(dataWord.split(':')[0]), style: TextStyle(fontSize: 17 * ratio, color: Colors.black), ), ]), ); } -// ignore: must_be_immutable class PrintWallet extends StatelessWidget { const PrintWallet(this.sentence, {Key? key}) : super(key: key); @@ -230,10 +230,10 @@ Widget nextButton( width: 380 * ratio, height: 60 * ratio, child: ElevatedButton( + key: keyGoNext, style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, // background - onPrimary: Colors.white, // foreground + foregroundColor: Colors.white, elevation: 4, + backgroundColor: orangeC, // foreground ), onPressed: () { generateWalletProvider.nbrWord = generateWalletProvider.getRandomInt(); diff --git a/lib/screens/onBoarding/6.dart b/lib/screens/onBoarding/6.dart index 5a58006..8e3a624 100644 --- a/lib/screens/onBoarding/6.dart +++ b/lib/screens/onBoarding/6.dart @@ -1,16 +1,17 @@ // ignore_for_file: file_names +// ignore_for_file: must_be_immutable import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; import 'package:gecko/globals.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/generate_wallets.dart'; import 'package:gecko/screens/common_elements.dart'; import 'package:gecko/screens/onBoarding/7.dart'; import 'package:gecko/screens/onBoarding/9.dart'; import 'package:provider/provider.dart'; -// ignore: must_be_immutable class OnboardingStepSix extends StatelessWidget { OnboardingStepSix( {Key? key, required this.skipIntro, required this.generatedMnemonic}) @@ -66,7 +67,7 @@ class OnboardingStepSix extends StatelessWidget { true), SizedBox(height: isTall ? 70 : 20), Text('${generateWalletProvider.nbrWord + 1}', - key: const Key('askedWord'), + key: keyAskedWord, style: TextStyle( fontSize: isTall ? 17 : 15, color: orangeC, @@ -81,7 +82,7 @@ class OnboardingStepSix extends StatelessWidget { )), width: 430, child: TextFormField( - key: const Key('inputWord'), + key: keyInputWord, autofocus: true, enabled: !generateWalletProvider.isAskedWordValid, controller: wordController, @@ -206,7 +207,7 @@ Widget arrayCell(dataWord) { ), Text( dataWord.split(':')[1], - key: Key('word${dataWord.split(':')[0]}'), + key: keyMnemonicWord(dataWord.split(':')[0]), style: const TextStyle(fontSize: 20, color: Colors.black), ), ]), @@ -224,10 +225,10 @@ Widget nextButton(BuildContext context, String text, nextScreen, bool isFast) { width: 380 * ratio, height: 60 * ratio, child: ElevatedButton( + key: keyGoNext, style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, // background - onPrimary: Colors.white, // foreground + foregroundColor: Colors.white, elevation: 4, + backgroundColor: orangeC, // foreground ), onPressed: () { Navigator.push( diff --git a/lib/screens/onBoarding/9.dart b/lib/screens/onBoarding/9.dart index ed29fce..7b54b60 100644 --- a/lib/screens/onBoarding/9.dart +++ b/lib/screens/onBoarding/9.dart @@ -3,12 +3,12 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; import 'package:gecko/globals.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/generate_wallets.dart'; import 'package:gecko/screens/common_elements.dart'; import 'package:gecko/screens/onBoarding/10.dart'; import 'package:provider/provider.dart'; -// ignore: must_be_immutable class OnboardingStepNine extends StatelessWidget { const OnboardingStepNine({Key? key, this.scanDerivation = false}) : super(key: key); @@ -52,7 +52,7 @@ class OnboardingStepNine extends StatelessWidget { alignment: Alignment.centerRight, children: [ TextField( - key: const Key('generatedPin'), + key: keyGeneratedPin, enabled: false, controller: generateWalletProvider.pin, maxLines: 1, @@ -79,11 +79,12 @@ class OnboardingStepNine extends StatelessWidget { width: 380 * ratio, height: 60 * ratio, child: ElevatedButton( - key: const Key('changeSecretCode'), + key: keyChangePin, style: ElevatedButton.styleFrom( + foregroundColor: Colors.black, elevation: 4, - primary: const Color(0xffFFD58D), - onPrimary: Colors.black, // foreground + backgroundColor: + const Color(0xffFFD58D), // foreground ), onPressed: () { generateWalletProvider.changePinCode( diff --git a/lib/screens/qrcode_fullscreen.dart b/lib/screens/qrcode_fullscreen.dart index c789e01..4ad6e6d 100644 --- a/lib/screens/qrcode_fullscreen.dart +++ b/lib/screens/qrcode_fullscreen.dart @@ -1,3 +1,5 @@ +// ignore_for_file: must_be_immutable + import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:flutter/material.dart'; @@ -6,7 +8,6 @@ import 'package:qr_flutter/qr_flutter.dart'; // import 'package:gecko/models/home.dart'; // import 'package:provider/provider.dart'; -// ignore: must_be_immutable class QrCodeFullscreen extends StatelessWidget { TextEditingController tplController = TextEditingController(); diff --git a/lib/screens/search.dart b/lib/screens/search.dart index 5ab523e..ece009b 100644 --- a/lib/screens/search.dart +++ b/lib/screens/search.dart @@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:flutter/material.dart'; +import 'package:gecko/models/widgets_keys.dart'; // import 'package:gecko/providers/home.dart'; import 'package:gecko/providers/search.dart'; import 'package:gecko/screens/common_elements.dart'; @@ -51,6 +52,7 @@ class SearchScreen extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(horizontal: 17), child: TextField( + key: keySearchField, controller: searchProvider.searchController, autofocus: true, maxLines: 1, @@ -91,10 +93,10 @@ class SearchScreen extends StatelessWidget { width: 410, height: 70, child: ElevatedButton( + key: keyConfirmSearch, style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, // background - onPrimary: Colors.white, // foreground + foregroundColor: Colors.white, elevation: 4, + backgroundColor: orangeC, // foreground ), onPressed: searchProvider.searchController.text.length >= 2 ? () { diff --git a/lib/screens/search_result.dart b/lib/screens/search_result.dart index a563873..c1ca7f0 100644 --- a/lib/screens/search_result.dart +++ b/lib/screens/search_result.dart @@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:flutter/material.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/cesium_plus.dart'; import 'package:gecko/models/g1_wallets_list.dart'; import 'package:gecko/providers/duniter_indexer.dart'; @@ -31,7 +32,6 @@ class SearchResultScreen extends StatelessWidget { DuniterIndexer duniterIndexer = Provider.of(context, listen: false); - int keyID = 0; double avatarSize = 55; return Scaffold( @@ -94,7 +94,7 @@ class SearchResultScreen extends StatelessWidget { padding: const EdgeInsets.symmetric(horizontal: 5), child: ListTile( - key: Key('searchResult${keyID++}'), + key: keySearchResult(g1Wallet.pubkey!), horizontalTitleGap: 40, contentPadding: const EdgeInsets.all(5), leading: cesiumPlusProvider diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart index b3e8715..e4931c0 100644 --- a/lib/screens/settings.dart +++ b/lib/screens/settings.dart @@ -1,6 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/duniter_indexer.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/providers/settings_provider.dart'; @@ -8,9 +9,7 @@ import 'package:gecko/providers/substrate_sdk.dart'; import 'package:gecko/globals.dart'; import 'package:polkawallet_sdk/api/types/networkParams.dart'; import 'package:provider/provider.dart'; -// import 'package:dropdown_button2/dropdown_button2.dart'; -// ignore: must_be_immutable class SettingsScreen extends StatelessWidget { final MyWalletsProvider _myWallets = MyWalletsProvider(); @@ -45,7 +44,7 @@ class SettingsScreen extends StatelessWidget { width: buttonWidth, child: Center( child: InkWell( - key: const Key('deleteChest'), + key: keyDeleteAllWallets, onTap: () async { log.i('Oublier tous mes coffres'); await _myWallets.deleteAllWallet(context); @@ -115,11 +114,14 @@ class SettingsScreen extends StatelessWidget { Icon(sub.nodeConnected && !sub.isLoadingEndpoint ? Icons.check : Icons.close), + if (sub.nodeConnected && !sub.isLoadingEndpoint) + const Icon(Icons.add_card_sharp, size: 0.01), const Spacer(), SizedBox( width: 265, child: Consumer(builder: (context, set, _) { return DropdownButtonHideUnderline( + key: keySelectDuniterNodeDropDown, child: DropdownButton( // alignment: AlignmentDirectional.topStart, value: selectedDuniterEndpoint, @@ -127,6 +129,7 @@ class SettingsScreen extends StatelessWidget { items: duniterBootstrapNodes .map((NetworkParams endpointParams) { return DropdownMenuItem( + key: keySelectDuniterNode(endpointParams.endpoint!), value: endpointParams.endpoint, child: Text(endpointParams.endpoint!), ); @@ -145,6 +148,7 @@ class SettingsScreen extends StatelessWidget { ? CircularProgressIndicator(color: orangeC) : Consumer(builder: (context, set, _) { return IconButton( + key: keyConnectToEndpoint, icon: Icon( Icons.send, color: selectedDuniterEndpoint != @@ -185,6 +189,7 @@ class SettingsScreen extends StatelessWidget { width: 200, height: 50, child: TextField( + key: keyCustomDuniterEndpoint, controller: endpointController, autocorrect: false, ), diff --git a/lib/screens/transaction_in_progress.dart b/lib/screens/transaction_in_progress.dart index dafd381..88d10e2 100644 --- a/lib/screens/transaction_in_progress.dart +++ b/lib/screens/transaction_in_progress.dart @@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:flutter/material.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/my_wallets.dart'; import 'package:gecko/providers/substrate_sdk.dart'; import 'package:gecko/providers/wallets_profiles.dart'; @@ -9,7 +10,6 @@ import 'package:provider/provider.dart'; // import 'package:gecko/models/home.dart'; // import 'package:provider/provider.dart'; -// ignore: must_be_immutable class TransactionInProgress extends StatelessWidget { const TransactionInProgress( {Key? key, this.transType = 'pay', this.fromAddress, this.toAddress}) @@ -33,6 +33,8 @@ class TransactionInProgress extends StatelessWidget { // Map jsonResult; final result = sub.transactionStatus; + // sub.spawnBlock(); + log.d(walletViewProvider.address!); final from = fromAddress ?? myWalletProvider.getDefaultWallet().name!; @@ -263,10 +265,10 @@ class TransactionInProgress extends StatelessWidget { width: 380 * ratio, height: 60 * ratio, child: ElevatedButton( + key: keyCloseTransactionScreen, style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, // background - onPrimary: Colors.white, // foreground + foregroundColor: Colors.white, elevation: 4, + backgroundColor: orangeC, // foreground ), onPressed: () { Navigator.pop(context); diff --git a/lib/screens/wallet_view.dart b/lib/screens/wallet_view.dart index bbe47d7..172bade 100644 --- a/lib/screens/wallet_view.dart +++ b/lib/screens/wallet_view.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; import 'package:gecko/globals.dart'; import 'package:flutter/material.dart'; import 'package:gecko/models/g1_wallets_list.dart'; +import 'package:gecko/models/widgets_keys.dart'; import 'package:gecko/providers/cesium_plus.dart'; import 'package:gecko/providers/home.dart'; import 'package:gecko/providers/substrate_sdk.dart'; @@ -42,12 +43,15 @@ class WalletViewScreen extends StatelessWidget { SubstrateSdk sub = Provider.of(context, listen: false); HomeProvider homeProvider = Provider.of(context, listen: false); - MyWalletsProvider myWalletProvider = Provider.of(context, listen: false); WalletData? defaultWallet = myWalletProvider.getDefaultWallet(); + sub.setCurrentWallet(defaultWallet); + // sub.spawnBlock(); + // sub.spawnBlock(0, 25); + return Scaffold( backgroundColor: backgroundColor, resizeToAvoidBottomInset: true, @@ -116,7 +120,7 @@ class WalletViewScreen extends StatelessWidget { child: Material( color: yellowC, //const Color(0xffFFD58D), // button color child: InkWell( - key: const Key('viewHistory'), + key: keyViewActivity, splashColor: orangeC, // inkwell color child: const Padding( padding: EdgeInsets.all(13), @@ -206,7 +210,7 @@ class WalletViewScreen extends StatelessWidget { color: const Color(0xffFFD58D), // button color child: InkWell( - key: const Key('certify'), + key: keyCertify, splashColor: orangeC, // inkwell color child: const Padding( padding: EdgeInsets.only(bottom: 0), @@ -245,8 +249,9 @@ class WalletViewScreen extends StatelessWidget { final acc = sub.getCurrentWallet(); sub.certify( acc.address!, - pin ?? myWalletProvider.pinCode, - walletViewProvider.address!); + walletViewProvider.address!, + pin ?? + myWalletProvider.pinCode); Navigator.push( context, @@ -290,7 +295,7 @@ class WalletViewScreen extends StatelessWidget { child: Material( color: const Color(0xffFFD58D), // button color child: InkWell( - key: const Key('copyKey'), + key: keyCopyAddress, splashColor: orangeC, // inkwell color child: const Padding( padding: EdgeInsets.all(20), @@ -331,7 +336,7 @@ class WalletViewScreen extends StatelessWidget { child: Material( color: orangeC, // button color child: InkWell( - key: const Key('pay'), + key: keyPay, splashColor: yellowC, onTap: sub.nodeConnected ? () { @@ -481,6 +486,7 @@ class WalletViewScreen extends StatelessWidget { const SizedBox(height: 10), Consumer(builder: (context, sub, _) { return InkWell( + key: keyChangeChest, onTap: () async { String? pin; if (myWalletProvider.pinCode == '') { @@ -572,6 +578,7 @@ class WalletViewScreen extends StatelessWidget { ), const SizedBox(height: 10), TextField( + key: keyAmountField, controller: walletViewProvider.payAmount, autofocus: true, maxLines: 1, @@ -617,10 +624,10 @@ class WalletViewScreen extends StatelessWidget { width: double.infinity, height: 60, child: ElevatedButton( + key: keyConfirmPayment, style: ElevatedButton.styleFrom( - elevation: 4, - primary: orangeC, // background - onPrimary: Colors.white, // foreground + foregroundColor: Colors.white, elevation: 4, + backgroundColor: orangeC, // foreground ), onPressed: canValidate ? () async { diff --git a/pubspec.lock b/pubspec.lock index d404e72..2ace6ec 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,21 +7,21 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "44.0.0" + version: "47.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "4.4.0" + version: "4.7.0" archive: dependency: transitive description: name: archive url: "https://pub.dartlang.org" source: hosted - version: "3.1.11" + version: "3.3.0" args: dependency: transitive description: @@ -29,20 +29,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.3.1" - assorted_layout_widgets: - dependency: "direct main" - description: - name: assorted_layout_widgets - url: "https://pub.dartlang.org" - source: hosted - version: "6.1.1" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.2" + version: "2.9.0" auth_header: dependency: transitive description: @@ -56,7 +49,7 @@ packages: name: barcode url: "https://pub.dartlang.org" source: hosted - version: "2.2.1" + version: "2.2.3" barcode_scan2: dependency: "direct main" description: @@ -147,7 +140,7 @@ packages: name: built_value url: "https://pub.dartlang.org" source: hosted - version: "8.4.0" + version: "8.4.1" carousel_slider: dependency: "direct main" description: @@ -161,7 +154,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" charcode: dependency: transitive description: @@ -182,7 +175,7 @@ packages: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" code_builder: dependency: transitive description: @@ -197,20 +190,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.16.0" - confirm_dialog: - dependency: "direct main" - description: - name: confirm_dialog - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" connectivity_plus: dependency: "direct main" description: name: connectivity_plus url: "https://pub.dartlang.org" source: hosted - version: "2.3.6" + version: "2.3.6+1" connectivity_plus_linux: dependency: transitive description: @@ -253,13 +239,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.2" - coverage: - dependency: transitive - description: - name: coverage - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.0" cross_file: dependency: transitive description: @@ -268,12 +247,12 @@ packages: source: hosted version: "0.3.3+1" crypto: - dependency: "direct main" + dependency: transitive description: name: crypto url: "https://pub.dartlang.org" source: hosted - version: "3.0.1" + version: "3.0.2" dart_style: dependency: transitive description: @@ -287,14 +266,7 @@ packages: name: dbus url: "https://pub.dartlang.org" source: hosted - version: "0.7.7" - desktop_window: - dependency: "direct main" - description: - name: desktop_window - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.0" + version: "0.7.8" dio: dependency: "direct main" description: @@ -309,13 +281,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" - dropdown_button2: - dependency: "direct main" - description: - name: dropdown_button2 - url: "https://pub.dartlang.org" - source: hosted - version: "1.7.1" durt: dependency: "direct main" description: @@ -343,9 +308,9 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.3.1" fast_base58: - dependency: "direct main" + dependency: transitive description: name: fast_base58 url: "https://pub.dartlang.org" @@ -384,6 +349,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.2" + flutter_dotenv: + dependency: "direct main" + description: + name: flutter_dotenv + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.2" flutter_driver: dependency: "direct main" description: flutter @@ -415,20 +387,13 @@ packages: description: flutter source: sdk version: "0.0.0" - flutter_logs: - dependency: "direct main" - description: - name: flutter_logs - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.7" flutter_markdown: dependency: "direct main" description: name: flutter_markdown url: "https://pub.dartlang.org" source: hosted - version: "0.6.10+3" + version: "0.6.10+5" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -436,13 +401,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.7" - flutter_svg: - dependency: "direct main" - description: - name: flutter_svg - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.3" flutter_test: dependency: "direct dev" description: flutter @@ -564,7 +522,7 @@ packages: source: hosted version: "0.2.0" hive: - dependency: "direct main" + dependency: transitive description: name: hive url: "https://pub.dartlang.org" @@ -577,15 +535,8 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" - hive_generator: - dependency: "direct dev" - description: - name: hive_generator - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.3" http: - dependency: "direct main" + dependency: transitive description: name: http url: "https://pub.dartlang.org" @@ -618,7 +569,7 @@ packages: name: icons_launcher url: "https://pub.dartlang.org" source: hosted - version: "2.0.2" + version: "2.0.5" image: dependency: transitive description: @@ -632,21 +583,21 @@ packages: name: image_cropper url: "https://pub.dartlang.org" source: hosted - version: "2.0.3" + version: "3.0.0" image_cropper_for_web: dependency: transitive description: name: image_cropper_for_web url: "https://pub.dartlang.org" source: hosted - version: "0.0.4" + version: "1.0.2" image_cropper_platform_interface: dependency: transitive description: name: image_cropper_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "3.0.2" image_picker: dependency: "direct main" description: @@ -674,7 +625,7 @@ packages: name: image_picker_ios url: "https://pub.dartlang.org" source: hosted - version: "0.8.5+6" + version: "0.8.6" image_picker_platform_interface: dependency: transitive description: @@ -682,20 +633,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.6.1" - infinite_scroll_pagination: - dependency: "direct main" - description: - name: infinite_scroll_pagination - url: "https://pub.dartlang.org" - source: hosted - version: "3.2.0" integration_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" intl: - dependency: "direct main" + dependency: transitive description: name: intl url: "https://pub.dartlang.org" @@ -784,28 +728,21 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.11" + version: "0.12.12" material_color_utilities: dependency: transitive description: name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" - matrix4_transform: - dependency: transitive - description: - name: matrix4_transform - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.1" + version: "0.1.5" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.7.0" + version: "1.8.0" mime: dependency: transitive description: @@ -834,13 +771,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.5.0" - node_preamble: - dependency: transitive - description: - name: node_preamble - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.1" normalize: dependency: transitive description: @@ -861,7 +791,7 @@ packages: name: package_info_plus url: "https://pub.dartlang.org" source: hosted - version: "1.4.3" + version: "1.4.3+1" package_info_plus_linux: dependency: transitive description: @@ -903,14 +833,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.1" - path_drawing: - dependency: transitive - description: - name: path_drawing - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.1" + version: "1.8.2" path_parsing: dependency: transitive description: @@ -931,7 +854,7 @@ packages: name: path_provider_android url: "https://pub.dartlang.org" source: hosted - version: "2.0.17" + version: "2.0.20" path_provider_ios: dependency: transitive description: @@ -966,7 +889,7 @@ packages: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.3" path_tree: dependency: transitive description: @@ -980,7 +903,7 @@ packages: name: pdf url: "https://pub.dartlang.org" source: hosted - version: "3.8.2" + version: "3.8.3" permission_handler: dependency: "direct main" description: @@ -1115,14 +1038,7 @@ packages: name: pubspec_parse url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" - pull_to_refresh: - dependency: "direct main" - description: - name: pull_to_refresh - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" + version: "1.2.1" qr: dependency: transitive description: @@ -1139,13 +1055,6 @@ packages: url: "https://github.com/insinfo/qr.flutter.git" source: git version: "4.0.0" - responsive_builder: - dependency: "direct main" - description: - name: responsive_builder - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.2" responsive_framework: dependency: "direct main" description: @@ -1161,19 +1070,19 @@ packages: source: hosted version: "0.27.5" sentry: - dependency: "direct main" + dependency: transitive description: name: sentry url: "https://pub.dartlang.org" source: hosted - version: "6.9.0" + version: "6.9.1" sentry_flutter: dependency: "direct main" description: name: sentry_flutter url: "https://pub.dartlang.org" source: hosted - version: "6.9.0" + version: "6.9.1" shared_preferences: dependency: "direct main" description: @@ -1215,7 +1124,7 @@ packages: name: shared_preferences_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.0" shared_preferences_web: dependency: transitive description: @@ -1237,20 +1146,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.3.2" - shelf_packages_handler: - dependency: transitive - description: - name: shelf_packages_handler - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.1" - shelf_static: - dependency: transitive - description: - name: shelf_static - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.1" shelf_web_socket: dependency: transitive description: @@ -1263,48 +1158,13 @@ packages: description: flutter source: sdk version: "0.0.99" - sliver_tools: - dependency: transitive - description: - name: sliver_tools - url: "https://pub.dartlang.org" - source: hosted - version: "0.2.7" - source_gen: - dependency: transitive - description: - name: source_gen - url: "https://pub.dartlang.org" - source: hosted - version: "1.2.2" - source_helper: - dependency: transitive - description: - name: source_helper - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.2" - source_map_stack_trace: - dependency: transitive - description: - name: source_map_stack_trace - url: "https://pub.dartlang.org" - source: hosted - version: "2.1.0" - source_maps: - dependency: transitive - description: - name: source_maps - url: "https://pub.dartlang.org" - source: hosted - version: "0.10.10" source_span: dependency: transitive description: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.2" + version: "1.9.0" stack_trace: dependency: transitive description: @@ -1332,42 +1192,28 @@ packages: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.1.1" sync_http: - dependency: "direct main" + dependency: transitive description: name: sync_http url: "https://pub.dartlang.org" source: hosted - version: "0.3.0" + version: "0.3.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" - test: - dependency: "direct main" - description: - name: test - url: "https://pub.dartlang.org" - source: hosted - version: "1.21.1" + version: "1.2.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.9" - test_core: - dependency: transitive - description: - name: test_core - url: "https://pub.dartlang.org" - source: hosted - version: "0.4.13" + version: "0.4.12" timing: dependency: transitive description: @@ -1388,7 +1234,7 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.3.1" universal_io: dependency: transitive description: @@ -1423,7 +1269,7 @@ packages: name: vm_service url: "https://pub.dartlang.org" source: hosted - version: "8.2.2" + version: "9.0.0" watcher: dependency: transitive description: @@ -1432,7 +1278,7 @@ packages: source: hosted version: "1.0.1" web_socket_channel: - dependency: "direct main" + dependency: transitive description: name: web_socket_channel url: "https://pub.dartlang.org" @@ -1445,13 +1291,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "3.0.0" - webkit_inspection_protocol: - dependency: transitive - description: - name: webkit_inspection_protocol - url: "https://pub.dartlang.org" - source: hosted - version: "1.1.0" webview_flutter: dependency: transitive description: @@ -1465,14 +1304,14 @@ packages: name: webview_flutter_android url: "https://pub.dartlang.org" source: hosted - version: "2.9.3" + version: "2.10.0" webview_flutter_platform_interface: dependency: transitive description: name: webview_flutter_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "1.9.1" + version: "1.9.3" webview_flutter_wkwebview: dependency: transitive description: @@ -1493,9 +1332,9 @@ packages: name: xdg_directories url: "https://pub.dartlang.org" source: hosted - version: "0.2.0+1" + version: "0.2.0+2" xml: - dependency: "direct main" + dependency: transitive description: name: xml url: "https://pub.dartlang.org" diff --git a/pubspec.yaml b/pubspec.yaml index 225f635..6797c1a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,23 +15,12 @@ dependencies: sdk: flutter flutter_driver: sdk: flutter - assorted_layout_widgets: ^6.1.1 bubble: ^1.2.1 carousel_slider: ^4.0.0 - confirm_dialog: ^1.0.0 - crypto: ^3.0.1 - fast_base58: ^0.2.0 flutter_lints: ^2.0.1 - flutter_logs: ^2.1.4 - flutter_svg: ^1.1.3 graphql_flutter: ^5.1.1-beta.3 - hive: ^2.0.4 hive_flutter: ^1.1.0 - http: ^0.13.4 - # image_gallery_saver: ^1.6.9 image_picker: ^0.8.4 - infinite_scroll_pagination: ^3.1.0 - intl: ^0.17.0 jdenticon_dart: ^2.0.0 logger: ^1.1.0 path_provider: ^2.0.9 @@ -45,19 +34,12 @@ dependencies: git: url: https://github.com/insinfo/qr.flutter.git ref: master - responsive_builder: ^0.4.1 responsive_framework: ^0.2.0 - sentry: ^6.5.1 sentry_flutter: ^6.5.1 shared_preferences: ^2.0.7 - sync_http: ^0.3.0 - test: ^1.17.10 truncate: ^3.0.1 unorm_dart: ^0.2.0 - xml: ^6.1.0 - pull_to_refresh: ^2.0.0 dio: ^4.0.4 - desktop_window: ^0.4.0 durt: ^0.1.6 package_info_plus: ^1.4.2 polkawallet_sdk: #^0.4.9 @@ -68,14 +50,13 @@ dependencies: # ref: gecko-old ref: gecko-unwrapbytes dots_indicator: ^2.1.0 - web_socket_channel: ^2.2.0 connectivity_plus: ^2.3.3 - image_cropper: ^2.0.3 + image_cropper: ^3.0.0 easy_localization: ^3.0.1 flutter_markdown: ^0.6.10+2 - dropdown_button2: ^1.6.3 pointycastle: ^3.6.1 hex: ^0.2.0 + flutter_dotenv: ^5.0.2 dev_dependencies: # flutter_launcher_icons: ^0.9.2 @@ -84,7 +65,6 @@ dev_dependencies: build_runner: ^2.1.2 flutter_test: sdk: flutter - hive_generator: ^1.1.1 integration_test: sdk: flutter @@ -112,3 +92,4 @@ flutter: - assets/onBoarding/progress_bar/ - assets/walletOptions/ - sounds/ + - .env diff --git a/scripts/startIntegrationsTests.sh b/scripts/startIntegrationsTests.sh deleted file mode 100755 index eca24d1..0000000 --- a/scripts/startIntegrationsTests.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -flutter drive --target=test_driver/app.dart diff --git a/test/widget_test.dart b/test/widget_test.dart deleted file mode 100644 index 96c517f..0000000 --- a/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// // This is a basic Flutter widget test. -// // -// // To perform an interaction with a widget in your test, use the WidgetTester -// // utility that Flutter provides. For example, you can send tap and scroll -// // gestures. You can also use WidgetTester to find child widgets in the widget -// // tree, read text, and verify that the values of widget properties are correct. - -// import 'package:flutter/material.dart'; -// import 'package:flutter_test/flutter_test.dart'; - -// import 'package:gecko/main.dart'; - -// void main() { -// testWidgets('Counter increments smoke test', (WidgetTester tester) async { -// // Build our app and trigger a frame. -// await tester.pumpWidget(Gecko()); - -// // Verify that our counter starts at 0. -// expect(find.text('0'), findsOneWidget); -// expect(find.text('1'), findsNothing); - -// // Tap the '+' icon and trigger a frame. -// await tester.tap(find.byIcon(Icons.add)); -// await tester.pump(); - -// // Verify that our counter has incremented. -// expect(find.text('0'), findsNothing); -// expect(find.text('1'), findsOneWidget); -// }); -// } diff --git a/test_driver/app.dart b/test_driver/app.dart deleted file mode 100644 index ec9a318..0000000 --- a/test_driver/app.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:flutter_driver/driver_extension.dart'; -import 'package:gecko/main.dart' as app; - -void main() { - // This line enables the extension. - enableFlutterDriverExtension(); - - // Call the `main()` function of the app, or call `runApp` with - // any widget you are interested in testing. - app.main(); -} diff --git a/test_driver/app_test.dart b/test_driver/app_test.dart deleted file mode 100644 index fa44048..0000000 --- a/test_driver/app_test.dart +++ /dev/null @@ -1,455 +0,0 @@ -import 'dart:async'; -import 'dart:io'; -import 'package:easy_localization/easy_localization.dart'; -import 'package:flutter_driver/flutter_driver.dart'; -import 'package:gecko/globals.dart'; -import 'package:test/test.dart'; -// import 'package:flutter/services.dart'; - -void main() { - int globalTimeout = 2; - group('Gecko App', () { - // First, define the Finders and use them to locate widgets from the - // test suite. Note: the Strings provided to the `byValueKey` method must - // be the same as the Strings we used for the Keys in step 1. - final manageWalletsFinder = find.byValueKey('manageWallets'); - // final buttonFinder = find.byValueKey('increment'); - - FlutterDriver? driver; - String? pinCode; - - // Connect to the Flutter driver before running any tests. - setUpAll(() async { - driver = await FlutterDriver.connect(); - await driver!.waitUntilFirstFrameRasterized(); - }); - - // Close the connection to the driver after the tests have completed. - tearDownAll(() async { - if (driver != null) { - driver!.close(); - } - }); - - // *** Global functions *** // - - // Function to tap the widget by key - Future tapOn(String key) async { - await driver!.tap(find.byValueKey(key)); - } - - // Easy get text - Future getText(String text) async { - return await driver!.getText(find.byValueKey( - text, - )); - } - - // Function to go back to previous screen - Future goBack() async { - await Process.run( - 'adb', - ['shell', 'input', 'keyevent', 'KEYCODE_BACK'], - runInShell: true, - ); - } - - // Easy sleep - Future sleep(int time) async { - await Future.delayed(Duration(milliseconds: time)); - } - - // Test if widget exist on screen, return a boolean - Future isPresent(SerializableFinder byValueKey, - {Duration timeout = const Duration(seconds: 1)}) async { - try { - await driver!.waitFor(byValueKey, timeout: timeout); - return true; - } catch (exception) { - return false; - } - } - - // Create a derivation - Future createDerivation() async { - await tapOn('addDerivation'); - await sleep(300); - } - - // Delete a derivation - Future deleteWallet(bool confirm) async { - await tapOn('deleteWallet'); - await sleep(100); - confirm ? await tapOn('confirmDeleting') : await tapOn('cancelDeleting'); - await sleep(300); - } - - // Delete all wallets - Future deleteAllWallets() async { - await tapOn('drawerMenu'); - await sleep(300); - await tapOn('parameters'); - await sleep(300); - await tapOn('deleteAllWallets'); - await sleep(300); - await tapOn('confirmDeletingAllWallets'); - await sleep(300); - } - - // Fast creation of new Keychain - Future createNewKeychain(String name) async { - await tapOn('drawerMenu'); - await sleep(300); - await tapOn('parameters'); - await sleep(300); - await tapOn('generateKeychain'); - while (await getText('generatedPin') == '') { - log.d('Waiting for pin code generation...'); - await sleep(100); - } - pinCode = await getText('generatedPin'); - await tapOn('storeKeychain'); - await sleep(100); - await driver!.enterText('triche'); - await tapOn('walletName'); - await driver!.enterText(name); - await sleep(50); - await tapOn('confirmStorage'); - await sleep(300); - return pinCode; - } - - // *** Begin of tests *** // - - test('OnBoarding - Open wallets management', ( - {timeout = Timeout.none}) async { - // await driver.runUnsynchronized(() async { // Needed if we want to manage async drivers - await driver!.tap(manageWalletsFinder); - - // If a wallet exist, go to delete theme all - if (!await isPresent(find.byValueKey('goStep1'))) { - await goBack(); - - await deleteAllWallets(); - - await driver!.tap(manageWalletsFinder); - } - - // Get the SerializableFinder for text widget with key 'textOnboarding' - SerializableFinder textOnboarding = find.byValueKey( - 'textOnboarding', - ); - - await sleep(100); - - // Verify onboarding is starting, with text - expect(await driver!.getText(textOnboarding), - "Je ne connais pour l’instant aucun de vos portefeuilles.\n\nVous pouvez en créer un nouveau, ou bien importer un portefeuille Cesium existant."); - }); - - test('OnBoarding - Go to create restore sentance', ( - {timeout = Timeout.none}) async { - await tapOn('goStep1'); - await tapOn('goStep2'); - await tapOn('goStep3'); - await tapOn('goStep4'); - await tapOn('goStep5'); - await tapOn('goStep6'); - - expect( - await driver!.getText(find.byValueKey( - 'step6', - )), - "iGeneratedYourMnemonicKeepItSecret".tr()); - }); - - test('OnBoarding - Generate sentance and confirme it', ( - {timeout = Timeout.none}) async { - await tapOn('goStep7'); - - while (await getText('word1') == '...') { - log.d('Waiting for Mnemonic generation...'); - await sleep(100); - } - - Future selectWord() async { - List words = [for (var i = 1; i <= 13; i += 1) i]; - - for (var j = 1; j < 13; j++) { - words[j] = await getText('word$j'); - } - expect( - await getText('step7'), "C'est le moment de noter votre phrase !"); - - await tapOn('goStep8'); - await sleep(200); - - String goodWord = words[int.parse( - await getText('askedWord'), - )]; - - // Enter the expected word - await driver!.enterText(goodWord); - - // Check if word is valid - await driver!.waitFor(find.text("C'est le bon mot !")); - - // Continue onboarding workflow - await tapOn('goStep9'); - } - - await selectWord(); - - //Go back 2 times to mnemonic generation screen - await goBack(); - await goBack(); - await sleep(100); - - // Generate 3 times mnemonic - await tapOn('generateMnemonic'); - await tapOn('generateMnemonic'); - await tapOn('generateMnemonic'); - await sleep(500); - - await selectWord(); - }); - test('OnBoarding - Generate secret code and confirm it', ( - {timeout = Timeout.none}) async { - expect(await getText('step9'), - "Super !\n\nJe vais maintenant créer votre code secret. \n\nVotre code secret chiffre votre coffre de clefs, ce qui le rend inutilisable par d’autres, par exemple si vous perdez votre téléphone ou si on vous le vole."); - await sleep(800); - await tapOn('goStep10'); - await sleep(50); - await tapOn('goStep11'); - - while (await getText('generatedPin') == '') { - log.d('Waiting for pin code generation...'); - await sleep(100); - } - - // Change secret code 4 times - for (int i = 0; i < 4; i++) { - await tapOn('changeSecretCode'); - } - - await sleep(500); - pinCode = await getText('generatedPin'); - - await tapOn('goStep12'); - await sleep(300); - - // //Enter bad secret code - // await driver.enterText('abcde'); - // await tapOn('formKey'); - // await sleep(1500); - // await tapOn('formKey2'); - - //Enter good secret code - await driver!.enterText(pinCode!); - - expect(await getText('step13'), - "Top !\n\nVotre coffre et votre portefeuille ont été créés avec un immense succès.\n\nFélicitations !"); - }); - - test('My wallets - Rename first derivation', ( - {timeout = const Duration(seconds: 2)}) async { - await tapOn('goWalletHome'); - - expect(await getText('myWallets'), "geckoChest".tr()); - await sleep(300); - - // Go to first derivation and rename it - await driver!.tap(find.text('Mon portefeuille courant')); - await sleep(300); - await tapOn('renameWallet'); - await sleep(100); - await tapOn('walletName'); - await sleep(100); - await driver!.enterText('Renommage wallet 1'); - await sleep(300); - await tapOn('renameWallet'); - await sleep(400); - await driver!.waitFor(find.text('Renommage wallet 1'), timeout: timeout); - // expect(await getText('walletName'), "Renommage wallet 1"); - await goBack(); - }); - - test('My wallets - Create a derivations, open thems, tap all buttons', ( - {timeout = const Duration(seconds: 2)}) async { - await driver!.waitFor(find.text('Renommage wallet 1'), timeout: timeout); - // Add a second derivation - await createDerivation(); - - // Go to second derivation options - await driver!.tap(find.text('Portefeuille 2')); - await sleep(100); - - // Test options - await tapOn('displayBalance'); - await tapOn('displayHistory'); - await sleep(300); - await goBack(); - await tapOn('displayBalance'); - await sleep(100); - await tapOn('displayBalance'); - await sleep(100); - await tapOn('displayBalance'); - await tapOn('setDefaultWallet'); - await sleep(50); - await tapOn('copyPubkey'); - await driver!.waitFor(find - .text('Cette clé publique a été copié dans votre presse-papier.')); - await goBack(); - - // Add a third derivation - await createDerivation(); - - // Add a fourth derivation - await createDerivation(); - await sleep(50); - - // Go to third derivation options - await driver!.tap(find.text('Portefeuille 3')); - await sleep(100); - await tapOn('displayBalance'); - - // Delete third derivation - await deleteWallet(true); - }); - - test('My wallets - Extra tests', ( - {timeout = const Duration(seconds: 2)}) async { - // Add derivation 5,6 and 7 - await driver!.waitFor(find.text('Portefeuille 4'), timeout: timeout); - await createDerivation(); - await createDerivation(); - await createDerivation(); - - // Go home and come back to my wallets view - await goBack(); - await sleep(100); - await tapOn('manageWallets'); - await sleep(200); - //Enter secret code - await driver!.enterText(pinCode!); - await sleep(200); - - // Go to derivation 6 and delete it - await driver!.tap(find.text('Portefeuille 6')); - await sleep(100); - await deleteWallet(true); - - // Go to 2nd derivation and check if it's de default - await driver!.tap(find.text('Portefeuille 2')); - await driver!.waitFor(find.text('Ce portefeuille est celui par defaut')); - await tapOn('setDefaultWallet'); - await sleep(100); - await driver!.waitFor(find.text('Ce portefeuille est celui par defaut')); - await sleep(300); - - // Display history, copy pubkey, go back and rename wallet name - await tapOn('displayHistory'); - await sleep(400); - await tapOn('copyPubkey'); - await driver!.waitFor(find - .text('Cette clé publique a été copié dans votre presse-papier.')); - await sleep(800); - await goBack(); - await sleep(300); - await tapOn('renameWallet'); - await sleep(100); - await tapOn('walletName'); - await sleep(100); - await driver!.enterText('Renommage wallet 2'); - await sleep(300); - await tapOn('renameWallet'); - await sleep(400); - await goBack(); - await driver!.waitFor(find.text('Renommage wallet 2')); - await driver!.scrollIntoView(find.text('+')); - await createDerivation(); - await createDerivation(); - await driver!.scrollIntoView(find.text('+')); - await createDerivation(); - await createDerivation(); - await driver!.scrollIntoView(find.text('+')); - await createDerivation(); - await createDerivation(); - await driver!.scrollIntoView(find.text('+')); - await createDerivation(); - await createDerivation(); - await driver!.scrollIntoView(find.text('+')); - await createDerivation(); - await createDerivation(); - await driver!.scrollIntoView(find.text('+')); - await createDerivation(); - await createDerivation(); - await driver!.scrollIntoView(find.text('+')); - await createDerivation(); - await sleep(400); - - // Scroll the wallet screen until Derivation 20 and open it - await driver!.scrollUntilVisible( - find.byValueKey('listWallets'), - find.text('Portefeuille 20'), - dyScroll: -300.0, - ); - - await driver!.waitFor(find.text('Portefeuille 20')); - await sleep(400); - await driver!.tap(find.text('Portefeuille 20')); - await tapOn('copyPubkey'); - }); - - test('Search - Search Pi profile, navigate in history transactions', ( - {timeout = const Duration(seconds: 2)}) async { - await driver!.waitFor(find.text('Portefeuille 20'), timeout: timeout); - await goBack(); - await goBack(); - await sleep(200); - await tapOn('searchIcon'); - await sleep(400); - await driver!.enterText('D2meevcAHFTS2gQMvmRW5Hzi25jDdikk4nC4u1FkwRaU'); - await sleep(100); - await tapOn('copyPubkey'); - await sleep(500); - await tapOn('switchPayHistory'); - await sleep(1200); - // await driver.scrollIntoView(find.byValueKey('listTransactions')); - await driver!.scrollUntilVisible( - find.byValueKey('listTransactions'), - find.byValueKey('transaction35'), - dyScroll: -600.0, - ); - await sleep(100); - await tapOn('transaction33'); - await driver!.waitFor(find.text('Commentaire:')); - - // Want to paste pubkey copied, but doesn't work actualy with flutter driver: https://github.com/flutter/flutter/issues/47448 - // final ClipboardData pubkeyCopied = - // await Clipboard.getData(Clipboard.kTextPlain); - // await driver.enterText(pubkeyCopied.text); - - await sleep(300); - }, timeout: Timeout(Duration(minutes: globalTimeout))); - - test('Wallet generation - Fast wallets generations', ( - {timeout = const Duration(seconds: 2)}) async { - await driver!.waitFor(find.text('Commentaire:'), timeout: timeout); - await goBack(); - await goBack(); - await deleteAllWallets(); - await sleep(100); - final String? pincode = await (createNewKeychain('Fast wallet')); - await sleep(200); - await driver!.enterText(pincode!); - await sleep(100); - await createDerivation(); - await sleep(100); - await driver!.tap(find.text('Fast wallet')); - await driver!.waitFor(find.text('Fast wallet')); - // Wait 3 seconds at the end - await sleep(3000); - }); - }, timeout: Timeout(Duration(minutes: globalTimeout))); -}