diff --git a/android/.tool-versions b/android/.tool-versions index 3d505cf2..af32ddaa 100644 --- a/android/.tool-versions +++ b/android/.tool-versions @@ -1,2 +1,2 @@ gradle 8.10.2 -kotlin 2.0.20 +kotlin 1.8.22 diff --git a/android/app/build.gradle b/android/app/build.gradle index 78ba6a63..75a53588 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -53,9 +53,15 @@ android { versionName flutterVersionName multiDexEnabled true consumerProguardFiles 'proguard-rules.pro' + testInstrumentationRunner "pl.leancode.patrol.PatrolJUnitRunner" + testInstrumentationRunnerArguments clearPackageData: "true" } + testOptions { + execution "ANDROIDX_TEST_ORCHESTRATOR" + } + compileOptions { coreLibraryDesugaringEnabled true @@ -94,6 +100,8 @@ android { debug { signingConfig signingConfigs.debug proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + minifyEnabled false + shrinkResources false } } } @@ -113,6 +121,11 @@ dependencies { } coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3' + androidTestUtil "androidx.test:orchestrator:1.5.1" +// androidTestImplementation 'androidx.test:runner:1.4.0' +// androidTestImplementation 'androidx.test:rules:1.4.0' + + } apply plugin: 'com.google.gms.google-services' \ No newline at end of file diff --git a/android/app/src/androidTest/java/com/freaccess/MainActivityTest.java b/android/app/src/androidTest/java/com/freaccess/MainActivityTest.java new file mode 100644 index 00000000..feea88e9 --- /dev/null +++ b/android/app/src/androidTest/java/com/freaccess/MainActivityTest.java @@ -0,0 +1,36 @@ +package com.freaccess.hub; + +import androidx.test.platform.app.InstrumentationRegistry; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import pl.leancode.patrol.PatrolJUnitRunner; + +@RunWith(Parameterized.class) +public class MainActivityTest { + private final String dartTestName; + + public MainActivityTest(String dartTestName) { + this.dartTestName = dartTestName; + } + + @Parameters(name = "{0}") + public static Object[] testCases() { + PatrolJUnitRunner instrumentation = (PatrolJUnitRunner) InstrumentationRegistry.getInstrumentation(); + // replace "MainActivity.class" with "io.flutter.embedding.android.FlutterActivity.class" + // if in AndroidManifest.xml in manifest/application/activity you have + // android:name="io.flutter.embedding.android.FlutterActivity" + instrumentation.setUp(MainActivity.class); + instrumentation.waitForPatrolAppService(); + return instrumentation.listDartTests(); + } + + @Test + public void runDartTest() { + PatrolJUnitRunner instrumentation = (PatrolJUnitRunner) InstrumentationRegistry.getInstrumentation(); + instrumentation.runDartTest(dartTestName); + } +} \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index c652f990..cd5795fe 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,6 +1,6 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '2.0.20' // Replace with the latest version + ext.kotlin_version = '1.8.22' ext.gradle_version = '8.6.0' // Replace with the latest version ext.google_services_version = '4.4.2' // Replace with the latest version repositories { @@ -8,12 +8,14 @@ buildscript { mavenCentral() } dependencies { - classpath "com.android.tools.build:gradle:$gradle_version" // Use a versão do Gradle que corresponde à sua configuração + classpath "com.android.tools.build:gradle:$gradle_version" + // Use a versão do Gradle que corresponde à sua configuração classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "com.google.gms:google-services:$google_services_version" // Google Services plugin - } + classpath "com.google.gms:google-services:$google_services_version" + // Google Services plugin + } } - + allprojects { repositories { google() diff --git a/android/settings.gradle b/android/settings.gradle index 4fdec649..28b925fa 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -20,7 +20,7 @@ plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id 'com.android.application' version '8.6.0' apply false id 'com.android.library' version '8.6.0' apply false - id 'org.jetbrains.kotlin.android' version '2.0.20' apply false + id 'org.jetbrains.kotlin.android' version '1.8.22' apply false // START: FlutterFire Configuration @@ -29,8 +29,7 @@ plugins { // id "org.jetbrains.kotlin.android" version "1.7.10" apply false // id "org.jetbrains.kotlin.android" version "1.8.10" apply false - - + } include ":app" \ No newline at end of file diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart index b4cba6ff..84959631 100644 --- a/integration_test/app_test.dart +++ b/integration_test/app_test.dart @@ -1,8 +1,10 @@ import 'dart:collection'; +import 'dart:math'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:hub/components/molecular_components/throw_exception/throw_exception_widget.dart'; import 'package:hub/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart'; import 'package:hub/components/templates_components/card_item_template_component/card_item_template_component_widget.dart'; import 'package:hub/features/backend/api_requests/index.dart'; @@ -16,6 +18,7 @@ import 'package:hub/initialization.dart'; import 'package:hub/main.dart'; import 'package:integration_test/integration_test.dart'; import 'package:material_symbols_icons/symbols.dart'; +import 'package:patrol_finders/patrol_finders.dart'; part 'auth_test.dart'; part 'home_test.dart'; @@ -30,12 +33,15 @@ part 'storage_test.dart'; part 'utils_test.dart'; part 'welcome_test.dart'; +late PatrolTester $; + void main() { + //init integration test IntegrationTestWidgetsFlutterBinding.ensureInitialized(); // WelcomeTest.signInToSignUp(); // WelcomeTest.signUpToSignIn(); - // + // // // AuthenticationTest.signIn(); // AuthenticationTest.signUp(); // AuthenticationTest.signOut(); @@ -43,7 +49,7 @@ void main() { // ModularizationTest.switchLicense(); // ModularizationTest.containLicense(); // - // MenuTest.navToEntries(); + MenuTest.navToEntries(); // MenuTest.containEntries(); // MenuTest.labels2AppbarConsistency(); // diff --git a/integration_test/auth_test.dart b/integration_test/auth_test.dart index 7d9194f4..154b8dfe 100644 --- a/integration_test/auth_test.dart +++ b/integration_test/auth_test.dart @@ -1,145 +1,143 @@ part of 'app_test.dart'; class AuthenticationTest { - static Future signIn() async { - _setUpUnlogged(); - testWidgets('Sign-In with erro@exemplo.com', // - (WidgetTester tester) async { - await tester.pumpWidget(const App()); - tester.printToConsole('AuthenticationTest - Navigate Sign-In'); - await _navigateToSignIn(tester); - await _auth({ - 'emailTextFormField': 'erro@exemplo.com', - 'passwordTextFormField': '12345678' - }, tester); - await tester.pumpAndSettle(); - await Future.delayed(const Duration(milliseconds: 500)); - return; - }); - _tearDown(); + static Future signIn() async { + patrolWidgetTest( + 'Sign-In with erro@exemplo.com', + (PatrolTester tester) async { + $ = tester; + final PatrolFinder throwsException = $(Dialog).$(ThrowExceptionWidget); + final Map credentials = { + 'emailTextFormField': 'erro@exemplo.com', + 'passwordTextFormField': '12345678', + }; + await _unlogged(); + await $.pumpWidgetAndSettle(const App()); + await Future.delayed(const Duration(milliseconds: 500)); + await _navigateToSignIn($); + await _auth(credentials, $, throwsException); + await Future.delayed(const Duration(milliseconds: 500)); + }, + ); - _setUpUnlogged(); - testWidgets('Sign-In with email_app@exemplo.com', - (WidgetTester tester) async { - await tester.pumpWidget(const App()); - tester.printToConsole('AuthenticationTest - Navigate Sign-In'); - await _navigateToSignIn(tester); - await _auth({ - 'emailTextFormField': 'email_app@exemplo.com', - 'passwordTextFormField': '12345678' - }, tester); - await Future.delayed(const Duration(milliseconds: 500)); - return; - }); - _tearDown(); + patrolWidgetTest( + 'Sign-In with email_app@exemplo.com', + (PatrolTester tester) async { + $ = tester; + final PatrolFinder throwsException = $(Dialog).$(ThrowExceptionWidget); + final Map credentials = { + 'emailTextFormField': 'email_app@exemplo.com', + 'passwordTextFormField': '12345678', + }; + await _unlogged(); + await $.pumpWidgetAndSettle(const App()); + await _navigateToSignIn($); + await _auth(credentials, $, throwsException); + await Future.delayed(const Duration(milliseconds: 500)); + }, + ); } - static Future signUp() async { - _setUpUnlogged(); - testWidgets('Sign Up - credenciais já registradas', - (WidgetTester tester) async { - var credentials = { - 'nameTextFormField': 'app', - 'emailTextFormField': 'email_app@exemplo.com', - 'passwordTextFormField': '12345678' - }; + static Future signUp() async { + patrolWidgetTest( + 'Sign Up - credenciais já registradas', + (PatrolTester tester) async { + $ = tester; + final PatrolFinder throwsException = $(Dialog).$(ThrowExceptionWidget); + final Map credentials = { + 'nameTextFormField': 'app', + 'emailTextFormField': 'email_app@exemplo.com', + 'passwordTextFormField': '12345678', + }; + await _unlogged(); + await $.pumpWidgetAndSettle(const App()); + await _navigateToSignUp($); + await _auth(credentials, $, throwsException); + await Future.delayed(const Duration(milliseconds: 500)); + }, + ); - await tester.pumpWidget(const App()); - tester.printToConsole('AuthenticationTest - Navigate Sign-Up'); - await _navigateToSignUp(tester); - await _auth(credentials, tester); - await tester.pumpAndSettle(); - await Future.delayed(const Duration(milliseconds: 500)); - return; - }); - _tearDown(); + patrolWidgetTest( + 'Sign Up - credenciais novas', + (PatrolTester tester) async { + $ = tester; + final PatrolFinder throwsException = $(Dialog).$(ThrowExceptionWidget); + final name = _generateRandomString(7); + final email = '$name@example.com'; + final password = '12345678'; + final Map credentials = { + 'nameTextFormField': name, + 'emailTextFormField': email, + 'passwordTextFormField': password, + }; - _setUpUnlogged(); - testWidgets('Sign Up - credenciais novas', // - (WidgetTester tester) async { - late Map credentials; + await _unlogged(); - var name = ff.randomString(7, 7, true, true, true); - var email = '$name@example.com'; - var password = '12345678'; - credentials = { - 'nameTextFormField': name, - 'emailTextFormField': email, - 'passwordTextFormField': password - }; + await $.pumpWidgetAndSettle(const App()); + await _navigateToSignUp($); + await _auth(credentials, $, throwsException); - await tester.pumpWidget(const App()); - tester.printToConsole('AuthenticationTest - Navigate Sign-Up'); - await _navigateToSignUp(tester); - await _auth(credentials, tester); - await Future.delayed(const Duration(milliseconds: 500)); - return; - }); - _tearDown(); + await Future.delayed(const Duration(milliseconds: 500)); + }, + ); } - static Future signOut() async { - _setUpLogged(); - testWidgets('Deslogar da Conta', (WidgetTester tester) async { - await tester.pumpWidget(const App()); - final Finder drawerButton = find.byIcon(Icons.menu_rounded); - await tester.pumpAndSettle(); - await tester.tap(drawerButton); - await tester.pumpAndSettle(); - final Finder signOutButton = find.byIcon(Icons.exit_to_app); - await tester.pumpAndSettle(); - await tester.tap(signOutButton); - await tester.pumpAndSettle(); - await Future.delayed(const Duration(milliseconds: 500)); - return; - }); - _tearDown(); + static Future signOut() async { + patrolWidgetTest( + 'Deslogar da Conta', + (PatrolTester tester) async { + $ = tester; + await _logged(); + await $.pumpWidgetAndSettle(const App()); + await $.waitUntilVisible($(MenuStaggeredView)); + await Future.delayed(const Duration(milliseconds: 500)); + + await $(Icons.menu_rounded).tap(); + await $.waitUntilVisible($(MenuListView)); + await $(Icons.exit_to_app) + .waitUntilVisible() + .tap(settlePolicy: SettlePolicy.trySettle); + await Future.delayed(const Duration(milliseconds: 500)); + }, + ); } - static Future recovery() async {} + static Future recovery() async {} } Future _auth( - Map credentials, WidgetTester tester) async { - await _enterCredentials(credentials, tester); - await _submit('SubmitButtonWidget', tester); -} - -Future _send( - Map credentials, WidgetTester tester) async { - await _enterCredentials(credentials, tester); - await _submit('SendButtonWidget', tester); + Map credentials, + PatrolTester $, + PatrolFinder throwsException, +) async { + await _enterCredentials(credentials, $); + await _submit('SubmitButtonWidget', $, throwsException); } Future _enterCredentials( - Map credentials, WidgetTester tester) async { - await tester.pumpAndSettle(); + Map credentials, + PatrolTester $, +) async { for (var entry in credentials.entries) { - final Finder field = find.byKey(ValueKey(entry.key)); - await tester.pumpAndSettle(); - expect(field, findsOneWidget); - await tester.enterText(field, entry.value); - await tester.pumpAndSettle(); + await $(ValueKey(entry.key)).enterText(entry.value); + await $.pumpAndSettle(); } - await tester.pumpAndSettle(); } -Future _submit(String key, WidgetTester tester) async { - await tester.pumpAndSettle(); - final Finder submitButton = find.byKey(ValueKey(key)); - await tester.pumpAndSettle(); - if (submitButton.evaluate().isNotEmpty) { - await tester.tap(submitButton); - } - final Finder throwExceptionWidget = - find.byKey(const ValueKey('ThrowExceptionWidget')); - - if (throwExceptionWidget.evaluate().isNotEmpty) { - await tester.pumpAndSettle(); - await tester.ensureVisible(throwExceptionWidget); - await tester.tap(throwExceptionWidget); - await tester.pumpAndSettle(); +Future _submit( + String key, PatrolTester $, PatrolFinder throwsException) async { + await $(ValueKey(key)).tap(); + if ($(ValueKey('ThrowExceptionWidget')).exists) { + // expect(throwsException, findsOneWidget); + await $(ValueKey('ThrowExceptionWidget')).tap(); } else { - await _navigateBackUsingSystemGesture(); + _navigateBackUsingSystemGesture(); } } + +String _generateRandomString(int length) { + const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'; + final rand = Random(); + return List.generate(length, (index) => chars[rand.nextInt(chars.length)]) + .join(); +} diff --git a/integration_test/locals_test.dart b/integration_test/locals_test.dart index 5216fd51..627cc0c3 100644 --- a/integration_test/locals_test.dart +++ b/integration_test/locals_test.dart @@ -2,69 +2,67 @@ part of 'app_test.dart'; class LocalsTest { static Future setLocal() async { - _setUpLogged(); - testWidgets('Selecionar um local disponível', // - (WidgetTester tester) async { - await tester.pumpWidget(const App()); - await tester.pumpAndSettle(); + patrolWidgetTest( + 'Selecionar um local disponível', // + (PatrolTester tester) async { + $ = tester; + await _logged(); + await $.pumpWidget(const App()); + await $.pumpAndSettle(); - final Finder profileFinder = - find.byKey(const Key('AsyncLocalProfileComponentWidget_InkWell')); - expect(profileFinder, findsOneWidget); + final Finder profileFinder = + find.byKey(const Key('AsyncLocalProfileComponentWidget_InkWell')); + expect(profileFinder, findsOneWidget); - await tester.tap(profileFinder); - await tester.pump(const Duration(seconds: 1)); + await $.tap(profileFinder); + await $.pump(const Duration(seconds: 1)); - final Finder bottomSheetFinder = - find.byType(BottomArrowLinkedLocalsComponentWidget); - expect(bottomSheetFinder, findsOneWidget); + final Finder bottomSheetFinder = + find.byType(BottomArrowLinkedLocalsComponentWidget); + expect(bottomSheetFinder, findsOneWidget); - await tester.ensureVisible(bottomSheetFinder); - await tester.pump(const Duration(seconds: 1)); + await $.pump(const Duration(seconds: 1)); - final Finder listViewFinder = find.descendant( - of: bottomSheetFinder, - matching: find.byType(ListView), - ); - expect(listViewFinder, findsOneWidget); + final Finder listViewFinder = find.descendant( + of: bottomSheetFinder, + matching: find.byType(ListView), + ); + expect(listViewFinder, findsOneWidget); - await tester.ensureVisible(listViewFinder); - await tester.pump(const Duration(seconds: 1)); + await $.pump(const Duration(seconds: 1)); - final Finder entriesFinder = find.descendant( - of: listViewFinder, - matching: find.byType(CardItemTemplateComponentWidget), - ); - expect(entriesFinder, findsWidgets); + final Finder entriesFinder = find.descendant( + of: listViewFinder, + matching: find.byType(CardItemTemplateComponentWidget), + ); + expect(entriesFinder, findsWidgets); - if (entriesFinder.evaluate().isNotEmpty) { - await tester.ensureVisible(entriesFinder.first); - await tester.tap(entriesFinder.first); - await tester.pumpAndSettle(); - } + if (entriesFinder.evaluate().isNotEmpty) { + await $.tap(entriesFinder.first); + await $.pumpAndSettle(); + } - await Future.delayed(const Duration(milliseconds: 500)); - return; - }); - _tearDown(); + await Future.delayed(const Duration(milliseconds: 500)); + return; + }, + ); } static Future unlinkLocal() async { - _setUpAllLogged(); - - testWidgets('Desvincular do local selecionado', // - (WidgetTester tester) async { - await tester.pumpWidget(const App()); - await tester.pumpAndSettle(); - await tester.pump(const Duration(seconds: 1)); + patrolWidgetTest('Desvincular do local selecionado', // + (PatrolTester tester) async { + $ = tester; + await _logged(); + await $.pumpWidget(const App()); + await $.pumpAndSettle(); + await $.pump(const Duration(seconds: 1)); final Finder gridView = find.byType(GridView); - await tester.ensureVisible(gridView); final Finder entries = find.descendant( of: gridView, matching: find.byType(ButtonMenuItem), ); - await tester.pumpAndSettle(); + await $.pumpAndSettle(); expect(entries, findsWidgets); final Finder settings = find.descendant( of: gridView, @@ -72,28 +70,25 @@ class LocalsTest { ); expect(settings, findsOneWidget); - await tester.ensureVisible(settings); - await tester.tap(settings); - await tester.pumpAndSettle(); + await $.tap(settings); + await $.pumpAndSettle(); final Finder unlinkButton = find.byIcon(Symbols.digital_out_of_home); expect(unlinkButton, findsOneWidget); - await tester.ensureVisible(unlinkButton); - await tester.tap(unlinkButton); - await tester.pumpAndSettle(); - await tester.tap(find.text('Sim')); - await tester.pump(); - await tester.pump(const Duration(seconds: 1)); - await tester.pump(); + await $.tap(unlinkButton); + await $.pumpAndSettle(); + await $.tap(find.text('Sim')); + await $.pump(); + await $.pump(const Duration(seconds: 1)); + await $.pump(); final Finder bottomSheetFinder = find.byType(BottomArrowLinkedLocalsComponentWidget); - await tester.pump(const Duration(seconds: 1)); + await $.pump(const Duration(seconds: 1)); expect(bottomSheetFinder, findsOneWidget); - await tester.pump(const Duration(seconds: 1)); + await $.pump(const Duration(seconds: 1)); - await tester.ensureVisible(bottomSheetFinder); - await tester.pump(const Duration(seconds: 1)); + await $.pump(const Duration(seconds: 1)); final Finder listViewFinder = find.descendant( of: bottomSheetFinder, @@ -101,8 +96,7 @@ class LocalsTest { ); expect(listViewFinder, findsOneWidget); - await tester.ensureVisible(listViewFinder); - await tester.pump(const Duration(seconds: 1)); + await $.pump(const Duration(seconds: 1)); final Finder entriesFinder = find.descendant( of: listViewFinder, @@ -113,59 +107,61 @@ class LocalsTest { return; }); - testWidgets('Desvincular de um local já desvinculado', // - (WidgetTester tester) async { - await tester.pumpWidget(const App()); - try { - await tester.pumpAndSettle( - const Duration(seconds: 2), - EnginePhase.sendSemanticsUpdate, - const Duration(seconds: 2), - ); - throw Exception('Local está vinculado'); - } catch (e) { - await Future.delayed(const Duration(milliseconds: 500)); - return; - } - }); - - _tearDownAll(); + patrolWidgetTest( + 'Desvincular de um local já desvinculado', // + (PatrolTester tester) async { + $ = tester; + await $.pumpWidget(const App()); + try { + await $.pumpAndSettle( + // const Duration(seconds: 2), + // EnginePhase.sendSemanticsUpdate, + // const Duration(seconds: 2), + ); + throw Exception('Local está vinculado'); + } catch (e) { + await Future.delayed(const Duration(milliseconds: 500)); + return; + } + }, + ); } static Future attachLocal() async { - _setUpAllLogged(); + patrolWidgetTest( + 'Selecionar um local disponível', // + (PatrolTester tester) async { + $ = tester; + await _logged(); + await $.pumpWidget(const App()); + late Map credentials; + final PatrolFinder throwsException = $(''); - testWidgets('Selecionar um local disponível', // - (WidgetTester tester) async { - await tester.pumpWidget(const App()); - late Map credentials; + var name = ff.randomString(7, 7, true, true, true); + var email = '$name@example.com'; + var password = '12345678'; + credentials = { + 'nameTextFormField': name, + 'emailTextFormField': email, + 'passwordTextFormField': password + }; - var name = ff.randomString(7, 7, true, true, true); - var email = '$name@example.com'; - var password = '12345678'; - credentials = { - 'nameTextFormField': name, - 'emailTextFormField': email, - 'passwordTextFormField': password - }; + await $.pumpWidget(const App()); + await _navigateToSignUp($); + await _auth(credentials, $, throwsException); + credentials = { + 'emailTextFormField': email, + 'passwordTextFormField': password + }; + await _auth(credentials, $, throwsException); - await tester.pumpWidget(const App()); - await _navigateToSignUp(tester); - await _auth(credentials, tester); - credentials = { - 'emailTextFormField': email, - 'passwordTextFormField': password - }; - await _auth(credentials, tester); - - await tester.pumpAndSettle(); - await StorageHelper() // - .set(ProfileStorageKey.clientUUID.key, '7'); - await tester.pumpAndSettle(); - await Future.delayed(const Duration(milliseconds: 500)); - return; - }); - - _tearDownAll(); + await $.pumpAndSettle(); + await StorageHelper() // + .set(ProfileStorageKey.clientUUID.key, '7'); + await $.pumpAndSettle(); + await Future.delayed(const Duration(milliseconds: 500)); + return; + }, + ); } } diff --git a/integration_test/menu_test.dart b/integration_test/menu_test.dart index 26824558..6d7770b4 100644 --- a/integration_test/menu_test.dart +++ b/integration_test/menu_test.dart @@ -2,173 +2,190 @@ part of 'app_test.dart'; class MenuTest { static Future labels2AppbarConsistency() async { - _setUpAllLogged(); - testWidgets('As labels dos menuItems correspondem aos títulos das AppBars?', - (WidgetTester tester) async { - await tester.pumpWidget(const App()); - await tester.pumpAndSettle(); + patrolWidgetTest( + 'As labels dos menuItems correspondem aos títulos das AppBars?', // + (PatrolTester tester) async { + $ = tester; + await _logged(); + await $.pumpWidget(const App()); + await $.pumpAndSettle(); - final List routes = MenuEntry.entries - .where((entry) => entry.key != 'FRE-HUB-LOGOUT') - .map((entry) => entry.route) - .toList(); - final List titles = MenuEntry.entries - .where((entry) => entry.key != 'FRE-HUB-LOGOUT') - .map((entry) => entry.name) - .toList(); + final List routes = MenuEntry.entries + .where((entry) => entry.key != 'FRE-HUB-LOGOUT') + .map((entry) => entry.route) + .toList(); + final List titles = MenuEntry.entries + .where((entry) => entry.key != 'FRE-HUB-LOGOUT') + .map((entry) => entry.name) + .toList(); - tester.printToConsole(routes.toString()); + final LinkedHashMap routesTitles = + LinkedHashMap.fromIterables( + routes, + titles, + ); + for (final entry in routesTitles.entries) { + final String route = entry.key; + final String title = entry.value; + if (route == '/petsPage') continue; + if (route == '/fastPassPage') continue; + if (route == '/reservation') continue; + await $.pumpAndSettle(); - final LinkedHashMap routesTitles = - LinkedHashMap.fromIterables( - routes, - titles, - ); - for (final entry in routesTitles.entries) { - final String route = entry.key; - final String title = entry.value; - tester.printToConsole('Start: $title'); - if (route == '/petsPage') continue; - if (route == '/fastPassPage') continue; - if (route == '/reservation') continue; - await tester.pumpAndSettle(); + ff.navigatorKey.currentContext!.go(route); + await $.pumpAndSettle(); - ff.navigatorKey.currentContext!.go(route); - await tester.pumpAndSettle(); + Future.delayed(const Duration(milliseconds: 500)); - Future.delayed(const Duration(milliseconds: 500)); - - final Finder appBar = find.text(title); - await tester.pumpAndSettle(); - expect(appBar, findsOneWidget); - await tester.pumpAndSettle(); - tester.printToConsole('Finish: $title'); - } - await Future.delayed(const Duration(milliseconds: 500)); - return; - }); + final Finder appBar = find.text(title); + await $.pumpAndSettle(); + expect(appBar, findsOneWidget); + await $.pumpAndSettle(); + } + await Future.delayed(const Duration(milliseconds: 500)); + return; + }, + ); } static Future containEntries() async { - _setUpAllLogged(); - testWidgets('HomeMenu contém seus itens?', (WidgetTester tester) async { - await tester.pumpWidget(const App()); - await tester.pumpAndSettle(); - await Future.delayed(const Duration(seconds: 1)); + patrolWidgetTest( + 'HomeMenu contém seus itens?', // + (PatrolTester tester) async { + $ = tester; + await _logged(); + await $.pumpWidget(const App()); + await $.pumpAndSettle(); + await Future.delayed(const Duration(seconds: 1)); - final Finder gridView = find.byType(GridView); - await tester.pumpAndSettle(); - await tester.ensureVisible(gridView); - await tester.pumpAndSettle(); - final Finder gridEntries = find.descendant( - of: gridView, - matching: find.byType(ButtonMenuItem), - ); - await tester.pumpAndSettle(); - expect(gridEntries, findsWidgets); + final Finder gridView = find.byType(GridView); + await $.pumpAndSettle(); + await $.pumpAndSettle(); + final Finder gridEntries = find.descendant( + of: gridView, + matching: find.byType(ButtonMenuItem), + ); + await $.pumpAndSettle(); + expect(gridEntries, findsWidgets); - final List menuKeys = gridEntries - .evaluate() - .map((element) { - final key = element.widget.key; - if (key is ValueKey) { - return key.value; - } - return null; - }) - .where((key) => key != null) - .toList(); + final List menuKeys = gridEntries + .evaluate() + .map((element) { + final key = element.widget.key; + if (key is ValueKey) { + return key.value; + } + return null; + }) + .where((key) => key != null) + .toList(); - await tester.pumpAndSettle(); - final List entries = MenuEntry.entries; - await tester.pumpAndSettle(); - final List entriesKey = entries - .where((entry) => entry.types.contains(MenuEntryType.Home)) - .map((entry) => entry.key) - .toList(); - await tester.pumpAndSettle(); - expect(entriesKey, containsAll(menuKeys)); - await Future.delayed(const Duration(milliseconds: 500)); - return; - }); + await $.pumpAndSettle(); + final List entries = MenuEntry.entries; + await $.pumpAndSettle(); + final List entriesKey = entries + .where((entry) => entry.types.contains(MenuEntryType.Home)) + .map((entry) => entry.key) + .toList(); + await $.pumpAndSettle(); + expect(entriesKey, containsAll(menuKeys)); + await Future.delayed(const Duration(milliseconds: 500)); + return; + }, + ); - testWidgets('DrawerMenu contém seus itens?', (WidgetTester tester) async { - await tester.pumpWidget(const App()); - await tester.pumpAndSettle(); - await Future.delayed(const Duration(seconds: 1)); + patrolWidgetTest( + 'DrawerMenu contém seus itens?', // + (PatrolTester tester) async { + $ = tester; + await _logged(); + await $.pumpWidget(const App()); + await $.pumpAndSettle(); + await Future.delayed(const Duration(seconds: 1)); - final Finder drawerButton = find.byIcon(Icons.menu_rounded); - await tester.pumpAndSettle(); - await tester.tap(drawerButton); - await tester.pumpAndSettle(); + final Finder drawerButton = find.byIcon(Icons.menu_rounded); + await $.pumpAndSettle(); + await $.tap(drawerButton); + await $.pumpAndSettle(); - final Finder gridView = find.byType(ListView); - await tester.pumpAndSettle(); - await tester.ensureVisible(gridView); - await tester.pumpAndSettle(); - final Finder gridEntries = find.descendant( - of: gridView, - matching: find.byType(CardMenuItem), - ); - await tester.pumpAndSettle(); - expect(gridEntries, findsWidgets); + final Finder gridView = find.byType(ListView); + await $.pumpAndSettle(); + final Finder gridEntries = find.descendant( + of: gridView, + matching: find.byType(CardMenuItem), + ); + await $.pumpAndSettle(); + expect(gridEntries, findsWidgets); - final List menuKeys = gridEntries - .evaluate() - .map((element) { - final key = element.widget.key; - if (key is ValueKey) { - return key.value; - } - return null; - }) - .where((key) => key != null) - .toList(); + final List menuKeys = gridEntries + .evaluate() + .map((element) { + final key = element.widget.key; + if (key is ValueKey) { + return key.value; + } + return null; + }) + .where((key) => key != null) + .toList(); - await tester.pumpAndSettle(); - final List entries = MenuEntry.entries; - await tester.pumpAndSettle(); - final List entriesKey = entries - .where((entry) => entry.types.contains(MenuEntryType.Drawer)) - .map((entry) => entry.key) - .toList(); - await tester.pumpAndSettle(); - expect(entriesKey, containsAll(menuKeys)); - await Future.delayed(const Duration(milliseconds: 500)); - return; - }); - _tearDownAll(); + await $.pumpAndSettle(); + final List entries = MenuEntry.entries; + await $.pumpAndSettle(); + final List entriesKey = entries + .where((entry) => entry.types.contains(MenuEntryType.Drawer)) + .map((entry) => entry.key) + .toList(); + await $.pumpAndSettle(); + expect(entriesKey, containsAll(menuKeys)); + await Future.delayed(const Duration(milliseconds: 500)); + return; + }, + ); } static Future navToEntries() async { - _setUpAllLogged(); - testWidgets('Navegação entre items do Menu', (WidgetTester tester) async { - tester = tester; - await tester.pumpWidget(const App()); - await tester.pumpAndSettle(); + patrolWidgetTest( + 'Navegação entre items do Menu', + (PatrolTester tester) async { + $ = tester; + await _logged(); + await $.pumpWidgetAndSettle(const App()); + await $.waitUntilVisible($(MenuStaggeredView)); - final Finder gridView = find.byType(GridView); - await tester.ensureVisible(gridView); - final Finder gridEntries = find.descendant( - of: gridView, - matching: find.byType(ButtonMenuItem), - ); - await tester.pumpAndSettle(); - await Future.delayed(const Duration(seconds: 5)); - expect(gridEntries, findsWidgets); + final PatrolFinder profile = + $(const Key('AsyncLocalProfileComponentWidget_InkWell')); + await $(profile) + .waitUntilVisible() + .tap(settlePolicy: SettlePolicy.noSettle); + await $.waitUntilVisible($(BottomArrowLinkedLocalsComponentWidget)); - final int gridEntriesCount = gridEntries.evaluate().length; - for (int i = 0; i < gridEntriesCount; i++) { - await Future.delayed(const Duration(seconds: 1)); - await tester.tap(gridEntries.at(i)); - await tester.pumpAndSettle(); - await Future.delayed(const Duration(seconds: 1)); - await tester.tap(find.byIcon(Icons.keyboard_arrow_left)); - await tester.pumpAndSettle(); - await Future.delayed(const Duration(milliseconds: 500)); - return; - } - }); - _tearDownAll(); + final PatrolFinder local = $('FRE ACCESS DEMO'); + await $(local) + .waitUntilVisible() + .tap(settlePolicy: SettlePolicy.trySettle); + + await $.waitUntilVisible($(MenuStaggeredView)); + + final Finder gridView = find.byType(GridView); + final Finder gridEntries = find.descendant( + of: gridView, + matching: find.byType(ButtonMenuItem), + ); + await $.pumpAndSettle(); + expect(gridEntries, findsWidgets); + + final int gridEntriesCount = gridEntries.evaluate().length; + for (int i = 0; i < gridEntriesCount; i++) { + await tester.tap(gridEntries.at(i)); + await tester.pumpAndSettle(); + await Future.delayed(const Duration(seconds: 1)); + await tester.tap(find.byIcon(Icons.keyboard_arrow_left)); + await tester.pumpAndSettle(); + await Future.delayed(const Duration(milliseconds: 500)); + return; + } + }, + ); } } diff --git a/integration_test/module_test.dart b/integration_test/module_test.dart index 9da888ce..b08d8b93 100644 --- a/integration_test/module_test.dart +++ b/integration_test/module_test.dart @@ -2,49 +2,52 @@ part of 'app_test.dart'; class ModularizationTest { static Future containLicense() async { - _setUpLogged(); - testWidgets('Os modulos de licença está sendo processados?', - (WidgetTester tester) async { - await tester.pumpWidget(const App()); - await tester.pumpAndSettle(); - await Future.delayed(const Duration(seconds: 1)); + patrolWidgetTest('Os modulos de licença está sendo processados?', + (PatrolTester tester) async { + $ = tester; + await _logged(); + await $.pumpWidgetAndSettle(const App()); + await $.waitUntilVisible($(MenuStaggeredView)); - await tester.pumpAndSettle(); final LicenseRepository licenseRepository = LicenseRepositoryImpl(); final List result = await licenseRepository.getLicense(); expect(result, isNotEmpty); - await tester.pumpAndSettle(); + await $.pumpAndSettle(); + final List entries = MenuEntry.entries; - await tester.pumpAndSettle(); final List entriesKey = entries .where((entry) => entry.types.contains(MenuEntryType.Home)) .map((entry) => '{key: ${entry.key}}') .toList(); - await tester.pumpAndSettle(); expect(result, containsAll(entriesKey)); - await Future.delayed(const Duration(milliseconds: 500)); + return; }); - _tearDown(); } static Future switchLicense() async { - _setUpLogged(); - testWidgets('Licença está sendo atualizada?', (WidgetTester tester) async { - await tester.pumpWidget(const App()); - final Finder profile = - find.byKey(const Key('AsyncLocalProfileComponentWidget_InkWell')); - await tester.pumpAndSettle(); - await tester.tap(profile); - await tester.pumpAndSettle(); - final Finder local = find.text('FRE ACCESS DEMO'); - await tester.pumpAndSettle(); - await tester.tap(local); - await tester.pumpAndSettle(); - await Future.delayed(const Duration(milliseconds: 500)); - return; - }); - _tearDown(); + patrolWidgetTest( + 'Licença está sendo atualizada?', + (PatrolTester tester) async { + $ = tester; + await _logged(); + + await $.pumpWidgetAndSettle(const App()); + await $.waitUntilVisible($(MenuStaggeredView)); + + final PatrolFinder profile = + $(const Key('AsyncLocalProfileComponentWidget_InkWell')); + await $(profile) + .waitUntilVisible() + .tap(settlePolicy: SettlePolicy.noSettle); + await $.waitUntilVisible($(BottomArrowLinkedLocalsComponentWidget)); + + final PatrolFinder local = $('FRE ACCESS DEMO'); + await $(local) + .waitUntilVisible() + .tap(settlePolicy: SettlePolicy.trySettle); + }, + ); } } diff --git a/integration_test/patrol_test.dart b/integration_test/patrol_test.dart index 47fc0c07..689f856f 100644 --- a/integration_test/patrol_test.dart +++ b/integration_test/patrol_test.dart @@ -1,2 +1,27 @@ -class PatrolTest { -} \ No newline at end of file +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:patrol/patrol.dart'; + +void main() { + patrolTest( + 'counter state is the same after going to home and switching apps', + ($) async { + // Replace later with your app's main widget + await $.pumpWidgetAndSettle( + MaterialApp( + home: Scaffold( + appBar: AppBar(title: const Text('app')), + backgroundColor: Colors.blue, + ), + ), + ); + + expect($('app'), findsOneWidget); + if (!Platform.isMacOS) { + await $.native.pressHome(); + } + }, + ); +} diff --git a/integration_test/test_bundle.dart b/integration_test/test_bundle.dart new file mode 100644 index 00000000..32287821 --- /dev/null +++ b/integration_test/test_bundle.dart @@ -0,0 +1,86 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND AND DO NOT COMMIT TO VERSION CONTROL +// ignore_for_file: type=lint, invalid_use_of_internal_member + +import 'dart:async'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:patrol/patrol.dart'; +import 'package:patrol/src/native/contracts/contracts.dart'; +import 'package:test_api/src/backend/invoker.dart'; + +// START: GENERATED TEST IMPORTS +import 'app_test.dart' as app_test; +// END: GENERATED TEST IMPORTS + +Future main() async { + // This is the entrypoint of the bundled Dart test. + // + // Its responsibilies are: + // * Running a special Dart test that runs before all the other tests and + // explores the hierarchy of groups and tests. + // * Hosting a PatrolAppService, which the native side of Patrol uses to get + // the Dart tests, and to request execution of a specific Dart test. + // + // When running on Android, the Android Test Orchestrator, before running the + // tests, makes an initial run to gather the tests that it will later run. The + // native side of Patrol (specifically: PatrolJUnitRunner class) is hooked + // into the Android Test Orchestrator lifecycle and knows when that initial + // run happens. When it does, PatrolJUnitRunner makes an RPC call to + // PatrolAppService and asks it for Dart tests. + // + // When running on iOS, the native side of Patrol (specifically: the + // PATROL_INTEGRATION_TEST_IOS_RUNNER macro) makes an initial run to gather + // the tests that it will later run (same as the Android). During that initial + // run, it makes an RPC call to PatrolAppSevice and asks it for Dart tests. + // + // Once the native runner has the list of Dart tests, it dynamically creates + // native test cases from them. On Android, this is done using the + // Parametrized JUnit runner. On iOS, new test case methods are swizzled into + // the RunnerUITests class, taking advantage of the very dynamic nature of + // Objective-C runtime. + // + // Execution of these dynamically created native test cases is then fully + // managed by the underlying native test framework (JUnit on Android, XCTest + // on iOS). The native test cases do only one thing - request execution of the + // Dart test (out of which they had been created) and wait for it to complete. + // The result of running the Dart test is the result of the native test case. + + final nativeAutomator = NativeAutomator(config: NativeAutomatorConfig()); + await nativeAutomator.initialize(); + final binding = PatrolBinding.ensureInitialized(NativeAutomatorConfig()); + final testExplorationCompleter = Completer(); + + // A special test to explore the hierarchy of groups and tests. This is a hack + // around https://github.com/dart-lang/test/issues/1998. + // + // This test must be the first to run. If not, the native side likely won't + // receive any tests, and everything will fall apart. + test('patrol_test_explorer', () { + // Maybe somewhat counterintuitively, this callback runs *after* the calls + // to group() below. + final topLevelGroup = Invoker.current!.liveTest.groups.first; + final dartTestGroup = createDartTestGroup(topLevelGroup, + tags: null, + excludeTags: null, + ); + testExplorationCompleter.complete(dartTestGroup); + print('patrol_test_explorer: obtained Dart-side test hierarchy:'); + reportGroupStructure(dartTestGroup); + }); + + // START: GENERATED TEST GROUPS + group('app_test', app_test.main); + // END: GENERATED TEST GROUPS + + final dartTestGroup = await testExplorationCompleter.future; + final appService = PatrolAppService(topLevelDartTestGroup: dartTestGroup); + binding.patrolAppService = appService; + await runAppService(appService); + + // Until now, the native test runner was waiting for us, the Dart side, to + // come alive. Now that we did, let's tell it that we're ready to be asked + // about Dart tests. + await nativeAutomator.markPatrolAppServiceReady(); + + await appService.testExecutionCompleted; +} diff --git a/integration_test/utils_test.dart b/integration_test/utils_test.dart index d1c8e5f4..7a806212 100644 --- a/integration_test/utils_test.dart +++ b/integration_test/utils_test.dart @@ -1,137 +1,58 @@ part of 'app_test.dart'; -Future _setUpLogged() async { - setUp(() async { - await initializeApp(); - await StorageHelper() // - .set(SecureStorageKey.isLogged.value, 'true'); - await StorageHelper() // - .set(SecureStorageKey.haveLocal.value, 'true'); - await StorageHelper() // - .set(ProfileStorageKey.devUUID.key, 'b5c3818753e76d85'); - await StorageHelper() // - .set(ProfileStorageKey.userUUID.key, '649c45d7514a28.85876308'); - await StorageHelper() // - .set(ProfileStorageKey.clientUUID.key, '7'); - await StorageHelper() // - .set(SecureStorageKey.email.value, 'email_app@exemplo.com'); - await StorageHelper() // - .set(SecureStorageKey.password.value, '123456'); - await StorageHelper() // - .set(LocalsStorageKey.isNewVersion.key, true); - await PhpGroup.resopndeVinculo.call(tarefa: 'A'); - await LicenseRepositoryImpl().resetLicense(); - }); +Future _logged() async { + await initializeApp(); + await StorageHelper() // + .set(SecureStorageKey.isLogged.value, 'true'); + await StorageHelper() // + .set(SecureStorageKey.haveLocal.value, 'true'); + await StorageHelper() // + .set(ProfileStorageKey.devUUID.key, 'b5c3818753e76d85'); + await StorageHelper() // + .set(ProfileStorageKey.userUUID.key, '649c45d7514a28.85876308'); + await StorageHelper() // + .set(ProfileStorageKey.clientUUID.key, '7'); + await StorageHelper() // + .set(SecureStorageKey.email.value, 'email_app@exemplo.com'); + await StorageHelper() // + .set(SecureStorageKey.password.value, '123456'); + await StorageHelper() // + .set(LocalsStorageKey.isNewVersion.key, true); + await PhpGroup // + .resopndeVinculo + .call(tarefa: 'A'); + await LicenseRepositoryImpl() // + .resetLicense(); } -Future _setUpUnlogged() async { - setUp(() async { - await initializeApp(); - await StorageHelper() // - .set(SecureStorageKey.isLogged.value, 'false'); - await StorageHelper() // - .set(SecureStorageKey.haveLocal.value, 'false'); - await StorageHelper() // - .set(ProfileStorageKey.devUUID.key, ''); - await StorageHelper() // - .set(ProfileStorageKey.userUUID.key, ''); - await StorageHelper() // - .set(ProfileStorageKey.clientUUID.key, ''); - await StorageHelper() // - .set(SecureStorageKey.email.value, ''); - await StorageHelper() // - .set(SecureStorageKey.password.value, ''); - await StorageHelper() // - .set(LocalsStorageKey.isNewVersion.key, true); - }); +Future _unlogged() async { + await initializeApp(); + await StorageHelper() // + .set(SecureStorageKey.isLogged.value, 'false'); + await StorageHelper() // + .set(SecureStorageKey.haveLocal.value, 'false'); + await StorageHelper() // + .set(ProfileStorageKey.devUUID.key, ''); + await StorageHelper() // + .set(ProfileStorageKey.userUUID.key, ''); + await StorageHelper() // + .set(ProfileStorageKey.clientUUID.key, ''); + await StorageHelper() // + .set(SecureStorageKey.email.value, ''); + await StorageHelper() // + .set(SecureStorageKey.password.value, ''); + await StorageHelper() // + .set(LocalsStorageKey.isNewVersion.key, true); } -Future _tearDownAll() async { - tearDownAll(() async { - await StorageHelper().clean(Storage.databaseStorage); - await StorageHelper().clean(Storage.secureStorage); - await StorageHelper().clean(Storage.sharedPreferences); - }); +Future _navigateToSignIn(PatrolTester $) async { + final signInButton = $(#toggleSignInPage); + await signInButton.tap(); } -Future _tearDown() async { - tearDown(() async { - await StorageHelper().clean(Storage.databaseStorage); - await StorageHelper().clean(Storage.secureStorage); - await StorageHelper().clean(Storage.sharedPreferences); - }); -} - -Future _setUpAllLogged() async { - setUpAll(() async { - await initializeApp().then((_) async { - await StorageHelper() // - .set(SecureStorageKey.isLogged.value, 'true'); - await StorageHelper() // - .set(SecureStorageKey.haveLocal.value, 'true'); - await StorageHelper() // - .set(ProfileStorageKey.devUUID.key, 'b5c3818753e76d85'); - await StorageHelper() // - .set(ProfileStorageKey.userUUID.key, '649c45d7514a28.85876308'); - await StorageHelper() // - .set(ProfileStorageKey.clientUUID.key, '7'); - await StorageHelper() // - .set(SecureStorageKey.email.value, 'email_app@exemplo.com'); - await StorageHelper() // - .set(SecureStorageKey.password.value, '123456'); - await StorageHelper() // - .set(LocalsStorageKey.isNewVersion.key, true); - await PhpGroup.resopndeVinculo.call(tarefa: 'A'); - }); - await LicenseRepositoryImpl().resetLicense(); - }); -} - -Future _setUpAllUnlogged() async { - setUpAll(() async { - await initializeApp().then((_) async { - await StorageHelper() // - .set(SecureStorageKey.isLogged.value, 'false'); - await StorageHelper() // - .set(SecureStorageKey.haveLocal.value, 'false'); - await StorageHelper() // - .set(ProfileStorageKey.devUUID.key, ''); - await StorageHelper() // - .set(ProfileStorageKey.userUUID.key, ''); - await StorageHelper() // - .set(ProfileStorageKey.clientUUID.key, ''); - await StorageHelper() // - .set(SecureStorageKey.email.value, ''); - await StorageHelper() // - .set(SecureStorageKey.password.value, ''); - await StorageHelper() // - .set(LocalsStorageKey.isNewVersion.key, true); - }); - }); -} - -Future _navigateToSignIn(WidgetTester tester) async { - await tester.pumpAndSettle(); - final Finder navToSignIn = - find.byKey(const ValueKey('toggleSignInPage')); - await tester.pumpAndSettle(); - expect(navToSignIn, findsOneWidget); - await tester.pumpAndSettle(); - if (navToSignIn.evaluate().isNotEmpty) { - await tester.tap(navToSignIn); - await tester.pumpAndSettle(); - } -} - -Future _navigateToSignUp(WidgetTester tester) async { - await tester.pumpAndSettle(); - final Finder navToSignUp = - find.byKey(const ValueKey('toggleSignUpPage')); - await tester.pumpAndSettle(); - if (navToSignUp.evaluate().isNotEmpty) { - await tester.tap(navToSignUp); - await tester.pumpAndSettle(); - } +Future _navigateToSignUp(PatrolTester $) async { + final signUpButton = $(#toggleSignUpPage); + await signUpButton.tap(); } Future _navigateBackUsingSystemGesture() async => diff --git a/integration_test/welcome_test.dart b/integration_test/welcome_test.dart index e7671728..1b469617 100644 --- a/integration_test/welcome_test.dart +++ b/integration_test/welcome_test.dart @@ -2,36 +2,28 @@ part of 'app_test.dart'; class WelcomeTest { static Future signInToSignUp() async { - _setUpAllUnlogged(); - testWidgets('Sign-In to Sign-Up', (WidgetTester tester) async { - await tester.pumpWidget(const App()); - tester.printToConsole('WelcomeTest - Navigate to Sign-Up to Sign-In'); - await tester.pumpAndSettle(); - tester.printToConsole('WelcomeTest - Navigate Sign-In'); - await _navigateToSignIn(tester); - tester.printToConsole('WelcomeTest - Navigate Sign-Up'); - await _navigateToSignUp(tester); - await tester.pumpAndSettle(); - await Future.delayed(const Duration(milliseconds: 500)); - return; - }); - _tearDownAll(); + patrolWidgetTest( + 'Sign-In to Sign-Up', + (PatrolTester tester) async { + $ = tester; + await _unlogged(); + await $.pumpWidgetAndSettle(const App()); + await _navigateToSignIn($); + await _navigateToSignUp($); + }, + ); } static Future signUpToSignIn() async { - _setUpAllUnlogged(); - testWidgets('Sign-Up to Sign-In', (WidgetTester tester) async { - await tester.pumpWidget(const App()); - tester.printToConsole('WelcomeTest - Navigate to Sign-In to Sign-Up'); - await tester.pumpAndSettle(); - await _navigateToSignUp(tester); - tester.printToConsole('WelcomeTest - Navigate Sign-Up'); - await _navigateToSignIn(tester); - tester.printToConsole('WelcomeTest - Navigate Sign-In'); - await tester.pumpAndSettle(); - await Future.delayed(const Duration(milliseconds: 500)); - return; - }); - _tearDownAll(); + patrolWidgetTest( + 'Sign-Up to Sign-In', + (PatrolTester tester) async { + $ = tester; + await _unlogged(); + await $.pumpWidgetAndSettle(const App()); + await _navigateToSignUp($); + await _navigateToSignIn($); + }, + ); } } diff --git a/lib/features/menu/presentation/widgets/menu_item/menu_item_card.dart b/lib/features/menu/presentation/widgets/menu_item/menu_item_card.dart index 19843d1c..c044914f 100644 --- a/lib/features/menu/presentation/widgets/menu_item/menu_item_card.dart +++ b/lib/features/menu/presentation/widgets/menu_item/menu_item_card.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:hub/features/menu/index.dart'; import 'package:hub/flutter_flow/flutter_flow_theme.dart'; +import 'package:hub/flutter_flow/flutter_flow_util.dart'; class CardMenuItem extends MenuItem { const CardMenuItem({ @@ -35,11 +36,11 @@ class _MenuCardItemState extends State { onTap: _isProcessing ? null : () async { - setState(() { + safeSetState(() { _isProcessing = true; }); await widget.action.call(); - setState(() { + safeSetState(() { _isProcessing = false; }); }, diff --git a/lib/features/module/data/data_sources/license_remote_data_source.dart b/lib/features/module/data/data_sources/license_remote_data_source.dart index 94bb0c84..982b5ebe 100644 --- a/lib/features/module/data/data_sources/license_remote_data_source.dart +++ b/lib/features/module/data/data_sources/license_remote_data_source.dart @@ -9,7 +9,9 @@ import 'package:hub/features/module/index.dart'; abstract class LicenseRemoteDataSource { Future fetchLicenses(bool isNewVersion); + Future cleanLicense(); + Future processLicense(); } @@ -70,7 +72,7 @@ class LicenseRemoteDataSourceImpl implements LicenseRemoteDataSource { } catch (e, s) { log('Erro ao obter licenças: $e', stackTrace: s); // return await setupLicense(DatabaseStorage.database, isNewVersion); - return false; + return true; } } diff --git a/lib/features/module/data/repositories/license_repository_impl.dart b/lib/features/module/data/repositories/license_repository_impl.dart index 57e8ceb7..097c7710 100644 --- a/lib/features/module/data/repositories/license_repository_impl.dart +++ b/lib/features/module/data/repositories/license_repository_impl.dart @@ -34,9 +34,11 @@ class LicenseRepositoryImpl implements LicenseRepository { log('updateLicense'); bool result = false; final bool isNewVersion = await localDataSource.isNewVersion(); - result = await localDataSource.setupLicense(isNewVersion); + if (isNewVersion) { result = await remoteDataSource.fetchLicenses(isNewVersion); + } else { + result = await localDataSource.setupLicense(isNewVersion); } return result; diff --git a/pubspec.lock b/pubspec.lock index 42d26e5d..8abfa797 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -350,6 +350,14 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.2" + dispose_scope: + dependency: transitive + description: + name: dispose_scope + sha256: "48ec38ca2631c53c4f8fa96b294c801e55c335db5e3fb9f82cede150cfe5a2af" + url: "https://pub.dev" + source: hosted + version: "2.1.0" dropdown_button2: dependency: "direct main" description: @@ -665,10 +673,18 @@ packages: dependency: "direct main" description: name: flutter_secure_storage - sha256: c0f1abb088adddc193286ea91eedd71900ec5707ac86503a7ae09d88c9ffc22b + sha256: f7eceb0bc6f4fd0441e29d43cab9ac2a1c5ffd7ea7b64075136b718c46954874 url: "https://pub.dev" source: hosted - version: "10.0.0-beta.2" + version: "10.0.0-beta.4" + flutter_secure_storage_darwin: + dependency: transitive + description: + name: flutter_secure_storage_darwin + sha256: f226f2a572bed96bc6542198ebaec227150786e34311d455a7e2d3d06d951845 + url: "https://pub.dev" + source: hosted + version: "0.1.0" flutter_secure_storage_linux: dependency: "direct main" description: @@ -737,10 +753,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "54900a1a1243f3c4a5506d853a2b5c2dbc38d5f27e52a52618a8054401431123" + sha256: c200fd79c918a40c5cd50ea0877fa13f81bdaf6f0a5d3dbcc2a13e3285d6aa1b url: "https://pub.dev" source: hosted - version: "2.0.16" + version: "2.0.17" flutter_test: dependency: "direct dev" description: flutter @@ -904,10 +920,10 @@ packages: dependency: "direct main" description: name: image_picker_android - sha256: aa6f1280b670861ac45220cc95adc59bb6ae130259d36f980ccb62220dc5e59f + sha256: b62d34a506e12bb965e824b6db4fbf709ee4589cf5d3e99b45ab2287b008ee0c url: "https://pub.dev" source: hosted - version: "0.8.12+19" + version: "0.8.12+20" image_picker_for_web: dependency: "direct main" description: @@ -1289,6 +1305,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + patrol: + dependency: "direct dev" + description: + name: patrol + sha256: f2b423122e63b1251fc31165ba1f7d85d60e22b4d20076ef39dcb47f83e35467 + url: "https://pub.dev" + source: hosted + version: "3.13.2" + patrol_finders: + dependency: "direct dev" + description: + name: patrol_finders + sha256: "5a1e2b4c6636e89645fc596d68224cfe6cca28e11c855b98dd0df9bed0d80405" + url: "https://pub.dev" + source: hosted + version: "2.6.0" + patrol_log: + dependency: transitive + description: + name: patrol_log + sha256: ad5d7b759d16071ca16aa9b27eb4f106ce23079792d4312941874dbc33e795cb + url: "https://pub.dev" + source: hosted + version: "0.2.2" percent_indicator: dependency: "direct main" description: @@ -1501,10 +1541,10 @@ packages: dependency: "direct main" description: name: shared_preferences_android - sha256: "02a7d8a9ef346c9af715811b01fbd8e27845ad2c41148eefd31321471b41863d" + sha256: bf808be89fe9dc467475e982c1db6c2faf3d2acf54d526cd5ec37d86c99dbd84 url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" shared_preferences_foundation: dependency: "direct main" description: @@ -1834,18 +1874,18 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" + sha256: "3ba963161bd0fe395917ba881d320b9c4f6dd3c4a233da62ab18a5025c85f1e9" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.4.0" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4" + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.4" uuid: dependency: "direct overridden" description: @@ -1970,10 +2010,10 @@ packages: dependency: transitive description: name: webview_flutter_wkwebview - sha256: b7e92f129482460951d96ef9a46b49db34bd2e1621685de26e9eaafd9674e7eb + sha256: "4adc14ea9a770cc9e2c8f1ac734536bd40e82615bd0fa6b94be10982de656cc7" url: "https://pub.dev" source: hosted - version: "3.16.3" + version: "3.17.0" win32: dependency: "direct overridden" description: @@ -2016,4 +2056,4 @@ packages: version: "3.1.3" sdks: dart: ">=3.6.0 <4.0.0" - flutter: ">=3.24.0" + flutter: ">=3.27.0" diff --git a/pubspec.yaml b/pubspec.yaml index 39a7839e..f1ae229e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -125,6 +125,8 @@ dev_dependencies: freezed: ^2.5.7 json_serializable: ^6.9.0 test: ^1.25.8 + patrol: ^3.13.2 + patrol_finders: ^2.6.0 flutter_launcher_icons: android: "launcher_icon" @@ -159,3 +161,9 @@ fonts: fonts: - asset: assets/fonts/menu.ttf +patrol: + app_name: FRE ACCESS HUB + android: + package_name: com.freaccess.hub + ios: + bundle_id: br.com.freaccess.hub \ No newline at end of file